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

diff -urN linux-2.4.21-pre5/Documentation/Configure.help linux-2.4.21-pre6/Documentation/Configure.help
--- linux-2.4.21-pre5/Documentation/Configure.help	Wed Mar 26 16:11:26 2003
+++ linux-2.4.21-pre6/Documentation/Configure.help	Wed Mar 26 16:11:31 2003
@@ -1041,7 +1041,7 @@
 
 Pacific Digital A-DMA support (EXPERIMENTAL)
 CONFIG_BLK_DEV_PDC_ADMA
-  Please read the comments at the top of <file:drivers/ide/ide-pci.c>.
+  Please read the comments at the top of <file:drivers/ide/setup-pci.c>.
 
 3ware Hardware ATA-RAID support
 CONFIG_BLK_DEV_3W_XXXX_RAID
@@ -1071,13 +1071,13 @@
   The ATP860 is an UltraDMA 66 chipset base.
   The ATP860M(acintosh) version is an UltraDMA 66 chipset base.
 
-  Please read the comments at the top of <file:drivers/ide/aec62xx.c>.
+  Please read the comments at the top of <file:drivers/ide/pci/aec62xx.c>.
   If you say Y here, then say Y to "Use DMA by default when available"
   as well.
 
 AEC62XX Tuning support
 CONFIG_AEC62XX_TUNING
-  Please read the comments at the top of <file:drivers/ide/aec62xx.c>.
+  Please read the comments at the top of <file:drivers/ide/pci/aec62xx.c>.
   If unsure, say N.
 
 ALI M15x3 chipset support
@@ -1088,7 +1088,7 @@
 
   If you say Y here, you also need to say Y to "Use DMA by default
   when available", above.  Please read the comments at the top of
-  <file:drivers/ide/alim15x3.c>.
+  <file:drivers/ide/pci/alim15x3.c>.
 
   If unsure, say N.
 
@@ -1112,7 +1112,7 @@
 
   If you say Y here, you also need to say Y to "Use DMA by default
   when available", above.
-  Please read the comments at the top of <file:drivers/ide/amd74xx.c>.
+  Please read the comments at the top of <file:drivers/ide/pci/amd74xx.c>.
 
   If unsure, say N.
 
@@ -1156,16 +1156,18 @@
 HPT34X AUTODMA support (WIP)
 CONFIG_HPT34X_AUTODMA
   This is a dangerous thing to attempt currently! Please read the
-  comments at the top of <file:drivers/ide/hpt34x.c>.  If you say Y
+  comments at the top of <file:drivers/ide/pci/hpt34x.c>.  If you say Y
   here, then say Y to "Use DMA by default when available" as well.
 
   If unsure, say N.
 
-HPT366/368/370 chipset support
+HPT36X/37X chipset support
 CONFIG_BLK_DEV_HPT366
   HPT366 is an Ultra DMA chipset for ATA-66.
   HPT368 is an Ultra DMA chipset for ATA-66 RAID Based.
   HPT370 is an Ultra DMA chipset for ATA-100.
+  HPT372 is an Ultra DMA chipset for ATA-133.
+  HPT374 is an Ultra DMA chipset for ATA-133.
 
   This driver adds up to 4 more EIDE devices sharing a single
   interrupt.
@@ -1187,12 +1189,12 @@
   This driver adds detection and support for the NS87415 chip
   (used in SPARC64, among others).
 
-  Please read the comments at the top of <file:drivers/ide/ns87415.c>.
+  Please read the comments at the top of <file:drivers/ide/pci/ns87415.c>.
 
 OPTi 82C621 chipset enhanced support (EXPERIMENTAL)
 CONFIG_BLK_DEV_OPTI621
   This is a driver for the OPTi 82C621 EIDE controller.
-  Please read the comments at the top of <file:drivers/ide/opti621.c>.
+  Please read the comments at the top of <file:drivers/ide/pci/opti621.c>.
 
 ServerWorks OSB4/CSB5 chipset support
 CONFIG_BLK_DEV_SVWKS
@@ -1206,7 +1208,7 @@
   PIO 0-4 mode settings, this allows dynamic tuning of the chipset
   via the standard end-user tool 'hdparm'.
 
-  Please read the comments at the top of <file:drivers/ide/piix.c>.
+  Please read the comments at the top of <file:drivers/ide/pci/piix.c>.
 
   If you say Y here, you should also say Y to "PIIXn Tuning support",
   below.
@@ -1226,7 +1228,7 @@
   If unsure, say N.
 
 PROMISE PDC20246/PDC20262/PDC20265/PDC20267/PDC20268 support
-CONFIG_BLK_DEV_PDC202XX
+CONFIG_BLK_DEV_PDC202XX_OLD
   Promise Ultra33 or PDC20246
   Promise Ultra66 or PDC20262
   Promise Ultra100 or PDC20265/PDC20267/PDC20268
@@ -1244,7 +1246,7 @@
   available" as well.
 
   Please read the comments at the top of
-  <file:drivers/ide/pdc202xx.c>.
+  <file:drivers/ide/pci/pdc202xx_old.c>.
 
   If unsure, say N.
 
@@ -1259,7 +1261,7 @@
   when the PDC20265 BIOS has been disabled (for faster boot up).
 
   Please read the comments at the top of
-  <file:drivers/ide/pdc202xx.c>.
+  <file:drivers/ide/pci/pdc202xx_old.c>.
 
   If unsure, say N.
 
@@ -1282,7 +1284,7 @@
   If you say Y here, you need to say Y to "Use DMA by default when
   available" as well.
 
-  Please read the comments at the top of <file:drivers/ide/sis5513.c>.
+  Please read the comments at the top of <file:drivers/ide/pci/sis5513.c>.
 
 Silicon Image chipset support
 CONFIG_BLK_DEV_SIIMAGE
@@ -1301,7 +1303,7 @@
   available" as well.
 
   Please read the comments at the top of
-  <file:drivers/ide/slc90e66.c>.
+  <file:drivers/ide/pci/slc90e66.c>.
 
 Winbond SL82c105 support
 CONFIG_BLK_DEV_SL82C105
@@ -1314,7 +1316,7 @@
   This driver adds support for bus master DMA transfers
   using the Tekram TRM290 PCI IDE chip. Volunteers are
   needed for further tweaking and development.
-  Please read the comments at the top of <file:drivers/ide/trm290.c>.
+  Please read the comments at the top of <file:drivers/ide/pci/trm290.c>.
 
 VIA82CXXX chipset support
 CONFIG_BLK_DEV_VIA82CXXX
@@ -1326,7 +1328,7 @@
   system" support.
 
   Please read the comments at the top of
-  <file:drivers/ide/via82cxxx.c>.
+  <file:drivers/ide/pci/via82cxxx.c>.
 
   If you say Y here, then say Y to "Use DMA by default when available"
   as well.
@@ -1366,7 +1368,7 @@
   boot parameter.  It enables support for the secondary IDE interface
   of the ALI M1439/1443/1445/1487/1489 chipsets, and permits faster
   I/O speeds to be set as well.  See the files
-  <file:Documentation/ide.txt> and <file:drivers/ide/ali14xx.c> for
+  <file:Documentation/ide.txt> and <file:drivers/ide/legacy/ali14xx.c> for
   more info.
 
 DTC-2278 support
@@ -1375,7 +1377,7 @@
   boot parameter. It enables support for the secondary IDE interface
   of the DTC-2278 card, and permits faster I/O speeds to be set as
   well. See the <file:Documentation/ide.txt> and
-  <file:drivers/ide/dtc2278.c> files for more info.
+  <file:drivers/ide/legacy/dtc2278.c> files for more info.
 
 Holtek HT6560B support
 CONFIG_BLK_DEV_HT6560B
@@ -1383,7 +1385,7 @@
   boot parameter. It enables support for the secondary IDE interface
   of the Holtek card, and permits faster I/O speeds to be set as well.
   See the <file:Documentation/ide.txt> and
-  <file:drivers/ide/ht6560b.c> files for more info.
+  <file:drivers/ide/legacy/ht6560b.c> files for more info.
 
 PROMISE DC4030 support (EXPERIMENTAL)
 CONFIG_BLK_DEV_PDC4030
@@ -1393,14 +1395,14 @@
   attached to the secondary interface.  CD-ROM and TAPE devices are
   not supported yet.  This driver is enabled at runtime using the
   "ide0=dc4030" kernel boot parameter.  See the
-  <file:Documentation/ide.txt> and <file:drivers/ide/pdc4030.c> files
+  <file:Documentation/ide.txt> and <file:drivers/ide/legacy/pdc4030.c> files
   for more info.
 
 QDI QD65XX support
 CONFIG_BLK_DEV_QD65XX
   This driver is enabled at runtime using the "ide0=qd65xx" kernel
   boot parameter.  It permits faster I/O speeds to be set.  See the
-  <file:Documentation/ide.txt> and <file:drivers/ide/qd65xx.c> for
+  <file:Documentation/ide.txt> and <file:drivers/ide/legacy/qd65xx.c> for
   more info.
 
 UMC 8672 support
@@ -1409,7 +1411,7 @@
   boot parameter. It enables support for the secondary IDE interface
   of the UMC-8672, and permits faster I/O speeds to be set as well.
   See the files <file:Documentation/ide.txt> and
-  <file:drivers/ide/umc8672.c> for more info.
+  <file:drivers/ide/legacy/umc8672.c> for more info.
 
 Amiga Gayle IDE interface support
 CONFIG_BLK_DEV_GAYLE
@@ -4175,6 +4177,7 @@
    - "Winchip-2" for IDT Winchip 2.
    - "Winchip-2A" for IDT Winchips with 3dNow! capabilities.
    - "CyrixIII" for VIA Cyrix III or VIA C3.
+   - "VIA C3-2 for VIA C3-2 "Nehemiah" (model 9 and above).
 
   If you don't know what to do, choose "386".
 
@@ -4196,6 +4199,13 @@
   Select this for a Pentium Classic processor with the RDTSC (Read
   Time Stamp Counter) instruction for benchmarking.
 
+VIA C3-2 (Nehemiah)
+CONFIG_MVIAC3_2
+  Select this for a VIA C3 "Nehemiah". Selecting this enables usage of SSE
+  and tells gcc to treat the CPU as a 686.
+
+  Note, this kernel will not boot on older (pre model 9) C3s.
+
 32-bit PDC
 CONFIG_PDC_NARROW
   Saying Y here will allow developers with a C180, C200, C240, C360,
@@ -4583,14 +4593,11 @@
   BIOS routines contained in a ROM chip in HP PA-RISC based machines.
   Enabling this option will implement the linux framebuffer device and
   an fbcon color text console using calls to the STI BIOS routines.
-  The HP framebuffer device is usually planar, uses a strange memory
+  The HP framebuffer device is sometimes planar, using a strange memory
   layout, and changing the plane mask to create colored pixels
-  requires a call to the STI routines, so do not expect /dev/fb to
-  actually be useful.  However, it is the best we have as far as
-  graphics on the HP chipsets due to lack of hardware level
-  documentation for the various on-board HP chipsets used in these
-  systems.  It is sufficient for basic text console functions,
-  including fonts.
+  can require a call to the STI routines, so /dev/fb may not actually 
+  be useful.  However, on some systems packed pixel formats are supported.  
+  It is sufficient for basic text console functions, including fonts.
 
   You should probably enable this option, unless you are having
   trouble getting video when booting the kernel (make sure it isn't
@@ -22096,8 +22103,8 @@
 Sun3 NCR5380 SCSI
 CONFIG_SUN3_SCSI
   This option will enable support for the OBIO (onboard io) NCR5380
-  SCSI controller found in the Sun 3/50 and 3/60.  Note that this
-  driver does not provide support for VME SCSI boards.
+  SCSI controller found in the Sun 3/50 and 3/60, as well as for
+  "Sun3" type VME scsi controllers also based on the NCR5380.
   General Linux information on the Sun 3 series (now discontinued)
   is at <http://www.angelfire.com/ca2/tech68k/sun3.html>.
 
diff -urN linux-2.4.21-pre5/Documentation/DocBook/kernel-api.tmpl linux-2.4.21-pre6/Documentation/DocBook/kernel-api.tmpl
--- linux-2.4.21-pre5/Documentation/DocBook/kernel-api.tmpl	Fri Aug  2 17:39:42 2002
+++ linux-2.4.21-pre6/Documentation/DocBook/kernel-api.tmpl	Wed Mar 26 16:11:31 2003
@@ -85,7 +85,11 @@
      <title>Memory Management in Linux</title>
      <sect1><title>The Slab Cache</title>
 !Emm/slab.c
-      </sect1>
+     </sect1>
+     <sect1><title>User Space Memory Access</title>
+!Iinclude/asm-i386/uaccess.h
+!Iarch/i386/lib/usercopy.c
+     </sect1>
   </chapter>
 
   <chapter id="proc">
diff -urN linux-2.4.21-pre5/Documentation/IPMI.txt linux-2.4.21-pre6/Documentation/IPMI.txt
--- linux-2.4.21-pre5/Documentation/IPMI.txt	Wed Mar 26 16:11:26 2003
+++ linux-2.4.21-pre6/Documentation/IPMI.txt	Wed Mar 26 16:11:31 2003
@@ -5,6 +5,18 @@
 			  <minyard@mvista.com>
 			    <minyard@acm.org>
 
+The Intelligent Platform Management Interface, or IPMI, is a
+standard for controlling intelligent devices that monitor a system.
+It provides for dynamic discovery of sensors in the system and the
+ability to monitor the sensors and be informed when the sensor's
+values change or go outside certain boundaries.  It also has a
+standardized database for field-replacable units (FRUs) and a watchdog
+timer.
+
+To use this, you need an interface to an IPMI controller in your
+system (called a Baseboard Management Controller, or BMC) and
+management software that can use the IPMI system.
+
 This document describes how to use the IPMI driver for Linux.  If you
 are not familiar with IPMI itself, see the web site at
 http://www.intel.com/design/servers/ipmi/index.htm.  IPMI is a big
diff -urN linux-2.4.21-pre5/MAINTAINERS linux-2.4.21-pre6/MAINTAINERS
--- linux-2.4.21-pre5/MAINTAINERS	Wed Mar 26 16:11:26 2003
+++ linux-2.4.21-pre6/MAINTAINERS	Wed Mar 26 16:11:31 2003
@@ -257,6 +257,13 @@
 L:	linux-net@vger.kernel.org
 S:	Maintained
 
+ATM
+P:	Chas Williams
+M:	chas@cmf.nrl.navy.mil
+L:	linux-atm-general@lists.sourceforge.net
+W:	http://linux-atm.sourceforge.net
+S:	Maintained
+
 AX.25 NETWORK LAYER
 P:	Matthias Welwarsky
 M:	dg2fef@afthd.tu-darmstadt.de
@@ -307,6 +314,15 @@
 W:	http://www.holtmann.org/linux/bluetooth/
 S:	Maintained
 
+BONDING DRIVER
+P:   Chad Tindel
+M:   ctindel@users.sourceforge.net
+P:   Jay Vosburgh
+M:   fubar@us.ibm.com
+L:   bonding-devel@lists.sourceforge.net
+W:   http://sourceforge.net/projects/bonding/
+S:   Supported
+
 BTTV VIDEO4LINUX DRIVER
 P:	Gerd Knorr
 M:	kraxel@bytesex.org
@@ -1157,6 +1173,8 @@
 M:	kuznet@ms2.inr.ac.ru
 P:	Pekka Savola (ipv6)
 M:	pekkas@netcore.fi
+P:	James Morris
+M:	jmorris@intercode.com.au
 L:	netdev@oss.sgi.com
 S:	Maintained
 
diff -urN linux-2.4.21-pre5/Makefile linux-2.4.21-pre6/Makefile
--- linux-2.4.21-pre5/Makefile	Wed Mar 26 16:11:26 2003
+++ linux-2.4.21-pre6/Makefile	Wed Mar 26 16:11:31 2003
@@ -1,7 +1,7 @@
 VERSION = 2
 PATCHLEVEL = 4
 SUBLEVEL = 21
-EXTRAVERSION = -pre5
+EXTRAVERSION = -pre6
 
 KERNELRELEASE=$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)
 
diff -urN linux-2.4.21-pre5/REPORTING-BUGS linux-2.4.21-pre6/REPORTING-BUGS
--- linux-2.4.21-pre5/REPORTING-BUGS	Fri Apr  6 10:42:48 2001
+++ linux-2.4.21-pre6/REPORTING-BUGS	Wed Mar 26 16:11:31 2003
@@ -9,7 +9,7 @@
 bug report. This explains what you should do with the "Oops" information
 to make it useful to the recipient.
 
-      Send the output the maintainer of the kernel area that seems to
+      Send the output to the maintainer of the kernel area that seems to
 be involved with the problem. Don't worry too much about getting the
 wrong person. If you are unsure send it to the person responsible for the
 code relevant to what you were doing. If it occurs repeatably try and
diff -urN linux-2.4.21-pre5/arch/alpha/kernel/entry.S linux-2.4.21-pre6/arch/alpha/kernel/entry.S
--- linux-2.4.21-pre5/arch/alpha/kernel/entry.S	Fri Aug  2 17:39:42 2002
+++ linux-2.4.21-pre6/arch/alpha/kernel/entry.S	Wed Mar 26 16:11:31 2003
@@ -231,12 +231,12 @@
 .end	kernel_clone
 
 /*
- * kernel_thread(fn, arg, clone_flags)
+ * arch_kernel_thread(fn, arg, clone_flags)
  */
 .align 3
 .globl	kernel_thread
 .ent	kernel_thread
-kernel_thread:
+arch_kernel_thread:
 	ldgp	$29,0($27)	/* we can be called from a module */
 	.frame $30, 4*8, $26
 	subq	$30,4*8,$30
diff -urN linux-2.4.21-pre5/arch/arm/kernel/process.c linux-2.4.21-pre6/arch/arm/kernel/process.c
--- linux-2.4.21-pre5/arch/arm/kernel/process.c	Fri Aug  2 17:39:42 2002
+++ linux-2.4.21-pre6/arch/arm/kernel/process.c	Wed Mar 26 16:11:31 2003
@@ -366,7 +366,7 @@
  * a system call from a "real" process, but the process memory space will
  * not be free'd until both the parent and the child have exited.
  */
-pid_t kernel_thread(int (*fn)(void *), void *arg, unsigned long flags)
+pid_t arch_kernel_thread(int (*fn)(void *), void *arg, unsigned long flags)
 {
 	pid_t __ret;
 
diff -urN linux-2.4.21-pre5/arch/cris/drivers/eeprom.c linux-2.4.21-pre6/arch/cris/drivers/eeprom.c
--- linux-2.4.21-pre5/arch/cris/drivers/eeprom.c	Thu Jul 26 15:10:06 2001
+++ linux-2.4.21-pre6/arch/cris/drivers/eeprom.c	Wed Mar 26 16:11:31 2003
@@ -808,7 +808,7 @@
       i2c_outbyte( eeprom.select_cmd | 1 );
     }
 
-    if(i2c_getack());
+    if(i2c_getack())
     {
       break;
     }
diff -urN linux-2.4.21-pre5/arch/cris/kernel/entry.S linux-2.4.21-pre6/arch/cris/kernel/entry.S
--- linux-2.4.21-pre5/arch/cris/kernel/entry.S	Wed Mar 26 16:11:26 2003
+++ linux-2.4.21-pre6/arch/cris/kernel/entry.S	Wed Mar 26 16:11:32 2003
@@ -739,12 +739,12 @@
  * the grosser the code, at least with the gcc version in cris-dist-1.13.
  */
 
-/* int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags) */
+/* int arch_kernel_thread(int (*fn)(void *), void * arg, unsigned long flags) */
 /*                   r10                r11         r12  */
 
 	.text
-	.global kernel_thread
-kernel_thread:
+	.global arch_kernel_thread
+arch_kernel_thread:
 
 	/* Save ARG for later.  */
 	move.d $r11, $r13
diff -urN linux-2.4.21-pre5/arch/i386/Makefile linux-2.4.21-pre6/arch/i386/Makefile
--- linux-2.4.21-pre5/arch/i386/Makefile	Wed Mar 26 16:11:26 2003
+++ linux-2.4.21-pre6/arch/i386/Makefile	Wed Mar 26 16:11:32 2003
@@ -90,6 +90,10 @@
 CFLAGS += $(call check_gcc,-falign-functions=0 -falign-jumps=0 -falign-loops=0,-malign-functions=0 -malign-jumps=0 -malign-loops=0)
 endif
 
+ifdef CONFIG_MVIAC3_2
+CFLAGS += $(call check_gcc,-march=c3-2,-march=i686)
+endif
+
 HEAD := arch/i386/kernel/head.o arch/i386/kernel/init_task.o
 
 SUBDIRS += arch/i386/kernel arch/i386/mm arch/i386/lib
diff -urN linux-2.4.21-pre5/arch/i386/config.in linux-2.4.21-pre6/arch/i386/config.in
--- linux-2.4.21-pre5/arch/i386/config.in	Wed Mar 26 16:11:26 2003
+++ linux-2.4.21-pre6/arch/i386/config.in	Wed Mar 26 16:11:32 2003
@@ -42,7 +42,8 @@
 	 Winchip-C6				CONFIG_MWINCHIPC6 \
 	 Winchip-2				CONFIG_MWINCHIP2 \
 	 Winchip-2A/Winchip-3			CONFIG_MWINCHIP3D \
-	 CyrixIII/VIA-C3/VIA-C5			CONFIG_MCYRIXIII" Pentium-Pro
+	 CyrixIII/VIA-C3			CONFIG_MCYRIXIII \
+	 VIA-C3-2				CONFIG_MVIAC3_2" Pentium-Pro
 #
 # Define implied options from the CPU selection here
 #
@@ -153,6 +154,14 @@
    define_bool CONFIG_X86_ALIGNMENT_16 y
    define_bool CONFIG_X86_USE_3DNOW y
    define_bool CONFIG_X86_USE_PPRO_CHECKSUM y
+   define_bool CONFIG_X86_F00F_WORKS_OK y
+fi
+if [ "$CONFIG_MVIAC3_2" = "y" ]; then
+   define_int  CONFIG_X86_L1_CACHE_SHIFT 5
+   define_bool CONFIG_X86_HAS_TSC y
+   define_bool CONFIG_X86_ALIGNMENT_16 y
+   define_bool CONFIG_X86_USE_PPRO_CHECKSUM y
+   define_bool CONFIG_X86_F00F_WORKS_OK y
 fi
 if [ "$CONFIG_MCRUSOE" = "y" ]; then
    define_int  CONFIG_X86_L1_CACHE_SHIFT 5
diff -urN linux-2.4.21-pre5/arch/i386/kernel/entry.S linux-2.4.21-pre6/arch/i386/kernel/entry.S
--- linux-2.4.21-pre5/arch/i386/kernel/entry.S	Wed Mar 26 16:11:26 2003
+++ linux-2.4.21-pre6/arch/i386/kernel/entry.S	Wed Mar 26 16:11:32 2003
@@ -643,7 +643,7 @@
 	.long SYMBOL_NAME(sys_lremovexattr)
 	.long SYMBOL_NAME(sys_fremovexattr)
  	.long SYMBOL_NAME(sys_tkill)
-	.long SYMBOL_NAME(sys_ni_syscall)	/* reserved for sendfile64 */
+	.long SYMBOL_NAME(sys_sendfile64)
 	.long SYMBOL_NAME(sys_ni_syscall)	/* 240 reserved for futex */
 	.long SYMBOL_NAME(sys_ni_syscall)	/* reserved for sched_setaffinity */
 	.long SYMBOL_NAME(sys_ni_syscall)	/* reserved for sched_getaffinity */
diff -urN linux-2.4.21-pre5/arch/i386/kernel/i386_ksyms.c linux-2.4.21-pre6/arch/i386/kernel/i386_ksyms.c
--- linux-2.4.21-pre5/arch/i386/kernel/i386_ksyms.c	Fri Aug  2 17:39:42 2002
+++ linux-2.4.21-pre6/arch/i386/kernel/i386_ksyms.c	Wed Mar 26 16:11:32 2003
@@ -86,6 +86,7 @@
 /* Networking helper routines. */
 EXPORT_SYMBOL(csum_partial_copy_generic);
 /* Delay loops */
+EXPORT_SYMBOL(__ndelay);
 EXPORT_SYMBOL(__udelay);
 EXPORT_SYMBOL(__delay);
 EXPORT_SYMBOL(__const_udelay);
diff -urN linux-2.4.21-pre5/arch/i386/kernel/pci-irq.c linux-2.4.21-pre6/arch/i386/kernel/pci-irq.c
--- linux-2.4.21-pre5/arch/i386/kernel/pci-irq.c	Thu Nov 28 15:53:09 2002
+++ linux-2.4.21-pre6/arch/i386/kernel/pci-irq.c	Wed Mar 26 16:11:32 2003
@@ -779,9 +779,16 @@
 void pcibios_enable_irq(struct pci_dev *dev)
 {
 	u8 pin;
+	extern int interrupt_line_quirk;
+	
 	pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin);
 	if (pin && !pcibios_lookup_irq(dev, 1) && !dev->irq) {
 		char *msg;
+
+		/* With IDE legacy devices the IRQ lookup failure is not a problem.. */
+		if (dev->class >> 8 == PCI_CLASS_STORAGE_IDE && !(dev->class & 0x5))
+			return;
+
 		if (io_apic_assign_pci_irqs)
 			msg = " Probably buggy MP table.";
 		else if (pci_probe & PCI_BIOS_IRQ_SCAN)
@@ -791,4 +798,9 @@
 		printk(KERN_WARNING "PCI: No IRQ known for interrupt pin %c of device %s.%s\n",
 		       'A' + pin - 1, dev->slot_name, msg);
 	}
+	/* VIA bridges use interrupt line for apic/pci steering across
+	   the V-Link */
+	else if (interrupt_line_quirk)
+		pci_write_config_byte(dev, PCI_INTERRUPT_LINE, dev->irq);
+		
 }
diff -urN linux-2.4.21-pre5/arch/i386/kernel/process.c linux-2.4.21-pre6/arch/i386/kernel/process.c
--- linux-2.4.21-pre5/arch/i386/kernel/process.c	Fri Aug  2 17:39:42 2002
+++ linux-2.4.21-pre6/arch/i386/kernel/process.c	Wed Mar 26 16:11:32 2003
@@ -485,7 +485,7 @@
 /*
  * Create a kernel thread
  */
-int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags)
+int arch_kernel_thread(int (*fn)(void *), void * arg, unsigned long flags)
 {
 	long retval, d0;
 
@@ -508,6 +508,7 @@
 		 "r" (arg), "r" (fn),
 		 "b" (flags | CLONE_VM)
 		: "memory");
+
 	return retval;
 }
 
diff -urN linux-2.4.21-pre5/arch/i386/kernel/setup.c linux-2.4.21-pre6/arch/i386/kernel/setup.c
--- linux-2.4.21-pre5/arch/i386/kernel/setup.c	Wed Mar 26 16:11:26 2003
+++ linux-2.4.21-pre6/arch/i386/kernel/setup.c	Wed Mar 26 16:11:32 2003
@@ -1266,10 +1266,19 @@
 			l2size = 256;
 	}
 
-	/* VIA C3 CPUs (670-68F) need further shifting. */
-	if (c->x86_vendor == X86_VENDOR_CENTAUR && (c->x86 == 6) &&
-		((c->x86_model == 7) || (c->x86_model == 8))) {
-		l2size = l2size >> 8;
+	if (c->x86_vendor == X86_VENDOR_CENTAUR) {
+		/* VIA C3 CPUs (670-68F) need further shifting. */
+		if ((c->x86 == 6) &&
+		    ((c->x86_model == 7) || (c->x86_model == 8))) {
+			l2size >>= 8;
+		}
+
+		/* VIA also screwed up Nehemiah stepping 1, and made
+		   it return '65KB' instead of '64KB'
+		   - Note, it seems this may only be in engineering samples. */
+		if ((c->x86==6) && (c->x86_model==9) &&
+		    (c->x86_mask==1) && (l2size==65))
+			l2size -= 1;
 	}
 
 	/* Allow user to override all this if necessary. */
@@ -1419,7 +1428,7 @@
 			 * If the BIOS didn't enable it already, enable it
 			 * here.
 			 */
-			if (c->x86_model == 6 || c->x86_model == 7) {
+			if (c->x86_model >= 6 && c->x86_model <= 10) {
 				if (!test_bit(X86_FEATURE_XMM,
 					      &c->x86_capability)) {
 					printk(KERN_INFO
@@ -2084,6 +2093,10 @@
 					set_bit(X86_FEATURE_CX8, &c->x86_capability);
 					set_bit(X86_FEATURE_3DNOW, &c->x86_capability);
 
+					/* fall through */
+
+				case 9: /* Nehemiah */
+				default:
 					get_model_name(c);
 					display_cacheinfo(c);
 					break;
@@ -2212,6 +2225,7 @@
 	{ 0x25, LVL_3,      2048 },
 	{ 0x29, LVL_3,      4096 },
 	{ 0x39, LVL_2,      128 },
+	{ 0x3b, LVL_2,      128 },
 	{ 0x3C, LVL_2,      256 },
 	{ 0x41, LVL_2,      128 },
 	{ 0x42, LVL_2,      256 },
diff -urN linux-2.4.21-pre5/arch/i386/lib/delay.c linux-2.4.21-pre6/arch/i386/lib/delay.c
--- linux-2.4.21-pre5/arch/i386/lib/delay.c	Wed Mar 26 16:11:26 2003
+++ linux-2.4.21-pre6/arch/i386/lib/delay.c	Wed Mar 26 16:11:32 2003
@@ -82,7 +82,7 @@
 	__const_udelay(usecs * 0x000010c6);  /* 2**32 / 1000000 */
 }
 
-void __ndelay(unsigned long usecs)
+void __ndelay(unsigned long nsecs)
 {
-	__const_udelay(usecs * 0x00005);  /* 2**32 / 1000000000 (rounded up) */
+	__const_udelay(nsecs * 0x00005);  /* 2**32 / 1000000000 (rounded up) */
 }
diff -urN linux-2.4.21-pre5/arch/i386/lib/usercopy.c linux-2.4.21-pre6/arch/i386/lib/usercopy.c
--- linux-2.4.21-pre5/arch/i386/lib/usercopy.c	Fri Nov  9 13:58:02 2001
+++ linux-2.4.21-pre6/arch/i386/lib/usercopy.c	Wed Mar 26 16:11:32 2003
@@ -95,6 +95,26 @@
 		: "memory");						   \
 } while (0)
 
+/**
+ * __strncpy_from_user: - Copy a NUL terminated string from userspace, with less checking.
+ * @dst:   Destination address, in kernel space.  This buffer must be at
+ *         least @count bytes long.
+ * @src:   Source address, in user space.
+ * @count: Maximum number of bytes to copy, including the trailing NUL.
+ * 
+ * Copies a NUL-terminated string from userspace to kernel space.
+ * Caller must check the specified block with access_ok() before calling
+ * this function.
+ *
+ * On success, returns the length of the string (not including the trailing
+ * NUL).
+ *
+ * If access to userspace fails, returns -EFAULT (some data may have been
+ * copied).
+ *
+ * If @count is smaller than the length of the string, copies @count bytes
+ * and returns @count.
+ */
 long
 __strncpy_from_user(char *dst, const char *src, long count)
 {
@@ -103,6 +123,24 @@
 	return res;
 }
 
+/**
+ * strncpy_from_user: - Copy a NUL terminated string from userspace.
+ * @dst:   Destination address, in kernel space.  This buffer must be at
+ *         least @count bytes long.
+ * @src:   Source address, in user space.
+ * @count: Maximum number of bytes to copy, including the trailing NUL.
+ * 
+ * Copies a NUL-terminated string from userspace to kernel space.
+ *
+ * On success, returns the length of the string (not including the trailing
+ * NUL).
+ *
+ * If access to userspace fails, returns -EFAULT (some data may have been
+ * copied).
+ *
+ * If @count is smaller than the length of the string, copies @count bytes
+ * and returns @count.
+ */
 long
 strncpy_from_user(char *dst, const char *src, long count)
 {
@@ -138,6 +176,16 @@
 		: "r"(size & 3), "0"(size / 4), "1"(addr), "a"(0));	\
 } while (0)
 
+/**
+ * clear_user: - Zero a block of memory in user space.
+ * @to:   Destination address, in user space.
+ * @n:    Number of bytes to zero.
+ *
+ * Zero a block of memory in user space.
+ *
+ * Returns number of bytes that could not be cleared.
+ * On success, this will be zero.
+ */
 unsigned long
 clear_user(void *to, unsigned long n)
 {
@@ -146,6 +194,17 @@
 	return n;
 }
 
+/**
+ * __clear_user: - Zero a block of memory in user space, with less checking.
+ * @to:   Destination address, in user space.
+ * @n:    Number of bytes to zero.
+ *
+ * Zero a block of memory in user space.  Caller must check
+ * the specified block with access_ok() before calling this function.
+ *
+ * Returns number of bytes that could not be cleared.
+ * On success, this will be zero.
+ */
 unsigned long
 __clear_user(void *to, unsigned long n)
 {
@@ -153,12 +212,17 @@
 	return n;
 }
 
-/*
- * Return the size of a string (including the ending 0)
+/**
+ * strlen_user: - Get the size of a string in user space.
+ * @str: The string to measure.
+ * @n:   The maximum valid length
  *
- * Return 0 on exception, a value greater than N if too long
+ * Get the size of a NUL-terminated string in user space.
+ *
+ * Returns the size of the string INCLUDING the terminating NUL.
+ * On exception, returns 0.
+ * If the string is too long, returns a value greater than @n.
  */
-
 long strnlen_user(const char *s, long n)
 {
 	unsigned long mask = -__addr_ok(s);
diff -urN linux-2.4.21-pre5/arch/ia64/kernel/process.c linux-2.4.21-pre6/arch/ia64/kernel/process.c
--- linux-2.4.21-pre5/arch/ia64/kernel/process.c	Wed Mar 26 16:11:26 2003
+++ linux-2.4.21-pre6/arch/ia64/kernel/process.c	Wed Mar 26 16:11:32 2003
@@ -230,7 +230,7 @@
  *	|                     | <-- sp (lowest addr)
  *	+---------------------+
  *
- * Note: if we get called through kernel_thread() then the memory
+ * Note: if we get called through arch_kernel_thread() then the memory
  * above "(highest addr)" is valid kernel stack memory that needs to
  * be copied as well.
  *
@@ -485,7 +485,7 @@
 }
 
 pid_t
-kernel_thread (int (*fn)(void *), void *arg, unsigned long flags)
+arch_kernel_thread (int (*fn)(void *), void *arg, unsigned long flags)
 {
 	struct task_struct *parent = current;
 	int result, tid;
diff -urN linux-2.4.21-pre5/arch/m68k/amiga/config.c linux-2.4.21-pre6/arch/m68k/amiga/config.c
--- linux-2.4.21-pre5/arch/m68k/amiga/config.c	Thu Nov 28 15:53:09 2002
+++ linux-2.4.21-pre6/arch/m68k/amiga/config.c	Wed Mar 26 16:11:32 2003
@@ -21,6 +21,7 @@
 #include <linux/rtc.h>
 #include <linux/init.h>
 #include <linux/vt_kern.h>
+#include <linux/delay.h>
 #ifdef CONFIG_ZORRO
 #include <linux/zorro.h>
 #endif
@@ -95,6 +96,7 @@
 static void a2000_gettod (int *, int *, int *, int *, int *, int *);
 static int amiga_hwclk (int, struct rtc_time *);
 static int amiga_set_clock_mmss (unsigned long);
+static unsigned int amiga_get_ss (void);
 extern void amiga_mksound( unsigned int count, unsigned int ticks );
 #ifdef CONFIG_AMIGA_FLOPPY
 extern void amiga_floppy_setup(char *, int *);
@@ -428,6 +430,7 @@
 
   mach_hwclk           = amiga_hwclk;
   mach_set_clock_mmss  = amiga_set_clock_mmss;
+  mach_get_ss          = amiga_get_ss; 
 #ifdef CONFIG_AMIGA_FLOPPY
   mach_floppy_setup    = amiga_floppy_setup;
 #endif
@@ -574,135 +577,145 @@
 static void a3000_gettod (int *yearp, int *monp, int *dayp,
 			  int *hourp, int *minp, int *secp)
 {
-	volatile struct tod3000 *tod = TOD_3000;
+	tod_3000.cntrl1 = TOD3000_CNTRL1_HOLD;
 
-	tod->cntrl1 = TOD3000_CNTRL1_HOLD;
+	*secp  = tod_3000.second1 * 10 + tod_3000.second2;
+	*minp  = tod_3000.minute1 * 10 + tod_3000.minute2;
+	*hourp = tod_3000.hour1   * 10 + tod_3000.hour2;
+	*dayp  = tod_3000.day1    * 10 + tod_3000.day2;
+	*monp  = tod_3000.month1  * 10 + tod_3000.month2;
+	*yearp = tod_3000.year1   * 10 + tod_3000.year2;
 
-	*secp  = tod->second1 * 10 + tod->second2;
-	*minp  = tod->minute1 * 10 + tod->minute2;
-	*hourp = tod->hour1   * 10 + tod->hour2;
-	*dayp  = tod->day1    * 10 + tod->day2;
-	*monp  = tod->month1  * 10 + tod->month2;
-	*yearp = tod->year1   * 10 + tod->year2;
-
-	tod->cntrl1 = TOD3000_CNTRL1_FREE;
+	tod_3000.cntrl1 = TOD3000_CNTRL1_FREE;
 }
 
 static void a2000_gettod (int *yearp, int *monp, int *dayp,
 			  int *hourp, int *minp, int *secp)
 {
-	volatile struct tod2000 *tod = TOD_2000;
+	int cnt = 5;
 
-	tod->cntrl1 = TOD2000_CNTRL1_HOLD;
+	tod_2000.cntrl1 |= TOD2000_CNTRL1_HOLD;
+	
+	while ((tod_2000.cntrl1 & TOD2000_CNTRL1_BUSY) && cnt--)
+	{
+	        tod_2000.cntrl1 &= ~TOD2000_CNTRL1_HOLD;
+	        udelay(70);
+	        tod_2000.cntrl1 |= TOD2000_CNTRL1_HOLD;
+	}
 
-	while (tod->cntrl1 & TOD2000_CNTRL1_BUSY)
-		;
+	if (!cnt)
+		printk(KERN_INFO "gettod: timed out waiting for RTC (0x%x)\n", tod_2000.cntrl1);
 
-	*secp  = tod->second1     * 10 + tod->second2;
-	*minp  = tod->minute1     * 10 + tod->minute2;
-	*hourp = (tod->hour1 & 3) * 10 + tod->hour2;
-	*dayp  = tod->day1        * 10 + tod->day2;
-	*monp  = tod->month1      * 10 + tod->month2;
-	*yearp = tod->year1       * 10 + tod->year2;
+	*secp  = tod_2000.second1     * 10 + tod_2000.second2;
+	*minp  = tod_2000.minute1     * 10 + tod_2000.minute2;
+	*hourp = (tod_2000.hour1 & 3) * 10 + tod_2000.hour2;
+	*dayp  = tod_2000.day1        * 10 + tod_2000.day2;
+	*monp  = tod_2000.month1      * 10 + tod_2000.month2;
+	*yearp = tod_2000.year1       * 10 + tod_2000.year2;
 
-	if (!(tod->cntrl3 & TOD2000_CNTRL3_24HMODE)){
-		if (!(tod->hour1 & TOD2000_HOUR1_PM) && *hourp == 12)
+	if (!(tod_2000.cntrl3 & TOD2000_CNTRL3_24HMODE)){
+		if (!(tod_2000.hour1 & TOD2000_HOUR1_PM) && *hourp == 12)
 			*hourp = 0;
-		else if ((tod->hour1 & TOD2000_HOUR1_PM) && *hourp != 12)
+		else if ((tod_2000.hour1 & TOD2000_HOUR1_PM) && *hourp != 12)
 			*hourp += 12;
 	}
 
-	tod->cntrl1 &= ~TOD2000_CNTRL1_HOLD;
+	tod_2000.cntrl1 &= ~TOD2000_CNTRL1_HOLD;
 }
 
 static int amiga_hwclk(int op, struct rtc_time *t)
 {
 	if (AMIGAHW_PRESENT(A3000_CLK)) {
-		volatile struct tod3000 *tod = TOD_3000;
-
-		tod->cntrl1 = TOD3000_CNTRL1_HOLD;
+		tod_3000.cntrl1 = TOD3000_CNTRL1_HOLD;
 
 		if (!op) { /* read */
-			t->tm_sec  = tod->second1 * 10 + tod->second2;
-			t->tm_min  = tod->minute1 * 10 + tod->minute2;
-			t->tm_hour = tod->hour1   * 10 + tod->hour2;
-			t->tm_mday = tod->day1    * 10 + tod->day2;
-			t->tm_wday = tod->weekday;
-			t->tm_mon  = tod->month1  * 10 + tod->month2 - 1;
-			t->tm_year = tod->year1   * 10 + tod->year2;
+			t->tm_sec  = tod_3000.second1 * 10 + tod_3000.second2;
+			t->tm_min  = tod_3000.minute1 * 10 + tod_3000.minute2;
+			t->tm_hour = tod_3000.hour1   * 10 + tod_3000.hour2;
+			t->tm_mday = tod_3000.day1    * 10 + tod_3000.day2;
+			t->tm_wday = tod_3000.weekday;
+			t->tm_mon  = tod_3000.month1  * 10 + tod_3000.month2 - 1;
+			t->tm_year = tod_3000.year1   * 10 + tod_3000.year2;
 			if (t->tm_year <= 69)
 				t->tm_year += 100;
 		} else {
-			tod->second1 = t->tm_sec / 10;
-			tod->second2 = t->tm_sec % 10;
-			tod->minute1 = t->tm_min / 10;
-			tod->minute2 = t->tm_min % 10;
-			tod->hour1   = t->tm_hour / 10;
-			tod->hour2   = t->tm_hour % 10;
-			tod->day1    = t->tm_mday / 10;
-			tod->day2    = t->tm_mday % 10;
+			tod_3000.second1 = t->tm_sec / 10;
+			tod_3000.second2 = t->tm_sec % 10;
+			tod_3000.minute1 = t->tm_min / 10;
+			tod_3000.minute2 = t->tm_min % 10;
+			tod_3000.hour1   = t->tm_hour / 10;
+			tod_3000.hour2   = t->tm_hour % 10;
+			tod_3000.day1    = t->tm_mday / 10;
+			tod_3000.day2    = t->tm_mday % 10;
 			if (t->tm_wday != -1)
-				tod->weekday = t->tm_wday;
-			tod->month1  = (t->tm_mon + 1) / 10;
-			tod->month2  = (t->tm_mon + 1) % 10;
+				tod_3000.weekday = t->tm_wday;
+			tod_3000.month1  = (t->tm_mon + 1) / 10;
+			tod_3000.month2  = (t->tm_mon + 1) % 10;
 			if (t->tm_year >= 100)
 				t->tm_year -= 100;
-			tod->year1   = t->tm_year / 10;
-			tod->year2   = t->tm_year % 10;
+			tod_3000.year1   = t->tm_year / 10;
+			tod_3000.year2   = t->tm_year % 10;
 		}
 
-		tod->cntrl1 = TOD3000_CNTRL1_FREE;
+		tod_3000.cntrl1 = TOD3000_CNTRL1_FREE;
 	} else /* if (AMIGAHW_PRESENT(A2000_CLK)) */ {
-		volatile struct tod2000 *tod = TOD_2000;
+		int cnt = 5;
 
-		tod->cntrl1 = TOD2000_CNTRL1_HOLD;
-	    
-		while (tod->cntrl1 & TOD2000_CNTRL1_BUSY)
-			;
+		tod_2000.cntrl1 |= TOD2000_CNTRL1_HOLD;
+		
+		while ((tod_2000.cntrl1 & TOD2000_CNTRL1_BUSY) && cnt--)
+		{
+			tod_2000.cntrl1 &= ~TOD2000_CNTRL1_HOLD;
+			udelay(70);
+			tod_2000.cntrl1 |= TOD2000_CNTRL1_HOLD;
+		}
+
+		if (!cnt)
+			printk(KERN_INFO "hwclk: timed out waiting for RTC (0x%x)\n", tod_2000.cntrl1);
 
 		if (!op) { /* read */
-			t->tm_sec  = tod->second1     * 10 + tod->second2;
-			t->tm_min  = tod->minute1     * 10 + tod->minute2;
-			t->tm_hour = (tod->hour1 & 3) * 10 + tod->hour2;
-			t->tm_mday = tod->day1        * 10 + tod->day2;
-			t->tm_wday = tod->weekday;
-			t->tm_mon  = tod->month1      * 10 + tod->month2 - 1;
-			t->tm_year = tod->year1       * 10 + tod->year2;
+			t->tm_sec  = tod_2000.second1     * 10 + tod_2000.second2;
+			t->tm_min  = tod_2000.minute1     * 10 + tod_2000.minute2;
+			t->tm_hour = (tod_2000.hour1 & 3) * 10 + tod_2000.hour2;
+			t->tm_mday = tod_2000.day1        * 10 + tod_2000.day2;
+			t->tm_wday = tod_2000.weekday;
+			t->tm_mon  = tod_2000.month1      * 10 + tod_2000.month2 - 1;
+			t->tm_year = tod_2000.year1       * 10 + tod_2000.year2;
 			if (t->tm_year <= 69)
 				t->tm_year += 100;
 
-			if (!(tod->cntrl3 & TOD2000_CNTRL3_24HMODE)){
-				if (!(tod->hour1 & TOD2000_HOUR1_PM) && t->tm_hour == 12)
+			if (!(tod_2000.cntrl3 & TOD2000_CNTRL3_24HMODE)){
+				if (!(tod_2000.hour1 & TOD2000_HOUR1_PM) && t->tm_hour == 12)
 					t->tm_hour = 0;
-				else if ((tod->hour1 & TOD2000_HOUR1_PM) && t->tm_hour != 12)
+				else if ((tod_2000.hour1 & TOD2000_HOUR1_PM) && t->tm_hour != 12)
 					t->tm_hour += 12;
 			}
 		} else {
-			tod->second1 = t->tm_sec / 10;
-			tod->second2 = t->tm_sec % 10;
-			tod->minute1 = t->tm_min / 10;
-			tod->minute2 = t->tm_min % 10;
-			if (tod->cntrl3 & TOD2000_CNTRL3_24HMODE)
-				tod->hour1 = t->tm_hour / 10;
+			tod_2000.second1 = t->tm_sec / 10;
+			tod_2000.second2 = t->tm_sec % 10;
+			tod_2000.minute1 = t->tm_min / 10;
+			tod_2000.minute2 = t->tm_min % 10;
+			if (tod_2000.cntrl3 & TOD2000_CNTRL3_24HMODE)
+				tod_2000.hour1 = t->tm_hour / 10;
 			else if (t->tm_hour >= 12)
-				tod->hour1 = TOD2000_HOUR1_PM +
+				tod_2000.hour1 = TOD2000_HOUR1_PM +
 					(t->tm_hour - 12) / 10;
 			else
-				tod->hour1 = t->tm_hour / 10;
-			tod->hour2   = t->tm_hour % 10;
-			tod->day1    = t->tm_mday / 10;
-			tod->day2    = t->tm_mday % 10;
+				tod_2000.hour1 = t->tm_hour / 10;
+			tod_2000.hour2   = t->tm_hour % 10;
+			tod_2000.day1    = t->tm_mday / 10;
+			tod_2000.day2    = t->tm_mday % 10;
 			if (t->tm_wday != -1)
-				tod->weekday = t->tm_wday;
-			tod->month1  = (t->tm_mon + 1) / 10;
-			tod->month2  = (t->tm_mon + 1) % 10;
+				tod_2000.weekday = t->tm_wday;
+			tod_2000.month1  = (t->tm_mon + 1) / 10;
+			tod_2000.month2  = (t->tm_mon + 1) % 10;
 			if (t->tm_year >= 100)
 				t->tm_year -= 100;
-			tod->year1   = t->tm_year / 10;
-			tod->year2   = t->tm_year % 10;
+			tod_2000.year1   = t->tm_year / 10;
+			tod_2000.year2   = t->tm_year % 10;
 		}
 
-		tod->cntrl1 &= ~TOD2000_CNTRL1_HOLD;
+		tod_2000.cntrl1 &= ~TOD2000_CNTRL1_HOLD;
 	}
 
 	return 0;
@@ -713,35 +726,54 @@
 	short real_seconds = nowtime % 60, real_minutes = (nowtime / 60) % 60;
 
 	if (AMIGAHW_PRESENT(A3000_CLK)) {
-		volatile struct tod3000 *tod = TOD_3000;
-
-		tod->cntrl1 = TOD3000_CNTRL1_HOLD;
+		tod_3000.cntrl1 = TOD3000_CNTRL1_HOLD;
 
-		tod->second1 = real_seconds / 10;
-		tod->second2 = real_seconds % 10;
-		tod->minute1 = real_minutes / 10;
-		tod->minute2 = real_minutes % 10;
+		tod_3000.second1 = real_seconds / 10;
+		tod_3000.second2 = real_seconds % 10;
+		tod_3000.minute1 = real_minutes / 10;
+		tod_3000.minute2 = real_minutes % 10;
 		
-		tod->cntrl1 = TOD3000_CNTRL1_FREE;
+		tod_3000.cntrl1 = TOD3000_CNTRL1_FREE;
 	} else /* if (AMIGAHW_PRESENT(A2000_CLK)) */ {
-		volatile struct tod2000 *tod = TOD_2000;
+		int cnt = 5;
 
-		tod->cntrl1 = TOD2000_CNTRL1_HOLD;
-	    
-		while (tod->cntrl1 & TOD2000_CNTRL1_BUSY)
-			;
-
-		tod->second1 = real_seconds / 10;
-		tod->second2 = real_seconds % 10;
-		tod->minute1 = real_minutes / 10;
-		tod->minute2 = real_minutes % 10;
+		tod_2000.cntrl1 |= TOD2000_CNTRL1_HOLD;
+		
+		while ((tod_2000.cntrl1 & TOD2000_CNTRL1_BUSY) && cnt--)
+		{
+			tod_2000.cntrl1 &= ~TOD2000_CNTRL1_HOLD;
+			udelay(70);
+			tod_2000.cntrl1 |= TOD2000_CNTRL1_HOLD;
+		}
 
-		tod->cntrl1 &= ~TOD2000_CNTRL1_HOLD;
+		if (!cnt)
+			printk(KERN_INFO "set_clock_mmss: timed out waiting for RTC (0x%x)\n", tod_2000.cntrl1);
+
+		tod_2000.second1 = real_seconds / 10;
+		tod_2000.second2 = real_seconds % 10;
+		tod_2000.minute1 = real_minutes / 10;
+		tod_2000.minute2 = real_minutes % 10;
+
+		tod_2000.cntrl1 &= ~TOD2000_CNTRL1_HOLD;
 	}
 
 	return 0;
 }
 
+static unsigned int amiga_get_ss( void )
+{
+	unsigned int s;
+
+	if (AMIGAHW_PRESENT(A3000_CLK)) {
+		tod_3000.cntrl1 = TOD3000_CNTRL1_HOLD;
+		s = tod_3000.second1 * 10 + tod_3000.second2;
+		tod_3000.cntrl1 = TOD3000_CNTRL1_FREE;
+	} else /* if (AMIGAHW_PRESENT(A2000_CLK)) */ { 
+		s = tod_2000.second1 * 10 + tod_2000.second2;
+	}
+	return s;
+}
+
 static NORET_TYPE void amiga_reset( void )
     ATTRIB_NORET;
 
diff -urN linux-2.4.21-pre5/arch/m68k/apollo/config.c linux-2.4.21-pre6/arch/m68k/apollo/config.c
--- linux-2.4.21-pre5/arch/m68k/apollo/config.c	Thu Nov 28 15:53:09 2002
+++ linux-2.4.21-pre6/arch/m68k/apollo/config.c	Wed Mar 26 16:11:32 2003
@@ -51,7 +51,6 @@
 static void dn_timer_int(int irq,void *, struct pt_regs *);
 static void (*sched_timer_handler)(int, void *, struct pt_regs *)=NULL;
 static void dn_get_model(char *model);
-static int dn_cpuctrl=0xff00;
 static const char *apollo_models[] = {
 	"DN3000 (Otter)",
 	"DN3010 (Otter)",
@@ -313,6 +312,8 @@
 }
 
 #ifdef CONFIG_HEARTBEAT
+static int dn_cpuctrl=0xff00;
+
 static void dn_heartbeat(int on) {
 
 	if(on) { 
diff -urN linux-2.4.21-pre5/arch/m68k/apollo/dma.c linux-2.4.21-pre6/arch/m68k/apollo/dma.c
--- linux-2.4.21-pre5/arch/m68k/apollo/dma.c	Fri Jan 28 08:04:58 2000
+++ linux-2.4.21-pre6/arch/m68k/apollo/dma.c	Wed Mar 26 16:11:32 2003
@@ -10,6 +10,7 @@
 #include <asm/system.h>
 #include <asm/pgtable.h>
 #include <asm/apollodma.h>
+#include <asm/io.h>
 
 /* note only works for 16 Bit 1 page DMA's */
 
@@ -27,7 +28,7 @@
 #if 0
 		printk("phys_addr: %x, page_aligned_addr: %x, start_map_addr: %x\n",phys_addr,page_aligned_addr,start_map_addr+i);
 #endif
-		outw(start_map_addr+i, xlat_map_entry);
+		out_be16(xlat_map_entry, start_map_addr+i);
 	}
 
 	next_free_xlat_entry+=2;
diff -urN linux-2.4.21-pre5/arch/m68k/atari/stram.c linux-2.4.21-pre6/arch/m68k/atari/stram.c
--- linux-2.4.21-pre5/arch/m68k/atari/stram.c	Fri Aug  2 17:39:43 2002
+++ linux-2.4.21-pre6/arch/m68k/atari/stram.c	Wed Mar 26 16:11:32 2003
@@ -146,7 +146,7 @@
 
 /* The following two numbers define the maximum fraction of ST-RAM in total
  * memory, below that the kernel would automatically use ST-RAM as swap
- * space. This decision can be overriden with stram_swap= */
+ * space. This decision can be overridden with stram_swap= */
 #define MAX_STRAM_FRACTION_NOM		1
 #define MAX_STRAM_FRACTION_DENOM	3
 
@@ -347,7 +347,7 @@
 		/*
 		 * If the whole ST-RAM is used for swapping, there are no allocatable
 		 * dma pages left. But unfortunately, some shared parts of the kernel
-		 * (particularily the SCSI mid-level) call __get_dma_pages()
+		 * (particularly the SCSI mid-level) call __get_dma_pages()
 		 * unconditionally :-( These calls then fail, and scsi.c even doesn't
 		 * check for NULL return values and just crashes. The quick fix for
 		 * this (instead of doing much clean up work in the SCSI code) is to
diff -urN linux-2.4.21-pre5/arch/m68k/config.in linux-2.4.21-pre6/arch/m68k/config.in
--- linux-2.4.21-pre5/arch/m68k/config.in	Thu Nov 28 15:53:09 2002
+++ linux-2.4.21-pre6/arch/m68k/config.in	Wed Mar 26 16:11:32 2003
@@ -111,7 +111,9 @@
    bool 'Support for ST-RAM as swap space' CONFIG_STRAM_SWAP
    bool 'ST-RAM statistics in /proc' CONFIG_STRAM_PROC
 fi
-if [ "$CONFIG_AMIGA" = "y" -o "$CONFIG_ATARI" = "y" -o "$CONFIG_Q40" = "y" ]; then
+if [ "$CONFIG_AMIGA" = "y" -o "$CONFIG_APOLLO" = "y" -o \
+     "$CONFIG_ATARI" = "y" -o "$CONFIG_MAC" = "y" -o \
+     "$CONFIG_Q40" = "y" ]; then
    bool 'Use power LED as a heartbeat' CONFIG_HEARTBEAT
 else
   if [ "$CONFIG_HP300" = "y" ]; then
@@ -502,7 +504,7 @@
      "$CONFIG_SUN3X_ZS" = "y" -o "$CONFIG_SERIAL" = "y" -o \
      "$CONFIG_MVME147_SCC" -o "$CONFIG_SERIAL167" = "y" -o \
      "$CONFIG_MVME162_SCC" -o "$CONFIG_BVME6000_SCC" = "y" -o \
-     "$CONFIG_DN_SERIAL" -o ]; then
+     "$CONFIG_DN_SERIAL" ]; then
    bool 'Support for serial port console' CONFIG_SERIAL_CONSOLE
 fi
 bool 'Support for user serial device modules' CONFIG_USERIAL
diff -urN linux-2.4.21-pre5/arch/m68k/ifpsp060/fskeleton.S linux-2.4.21-pre6/arch/m68k/ifpsp060/fskeleton.S
--- linux-2.4.21-pre5/arch/m68k/ifpsp060/fskeleton.S	Wed Sep 25 00:47:39 1996
+++ linux-2.4.21-pre6/arch/m68k/ifpsp060/fskeleton.S	Wed Mar 26 16:11:32 2003
@@ -302,7 +302,6 @@
 
 | The size of this section MUST be 128 bytes!!!
 
-	.global	_FP_CALL_TOP
 _FP_CALL_TOP:
 	.long	_060_real_bsun		- _FP_CALL_TOP
 	.long	_060_real_snan		- _FP_CALL_TOP
diff -urN linux-2.4.21-pre5/arch/m68k/ifpsp060/iskeleton.S linux-2.4.21-pre6/arch/m68k/ifpsp060/iskeleton.S
--- linux-2.4.21-pre5/arch/m68k/ifpsp060/iskeleton.S	Tue Jan 19 10:58:26 1999
+++ linux-2.4.21-pre6/arch/m68k/ifpsp060/iskeleton.S	Wed Mar 26 16:11:32 2003
@@ -270,7 +270,6 @@
 
 | The size of this section MUST be 128 bytes!!!
 
-	.global	_I_CALL_TOP
 _I_CALL_TOP:
 	.long	_060_real_chk		- _I_CALL_TOP
 	.long	_060_real_divbyzero	- _I_CALL_TOP
diff -urN linux-2.4.21-pre5/arch/m68k/kernel/entry.S linux-2.4.21-pre6/arch/m68k/kernel/entry.S
--- linux-2.4.21-pre5/arch/m68k/kernel/entry.S	Thu Nov 28 15:53:09 2002
+++ linux-2.4.21-pre6/arch/m68k/kernel/entry.S	Wed Mar 26 16:11:32 2003
@@ -240,13 +240,10 @@
 2:
 	RESTORE_ALL
 1:
-#if 1
-	bfextu  %sp@(PT_SR){#5,#3},%d0	| Check for nested interrupt.
-#if MAX_NOINT_IPL > 0
-	cmpiw	#MAX_NOINT_IPL,%d0
-#endif
-	jhi	2b
-#endif
+	moveq	#(~ALLOWINT>>8)&0xff,%d0
+	andb	%sp@(PT_SR),%d0
+	jne	2b
+
 	/* check if we need to do software interrupts */
 	tstl	SYMBOL_NAME(irq_stat)	| softirq_pending ?
 	jeq	SYMBOL_NAME(ret_from_exception)
diff -urN linux-2.4.21-pre5/arch/m68k/kernel/head.S linux-2.4.21-pre6/arch/m68k/kernel/head.S
--- linux-2.4.21-pre5/arch/m68k/kernel/head.S	Wed Mar 26 16:11:27 2003
+++ linux-2.4.21-pre6/arch/m68k/kernel/head.S	Wed Mar 26 16:11:32 2003
@@ -190,7 +190,7 @@
  *
  * options
  * -------
- *	There are many options availble in a build of this file.  I've
+ *	There are many options available in a build of this file.  I've
  * taken the time to describe them here to save you the time of searching
  * for them and trying to understand what they mean.
  *
diff -urN linux-2.4.21-pre5/arch/m68k/kernel/m68k_ksyms.c linux-2.4.21-pre6/arch/m68k/kernel/m68k_ksyms.c
--- linux-2.4.21-pre5/arch/m68k/kernel/m68k_ksyms.c	Thu Jan  4 13:00:55 2001
+++ linux-2.4.21-pre6/arch/m68k/kernel/m68k_ksyms.c	Wed Mar 26 16:11:32 2003
@@ -26,7 +26,7 @@
 extern char m68k_debug_device[];
 
 extern void dump_thread(struct pt_regs *, struct user *);
-extern int dump_fpu(elf_fpregset_t *);
+extern int dump_fpu (struct pt_regs *regs, struct user_m68kfp_struct *fpu);
 
 /* platform dependent support */
 
diff -urN linux-2.4.21-pre5/arch/m68k/kernel/process.c linux-2.4.21-pre6/arch/m68k/kernel/process.c
--- linux-2.4.21-pre5/arch/m68k/kernel/process.c	Fri Aug  2 17:39:43 2002
+++ linux-2.4.21-pre6/arch/m68k/kernel/process.c	Wed Mar 26 16:11:32 2003
@@ -124,7 +124,7 @@
 /*
  * Create a kernel thread
  */
-int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags)
+int arch_kernel_thread(int (*fn)(void *), void * arg, unsigned long flags)
 {
 	int pid;
 	mm_segment_t fs;
diff -urN linux-2.4.21-pre5/arch/m68k/kernel/signal.c linux-2.4.21-pre6/arch/m68k/kernel/signal.c
--- linux-2.4.21-pre5/arch/m68k/kernel/signal.c	Fri Aug  2 17:39:43 2002
+++ linux-2.4.21-pre6/arch/m68k/kernel/signal.c	Wed Mar 26 16:11:32 2003
@@ -16,7 +16,7 @@
  * 1997-12-01  Modified for POSIX.1b signals by Andreas Schwab
  *
  * mathemu support by Roman Zippel
- *  (Note: fpstate in the signal context is completly ignored for the emulator
+ *  (Note: fpstate in the signal context is completely ignored for the emulator
  *         and the internal floating point format is put on stack)
  */
 
diff -urN linux-2.4.21-pre5/arch/m68k/kernel/time.c linux-2.4.21-pre6/arch/m68k/kernel/time.c
--- linux-2.4.21-pre5/arch/m68k/kernel/time.c	Thu Jan  4 13:00:55 2001
+++ linux-2.4.21-pre6/arch/m68k/kernel/time.c	Wed Mar 26 16:11:32 2003
@@ -55,28 +55,11 @@
  */
 static void timer_interrupt(int irq, void *dummy, struct pt_regs * regs)
 {
-	/* last time the cmos clock got updated */
-	static long last_rtc_update=0;
-
 	do_timer(regs);
 
 	if (!user_mode(regs))
 		do_profile(regs->pc);
 
-	/*
-	 * If we have an externally synchronized Linux clock, then update
-	 * CMOS clock accordingly every ~11 minutes. Set_rtc_mmss() has to be
-	 * called as close as possible to 500 ms before the new second starts.
-	 */
-	if ((time_status & STA_UNSYNC) == 0 &&
-	    xtime.tv_sec > last_rtc_update + 660 &&
-	    xtime.tv_usec >= 500000 - ((unsigned) tick) / 2 &&
-	    xtime.tv_usec <= 500000 + ((unsigned) tick) / 2) {
-	  if (set_rtc_mmss(xtime.tv_sec) == 0)
-	    last_rtc_update = xtime.tv_sec;
-	  else
-	    last_rtc_update = xtime.tv_sec - 600; /* do it again in 60 s */
-	}
 #ifdef CONFIG_HEARTBEAT
 	/* use power LED as a heartbeat instead -- much more useful
 	   for debugging -- based on the version for PReP by Cort */
diff -urN linux-2.4.21-pre5/arch/m68k/kernel/traps.c linux-2.4.21-pre6/arch/m68k/kernel/traps.c
--- linux-2.4.21-pre5/arch/m68k/kernel/traps.c	Thu Nov 28 15:53:09 2002
+++ linux-2.4.21-pre6/arch/m68k/kernel/traps.c	Wed Mar 26 16:11:32 2003
@@ -302,7 +302,7 @@
 	return res;
 }
 
-/* after an exception in a writeback the stack frame coresponding
+/* after an exception in a writeback the stack frame corresponding
  * to that exception is discarded, set a few bits in the old frame 
  * to simulate what it should look like
  */
@@ -332,7 +332,7 @@
 			fp->un.fmt7.wb2s = 0;
 	}
 
-	/* do the 2nd wb only if the first one was succesful (except for a kernel wb) */
+	/* do the 2nd wb only if the first one was successful (except for a kernel wb) */
 	if (fp->un.fmt7.wb3s & WBV_040 && (!res || fp->un.fmt7.wb3s & 4)) {
 		res = do_040writeback1(fp->un.fmt7.wb3s, fp->un.fmt7.wb3a,
 				       fp->un.fmt7.wb3d);
diff -urN linux-2.4.21-pre5/arch/m68k/mac/baboon.c linux-2.4.21-pre6/arch/m68k/mac/baboon.c
--- linux-2.4.21-pre5/arch/m68k/mac/baboon.c	Sun Dec  3 17:48:19 2000
+++ linux-2.4.21-pre6/arch/m68k/mac/baboon.c	Wed Mar 26 16:11:32 2003
@@ -1,5 +1,5 @@
 /*
- * Baboon Custom IC Managment
+ * Baboon Custom IC Management
  *
  * The Baboon custom IC controls the IDE, PCMCIA and media bay on the
  * PowerBook 190. It multiplexes multiple interrupt sources onto the
diff -urN linux-2.4.21-pre5/arch/m68k/mac/iop.c linux-2.4.21-pre6/arch/m68k/mac/iop.c
--- linux-2.4.21-pre5/arch/m68k/mac/iop.c	Wed Mar 26 16:11:27 2003
+++ linux-2.4.21-pre6/arch/m68k/mac/iop.c	Wed Mar 26 16:11:32 2003
@@ -236,7 +236,7 @@
 
 /*
  * This is called by the startup code before anything else. Its purpose
- * is to find and initalize the IOPs early in the boot sequence, so that
+ * is to find and initialize the IOPs early in the boot sequence, so that
  * the serial IOP can be placed into bypass mode _before_ we try to
  * initialize the serial console.
  */
diff -urN linux-2.4.21-pre5/arch/m68k/mac/macints.c linux-2.4.21-pre6/arch/m68k/mac/macints.c
--- linux-2.4.21-pre5/arch/m68k/mac/macints.c	Thu Nov 28 15:53:09 2002
+++ linux-2.4.21-pre6/arch/m68k/mac/macints.c	Wed Mar 26 16:11:32 2003
@@ -30,7 +30,7 @@
  *		  - slot 0: SCSI interrupt
  *		  - slot 1: Sound interrupt
  *
- * Levels 3-6 vary by machine type. For VIA or RBV Macintohes:
+ * Levels 3-6 vary by machine type. For VIA or RBV Macintoshes:
  *
  *	3	- unused (?)
  *
@@ -494,7 +494,7 @@
  * Add an interrupt service routine to an interrupt source.
  * Returns 0 on success.
  *
- * FIXME: You can register interrupts on nonexistant source (ie PSC4 on a
+ * FIXME: You can register interrupts on nonexistent source (ie PSC4 on a
  *        non-PSC machine). We should return -EINVAL in those cases.
  */
  
diff -urN linux-2.4.21-pre5/arch/m68k/mac/via.c linux-2.4.21-pre6/arch/m68k/mac/via.c
--- linux-2.4.21-pre5/arch/m68k/mac/via.c	Mon Jun 11 19:15:27 2001
+++ linux-2.4.21-pre6/arch/m68k/mac/via.c	Wed Mar 26 16:11:32 2003
@@ -137,15 +137,15 @@
 			panic("UNKNOWN VIA TYPE");
 	}
 
-	printk("VIA1 at %p is a 6522 or clone\n", via1);
+	printk(KERN_INFO "VIA1 at %p is a 6522 or clone\n", via1);
 
-	printk("VIA2 at %p is ", via2);
+	printk(KERN_INFO "VIA2 at %p is ", via2);
 	if (rbv_present) {
-		printk("an RBV\n");
+		printk(KERN_INFO "an RBV\n");
 	} else if (oss_present) {
-		printk("an OSS\n");
+		printk(KERN_INFO "an OSS\n");
 	} else {
-		printk("a 6522 or clone\n");
+		printk(KERN_INFO "a 6522 or clone\n");
 	}
 
 #ifdef DEBUG_VIA
@@ -291,22 +291,22 @@
 
 void via_debug_dump(void)
 {
-	printk("VIA1: DDRA = 0x%02X DDRB = 0x%02X ACR = 0x%02X\n",
+	printk(KERN_DEBUG "VIA1: DDRA = 0x%02X DDRB = 0x%02X ACR = 0x%02X\n",
 		(uint) via1[vDirA], (uint) via1[vDirB], (uint) via1[vACR]);
-	printk("         PCR = 0x%02X  IFR = 0x%02X IER = 0x%02X\n",
+	printk(KERN_DEBUG "         PCR = 0x%02X  IFR = 0x%02X IER = 0x%02X\n",
 		(uint) via1[vPCR], (uint) via1[vIFR], (uint) via1[vIER]);
 	if (oss_present) {
-		printk("VIA2: <OSS>\n");
+		printk(KERN_DEBUG "VIA2: <OSS>\n");
 	} else if (rbv_present) {
-		printk("VIA2:  IFR = 0x%02X  IER = 0x%02X\n",
+		printk(KERN_DEBUG "VIA2:  IFR = 0x%02X  IER = 0x%02X\n",
 			(uint) via2[rIFR], (uint) via2[rIER]);
-		printk("      SIFR = 0x%02X SIER = 0x%02X\n",
+		printk(KERN_DEBUG "      SIFR = 0x%02X SIER = 0x%02X\n",
 			(uint) via2[rSIFR], (uint) via2[rSIER]);
 	} else {
-		printk("VIA2: DDRA = 0x%02X DDRB = 0x%02X ACR = 0x%02X\n",
+		printk(KERN_DEBUG "VIA2: DDRA = 0x%02X DDRB = 0x%02X ACR = 0x%02X\n",
 			(uint) via2[vDirA], (uint) via2[vDirB],
 			(uint) via2[vACR]);
-		printk("         PCR = 0x%02X  IFR = 0x%02X IER = 0x%02X\n",
+		printk(KERN_DEBUG "         PCR = 0x%02X  IFR = 0x%02X IER = 0x%02X\n",
 			(uint) via2[vPCR],
 			(uint) via2[vIFR], (uint) via2[vIER]);
 	}
@@ -374,7 +374,10 @@
 
 	if (!rbv_present) {
 		/* set the line to be an output on non-RBV machines */
-		via2[vDirB] |= 0x02;
+		if ((macintosh_config->adb_type != MAC_ADB_PB1) &&
+		   (macintosh_config->adb_type != MAC_ADB_PB2)) {
+			via2[vDirB] |= 0x02;
+		}
 	}
 
 	/* this seems to be an ADB bit on PMU machines */
@@ -390,8 +393,23 @@
 		via2[rSIER] = 0x7F;
 		via2[rSIER] = nubus_active | 0x80;
 	} else {
-		via2[vBufA] = 0xFF;
-		via2[vDirA] = ~nubus_active;
+		/* These are ADB bits on PMU */
+		if ((macintosh_config->adb_type != MAC_ADB_PB1) &&
+		   (macintosh_config->adb_type != MAC_ADB_PB2)) {
+			switch(macintosh_config->ident)
+			{
+				case MAC_MODEL_II:
+				case MAC_MODEL_IIX:
+				case MAC_MODEL_IICX:
+				case MAC_MODEL_SE30:
+					via2[vBufA] |= 0x3F;
+					via2[vDirA] = ~nubus_active | 0xc0;
+					break;
+				default:
+					via2[vBufA] = 0xFF;
+					via2[vDirA] = ~nubus_active;
+			}
+		}
 	}
 }
 
@@ -481,7 +499,7 @@
 	int irq_bit	= 1 << irq_idx;
 
 #ifdef DEBUG_IRQUSE
-	printk("via_irq_enable(%d)\n", irq);
+	printk(KERN_DEBUG "via_irq_enable(%d)\n", irq);
 #endif
 
 	if (irq_src == 1) {
@@ -508,7 +526,21 @@
 			via2[rSIER] = IER_SET_BIT(irq_idx);
 		} else {
 			/* Make sure the bit is an input, to enable the irq */
-			via2[vDirA] &= ~irq_bit;
+			/* But not on PowerBooks, that's ADB... */
+			if ((macintosh_config->adb_type != MAC_ADB_PB1) &&
+			   (macintosh_config->adb_type != MAC_ADB_PB2)) {
+			   	switch(macintosh_config->ident)
+				{
+					case MAC_MODEL_II:
+					case MAC_MODEL_IIX:
+					case MAC_MODEL_IICX:
+					case MAC_MODEL_SE30:
+						via2[vDirA] &= (~irq_bit | 0xc0);
+						break;
+					default:
+						via2[vDirA] &= ~irq_bit;
+				}
+			}
 		}
 		nubus_active |= irq_bit;
 	}
@@ -520,7 +552,7 @@
 	int irq_bit	= 1 << irq_idx;
 
 #ifdef DEBUG_IRQUSE
-	printk("via_irq_disable(%d)\n", irq);
+	printk(KERN_DEBUG "via_irq_disable(%d)\n", irq);
 #endif
 
 	if (irq_src == 1) {
@@ -533,7 +565,11 @@
 			via2[rSIER] = IER_CLR_BIT(irq_idx);
 		} else {
 			/* disable the nubus irq by changing dir to output */
-			via2[vDirA] |= irq_bit;
+			/* except on PMU */
+			if ((macintosh_config->adb_type != MAC_ADB_PB1) &&
+			   (macintosh_config->adb_type != MAC_ADB_PB2)) {
+				via2[vDirA] |= irq_bit;
+			}
 		}
 		nubus_active &= ~irq_bit;
 	}
diff -urN linux-2.4.21-pre5/arch/m68k/math-emu/fp_arith.c linux-2.4.21-pre6/arch/m68k/math-emu/fp_arith.c
--- linux-2.4.21-pre5/arch/m68k/math-emu/fp_arith.c	Sun Aug 15 11:47:29 1999
+++ linux-2.4.21-pre6/arch/m68k/math-emu/fp_arith.c	Wed Mar 26 16:11:32 2003
@@ -113,7 +113,7 @@
 	return dest;
 }
 
-/* fp_fsub: Implementes the kernel of the FSUB, FSSUB, and FDSUB
+/* fp_fsub: Implements the kernel of the FSUB, FSSUB, and FDSUB
    instructions.
 
    Remember that the arguments are in assembler-syntax order! */
diff -urN linux-2.4.21-pre5/arch/m68k/mm/Makefile linux-2.4.21-pre6/arch/m68k/mm/Makefile
--- linux-2.4.21-pre5/arch/m68k/mm/Makefile	Fri Dec 29 14:07:20 2000
+++ linux-2.4.21-pre6/arch/m68k/mm/Makefile	Wed Mar 26 16:11:32 2003
@@ -14,7 +14,7 @@
 ifndef CONFIG_SUN3
 obj-y		+= kmap.o memory.o motorola.o
 else
-obj-y		+= sun3mmu.o
+obj-y		+= sun3kmap.o sun3mmu.o
 endif
 
 
diff -urN linux-2.4.21-pre5/arch/m68k/mm/memory.c linux-2.4.21-pre6/arch/m68k/mm/memory.c
--- linux-2.4.21-pre5/arch/m68k/mm/memory.c	Fri Aug  2 17:39:43 2002
+++ linux-2.4.21-pre6/arch/m68k/mm/memory.c	Wed Mar 26 16:11:33 2003
@@ -434,46 +434,51 @@
 #endif
 
 /* invalidate page in both caches */
-#define	clear040(paddr)					\
-	__asm__ __volatile__ ("nop\n\t"			\
-			      ".chip 68040\n\t"		\
-			      "cinvp %%bc,(%0)\n\t"	\
-			      ".chip 68k"		\
-			      : : "a" (paddr))
+static inline void clear040(unsigned long paddr)
+{
+	asm volatile (
+		"nop\n\t"
+		".chip 68040\n\t"
+		"cinvp %%bc,(%0)\n\t"
+		".chip 68k"
+		: : "a" (paddr));
+}
 
 /* invalidate page in i-cache */
-#define	cleari040(paddr)				\
-	__asm__ __volatile__ ("nop\n\t"			\
-			      ".chip 68040\n\t"		\
-			      "cinvp %%ic,(%0)\n\t"	\
-			      ".chip 68k"		\
-			      : : "a" (paddr))
+static inline void cleari040(unsigned long paddr)
+{
+	asm volatile (
+		"nop\n\t"
+		".chip 68040\n\t"
+		"cinvp %%ic,(%0)\n\t"
+		".chip 68k"
+		: : "a" (paddr));
+}
 
 /* push page in both caches */
-#define	push040(paddr)					\
-	__asm__ __volatile__ ("nop\n\t"			\
-			      ".chip 68040\n\t"		\
-			      "cpushp %%bc,(%0)\n\t"	\
-			      ".chip 68k"		\
-			      : : "a" (paddr))
+/* RZ: cpush %bc DOES invalidate %ic, regardless of DPI */
+static inline void push040(unsigned long paddr)
+{
+	asm volatile (
+		"nop\n\t"
+		".chip 68040\n\t"
+		"cpushp %%bc,(%0)\n\t"
+		".chip 68k"
+		: : "a" (paddr));
+}
 
 /* push and invalidate page in both caches, must disable ints
  * to avoid invalidating valid data */
-#define	pushcl040(paddr)			\
-	do { unsigned long flags;               \
-             save_flags(flags);                 \
-	     cli();                             \
-             push040(paddr);			\
-	     if (CPU_IS_060) clear040(paddr);	\
-	     restore_flags(flags);              \
-	} while(0)
-
-/* push page in both caches, invalidate in i-cache */
-/* RZ: cpush %bc DOES invalidate %ic, regardless of DPI */
-#define	pushcli040(paddr)			\
-	do { push040(paddr);			\
-	} while(0)
+static inline void pushcl040(unsigned long paddr)
+{
+	unsigned long flags;
 
+	local_irq_save(flags);
+	push040(paddr);
+	if (CPU_IS_060)
+		clear040(paddr);
+	local_irq_restore(flags);
+}
 
 /*
  * 040: Hit every page containing an address in the range paddr..paddr+len-1.
@@ -567,7 +572,7 @@
 	paddr &= PAGE_MASK;
 
 	do {
-	    pushcli040(paddr);
+	    push040(paddr);
 	    paddr += tmp;
 	} while ((len -= tmp) > 0);
     }
@@ -593,12 +598,6 @@
 }
 
 
-#undef clear040
-#undef cleari040
-#undef push040
-#undef pushcl040
-#undef pushcli040
-
 #ifndef CONFIG_SINGLE_MEMORY_CHUNK
 int mm_end_of_chunk (unsigned long addr, int len)
 {
diff -urN linux-2.4.21-pre5/arch/m68k/mm/sun3kmap.c linux-2.4.21-pre6/arch/m68k/mm/sun3kmap.c
--- linux-2.4.21-pre5/arch/m68k/mm/sun3kmap.c	Wed Dec 31 16:00:00 1969
+++ linux-2.4.21-pre6/arch/m68k/mm/sun3kmap.c	Wed Mar 26 16:11:33 2003
@@ -0,0 +1,156 @@
+/*
+ * linux/arch/m68k/mm/sun3kmap.c
+ *
+ * Copyright (C) 2002 Sam Creasey <sammy@sammy.net>
+ *
+ * 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 <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/vmalloc.h>
+
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <asm/io.h>
+#include <asm/sun3mmu.h>
+
+#undef SUN3_KMAP_DEBUG
+
+#ifdef SUN3_KMAP_DEBUG
+extern void print_pte_vaddr(unsigned long vaddr);
+#endif
+
+extern void mmu_emu_map_pmeg (int context, int vaddr);
+
+static inline void do_page_mapin(unsigned long phys, unsigned long virt, 
+				 unsigned long type)
+{
+	unsigned long pte;
+	pte_t ptep;
+
+	ptep = mk_pte_phys(phys, PAGE_KERNEL);
+	pte = pte_val(ptep);
+	pte |= type;
+
+	sun3_put_pte(virt, pte);
+
+#ifdef SUN3_KMAP_DEBUG
+	print_pte_vaddr(virt);
+#endif
+
+}
+
+static inline void do_pmeg_mapin(unsigned long phys, unsigned long virt, 
+				 unsigned long type, int pages)
+{
+
+	if(sun3_get_segmap(virt & ~SUN3_PMEG_MASK) == SUN3_INVALID_PMEG) 
+		mmu_emu_map_pmeg(sun3_get_context(), virt);
+
+	while(pages) {
+		do_page_mapin(phys, virt, type);
+		phys += PAGE_SIZE;
+		virt += PAGE_SIZE;
+		pages--;
+	}
+}
+
+void *sun3_ioremap(unsigned long phys, unsigned long size, 
+		   unsigned long type)
+{
+	struct vm_struct *area;
+	unsigned long offset, virt, ret;
+	int pages;
+
+	if(!size)
+		return NULL;
+
+	/* page align */
+	offset = phys & (PAGE_SIZE-1);
+	phys &= ~(PAGE_SIZE-1);
+
+	size += offset;
+	size = PAGE_ALIGN(size);
+	if((area = get_vm_area(size, VM_IOREMAP)) == NULL)
+		return NULL;
+
+#ifdef SUN3_KMAP_DEBUG
+	printk("ioremap: got virt %p size %lx(%lx)\n", 
+	       area->addr, size, area->size);
+#endif
+
+	pages = size / PAGE_SIZE;
+	virt = (unsigned long)area->addr;
+	ret = virt + offset;
+
+	while(pages) {
+		int seg_pages;
+
+		seg_pages = (SUN3_PMEG_SIZE - (virt & SUN3_PMEG_MASK)) / PAGE_SIZE;
+		if(seg_pages > pages)
+			seg_pages = pages;
+		
+		do_pmeg_mapin(phys, virt, type, seg_pages);
+
+		pages -= seg_pages;
+		phys += seg_pages * PAGE_SIZE;
+		virt += seg_pages * PAGE_SIZE;
+	}
+		
+	return (void *)ret;
+
+}
+ 
+ 
+void *__ioremap(unsigned long phys, unsigned long size, int cache)
+{
+	
+	return sun3_ioremap(phys, size, SUN3_PAGE_TYPE_IO);
+	
+}
+
+void iounmap(void *addr)
+{
+	vfree((void *)(PAGE_MASK & (unsigned long)addr));	
+}
+
+/* sun3_map_test(addr, val) -- Reads a byte from addr, storing to val,
+ * trapping the potential read fault.  Returns 0 if the access faulted,
+ * 1 on success.
+ *  
+ * This function is primarily used to check addresses on the VME bus.
+ *
+ * Mucking with the page fault handler seems a little hackish to me, but
+ * SunOS, NetBSD, and Mach all implemented this check in such a manner, 
+ * so I figure we're allowed.
+ */
+int sun3_map_test(unsigned long addr, char *val)
+{
+	int ret = 0;
+
+	__asm__ __volatile__
+		(".globl _sun3_map_test_start\n"
+		 "_sun3_map_test_start:\n"
+		 "1: moveb (%2), (%0)\n"
+		 "   moveq #1, %1\n"
+		 "2:\n"
+		 ".section .fixup,\"ax\"\n"
+		 ".even\n"
+		 "3: moveq #0, %1\n"
+		 "   jmp 2b\n"
+		 ".previous\n"
+		 ".section __ex_table,\"a\"\n"
+		 ".align 4\n"
+		 ".long 1b,3b\n"
+		 ".previous\n"
+		 ".globl _sun3_map_test_end\n"
+		 "_sun3_map_test_end:\n"
+		 : "=a"(val), "=r"(ret)
+		 : "a"(addr));
+	
+	return ret;
+}
diff -urN linux-2.4.21-pre5/arch/m68k/mm/sun3mmu.c linux-2.4.21-pre6/arch/m68k/mm/sun3mmu.c
--- linux-2.4.21-pre5/arch/m68k/mm/sun3mmu.c	Fri Dec 29 14:07:20 2000
+++ linux-2.4.21-pre6/arch/m68k/mm/sun3mmu.c	Wed Mar 26 16:11:33 2003
@@ -96,13 +96,12 @@
 	}
 
 	mmu_emu_init(bootmem_end);
-	
+
 	current->mm = NULL;
 
 	/* memory sizing is a hack stolen from motorola.c..  hope it works for us */
-	zones_size[1] = ((unsigned long)high_memory - PAGE_OFFSET) >> PAGE_SHIFT;
-	zones_size[0] = zones_size[1]/2;
-	zones_size[1] -= zones_size[0];
+	zones_size[0] = ((unsigned long)high_memory - PAGE_OFFSET) >> PAGE_SHIFT;
+	zones_size[1] = 0;
 	
 	free_area_init(zones_size);
 
diff -urN linux-2.4.21-pre5/arch/m68k/q40/q40ints.c linux-2.4.21-pre6/arch/m68k/q40/q40ints.c
--- linux-2.4.21-pre5/arch/m68k/q40/q40ints.c	Wed Mar 26 16:11:27 2003
+++ linux-2.4.21-pre6/arch/m68k/q40/q40ints.c	Wed Mar 26 16:11:33 2003
@@ -7,7 +7,7 @@
  * License.  See the file COPYING in the main directory of this archive
  * for more details.
  *
- * .. used to be losely based on bvme6000ints.c
+ * .. used to be loosely based on bvme6000ints.c
  *
  */
 
@@ -438,7 +438,7 @@
 {
   /* disable ISA iqs : only do something if the driver has been
    * verified to be Q40 "compatible" - right now IDE, NE2K
-   * Any driver should not attempt to sleep accross disable_irq !!
+   * Any driver should not attempt to sleep across disable_irq !!
    */
 
   if ( irq>=5 && irq<=15 ) {
diff -urN linux-2.4.21-pre5/arch/m68k/sun3/Makefile linux-2.4.21-pre6/arch/m68k/sun3/Makefile
--- linux-2.4.21-pre5/arch/m68k/sun3/Makefile	Mon Jun 11 19:15:27 2001
+++ linux-2.4.21-pre6/arch/m68k/sun3/Makefile	Wed Mar 26 16:11:33 2003
@@ -14,9 +14,9 @@
 
 export-objs	:= sun3_ksyms.o
 
-obj-y	:= sun3_ksyms.o sun3ints.o sun3dvma.o sbus.o
+obj-y	:= sun3_ksyms.o sun3ints.o sun3dvma.o sbus.o idprom.o
 
-obj-$(CONFIG_SUN3) += config.o idprom.o mmu_emu.o leds.o dvma.o \
+obj-$(CONFIG_SUN3) += config.o mmu_emu.o leds.o dvma.o \
 			intersil.o
 
 include $(TOPDIR)/Rules.make
diff -urN linux-2.4.21-pre5/arch/m68k/sun3/idprom.c linux-2.4.21-pre6/arch/m68k/sun3/idprom.c
--- linux-2.4.21-pre5/arch/m68k/sun3/idprom.c	Wed Sep  8 11:20:42 1999
+++ linux-2.4.21-pre6/arch/m68k/sun3/idprom.c	Wed Mar 26 16:11:33 2003
@@ -31,8 +31,8 @@
 { "Sun 3/60", (SM_SUN3 | SM_3_60) },
 { "Sun 3/E", (SM_SUN3 | SM_3_E) },
 /* Now, Sun3x's */
-{ "Sun 3/460 Series", (SM_SUN3 | SM_3_460) },
-{ "Sun 3/80", (SM_SUN3 | SM_3_80) },
+{ "Sun 3/460 Series", (SM_SUN3X | SM_3_460) },
+{ "Sun 3/80", (SM_SUN3X | SM_3_80) },
 /* Then, Sun4's */
 //{ "Sun 4/100 Series", (SM_SUN4 | SM_4_110) },
 //{ "Sun 4/200 Series", (SM_SUN4 | SM_4_260) },
diff -urN linux-2.4.21-pre5/arch/m68k/sun3/mmu_emu.c linux-2.4.21-pre6/arch/m68k/sun3/mmu_emu.c
--- linux-2.4.21-pre5/arch/m68k/sun3/mmu_emu.c	Thu Nov 28 15:53:09 2002
+++ linux-2.4.21-pre6/arch/m68k/sun3/mmu_emu.c	Wed Mar 26 16:11:33 2003
@@ -178,7 +178,6 @@
 			pmeg_alloc[sun3_get_segmap(seg)] = 2;
 		}
 	}
-
 	
 	dvma_init();
 	
@@ -277,7 +276,7 @@
 //todo: better allocation scheme? but is extra complexity worthwhile?
 //todo: only clear old entries if necessary? how to tell?
 
-static inline void mmu_emu_map_pmeg (int context, int vaddr)
+inline void mmu_emu_map_pmeg (int context, int vaddr)
 {
 	static unsigned char curr_pmeg = 128;
 	int i;
diff -urN linux-2.4.21-pre5/arch/m68k/sun3/sun3dvma.c linux-2.4.21-pre6/arch/m68k/sun3/sun3dvma.c
--- linux-2.4.21-pre5/arch/m68k/sun3/sun3dvma.c	Fri Aug  2 17:39:43 2002
+++ linux-2.4.21-pre6/arch/m68k/sun3/sun3dvma.c	Wed Mar 26 16:11:33 2003
@@ -97,7 +97,7 @@
 	printk("end of hole listing...\n");
 	
 }
-#endif DVMA_DEBUG
+#endif /* DVMA_DEBUG */
 
 static inline int refill(void)
 {
@@ -317,8 +317,14 @@
 
 void dvma_unmap(void *baddr)
 {
-
-	free_baddr((unsigned long)baddr);
+	unsigned long addr;
+	
+	addr = (unsigned long)baddr;
+	/* check if this is a vme mapping */
+	if(!(addr & 0x00f00000))
+		addr |= 0xf00000;
+	
+	free_baddr(addr);
 	
 	return;
 
diff -urN linux-2.4.21-pre5/arch/m68k/sun3/sun3ints.c linux-2.4.21-pre6/arch/m68k/sun3/sun3ints.c
--- linux-2.4.21-pre5/arch/m68k/sun3/sun3ints.c	Thu Nov 28 15:53:09 2002
+++ linux-2.4.21-pre6/arch/m68k/sun3/sun3ints.c	Wed Mar 26 16:11:33 2003
@@ -17,6 +17,7 @@
 #include <asm/sun3ints.h>
 
 extern void sun3_leds (unsigned char);
+static void sun3_inthandle(int irq, void *dev_id, struct pt_regs *fp);
 
 void sun3_disable_interrupts(void)
 {
@@ -62,11 +63,6 @@
 	*sun3_intreg |=  (1<<irq);
 }
 
-int sun3_get_irq_list(char *buf)
-{
-	return 0;
-}
-
 static void sun3_int7(int irq, void *dev_id, struct pt_regs *fp)
 {
 	sun3_do_irq(irq,fp);
@@ -93,13 +89,39 @@
 
 /* handle requested ints, excepting 5 and 7, which always do the same
    thing */
+void (*sun3_default_handler[SYS_IRQS])(int, void *, struct pt_regs *) = {
+	sun3_inthandle, sun3_inthandle, sun3_inthandle, sun3_inthandle,
+	sun3_inthandle, sun3_int5, sun3_inthandle, sun3_int7
+};
+
+static const char *dev_names[SYS_IRQS] = { NULL, NULL, NULL, NULL,
+				     NULL, "timer", NULL, "int7 handler" };
 static void *dev_ids[SYS_IRQS];
 static void (*sun3_inthandler[SYS_IRQS])(int, void *, struct pt_regs *) = {
 	NULL, NULL, NULL, NULL, NULL, sun3_int5, NULL, sun3_int7
 };
-static void (*sun3_vechandler[192])(int, void *, struct pt_regs *);
-static void *vec_ids[192];
-static const char *vec_names[192];
+
+static void (*sun3_vechandler[SUN3_INT_VECS])(int, void *, struct pt_regs *);
+static void *vec_ids[SUN3_INT_VECS];
+static const char *vec_names[SUN3_INT_VECS];
+static int vec_ints[SUN3_INT_VECS];
+
+
+int sun3_get_irq_list(char *buf)
+{
+	int i, len = 0;
+
+	for(i = 0; i < (SUN3_INT_VECS-1); i++) {
+		if(sun3_vechandler[i] != NULL) {
+			len += sprintf(buf+len, "vec %3d: %10u %s\n", i+64,
+				       vec_ints[i],
+				       (vec_names[i]) ? vec_names[i] :
+				       "sun3_vechandler");
+		}
+	}
+
+	return 0;
+}
 
 static void sun3_inthandle(int irq, void *dev_id, struct pt_regs *fp)
 {
@@ -117,14 +139,6 @@
 //	intersil_clear();
 }
 
-void (*sun3_default_handler[SYS_IRQS])(int, void *, struct pt_regs *) = {
-	sun3_inthandle, sun3_inthandle, sun3_inthandle, sun3_inthandle,
-	sun3_inthandle, sun3_int5, sun3_inthandle, sun3_int7
-};
-
-static const char *dev_names[SYS_IRQS] = { NULL, NULL, NULL, NULL,
-					   NULL, "timer", NULL, NULL };
-
 void sun3_init_IRQ(void)
 {
 	int i;
@@ -175,6 +189,7 @@
 			sun3_vechandler[vec] = handler;
 			vec_ids[vec] = dev_id;
 			vec_names[vec] = devname;
+			vec_ints[vec] = 0;
 			
 			return 0;
 		}
@@ -221,7 +236,8 @@
 		vec = irq - 64;
 		if(sun3_vechandler[vec] == NULL) 
 			panic ("bad interrupt vector %d received\n",irq);
-			
+
+		vec_ints[vec]++;
 		sun3_vechandler[vec](irq, vec_ids[vec], regs);
 		return;
 	} else {
diff -urN linux-2.4.21-pre5/arch/m68k/sun3x/config.c linux-2.4.21-pre6/arch/m68k/sun3x/config.c
--- linux-2.4.21-pre5/arch/m68k/sun3x/config.c	Thu Nov 28 15:53:09 2002
+++ linux-2.4.21-pre6/arch/m68k/sun3x/config.c	Wed Mar 26 16:11:33 2003
@@ -17,12 +17,14 @@
 #include <asm/sun3xprom.h>
 #include <asm/sun3ints.h>
 #include <asm/setup.h>
+#include <asm/oplib.h>
 
 #include "time.h"
 
 volatile char *clock_va;
 extern volatile unsigned char *sun3_intreg;
 
+extern void sun3_get_model(char *model);
 
 int __init sun3x_keyb_init(void)
 {
@@ -44,10 +46,16 @@
 
 }
 
-/* should probably detect types of these eventually. */
-static void sun3x_get_model(char *model)
+static int sun3x_get_hardware_list(char *buffer)
 {
-	sprintf(model, "Sun3x");
+
+	int len = 0;
+
+	len += sprintf(buffer + len, "PROM Revision:\t%s\n",
+		       romvec->pv_monid);
+
+	return len;
+
 }
 
 /*
@@ -81,7 +89,8 @@
 
 	mach_gettod          = sun3x_gettod;
 	mach_hwclk           = sun3x_hwclk;
-	mach_get_model       = sun3x_get_model;
+	mach_get_model       = sun3_get_model;
+	mach_get_hardware_list = sun3x_get_hardware_list;
 
 	sun3_intreg = (unsigned char *)SUN3X_INTREG;
 
diff -urN linux-2.4.21-pre5/arch/m68k/sun3x/prom.c linux-2.4.21-pre6/arch/m68k/sun3x/prom.c
--- linux-2.4.21-pre5/arch/m68k/sun3x/prom.c	Thu Nov 28 15:53:09 2002
+++ linux-2.4.21-pre6/arch/m68k/sun3x/prom.c	Wed Mar 26 16:11:33 2003
@@ -26,8 +26,6 @@
 int (*sun3x_mayput)(int);
 void (*sun3x_prom_reboot)(void);
 e_vector sun3x_prom_abort;
-struct idprom *idprom;
-static struct idprom idprom_buffer;
 struct linux_romvec *romvec;
 
 /* prom vector table */
@@ -107,8 +105,6 @@
 void sun3x_prom_init(void)
 {
     /* Read the vector table */
-	int i;
-
 
     sun3x_putchar = *(void (**)(int)) (SUN3X_P_PUTCHAR);
     sun3x_getchar = *(int (**)(void)) (SUN3X_P_GETCHAR);
@@ -118,12 +114,9 @@
     sun3x_prom_abort = *(e_vector *)  (SUN3X_P_ABORT);
     romvec = (struct linux_romvec *)SUN3X_PROM_BASE;
 
-    /* make a copy of the idprom structure */
-    for(i = 0; i < sizeof(struct idprom); i++) 
-	    ((unsigned char *)(&idprom_buffer))[i] = ((unsigned char *)SUN3X_IDPROM)[i];
-    idprom = &idprom_buffer;
+    idprom_init();
 
-    if((idprom->id_machtype & SM_ARCH_MASK) != SM_SUN3X) {
+    if(!((idprom->id_machtype & SM_ARCH_MASK) == SM_SUN3X)) {
 	    printk("Warning: machine reports strange type %02x\n",
 		   idprom->id_machtype);
 	    printk("Pretending it's a 3/80, but very afraid...\n");
@@ -163,3 +156,19 @@
 {
 	sun3x_halt();
 }
+
+/* Get the idprom and stuff it into buffer 'idbuf'.  Returns the
+ * format type.  'num_bytes' is the number of bytes that your idbuf
+ * has space for.  Returns 0xff on error.
+ */
+unsigned char
+prom_get_idprom(char *idbuf, int num_bytes)
+{
+        int i;
+
+	/* make a copy of the idprom structure */
+	for(i = 0; i < num_bytes; i++) 
+		idbuf[i] = ((char *)SUN3X_IDPROM)[i];
+
+        return idbuf[0];
+}
diff -urN linux-2.4.21-pre5/arch/mips/kernel/process.c linux-2.4.21-pre6/arch/mips/kernel/process.c
--- linux-2.4.21-pre5/arch/mips/kernel/process.c	Thu Nov 28 15:53:10 2002
+++ linux-2.4.21-pre6/arch/mips/kernel/process.c	Wed Mar 26 16:11:33 2003
@@ -152,7 +152,7 @@
 /*
  * Create a kernel thread
  */
-int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags)
+int arch_kernel_thread(int (*fn)(void *), void * arg, unsigned long flags)
 {
 	long retval;
 
diff -urN linux-2.4.21-pre5/arch/mips64/kernel/process.c linux-2.4.21-pre6/arch/mips64/kernel/process.c
--- linux-2.4.21-pre5/arch/mips64/kernel/process.c	Thu Nov 28 15:53:10 2002
+++ linux-2.4.21-pre6/arch/mips64/kernel/process.c	Wed Mar 26 16:11:33 2003
@@ -151,7 +151,7 @@
 /*
  * Create a kernel thread
  */
-int kernel_thread(int (*fn)(void *), void *arg, unsigned long flags)
+int arch_kernel_thread(int (*fn)(void *), void *arg, unsigned long flags)
 {
 	int retval;
 
diff -urN linux-2.4.21-pre5/arch/parisc/kernel/process.c linux-2.4.21-pre6/arch/parisc/kernel/process.c
--- linux-2.4.21-pre5/arch/parisc/kernel/process.c	Wed Mar 26 16:11:27 2003
+++ linux-2.4.21-pre6/arch/parisc/kernel/process.c	Wed Mar 26 16:11:33 2003
@@ -169,7 +169,7 @@
  */
 
 extern pid_t __kernel_thread(int (*fn)(void *), void *arg, unsigned long flags);
-pid_t kernel_thread(int (*fn)(void *), void *arg, unsigned long flags)
+pid_t arch_kernel_thread(int (*fn)(void *), void *arg, unsigned long flags)
 {
 
 	/*
diff -urN linux-2.4.21-pre5/arch/ppc/8xx_io/fec.c linux-2.4.21-pre6/arch/ppc/8xx_io/fec.c
--- linux-2.4.21-pre5/arch/ppc/8xx_io/fec.c	Wed Mar 26 16:11:27 2003
+++ linux-2.4.21-pre6/arch/ppc/8xx_io/fec.c	Wed Mar 26 16:11:33 2003
@@ -1901,9 +1901,9 @@
 	fep->phy_id_done = 0;
 	fep->phy_addr = 0;
 	mii_queue(dev, mk_mii_read(MII_REG_PHYIR1), mii_discover_phy, 0);
-#endif	/* CONFIG_USE_MDIO */
 
 	fep->old_status = 0;
+#endif	/* CONFIG_USE_MDIO */
 
 	return 0;
 }
diff -urN linux-2.4.21-pre5/arch/ppc/8xx_io/uart.c linux-2.4.21-pre6/arch/ppc/8xx_io/uart.c
--- linux-2.4.21-pre5/arch/ppc/8xx_io/uart.c	Wed Mar 26 16:11:27 2003
+++ linux-2.4.21-pre6/arch/ppc/8xx_io/uart.c	Wed Mar 26 16:11:33 2003
@@ -129,6 +129,7 @@
 #define smc_scc_num	hub6
 #define NUM_IS_SCC	((int)0x00010000)
 #define PORT_NUM(P)	((P) & 0x0000ffff)
+#define PORT_IS_SCC(P)	((P) & NUM_IS_SCC)
 
 /* The serial port to use for KGDB. */
 #ifdef CONFIG_KGDB_TTYS1
@@ -283,7 +284,7 @@
 	
 	save_flags(flags); cli();
 	idx = PORT_NUM(info->state->smc_scc_num);
-	if (info->state->smc_scc_num & NUM_IS_SCC) {
+	if (PORT_IS_SCC(info->state->smc_scc_num)) {
 		sccp = &cpmp->cp_scc[idx];
 		sccp->scc_sccm &= ~UART_SCCM_TX;
 	}
@@ -307,7 +308,7 @@
 	
 	idx = PORT_NUM(info->state->smc_scc_num);
 	save_flags(flags); cli();
-	if (info->state->smc_scc_num & NUM_IS_SCC) {
+	if (PORT_IS_SCC(info->state->smc_scc_num)) {
 		sccp = &cpmp->cp_scc[idx];
 		sccp->scc_sccm |= UART_SCCM_TX;
 	}
@@ -640,7 +641,7 @@
 	info = (ser_info_t *)dev_id;
 
 	idx = PORT_NUM(info->state->smc_scc_num);
-	if (info->state->smc_scc_num & NUM_IS_SCC) {
+	if (PORT_IS_SCC(info->state->smc_scc_num)) {
 		sccp = &cpmp->cp_scc[idx];
 		events = sccp->scc_scce;
 		if (events & SMCM_BRKE)
@@ -786,7 +787,7 @@
 	change_speed(info);
 	
 	idx = PORT_NUM(info->state->smc_scc_num);
-	if (info->state->smc_scc_num & NUM_IS_SCC) {
+	if (PORT_IS_SCC(info->state->smc_scc_num)) {
 		sccp = &cpmp->cp_scc[idx];
 		scup = (scc_uart_t *)&cpmp->cp_dparam[state->port];
 		scup->scc_genscc.scc_mrblr = RX_BUF_SIZE;
@@ -850,7 +851,7 @@
 	save_flags(flags); cli(); /* Disable interrupts */
 
 	idx = PORT_NUM(state->smc_scc_num);
-	if (state->smc_scc_num & NUM_IS_SCC) {
+	if (PORT_IS_SCC(state->smc_scc_num)) {
 		sccp = &cpmp->cp_scc[idx];
 		sccp->scc_gsmrl &= ~(SCC_GSMRL_ENR | SCC_GSMRL_ENT);
 #ifdef CONFIG_SERIAL_CONSOLE
@@ -1005,7 +1006,7 @@
 	 */
 	bits++;
 	idx = PORT_NUM(state->smc_scc_num);
-	if (state->smc_scc_num & NUM_IS_SCC) {
+	if (PORT_IS_SCC(state->smc_scc_num)) {
 		sccp = &cpmp->cp_scc[idx];
 		new_mode = (sbits << 12) | scval;
 		prev_mode = sccp->scc_pmsr;
@@ -1404,7 +1405,7 @@
 	cp = cpmp;
 
 	idx = PORT_NUM(info->state->smc_scc_num);
-	if (info->state->smc_scc_num & NUM_IS_SCC)
+	if (PORT_IS_SCC(info->state->smc_scc_num))
 		chan = scc_chan_map[idx];
 	else
 		chan = smc_chan_map[idx];
@@ -1421,7 +1422,7 @@
 	cp = cpmp;
 
 	idx = PORT_NUM(info->state->smc_scc_num);
-	if (info->state->smc_scc_num & NUM_IS_SCC)
+	if (PORT_IS_SCC(info->state->smc_scc_num))
 		chan = scc_chan_map[idx];
 	else
 		chan = smc_chan_map[idx];
@@ -1726,7 +1727,7 @@
 	info->read_status_mask &= ~BD_SC_EMPTY;
 	if (info->flags & ASYNC_INITIALIZED) {
 		idx = PORT_NUM(info->state->smc_scc_num);
-		if (info->state->smc_scc_num & NUM_IS_SCC) {
+		if (PORT_IS_SCC(info->state->smc_scc_num)) {
 			sccp = &cpmp->cp_scc[idx];
 			sccp->scc_sccm &= ~UART_SCCM_RX;
 			sccp->scc_gsmrl &= ~SCC_GSMRL_ENR;
@@ -1917,7 +1918,7 @@
 	 */
 	if ((filp->f_flags & O_NONBLOCK) ||
 	    (tty->flags & (1 << TTY_IO_ERROR)) ||
-	    !(info->state->smc_scc_num & NUM_IS_SCC)) {
+	    !(PORT_IS_SCC(info->state->smc_scc_num))) {
 		if (info->flags & ASYNC_CALLOUT_ACTIVE)
 			return -EBUSY;
 		info->flags |= ASYNC_NORMAL_ACTIVE;
@@ -2095,7 +2096,7 @@
 
 	ret = sprintf(buf, "%d: uart:%s port:%X irq:%d",
 		      state->line,
-		      (state->smc_scc_num & NUM_IS_SCC) ? "SCC" : "SMC",
+		      (PORT_IS_SCC(state->smc_scc_num)) ? "SCC" : "SMC",
 		      (unsigned int)(state->port), state->irq);
 
 	if (!state->port || (state->type == PORT_UNKNOWN)) {
@@ -2333,6 +2334,7 @@
 }
 #endif
 
+#if defined(CONFIG_KGDB) || defined(CONFIG_XMON)
 /*
  * Receive character from the serial port.  This only works well
  * before the port is initialized for real use.
@@ -2405,6 +2407,7 @@
 
 	return((int)c);
 }
+#endif /* CONFIG_KGDB || CONFIG_XMON */
 
 #ifdef CONFIG_XMON
 int
@@ -2667,7 +2670,7 @@
 		state->icount.overrun = state->icount.brk = 0;
 		printk(KERN_INFO "ttyS%02d at 0x%04x is a %s\n",
 		       i, (unsigned int)(state->port),
-		       (state->smc_scc_num & NUM_IS_SCC) ? "SCC" : "SMC");
+		       PORT_IS_SCC(state->smc_scc_num) ? "SCC" : "SMC");
 #ifdef CONFIG_SERIAL_CONSOLE
 		/* If we just printed the message on the console port, and
 		 * we are about to initialize it for general use, we have
@@ -2719,7 +2722,7 @@
 			bdp->cbd_sc = BD_SC_WRAP | BD_SC_EMPTY | BD_SC_INTRPT;
 
 			idx = PORT_NUM(info->state->smc_scc_num);
-			if (info->state->smc_scc_num & NUM_IS_SCC) {
+			if (PORT_IS_SCC(info->state->smc_scc_num)) {
 				scp = &cp->cp_scc[idx];
 				sup = (scc_uart_t *)&cp->cp_dparam[state->port];
 				sup->scc_genscc.scc_rbase = dp_addr;
@@ -2752,7 +2755,7 @@
 			bdp->cbd_bufaddr = __pa(mem_addr);
 			bdp->cbd_sc = (BD_SC_WRAP | BD_SC_INTRPT);
 
-			if (info->state->smc_scc_num & NUM_IS_SCC) {
+			if (PORT_IS_SCC(info->state->smc_scc_num)) {
 				sup->scc_genscc.scc_tbase = dp_addr;
 
 				/* Set up the uart parameters in the
@@ -2941,7 +2944,7 @@
 	cp = cpmp;	/* Get pointer to Communication Processor */
 
 	idx = PORT_NUM(ser->smc_scc_num);
-	if (ser->smc_scc_num & NUM_IS_SCC) {
+	if (PORT_IS_SCC(ser->smc_scc_num)) {
 		scp = &cp->cp_scc[idx];
 		sup = (scc_uart_t *)&cp->cp_dparam[ser->port];
 	}
@@ -2979,7 +2982,7 @@
 
 	/* Set up the uart parameters in the parameter ram.
 	*/
-	if (ser->smc_scc_num & NUM_IS_SCC) {
+	if (PORT_IS_SCC(ser->smc_scc_num)) {
 
 		sup->scc_genscc.scc_rbase = dp_addr;
 		sup->scc_genscc.scc_tbase = dp_addr + sizeof(cbd_t);
diff -urN linux-2.4.21-pre5/arch/ppc/boot/common/misc-simple.c linux-2.4.21-pre6/arch/ppc/boot/common/misc-simple.c
--- linux-2.4.21-pre5/arch/ppc/boot/common/misc-simple.c	Wed Mar 26 16:11:27 2003
+++ linux-2.4.21-pre6/arch/ppc/boot/common/misc-simple.c	Wed Mar 26 16:11:33 2003
@@ -75,6 +75,14 @@
 extern void gunzip(void *, int, unsigned char *, int *);
 extern void serial_fixups(void);
 
+/* Allow decompress_kernel to be hooked into.  This is the default. */
+void * __attribute__ ((weak))
+load_kernel(unsigned long load_addr, int num_words, unsigned long cksum,
+		void *bp)
+{
+	return decompress_kernel(load_addr, num_words, cksum, bp);
+}
+
 struct bi_record *
 decompress_kernel(unsigned long load_addr, int num_words, unsigned long cksum)
 {
diff -urN linux-2.4.21-pre5/arch/ppc/boot/common/relocate.S linux-2.4.21-pre6/arch/ppc/boot/common/relocate.S
--- linux-2.4.21-pre5/arch/ppc/boot/common/relocate.S	Wed Mar 26 16:11:27 2003
+++ linux-2.4.21-pre6/arch/ppc/boot/common/relocate.S	Wed Mar 26 16:11:33 2003
@@ -189,7 +189,7 @@
 	mr	r4,r7		/* Program length */
 	mr	r5,r6		/* Checksum */
 	mr	r6,r11		/* Residual data */
-	bl	decompress_kernel
+	bl	load_kernel
 
 	/*
 	 * Make sure the kernel knows we don't have things set  in
@@ -199,10 +199,10 @@
 	li	r6,0
 
 	/*
-	 * Start at the begining.
+	 * Start at the beginning of real code (the first
+	 * three instructions are 'nop' since we 
+	 * sometimes overwrite them).
 	 */
-	li	r9,0x0000
-	mtlr	r9
-	blr
+	ba	0x000c
 
 	.comm	.stack,4096*2,4
diff -urN linux-2.4.21-pre5/arch/ppc/boot/lib/Makefile linux-2.4.21-pre6/arch/ppc/boot/lib/Makefile
--- linux-2.4.21-pre5/arch/ppc/boot/lib/Makefile	Thu May 24 15:02:06 2001
+++ linux-2.4.21-pre6/arch/ppc/boot/lib/Makefile	Wed Mar 26 16:11:33 2003
@@ -2,8 +2,10 @@
 # Makefile for some libs needed by zImage.
 #
 
+USE_STANDARD_AS_RULE	:= true
+
 L_TARGET := zlib.a
 
-obj-y := zlib.o
+obj-y := zlib.o div64.o
 
 include $(TOPDIR)/Rules.make
diff -urN linux-2.4.21-pre5/arch/ppc/boot/lib/div64.S linux-2.4.21-pre6/arch/ppc/boot/lib/div64.S
--- linux-2.4.21-pre5/arch/ppc/boot/lib/div64.S	Wed Dec 31 16:00:00 1969
+++ linux-2.4.21-pre6/arch/ppc/boot/lib/div64.S	Wed Mar 26 16:11:33 2003
@@ -0,0 +1,58 @@
+/*
+ * Divide a 64-bit unsigned number by a 32-bit unsigned number.
+ * This routine assumes that the top 32 bits of the dividend are
+ * non-zero to start with.
+ * On entry, r3 points to the dividend, which get overwritten with
+ * the 64-bit quotient, and r4 contains the divisor.
+ * On exit, r3 contains the remainder.
+ *
+ * Copyright (C) 2002 Paul Mackerras, IBM Corp.
+ *
+ * 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 <asm/ppc_asm.h>
+#include <asm/processor.h>
+
+_GLOBAL(__div64_32)
+	lwz	r5,0(r3)	# get the dividend into r5/r6
+	lwz	r6,4(r3)
+	cmplw	r5,r4
+	li	r7,0
+	li	r8,0
+	blt	1f
+	divwu	r7,r5,r4	# if dividend.hi >= divisor,
+	mullw	r0,r7,r4	# quotient.hi = dividend.hi / divisor
+	subf.	r5,r0,r5	# dividend.hi %= divisor
+	beq	3f
+1:	mr	r11,r5		# here dividend.hi != 0
+	andis.	r0,r5,0xc000
+	bne	2f
+	cntlzw	r0,r5		# we are shifting the dividend right
+	li	r10,-1		# to make it < 2^32, and shifting
+	srw	r10,r10,r0	# the divisor right the same amount,
+	add	r9,r4,r10	# rounding up (so the estimate cannot
+	andc	r11,r6,r10	# ever be too large, only too small)
+	andc	r9,r9,r10
+	or	r11,r5,r11
+	rotlw	r9,r9,r0
+	rotlw	r11,r11,r0
+	divwu	r11,r11,r9	# then we divide the shifted quantities
+2:	mullw	r10,r11,r4	# to get an estimate of the quotient,
+	mulhwu	r9,r11,r4	# multiply the estimate by the divisor,
+	subfc	r6,r10,r6	# take the product from the divisor,
+	add	r8,r8,r11	# and add the estimate to the accumulated
+	subfe.	r5,r9,r5	# quotient
+	bne	1b
+3:	cmplw	r6,r4
+	blt	4f
+	divwu	r0,r6,r4	# perform the remaining 32-bit division
+	mullw	r10,r0,r4	# and get the remainder
+	add	r8,r8,r0
+	subf	r6,r10,r6
+4:	stw	r7,0(r3)	# return the quotient in *r3
+	stw	r8,4(r3)
+	mr	r3,r6		# return the remainder in r3
+	blr
diff -urN linux-2.4.21-pre5/arch/ppc/boot/simple/misc-embedded.c linux-2.4.21-pre6/arch/ppc/boot/simple/misc-embedded.c
--- linux-2.4.21-pre5/arch/ppc/boot/simple/misc-embedded.c	Wed Mar 26 16:11:27 2003
+++ linux-2.4.21-pre6/arch/ppc/boot/simple/misc-embedded.c	Wed Mar 26 16:11:33 2003
@@ -79,7 +79,8 @@
 extern void embed_config(bd_t **bp);
 
 unsigned long
-decompress_kernel(unsigned long load_addr, int num_words, unsigned long cksum, bd_t *bp)
+load_kernel(unsigned long load_addr, int num_words, unsigned long cksum,
+		bd_t *bp)
 {
 #ifdef INTERACTIVE_CONSOLE
 	int timer = 0;
diff -urN linux-2.4.21-pre5/arch/ppc/boot/simple/misc-spruce.c linux-2.4.21-pre6/arch/ppc/boot/simple/misc-spruce.c
--- linux-2.4.21-pre5/arch/ppc/boot/simple/misc-spruce.c	Wed Mar 26 16:11:27 2003
+++ linux-2.4.21-pre6/arch/ppc/boot/simple/misc-spruce.c	Wed Mar 26 16:11:33 2003
@@ -152,7 +152,7 @@
 #define MEM_B2EA	0x60
 
 unsigned long
-decompress_kernel(unsigned long load_addr, int num_words, unsigned long cksum)
+load_kernel(unsigned long load_addr, int num_words, unsigned long cksum)
 {
 #ifdef INTERACTIVE_CONSOLE
 	int timer = 0;
diff -urN linux-2.4.21-pre5/arch/ppc/config.in linux-2.4.21-pre6/arch/ppc/config.in
--- linux-2.4.21-pre5/arch/ppc/config.in	Wed Mar 26 16:11:27 2003
+++ linux-2.4.21-pre6/arch/ppc/config.in	Wed Mar 26 16:11:33 2003
@@ -441,11 +441,18 @@
   fi
   bool '  Include xmon kernel debugger' CONFIG_XMON
   bool '  Include BDI-2000 user context switcher' CONFIG_BDI_SWITCH
+  # CONFIG_KGDB is much more useful with -g enbaled.  This makes the
+  # following look a little odd.
   if [ "$CONFIG_KGDB" = "y" -o "$CONFIG_XMON" = "y" \
 	-o "$CONFIG_BDI_SWITCH" = "y" ]; then
     bool '  Add any additional compile options' CONFIG_MORE_COMPILE_OPTIONS
     if [ "$CONFIG_MORE_COMPILE_OPTIONS" = "y" ]; then
       string '    Additional compile arguments' CONFIG_COMPILE_OPTIONS "-g -ggdb"
+    else
+      if [ "$CONFIG_KGDB" = "y" ]; then
+        define_bool CONFIG_MORE_COMPILE_OPTIONS y
+	define_string CONFIG_COMPILE_OPTIONS "-g"
+      fi
     fi
   fi
 fi
diff -urN linux-2.4.21-pre5/arch/ppc/kernel/head_4xx.S linux-2.4.21-pre6/arch/ppc/kernel/head_4xx.S
--- linux-2.4.21-pre5/arch/ppc/kernel/head_4xx.S	Wed Mar 26 16:11:27 2003
+++ linux-2.4.21-pre6/arch/ppc/kernel/head_4xx.S	Wed Mar 26 16:11:33 2003
@@ -67,6 +67,14 @@
 	.text
 _GLOBAL(_stext)
 _GLOBAL(_start)
+
+	/* To accomodate some SMP systems that overwrite the first few
+	 * locations before cpu 0 starts, the bootloader starts us at 0xc.
+	 */
+	nop
+	nop
+	nop
+
 	## Save residual data, init RAM disk, and command line parameters
 	
 	mr	r31,r3
diff -urN linux-2.4.21-pre5/arch/ppc/kernel/head_8xx.S linux-2.4.21-pre6/arch/ppc/kernel/head_8xx.S
--- linux-2.4.21-pre5/arch/ppc/kernel/head_8xx.S	Wed Mar 26 16:11:27 2003
+++ linux-2.4.21-pre6/arch/ppc/kernel/head_8xx.S	Wed Mar 26 16:11:33 2003
@@ -79,6 +79,12 @@
 
 	.globl	__start
 __start:
+	/* To accomodate some SMP systems that overwrite the first few
+	 * locations before cpu 0 starts, the bootloader starts us at 0xc.
+	 */
+	nop
+	nop
+	nop
 	mr	r31,r3			/* save parameters */
 	mr	r30,r4
 	mr	r29,r5
diff -urN linux-2.4.21-pre5/arch/ppc/kernel/m8xx_setup.c linux-2.4.21-pre6/arch/ppc/kernel/m8xx_setup.c
--- linux-2.4.21-pre5/arch/ppc/kernel/m8xx_setup.c	Wed Mar 26 16:11:27 2003
+++ linux-2.4.21-pre6/arch/ppc/kernel/m8xx_setup.c	Wed Mar 26 16:11:33 2003
@@ -63,9 +63,9 @@
 m8xx_setup_arch(void)
 {
 	int	cpm_page;
-	
+
 	cpm_page = (int) alloc_bootmem_pages(PAGE_SIZE);
-	
+
 	/* Reset the Communication Processor Module.
 	*/
 	m8xx_cpm_reset(cpm_page);
@@ -73,10 +73,10 @@
 #ifdef notdef
 	ROOT_DEV = to_kdev_t(0x0301); /* hda1 */
 #endif
-	
+
 #ifdef CONFIG_BLK_DEV_INITRD
 #if 0
-	ROOT_DEV = to_kdev_t(0x0200); /* floppy */  
+	ROOT_DEV = to_kdev_t(0x0200); /* floppy */
 	rd_prompt = 1;
 	rd_doload = 1;
 	rd_image_start = 0;
@@ -106,6 +106,9 @@
 	xmon(0);
 #endif
 	machine_restart(NULL);
+
+	/* not reached */
+	for (;;);
 }
 
 /* A place holder for time base interrupts, if they are ever enabled. */
@@ -219,7 +222,7 @@
 	__asm__("mtmsr %0" : : "r" (msr) );
 
 	dummy = ((immap_t *)IMAP_ADDR)->im_clkrst.res[0];
-	printk("Restart failed\n"); 
+	printk("Restart failed\n");
 	while(1);
 }
 
@@ -242,9 +245,9 @@
 	bd_t	*bp;
 
 	bp = (bd_t *)__res;
-			
-	seq_printf(m, "clock\t\t: %ldMHz\n"
-		   "bus clock\t: %ldMHz\n",
+
+	seq_printf(m, "clock\t\t: %dMHz\n"
+		   "bus clock\t: %dMHz\n",
 		   bp->bi_intfreq / 1000000,
 		   bp->bi_busfreq / 1000000);
 
@@ -301,7 +304,7 @@
 {
 	bd_t	*binfo;
 	extern unsigned char __res[];
-	
+
 	binfo = (bd_t *)__res;
 
 	return binfo->bi_memsize;
@@ -352,7 +355,7 @@
 
 	if ( r3 )
 		memcpy( (void *)__res,(void *)(r3+KERNELBASE), sizeof(bd_t) );
-	
+
 #ifdef CONFIG_PCI
 	m8xx_setup_pci_ptrs();
 #endif
@@ -367,7 +370,7 @@
 #endif /* CONFIG_BLK_DEV_INITRD */
 	/* take care of cmd line */
 	if ( r6 )
-	{	
+	{
 		*(char *)(r7+KERNELBASE) = 0;
 		strcpy(cmd_line, (char *)(r6+KERNELBASE));
 	}
@@ -401,5 +404,5 @@
 
 #if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE)
 	m8xx_ide_init();
-#endif		
+#endif
 }
diff -urN linux-2.4.21-pre5/arch/ppc/kernel/misc.S linux-2.4.21-pre6/arch/ppc/kernel/misc.S
--- linux-2.4.21-pre5/arch/ppc/kernel/misc.S	Wed Mar 26 16:11:27 2003
+++ linux-2.4.21-pre6/arch/ppc/kernel/misc.S	Wed Mar 26 16:11:33 2003
@@ -899,9 +899,9 @@
 
 /*
  * Create a kernel thread
- *   kernel_thread(fn, arg, flags)
+ *   arch_kernel_thread(fn, arg, flags)
  */
-_GLOBAL(kernel_thread)
+_GLOBAL(arch_kernel_thread)
 	mr	r6,r3		/* function */
 	ori	r3,r5,CLONE_VM	/* flags */
 	li	r0,__NR_clone
diff -urN linux-2.4.21-pre5/arch/ppc/kernel/ppc-stub.c linux-2.4.21-pre6/arch/ppc/kernel/ppc-stub.c
--- linux-2.4.21-pre5/arch/ppc/kernel/ppc-stub.c	Wed Mar 26 16:11:27 2003
+++ linux-2.4.21-pre6/arch/ppc/kernel/ppc-stub.c	Wed Mar 26 16:11:33 2003
@@ -347,18 +347,6 @@
 	flush_instruction_cache();
 }
 
-static inline int get_msr(void)
-{
-	int msr;
-	asm volatile("mfmsr %0" : "=r" (msr):);
-	return msr;
-}
-
-static inline void set_msr(int msr)
-{
-	asm volatile("mtmsr %0" : : "r" (msr));
-}
-
 /* Set up exception handlers for tracing and breakpoints
  * [could be called kgdb_init()]
  */
@@ -498,8 +486,8 @@
 
 	kgdb_interruptible(0);
 	lock_kernel();
-	msr = get_msr();
-	set_msr(msr & ~MSR_EE);	/* disable interrupts */
+	msr = mfmsr();
+	mtmsr(msr & ~MSR_EE);	/* disable interrupts */
 
 	if (regs->nip == (unsigned long)breakinst) {
 		/* Skip over breakpoint trap insn */
@@ -521,7 +509,7 @@
 	*ptr++ = hexchars[PC_REGNUM >> 4];
 	*ptr++ = hexchars[PC_REGNUM & 0xf];
 	*ptr++ = ':';
-	ptr = mem2hex((char *)&regs->nip, ptr, 4);
+	ptr = mem2hex((char *)regs->nip, ptr, 4);
 	*ptr++ = ';';
 	*ptr++ = hexchars[SP_REGNUM >> 4];
 	*ptr++ = hexchars[SP_REGNUM & 0xf];
@@ -685,7 +673,7 @@
  * some location may have changed something that is in the instruction cache.
  */
 			kgdb_flush_cache_all();
-			set_msr(msr);
+			mtmsr(msr);
 			kgdb_interruptible(1);
 			unlock_kernel();
 			kgdb_active = 0;
@@ -694,9 +682,6 @@
 		case 's':
 			kgdb_flush_cache_all();
 			regs->msr |= MSR_SE;
-#if 0
-			set_msr(msr | MSR_SE);
-#endif
 			unlock_kernel();
 			kgdb_active = 0;
 			return;
diff -urN linux-2.4.21-pre5/arch/ppc/kernel/ppc_ksyms.c linux-2.4.21-pre6/arch/ppc/kernel/ppc_ksyms.c
--- linux-2.4.21-pre5/arch/ppc/kernel/ppc_ksyms.c	Wed Mar 26 16:11:27 2003
+++ linux-2.4.21-pre6/arch/ppc/kernel/ppc_ksyms.c	Wed Mar 26 16:11:33 2003
@@ -340,6 +340,7 @@
 EXPORT_SYMBOL(__res);
 EXPORT_SYMBOL(cpm_install_handler);
 EXPORT_SYMBOL(cpm_free_handler);
+EXPORT_SYMBOL(m8xx_cpm_hostalloc);
 #endif /* CONFIG_8xx */
 
 /* Those should really be inline */
diff -urN linux-2.4.21-pre5/arch/ppc/kernel/setup.c linux-2.4.21-pre6/arch/ppc/kernel/setup.c
--- linux-2.4.21-pre5/arch/ppc/kernel/setup.c	Wed Mar 26 16:11:27 2003
+++ linux-2.4.21-pre6/arch/ppc/kernel/setup.c	Wed Mar 26 16:11:33 2003
@@ -476,6 +476,12 @@
 		case BI_MEMSIZE:
 			boot_mem_size = data[0];
 			break;
+		case BI_BOARD_INFO:
+			/* data is typically a bd_t */
+			if (ppc_md.board_info)
+				ppc_md.board_info((void *)data,
+					rec->size - sizeof(struct bi_record));
+			break;
 		}
 		rec = (struct bi_record *)((ulong)rec + rec->size);
 	}
diff -urN linux-2.4.21-pre5/arch/ppc/kernel/softemu8xx.c linux-2.4.21-pre6/arch/ppc/kernel/softemu8xx.c
--- linux-2.4.21-pre5/arch/ppc/kernel/softemu8xx.c	Wed Mar 26 16:11:27 2003
+++ linux-2.4.21-pre6/arch/ppc/kernel/softemu8xx.c	Wed Mar 26 16:11:33 2003
@@ -32,6 +32,11 @@
 #include <asm/io.h>
 #include <asm/processor.h>
 
+extern void
+print_8xx_pte(struct mm_struct *mm, unsigned long addr);
+extern int
+get_8xx_pte(struct mm_struct *mm, unsigned long addr);
+
 /* Eventually we may need a look-up table, but this works for now.
 */
 #define LFS	48
@@ -114,7 +119,7 @@
 	default:
 		retval = 1;
 		printk("Bad emulation %s/%d\n"
-		       " NIP: %08x instruction: %08x opcode: %x "
+		       " NIP: %08lx instruction: %08x opcode: %x "
 		       "A: %x B: %x C: %x code: %x rc: %x\n",
 		       current->comm,current->pid,
 		       regs->nip,
@@ -125,13 +130,12 @@
 		       (instword>>1)&0x3ff,
 		       instword&1);
 		{
-			int pa;
+			unsigned long pa;
 			print_8xx_pte(current->mm,regs->nip);
-			pa = get_8xx_pte(current->mm,regs->nip) & PAGE_MASK;
-			pa |= (regs->nip & ~PAGE_MASK);
-			pa = __va(pa);
-			printk("Kernel VA for NIP %x ", pa);
-			print_8xx_pte(current->mm,pa);
+			pa = (get_8xx_pte(current->mm,regs->nip) & PAGE_MASK)
+				| (regs->nip & ~PAGE_MASK);
+			printk("Kernel VA for NIP 0x%p ", __va(pa));
+			print_8xx_pte(current->mm, (unsigned long)__va(pa));
 		}
 		
 	}
diff -urN linux-2.4.21-pre5/arch/ppc/lib/Makefile linux-2.4.21-pre6/arch/ppc/lib/Makefile
--- linux-2.4.21-pre5/arch/ppc/lib/Makefile	Wed Mar 26 16:11:27 2003
+++ linux-2.4.21-pre6/arch/ppc/lib/Makefile	Wed Mar 26 16:11:33 2003
@@ -8,7 +8,7 @@
 
 export-objs		:= dec_and_lock.o
 
-obj-y			:= checksum.o string.o strcase.o dec_and_lock.o
+obj-y			:= checksum.o string.o strcase.o dec_and_lock.o div64.o
 
 obj-$(CONFIG_SMP)	+= locks.o
 
diff -urN linux-2.4.21-pre5/arch/ppc/lib/div64.S linux-2.4.21-pre6/arch/ppc/lib/div64.S
--- linux-2.4.21-pre5/arch/ppc/lib/div64.S	Wed Dec 31 16:00:00 1969
+++ linux-2.4.21-pre6/arch/ppc/lib/div64.S	Wed Mar 26 16:11:33 2003
@@ -0,0 +1,58 @@
+/*
+ * Divide a 64-bit unsigned number by a 32-bit unsigned number.
+ * This routine assumes that the top 32 bits of the dividend are
+ * non-zero to start with.
+ * On entry, r3 points to the dividend, which get overwritten with
+ * the 64-bit quotient, and r4 contains the divisor.
+ * On exit, r3 contains the remainder.
+ *
+ * Copyright (C) 2002 Paul Mackerras, IBM Corp.
+ *
+ * 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 <asm/ppc_asm.h>
+#include <asm/processor.h>
+
+_GLOBAL(__div64_32)
+	lwz	r5,0(r3)	# get the dividend into r5/r6
+	lwz	r6,4(r3)
+	cmplw	r5,r4
+	li	r7,0
+	li	r8,0
+	blt	1f
+	divwu	r7,r5,r4	# if dividend.hi >= divisor,
+	mullw	r0,r7,r4	# quotient.hi = dividend.hi / divisor
+	subf.	r5,r0,r5	# dividend.hi %= divisor
+	beq	3f
+1:	mr	r11,r5		# here dividend.hi != 0
+	andis.	r0,r5,0xc000
+	bne	2f
+	cntlzw	r0,r5		# we are shifting the dividend right
+	li	r10,-1		# to make it < 2^32, and shifting
+	srw	r10,r10,r0	# the divisor right the same amount,
+	add	r9,r4,r10	# rounding up (so the estimate cannot
+	andc	r11,r6,r10	# ever be too large, only too small)
+	andc	r9,r9,r10
+	or	r11,r5,r11
+	rotlw	r9,r9,r0
+	rotlw	r11,r11,r0
+	divwu	r11,r11,r9	# then we divide the shifted quantities
+2:	mullw	r10,r11,r4	# to get an estimate of the quotient,
+	mulhwu	r9,r11,r4	# multiply the estimate by the divisor,
+	subfc	r6,r10,r6	# take the product from the divisor,
+	add	r8,r8,r11	# and add the estimate to the accumulated
+	subfe.	r5,r9,r5	# quotient
+	bne	1b
+3:	cmplw	r6,r4
+	blt	4f
+	divwu	r0,r6,r4	# perform the remaining 32-bit division
+	mullw	r10,r0,r4	# and get the remainder
+	add	r8,r8,r0
+	subf	r6,r10,r6
+4:	stw	r7,0(r3)	# return the quotient in *r3
+	stw	r8,4(r3)
+	mr	r3,r6		# return the remainder in r3
+	blr
diff -urN linux-2.4.21-pre5/arch/ppc64/kernel/misc.S linux-2.4.21-pre6/arch/ppc64/kernel/misc.S
--- linux-2.4.21-pre5/arch/ppc64/kernel/misc.S	Wed Mar 26 16:11:27 2003
+++ linux-2.4.21-pre6/arch/ppc64/kernel/misc.S	Wed Mar 26 16:11:34 2003
@@ -481,9 +481,9 @@
 
 /*
  * Create a kernel thread
- *   kernel_thread(fn, arg, flags)
+ *   arch_kernel_thread(fn, arg, flags)
  */
-_GLOBAL(kernel_thread)
+_GLOBAL(arch_kernel_thread)
 	mr	r6,r3		/* function */
 	ori	r3,r5,CLONE_VM	/* flags */
 	li	r0,__NR_clone
diff -urN linux-2.4.21-pre5/arch/s390/kernel/process.c linux-2.4.21-pre6/arch/s390/kernel/process.c
--- linux-2.4.21-pre5/arch/s390/kernel/process.c	Wed Mar 26 16:11:27 2003
+++ linux-2.4.21-pre6/arch/s390/kernel/process.c	Wed Mar 26 16:11:34 2003
@@ -105,7 +105,7 @@
 		show_trace((unsigned long *) regs->gprs[15]);
 }
 
-int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags)
+int arch_kernel_thread(int (*fn)(void *), void * arg, unsigned long flags)
 {
         int clone_arg = flags | CLONE_VM;
         int retval;
diff -urN linux-2.4.21-pre5/arch/s390x/kernel/process.c linux-2.4.21-pre6/arch/s390x/kernel/process.c
--- linux-2.4.21-pre5/arch/s390x/kernel/process.c	Wed Mar 26 16:11:27 2003
+++ linux-2.4.21-pre6/arch/s390x/kernel/process.c	Wed Mar 26 16:11:34 2003
@@ -102,7 +102,7 @@
 		show_trace((unsigned long *) regs->gprs[15]);
 }
 
-int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags)
+int arch_kernel_thread(int (*fn)(void *), void * arg, unsigned long flags)
 {
         int clone_arg = flags | CLONE_VM;
         int retval;
diff -urN linux-2.4.21-pre5/arch/sh/kernel/process.c linux-2.4.21-pre6/arch/sh/kernel/process.c
--- linux-2.4.21-pre5/arch/sh/kernel/process.c	Mon Oct 15 13:36:48 2001
+++ linux-2.4.21-pre6/arch/sh/kernel/process.c	Wed Mar 26 16:11:34 2003
@@ -118,7 +118,7 @@
  * This is the mechanism for creating a new kernel thread.
  *
  */
-int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags)
+int arch_kernel_thread(int (*fn)(void *), void * arg, unsigned long flags)
 {	/* Don't use this in BL=1(cli).  Or else, CPU resets! */
 	register unsigned long __sc0 __asm__ ("r0");
 	register unsigned long __sc3 __asm__ ("r3") = __NR_clone;
diff -urN linux-2.4.21-pre5/arch/sparc/kernel/process.c linux-2.4.21-pre6/arch/sparc/kernel/process.c
--- linux-2.4.21-pre5/arch/sparc/kernel/process.c	Fri Aug  2 17:39:43 2002
+++ linux-2.4.21-pre6/arch/sparc/kernel/process.c	Wed Mar 26 16:11:34 2003
@@ -676,7 +676,7 @@
  * a system call from a "real" process, but the process memory space will
  * not be free'd until both the parent and the child have exited.
  */
-pid_t kernel_thread(int (*fn)(void *), void * arg, unsigned long flags)
+pid_t arch_kernel_thread(int (*fn)(void *), void * arg, unsigned long flags)
 {
 	long retval;
 
diff -urN linux-2.4.21-pre5/arch/sparc/kernel/systbls.S linux-2.4.21-pre6/arch/sparc/kernel/systbls.S
--- linux-2.4.21-pre5/arch/sparc/kernel/systbls.S	Fri Aug  2 17:39:43 2002
+++ linux-2.4.21-pre6/arch/sparc/kernel/systbls.S	Wed Mar 26 16:11:34 2003
@@ -51,11 +51,11 @@
 /*150*/	.long sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_poll, sys_getdents64
 /*155*/	.long sys_fcntl64, sys_nis_syscall, sys_statfs, sys_fstatfs, sys_oldumount
 /*160*/	.long sys_nis_syscall, sys_nis_syscall, sys_getdomainname, sys_setdomainname, sys_nis_syscall
-/*165*/	.long sys_quotactl, sys_nis_syscall, sys_mount, sys_ustat, sys_nis_syscall
-/*170*/	.long sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_getdents
-/*175*/	.long sys_setsid, sys_fchdir, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall
-/*180*/	.long sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_sigpending, sys_query_module
-/*185*/	.long sys_setpgid, sys_nis_syscall, sys_tkill, sys_nis_syscall, sys_newuname
+/*165*/	.long sys_quotactl, sys_nis_syscall, sys_mount, sys_ustat, sys_setxattr
+/*170*/	.long sys_lsetxattr, sys_fsetxattr, sys_getxattr, sys_lgetxattr, sys_getdents
+/*175*/	.long sys_setsid, sys_fchdir, sys_fgetxattr, sys_listxattr, sys_llistxattr
+/*180*/	.long sys_flistxattr, sys_removexattr, sys_lremovexattr, sys_sigpending, sys_query_module
+/*185*/	.long sys_setpgid, sys_fremovexattr, sys_tkill, sys_nis_syscall, sys_newuname
 /*190*/	.long sys_init_module, sys_personality, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall
 /*195*/	.long sys_nis_syscall, sys_nis_syscall, sys_getppid, sparc_sigaction, sys_sgetmask
 /*200*/	.long sys_ssetmask, sys_sigsuspend, sys_newlstat, sys_uselib, old_readdir
diff -urN linux-2.4.21-pre5/arch/sparc/mm/fault.c linux-2.4.21-pre6/arch/sparc/mm/fault.c
--- linux-2.4.21-pre5/arch/sparc/mm/fault.c	Fri Dec 21 09:41:53 2001
+++ linux-2.4.21-pre6/arch/sparc/mm/fault.c	Wed Mar 26 16:11:34 2003
@@ -139,7 +139,7 @@
 {
 	if((unsigned long) address < PAGE_SIZE) {
 		printk(KERN_ALERT "Unable to handle kernel NULL "
-		       "pointer dereference");
+		       "pointer dereference\n");
 	} else {
 		printk(KERN_ALERT "Unable to handle kernel paging request "
 		       "at virtual address %08lx\n", address);
diff -urN linux-2.4.21-pre5/arch/sparc64/defconfig linux-2.4.21-pre6/arch/sparc64/defconfig
--- linux-2.4.21-pre5/arch/sparc64/defconfig	Wed Mar 26 16:11:28 2003
+++ linux-2.4.21-pre6/arch/sparc64/defconfig	Wed Mar 26 16:11:34 2003
@@ -275,7 +275,6 @@
 CONFIG_BLK_DEV_IDEFLOPPY=m
 # CONFIG_BLK_DEV_IDESCSI is not set
 # CONFIG_IDE_TASK_IOCTL is not set
-CONFIG_IDE_TASKFILE_IO=y
 
 #
 # IDE chipset support/bugfixes
@@ -300,6 +299,7 @@
 CONFIG_BLK_DEV_AMD74XX=m
 # CONFIG_AMD74XX_OVERRIDE is not set
 CONFIG_BLK_DEV_CMD64X=y
+# CONFIG_BLK_DEV_TRIFLEX is not set
 CONFIG_BLK_DEV_CY82C693=m
 CONFIG_BLK_DEV_CS5530=m
 CONFIG_BLK_DEV_HPT34X=m
@@ -330,6 +330,7 @@
 CONFIG_BLK_DEV_ATARAID=m
 CONFIG_BLK_DEV_ATARAID_PDC=m
 CONFIG_BLK_DEV_ATARAID_HPT=m
+# CONFIG_BLK_DEV_ATARAID_SII is not set
 
 #
 # SCSI support
@@ -462,6 +463,7 @@
 # CONFIG_ELMC is not set
 # CONFIG_ELMC_II is not set
 CONFIG_VORTEX=m
+CONFIG_TYPHOON=m
 # CONFIG_LANCE is not set
 # CONFIG_NET_VENDOR_SMC is not set
 # CONFIG_NET_VENDOR_RACAL is not set
@@ -469,6 +471,7 @@
 # CONFIG_NET_ISA is not set
 CONFIG_NET_PCI=y
 CONFIG_PCNET32=m
+# CONFIG_AMD8111_ETH is not set
 CONFIG_ADAPTEC_STARFIRE=m
 # CONFIG_APRICOT is not set
 # CONFIG_CS89x0 is not set
@@ -479,6 +482,7 @@
 CONFIG_DGRS=m
 # CONFIG_DM9102 is not set
 CONFIG_EEPRO100=m
+# CONFIG_EEPRO100_PIO is not set
 CONFIG_E100=m
 # CONFIG_LNE390 is not set
 CONFIG_FEALNX=m
@@ -514,6 +518,7 @@
 CONFIG_NS83820=m
 # CONFIG_HAMACHI is not set
 CONFIG_YELLOWFIN=m
+# CONFIG_R8169 is not set
 CONFIG_SK98LIN=m
 CONFIG_TIGON3=m
 CONFIG_FDDI=y
@@ -756,7 +761,6 @@
 #
 CONFIG_USB_DEVICEFS=y
 # CONFIG_USB_BANDWIDTH is not set
-# CONFIG_USB_LONG_TIMEOUT is not set
 
 #
 # USB Host Controller Drivers
@@ -771,7 +775,10 @@
 #
 # CONFIG_USB_AUDIO is not set
 # CONFIG_USB_EMI26 is not set
-CONFIG_USB_BLUETOOTH=m
+
+#
+#   USB Bluetooth can only be used with disabled Bluetooth subsystem
+#
 CONFIG_USB_MIDI=m
 CONFIG_USB_STORAGE=m
 # CONFIG_USB_STORAGE_DEBUG is not set
@@ -794,6 +801,7 @@
 # CONFIG_USB_HIDDEV is not set
 CONFIG_USB_AIPTEK=m
 CONFIG_USB_WACOM=m
+CONFIG_USB_POWERMATE=m
 
 #
 # USB Imaging devices
@@ -808,6 +816,7 @@
 # USB Multimedia devices
 #
 CONFIG_USB_IBMCAM=m
+CONFIG_USB_KONICAWC=m
 CONFIG_USB_OV511=m
 CONFIG_USB_PWC=m
 CONFIG_USB_SE401=m
@@ -861,6 +870,7 @@
 # CONFIG_USB_SERIAL_KEYSPAN_USA49W is not set
 CONFIG_USB_SERIAL_MCT_U232=m
 CONFIG_USB_SERIAL_KLSI=m
+CONFIG_USB_SERIAL_KOBIL_SCT=m
 CONFIG_USB_SERIAL_PL2303=m
 CONFIG_USB_SERIAL_CYBERJACK=m
 CONFIG_USB_SERIAL_XIRCOM=m
@@ -881,9 +891,11 @@
 CONFIG_BLUEZ=m
 CONFIG_BLUEZ_L2CAP=m
 CONFIG_BLUEZ_SCO=m
+CONFIG_BLUEZ_RFCOMM=m
+CONFIG_BLUEZ_RFCOMM_TTY=y
 CONFIG_BLUEZ_BNEP=m
-CONFIG_BNEP_MC_FILTER=y
-CONFIG_BNEP_PROTO_FILTER=y
+CONFIG_BLUEZ_BNEP_MC_FILTER=y
+CONFIG_BLUEZ_BNEP_PROTO_FILTER=y
 
 #
 # Bluetooth device drivers
@@ -892,9 +904,12 @@
 CONFIG_BLUEZ_USB_ZERO_PACKET=y
 CONFIG_BLUEZ_HCIUART=m
 CONFIG_BLUEZ_HCIUART_H4=y
+CONFIG_BLUEZ_HCIUART_BCSP=y
+CONFIG_BLUEZ_HCIUART_BCSP_TXCRC=y
 # CONFIG_BLUEZ_HCIDTL1 is not set
 # CONFIG_BLUEZ_HCIBT3C is not set
 # CONFIG_BLUEZ_HCIBLUECARD is not set
+# CONFIG_BLUEZ_HCIBTUART is not set
 CONFIG_BLUEZ_HCIVHCI=m
 
 #
diff -urN linux-2.4.21-pre5/arch/sparc64/kernel/entry.S linux-2.4.21-pre6/arch/sparc64/kernel/entry.S
--- linux-2.4.21-pre5/arch/sparc64/kernel/entry.S	Thu Nov 28 15:53:12 2002
+++ linux-2.4.21-pre6/arch/sparc64/kernel/entry.S	Wed Mar 26 16:11:34 2003
@@ -1843,7 +1843,7 @@
 	sub	%o1, %g7, %o1
 	mulx	%o3, %o1, %o1
 	brz,pt	%o2, 1f
-	 srlx	%o1, 32, %o1
+	 srlx	%o1, 30, %o1
 	sethi	%hi(10000), %g2
 	or	%g2, %lo(10000), %g2
 	add	%o1, %g2, %o1
diff -urN linux-2.4.21-pre5/arch/sparc64/kernel/head.S linux-2.4.21-pre6/arch/sparc64/kernel/head.S
--- linux-2.4.21-pre5/arch/sparc64/kernel/head.S	Thu Nov 28 15:53:12 2002
+++ linux-2.4.21-pre6/arch/sparc64/kernel/head.S	Wed Mar 26 16:11:34 2003
@@ -673,7 +673,9 @@
 #endif
 
 	/* Kill PROM timer */
-	wr	%g0, 0, %tick_cmpr
+	sethi	%hi(0x80000000), %g1
+	sllx	%g1, 32, %g1
+	wr	%g1, 0, %tick_cmpr
 
 	BRANCH_IF_ANY_CHEETAH(g1,g5,1f)
 
diff -urN linux-2.4.21-pre5/arch/sparc64/kernel/ioctl32.c linux-2.4.21-pre6/arch/sparc64/kernel/ioctl32.c
--- linux-2.4.21-pre5/arch/sparc64/kernel/ioctl32.c	Wed Mar 26 16:11:28 2003
+++ linux-2.4.21-pre6/arch/sparc64/kernel/ioctl32.c	Wed Mar 26 16:11:34 2003
@@ -37,6 +37,7 @@
 #include <linux/cdrom.h>
 #include <linux/loop.h>
 #include <linux/auto_fs.h>
+#include <linux/auto_fs4.h>
 #include <linux/devfs_fs.h>
 #include <linux/tty.h>
 #include <linux/vt_kern.h>
@@ -3219,6 +3220,175 @@
 }
 #endif
 
+#if defined(CONFIG_IEEE1394) || defined(CONFIG_IEEE1394_MODULE)
+#include "../../../drivers/ieee1394/ieee1394-ioctl.h"
+#include "../../../drivers/ieee1394/amdtp.h"
+#include "../../../drivers/ieee1394/dv1394.h"
+#include "../../../drivers/ieee1394/video1394.h"
+
+#define DV1394_IOC32_INIT	_IOW('#', 0x06, struct dv1394_init32)
+#define DV1394_IOC32_GET_STATUS	_IOR('#', 0x0c, struct dv1394_status32)
+
+struct dv1394_init32 {
+	u32 api_version;
+	u32 channel;
+	u32 n_frames;
+	u32 format;
+	u32 cip_n;
+	u32 cip_d;
+	u32 syt_offset;
+};
+
+struct dv1394_status32 {
+	struct dv1394_init32 init;
+	s32 active_frame;
+	u32 first_clear_frame;
+	u32 n_clear_frames;
+	u32 dropped_frames;
+};
+
+static int handle_dv1394_init(unsigned int fd, unsigned int cmd, unsigned long arg)
+{
+	struct dv1394_init32 dv32;
+	struct dv1394_init dv;
+	mm_segment_t old_fs;
+	int ret;
+
+	if (copy_from_user(&dv32, (void *)arg, sizeof(dv32)))
+		return -EFAULT;
+
+	dv.api_version = dv32.api_version;
+	dv.channel = dv32.channel;
+	dv.n_frames = dv32.n_frames;
+	dv.format = dv32.format;
+	dv.cip_n = (unsigned long)dv32.cip_n;
+	dv.cip_d = (unsigned long)dv32.cip_d;
+	dv.syt_offset = dv32.syt_offset;
+
+        old_fs = get_fs();
+	set_fs(KERNEL_DS);
+	ret = sys_ioctl(fd, DV1394_IOC_INIT, (unsigned long) &dv);
+	set_fs(old_fs);
+
+	return ret;
+}
+
+static int handle_dv1394_get_status(unsigned int fd, unsigned int cmd, unsigned long arg)
+{
+	struct dv1394_status32 dv32;
+	struct dv1394_status dv;
+	mm_segment_t old_fs;
+	int ret;
+
+	old_fs = get_fs();
+	set_fs(KERNEL_DS);
+	ret = sys_ioctl(fd, DV1394_IOC_GET_STATUS, (unsigned long) &dv);
+	set_fs(old_fs);
+
+	if (!ret) {
+		dv32.init.api_version = dv.init.api_version;
+		dv32.init.channel = dv.init.channel;
+		dv32.init.n_frames = dv.init.n_frames;
+		dv32.init.format = dv.init.format;
+		dv32.init.cip_n = (u32)dv.init.cip_n;
+		dv32.init.cip_d = (u32)dv.init.cip_d;
+		dv32.init.syt_offset = dv.init.syt_offset;
+		dv32.active_frame = dv.active_frame;
+		dv32.first_clear_frame = dv.first_clear_frame;
+		dv32.n_clear_frames = dv.n_clear_frames;
+		dv32.dropped_frames = dv.dropped_frames;
+
+		if (copy_to_user((struct dv1394_status32 *)arg, &dv32, sizeof(dv32)))
+			ret = -EFAULT;
+	}
+
+	return ret;
+}
+
+#define VIDEO1394_IOC32_LISTEN_QUEUE_BUFFER	\
+	_IOW ('#', 0x12, struct video1394_wait32)
+#define VIDEO1394_IOC32_LISTEN_WAIT_BUFFER	\
+	_IOWR('#', 0x13, struct video1394_wait32)
+#define VIDEO1394_IOC32_TALK_WAIT_BUFFER	\
+        _IOW ('#', 0x17, struct video1394_wait32)
+#define VIDEO1394_IOC32_LISTEN_POLL_BUFFER	\
+        _IOWR('#', 0x18, struct video1394_wait32)
+
+struct video1394_wait32 {
+	u32 channel;
+	u32 buffer;
+        struct timeval32 filltime;
+};
+
+static int video1394_wr_wait32(unsigned int fd, unsigned int cmd, unsigned long arg)
+{
+	struct video1394_wait32 wait32;
+	struct video1394_wait wait;
+	mm_segment_t old_fs;
+	int ret;
+
+	if (copy_from_user(&wait32, (void *)arg, sizeof(wait32)))
+		return -EFAULT;
+
+	wait.channel = wait32.channel;
+	wait.buffer = wait32.buffer;
+	wait.filltime.tv_sec = (time_t)wait32.filltime.tv_sec;
+	wait.filltime.tv_usec = (suseconds_t)wait32.filltime.tv_usec;
+
+	old_fs = get_fs();
+	set_fs(KERNEL_DS);
+	if (cmd == VIDEO1394_IOC32_LISTEN_WAIT_BUFFER)
+		ret = sys_ioctl(fd, VIDEO1394_IOC_LISTEN_WAIT_BUFFER, (unsigned long) &wait);
+	else
+		ret = sys_ioctl(fd, VIDEO1394_IOC_LISTEN_POLL_BUFFER, (unsigned long) &wait);
+	set_fs(old_fs);
+
+	if (!ret) {
+		wait32.channel = wait.channel;
+		wait32.buffer = wait.buffer;
+		wait32.filltime.tv_sec = (int)wait.filltime.tv_sec;
+		wait32.filltime.tv_usec = (int)wait.filltime.tv_usec;
+
+		if (copy_to_user((struct video1394_wait32 *)arg, &wait32, sizeof(wait32)))
+			ret = -EFAULT;
+	}
+
+	return ret;
+}
+
+static int video1394_w_wait32(unsigned int fd, unsigned int cmd, unsigned long arg)
+{
+	struct video1394_wait32 wait32;
+	struct video1394_wait wait;
+	mm_segment_t old_fs;
+	int ret;
+
+	if (copy_from_user(&wait32, (void *)arg, sizeof(wait32)))
+		return -EFAULT;
+
+	wait.channel = wait32.channel;
+	wait.buffer = wait32.buffer;
+	wait.filltime.tv_sec = (time_t)wait32.filltime.tv_sec;
+	wait.filltime.tv_usec = (suseconds_t)wait32.filltime.tv_usec;
+
+	old_fs = get_fs();
+	set_fs(KERNEL_DS);
+	if (cmd == VIDEO1394_IOC32_LISTEN_QUEUE_BUFFER)
+		ret = sys_ioctl(fd, VIDEO1394_IOC_LISTEN_QUEUE_BUFFER, (unsigned long) &wait);
+	else
+		ret = sys_ioctl(fd, VIDEO1394_IOC_TALK_WAIT_BUFFER, (unsigned long) &wait);
+	set_fs(old_fs);
+
+	return ret;
+}
+
+static int video1394_queue_buf32(unsigned int fd, unsigned int cmd, unsigned long arg)
+{
+	return -EFAULT;
+}
+
+#endif /* CONFIG_IEEE1394 */
+
 #if defined(CONFIG_DRM_NEW) || defined(CONFIG_DRM_NEW_MODULE)
 /* This really belongs in include/linux/drm.h -DaveM */
 #include "../../../drivers/char/drm/drm.h"
@@ -4318,6 +4488,7 @@
 COMPATIBLE_IOCTL(TCSETAW)
 COMPATIBLE_IOCTL(TCSETAF)
 COMPATIBLE_IOCTL(TCSBRK)
+COMPATIBLE_IOCTL(TCSBRKP)
 COMPATIBLE_IOCTL(TCXONC)
 COMPATIBLE_IOCTL(TCFLSH)
 COMPATIBLE_IOCTL(TCGETS)
@@ -4881,6 +5052,7 @@
 COMPATIBLE_IOCTL(AUTOFS_IOC_CATATONIC)
 COMPATIBLE_IOCTL(AUTOFS_IOC_PROTOVER)
 COMPATIBLE_IOCTL(AUTOFS_IOC_EXPIRE)
+COMPATIBLE_IOCTL(AUTOFS_IOC_EXPIRE_MULTI)
 /* DEVFS */
 COMPATIBLE_IOCTL(DEVFSDIOC_GET_PROTO_REV)
 COMPATIBLE_IOCTL(DEVFSDIOC_SET_EVENT_MASK)
@@ -5049,7 +5221,33 @@
 COMPATIBLE_IOCTL(NBD_PRINT_DEBUG)
 COMPATIBLE_IOCTL(NBD_SET_SIZE_BLOCKS)
 COMPATIBLE_IOCTL(NBD_DISCONNECT)
+/* Linux-1394 */
+#if defined(CONFIG_IEEE1394) || defined(CONFIG_IEEE1394_MODULE)
+COMPATIBLE_IOCTL(AMDTP_IOC_CHANNEL)
+COMPATIBLE_IOCTL(AMDTP_IOC_PLUG)
+COMPATIBLE_IOCTL(AMDTP_IOC_PING)
+COMPATIBLE_IOCTL(AMDTP_IOC_ZAP)
+COMPATIBLE_IOCTL(DV1394_IOC_SHUTDOWN)
+COMPATIBLE_IOCTL(DV1394_IOC_SUBMIT_FRAMES)
+COMPATIBLE_IOCTL(DV1394_IOC_WAIT_FRAMES)
+COMPATIBLE_IOCTL(DV1394_IOC_RECEIVE_FRAMES)
+COMPATIBLE_IOCTL(DV1394_IOC_START_RECEIVE)
+COMPATIBLE_IOCTL(VIDEO1394_IOC_LISTEN_CHANNEL)
+COMPATIBLE_IOCTL(VIDEO1394_IOC_UNLISTEN_CHANNEL)
+COMPATIBLE_IOCTL(VIDEO1394_IOC_TALK_CHANNEL)
+COMPATIBLE_IOCTL(VIDEO1394_IOC_UNTALK_CHANNEL)
+#endif
+
 /* And these ioctls need translation */
+#if defined(CONFIG_IEEE1394) || defined(CONFIG_IEEE1394_MODULE)
+HANDLE_IOCTL(DV1394_IOC32_INIT, handle_dv1394_init)
+HANDLE_IOCTL(DV1394_IOC32_GET_STATUS, handle_dv1394_get_status)
+HANDLE_IOCTL(VIDEO1394_IOC32_LISTEN_QUEUE_BUFFER, video1394_w_wait32)
+HANDLE_IOCTL(VIDEO1394_IOC32_LISTEN_WAIT_BUFFER, video1394_wr_wait32)
+HANDLE_IOCTL(VIDEO1394_IOC_TALK_QUEUE_BUFFER, video1394_queue_buf32)
+HANDLE_IOCTL(VIDEO1394_IOC32_TALK_WAIT_BUFFER, video1394_w_wait32)
+HANDLE_IOCTL(VIDEO1394_IOC32_LISTEN_POLL_BUFFER, video1394_wr_wait32)
+#endif
 HANDLE_IOCTL(MEMREADOOB32, mtd_rw_oob)
 HANDLE_IOCTL(MEMWRITEOOB32, mtd_rw_oob)
 #ifdef CONFIG_NET
diff -urN linux-2.4.21-pre5/arch/sparc64/kernel/irq.c linux-2.4.21-pre6/arch/sparc64/kernel/irq.c
--- linux-2.4.21-pre5/arch/sparc64/kernel/irq.c	Thu Nov 28 15:53:12 2002
+++ linux-2.4.21-pre6/arch/sparc64/kernel/irq.c	Wed Mar 26 16:11:34 2003
@@ -803,12 +803,8 @@
 	 */
 	{
 		unsigned long clr_mask = 1 << irq;
-		unsigned long tick_mask;
+		unsigned long tick_mask = tick_ops->softint_mask;
 
-		if (SPARC64_USE_STICK)
-			tick_mask = (1UL << 16);
-		else
-			tick_mask = (1UL << 0);
 		if ((irq == 14) && (get_softint() & tick_mask)) {
 			irq = 0;
 			clr_mask = tick_mask;
@@ -1025,113 +1021,6 @@
 	return 0;
 }
 
-/* This is gets the master TICK_INT timer going. */
-void init_timers(void (*cfunc)(int, void *, struct pt_regs *),
-		 unsigned long *clock)
-{
-	unsigned long pstate;
-	extern unsigned long timer_tick_offset;
-	int node, err;
-#ifdef CONFIG_SMP
-	extern void smp_tick_init(void);
-#endif
-
-	if (!SPARC64_USE_STICK) {
-		node = linux_cpus[0].prom_node;
-		*clock = prom_getint(node, "clock-frequency");
-	} else {
-		node = prom_root_node;
-		*clock = prom_getint(node, "stick-frequency");
-	}
-	timer_tick_offset = *clock / HZ;
-#ifdef CONFIG_SMP
-	smp_tick_init();
-#endif
-
-	/* Register IRQ handler. */
-	err = request_irq(build_irq(0, 0, 0UL, 0UL), cfunc, SA_STATIC_ALLOC,
-			  "timer", NULL);
-
-	if(err) {
-		prom_printf("Serious problem, cannot register TICK_INT\n");
-		prom_halt();
-	}
-
-	/* Guarentee that the following sequences execute
-	 * uninterrupted.
-	 */
-	__asm__ __volatile__("rdpr	%%pstate, %0\n\t"
-			     "wrpr	%0, %1, %%pstate"
-			     : "=r" (pstate)
-			     : "i" (PSTATE_IE));
-
-	/* Set things up so user can access tick register for profiling
-	 * purposes.  Also workaround BB_ERRATA_1 by doing a dummy
-	 * read back of %tick after writing it.
-	 */
-	__asm__ __volatile__(
-	"	sethi	%%hi(0x80000000), %%g1\n"
-	"	ba,pt	%%xcc, 1f\n"
-	"	 sllx	%%g1, 32, %%g1\n"
-	"	.align	64\n"
-	"1:	rd	%%tick, %%g2\n"
-	"	add	%%g2, 6, %%g2\n"
-	"	andn	%%g2, %%g1, %%g2\n"
-	"	wrpr	%%g2, 0, %%tick\n"
-	"	rdpr	%%tick, %%g0"
-	: /* no outputs */
-	: /* no inputs */
-	: "g1", "g2");
-
-	/* Workaround for Spitfire Errata (#54 I think??), I discovered
-	 * this via Sun BugID 4008234, mentioned in Solaris-2.5.1 patch
-	 * number 103640.
-	 *
-	 * On Blackbird writes to %tick_cmpr can fail, the
-	 * workaround seems to be to execute the wr instruction
-	 * at the start of an I-cache line, and perform a dummy
-	 * read back from %tick_cmpr right after writing to it. -DaveM
-	 */
-	if (!SPARC64_USE_STICK) {
-	__asm__ __volatile__(
-	"	rd	%%tick, %%g1\n"
-	"	ba,pt	%%xcc, 1f\n"
-	"	 add	%%g1, %0, %%g1\n"
-	"	.align	64\n"
-	"1:	wr	%%g1, 0x0, %%tick_cmpr\n"
-	"	rd	%%tick_cmpr, %%g0"
-	: /* no outputs */
-	: "r" (timer_tick_offset)
-	: "g1");
-	} else {
-	/* Let the user get at STICK too. */
-	__asm__ __volatile__(
-	"	sethi	%%hi(0x80000000), %%g1\n"
-	"	sllx	%%g1, 32, %%g1\n"
-	"	rd	%%asr24, %%g2\n"
-	"	andn	%%g2, %%g1, %%g2\n"
-	"	wr	%%g2, 0, %%asr24"
-	: /* no outputs */
-	: /* no inputs */
-	: "g1", "g2");
-
-	__asm__ __volatile__(
-	"	rd	%%asr24, %%g1\n"
-	"	add	%%g1, %0, %%g1\n"
-	"	wr	%%g1, 0x0, %%asr25"
-	: /* no outputs */
-	: "r" (timer_tick_offset)
-	: "g1");
-	}
-
-	/* Restore PSTATE_IE. */
-	__asm__ __volatile__("wrpr	%0, 0x0, %%pstate"
-			     : /* no outputs */
-			     : "r" (pstate));
-
-	sti();
-}
-
 #ifdef CONFIG_SMP
 static int retarget_one_irq(struct irqaction *p, int goal_cpu)
 {
diff -urN linux-2.4.21-pre5/arch/sparc64/kernel/process.c linux-2.4.21-pre6/arch/sparc64/kernel/process.c
--- linux-2.4.21-pre5/arch/sparc64/kernel/process.c	Thu Nov 28 15:53:12 2002
+++ linux-2.4.21-pre6/arch/sparc64/kernel/process.c	Wed Mar 26 16:11:34 2003
@@ -673,7 +673,7 @@
  * a system call from a "real" process, but the process memory space will
  * not be free'd until both the parent and the child have exited.
  */
-pid_t kernel_thread(int (*fn)(void *), void * arg, unsigned long flags)
+pid_t arch_kernel_thread(int (*fn)(void *), void * arg, unsigned long flags)
 {
 	long retval;
 
diff -urN linux-2.4.21-pre5/arch/sparc64/kernel/signal.c linux-2.4.21-pre6/arch/sparc64/kernel/signal.c
--- linux-2.4.21-pre5/arch/sparc64/kernel/signal.c	Wed Mar 26 16:11:28 2003
+++ linux-2.4.21-pre6/arch/sparc64/kernel/signal.c	Wed Mar 26 16:11:34 2003
@@ -219,7 +219,7 @@
 	err |= __put_user(regs->u_regs[UREG_G4], &((*grp)[MC_G4]));
 	err |= __put_user(regs->u_regs[UREG_G5], &((*grp)[MC_G5]));
 	err |= __put_user(regs->u_regs[UREG_G6], &((*grp)[MC_G6]));
-	err |= __put_user(regs->u_regs[UREG_G6], &((*grp)[MC_G7]));
+	err |= __put_user(regs->u_regs[UREG_G7], &((*grp)[MC_G7]));
 	err |= __put_user(regs->u_regs[UREG_I0], &((*grp)[MC_O0]));
 	err |= __put_user(regs->u_regs[UREG_I1], &((*grp)[MC_O1]));
 	err |= __put_user(regs->u_regs[UREG_I2], &((*grp)[MC_O2]));
@@ -438,8 +438,8 @@
 	err |= copy_from_user(regs->u_regs, sf->regs.u_regs, sizeof(regs->u_regs));
 
 	/* User can only change condition codes in %tstate. */
-	regs->tstate &= ~(TSTATE_ICC);
-	regs->tstate |= (tstate & TSTATE_ICC);
+	regs->tstate &= ~(TSTATE_ICC | TSTATE_XCC);
+	regs->tstate |= (tstate & (TSTATE_ICC | TSTATE_XCC));
 
 	err |= __get_user(fpu_save, &sf->fpu_save);
 	if (fpu_save)
diff -urN linux-2.4.21-pre5/arch/sparc64/kernel/smp.c linux-2.4.21-pre6/arch/sparc64/kernel/smp.c
--- linux-2.4.21-pre5/arch/sparc64/kernel/smp.c	Thu Nov 28 15:53:12 2002
+++ linux-2.4.21-pre6/arch/sparc64/kernel/smp.c	Wed Mar 26 16:11:34 2003
@@ -18,6 +18,7 @@
 #include <linux/fs.h>
 #include <linux/seq_file.h>
 #include <linux/cache.h>
+#include <linux/timer.h>
 
 #include <asm/head.h>
 #include <asm/ptrace.h>
@@ -137,7 +138,6 @@
 void __init smp_callin(void)
 {
 	int cpuid = hard_smp_processor_id();
-	unsigned long pstate;
 	extern int bigkernel;
 	extern unsigned long kern_locked_tte_data;
 
@@ -155,50 +155,6 @@
 
 	cpu_probe();
 
-	/* Guarentee that the following sequences execute
-	 * uninterrupted.
-	 */
-	__asm__ __volatile__("rdpr	%%pstate, %0\n\t"
-			     "wrpr	%0, %1, %%pstate"
-			     : "=r" (pstate)
-			     : "i" (PSTATE_IE));
-
-	/* Set things up so user can access tick register for profiling
-	 * purposes.  Also workaround BB_ERRATA_1 by doing a dummy
-	 * read back of %tick after writing it.
-	 */
-	__asm__ __volatile__(
-		"sethi	%%hi(0x80000000), %%g1\n\t"
-		"ba,pt	%%xcc, 1f\n\t"
-		"sllx	%%g1, 32, %%g1\n\t"
-		".align	64\n"
-	"1:	rd	%%tick, %%g2\n\t"
-		"add	%%g2, 6, %%g2\n\t"
-		"andn	%%g2, %%g1, %%g2\n\t"
-		"wrpr	%%g2, 0, %%tick\n\t"
-		"rdpr	%%tick, %%g0"
-	: /* no outputs */
-	: /* no inputs */
-	: "g1", "g2");
-
-	if (SPARC64_USE_STICK) {
-		/* Let the user get at STICK too. */
-		__asm__ __volatile__(
-			"sethi	%%hi(0x80000000), %%g1\n\t"
-			"sllx	%%g1, 32, %%g1\n\t"
-			"rd	%%asr24, %%g2\n\t"
-			"andn	%%g2, %%g1, %%g2\n\t"
-			"wr	%%g2, 0, %%asr24"
-		: /* no outputs */
-		: /* no inputs */
-		: "g1", "g2");
-	}
-
-	/* Restore PSTATE_IE. */
-	__asm__ __volatile__("wrpr	%0, 0x0, %%pstate"
-			     : /* no outputs */
-			     : "r" (pstate));
-
 	smp_setup_percpu_timer();
 
 	__sti();
@@ -242,6 +198,158 @@
 	panic("SMP bolixed\n");
 }
 
+static unsigned long current_tick_offset;
+
+/* This tick register synchronization scheme is taken entirely from
+ * the ia64 port, see arch/ia64/kernel/smpboot.c for details and credit.
+ *
+ * The only change I've made is to rework it so that the master
+ * initiates the synchonization instead of the slave. -DaveM
+ */
+
+#define MASTER	0
+#define SLAVE	(SMP_CACHE_BYTES/sizeof(unsigned long))
+
+#define NUM_ROUNDS	64	/* magic value */
+#define NUM_ITERS	5	/* likewise */
+
+static spinlock_t itc_sync_lock = SPIN_LOCK_UNLOCKED;
+static unsigned long go[SLAVE + 1];
+
+#define DEBUG_TICK_SYNC	0
+
+static inline long get_delta (long *rt, long *master)
+{
+	unsigned long best_t0 = 0, best_t1 = ~0UL, best_tm = 0;
+	unsigned long tcenter, t0, t1, tm;
+	unsigned long i;
+
+	for (i = 0; i < NUM_ITERS; i++) {
+		t0 = tick_ops->get_tick();
+		go[MASTER] = 1;
+		membar("#StoreLoad");
+		while (!(tm = go[SLAVE]))
+			membar("#LoadLoad");
+		go[SLAVE] = 0;
+		membar("#StoreStore");
+		t1 = tick_ops->get_tick();
+
+		if (t1 - t0 < best_t1 - best_t0)
+			best_t0 = t0, best_t1 = t1, best_tm = tm;
+	}
+
+	*rt = best_t1 - best_t0;
+	*master = best_tm - best_t0;
+
+	/* average best_t0 and best_t1 without overflow: */
+	tcenter = (best_t0/2 + best_t1/2);
+	if (best_t0 % 2 + best_t1 % 2 == 2)
+		tcenter++;
+	return tcenter - best_tm;
+}
+
+void smp_synchronize_tick_client(void)
+{
+	long i, delta, adj, adjust_latency = 0, done = 0;
+	unsigned long flags, rt, master_time_stamp, bound;
+#if DEBUG_TICK_SYNC
+	struct {
+		long rt;	/* roundtrip time */
+		long master;	/* master's timestamp */
+		long diff;	/* difference between midpoint and master's timestamp */
+		long lat;	/* estimate of itc adjustment latency */
+	} t[NUM_ROUNDS];
+#endif
+
+	go[MASTER] = 1;
+
+	while (go[MASTER])
+		membar("#LoadLoad");
+
+	local_irq_save(flags);
+	{
+		for (i = 0; i < NUM_ROUNDS; i++) {
+			delta = get_delta(&rt, &master_time_stamp);
+			if (delta == 0) {
+				done = 1;	/* let's lock on to this... */
+				bound = rt;
+			}
+
+			if (!done) {
+				if (i > 0) {
+					adjust_latency += -delta;
+					adj = -delta + adjust_latency/4;
+				} else
+					adj = -delta;
+
+				tick_ops->add_tick(adj, current_tick_offset);
+			}
+#if DEBUG_TICK_SYNC
+			t[i].rt = rt;
+			t[i].master = master_time_stamp;
+			t[i].diff = delta;
+			t[i].lat = adjust_latency/4;
+#endif
+		}
+	}
+	local_irq_restore(flags);
+
+#if DEBUG_TICK_SYNC
+	for (i = 0; i < NUM_ROUNDS; i++)
+		printk("rt=%5ld master=%5ld diff=%5ld adjlat=%5ld\n",
+		       t[i].rt, t[i].master, t[i].diff, t[i].lat);
+#endif
+
+	printk(KERN_INFO "CPU %d: synchronized TICK with master CPU (last diff %ld cycles,"
+	       "maxerr %lu cycles)\n", smp_processor_id(), delta, rt);
+}
+
+static void smp_start_sync_tick_client(int cpu);
+
+static void smp_synchronize_one_tick(int cpu)
+{
+	unsigned long flags, i;
+
+	go[MASTER] = 0;
+
+	smp_start_sync_tick_client(cpu);
+
+	/* wait for client to be ready */
+	while (!go[MASTER])
+		membar("#LoadLoad");
+
+	/* now let the client proceed into his loop */
+	go[MASTER] = 0;
+	membar("#StoreLoad");
+
+	spin_lock_irqsave(&itc_sync_lock, flags);
+	{
+		for (i = 0; i < NUM_ROUNDS*NUM_ITERS; i++) {
+			while (!go[MASTER])
+				membar("#LoadLoad");
+			go[MASTER] = 0;
+			membar("#StoreStore");
+			go[SLAVE] = tick_ops->get_tick();
+			membar("#StoreLoad");
+		}
+	}
+	spin_unlock_irqrestore(&itc_sync_lock, flags);
+}
+
+static void smp_synchronize_tick(void)
+{
+	int cpu = smp_processor_id();
+	int i;
+
+	for (i = 0; i < NR_CPUS; i++) {
+		if (cpu_present_map & (1UL << i)) {
+			if (i == cpu)
+				continue;
+			smp_synchronize_one_tick(i);
+		}
+	}
+}
+
 extern struct prom_cpuinfo linux_cpus[64];
 
 extern unsigned long sparc64_cpu_startup;
@@ -340,6 +448,8 @@
 	}
 	smp_processors_ready = 1;
 	membar("#StoreStore | #StoreLoad");
+
+	smp_synchronize_tick();
 }
 
 static void spitfire_xcall_helper(u64 data0, u64 data1, u64 data2, u64 pstate, unsigned long cpu)
@@ -542,6 +652,15 @@
 	}
 }
 
+extern unsigned long xcall_sync_tick;
+
+static void smp_start_sync_tick_client(int cpu)
+{
+	smp_cross_call_masked(&xcall_sync_tick,
+			      0, 0, 0,
+			      (1UL << cpu));
+}
+
 /* Send cross call to all processors except self. */
 #define smp_cross_call(func, ctx, data1, data2) \
 	smp_cross_call_masked(func, ctx, data1, data2, cpu_present_map)
@@ -987,8 +1106,6 @@
 
 extern void sparc64_do_profile(unsigned long pc, unsigned long o7);
 
-static unsigned long current_tick_offset;
-
 #define prof_multiplier(__cpu)		cpu_data[(__cpu)].multiplier
 #define prof_counter(__cpu)		cpu_data[(__cpu)].counter
 
@@ -1002,12 +1119,7 @@
 	 * Check for level 14 softint.
 	 */
 	{
-		unsigned long tick_mask;
-
-		if (SPARC64_USE_STICK)
-			tick_mask = (1UL << 16);
-		else
-			tick_mask = (1UL << 0);
+		unsigned long tick_mask = tick_ops->softint_mask;
 
 		if (!(get_softint() & tick_mask)) {
 			extern void handler_irq(int, struct pt_regs *);
@@ -1022,17 +1134,17 @@
 		if (!user)
 			sparc64_do_profile(regs->tpc, regs->u_regs[UREG_RETPC]);
 		if (!--prof_counter(cpu)) {
-			if (cpu == boot_cpu_id) {
-				irq_enter(cpu, 0);
+			irq_enter(cpu, 0);
 
+			if (cpu == boot_cpu_id) {
 				kstat.irqs[cpu][0]++;
 				timer_tick_interrupt(regs);
-
-				irq_exit(cpu, 0);
 			}
 
 			update_process_times(user);
 
+			irq_exit(cpu, 0);
+
 			prof_counter(cpu) = prof_multiplier(cpu);
 		}
 
@@ -1044,47 +1156,14 @@
 				     : "=r" (pstate)
 				     : "i" (PSTATE_IE));
 
-		/* Workaround for Spitfire Errata (#54 I think??), I discovered
-		 * this via Sun BugID 4008234, mentioned in Solaris-2.5.1 patch
-		 * number 103640.
-		 *
-		 * On Blackbird writes to %tick_cmpr can fail, the
-		 * workaround seems to be to execute the wr instruction
-		 * at the start of an I-cache line, and perform a dummy
-		 * read back from %tick_cmpr right after writing to it. -DaveM
-		 *
-		 * Just to be anal we add a workaround for Spitfire
-		 * Errata 50 by preventing pipeline bypasses on the
-		 * final read of the %tick register into a compare
-		 * instruction.  The Errata 50 description states
-		 * that %tick is not prone to this bug, but I am not
-		 * taking any chances.
-		 */
-		if (!SPARC64_USE_STICK) {
-		__asm__ __volatile__("rd	%%tick_cmpr, %0\n\t"
-				     "ba,pt	%%xcc, 1f\n\t"
-				     " add	%0, %2, %0\n\t"
-				     ".align	64\n"
-				  "1: wr	%0, 0x0, %%tick_cmpr\n\t"
-				     "rd	%%tick_cmpr, %%g0\n\t"
-				     "rd	%%tick, %1\n\t"
-				     "mov	%1, %1"
-				     : "=&r" (compare), "=r" (tick)
-				     : "r" (current_tick_offset));
-		} else {
-		__asm__ __volatile__("rd	%%asr25, %0\n\t"
-				     "add	%0, %2, %0\n\t"
-				     "wr	%0, 0x0, %%asr25\n\t"
-				     "rd	%%asr24, %1\n\t"
-				     : "=&r" (compare), "=r" (tick)
-				     : "r" (current_tick_offset));
-		}
+		compare = tick_ops->add_compare(current_tick_offset);
+		tick = tick_ops->get_tick();
 
 		/* Restore PSTATE_IE. */
 		__asm__ __volatile__("wrpr	%0, 0x0, %%pstate"
 				     : /* no outputs */
 				     : "r" (pstate));
-	} while (tick >= compare);
+	} while (time_after_eq(tick, compare));
 }
 
 static void __init smp_setup_percpu_timer(void)
@@ -1102,35 +1181,7 @@
 			     : "=r" (pstate)
 			     : "i" (PSTATE_IE));
 
-	/* Workaround for Spitfire Errata (#54 I think??), I discovered
-	 * this via Sun BugID 4008234, mentioned in Solaris-2.5.1 patch
-	 * number 103640.
-	 *
-	 * On Blackbird writes to %tick_cmpr can fail, the
-	 * workaround seems to be to execute the wr instruction
-	 * at the start of an I-cache line, and perform a dummy
-	 * read back from %tick_cmpr right after writing to it. -DaveM
-	 */
-	if (!SPARC64_USE_STICK) {
-	__asm__ __volatile__(
-		"rd	%%tick, %%g1\n\t"
-		"ba,pt	%%xcc, 1f\n\t"
-		" add	%%g1, %0, %%g1\n\t"
-		".align	64\n"
-	"1:	wr	%%g1, 0x0, %%tick_cmpr\n\t"
-		"rd	%%tick_cmpr, %%g0"
-	: /* no outputs */
-	: "r" (current_tick_offset)
-	: "g1");
-	} else {
-	__asm__ __volatile__(
-		"rd	%%asr24, %%g1\n\t"
-		"add	%%g1, %0, %%g1\n\t"
-		"wr	%%g1, 0x0, %%asr25"
-	: /* no outputs */
-	: "r" (current_tick_offset)
-	: "g1");
-	}
+	tick_ops->init_tick(current_tick_offset);
 
 	/* Restore PSTATE_IE. */
 	__asm__ __volatile__("wrpr	%0, 0x0, %%pstate"
diff -urN linux-2.4.21-pre5/arch/sparc64/kernel/sparc64_ksyms.c linux-2.4.21-pre6/arch/sparc64/kernel/sparc64_ksyms.c
--- linux-2.4.21-pre5/arch/sparc64/kernel/sparc64_ksyms.c	Wed Mar 26 16:11:28 2003
+++ linux-2.4.21-pre6/arch/sparc64/kernel/sparc64_ksyms.c	Wed Mar 26 16:11:34 2003
@@ -60,7 +60,6 @@
 
 extern unsigned prom_cpu_nodes[64];
 extern void die_if_kernel(char *str, struct pt_regs *regs);
-extern pid_t kernel_thread(int (*fn)(void *), void * arg, unsigned long flags);
 void _sigpause_common (unsigned int set, struct pt_regs *);
 extern void *__bzero(void *, size_t);
 extern void *__bzero_noasi(void *, size_t);
diff -urN linux-2.4.21-pre5/arch/sparc64/kernel/systbls.S linux-2.4.21-pre6/arch/sparc64/kernel/systbls.S
--- linux-2.4.21-pre5/arch/sparc64/kernel/systbls.S	Fri Aug  2 17:39:43 2002
+++ linux-2.4.21-pre6/arch/sparc64/kernel/systbls.S	Wed Mar 26 16:11:34 2003
@@ -52,11 +52,11 @@
 /*150*/	.word sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_poll, sys_getdents64
 	.word sys32_fcntl64, sys_nis_syscall, sys32_statfs, sys32_fstatfs, sys_oldumount
 /*160*/	.word sys_nis_syscall, sys_nis_syscall, sys_getdomainname, sys_setdomainname, sys_nis_syscall
-	.word sys32_quotactl, sys_nis_syscall, sys32_mount, sys_ustat, sys_nis_syscall
-/*170*/	.word sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys32_getdents
-	.word sys_setsid, sys_fchdir, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall
-/*180*/	.word sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys32_sigpending, sys32_query_module
-	.word sys_setpgid, sys_nis_syscall, sys_tkill, sys_nis_syscall, sparc64_newuname
+	.word sys32_quotactl, sys_nis_syscall, sys32_mount, sys_ustat, sys_setxattr
+/*170*/	.word sys_lsetxattr, sys_fsetxattr, sys_getxattr, sys_lgetxattr, sys32_getdents
+	.word sys_setsid, sys_fchdir, sys_fgetxattr, sys_listxattr, sys_llistxattr
+/*180*/	.word sys_flistxattr, sys_removexattr, sys_lremovexattr, sys32_sigpending, sys32_query_module
+	.word sys_setpgid, sys_fremovexattr, sys_tkill, sys_nis_syscall, sparc64_newuname
 /*190*/	.word sys32_init_module, sparc64_personality, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall
 	.word sys_nis_syscall, sys_nis_syscall, sys_getppid, sys32_sigaction, sys_sgetmask
 /*200*/	.word sys_ssetmask, sys_sigsuspend, sys32_newlstat, sys_uselib, old32_readdir
@@ -111,11 +111,11 @@
 /*150*/	.word sys_getsockname, sys_nis_syscall, sys_nis_syscall, sys_poll, sys_getdents64
 	.word sys_nis_syscall, sys_nis_syscall, sys_statfs, sys_fstatfs, sys_oldumount
 /*160*/	.word sys_nis_syscall, sys_nis_syscall, sys_getdomainname, sys_setdomainname, sys_utrap_install
-	.word sys_quotactl, sys_nis_syscall, sys_mount, sys_ustat, sys_nis_syscall
-/*170*/	.word sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_getdents
-	.word sys_setsid, sys_fchdir, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall
-/*180*/	.word sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_query_module
-	.word sys_setpgid, sys_nis_syscall, sys_tkill, sys_nis_syscall, sparc64_newuname
+	.word sys_quotactl, sys_nis_syscall, sys_mount, sys_ustat, sys_setxattr
+/*170*/	.word sys_lsetxattr, sys_fsetxattr, sys_getxattr, sys_lgetxattr, sys_getdents
+	.word sys_setsid, sys_fchdir, sys_fgetxattr, sys_listxattr, sys_llistxattr
+/*180*/	.word sys_flistxattr, sys_removexattr, sys_lremovexattr, sys_nis_syscall, sys_query_module
+	.word sys_setpgid, sys_fremovexattr, sys_tkill, sys_nis_syscall, sparc64_newuname
 /*190*/	.word sys_init_module, sparc64_personality, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall
 	.word sys_nis_syscall, sys_nis_syscall, sys_getppid, sys_nis_syscall, sys_sgetmask
 /*200*/	.word sys_ssetmask, sys_nis_syscall, sys_newlstat, sys_uselib, sys_nis_syscall
diff -urN linux-2.4.21-pre5/arch/sparc64/kernel/time.c linux-2.4.21-pre6/arch/sparc64/kernel/time.c
--- linux-2.4.21-pre5/arch/sparc64/kernel/time.c	Thu Nov 28 15:53:12 2002
+++ linux-2.4.21-pre6/arch/sparc64/kernel/time.c	Wed Mar 26 16:11:34 2003
@@ -22,6 +22,7 @@
 #include <linux/ioport.h>
 #include <linux/mc146818rtc.h>
 #include <linux/delay.h>
+#include <linux/timer.h>
 
 #include <asm/oplib.h>
 #include <asm/mostek.h>
@@ -34,6 +35,7 @@
 #include <asm/ebus.h>
 #include <asm/isa.h>
 #include <asm/starfire.h>
+#include <asm/smp.h>
 
 extern rwlock_t xtime_lock;
 
@@ -49,6 +51,352 @@
 
 static int set_rtc_mmss(unsigned long);
 
+struct sparc64_tick_ops *tick_ops;
+
+#define TICK_PRIV_BIT	(1UL << 63)
+
+static void tick_disable_protection(void)
+{
+	/* Set things up so user can access tick register for profiling
+	 * purposes.  Also workaround BB_ERRATA_1 by doing a dummy
+	 * read back of %tick after writing it.
+	 */
+	__asm__ __volatile__(
+	"	ba,pt	%%xcc, 1f\n"
+	"	 nop\n"
+	"	.align	64\n"
+	"1:	rd	%%tick, %%g2\n"
+	"	add	%%g2, 6, %%g2\n"
+	"	andn	%%g2, %0, %%g2\n"
+	"	wrpr	%%g2, 0, %%tick\n"
+	"	rdpr	%%tick, %%g0"
+	: /* no outputs */
+	: "r" (TICK_PRIV_BIT)
+	: "g2");
+}
+
+static void tick_init_tick(unsigned long offset)
+{
+	tick_disable_protection();
+
+	__asm__ __volatile__(
+	"	rd	%%tick, %%g1\n"
+	"	andn	%%g1, %1, %%g1\n"
+	"	ba,pt	%%xcc, 1f\n"
+	"	 add	%%g1, %0, %%g1\n"
+	"	.align	64\n"
+	"1:	wr	%%g1, 0x0, %%tick_cmpr\n"
+	"	rd	%%tick_cmpr, %%g0"
+	: /* no outputs */
+	: "r" (offset), "r" (TICK_PRIV_BIT)
+	: "g1");
+}
+
+static unsigned long tick_get_tick(void)
+{
+	unsigned long ret;
+
+	__asm__ __volatile__("rd	%%tick, %0\n\t"
+			     "mov	%0, %0"
+			     : "=r" (ret));
+
+	return ret & ~TICK_PRIV_BIT;
+}
+
+static unsigned long tick_get_compare(void)
+{
+	unsigned long ret;
+
+	__asm__ __volatile__("rd	%%tick_cmpr, %0\n\t"
+			     "mov	%0, %0"
+			     : "=r" (ret));
+
+	return ret;
+}
+
+static unsigned long tick_add_compare(unsigned long adj)
+{
+	unsigned long new_compare;
+
+	/* Workaround for Spitfire Errata (#54 I think??), I discovered
+	 * this via Sun BugID 4008234, mentioned in Solaris-2.5.1 patch
+	 * number 103640.
+	 *
+	 * On Blackbird writes to %tick_cmpr can fail, the
+	 * workaround seems to be to execute the wr instruction
+	 * at the start of an I-cache line, and perform a dummy
+	 * read back from %tick_cmpr right after writing to it. -DaveM
+	 */
+	__asm__ __volatile__("rd	%%tick_cmpr, %0\n\t"
+			     "ba,pt	%%xcc, 1f\n\t"
+			     " add	%0, %1, %0\n\t"
+			     ".align	64\n"
+			     "1:\n\t"
+			     "wr	%0, 0, %%tick_cmpr\n\t"
+			     "rd	%%tick_cmpr, %%g0"
+			     : "=&r" (new_compare)
+			     : "r" (adj));
+
+	return new_compare;
+}
+
+static unsigned long tick_add_tick(unsigned long adj, unsigned long offset)
+{
+	unsigned long new_tick, tmp;
+
+	/* Also need to handle Blackbird bug here too. */
+	__asm__ __volatile__("rd	%%tick, %0\n\t"
+			     "add	%0, %2, %0\n\t"
+			     "wrpr	%0, 0, %%tick\n\t"
+			     "andn	%0, %4, %1\n\t"
+			     "ba,pt	%%xcc, 1f\n\t"
+			     " add	%1, %3, %1\n\t"
+			     ".align	64\n"
+			     "1:\n\t"
+			     "wr	%1, 0, %%tick_cmpr\n\t"
+			     "rd	%%tick_cmpr, %%g0"
+			     : "=&r" (new_tick), "=&r" (tmp)
+			     : "r" (adj), "r" (offset), "r" (TICK_PRIV_BIT));
+
+	return new_tick;
+}
+
+static struct sparc64_tick_ops tick_operations = {
+	.init_tick	=	tick_init_tick,
+	.get_tick	=	tick_get_tick,
+	.get_compare	=	tick_get_compare,
+	.add_tick	=	tick_add_tick,
+	.add_compare	=	tick_add_compare,
+	.softint_mask	=	1UL << 0,
+};
+
+static void stick_init_tick(unsigned long offset)
+{
+	tick_disable_protection();
+
+	/* Let the user get at STICK too. */
+	__asm__ __volatile__(
+	"	rd	%%asr24, %%g2\n"
+	"	andn	%%g2, %0, %%g2\n"
+	"	wr	%%g2, 0, %%asr24"
+	: /* no outputs */
+	: "r" (TICK_PRIV_BIT)
+	: "g1", "g2");
+
+	__asm__ __volatile__(
+	"	rd	%%asr24, %%g1\n"
+	"	andn	%%g1, %1, %%g1\n"
+	"	add	%%g1, %0, %%g1\n"
+	"	wr	%%g1, 0x0, %%asr25"
+	: /* no outputs */
+	: "r" (offset), "r" (TICK_PRIV_BIT)
+	: "g1");
+}
+
+static unsigned long stick_get_tick(void)
+{
+	unsigned long ret;
+
+	__asm__ __volatile__("rd	%%asr24, %0"
+			     : "=r" (ret));
+
+	return ret & ~TICK_PRIV_BIT;
+}
+
+static unsigned long stick_get_compare(void)
+{
+	unsigned long ret;
+
+	__asm__ __volatile__("rd	%%asr25, %0"
+			     : "=r" (ret));
+
+	return ret;
+}
+
+static unsigned long stick_add_tick(unsigned long adj, unsigned long offset)
+{
+	unsigned long new_tick, tmp;
+
+	__asm__ __volatile__("rd	%%asr24, %0\n\t"
+			     "add	%0, %2, %0\n\t"
+			     "wr	%0, 0, %%asr24\n\t"
+			     "andn	%0, %4, %1\n\t"
+			     "add	%1, %3, %1\n\t"
+			     "wr	%1, 0, %%asr25"
+			     : "=&r" (new_tick), "=&r" (tmp)
+			     : "r" (adj), "r" (offset), "r" (TICK_PRIV_BIT));
+
+	return new_tick;
+}
+
+static unsigned long stick_add_compare(unsigned long adj)
+{
+	unsigned long new_compare;
+
+	__asm__ __volatile__("rd	%%asr25, %0\n\t"
+			     "add	%0, %1, %0\n\t"
+			     "wr	%0, 0, %%asr25"
+			     : "=&r" (new_compare)
+			     : "r" (adj));
+
+	return new_compare;
+}
+
+static struct sparc64_tick_ops stick_operations = {
+	.init_tick	=	stick_init_tick,
+	.get_tick	=	stick_get_tick,
+	.get_compare	=	stick_get_compare,
+	.add_tick	=	stick_add_tick,
+	.add_compare	=	stick_add_compare,
+	.softint_mask	=	1UL << 16,
+};
+
+/* On Hummingbird the STICK/STICK_CMPR register is implemented
+ * in I/O space.  There are two 64-bit registers each, the
+ * first holds the low 32-bits of the value and the second holds
+ * the high 32-bits.
+ *
+ * Since STICK is constantly updating, we have to access it carefully.
+ *
+ * The sequence we use to read is:
+ * 1) read low
+ * 2) read high
+ * 3) read low again, if it rolled over increment high by 1
+ *
+ * Writing STICK safely is also tricky:
+ * 1) write low to zero
+ * 2) write high
+ * 3) write low
+ */
+#define HBIRD_STICKCMP_ADDR	0x1fe0000f060UL
+#define HBIRD_STICK_ADDR	0x1fe0000f070UL
+
+static unsigned long __hbird_read_stick(void)
+{
+	unsigned long ret, tmp1, tmp2, tmp3;
+	unsigned long addr = HBIRD_STICK_ADDR;
+
+	__asm__ __volatile__("ldxa	[%1] %5, %2\n\t"
+			     "add	%1, 0x8, %1\n\t"
+			     "ldxa	[%1] %5, %3\n\t"
+			     "sub	%1, 0x8, %1\n\t"
+			     "ldxa	[%1] %5, %4\n\t"
+			     "cmp	%4, %2\n\t"
+			     "blu,a,pn	%%xcc, 1f\n\t"
+			     " add	%3, 1, %3\n"
+			     "1:\n\t"
+			     "sllx	%3, 32, %3\n\t"
+			     "or	%3, %4, %0\n\t"
+			     : "=&r" (ret), "=&r" (addr),
+			       "=&r" (tmp1), "=&r" (tmp2), "=&r" (tmp3)
+			     : "i" (ASI_PHYS_BYPASS_EC_E), "1" (addr));
+
+	return ret;
+}
+
+static unsigned long __hbird_read_compare(void)
+{
+	unsigned long low, high;
+	unsigned long addr = HBIRD_STICKCMP_ADDR;
+
+	__asm__ __volatile__("ldxa	[%2] %3, %0\n\t"
+			     "add	%2, 0x8, %2\n\t"
+			     "ldxa	[%2] %3, %1"
+			     : "=&r" (low), "=&r" (high), "=&r" (addr)
+			     : "i" (ASI_PHYS_BYPASS_EC_E), "2" (addr));
+
+	return (high << 32UL) | low;
+}
+
+static void __hbird_write_stick(unsigned long val)
+{
+	unsigned long low = (val & 0xffffffffUL);
+	unsigned long high = (val >> 32UL);
+	unsigned long addr = HBIRD_STICK_ADDR;
+
+	__asm__ __volatile__("stxa	%%g0, [%0] %4\n\t"
+			     "add	%0, 0x8, %0\n\t"
+			     "stxa	%3, [%0] %4\n\t"
+			     "sub	%0, 0x8, %0\n\t"
+			     "stxa	%2, [%0] %4"
+			     : "=&r" (addr)
+			     : "0" (addr), "r" (low), "r" (high),
+			       "i" (ASI_PHYS_BYPASS_EC_E));
+}
+
+static void __hbird_write_compare(unsigned long val)
+{
+	unsigned long low = (val & 0xffffffffUL);
+	unsigned long high = (val >> 32UL);
+	unsigned long addr = HBIRD_STICKCMP_ADDR + 0x8UL;
+
+	__asm__ __volatile__("stxa	%3, [%0] %4\n\t"
+			     "sub	%0, 0x8, %0\n\t"
+			     "stxa	%2, [%0] %4"
+			     : "=&r" (addr)
+			     : "0" (addr), "r" (low), "r" (high),
+			       "i" (ASI_PHYS_BYPASS_EC_E));
+}
+
+static void hbtick_init_tick(unsigned long offset)
+{
+	unsigned long val;
+
+	tick_disable_protection();
+
+	/* XXX This seems to be necessary to 'jumpstart' Hummingbird
+	 * XXX into actually sending STICK interrupts.  I think because
+	 * XXX of how we store %tick_cmpr in head.S this somehow resets the
+	 * XXX {TICK + STICK} interrupt mux.  -DaveM
+	 */
+	__hbird_write_stick(__hbird_read_stick());
+
+	val = __hbird_read_stick() & ~TICK_PRIV_BIT;
+	__hbird_write_compare(val + offset);
+}
+
+static unsigned long hbtick_get_tick(void)
+{
+	return __hbird_read_stick() & ~TICK_PRIV_BIT;
+}
+
+static unsigned long hbtick_get_compare(void)
+{
+	return __hbird_read_compare();
+}
+
+static unsigned long hbtick_add_tick(unsigned long adj, unsigned long offset)
+{
+	unsigned long val;
+
+	val = __hbird_read_stick() + adj;
+	__hbird_write_stick(val);
+
+	val &= ~TICK_PRIV_BIT;
+	__hbird_write_compare(val + offset);
+
+	return val;
+}
+
+static unsigned long hbtick_add_compare(unsigned long adj)
+{
+	unsigned long val = __hbird_read_compare() + adj;
+
+	val &= ~TICK_PRIV_BIT;
+	__hbird_write_compare(val);
+
+	return val;
+}
+
+static struct sparc64_tick_ops hbtick_operations = {
+	.init_tick	=	hbtick_init_tick,
+	.get_tick	=	hbtick_get_tick,
+	.get_compare	=	hbtick_get_compare,
+	.add_tick	=	hbtick_add_tick,
+	.add_compare	=	hbtick_add_compare,
+	.softint_mask	=	1UL << 0,
+};
+
 /* timer_interrupt() needs to keep up the real-time clock,
  * as well as call the "do_timer()" routine every clocktick
  *
@@ -129,49 +477,14 @@
 				     : "=r" (pstate)
 				     : "i" (PSTATE_IE));
 
-		/* Workaround for Spitfire Errata (#54 I think??), I discovered
-		 * this via Sun BugID 4008234, mentioned in Solaris-2.5.1 patch
-		 * number 103640.
-		 *
-		 * On Blackbird writes to %tick_cmpr can fail, the
-		 * workaround seems to be to execute the wr instruction
-		 * at the start of an I-cache line, and perform a dummy
-		 * read back from %tick_cmpr right after writing to it. -DaveM
-		 *
-		 * Just to be anal we add a workaround for Spitfire
-		 * Errata 50 by preventing pipeline bypasses on the
-		 * final read of the %tick register into a compare
-		 * instruction.  The Errata 50 description states
-		 * that %tick is not prone to this bug, but I am not
-		 * taking any chances.
-		 */
-		if (!SPARC64_USE_STICK) {
-		__asm__ __volatile__(
-		"	rd	%%tick_cmpr, %0\n"
-		"	ba,pt	%%xcc, 1f\n"
-		"	 add	%0, %2, %0\n"
-		"	.align	64\n"
-		"1: 	wr	%0, 0, %%tick_cmpr\n"
-		"	rd	%%tick_cmpr, %%g0\n"
-		"	rd	%%tick, %1\n"
-		"	mov	%1, %1"
-			: "=&r" (timer_tick_compare), "=r" (ticks)
-			: "r" (timer_tick_offset));
-		} else {
-		__asm__ __volatile__(
-		"	rd	%%asr25, %0\n"
-		"	add	%0, %2, %0\n"
-		"	wr	%0, 0, %%asr25\n"
-		"	rd	%%asr24, %1"
-			: "=&r" (timer_tick_compare), "=r" (ticks)
-			: "r" (timer_tick_offset));
-		}
+		timer_tick_compare = tick_ops->add_compare(timer_tick_offset);
+		ticks = tick_ops->get_tick();
 
 		/* Restore PSTATE_IE. */
 		__asm__ __volatile__("wrpr	%0, 0x0, %%pstate"
 				     : /* no outputs */
 				     : "r" (pstate));
-	} while (ticks >= timer_tick_compare);
+	} while (time_after_eq(ticks, timer_tick_compare));
 
 	timer_check_rtc();
 
@@ -188,19 +501,7 @@
 	/*
 	 * Only keep timer_tick_offset uptodate, but don't set TICK_CMPR.
 	 */
-	if (!SPARC64_USE_STICK) {
-	__asm__ __volatile__(
-	"	rd	%%tick_cmpr, %0\n"
-	"	add	%0, %1, %0"
-		: "=&r" (timer_tick_compare)
-		: "r" (timer_tick_offset));
-	} else {
-	__asm__ __volatile__(
-	"	rd	%%asr25, %0\n"
-	"	add	%0, %1, %0"
-		: "=&r" (timer_tick_compare)
-		: "r" (timer_tick_offset));
-	}
+	timer_tick_compare = tick_ops->get_compare() + timer_tick_offset;
 
 	timer_check_rtc();
 
@@ -611,43 +912,90 @@
 	__restore_flags(flags);
 }
 
-extern void init_timers(void (*func)(int, void *, struct pt_regs *),
-			unsigned long *);
+/* This is gets the master TICK_INT timer going. */
+static unsigned long sparc64_init_timers(void (*cfunc)(int, void *, struct pt_regs *))
+{
+	unsigned long pstate, clock;
+	int node, err;
+#ifdef CONFIG_SMP
+	extern void smp_tick_init(void);
+#endif
+
+	if (tlb_type == spitfire) {
+		unsigned long ver, manuf, impl;
+
+		__asm__ __volatile__ ("rdpr %%ver, %0"
+				      : "=&r" (ver));
+		manuf = ((ver >> 48) & 0xffff);
+		impl = ((ver >> 32) & 0xffff);
+		if (manuf == 0x17 && impl == 0x13) {
+			/* Hummingbird, aka Ultra-IIe */
+			tick_ops = &hbtick_operations;
+			node = prom_root_node;
+			clock = prom_getint(node, "stick-frequency");
+		} else {
+			tick_ops = &tick_operations;
+			node = linux_cpus[0].prom_node;
+			clock = prom_getint(node, "clock-frequency");
+		}
+	} else {
+		tick_ops = &stick_operations;
+		node = prom_root_node;
+		clock = prom_getint(node, "stick-frequency");
+	}
+	timer_tick_offset = clock / HZ;
+
+#ifdef CONFIG_SMP
+	smp_tick_init();
+#endif
+
+	/* Register IRQ handler. */
+	err = request_irq(build_irq(0, 0, 0UL, 0UL), cfunc, SA_STATIC_ALLOC,
+			  "timer", NULL);
+
+	if(err) {
+		prom_printf("Serious problem, cannot register TICK_INT\n");
+		prom_halt();
+	}
+
+	/* Guarentee that the following sequences execute
+	 * uninterrupted.
+	 */
+	__asm__ __volatile__("rdpr	%%pstate, %0\n\t"
+			     "wrpr	%0, %1, %%pstate"
+			     : "=r" (pstate)
+			     : "i" (PSTATE_IE));
+
+	tick_ops->init_tick(timer_tick_offset);
 
+	/* Restore PSTATE_IE. */
+	__asm__ __volatile__("wrpr	%0, 0x0, %%pstate"
+			     : /* no outputs */
+			     : "r" (pstate));
+
+	sti();
+
+	return clock;
+}
+
+/* The quotient formula is taken from the IA64 port. */
 void __init time_init(void)
 {
-	/* clock_probe() is now done at end of [se]bus_init on sparc64
-	 * so that sbus, fhc and ebus bus information is probed and
-	 * available.
-	 */
-	unsigned long clock;
+	unsigned long clock = sparc64_init_timers(timer_interrupt);
 
-	init_timers(timer_interrupt, &clock);
-	timer_ticks_per_usec_quotient = ((1UL<<32) / (clock / 1000020));
+	timer_ticks_per_usec_quotient =
+		(((1000000UL << 30) +
+		  (clock / 2)) / clock);
 }
 
 static __inline__ unsigned long do_gettimeoffset(void)
 {
-	unsigned long ticks;
+	unsigned long ticks = tick_ops->get_tick();
 
-	if (!SPARC64_USE_STICK) {
-	__asm__ __volatile__(
-	"	rd	%%tick, %%g1\n"
-	"	add	%1, %%g1, %0\n"
-	"	sub	%0, %2, %0\n"
-		: "=r" (ticks)
-		: "r" (timer_tick_offset), "r" (timer_tick_compare)
-		: "g1", "g2");
-	} else {
-	__asm__ __volatile__("rd	%%asr24, %%g1\n\t"
-			     "add	%1, %%g1, %0\n\t"
-			     "sub	%0, %2, %0\n\t"
-			     : "=&r" (ticks)
-			     : "r" (timer_tick_offset), "r" (timer_tick_compare)
-			     : "g1");
-	}
+	ticks += timer_tick_offset;
+	ticks -= timer_tick_compare;
 
-	return (ticks * timer_ticks_per_usec_quotient) >> 32UL;
+	return (ticks * timer_ticks_per_usec_quotient) >> 30UL;
 }
 
 void do_settimeofday(struct timeval *tv)
diff -urN linux-2.4.21-pre5/arch/sparc64/kernel/trampoline.S linux-2.4.21-pre6/arch/sparc64/kernel/trampoline.S
--- linux-2.4.21-pre5/arch/sparc64/kernel/trampoline.S	Thu Nov 28 15:53:12 2002
+++ linux-2.4.21-pre6/arch/sparc64/kernel/trampoline.S	Wed Mar 26 16:11:34 2003
@@ -85,7 +85,10 @@
 
 startup_continue:
 	wrpr		%g0, 15, %pil
-	wr		%g0, 0, %tick_cmpr
+
+	sethi		%hi(0x80000000), %g2
+	sllx		%g2, 32, %g2
+	wr		%g2, 0, %tick_cmpr
 
 	/* Call OBP by hand to lock KERNBASE into i/d tlbs. */
 	mov		%o0, %l0
diff -urN linux-2.4.21-pre5/arch/sparc64/mm/ultra.S linux-2.4.21-pre6/arch/sparc64/mm/ultra.S
--- linux-2.4.21-pre5/arch/sparc64/mm/ultra.S	Thu Nov 28 15:53:12 2002
+++ linux-2.4.21-pre6/arch/sparc64/mm/ultra.S	Wed Mar 26 16:11:34 2003
@@ -524,6 +524,24 @@
 	nop
 	nop
 
+	/* This runs in a very controlled environment, so we do
+	 * not need to worry about BH races etc.
+	 */
+	.globl		xcall_sync_tick
+xcall_sync_tick:
+	rdpr		%pstate, %g2
+	wrpr		%g2, PSTATE_IG | PSTATE_AG, %pstate
+	rdpr		%pil, %g2
+	wrpr		%g0, 15, %pil
+	sethi		%hi(109f), %g7
+	b,pt		%xcc, etrap_irq
+109:	 or		%g7, %lo(109b), %g7
+	call		smp_synchronize_tick_client
+	 nop
+	clr		%l6
+	b		rtrap_xcall
+	 ldx		[%sp + PTREGS_OFF + PT_V9_TSTATE], %l1
+
 	/* NOTE: This is SPECIAL!!  We do etrap/rtrap however
 	 *       we choose to deal with the "BH's run with
 	 *       %pil==15" problem (described in asm/pil.h)
diff -urN linux-2.4.21-pre5/arch/x86_64/Makefile linux-2.4.21-pre6/arch/x86_64/Makefile
--- linux-2.4.21-pre5/arch/x86_64/Makefile	Thu Nov 28 15:53:12 2002
+++ linux-2.4.21-pre6/arch/x86_64/Makefile	Wed Mar 26 16:11:34 2003
@@ -19,7 +19,7 @@
 # 20010105  Andi Kleen, add IA32 compiler.
 #           ....and later removed it again....
 #
-# $Id: Makefile,v 1.31 2002/03/22 15:56:07 ak Exp $
+# $Id: Makefile,v 1.32 2003/03/03 16:06:43 ak Exp $
 
 #
 # early bootup linking needs 32bit. You can either use real 32bit tools
@@ -47,6 +47,9 @@
 # needed for earlier gcc 3.1
 CFLAGS += -fno-strength-reduce
 #CFLAGS += -g
+ifneq ($(CONFIG_X86_REMOTE_DEBUG),y)
+CFLAGS += -fno-asynchronous-unwind-tables
+endif
 
 # prevent gcc from keeping the stack 16 byte aligned (FIXME)
 #CFLAGS += -mpreferred-stack-boundary=2
diff -urN linux-2.4.21-pre5/arch/x86_64/boot/bootsect.S linux-2.4.21-pre6/arch/x86_64/boot/bootsect.S
--- linux-2.4.21-pre5/arch/x86_64/boot/bootsect.S	Wed Mar 26 16:11:28 2003
+++ linux-2.4.21-pre6/arch/x86_64/boot/bootsect.S	Wed Mar 26 16:11:34 2003
@@ -395,9 +395,9 @@
 # NOTE: Doesn't save %ax or %dx; do it yourself if you need to.
 
 kill_motor:
-	xorw    %ax, %ax                # reset FDC
-        xorb    %dl, %dl
-        int     $0x13
+	xorw	%ax, %ax		# reset FDC
+	xorb	%dl, %dl
+	int	$0x13
 	ret
 
 sectors:	.word 0
diff -urN linux-2.4.21-pre5/arch/x86_64/boot/compressed/misc.c linux-2.4.21-pre6/arch/x86_64/boot/compressed/misc.c
--- linux-2.4.21-pre5/arch/x86_64/boot/compressed/misc.c	Wed Mar 26 16:11:28 2003
+++ linux-2.4.21-pre6/arch/x86_64/boot/compressed/misc.c	Wed Mar 26 16:11:34 2003
@@ -279,7 +279,7 @@
 	puts(x);
 	puts("\n\n -- System halted");
 
-	while(1);	/* Halt */
+	while(1);
 }
 
 void setup_normal_output_buffer(void)
@@ -332,116 +332,6 @@
 	}
 }
 
-void check_cpu(void)
-{
-	int res = 0;
-	int tmp, flags;
-	
-	asm volatile( " \n\
-	movl $3,%%edx		# at least 386 \n\
-	pushfl			# push EFLAGS \n\
-	popl %%eax		# get EFLAGS \n\
-	movl %%eax,%%ecx		# save original EFLAGS \n\
-	xorl $0x40000,%%eax	# flip AC bit in EFLAGS \n\
-	pushl %%eax		# copy to EFLAGS \n\
-	popfl			# set EFLAGS \n\
-	pushfl			# get new EFLAGS \n\
-	popl %%eax		# put it in eax \n\
-	xorl %%ecx,%%eax		# change in flags \n\
-	andl $0x40000,%%eax	# check if AC bit changed \n\
-	je 1f \n\
-\n\
-	movl $4,%%edx		# at least 486 \n\
-	movl %%ecx,%%eax \n\
-	xorl $0x200000,%%eax	# check ID flag \n\
-	pushl %%eax \n\
-	popfl			# if we are on a straight 486DX, SX, or \n\
-	pushfl			# 487SX we can't change it \n\
-	popl %%eax \n\
-	xorl %%ecx,%%eax \n\
-	pushl %%ecx		# restore original EFLAGS \n\
-	popfl \n\
-	andl $0x200000,%%eax \n\
-	je 1f \n\
-\n\
-	/* get vendor info */ \n\
-#	xorl %%eax,%%eax			# call CPUID with 0 -> return vendor ID \n\
-#	cpuid \n\
-#	movl $5, %%edx \n\
-#	cmpl $0x41757468,%%ebx		# check thats amd \n\
-#	jne 1f \n\
-\n\
-	mov $0x80000000,%%eax		# Is extended cpuid supported?\n\
-	cpuid\n\
-	test $0x80000000,%%eax\n\
-	movl $5, %%edx \n\
-	jz 1f\n\
-\n\
-	movl $0x80000001,%%eax \n\
-	cpuid \n\
-	andl $0x20000000,%%edx \n\
-	movl $6, %%edx \n\
-	jz 1f \n\
-\n\
-	movl $7, %%edx \n\
-1:" : "=d" (res) : : "eax", "ebx", "ecx" );
-
-	switch (res) {
-	case 3: puts( "386" );
-		break;
-	case 4: puts( "486" );
-		break;
-	case 5: puts( "no extended cpuid" );
-		break;
-	case 6: puts( "non-64bit 586+" );
-		break;
-	case 7: puts( "64bit" );
-		break;
-	default:puts( "internal error" );
-		break;
-	}
-	if (res !=7)
-		error( "Sorry, your CPU is not capable of running 64-bit kernel." );
-
-	/* check required feature flags */ 
-	/* see http://www.x86-64.org/lists/discuss/msg02971.html */
-#define REQUIRED_MASK1 ((1<<0)|(1<<3)|(1<<4)|(1<<5)|(1<<6)|(1<<8)|(1<<11)| \
-					   (1<<13)|(1<<15)|(1<<24))
-	
-	asm("cpuid" : "=d" (flags), "=a" (tmp) : "1" (0x80000001) : "ebx", "ecx"); 
-	flags &= REQUIRED_MASK1;
-	flags ^= REQUIRED_MASK1; 
-	if (flags & (1<<0))
-		error("CPU misses x87"); 
-	if (flags & (1<<3)) 
-		error("CPU doesn't support page size extension (PSE)");
-	if (flags & (1<<4))
-		error("CPU misses an time stamp counter"); 
-	if (flags & (1<<5))
-		error("CPU misses AMD style MSRs"); 
-	if (flags & (1<<6))
-		error("CPU misses physical address extension (PAE)"); 
-	if (flags & (1<<8))
-		error("CPU misses cmpxchg8"); 
-	if (flags & (1<<11))
-		error("CPU doesn't support SYSCALL/SYSRET");
-	if (flags & (1<<13))
-		error("CPU doesn't support PGE"); 
-	if (flags & (1<<15))
-		error("CPU doesn't support CMOV"); 
-	if (flags & (1<<24))
-		error("CPU doesn't support FXSAVE/FXRSTOR"); 
-	
-#define REQUIRED_MASK2 ((1<<25)|(1<<26))	
-	asm("cpuid" : "=d" (flags), "=a" (tmp) : "1" (1) : "ebx", "ecx"); 
-	flags &= REQUIRED_MASK2; 
-	flags ^= REQUIRED_MASK2; 
-	if (flags & (1<<25))
-		error("CPU doesn't support SSE1");
-	if (flags & (1<<26))
-		error("CPU doesn't support SSE2");
-}
-
 int decompress_kernel(struct moveparams *mv, void *rmode)
 {
 	real_mode = rmode;
@@ -461,8 +351,6 @@
 	else setup_output_buffer_if_we_run_high(mv);
 
 	makecrc();
-	puts("Checking CPU type...");
-	check_cpu();
 	puts(".\nDecompressing Linux...");
 	gunzip();
 	puts("done.\nBooting the kernel.\n");
diff -urN linux-2.4.21-pre5/arch/x86_64/boot/setup.S linux-2.4.21-pre6/arch/x86_64/boot/setup.S
--- linux-2.4.21-pre5/arch/x86_64/boot/setup.S	Thu Nov 28 15:53:12 2002
+++ linux-2.4.21-pre6/arch/x86_64/boot/setup.S	Wed Mar 26 16:11:34 2003
@@ -42,6 +42,7 @@
  * if CX/DX have been changed in the e801 call and if so use AX/BX .
  * Michael Miller, April 2001 <michaelm@mjmm.org>
  *
+ * Added long mode checking and SSE force. March 2003, Andi Kleen.		
  */
 
 #include <linux/config.h>
@@ -197,10 +198,10 @@
 prtsp2:	call	prtspc		# Print double space
 prtspc:	movb	$0x20, %al	# Print single space (note: fall-thru)
 
-# Part of above routine, this one just prints ascii al
-prtchr:	pushw	%ax
+prtchr:	
+	pushw	%ax
 	pushw	%cx
-	xorb	%bh, %bh
+	movw	$0007,%bx
 	movw	$0x01, %cx
 	movb	$0x0e, %ah
 	int	$0x10
@@ -277,6 +278,75 @@
 loader_panic_mess: .string "Wrong loader, giving up..."
 
 loader_ok:
+	/* check for long mode. */
+	/* we have to do this before the VESA setup, otherwise the user
+	   can't see the error message. */
+	
+	pushw	%ds
+	movw	%cs,%ax
+	movw	%ax,%ds
+	
+	/* minimum CPUID flags for x86-64 */
+	/* see http://www.x86-64.org/lists/discuss/msg02971.html */		
+#define SSE_MASK ((1<<25)|(1<<26))
+#define REQUIRED_MASK1 ((1<<0)|(1<<3)|(1<<4)|(1<<5)|(1<<6)|(1<<8)|(1<<11)| \
+					   (1<<13)|(1<<15)|(1<<24)|(1<<29))
+
+	pushfl				/* standard way to check for cpuid */
+	popl	%eax
+	movl	%eax,%ebx
+	xorl	$0x200000,%eax
+	pushl	%eax
+	popfl
+	pushfl
+	popl	%eax
+	cmpl	%eax,%ebx
+	jz	no_longmode		/* cpu has no cpuid */
+	movl	$0x80000000,%eax
+	cpuid
+	cmpl	$0x80000001,%eax
+	jb	no_longmode		/* no extended cpuid */
+	xor	%di,%di
+	cmpl	$0x68747541,%ebx	/* AuthenticAMD */
+	jnz	noamd
+	cmpl	$0x69746e65,%edx
+	jnz	noamd
+	cmpl	$0x444d4163,%ecx
+	jnz	noamd
+	mov	$1,%di			/* cpu is from AMD */
+noamd:		
+	movl	$0x80000001,%eax		
+	cpuid
+	andl	$REQUIRED_MASK1,%edx
+	xorl	$REQUIRED_MASK1,%edx
+	jnz	no_longmode
+sse_test:		
+	movl	$1,%eax
+	cpuid
+	andl	$SSE_MASK,%edx
+	cmpl	$SSE_MASK,%edx
+	je	sse_ok
+	test	%di,%di
+	jz	no_longmode	/* only try to force SSE on AMD */ 
+	movl	$0xc0010015,%ecx	/* HWCR */
+	rdmsr
+	btr	$15,%eax	/* enable SSE */
+	wrmsr
+	xor	%di,%di		/* don't loop */
+	jmp	sse_test	/* try again */	
+no_longmode:
+	call	beep
+	lea	long_mode_panic,%si
+	call	prtstr
+no_longmode_loop:		
+	jmp	no_longmode_loop
+long_mode_panic:
+	.string "Your CPU does not support long mode. Use a 32bit distribution."
+	.byte 0
+	
+sse_ok:
+	popw	%ds
+	
 # Get memory size (extended mem, kB)
 
 	xorl	%eax, %eax
diff -urN linux-2.4.21-pre5/arch/x86_64/defconfig linux-2.4.21-pre6/arch/x86_64/defconfig
--- linux-2.4.21-pre5/arch/x86_64/defconfig	Wed Mar 26 16:11:28 2003
+++ linux-2.4.21-pre6/arch/x86_64/defconfig	Wed Mar 26 16:11:34 2003
@@ -377,6 +377,7 @@
 # CONFIG_ELMC is not set
 # CONFIG_ELMC_II is not set
 CONFIG_VORTEX=y
+# CONFIG_TYPHOON is not set
 # CONFIG_LANCE is not set
 # CONFIG_NET_VENDOR_SMC is not set
 # CONFIG_NET_VENDOR_RACAL is not set
@@ -390,7 +391,7 @@
 #
 # CONFIG_ACENIC is not set
 # CONFIG_DL2K is not set
-CONFIG_E1000=m
+# CONFIG_E1000 is not set
 # CONFIG_MYRI_SBUS is not set
 # CONFIG_NS83820 is not set
 # CONFIG_HAMACHI is not set
@@ -696,8 +697,8 @@
 # CONFIG_CHECKING is not set
 # CONFIG_INIT_DEBUG is not set
 # CONFIG_IOMMU_DEBUG is not set
-# CONFIG_IOMMU_LEAK is not set
-CONFIG_DEBUG_STACKOVERFLOW=y
+CONFIG_IOMMU_LEAK=y
+# CONFIG_DEBUG_STACKOVERFLOW is not set
 
 #
 # Library routines
diff -urN linux-2.4.21-pre5/arch/x86_64/ia32/fpu32.c linux-2.4.21-pre6/arch/x86_64/ia32/fpu32.c
--- linux-2.4.21-pre5/arch/x86_64/ia32/fpu32.c	Wed Mar 26 16:11:28 2003
+++ linux-2.4.21-pre6/arch/x86_64/ia32/fpu32.c	Wed Mar 26 16:11:34 2003
@@ -2,7 +2,7 @@
  * Copyright 2002 Andi Kleen, SuSE Labs.
  * FXSAVE<->i387 conversion support. Based on code by Gareth Hughes.
  * This is used for ptrace, signals and coredumps in 32bit emulation.
- * $Id: fpu32.c,v 1.3 2003/01/31 18:57:41 ak Exp $
+ * $Id: fpu32.c,v 1.7 2003/03/14 16:06:35 ak Exp $
  */ 
 
 #include <linux/sched.h>
@@ -77,17 +77,20 @@
 	struct _fpxreg *to;
 	struct _fpreg *from;
 	int i;
-	int err; 
-	__u32 v;
+	int err = 0; 
+	u32 v;
 
-	err = __get_user(fxsave->cwd, &buf->cw); 
-	err |= __get_user(fxsave->swd, &buf->sw);
-	err |= __get_user(fxsave->twd, &buf->tag);
+#define G(num,val) err |= __get_user(val, num + (u32 *)buf)
+	G(0, fxsave->cwd);
+	G(1, fxsave->swd);
+	G(2, fxsave->twd);
 	fxsave->twd = twd_i387_to_fxsr(fxsave->twd);
-	err |= __get_user(fxsave->rip, &buf->ipoff); 
-	err |= __get_user(fxsave->rdp, &buf->dataoff); 
-	err |= __get_user(v, &buf->cssel); 
-	fxsave->fop = v >> 16;
+	G(3, fxsave->rip);
+	G(4, v);
+	fxsave->fop = v>>16;	/* cs ignored */
+	G(5, fxsave->rdp);
+	/* 6: ds ignored */
+#undef G
 	if (err) 
 		return -1; 
 
@@ -109,21 +112,29 @@
 	struct _fpreg *to;
 	struct _fpxreg *from;
 	int i;
-	u32 ds; 
-	int err; 
+	u16 cs,ds; 
+	int err = 0; 
 
-	err = __put_user((unsigned long)fxsave->cwd | 0xffff0000, &buf->cw);
-	err |= __put_user((unsigned long)fxsave->swd | 0xffff0000, &buf->sw);
-	err |= __put_user((u32)fxsave->rip, &buf->ipoff); 
-	err |= __put_user((u32)(regs->cs | ((u32)fxsave->fop << 16)), 
-			  &buf->cssel); 
-	err |= __put_user((u32)twd_fxsr_to_i387(fxsave), &buf->tag); 
-	err |= __put_user((u32)fxsave->rdp, &buf->dataoff); 
-	if (tsk == current) 
-		asm("movl %%ds,%0 " : "=r" (ds)); 
-	else /* ptrace. task has stopped. */
+	if (tsk == current) {
+		/* should be actually ds/cs at fpu exception time,
+		   but that information is not available in 64bit mode. */
+		asm("movw %%ds,%0 " : "=r" (ds)); 
+		asm("movw %%cs,%0 " : "=r" (cs)); 		
+	} else { /* ptrace. task has stopped. */
 		ds = tsk->thread.ds;
-	err |= __put_user(ds, &buf->datasel); 
+		cs = regs->cs;
+	} 
+
+#define P(num,val) err |= __put_user(val, num + (u32 *)buf)
+	P(0, (u32)fxsave->cwd | 0xffff0000);
+	P(1, (u32)fxsave->swd | 0xffff0000);
+	P(2, twd_fxsr_to_i387(fxsave));
+	P(3, (u32)fxsave->rip);
+	P(4,  cs | ((u32)fxsave->fop) << 16); 
+	P(5, fxsave->rdp);
+	P(6, 0xffff0000 | ds);
+#undef P
+
 	if (err) 
 		return -1; 
 
@@ -144,9 +155,8 @@
 				     &buf->_fxsr_env[0],
 				     sizeof(struct i387_fxsave_struct)))
 			return -1;
-	} 
 	tsk->thread.i387.fxsave.mxcsr &= 0xffbf;
-	current->used_math = 1;
+	}
 	return convert_fxsr_from_user(&tsk->thread.i387.fxsave, buf);
 }  
 
@@ -157,12 +167,11 @@
 {
 	int err = 0;
 
-	if (!tsk->used_math) 
-		return 0;
-	tsk->used_math = 0; 
-	unlazy_fpu(tsk);
+	init_fpu(tsk);
 	if (convert_fxsr_to_user(buf, &tsk->thread.i387.fxsave, regs, tsk))
 		return -1;
+	if (fsave) 
+		return 0;
 	err |= __put_user(tsk->thread.i387.fxsave.swd, &buf->status);
 	if (fsave) 
 		return err ? -1 : 1; 	
diff -urN linux-2.4.21-pre5/arch/x86_64/ia32/ia32_ioctl.c linux-2.4.21-pre6/arch/x86_64/ia32/ia32_ioctl.c
--- linux-2.4.21-pre5/arch/x86_64/ia32/ia32_ioctl.c	Wed Mar 26 16:11:28 2003
+++ linux-2.4.21-pre6/arch/x86_64/ia32/ia32_ioctl.c	Wed Mar 26 16:11:34 2003
@@ -1,4 +1,4 @@
-/* $Id: ia32_ioctl.c,v 1.28 2003/02/11 07:24:59 ak Exp $
+/* $Id: ia32_ioctl.c,v 1.30 2003/03/12 06:03:23 ak Exp $
  * ioctl32.c: Conversion between 32bit and 64bit native ioctls.
  *
  * Copyright (C) 1997-2000  Jakub Jelinek  (jakub@redhat.com)
@@ -38,6 +38,7 @@
 #include <linux/cdrom.h>
 #include <linux/loop.h>
 #include <linux/auto_fs.h>
+#include <linux/auto_fs4.h>
 #include <linux/devfs_fs.h>
 #include <linux/tty.h>
 #include <linux/vt_kern.h>
@@ -2940,6 +2941,14 @@
 	return err;	
 }
 
+/* Bluetooth ioctls */
+#define HCIUARTSETPROTO        _IOW('U', 200, int)
+#define HCIUARTGETPROTO        _IOR('U', 201, int)
+
+#define BNEPCONNADD    _IOW('B', 200, int)
+#define BNEPCONNDEL    _IOW('B', 201, int)
+#define BNEPGETCONNLIST        _IOR('B', 210, int)
+#define BNEPGETCONNINFO        _IOR('B', 211, int)
 
 #define REISERFS_IOC_UNPACK32               _IOW(0xCD,1,int)
 
@@ -4119,6 +4128,7 @@
 COMPATIBLE_IOCTL(AUTOFS_IOC_CATATONIC)
 COMPATIBLE_IOCTL(AUTOFS_IOC_PROTOVER)
 COMPATIBLE_IOCTL(AUTOFS_IOC_EXPIRE)
+COMPATIBLE_IOCTL(AUTOFS_IOC_EXPIRE_MULTI)
 /* DEVFS */
 COMPATIBLE_IOCTL(DEVFSDIOC_GET_PROTO_REV)
 COMPATIBLE_IOCTL(DEVFSDIOC_SET_EVENT_MASK)
diff -urN linux-2.4.21-pre5/arch/x86_64/ia32/ia32_signal.c linux-2.4.21-pre6/arch/x86_64/ia32/ia32_signal.c
--- linux-2.4.21-pre5/arch/x86_64/ia32/ia32_signal.c	Wed Mar 26 16:11:28 2003
+++ linux-2.4.21-pre6/arch/x86_64/ia32/ia32_signal.c	Wed Mar 26 16:11:34 2003
@@ -7,7 +7,7 @@
  *  2000-06-20  Pentium III FXSR, SSE support by Gareth Hughes
  *  2000-12-*   x86-64 compatibility mode signal handling by Andi Kleen
  * 
- *  $Id: ia32_signal.c,v 1.25 2002/11/28 06:05:28 ak Exp $
+ *  $Id: ia32_signal.c,v 1.31 2003/03/12 08:22:12 ak Exp $
  */
 
 #include <linux/sched.h>
@@ -33,6 +33,7 @@
 #include <asm/fpu32.h>
 
 #define ptr_to_u32(x) ((u32)(u64)(x))	/* avoid gcc warning */ 
+#define u32_to_ptr(x) ((void *)(u64)(x))
 
 #define DEBUG_SIG 0
 
@@ -45,9 +46,16 @@
 {
 	if (!access_ok (VERIFY_WRITE, to, sizeof(siginfo_t)))
 		return -EFAULT;
-	if (from->si_code < 0)
-		return __copy_to_user(to, from, sizeof(siginfo_t));
-	else {
+	if (from->si_code < 0) { 
+		/* the only field that's different is the alignment
+		   of the pointer in sigval_t. Move that 4 bytes down including
+		   padding. */
+		memmove(&((siginfo_t32 *)&from)->si_int,
+			&from->si_int, 
+			sizeof(siginfo_t) - offsetof(siginfo_t, si_int));
+		/* last 4 bytes stay the same */
+		return __copy_to_user(to, from, sizeof(siginfo_t32));
+	} else {
 		int err;
 
 		/* If you change siginfo_t structure, please be sure
@@ -57,7 +65,7 @@
 		   3 ints plus the relevant union member.  */
 		err = __put_user(from->si_signo, &to->si_signo);
 		err |= __put_user(from->si_errno, &to->si_errno);
-		err |= __put_user((short)from->si_code, &to->si_code);
+		err |= __put_user(from->si_code, &to->si_code);
 		/* First 32bits of unions are always present.  */
 		err |= __put_user(from->si_pid, &to->si_pid);
 		switch (from->si_code >> 16) {
@@ -104,20 +112,25 @@
 	stack_t uss,uoss; 
 	int ret;
 	mm_segment_t seg; 
+	if (uss_ptr) {
+		u32 val32;
+		memset(&uss, 0, sizeof(stack_t));
 	if (!access_ok(VERIFY_READ,uss_ptr,sizeof(stack_ia32_t)) ||
-	    __get_user(ptr_to_u32(uss.ss_sp), &uss_ptr->ss_sp) ||
-	    __get_user((u32)uss.ss_flags, &uss_ptr->ss_flags) ||
-	    __get_user((u32)uss.ss_size, &uss_ptr->ss_size))
+		    __get_user(val32, &uss_ptr->ss_sp) ||
+		    __get_user(uss.ss_flags, &uss_ptr->ss_flags) ||
+		    __get_user(uss.ss_size, &uss_ptr->ss_size))
 		return -EFAULT;
+		uss.ss_sp = u32_to_ptr(val32);	
+	}
 	seg = get_fs(); 
 	set_fs(KERNEL_DS); 
-	ret = do_sigaltstack(&uss, &uoss, regs.rsp);
+	ret = do_sigaltstack(uss_ptr ? &uss : NULL, &uoss, regs.rsp);
 	set_fs(seg); 
 	if (ret >= 0 && uoss_ptr)  {
-		if (!access_ok(VERIFY_WRITE,uss_ptr,sizeof(stack_ia32_t)) ||
-		    __put_user(ptr_to_u32(uss.ss_sp), &uss_ptr->ss_sp) ||
-		    __put_user((u32)uss.ss_flags, &uss_ptr->ss_flags) ||
-		    __put_user((u32)uss.ss_size, &uss_ptr->ss_size))
+		if (!access_ok(VERIFY_WRITE,uoss_ptr,sizeof(stack_ia32_t)) ||
+		    __put_user(ptr_to_u32(uoss.ss_sp), &uoss_ptr->ss_sp) ||
+		    __put_user((u32)uoss.ss_flags, &uoss_ptr->ss_flags) ||
+		    __put_user((u32)uoss.ss_size, &uoss_ptr->ss_size))
 			ret = -EFAULT;
 	} 	
 	return ret;	
@@ -331,8 +344,12 @@
 	tmp = save_i387_ia32(current, fpstate, regs, 0);
 	if (tmp < 0)
 	  err = -EFAULT;
-	else
+	else { 
+		/* trigger finit in signal handler */
+		current->used_math = 0;
+		stts();
 	  err |= __put_user((u32)(u64)(tmp ? fpstate : NULL), &sc->fpstate);
+	} 
 
 	/* non-iBCS2 extensions.. */
 	err |= __put_user(mask, &sc->oldmask);
diff -urN linux-2.4.21-pre5/arch/x86_64/ia32/ipc32.c linux-2.4.21-pre6/arch/x86_64/ia32/ipc32.c
--- linux-2.4.21-pre5/arch/x86_64/ia32/ipc32.c	Wed Mar 26 16:11:28 2003
+++ linux-2.4.21-pre6/arch/x86_64/ia32/ipc32.c	Wed Mar 26 16:11:34 2003
@@ -227,22 +227,25 @@
 		set_fs(old_fs);
 
 		if (version == IPC_64) {
-			struct semid64_ds32 *usp64 = (struct semid64_ds32 *) A(pad);
-
-			if (!access_ok(VERIFY_WRITE, usp64, sizeof(*usp64))) {
-				err = -EFAULT;
-				break;
-			}
-			err2 = __put_user(s.sem_perm.key, &usp64->sem_perm.key);
-			err2 |= __put_user(s.sem_perm.uid, &usp64->sem_perm.uid);
-			err2 |= __put_user(s.sem_perm.gid, &usp64->sem_perm.gid);
-			err2 |= __put_user(s.sem_perm.cuid, &usp64->sem_perm.cuid);
-			err2 |= __put_user(s.sem_perm.cgid, &usp64->sem_perm.cgid);
-			err2 |= __put_user(s.sem_perm.mode, &usp64->sem_perm.mode);
-			err2 |= __put_user(s.sem_perm.seq, &usp64->sem_perm.seq);
-			err2 |= __put_user(s.sem_otime, &usp64->sem_otime);
-			err2 |= __put_user(s.sem_ctime, &usp64->sem_ctime);
-			err2 |= __put_user(s.sem_nsems, &usp64->sem_nsems);
+			/* This works, but the equivalent __put_user code 
+			   does not. Why? */
+			struct semid64_ds32 u64; 
+			memset(&u64, 0, sizeof(u64));
+#define C(x) u64.x = s.x
+			C(sem_perm.key);
+			C(sem_perm.uid);
+			C(sem_perm.gid);
+			C(sem_perm.cuid);
+			C(sem_perm.cgid);
+			C(sem_perm.mode);
+			C(sem_perm.seq);
+			C(sem_otime);
+			C(sem_ctime);
+			C(sem_nsems);
+#undef C
+			err2 = copy_to_user((void *)A(pad), &u64, sizeof(struct semid64_ds32));
+			if (err2) 
+				err2 = -EFAULT;
 		} else {
 			struct semid_ds32 *usp32 = (struct semid_ds32 *) A(pad);
 
@@ -607,6 +610,10 @@
 			err = -EFAULT;
 		break;
 
+	default:
+		err = -EINVAL;
+		break;
+
 	}
 	return err;
 }
diff -urN linux-2.4.21-pre5/arch/x86_64/ia32/ptrace32.c linux-2.4.21-pre6/arch/x86_64/ia32/ptrace32.c
--- linux-2.4.21-pre5/arch/x86_64/ia32/ptrace32.c	Thu Nov 28 15:53:12 2002
+++ linux-2.4.21-pre6/arch/x86_64/ia32/ptrace32.c	Wed Mar 26 16:11:34 2003
@@ -8,7 +8,7 @@
  * This allows to access 64bit processes too; but there is no way to see the extended 
  * register contents.
  *
- * $Id: ptrace32.c,v 1.13 2002/07/18 13:44:12 ak Exp $
+ * $Id: ptrace32.c,v 1.16 2003/03/14 16:06:35 ak Exp $
  */ 
 
 #include <linux/kernel.h>
@@ -73,8 +73,16 @@
 		stack[offsetof(struct pt_regs, eflags)/8] = val & 0x44dd5; 
 		break;
 
-	case offsetof(struct user32, u_debugreg[0]) ... offsetof(struct user32, u_debugreg[6]):
-		child->thread.debugreg[(regno-offsetof(struct user32, u_debugreg[0]))/4] = val; 
+	case offsetof(struct user32, u_debugreg[4]): 
+	case offsetof(struct user32, u_debugreg[5]):
+		return -EIO;
+
+	case offsetof(struct user32, u_debugreg[0]) ...
+	     offsetof(struct user32, u_debugreg[3]):
+	case offsetof(struct user32, u_debugreg[6]):
+		child->thread.debugreg
+			[(regno-offsetof(struct user32, u_debugreg[0]))/4] 
+			= val; 
 		break; 
 
 	case offsetof(struct user32, u_debugreg[7]):
@@ -277,7 +285,6 @@
 			ret = -EIO;
 			break;
 		}
-		empty_fpu(child); 
 		ret = 0; 
 		for ( i = 0; i <= 16*4; i += sizeof(u32) ) {
 			ret |= __get_user(tmp, (u32 *) (unsigned long) data);
@@ -287,33 +294,47 @@
 		break;
 	}
 
-	case PTRACE_SETFPREGS:
-		empty_fpu(child); 
+	case PTRACE_GETFPREGS:
+		ret = -EIO; 
+		if (!access_ok(VERIFY_READ, (void *)(u64)data, 
+			       sizeof(struct user_i387_struct)))
+			break;
 		save_i387_ia32(child, (void *)(u64)data, childregs, 1);
 		ret = 0; 
 		break;
 
-	case PTRACE_GETFPREGS:
-		empty_fpu(child); 
-		restore_i387_ia32(child, (void *)(u64)data, 1);
+	case PTRACE_SETFPREGS:
+		ret = -EIO;
+		if (!access_ok(VERIFY_WRITE, (void *)(u64)data, 
+			       sizeof(struct user_i387_struct)))
+			break;
 		ret = 0;
+		/* don't check EFAULT to be bug-to-bug compatible to i386 */
+		restore_i387_ia32(child, (void *)(u64)data, 1);
 		break;
 
 	case PTRACE_GETFPXREGS: { 
 		struct user32_fxsr_struct *u = (void *)(u64)data; 
-		empty_fpu(child); 
-		ret = copy_to_user(u, &child->thread.i387.fxsave, sizeof(*u));
-		ret |= __put_user(childregs->cs, &u->fcs);
-		ret |= __put_user(child->thread.ds, &u->fos); 
-		if (ret) 
+		init_fpu(child); 
+		ret = -EIO;
+		if (!access_ok(VERIFY_WRITE, u, sizeof(*u)))
+			break;
 			ret = -EFAULT;
+		if (__copy_to_user(u, &child->thread.i387.fxsave, sizeof(*u)))
+			break;
+		ret = __put_user(childregs->cs, &u->fcs);
+		ret |= __put_user(child->thread.ds, &u->fos); 
 		break; 
 	} 
 	case PTRACE_SETFPXREGS: { 
 		struct user32_fxsr_struct *u = (void *)(u64)data; 
-		empty_fpu(child); 
-		/* no error checking to be bug to bug compatible with i386 */ 
-		copy_from_user(&child->thread.i387.fxsave, u, sizeof(*u));
+		unlazy_fpu(child);
+		ret = -EIO;
+		if (!access_ok(VERIFY_READ, u, sizeof(*u)))
+			break;
+		/* no checking to be bug-to-bug compatible with i386 */
+		__copy_from_user(&child->thread.i387.fxsave, u, sizeof(*u));
+		child->used_math = 1;
 	        child->thread.i387.fxsave.mxcsr &= 0xffbf;
 		ret = 0; 
 		break; 
diff -urN linux-2.4.21-pre5/arch/x86_64/ia32/sys_ia32.c linux-2.4.21-pre6/arch/x86_64/ia32/sys_ia32.c
--- linux-2.4.21-pre5/arch/x86_64/ia32/sys_ia32.c	Wed Mar 26 16:11:28 2003
+++ linux-2.4.21-pre6/arch/x86_64/ia32/sys_ia32.c	Wed Mar 26 16:11:34 2003
@@ -15,7 +15,8 @@
  * environment. In 2.5 most of this should be moved to a generic directory. 
  *
  * This file assumes that there is a hole at the end of user address space.
- * $Id: sys_ia32.c,v 1.49 2003/01/14 14:29:59 ak Exp $
+ *
+ * $Id: sys_ia32.c,v 1.53 2003/03/12 09:45:06 ak Exp $
  */
 
 #include <linux/config.h>
@@ -934,21 +935,23 @@
 asmlinkage long
 sys32_nanosleep(struct timespec32 *rqtp, struct timespec32 *rmtp)
 {
-	struct timespec t;
+	struct timespec t, tout;
 	int ret;
 	mm_segment_t old_fs = get_fs ();
 	
+	if (rqtp) { 
 	if (verify_area(VERIFY_READ, rqtp, sizeof(struct timespec32)) ||
 	    __get_user (t.tv_sec, &rqtp->tv_sec) ||
 	    __get_user (t.tv_nsec, &rqtp->tv_nsec))
 		return -EFAULT;
+	}
 	set_fs (KERNEL_DS);
-	ret = sys_nanosleep(&t, rmtp ? &t : NULL);
+	ret = sys_nanosleep(rqtp ? &t : NULL, rmtp ? &tout : NULL);
 	set_fs (old_fs);
 	if (rmtp && ret == -EINTR) {
 		if (verify_area(VERIFY_WRITE, rmtp, sizeof(struct timespec32)) ||
-		    __put_user (t.tv_sec, &rmtp->tv_sec) ||
-	    	    __put_user (t.tv_nsec, &rmtp->tv_nsec))
+		    __put_user (tout.tv_sec, &rmtp->tv_sec) ||
+	    	    __put_user (tout.tv_nsec, &rmtp->tv_nsec))
 			return -EFAULT;
 	}
 	return ret;
@@ -967,10 +970,13 @@
 
 	/* Get the "struct iovec" from user memory */
 
+	*errp = 0; 
 	if (!count)
 		return 0;
+	*errp = -EINVAL;
 	if (count > UIO_MAXIOV)
 		return(struct iovec *)0;
+	*errp = -EFAULT;
 	if(verify_area(VERIFY_READ, iov32, sizeof(struct iovec32)*count))
 		return(struct iovec *)0;
 	if (count > UIO_FASTIOV) {
@@ -1605,8 +1611,8 @@
 	if (s->si_signo >= SIGRTMIN) {
 		d->si_pid = s->si_pid;
 		d->si_uid = s->si_uid;
-		/* XXX: Ouch, how to find this out??? */
-		d->si_int = s->si_int;
+		memcpy(&d->si_int, &s->si_int, 
+		       sizeof(siginfo_t) - offsetof(siginfo_t,si_int));
 	} else switch (s->si_signo) {
 	/* XXX: What about POSIX1.b timers */
 	case SIGCHLD:
@@ -1643,8 +1649,9 @@
 	if (s->si_signo >= SIGRTMIN) {
 		d->si_pid = s->si_pid;
 		d->si_uid = s->si_uid;
-		/* XXX: Ouch, how to find this out??? */
-		d->si_int = s->si_int;
+		memcpy(&d->si_int,
+		       &s->si_int,
+		       sizeof(siginfo_t) - offsetof(siginfo_t, si_int)); 
 	} else switch (s->si_signo) {
 	/* XXX: What about POSIX1.b timers */
 	case SIGCHLD:
@@ -2119,18 +2126,23 @@
 asmlinkage long sys32_execve(char *name, u32 argv, u32 envp, struct pt_regs regs)
 { 
 	mm_segment_t oldseg; 
-	char **buf; 
-	int na,ne;
+	char **buf = NULL; 
+	int na = 0,ne = 0;
 	int ret;
-	unsigned sz; 
+	unsigned sz = 0; 
 	
+	if (argv) {
 	na = nargs(argv, NULL); 
 	if (na < 0) 
 		return -EFAULT; 
+	} 	
+	if (envp) { 
 	ne = nargs(envp, NULL); 
 	if (ne < 0) 
 		return -EFAULT; 
+	}
 
+	if (argv || envp) { 
 	sz = (na+ne)*sizeof(void *); 
 	if (sz > PAGE_SIZE) 
 		buf = vmalloc(sz); 
@@ -2138,14 +2150,19 @@
 		buf = kmalloc(sz, GFP_KERNEL); 
 	if (!buf)
 		return -ENOMEM; 
+	} 
 	
+	if (argv) { 
 	ret = nargs(argv, buf);
 	if (ret < 0)
 		goto free;
+	}
 
+	if (envp) { 
 	ret = nargs(envp, buf + na); 
 	if (ret < 0)
 		goto free; 
+	}
 
 	name = getname(name); 
 	ret = PTR_ERR(name); 
@@ -2154,7 +2171,7 @@
 
 	oldseg = get_fs(); 
 	set_fs(KERNEL_DS);
-	ret = do_execve(name, buf, buf+na, &regs);  
+	ret = do_execve(name, argv ? buf : NULL, envp ? buf+na : NULL, &regs);  
 	set_fs(oldseg); 
 
 	if (ret == 0)
@@ -2163,10 +2180,12 @@
 	putname(name);
  
 free:
+	if (argv || envp) { 
 	if (sz > PAGE_SIZE)
 		vfree(buf); 
 	else
 		kfree(buf);
+	}
 	return ret; 
 } 
 
@@ -2526,7 +2545,7 @@
 
 static int __init ia32_init (void)
 {
-	printk("IA32 emulation $Id: sys_ia32.c,v 1.49 2003/01/14 14:29:59 ak Exp $\n");  
+	printk("IA32 emulation $Id: sys_ia32.c,v 1.53 2003/03/12 09:45:06 ak Exp $\n");  
 	ia32_exec_domain.signal_map = default_exec_domain.signal_map;
 	ia32_exec_domain.signal_invmap = default_exec_domain.signal_invmap;
 	register_exec_domain(&ia32_exec_domain);
diff -urN linux-2.4.21-pre5/arch/x86_64/kernel/apic.c linux-2.4.21-pre6/arch/x86_64/kernel/apic.c
--- linux-2.4.21-pre5/arch/x86_64/kernel/apic.c	Wed Mar 26 16:11:28 2003
+++ linux-2.4.21-pre6/arch/x86_64/kernel/apic.c	Wed Mar 26 16:11:34 2003
@@ -377,10 +377,10 @@
 	value = apic_read(APIC_LVT0) & APIC_LVT_MASKED;
 	if (!smp_processor_id() && (pic_mode || !value)) {
 		value = APIC_DM_EXTINT;
-		printk("enabled ExtINT on CPU#%d\n", smp_processor_id());
+		Dprintk("enabled ExtINT on CPU#%d\n", smp_processor_id());
 	} else {
 		value = APIC_DM_EXTINT | APIC_LVT_MASKED;
-		printk("masked ExtINT on CPU#%d\n", smp_processor_id());
+		Dprintk("masked ExtINT on CPU#%d\n", smp_processor_id());
 	}
 	apic_write_around(APIC_LVT0, value);
 
@@ -400,7 +400,7 @@
 		if (maxlvt > 3)			/* Due to the Pentium erratum 3AP. */
 			apic_write(APIC_ESR, 0);
 		value = apic_read(APIC_ESR);
-		printk("ESR value before enabling vector: %08x\n", value);
+		Dprintk("ESR value before enabling vector: %08x\n", value);
 
 		value = ERROR_APIC_VECTOR;	/* enables sending errors */
 		apic_write_around(APIC_LVTERR, value);
@@ -410,7 +410,7 @@
 		if (maxlvt > 3)
 			apic_write(APIC_ESR, 0);
 		value = apic_read(APIC_ESR);
-		printk("ESR value after enabling vector: %08x\n", value);
+		Dprintk("ESR value after enabling vector: %08x\n", value);
 	} else {
 		if (esr_disable)
 			/*
@@ -419,9 +419,9 @@
 			 * ESR disabled - we can't do anything useful with the
 			 * errors anyway - mbligh
 			 */
-			printk("Leaving ESR disabled.\n");
+			Dprintk("Leaving ESR disabled.\n");
 		else
-			printk("No ESR for 82489DX.\n");
+			Dprintk("No ESR for 82489DX.\n");
 	}
 
 	if (nmi_watchdog == NMI_LOCAL_APIC)
diff -urN linux-2.4.21-pre5/arch/x86_64/kernel/bluesmoke.c linux-2.4.21-pre6/arch/x86_64/kernel/bluesmoke.c
--- linux-2.4.21-pre5/arch/x86_64/kernel/bluesmoke.c	Wed Mar 26 16:11:28 2003
+++ linux-2.4.21-pre6/arch/x86_64/kernel/bluesmoke.c	Wed Mar 26 16:11:34 2003
@@ -105,7 +105,7 @@
 
 void do_machine_check(struct pt_regs * regs, long error_code)
 {
-	notify_die(DIE_NMI, "machine check", regs, error_code);
+	notify_die(DIE_NMI, "machine check", regs, error_code, 255, SIGKILL);
 	machine_check_vector(regs, error_code);
 }
 
diff -urN linux-2.4.21-pre5/arch/x86_64/kernel/entry.S linux-2.4.21-pre6/arch/x86_64/kernel/entry.S
--- linux-2.4.21-pre5/arch/x86_64/kernel/entry.S	Wed Mar 26 16:11:28 2003
+++ linux-2.4.21-pre6/arch/x86_64/kernel/entry.S	Wed Mar 26 16:11:34 2003
@@ -5,7 +5,7 @@
  *  Copyright (C) 2000, 2001, 2002  Andi Kleen SuSE Labs
  *  Copyright (C) 2000  Pavel Machek <pavel@suse.cz>
  * 
- *  $Id: entry.S,v 1.89 2003/02/11 03:08:27 kkeil Exp $		
+ *  $Id: entry.S,v 1.93 2003/02/20 12:52:08 ak Exp $		
  */
 
 /*
@@ -86,6 +86,7 @@
 2:
 	movq %rsp,%rdi	
 	call syscall_trace
+	GET_CURRENT(%rcx)
 	jmp 1b
 
 /*
@@ -310,8 +311,13 @@
 	je   1f
 	swapgs
 1:	cld
+#ifdef CONFIG_KGDB
+	SAVE_ALL
+	movq %rsp,%rdi
+#else			
 	SAVE_ARGS
 	leaq -ARGOFFSET(%rsp),%rdi	# arg1 for handler
+#endif	
 	addl $1,PDAREF(pda_irqcount)	# XXX: should be merged with irq.c irqcount
 	movq PDAREF(pda_irqstackptr),%rax
 	cmoveq %rax,%rsp
@@ -518,7 +524,7 @@
 	swapgs
 gs_change:	
 	movl %edi,%gs	
-2:	sfence
+2:	mfence
 	swapgs
 	popf
 	ret
@@ -609,18 +615,6 @@
 	ret
 
 ENTRY(page_fault)
-#ifdef CONFIG_KDB
-	pushq %rcx
-	pushq %rdx
-	pushq %rax
-	movl  $473,%ecx
-	rdmsr
-	andl  $0xfffffffe,%eax		/* Disable last branch recording */
-	wrmsr
-	popq  %rax
-	popq  %rdx
-	popq  %rcx
-#endif	
 	errorentry do_page_fault
 
 ENTRY(coprocessor_error)
diff -urN linux-2.4.21-pre5/arch/x86_64/kernel/head.S linux-2.4.21-pre6/arch/x86_64/kernel/head.S
--- linux-2.4.21-pre5/arch/x86_64/kernel/head.S	Wed Mar 26 16:11:28 2003
+++ linux-2.4.21-pre6/arch/x86_64/kernel/head.S	Wed Mar 26 16:11:34 2003
@@ -6,7 +6,7 @@
  *  Copyright (C) 2000 Karsten Keil <kkeil@suse.de>
  *  Copyright (C) 2001,2002 Andi Kleen <ak@suse.de>
  *
- *  $Id: head.S,v 1.54 2002/11/14 16:26:55 ak Exp $
+ *  $Id: head.S,v 1.55 2003/02/11 12:29:15 ak Exp $
  */
 
 
@@ -116,7 +116,6 @@
 	jnz   second	
 	
 	/* Load new GDT with the 64bit segment using 32bit descriptor */
-	/* to avoid 32bit relocations we use fixed adresses here */
 	movl	$0x100F00, %eax
 	lgdt	(%eax)
 
diff -urN linux-2.4.21-pre5/arch/x86_64/kernel/i387.c linux-2.4.21-pre6/arch/x86_64/kernel/i387.c
--- linux-2.4.21-pre5/arch/x86_64/kernel/i387.c	Thu Nov 28 15:53:12 2002
+++ linux-2.4.21-pre6/arch/x86_64/kernel/i387.c	Wed Mar 26 16:11:34 2003
@@ -25,19 +25,16 @@
 
 extern int exception_trace;
 
-/*
- * The _current_ task is using the FPU for the first time
- * so initialize it and set the mxcsr to its default
- * value at reset if we support XMM instructions and then
- * remeber the current task has used the FPU.
- */
-void init_fpu(void)
+void init_fpu(struct task_struct *child)
 {
-	struct task_struct *me = current;
-	memset(&me->thread.i387.fxsave, 0, sizeof(struct i387_fxsave_struct));
-	me->thread.i387.fxsave.cwd = 0x37f;
-	me->thread.i387.fxsave.mxcsr = 0x1f80;
-	me->used_math = 1;
+	if (child->used_math) { 
+		unlazy_fpu(child);
+		return;
+	}
+	memset(&child->thread.i387.fxsave, 0, sizeof(struct i387_fxsave_struct));
+	child->thread.i387.fxsave.cwd = 0x37f;
+	child->thread.i387.fxsave.mxcsr = 0x1f80;
+	child->used_math = 1;
 }
 
 /*
@@ -81,7 +78,7 @@
 
 int get_fpregs(struct user_i387_struct *buf, struct task_struct *tsk)
 {
-	empty_fpu(tsk);
+	init_fpu(tsk);
 	return __copy_to_user((void *)buf, &tsk->thread.i387.fxsave,
 			       sizeof(struct user_i387_struct)) ? -EFAULT : 0;
 }
diff -urN linux-2.4.21-pre5/arch/x86_64/kernel/i8259.c linux-2.4.21-pre6/arch/x86_64/kernel/i8259.c
--- linux-2.4.21-pre5/arch/x86_64/kernel/i8259.c	Wed Mar 26 16:11:28 2003
+++ linux-2.4.21-pre6/arch/x86_64/kernel/i8259.c	Wed Mar 26 16:11:34 2003
@@ -404,7 +404,7 @@
 	 */
 	for (i = 0; i < NR_IRQS; i++) {
 		int vector = FIRST_EXTERNAL_VECTOR + i;
-		if (vector != IA32_SYSCALL_VECTOR && vector != KDBENTER_VECTOR) {
+		if (vector != IA32_SYSCALL_VECTOR) {
 			set_intr_gate(vector, interrupt[i]);
 		}
 	}
diff -urN linux-2.4.21-pre5/arch/x86_64/kernel/io_apic.c linux-2.4.21-pre6/arch/x86_64/kernel/io_apic.c
--- linux-2.4.21-pre5/arch/x86_64/kernel/io_apic.c	Thu Nov 28 15:53:12 2002
+++ linux-2.4.21-pre6/arch/x86_64/kernel/io_apic.c	Wed Mar 26 16:11:34 2003
@@ -728,8 +728,10 @@
 
 void __init UNEXPECTED_IO_APIC(void)
 {
+#if 0
 	printk(KERN_WARNING " WARNING: unexpected IO-APIC, please mail\n");
 	printk(KERN_WARNING "          to linux-smp@vger.kernel.org\n");
+#endif
 }
 
 void __init print_IO_APIC(void)
diff -urN linux-2.4.21-pre5/arch/x86_64/kernel/ioport.c linux-2.4.21-pre6/arch/x86_64/kernel/ioport.c
--- linux-2.4.21-pre5/arch/x86_64/kernel/ioport.c	Thu Nov 28 15:53:12 2002
+++ linux-2.4.21-pre6/arch/x86_64/kernel/ioport.c	Wed Mar 26 16:11:34 2003
@@ -53,7 +53,7 @@
 /*
  * this changes the io permissions bitmap in the current task.
  */
-asmlinkage int sys_ioperm(unsigned long from, unsigned long num, int turn_on)
+asmlinkage long sys_ioperm(unsigned long from, unsigned long num, int turn_on)
 {
 	struct thread_struct * t = &current->thread;
 	struct tss_struct * tss = init_tss + smp_processor_id();
diff -urN linux-2.4.21-pre5/arch/x86_64/kernel/nmi.c linux-2.4.21-pre6/arch/x86_64/kernel/nmi.c
--- linux-2.4.21-pre5/arch/x86_64/kernel/nmi.c	Wed Mar 26 16:11:28 2003
+++ linux-2.4.21-pre6/arch/x86_64/kernel/nmi.c	Wed Mar 26 16:11:34 2003
@@ -31,6 +31,8 @@
 static unsigned int nmi_hz = HZ;
 unsigned int nmi_perfctr_msr;	/* the MSR to reset in NMI handler */
 
+int nmi_watchdog_disabled; 
+
 #define K7_EVNTSEL_ENABLE	(1 << 22)
 #define K7_EVNTSEL_INT		(1 << 20)
 #define K7_EVNTSEL_OS		(1 << 17)
@@ -341,7 +343,7 @@
 		alert_counter[i] = 0;
 }
 
-void nmi_watchdog_tick (struct pt_regs * regs)
+void nmi_watchdog_tick (struct pt_regs * regs, unsigned reason)
 {
 
 	/*
@@ -350,6 +352,9 @@
 	 */
 	int sum, cpu = safe_smp_processor_id();
 
+	if (nmi_watchdog_disabled)
+		return;
+
 	sum = apic_timer_irqs[cpu];
 
 	if (last_irq_sums[cpu] == sum) {
@@ -359,6 +364,14 @@
 		 */
 		alert_counter[cpu]++;
 		if (alert_counter[cpu] == 5*nmi_hz) {
+
+
+			if (notify_die(DIE_NMI, "nmi", regs, reason, 2, SIGINT) == NOTIFY_BAD) { 
+				alert_counter[cpu] = 0; 
+				return;
+			} 
+
+
 			spin_lock(&nmi_print_lock);
 			/*
 			 * We are in trouble anyway, lets at least try
diff -urN linux-2.4.21-pre5/arch/x86_64/kernel/pci-gart.c linux-2.4.21-pre6/arch/x86_64/kernel/pci-gart.c
--- linux-2.4.21-pre5/arch/x86_64/kernel/pci-gart.c	Wed Mar 26 16:11:28 2003
+++ linux-2.4.21-pre6/arch/x86_64/kernel/pci-gart.c	Wed Mar 26 16:11:34 2003
@@ -8,7 +8,7 @@
  * See Documentation/DMA-mapping.txt for the interface specification.
  * 
  * Copyright 2002 Andi Kleen, SuSE Labs.
- * $Id: pci-gart.c,v 1.19 2003/02/03 14:53:03 ak Exp $
+ * $Id: pci-gart.c,v 1.20 2003/03/12 08:23:29 ak Exp $
  */
 
 /* 
@@ -153,7 +153,7 @@
 		int high = (unsigned long)virt_to_bus(memory) + size
 			>= 0xffffffff;
 		int mmu = high;
-		if (force_mmu) 
+		if (force_mmu && !(gfp & GFP_DMA)) 
 			mmu = 1;
 		if (no_iommu) { 
 			if (high) goto error;
diff -urN linux-2.4.21-pre5/arch/x86_64/kernel/process.c linux-2.4.21-pre6/arch/x86_64/kernel/process.c
--- linux-2.4.21-pre5/arch/x86_64/kernel/process.c	Wed Mar 26 16:11:28 2003
+++ linux-2.4.21-pre6/arch/x86_64/kernel/process.c	Wed Mar 26 16:11:34 2003
@@ -9,7 +9,7 @@
  *  X86-64 port
  *	Andi Kleen.
  * 
- *  $Id: process.c,v 1.61 2003/02/11 03:08:27 kkeil Exp $
+ *  $Id: process.c,v 1.63 2003/02/19 13:23:19 ak Exp $
  */
 
 /*
@@ -242,7 +242,6 @@
 	 * Stop all CPUs and turn off local APICs and the IO-APIC, so
 	 * other OSs see a clean IRQ state.
 	 */
-	if (notify_die(DIE_STOP,"cpustop",0,0) != NOTIFY_BAD)
 		smp_send_stop();
 	disable_IO_APIC();
 #endif
@@ -319,6 +318,8 @@
 	       fs,fsindex,gs,gsindex,shadowgs); 
 	printk("CS:  %04x DS: %04x ES: %04x CR0: %016lx\n", cs, ds, es, cr0); 
 	printk("CR2: %016lx CR3: %016lx CR4: %016lx\n", cr2, cr3, cr4);
+
+	show_trace(&regs->rsp);
 }
 
 /*
diff -urN linux-2.4.21-pre5/arch/x86_64/kernel/ptrace.c linux-2.4.21-pre6/arch/x86_64/kernel/ptrace.c
--- linux-2.4.21-pre5/arch/x86_64/kernel/ptrace.c	Wed Mar 26 16:11:28 2003
+++ linux-2.4.21-pre6/arch/x86_64/kernel/ptrace.c	Wed Mar 26 16:11:34 2003
@@ -234,8 +234,8 @@
 		unsigned long tmp;
 
 		ret = -EIO;
-		if ((addr & 3) || addr < 0 || 
-		    addr > sizeof(struct user) - 3)
+		if ((addr & 7) || addr < 0 || 
+		    addr > sizeof(struct user) - 7)
 			break;
 
 		tmp = 0;  /* Default return condition */
@@ -244,7 +244,7 @@
 		if(addr >= (long) &dummy->u_debugreg[0] &&
 		   addr <= (long) &dummy->u_debugreg[7]){
 			addr -= (long) &dummy->u_debugreg[0];
-			addr = addr >> 2;
+			addr = addr >> 3;
 			tmp = child->thread.debugreg[addr];
 		}
 		ret = put_user(tmp,(unsigned long *) data);
@@ -262,8 +262,8 @@
 
 	case PTRACE_POKEUSR: /* write the word at location addr in the USER area */
 		ret = -EIO;
-		if ((addr & 3) || addr < 0 || 
-		    addr > sizeof(struct user) - 3)
+		if ((addr & 7) || addr < 0 || 
+		    addr > sizeof(struct user) - 7)
 			break;
 
 		if (addr < sizeof(struct user_regs_struct)) {
@@ -284,6 +284,11 @@
 			  if(addr < (long) &dummy->u_debugreg[4] &&
 			     ((unsigned long) data) >= TASK_SIZE-3) break;
 			  
+			  if (addr == (long) &dummy->u_debugreg[6]) { 
+				  if (data >> 32) 
+					  goto out_tsk;
+			  }
+
 			  if(addr == (long) &dummy->u_debugreg[7]) {
 				  data &= ~DR_CONTROL_RESERVED;
 				  for(i=0; i<4; i++)
@@ -292,7 +297,7 @@
 			  }
 
 			  addr -= (long) &dummy->u_debugreg;
-			  addr = addr >> 2;
+			  addr = addr >> 3;
 			  child->thread.debugreg[addr] = data;
 			  ret = 0;
 		  }
diff -urN linux-2.4.21-pre5/arch/x86_64/kernel/setup.c linux-2.4.21-pre6/arch/x86_64/kernel/setup.c
--- linux-2.4.21-pre5/arch/x86_64/kernel/setup.c	Thu Nov 28 15:53:12 2002
+++ linux-2.4.21-pre6/arch/x86_64/kernel/setup.c	Wed Mar 26 16:11:34 2003
@@ -76,8 +76,6 @@
 extern int root_mountflags;
 extern char _text, _etext, _edata, _end;
 
-static int disable_x86_fxsr __initdata = 0;
-
 char command_line[COMMAND_LINE_SIZE];
 char saved_command_line[COMMAND_LINE_SIZE];
 
@@ -328,18 +326,6 @@
 	num_mappedpages = end_pfn;
 }
 
-#ifndef CONFIG_X86_TSC
-static int tsc_disable __initdata = 0;
-
-static int __init tsc_setup(char *str)
-{
-	tsc_disable = 1;
-	return 1;
-}
-
-__setup("notsc", tsc_setup);
-#endif
-
 static int __init get_model_name(struct cpuinfo_x86 *c)
 {
 	unsigned int *v;
@@ -362,7 +348,6 @@
 
 	n = cpuid_eax(0x80000000);
 
-
 	if (n >= 0x80000005) {
 		if (n >= 0x80000006) 
 			cpuid(0x80000006, &eax_2, &ebx_2, &ecx_2, &dummy); 
@@ -376,16 +361,17 @@
 			printk(KERN_INFO "CPU: L2 Cache: %dK (%d bytes/line/%d way)\n",
 			       ecx_2>>16, ecx_2&0xFF, (ecx_2>>12)&0xf);
 			c->x86_cache_size = ecx_2 >> 16;
-		}		
-		printk(KERN_INFO "CPU: DTLB L1 %d 4K %d 2MB L2: %d 4K %d 2MB\n",
-		       (ebx>>16)&0xff, (eax>>16)&0xff, 
-		       (ebx_2>>16)&0xfff, (eax_2>>16)&0xfff);
-		printk(KERN_INFO "CPU: ITLB L1 %d 4K %d 2MB L2: %d 4K %d 2MB\n",
-		       ebx&0xff, (eax)&0xff, 
-		       (ebx_2)&0xfff, (eax_2)&0xfff);		
 		c->x86_tlbsize = ((ebx>>16)&0xff) + ((ebx_2>>16)&0xfff) + 
 			(ebx&0xff) + ((ebx_2)&0xfff);
 	}
+		if (n >= 0x80000007)
+			cpuid(0x80000007, &dummy, &dummy, &dummy, &c->x86_power); 
+		if (n >= 0x80000008) {
+			cpuid(0x80000008, &eax, &dummy, &dummy, &dummy); 
+			c->x86_virt_bits = (eax >> 8) & 0xff;
+			c->x86_phys_bits = eax & 0xff;
+		}
+	}
 }
 
 static int __init init_amd(struct cpuinfo_x86 *c)
@@ -427,15 +413,6 @@
 	char *model_names[16];
 };
 
-int __init x86_fxsr_setup(char * s)
-{
-	disable_x86_fxsr = 1;
-	return 1;
-}
-__setup("nofxsr", x86_fxsr_setup);
-
-
-
 /*
  * This does the hard work of actually picking apart the CPU stuff...
  */
@@ -468,11 +445,11 @@
 		cpuid(0x00000001, &tfms, &misc, &junk,
 		      &c->x86_capability[0]);
 		c->x86 = (tfms >> 8) & 15;
-		if (c->x86 == 0xf)
-			c->x86 += (tfms >> 20) & 0xff;
 		c->x86_model = (tfms >> 4) & 15;
-		if (c->x86_model == 0xf)
+		if (c->x86 == 0xf) { /* extended */
+			c->x86 += (tfms >> 20) & 0xff;
 			c->x86_model += ((tfms >> 16) & 0xF) << 4; 
+		}
 		c->x86_mask = tfms & 15;
 		if (c->x86_capability[0] & (1<<19)) 
 			c->x86_clflush_size = ((misc >> 8) & 0xff) * 8;
@@ -525,18 +502,6 @@
 	 * we do "generic changes."
 	 */
 
-	/* TSC disabled? */
-#ifndef CONFIG_X86_TSC
-	if ( tsc_disable )
-		clear_bit(X86_FEATURE_TSC, &c->x86_capability);
-#endif
-
-	/* FXSR disabled? */
-	if (disable_x86_fxsr) {
-		clear_bit(X86_FEATURE_FXSR, &c->x86_capability);
-		clear_bit(X86_FEATURE_XMM, &c->x86_capability);
-	}
-
 	/*
 	 * On SMP, boot_cpu_data holds the common feature set between
 	 * all CPUs; so make sure that we indicate which features are
@@ -606,6 +571,12 @@
 		NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
 		NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
 	};
+	static char *x86_power_flags[] = { 
+		"ts",	/* temperature sensor */
+		"fid",  /* frequency id control */
+		"vid",  /* voltage id control */
+		"ttp",  /* thermal trip */
+	};
 
 #ifdef CONFIG_SMP
 	if (!(cpu_online_map & (1<<(c-cpu_data))))
@@ -659,7 +630,22 @@
 		seq_printf(m, "TLB size\t: %d 4K pages\n", c->x86_tlbsize);
 	seq_printf(m, "clflush size\t: %d\n", c->x86_clflush_size);
 
-	seq_printf(m, "\n"); 
+	seq_printf(m, "address sizes\t: %u bits physical, %u bits virtual\n", 
+		   c->x86_phys_bits, c->x86_virt_bits);
+
+	seq_printf(m, "power management:");
+	{
+		int i;
+		for (i = 0; i < 32; i++) 
+			if (c->x86_power & (1 << i)) {
+				if (i < ARRAY_SIZE(x86_power_flags))
+					seq_printf(m, " %s", x86_power_flags[i]);
+				else
+					seq_printf(m, " [%d]", i);
+			}
+	}
+
+	seq_printf(m, "\n\n"); 
 	return 0;
 }
 
diff -urN linux-2.4.21-pre5/arch/x86_64/kernel/setup64.c linux-2.4.21-pre6/arch/x86_64/kernel/setup64.c
--- linux-2.4.21-pre5/arch/x86_64/kernel/setup64.c	Wed Mar 26 16:11:28 2003
+++ linux-2.4.21-pre6/arch/x86_64/kernel/setup64.c	Wed Mar 26 16:11:34 2003
@@ -3,7 +3,7 @@
  * Copyright (C) 1995  Linus Torvalds
  * Copyright 2001, 2002 SuSE Labs / Andi Kleen.
  * See setup.c for older changelog.
- * $Id: setup64.c,v 1.18 2003/01/24 20:02:09 ak Exp $
+ * $Id: setup64.c,v 1.19 2003/02/21 19:37:21 ak Exp $
  */ 
 #include <linux/config.h>
 #include <linux/init.h>
@@ -32,7 +32,7 @@
 struct desc_ptr idt_descr = { 256 * 16, (unsigned long) idt_table }; 
 
 unsigned long __supported_pte_mask = ~0UL; 
-static int do_not_nx = 0; 
+static int do_not_nx = 1; 
 
 char boot_cpu_stack[IRQSTACKSIZE] __cacheline_aligned;
 
diff -urN linux-2.4.21-pre5/arch/x86_64/kernel/signal.c linux-2.4.21-pre6/arch/x86_64/kernel/signal.c
--- linux-2.4.21-pre5/arch/x86_64/kernel/signal.c	Wed Mar 26 16:11:28 2003
+++ linux-2.4.21-pre6/arch/x86_64/kernel/signal.c	Wed Mar 26 16:11:34 2003
@@ -8,7 +8,7 @@
  *  2000-06-20  Pentium III FXSR, SSE support by Gareth Hughes
  *  2000-2001   x86-64 support by Andi Kleen
  * 
- *  $Id: signal.c,v 1.40 2002/11/28 06:06:43 ak Exp $
+ *  $Id: signal.c,v 1.42 2003/02/24 21:06:02 ak Exp $
  */
 
 #include <linux/sched.h>
@@ -138,18 +138,7 @@
 	if ((regs->x >> 48)  != 0 && (regs->x >> 48) != 0xffff) \
 				regs->x = 0; 
 
-	{ 
-		unsigned int seg, oldseg; 
-		err |= __get_user(seg, &sc->gs); 
-		asm("movl %%gs,%0" : "=r" (oldseg)); 
-		if (oldseg != seg) {
-			seg |= 3;		       
-		load_gs_index(seg); 
-		}
-		err |= __get_user(seg, &sc->fs);
-		seg |= 3; 
-		loadsegment(fs,seg);
-	}
+	/* fs and gs are ignored because we cannot handle the 64bit base easily */ 
 
 	COPY(rdi); COPY(rsi); COPY(rbp); COPY_CANON(rsp); COPY(rbx);
 	COPY(rdx); COPY(rcx); COPY_CANON(rip);
@@ -236,10 +225,8 @@
 	struct task_struct *me = current;
 
 	tmp = 0;
-	__asm__("movl %%gs,%0" : "=r"(tmp): "0"(tmp));
-	err |= __put_user(tmp, (unsigned int *)&sc->gs);
-	__asm__("movl %%fs,%0" : "=r"(tmp): "0"(tmp));
-	err |= __put_user(tmp, (unsigned int *)&sc->fs);
+	err |= __put_user(0, &sc->gs);
+	err |= __put_user(0, &sc->fs);
 
 	err |= __put_user(regs->rdi, &sc->rdi);
 	err |= __put_user(regs->rsi, &sc->rsi);
@@ -562,7 +549,8 @@
 		 * have been cleared if the watchpoint triggered
 		 * inside the kernel.
 		 */
-		asm volatile("movq %0,%%db7"::"r"(current->thread.debugreg[7]));
+		if (current->thread.debugreg[7])
+			asm volatile("movq %0,%%db7" :: "r" (current->thread.debugreg[7]));
 		/* Whee!  Actually deliver the signal.  */
 		handle_signal(signr, ka, &info, oldset, regs);
 		return 1;
diff -urN linux-2.4.21-pre5/arch/x86_64/kernel/smp.c linux-2.4.21-pre6/arch/x86_64/kernel/smp.c
--- linux-2.4.21-pre5/arch/x86_64/kernel/smp.c	Wed Mar 26 16:11:28 2003
+++ linux-2.4.21-pre6/arch/x86_64/kernel/smp.c	Wed Mar 26 16:11:34 2003
@@ -115,8 +115,6 @@
 static inline unsigned int __prepare_ICR (unsigned int shortcut, int vector)
 {
 	unsigned int icr =  APIC_DM_FIXED | shortcut | vector | APIC_DEST_LOGICAL;
-	if (vector == KDB_VECTOR) 
-		icr = (icr & (~APIC_VECTOR_MASK)) | APIC_DM_NMI; 		
 	return icr;
 }
 
@@ -412,11 +410,6 @@
 	do_flush_tlb_all_local();
 }
 
-void smp_kdb_stop(void)
-{
-	send_IPI_allbutself(KDB_VECTOR);
-} 
-
 /*
  * this function sends a 'reschedule' IPI to another CPU.
  * it goes straight through and wastes no time serializing
diff -urN linux-2.4.21-pre5/arch/x86_64/kernel/smpboot.c linux-2.4.21-pre6/arch/x86_64/kernel/smpboot.c
--- linux-2.4.21-pre5/arch/x86_64/kernel/smpboot.c	Wed Mar 26 16:11:28 2003
+++ linux-2.4.21-pre6/arch/x86_64/kernel/smpboot.c	Wed Mar 26 16:11:34 2003
@@ -410,8 +410,6 @@
 	 */
  	smp_store_cpu_info(cpuid);
 
-	notify_die(DIE_CPUINIT, "cpuinit", NULL, 0);
-
 	/*
 	 * Allow the master to continue.
 	 */
diff -urN linux-2.4.21-pre5/arch/x86_64/kernel/traps.c linux-2.4.21-pre6/arch/x86_64/kernel/traps.c
--- linux-2.4.21-pre5/arch/x86_64/kernel/traps.c	Wed Mar 26 16:11:28 2003
+++ linux-2.4.21-pre6/arch/x86_64/kernel/traps.c	Wed Mar 26 16:11:35 2003
@@ -7,7 +7,7 @@
  *  Pentium III FXSR, SSE support
  *	Gareth Hughes <gareth@valinux.com>, May 2000
  *
- *  $Id: traps.c,v 1.57 2003/02/07 04:32:49 ak Exp $
+ *  $Id: traps.c,v 1.62 2003/03/14 16:06:35 ak Exp $
  */
 
 /*
@@ -149,7 +149,7 @@
 
 #endif
 
-static inline unsigned long *in_exception_stack(int cpu, unsigned long stack) 
+unsigned long *in_exception_stack(int cpu, unsigned long stack) 
 { 
 	int k;
 	for (k = 0; k < N_EXCEPTION_STACKS; k++) {
@@ -345,12 +345,11 @@
 void die(const char * str, struct pt_regs * regs, long err)
 {
 	int cpu;
-	struct die_args args = { regs, str, err };
 	console_verbose();
-	notifier_call_chain(&die_chain,  DIE_DIE, &args); 
 	bust_spinlocks(1);
 	handle_BUG(regs);		
 	printk(KERN_EMERG "%s: %04lx\n", str, err & 0xffff);
+ 	notify_die(DIE_OOPS, (char *)str, regs, err, 255, SIGSEGV);
 	cpu = safe_smp_processor_id(); 
 	/* racy, but better than risking deadlock. */ 
 	__cli();
@@ -364,7 +363,6 @@
 	show_registers(regs);
 	bust_spinlocks(0);
 	spin_unlock_irq(&die_lock);
-	notify_die(DIE_OOPS, (char *)str, regs, err);
 	do_exit(SIGSEGV);
 }
 
@@ -431,6 +429,8 @@
 #define DO_ERROR(trapnr, signr, str, name) \
 asmlinkage void do_##name(struct pt_regs * regs, long error_code) \
 { \
+	if (notify_die(DIE_TRAP, str, regs, error_code, trapnr, signr) == NOTIFY_BAD) \
+		return; \
 	do_trap(trapnr, signr, str, regs, error_code, NULL); \
 }
 
@@ -442,10 +442,13 @@
 	info.si_errno = 0; \
 	info.si_code = sicode; \
 	info.si_addr = (void *)siaddr; \
+	if (notify_die(DIE_TRAP, str, regs, error_code, trapnr, signr)==NOTIFY_BAD) \
+		return; \
 	do_trap(trapnr, signr, str, regs, error_code, &info); \
 }
 
 DO_ERROR_INFO( 0, SIGFPE,  "divide error", divide_error, FPE_INTDIV, regs->rip)
+DO_ERROR( 3, SIGTRAP, "int3", int3); 
 DO_ERROR( 4, SIGSEGV, "overflow", overflow)
 DO_ERROR( 5, SIGSEGV, "bounds", bounds)
 DO_ERROR_INFO( 6, SIGILL,  "invalid operand", invalid_op, ILL_ILLOPN, regs->rip)
@@ -458,13 +461,6 @@
 DO_ERROR_INFO(17, SIGBUS, "alignment check", alignment_check, BUS_ADRALN, get_cr2())
 DO_ERROR(18, SIGSEGV, "reserved", reserved)
 
-asmlinkage void do_int3(struct pt_regs * regs, long error_code)
-{
-	if (notify_die(DIE_INT3, "int3", regs, error_code) == NOTIFY_BAD)
-		return;
-	do_trap(3, SIGTRAP, "int3", regs, error_code, NULL);
-}
-
 extern void dump_pagetable(unsigned long);
 
 asmlinkage void do_general_protection(struct pt_regs * regs, long error_code)
@@ -507,6 +503,8 @@
 			regs->rip = fixup;
 			return;
 		}
+		notify_die(DIE_GPF, "general protection fault", regs, error_code,
+			   13, SIGSEGV); 
 		die("general protection fault", regs, error_code);
 	}
 }
@@ -554,16 +552,14 @@
 		 * so it must be the NMI watchdog.
 		 */
 		if (nmi_watchdog) {
-			nmi_watchdog_tick(regs);
+			nmi_watchdog_tick(regs, reason);
 			return;
 		}
 #endif
-		if (notify_die(DIE_NMI, "nmi", regs, reason) == NOTIFY_BAD)
-			return;
 		unknown_nmi_error(reason, regs);
 		return;
 	}
-	if (notify_die(DIE_NMI, "nmi", regs, reason) == NOTIFY_BAD)
+	if (notify_die(DIE_NMI, "nmi", regs, reason, 2, SIGINT) == NOTIFY_BAD)
 		return; 
 	if (reason & 0x80)
 		mem_parity_error(reason, regs);
@@ -592,6 +588,7 @@
 
 #ifdef CONFIG_CHECKING
 	{ 
+		/* XXX: interaction with debugger - could destroy gs */
 		unsigned long gs; 
 		struct x8664_pda *pda = cpu_pda + safe_smp_processor_id(); 
 		rdmsrl(MSR_GS_BASE, gs); 
@@ -602,9 +599,6 @@
 	}
 #endif
 
-	if (notify_die(DIE_DEBUG, "debug", regs, error_code) == NOTIFY_BAD)
-		return; 
-
 	/* Mask out spurious debug traps due to lazy DR7 setting */
 	if (condition & (DR_TRAP0|DR_TRAP1|DR_TRAP2|DR_TRAP3)) {
 		if (!tsk->thread.debugreg[7]) { 
@@ -637,14 +631,19 @@
 	info.si_signo = SIGTRAP;
 	info.si_errno = 0;
 	info.si_code = TRAP_BRKPT;
-	info.si_addr = ((regs->cs & 3) == 0) ? (void *)tsk->thread.rip : 
-	                                        (void *)regs->rip;
+	if ((regs->cs & 3) == 0) 
+		goto clear_dr7; 
+
+	info.si_addr = (void *)regs->rip;
 	force_sig_info(SIGTRAP, &info, tsk);	
 clear_dr7:
-	asm("movq %0,%%db7"::"r"(0UL));
+	asm volatile("movq %0,%%db7"::"r"(0UL));
+	notify_die(DIE_DEBUG, "debug", regs, error_code, 1, SIGTRAP);
 	return;
 
 clear_TF:
+	/* XXX: could cause spurious errors */
+	if (notify_die(DIE_DEBUG, "debug2", regs, error_code, 1, SIGTRAP) != NOTIFY_BAD)
 	regs->eflags &= ~TF_MASK;
 	return;
 }
@@ -792,7 +791,7 @@
 	clts();			/* Allow maths ops (or we recurse) */
 
 	if (!me->used_math)
-		init_fpu();        
+		init_fpu(me);
 	restore_fpu_checking(&me->thread.i387.fxsave);
 	me->flags |= PF_USEDFPU;	/* So we fxsave on switch_to() */
 }
@@ -804,7 +803,7 @@
 
 void do_call_debug(struct pt_regs *regs) 
 { 
-	notify_die(DIE_CALL, "debug call", regs, 0); 
+	notify_die(DIE_CALL, "debug call", regs, 0, 255, SIGINT); 
 } 
 
 #ifndef CONFIG_MCE
@@ -841,10 +840,6 @@
 	set_intr_gate(IA32_SYSCALL_VECTOR, ia32_syscall);
 #endif
 
-	set_intr_gate(KDB_VECTOR, call_debug);
-       
-	notify_die(DIE_TRAPINIT, "traps initialized", 0, 0); 
-	
 	/*
 	 * Should be a barrier for any external CPU state.
 	 */
diff -urN linux-2.4.21-pre5/arch/x86_64/kernel/vsyscall.c linux-2.4.21-pre6/arch/x86_64/kernel/vsyscall.c
--- linux-2.4.21-pre5/arch/x86_64/kernel/vsyscall.c	Wed Mar 26 16:11:28 2003
+++ linux-2.4.21-pre6/arch/x86_64/kernel/vsyscall.c	Wed Mar 26 16:11:35 2003
@@ -12,7 +12,7 @@
  *  vsyscalls. One vsyscall can reserve more than 1 slot to avoid
  *  jumping out of line if necessary.
  *
- *  $Id: vsyscall.c,v 1.25 2003/02/07 01:58:27 ak Exp $
+ *  $Id: vsyscall.c,v 1.26 2003/02/18 11:55:47 ak Exp $
  */
 
 /*
@@ -49,9 +49,11 @@
 
 #define __vsyscall(nr) __attribute__ ((unused,__section__(".vsyscall_" #nr)))
 
+#define force_inline inline __attribute__((always_inline))
+
 long __vxtime_sequence[2] __section_vxtime_sequence;
 
-static inline void do_vgettimeofday(struct timeval * tv)
+static force_inline void do_vgettimeofday(struct timeval * tv)
 {
 	long sequence, t;
 	unsigned long sec, usec;
@@ -85,7 +87,7 @@
 }
 
 
-static inline void do_get_tz(struct timezone * tz)
+static force_inline void do_get_tz(struct timezone * tz)
 {
 	long sequence;
 
diff -urN linux-2.4.21-pre5/arch/x86_64/lib/clear_page.S linux-2.4.21-pre6/arch/x86_64/lib/clear_page.S
--- linux-2.4.21-pre5/arch/x86_64/lib/clear_page.S	Thu Nov 28 15:53:12 2002
+++ linux-2.4.21-pre6/arch/x86_64/lib/clear_page.S	Wed Mar 26 16:11:35 2003
@@ -1,16 +1,18 @@
-	#include <linux/linkage.h>
 
 /*
  * Zero a page. 	
  * rdi	page
  */			
-ENTRY(clear_page)
+	.globl clear_page
+	.p2align 4
+clear_page:
 	xorl   %eax,%eax
-	movl   $4096/128,%ecx
-	movl   $128,%edx
-loop:
+	movl   $4096/64,%ecx
+	.p2align 4
+.Lloop:
+	decl	%ecx
 #define PUT(x) movq %rax,x*8(%rdi) 
-	PUT(0)
+	movq %rax,(%rdi)
 	PUT(1)
 	PUT(2)
 	PUT(3)
@@ -18,17 +20,8 @@
 	PUT(5)
 	PUT(6)
 	PUT(7)
-	PUT(8)
-	PUT(9)
-	PUT(10)
-	PUT(11)
-	PUT(12)
-	PUT(13)
-	PUT(14)
-	PUT(15)
-	addq    %rdx,%rdi
-	decl	%ecx
-	jnz	loop
-	sfence	
+	leaq	64(%rdi),%rdi
+	jnz	.Lloop
+	nop
 	ret
 	    	
diff -urN linux-2.4.21-pre5/arch/x86_64/lib/copy_page.S linux-2.4.21-pre6/arch/x86_64/lib/copy_page.S
--- linux-2.4.21-pre5/arch/x86_64/lib/copy_page.S	Thu Nov 28 15:53:12 2002
+++ linux-2.4.21-pre6/arch/x86_64/lib/copy_page.S	Wed Mar 26 16:11:35 2003
@@ -1,70 +1,91 @@
-	#include <linux/linkage.h>
-	#include <linux/config.h>
-	#ifdef CONFIG_PREEMPT
-	#warning "check your fpu context saving!"
-	#endif
-
-/*
- * Copy a page. 
- *		
- * rdi destination page
- * rsi source page
- *		
- * src/dst must be aligned to 16 bytes.
- * 
- * Warning: in case of super lazy FP save this needs to be preempt_stop	
- */
+/* Written 2003 by Andi Kleen, based on a kernel by Evandro Menezes */
 	
+/* Don't use streaming store because it's better when the target
+   ends up in cache. */
+	    
+/* Could vary the prefetch distance based on SMP/UP */
+
 	.globl copy_page
-	.p2align
+	.p2align 4
 copy_page:
-	prefetchnta (%rsi) 
-	prefetchnta 64(%rsi)
-	    
-	movq %rsp,%rax
-	subq $16*4,%rsp
-	andq $~15,%rsp		
-	movdqa %xmm0,(%rsp) 
-	movdqa %xmm1,16(%rsp) 
-	movdqa %xmm2,32(%rsp) 
-	movdqa %xmm3,48(%rsp)
-	
-	movl   $(4096/128)-2,%ecx
-	movl   $128,%edx
-loop:
-	prefetchnta (%rsi) 
-	prefetchnta 64(%rsi) 
-loop_no_prefetch:	
-	movdqa (%rsi),%xmm0 	 			 
-	movdqa 16(%rsi),%xmm1
-	movdqa 32(%rsi),%xmm2 	 			 
-	movdqa 48(%rsi),%xmm3 	 			 
-	movntdq %xmm0,(%rdi)	 
-	movntdq %xmm1,16(%rdi)	 
-	movntdq %xmm2,32(%rdi)	 
-	movntdq %xmm3,48(%rdi)
-	
-	movdqa 64(%rsi),%xmm0 	 			 
-	movdqa 80(%rsi),%xmm1
-	movdqa 96(%rsi),%xmm2 	 			 
-	movdqa 112(%rsi),%xmm3 	 			 
-	movntdq %xmm0,64(%rdi)	 
-	movntdq %xmm1,80(%rdi)	 
-	movntdq %xmm2,96(%rdi)	 
-	movntdq %xmm3,112(%rdi)
+	prefetch (%rsi) 
+	prefetch 1*64(%rsi)
+	prefetch 2*64(%rsi)
+	prefetch 3*64(%rsi)
+	prefetch 4*64(%rsi)
+	prefetchw (%rdi) 
+	prefetchw 1*64(%rdi) 
+	prefetchw 2*64(%rdi) 
+	prefetchw 3*64(%rdi)
+	prefetchw 4*64(%rdi)
+
+	subq	$3*8,%rsp
+	movq	%rbx,(%rsp)
+	movq	%r12,1*8(%rsp)
+	movq	%r13,2*8(%rsp)
+			
+	movl	$(4096/64)-5,%ecx
+	.p2align 4
+.Loop64:	
+  	dec     %rcx
+
+	movq        (%rsi), %rax
+	movq      8 (%rsi), %rbx
+	movq     16 (%rsi), %rdx
+	movq     24 (%rsi), %r8
+	movq     32 (%rsi), %r9
+	movq     40 (%rsi), %r10
+	movq     48 (%rsi), %r11
+	movq     56 (%rsi), %r12
+
+	prefetch 5*64(%rsi)
+
+	movq     %rax,    (%rdi)
+	movq     %rbx,  8 (%rdi)
+	movq     %rdx, 16 (%rdi)
+	movq     %r8,  24 (%rdi)
+	movq     %r9,  32 (%rdi)
+	movq     %r10, 40 (%rdi)
+	movq     %r11, 48 (%rdi)
+	movq     %r12, 56 (%rdi)
 
-	addq   %rdx,%rdi	
-	addq   %rdx,%rsi
+	prefetchw 5*64(%rdi)
+
+	leaq    64 (%rsi), %rsi
+	leaq    64 (%rdi), %rdi
+
+	jnz     .Loop64
+
+	movl	$5,%ecx
+	.p2align 4
+.Loop2:	
 	decl   %ecx
-	jns    loop
-	cmpl   $-1,%ecx
-	je     loop_no_prefetch
-	
-	sfence
 
-	movdqa (%rsp),%xmm0
-	movdqa 16(%rsp),%xmm1
-	movdqa 32(%rsp),%xmm2
-	movdqa 48(%rsp),%xmm3
-	movq   %rax,%rsp
+	movq        (%rsi), %rax
+	movq      8 (%rsi), %rbx
+	movq     16 (%rsi), %rdx
+	movq     24 (%rsi), %r8
+	movq     32 (%rsi), %r9
+	movq     40 (%rsi), %r10
+	movq     48 (%rsi), %r11
+	movq     56 (%rsi), %r12
+
+	movq     %rax,    (%rdi)
+	movq     %rbx,  8 (%rdi)
+	movq     %rdx, 16 (%rdi)
+	movq     %r8,  24 (%rdi)
+	movq     %r9,  32 (%rdi)
+	movq     %r10, 40 (%rdi)
+	movq     %r11, 48 (%rdi)
+	movq     %r12, 56 (%rdi)
+	
+	leaq	64(%rdi),%rdi			
+	leaq	64(%rsi),%rsi			
+	
+	jnz	.Loop2		
+	
+	movq	(%rsp),%rbx
+	movq	1*8(%rsp),%r12
+	movq	2*8(%rsp),%r13
+	addq	$3*8,%rsp
 	ret
diff -urN linux-2.4.21-pre5/arch/x86_64/lib/copy_user.S linux-2.4.21-pre6/arch/x86_64/lib/copy_user.S
--- linux-2.4.21-pre5/arch/x86_64/lib/copy_user.S	Thu Nov 28 15:53:12 2002
+++ linux-2.4.21-pre6/arch/x86_64/lib/copy_user.S	Wed Mar 26 16:11:35 2003
@@ -6,15 +6,12 @@
 
 #define FIX_ALIGNMENT 1
 
-#define movnti movq  /* write to cache for now */
-#define prefetch prefetcht2
-		
 	#include <asm/current.h>
 	#include <asm/offset.h>
 
 /* Standard copy_to_user with segment limit checking */		
 	.globl copy_to_user
-	.p2align 	
+	.p2align 4	
 copy_to_user:
 	GET_CURRENT(%rax)
 	movq %rdi,%rcx
@@ -26,7 +23,7 @@
 
 /* Standard copy_from_user with segment limit checking */	
 	.globl copy_from_user
-	.p2align 	
+	.p2align 4	
 copy_from_user:
 	GET_CURRENT(%rax)
 	movq %rsi,%rcx
@@ -60,6 +57,7 @@
  * eax uncopied bytes or 0 if successfull. 
  */
 	.globl copy_user_generic	
+	.p2align 4
 copy_user_generic:	
 	/* Put the first cacheline into cache. This should handle
 	   the small movements in ioctls etc., but not penalize the bigger
@@ -72,8 +70,8 @@
 	/* check for bad alignment of destination */
 	movl %edi,%ecx
 	andl $7,%ecx
-	jnz  bad_alignment
-after_bad_alignment:
+	jnz  .Lbad_alignment
+.Lafter_bad_alignment:
 #endif
 
 	movq %rdx,%rcx
@@ -81,133 +79,138 @@
 	movl $64,%ebx	
 	shrq $6,%rdx
 	decq %rdx
-	js   handle_tail
-	jz   loop_no_prefetch
-	
-loop:
-	prefetch 64(%rsi)
+	js   .Lhandle_tail
+	jz   .Lloop_no_prefetch
 	
-loop_no_prefetch:	
-s1:	movq (%rsi),%r11
-s2:	movq 1*8(%rsi),%r8
-s3:	movq 2*8(%rsi),%r9
-s4:	movq 3*8(%rsi),%r10
-d1:	movnti %r11,(%rdi)
-d2:	movnti %r8,1*8(%rdi)
-d3:	movnti %r9,2*8(%rdi)
-d4:	movnti %r10,3*8(%rdi)
+	.p2align 4
+.Lloop:
+	prefetch 64(%rsi)		/* prefetch distance too short */
+	
+.Lloop_no_prefetch:	
+.Ls1:	movq (%rsi),%r11
+.Ls2:	movq 1*8(%rsi),%r8
+.Ls3:	movq 2*8(%rsi),%r9
+.Ls4:	movq 3*8(%rsi),%r10
+.Ld1:	movq %r11,(%rdi)
+.Ld2:	movq %r8,1*8(%rdi)
+.Ld3:	movq %r9,2*8(%rdi)
+.Ld4:	movq %r10,3*8(%rdi)
 		
-s5:	movq 4*8(%rsi),%r11
-s6:	movq 5*8(%rsi),%r8
-s7:	movq 6*8(%rsi),%r9
-s8:	movq 7*8(%rsi),%r10
-d5:	movnti %r11,4*8(%rdi)
-d6:	movnti %r8,5*8(%rdi)
-d7:	movnti %r9,6*8(%rdi)
-d8:	movnti %r10,7*8(%rdi)
-
-	addq %rbx,%rsi	
-	addq %rbx,%rdi
+.Ls5:	movq 4*8(%rsi),%r11
+.Ls6:	movq 5*8(%rsi),%r8
+.Ls7:	movq 6*8(%rsi),%r9
+.Ls8:	movq 7*8(%rsi),%r10
+.Ld5:	movq %r11,4*8(%rdi)
+.Ld6:	movq %r8,5*8(%rdi)
+.Ld7:	movq %r9,6*8(%rdi)
+.Ld8:	movq %r10,7*8(%rdi)
 	
 	decq %rdx
-	jz   loop_no_prefetch
-	jns  loop
 
-handle_tail:
+	leaq 64(%rsi),%rsi
+	leaq 64(%rdi),%rdi
+	
+	jz   .Lloop_no_prefetch
+	jns  .Lloop
+
+	.p2align 4
+.Lhandle_tail:
 	movl %ecx,%edx
 	andl $63,%ecx
 	shrl $3,%ecx
-	jz   handle_7
+	jz   .Lhandle_7
 	movl $8,%ebx
-loop_8:
-s9:	movq (%rsi),%r8
-d9:	movq %r8,(%rdi)
-	addq %rbx,%rdi
-	addq %rbx,%rsi
+	.p2align 4
+.Lloop_8:
+.Ls9:	movq (%rsi),%r8
+.Ld9:	movq %r8,(%rdi)
 	decl %ecx
-	jnz loop_8
+	leaq 8(%rdi),%rdi
+	leaq 8(%rsi),%rsi
+	jnz .Lloop_8
 	
-handle_7:		
+.Lhandle_7:		
 	movl %edx,%ecx	
 	andl $7,%ecx
-	jz   ende
-loop_1:
-s10:	movb (%rsi),%bl
-d10:	movb %bl,(%rdi)
+	jz   .Lende
+	.p2align 4
+.Lloop_1:
+.Ls10:	movb (%rsi),%bl
+.Ld10:	movb %bl,(%rdi)
 	incq %rdi
 	incq %rsi
 	decl %ecx
-	jnz loop_1
+	jnz .Lloop_1
 			
-ende:
-	sfence
+.Lende:
 	popq %rbx
 	ret	
 
 #ifdef FIX_ALIGNMENT		  		
 	/* align destination */
-bad_alignment:
+	.p2align 4
+.Lbad_alignment:
 	movl $8,%r9d
 	subl %ecx,%r9d
 	movl %r9d,%ecx
 	subq %r9,%rdx
-	jz   small_align
-	js   small_align
-align_1:		
-s11:	movb (%rsi),%bl
-d11:	movb %bl,(%rdi)
+	jz   .Lsmall_align
+	js   .Lsmall_align
+.Lalign_1:		
+.Ls11:	movb (%rsi),%bl
+.Ld11:	movb %bl,(%rdi)
 	incq %rsi
 	incq %rdi
 	decl %ecx
-	jnz align_1
-	jmp after_bad_alignment
-small_align:
+	jnz .Lalign_1
+	jmp .Lafter_bad_alignment
+.Lsmall_align:
 	addq %r9,%rdx
-	jmp handle_7
+	jmp .Lhandle_7
 #endif
 	
 	/* table sorted by exception address */	
 	.section __ex_table,"a"
 	.align 8
-	.quad s1,s1e
-	.quad s2,s2e
-	.quad s3,s3e
-	.quad s4,s4e	
-	.quad d1,s1e
-	.quad d2,s2e
-	.quad d3,s3e
-	.quad d4,s4e
-	.quad s5,s5e
-	.quad s6,s6e
-	.quad s7,s7e
-	.quad s8,s8e	
-	.quad d5,s5e
-	.quad d6,s6e
-	.quad d7,s7e
-	.quad d8,s8e
-	.quad s9,e_quad
-	.quad d9,e_quad
-	.quad s10,e_byte
-	.quad d10,e_byte
+	.quad .Ls1,.Ls1e
+	.quad .Ls2,.Ls2e
+	.quad .Ls3,.Ls3e
+	.quad .Ls4,.Ls4e	
+	.quad .Ld1,.Ls1e
+	.quad .Ld2,.Ls2e
+	.quad .Ld3,.Ls3e
+	.quad .Ld4,.Ls4e
+	.quad .Ls5,.Ls5e
+	.quad .Ls6,.Ls6e
+	.quad .Ls7,.Ls7e
+	.quad .Ls8,.Ls8e	
+	.quad .Ld5,.Ls5e
+	.quad .Ld6,.Ls6e
+	.quad .Ld7,.Ls7e
+	.quad .Ld8,.Ls8e
+	.quad .Ls9,.Le_quad
+	.quad .Ld9,.Le_quad
+	.quad .Ls10,.Le_byte
+	.quad .Ld10,.Le_byte
 #ifdef FIX_ALIGNMENT	
-	.quad s11,e_byte
-	.quad d11,e_byte
+	.quad .Ls11,.Le_byte
+	.quad .Ld11,.Le_byte
 #endif
-	.quad e5,e_zero
+	.quad .Le5,.Le_zero
 	.previous
 
 	/* compute 64-offset for main loop. 8 bytes accuracy with error on the 
 	   pessimistic side. this is gross. it would be better to fix the 
 	   interface. */	
 	/* eax: zero, ebx: 64 */
-s1e: 	addl $8,%eax
-s2e: 	addl $8,%eax
-s3e: 	addl $8,%eax
-s4e: 	addl $8,%eax
-s5e: 	addl $8,%eax
-s6e: 	addl $8,%eax
-s7e: 	addl $8,%eax
-s8e: 	addl $8,%eax
+.Ls1e: 	addl $8,%eax
+.Ls2e: 	addl $8,%eax
+.Ls3e: 	addl $8,%eax
+.Ls4e: 	addl $8,%eax
+.Ls5e: 	addl $8,%eax
+.Ls6e: 	addl $8,%eax
+.Ls7e: 	addl $8,%eax
+.Ls8e: 	addl $8,%eax
 	addq %rbx,%rdi	/* +64 */
 	subq %rax,%rdi  /* correct destination with computed offset */
 
@@ -215,22 +218,22 @@
 	addq %rax,%rdx	/* add offset to loopcnt */
 	andl $63,%ecx	/* remaining bytes */
 	addq %rcx,%rdx	/* add them */
-	jmp zero_rest
+	jmp .Lzero_rest
 
 	/* exception on quad word loop in tail handling */
 	/* ecx:	loopcnt/8, %edx: length, rdi: correct */
-e_quad:
+.Le_quad:
 	shll $3,%ecx
 	andl $7,%edx
 	addl %ecx,%edx
 	/* edx: bytes to zero, rdi: dest, eax:zero */
-zero_rest:
+.Lzero_rest:
 	movq %rdx,%rcx
-e_byte:
+.Le_byte:
 	xorl %eax,%eax
-e5:	rep 
+.Le5:	rep 
 	stosb
 	/* when there is another exception while zeroing the rest just return */
-e_zero:		
+.Le_zero:		
 	movq %rdx,%rax
-	jmp ende
+	jmp .Lende
diff -urN linux-2.4.21-pre5/arch/x86_64/lib/csum-copy.S linux-2.4.21-pre6/arch/x86_64/lib/csum-copy.S
--- linux-2.4.21-pre5/arch/x86_64/lib/csum-copy.S	Thu Nov 28 15:53:12 2002
+++ linux-2.4.21-pre6/arch/x86_64/lib/csum-copy.S	Wed Mar 26 16:11:35 2003
@@ -1,5 +1,5 @@
 /*
- * Copyright 2002 Andi Kleen
+ * Copyright 2002,2003 Andi Kleen, SuSE Labs.
  *	
  * 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
@@ -8,7 +8,6 @@
  	#include <linux/linkage.h>
 	#include <asm/errno.h>
 
-//	#define FIX_ALIGNMENT 1
 /*
  * Checksum copy with exception handling.
  * On exceptions src_err_ptr or dst_err_ptr is set to -EFAULT and the 
@@ -26,17 +25,14 @@
  * eax  64bit sum. undefined in case of exception.
  * 
  * Wrappers need to take care of valid exception sum and zeroing.		 
+ * They also should align source or destination to 8 bytes.
  */
 
-/* for now - should vary this based on direction */
- #define prefetch prefetcht2
- #define movnti   movq
-
 	.macro source
 10:
 	.section __ex_table,"a"
 	.align 8
-	.quad 10b,bad_source
+	.quad 10b,.Lbad_source
 	.previous
 	.endm
 		
@@ -44,57 +40,55 @@
 20:
 	.section __ex_table,"a"
 	.align 8
-	.quad 20b,bad_dest
+	.quad 20b,.Lbad_dest
 	.previous
 	.endm
 			
 	.globl csum_partial_copy_generic
-	.p2align
+	.p2align 4
 csum_partial_copy_generic:
-	prefetchnta (%rdi)
+	cmpl	 $3*64,%edx
+	jle	 1f
+
+	prefetch (%rdi)
+	prefetch 1*64(%rdi)
+	prefetch 2*64(%rdi)
+	prefetch 3*64(%rdi)
+	prefetch 4*64(%rdi)
+	prefetchw (%rsi)
+	prefetchw 1*64(%rsi)
+	prefetchw 2*64(%rsi)
+	prefetchw 3*64(%rsi)
+	prefetchw 4*64(%rsi)
+
+1:			
+	subq  $7*8,%rsp
+	movq  %rbx,2*8(%rsp)
+	movq  %r12,3*8(%rsp)
+	movq  %r14,4*8(%rsp)
+	movq  %r13,5*8(%rsp)
+	movq  %rbp,6*8(%rsp)
+
+	movq  %r8,(%rsp)
+	movq  %r9,1*8(%rsp)
 	
-	pushq %rbx
-	pushq %r12
-	pushq %r14
-	pushq %r15
-	movq %r8,%r14
-	movq %r9,%r15
 	movl  %ecx,%eax
 	movl  %edx,%ecx
 
-#ifdef FIX_ALIGNMENT
-	/* align source to 8 bytes */	
-	movl %edi,%r8d
-	andl $7,%r8d
-	jnz  bad_alignment	
-after_bad_alignment:
-#endif
-
-	movl  $64,%r10d
 	xorl  %r9d,%r9d
 	movq  %rcx,%r12
 
 	shrq  $6,%r12
-	/* loopcounter is maintained as one less to test efficiently for the
-	   previous to last iteration. This is needed to stop the prefetching. */
-	decq  %r12
-	js    handle_tail       /* < 64 */
-	jz    loop_no_prefetch  /* = 64 + X */ 
+	jz    .Lhandle_tail       /* < 64 */
+
+	clc
 	
 	/* main loop. clear in 64 byte blocks */
-	/* tries hard not to prefetch over the boundary */
-	/* r10:	64, r9: zero, r8: temp2, rbx: temp1, rax: sum, rcx: saved length */
+	/* r9: zero, r8: temp2, rbx: temp1, rax: sum, rcx: saved length */
 	/* r11:	temp3, rdx: temp4, r12 loopcnt */
-	.p2align
-loop:
-	/* Could prefetch more than one loop, but then it would be even
-	   trickier to avoid prefetching over the boundary. The hardware prefetch
-	   should take care of this anyways. The reason for this prefetch is
-	   just the non temporal hint to avoid cache pollution. Hopefully this
-	   will be handled properly by the hardware. */
-	prefetchnta 64(%rdi) 
-	
-loop_no_prefetch:
+	/* r10:	temp5, rbp: temp6, r14 temp7, r13 temp8 */
+	.p2align 4
+.Lloop:
 	source
 	movq  (%rdi),%rbx
 	source
@@ -104,175 +98,133 @@
 	source
 	movq  24(%rdi),%rdx
 
-	dest
-	movnti %rbx,(%rsi)
-	dest
-	movnti %r8,8(%rsi)
-	dest
-	movnti %r11,16(%rsi)
-	dest
-	movnti %rdx,24(%rsi)
+	source
+	movq  32(%rdi),%r10
+	source
+	movq  40(%rdi),%rbp
+	source
+	movq  48(%rdi),%r14
+	source
+	movq  56(%rdi),%r13
 		
-	addq  %rbx,%rax
+	prefetch 5*64(%rdi)
+						
+	adcq  %rbx,%rax
 	adcq  %r8,%rax
 	adcq  %r11,%rax
 	adcq  %rdx,%rax
+	adcq  %r10,%rax
+	adcq  %rbp,%rax
+	adcq  %r14,%rax
+	adcq  %r13,%rax
 
-	source
-	movq  32(%rdi),%rbx
-	source
-	movq  40(%rdi),%r8
-	source
-	movq  48(%rdi),%r11
-	source
-	movq  56(%rdi),%rdx
+	decl %r12d
 	
 	dest
-	movnti %rbx,32(%rsi)
+	movq %rbx,(%rsi)
 	dest
-	movnti %r8,40(%rsi)
+	movq %r8,8(%rsi)
 	dest
-	movnti %r11,48(%rsi)
+	movq %r11,16(%rsi)
 	dest
-	movnti %rdx,56(%rsi)
+	movq %rdx,24(%rsi)
 
-	adcq %rbx,%rax
-	adcq %r8,%rax
-	adcq %r11,%rax
-	adcq %rdx,%rax
+	dest
+	movq %r10,32(%rsi)
+	dest
+	movq %rbp,40(%rsi)
+	dest
+	movq %r14,48(%rsi)
+	dest
+	movq %r13,56(%rsi)
 	
-	adcq %r9,%rax	/* add in carry */
+	prefetchw 5*64(%rsi)
 	
-	addq %r10,%rdi		
-	addq %r10,%rsi
+	leaq 64(%rdi),%rdi
+	leaq 64(%rsi),%rsi
+
+	jnz   .Lloop
 
-	decq %r12
-	jz   loop_no_prefetch	/* previous to last iteration? */
-	jns  loop
+	adcq  %r9,%rax
 
 	/* do last upto 56 bytes */
-handle_tail:
+.Lhandle_tail:
 	/* ecx:	count */
 	movl %ecx,%r10d
 	andl $63,%ecx
 	shrl $3,%ecx
-	jz 	 fold
+	jz 	 .Lfold
 	clc
-	movl $8,%edx
-loop_8:	
+	.p2align 4
+.Lloop_8:	
 	source
 	movq (%rdi),%rbx
 	adcq %rbx,%rax
-	dest
-	movnti %rbx,(%rsi)
-	leaq (%rsi,%rdx),%rsi /* preserve carry */
-	leaq (%rdi,%rdx),%rdi
 	decl %ecx
-	jnz	loop_8
+	dest
+	movq %rbx,(%rsi)
+	leaq 8(%rsi),%rsi /* preserve carry */
+	leaq 8(%rdi),%rdi
+	jnz	.Lloop_8
 	adcq %r9,%rax	/* add in carry */
 
-fold:
+.Lfold:
+	/* reduce checksum to 32bits */
 	movl %eax,%ebx
 	shrq $32,%rax
-	addq %rbx,%rax
+	addl %ebx,%eax
+	adcl %r9d,%eax
 
 	/* do last upto 6 bytes */	
-handle_7:
+.Lhandle_7:
 	movl %r10d,%ecx
 	andl $7,%ecx
 	shrl $1,%ecx
-	jz   handle_1
+	jz   .Lhandle_1
 	movl $2,%edx
 	xorl %ebx,%ebx
 	clc  
-loop_1:	
+	.p2align 4
+.Lloop_1:	
 	source
 	movw (%rdi),%bx
-	adcq %rbx,%rax
+	adcl %ebx,%eax
 	dest
-	movw %bx,(%rsi)
-	addq %rdx,%rdi
-	addq %rdx,%rsi
 	decl %ecx
-	jnz loop_1
-	adcw %r9w,%ax	/* add in carry */
+	movw %bx,(%rsi)
+	leaq 2(%rdi),%rdi
+	leaq 2(%rsi),%rsi
+	jnz .Lloop_1
+	adcl %r9d,%eax	/* add in carry */
 	
 	/* handle last odd byte */
-handle_1:	
+.Lhandle_1:
 	testl $1,%r10d
-	jz    ende
+	jz    .Lende
 	xorl  %ebx,%ebx
 	source
 	movb (%rdi),%bl
 	dest
 	movb %bl,(%rsi)
-	addw %bx,%ax
-	adcw %r9w,%ax		/* carry */
+	addl %ebx,%eax
+	adcl %r9d,%eax		/* carry */
 			
-ende:		
-	sfence
-	popq %r15
-	popq %r14
-	popq %r12
-	popq %rbx
+.Lende:
+	movq 2*8(%rsp),%rbx
+	movq 3*8(%rsp),%r12
+	movq 4*8(%rsp),%r14
+	movq 5*8(%rsp),%r13
+	movq 6*8(%rsp),%rbp
+	addq $7*8,%rsp
 	ret
 
-#ifdef FIX_ALIGNMENT
-	/* align source to 8 bytes. */
-	/* r8d:	unalignedness, ecx len */
-bad_alignment:
-	testl $1,%edi
-	jnz   odd_source
-
-	/* compute distance to next aligned position */
-	movl $8,%r8d
-	xchgl %r8d,%ecx
-	subl %r8d,%ecx
-
-	/* handle unaligned part */
-	shrl $1,%ecx
-	xorl %ebx,%ebx	
-	movl $2,%r10d
-align_loop:
-	source
-	movw (%rdi),%bx
-	addq %rbx,%rax	/* carry cannot happen */
-	dest
-	movw %bx,(%rsi)
-	addq %r10,%rdi
-	addq %r10,%rsi
-	decl %ecx
-	jnz align_loop
-	jmp after_bad_alignment
-
-	/* weird case. need to swap the sum at the end because the spec requires
-	   16 bit words of the sum to be always paired. 
-	   handle it recursively because it should be rather rare. */
-odd_source:
-	/* copy odd byte */
-	xorl %ebx,%ebx
-	source
-	movb (%rdi),%bl
-	addl %ebx,%eax       /* add to old checksum */
-	adcl $0,%ecx
-	dest
-	movb %al,(%rsi)
-	
-	/* fix arguments */
-	movl %eax,%ecx
-	incq %rsi
-	incq %rdi
-	decq %rdx 
-	call csum_partial_copy_generic
-	bswap %eax        /* this should work, but check */
-	jmp ende
-#endif
-
 	/* Exception handlers. Very simple, zeroing is done in the wrappers */
-bad_source:
-	movl $-EFAULT,(%r14)
-	jmp  ende
-	
-bad_dest:
-	movl $-EFAULT,(%r15)
-	jmp ende
+.Lbad_source:
+	movq (%rsp),%rax
+	movl $-EFAULT,(%rax)
+	jmp  .Lende
+	
+.Lbad_dest:
+	movq 8(%rsp),%rax
+	movl $-EFAULT,(%rax)
+	jmp .Lende
diff -urN linux-2.4.21-pre5/arch/x86_64/lib/csum-partial.c linux-2.4.21-pre6/arch/x86_64/lib/csum-partial.c
--- linux-2.4.21-pre5/arch/x86_64/lib/csum-partial.c	Thu Nov 28 15:53:12 2002
+++ linux-2.4.21-pre6/arch/x86_64/lib/csum-partial.c	Wed Mar 26 16:11:35 2003
@@ -7,35 +7,39 @@
  
 #include <linux/compiler.h>
 #include <linux/module.h>
+#include <asm/checksum.h>
 
-/* Better way for this sought */
-static inline unsigned short from64to16(unsigned long x)
+#define __force_inline inline __attribute__((always_inline))
+
+static inline unsigned short from32to16(unsigned a) 
 {
-	/* add up 32-bit words for 33 bits */
-	x = (x & 0xffffffff) + (x >> 32);
-	/* add up 16-bit and 17-bit words for 17+c bits */
-	x = (x & 0xffff) + (x >> 16);
-	/* add up 16-bit and 2-bit for 16+c bit */
-	x = (x & 0xffff) + (x >> 16);
-	/* add up carry.. */
-	x = (x & 0xffff) + (x >> 16);
-	return x;
+	unsigned short b = a >> 16; 
+	asm("addw %w2,%w0\n\t"
+	    "adcw $0,%w0\n" 
+	    : "=r" (b)
+	    : "0" (b), "r" (a));
+	return b;
 }
 
 /*
  * Do a 64-bit checksum on an arbitrary memory area.
  * Returns a 32bit checksum.
  *
- * This isn't a great routine, but it's not _horrible_ either. 
- * We rely on the compiler to unroll.
+ * This isn't as time critical as it used to be because many NICs
+ * do hardware checksumming these days.
+ * 
+ * Things tried and found to not make it faster:
+ * Manual Prefetching
+ * Unrolling to an 128 bytes inner loop.
+ * Using interleaving with more registers to break the carry chains.
  */
-static inline unsigned do_csum(const unsigned char * buff, int len)
+static __force_inline unsigned do_csum(const unsigned char *buff, unsigned len)
 {
-	int odd, count;
+	unsigned odd, count;
 	unsigned long result = 0;
 
-	if (len <= 0)
-		goto out;
+	if (unlikely(len == 0))
+		return result; 
 	odd = 1 & (unsigned long) buff;
 	if (unlikely(odd)) {
 		result = *buff << 8;
@@ -45,7 +49,7 @@
 	count = len >> 1;		/* nr of 16-bit words.. */
 	if (count) {
 		if (2 & (unsigned long) buff) {
-			result += *(unsigned short *) buff;
+			result += *(unsigned short *)buff;
 			count--;
 			len -= 2;
 			buff += 2;
@@ -59,18 +63,41 @@
 				buff += 4;
 			}
 			count >>= 1;	/* nr of 64-bit words.. */
-			if (count) {
+
+			/* main loop using 64byte blocks */
 				unsigned long zero = 0; 
-				do {
-					asm("  addq %1,%0\n"
-					    "  adcq %2,%0\n" 
+			unsigned count64 = count >> 3; 
+			while (count64) { 
+				asm("addq 0*8(%[src]),%[res]\n\t"
+				    "adcq 1*8(%[src]),%[res]\n\t"
+				    "adcq 2*8(%[src]),%[res]\n\t"
+				    "adcq 3*8(%[src]),%[res]\n\t"
+				    "adcq 4*8(%[src]),%[res]\n\t"
+				    "adcq 5*8(%[src]),%[res]\n\t"
+				    "adcq 6*8(%[src]),%[res]\n\t"
+				    "adcq 7*8(%[src]),%[res]\n\t"
+				    "adcq %[zero],%[res]"
+				    : [res] "=r" (result)
+				    : [src] "r" (buff), [zero] "r" (zero),
+				    "[res]" (result));
+				buff += 64;
+				count64--;
+			}
+
+			/* last upto 7 8byte blocks */
+			count %= 8; 
+			while (count) { 
+				asm("addq %1,%0\n\t"
+				    "adcq %2,%0\n" 
 					    : "=r" (result)
-					    : "m"  (*buff), "r" (zero),  "0" (result));
-					count--;
+				    : "m" (*(unsigned long *)buff), 
+				    "r" (zero),  "0" (result));
+				--count; 
 					buff += 8;
-				} while (count);
-				result = (result & 0xffffffff) + (result >> 32);
 			}
+			result = add32_with_carry(result>>32,
+						  result&0xffffffff); 
+
 			if (len & 4) {
 				result += *(unsigned int *) buff;
 				buff += 4;
@@ -83,10 +110,11 @@
 	}
 	if (len & 1)
 		result += *buff;
-	result = from64to16(result);
-	if (unlikely(odd))
-		return ((result >> 8) & 0xff) | ((result & 0xff) << 8);
-out:
+	result = add32_with_carry(result>>32, result & 0xffffffff); 
+	if (unlikely(odd)) { 
+		result = from32to16(result);
+		result = ((result >> 8) & 0xff) | ((result & 0xff) << 8);
+	}
 	return result;
 }
 
@@ -102,14 +130,9 @@
  *
  * it's best to have buff aligned on a 64-bit boundary
  */
-unsigned int csum_partial(const unsigned char * buff, int len, unsigned int sum)
+unsigned csum_partial(const unsigned char *buff, unsigned len, unsigned sum)
 {
-	unsigned result = do_csum(buff, len);
-
-	/* add in old sum, and carry.. */
-	asm("addl %1,%0\n\t"
-	    "adcl $0,%0" : "=r" (result) : "r" (sum), "0" (result)); 
-	return result;
+	return add32_with_carry(do_csum(buff, len), sum); 
 }
 
 /*
diff -urN linux-2.4.21-pre5/arch/x86_64/lib/csum-wrappers.c linux-2.4.21-pre6/arch/x86_64/lib/csum-wrappers.c
--- linux-2.4.21-pre5/arch/x86_64/lib/csum-wrappers.c	Thu Nov 28 15:53:12 2002
+++ linux-2.4.21-pre6/arch/x86_64/lib/csum-wrappers.c	Wed Mar 26 16:11:35 2003
@@ -1,4 +1,4 @@
-/* Copyright 2002 Andi Kleen, SuSE Labs.
+/* Copyright 2002,2003 Andi Kleen, SuSE Labs.
  * Subject to the GNU Public License v.2
  * 
  * Wrappers of assembly checksum functions for x86-64.
@@ -7,18 +7,6 @@
 #include <asm/checksum.h>
 #include <linux/module.h>
 
-/* Better way for this sought */
-static inline unsigned from64to32(unsigned long x)
-{
-	/* add up 32-bit words for 33 bits */
-        x = (x & 0xffffffff) + (x >> 32);
-        /* add up 16-bit and 17-bit words for 17+c bits */
-        x = (x & 0xffff) + (x >> 16);
-        /* add up 16-bit and 2-bit for 16+c bit */
-        x = (x & 0xffff) + (x >> 16);
-        return x;
-}
-
 /** 
  * csum_partial_copy_from_user - Copy and checksum from user space. 
  * @src: source address (user space) 
@@ -36,14 +24,32 @@
 { 
 	*errp = 0;
 	if (likely(access_ok(VERIFY_READ,src, len))) { 
-		unsigned long sum; 
-		sum = csum_partial_copy_generic(src,dst,len,isum,errp,NULL);
+		/* Why 6, not 7? To handle odd addresses aligned we
+		   would need to do considerable complications to fix the
+		   checksum which is defined as an 16bit accumulator. The
+		   fix alignment code is primarily for performance
+		   compatibility with 32bit and that will handle odd
+		   addresses slowly too. */
+		if (unlikely((unsigned long)src & 6)) {			
+			while (((unsigned long)src & 6) && len >= 2) { 
+				__u16 val16;			
+				*errp = __get_user(val16, (__u16 *)src); 
+				if (*errp)
+					return isum;
+				*(__u16 *)dst = val16;
+				isum = add32_with_carry(isum, val16); 
+				src += 2; 
+				dst += 2; 
+				len -= 2;
+			}
+		}
+		isum = csum_partial_copy_generic(src,dst,len,isum,errp,NULL);
 		if (likely(*errp == 0)) 
-			return from64to32(sum); 
+			return isum;
 	} 
 	*errp = -EFAULT;
 	memset(dst,0,len); 
-	return 0;		
+	return isum;		
 } 
 
 EXPORT_SYMBOL(csum_partial_copy_from_user);
@@ -67,8 +73,22 @@
 		*errp = -EFAULT;
 		return 0; 
 	}
+
+	if (unlikely((unsigned long)dst & 6)) {
+		while (((unsigned long)dst & 6) && len >= 2) { 
+			__u16 val16 = *(__u16 *)src;
+			isum = add32_with_carry(isum, val16);
+			*errp = __put_user(val16, (__u16 *)dst);
+			if (*errp)
+				return isum;
+			src += 2; 
+			dst += 2; 
+			len -= 2;
+		}
+	}
+
 	*errp = 0;
-	return from64to32(csum_partial_copy_generic(src,dst,len,isum,NULL,errp)); 
+	return csum_partial_copy_generic(src,dst,len,isum,NULL,errp); 
 } 
 
 EXPORT_SYMBOL(csum_partial_copy_to_user);
@@ -85,11 +105,9 @@
 unsigned int 
 csum_partial_copy_nocheck(const char *src, char *dst, int len, unsigned int sum)
 { 
-	return from64to32(csum_partial_copy_generic(src,dst,len,sum,NULL,NULL));
+	return csum_partial_copy_generic(src,dst,len,sum,NULL,NULL);
 } 
 
-//EXPORT_SYMBOL(csum_partial_copy_nocheck);
-
 unsigned short csum_ipv6_magic(struct in6_addr *saddr, struct in6_addr *daddr,
 			       __u32 len, unsigned short proto, unsigned int sum) 
 {
@@ -103,7 +121,7 @@
 	    "  adcq $0,%[sum]\n"
 	    : [sum] "=r" (sum64) 
 	    : "[sum]" (rest),[saddr] "r" (saddr), [daddr] "r" (daddr));
-	return csum_fold(from64to32(sum64)); 
+	return csum_fold(add32_with_carry(sum64 & 0xffffffff, sum64>>32));
 }
 
 EXPORT_SYMBOL(csum_ipv6_magic);
diff -urN linux-2.4.21-pre5/arch/x86_64/lib/delay.c linux-2.4.21-pre6/arch/x86_64/lib/delay.c
--- linux-2.4.21-pre5/arch/x86_64/lib/delay.c	Thu Nov 28 15:53:12 2002
+++ linux-2.4.21-pre6/arch/x86_64/lib/delay.c	Wed Mar 26 16:11:35 2003
@@ -19,7 +19,6 @@
 
 void __delay(unsigned long loops)
 {
-#ifndef CONFIG_SIMNOW
 	unsigned long bclock, now;
 	
 	rdtscl(bclock);
@@ -29,7 +28,6 @@
 		rdtscl(now);
 	}
 	while((now-bclock) < loops);
-#endif
 }
 
 inline void __const_udelay(unsigned long xloops)
@@ -41,3 +39,9 @@
 {
 	__const_udelay(usecs * 0x000010c6);  /* 2**32 / 1000000 */
 }
+
+void __ndelay(unsigned long usecs)
+{
+       __const_udelay(usecs * 0x00005);  /* 2**32 / 1000000000 (rounded up) */
+}
+
diff -urN linux-2.4.21-pre5/arch/x86_64/lib/memcpy.S linux-2.4.21-pre6/arch/x86_64/lib/memcpy.S
--- linux-2.4.21-pre5/arch/x86_64/lib/memcpy.S	Thu Nov 28 15:53:12 2002
+++ linux-2.4.21-pre6/arch/x86_64/lib/memcpy.S	Wed Mar 26 16:11:35 2003
@@ -24,16 +24,16 @@
 #ifdef FIX_ALIGNMENT
 	movl %edi,%ecx
 	andl $7,%ecx
-	jnz  bad_alignment	
-after_bad_alignment:
+	jnz  .Lbad_alignment	
+.Lafter_bad_alignment:
 #endif
 
 	movq %rdx,%rcx
 	movl $64,%ebx
 	shrq $6,%rcx
-	jz handle_tail
+	jz .Lhandle_tail
 	
-loop_64:
+.Lloop_64:
 	movq (%rsi),%r11
 	movq 8(%rsi),%r8
 	movq 2*8(%rsi),%r9
@@ -55,35 +55,35 @@
 	addq %rbx,%rsi	
 	addq %rbx,%rdi
 	decl %ecx
-	jnz  loop_64
+	jnz  .Lloop_64
 
-handle_tail:
+.Lhandle_tail:
 	movl %edx,%ecx
 	andl $63,%ecx
 	shrl $3,%ecx
-	jz   handle_7
+	jz   .Lhandle_7
 	movl $8,%ebx
-loop_8: 
+.Lloop_8: 
 	movq (%rsi),%r8
 	movq %r8,(%rdi) 
 	addq %rbx,%rdi
 	addq %rbx,%rsi
 	decl %ecx
-	jnz  loop_8
+	jnz  .Lloop_8
 
-handle_7:
+.Lhandle_7:
 	movl %edx,%ecx
 	andl $7,%ecx
-	jz ende
-loop_1:
+	jz .Lende
+.Lloop_1:
 	movb (%rsi),%r8b
 	movb %r8b,(%rdi) 
 	incq %rdi
 	incq %rsi
 	decl %ecx
-	jnz loop_1
+	jnz .Lloop_1
 	
-ende: 	
+.Lende: 	
 	sfence
 	popq %rbx
 	ret
@@ -93,22 +93,22 @@
 	/* align destination */
 	/* This is simpleminded. For bigger blocks it may make sense to align
 	   src and dst to their aligned subset and handle the rest separately */
-bad_alignment:
+.Lbad_alignment:
 	movl $8,%r9d
 	subl %ecx,%r9d
 	movl %r9d,%ecx
 	subq %r9,%rdx
-	js   small_alignment
-	jz   small_alignment
-align_1:
+	js   .Lsmall_alignment
+	jz   .Lsmall_alignment
+.Lalign_1:
 	movb (%rsi),%r8b
 	movb %r8b,(%rdi) 
 	incq %rdi
 	incq %rsi
 	decl %ecx
-	jnz  align_1
-	jmp after_bad_alignment
-small_alignment:
+	jnz  .Lalign_1
+	jmp .Lafter_bad_alignment
+.Lsmall_alignment:
 	addq %r9,%rdx
-	jmp handle_7
+	jmp .Lhandle_7
 #endif	
diff -urN linux-2.4.21-pre5/arch/x86_64/lib/memset.S linux-2.4.21-pre6/arch/x86_64/lib/memset.S
--- linux-2.4.21-pre5/arch/x86_64/lib/memset.S	Thu Nov 28 15:53:12 2002
+++ linux-2.4.21-pre6/arch/x86_64/lib/memset.S	Wed Mar 26 16:11:35 2003
@@ -25,15 +25,15 @@
 	/* align dst */
 	movl  %edi,%r9d
 	andl  $7,%r9d	
-	jnz  bad_alignment
-after_bad_alignment:
+	jnz  .Lbad_alignment
+.Lafter_bad_alignment:
 
 	movq %r11,%rcx
 	movl $64,%r8d
 	shrq $6,%rcx
-	jz	 handle_tail
+	jz	 .Lhandle_tail
 
-loop_64:	
+.Lloop_64:	
 	movq  %rax,(%rdi) 
 	movq  %rax,8(%rdi) 
 	movq  %rax,16(%rdi) 
@@ -44,41 +44,41 @@
 	movq  %rax,56(%rdi) 
 	addq    %r8,%rdi
 	decl   %ecx
-	jnz    loop_64
+	jnz    .Lloop_64
 
 	/* Handle tail in loops. The loops should be faster than hard
 	   to predict jump tables. */ 
-handle_tail:
+.Lhandle_tail:
 	movl	%r11d,%ecx
 	andl    $63&(~7),%ecx
-	jz 		handle_7
+	jz 		.Lhandle_7
 	shrl	$3,%ecx
-loop_8:
+.Lloop_8:
 	movq  %rax,(%rdi) 
 	addq    $8,%rdi
 	decl   %ecx
-	jnz    loop_8
+	jnz    .Lloop_8
 
-handle_7:
+.Lhandle_7:
 	movl	%r11d,%ecx
 	andl	$7,%ecx
-	jz      ende
-loop_1:
+	jz      .Lende
+.Lloop_1:
 	movb 	%al,(%rdi)
 	addq	$1,%rdi
 	decl    %ecx
-	jnz     loop_1
+	jnz     .Lloop_1
 	
-ende:	
+.Lende:	
 	movq	%r10,%rax
 	ret
 
-bad_alignment:
+.Lbad_alignment:
 	cmpq $7,%r11
-	jbe	handle_7
+	jbe	.Lhandle_7
 	movq %rax,(%rdi)	/* unaligned store */
 	movq $8,%r8
 	subq %r9,%r8 
 	addq %r8,%rdi
 	subq %r8,%r11
-	jmp after_bad_alignment
+	jmp .Lafter_bad_alignment
diff -urN linux-2.4.21-pre5/arch/x86_64/mm/fault.c linux-2.4.21-pre6/arch/x86_64/mm/fault.c
--- linux-2.4.21-pre5/arch/x86_64/mm/fault.c	Wed Mar 26 16:11:28 2003
+++ linux-2.4.21-pre6/arch/x86_64/mm/fault.c	Wed Mar 26 16:11:35 2003
@@ -57,29 +57,41 @@
 	}
 }
 
+static int bad_address(void *p) 
+{ 
+	unsigned long dummy;
+	return __get_user(dummy, (unsigned long *)p);
+} 
+
 void dump_pagetable(unsigned long address)
 {
-	static char *name[] = { "PML4", "PGD", "PDE", "PTE" }; 
-	int i, shift;
-	unsigned long page;
-
-	shift = 9+9+9+12;
-	address &= ~0xFFFF000000000000UL;
-	asm("movq %%cr3,%0" : "=r" (page)); 
-	for (i = 0; i < 4; i++) { 	
-		unsigned long *padr = (unsigned long *) __va(page); 
-		padr += (address >> shift) & 0x1FFU;
-		if (__get_user(page, padr)) { 
-			printk("%s: bad %p\n", name[i], padr); 
-			break;
-		}
-		printk("%s: %016lx ", name[i], page); 
-		if ((page & (1 | (1<<7))) != 1) /* Not present or 2MB page */
-			break;
-		page &= ~0xFFFUL;
-		shift -= (i == 0) ? 12 : 9;
-	} 
+	pml4_t *pml4;
+	asm("movq %%cr3,%0" : "=r" (pml4));
+
+	pml4 = __va((unsigned long)pml4 & PHYSICAL_PAGE_MASK); 
+	pml4 += pml4_index(address);
+	printk("PML4 %lx ", pml4_val(*pml4));
+	if (bad_address(pml4)) goto bad;
+	if (!pml4_present(*pml4)) goto ret; 
+
+	pgd_t *pgd = __pgd_offset_k((pgd_t *)pml4_page(*pml4), address); 
+	if (bad_address(pgd)) goto bad;
+	printk("PGD %lx ", pgd_val(*pgd)); 
+	if (!pgd_present(*pgd))	goto ret;
+
+	pmd_t *pmd = pmd_offset(pgd, address); 
+	if (bad_address(pmd)) goto bad;
+	printk("PMD %lx ", pmd_val(*pmd));
+	if (!pmd_present(*pmd))	goto ret;	 
+
+	pte_t *pte = pte_offset(pmd, address);
+	if (bad_address(pte)) goto bad;
+	printk("PTE %lx", pte_val(*pte)); 
+ret:
 	printk("\n");
+	return;
+bad:
+	printk("BAD\n");
 }
 
 int page_fault_trace; 
diff -urN linux-2.4.21-pre5/arch/x86_64/mm/ioremap.c linux-2.4.21-pre6/arch/x86_64/mm/ioremap.c
--- linux-2.4.21-pre5/arch/x86_64/mm/ioremap.c	Thu Nov 28 15:53:12 2002
+++ linux-2.4.21-pre6/arch/x86_64/mm/ioremap.c	Wed Mar 26 16:11:35 2003
@@ -142,7 +142,7 @@
 	 */
 	offset = phys_addr & ~PAGE_MASK;
 	phys_addr &= PAGE_MASK;
-	size = PAGE_ALIGN(last_addr) - phys_addr;
+	size = PAGE_ALIGN(last_addr + 1) - phys_addr;
 
 	/*
 	 * Ok, go for it..
diff -urN linux-2.4.21-pre5/arch/x86_64/mm/k8topology.c linux-2.4.21-pre6/arch/x86_64/mm/k8topology.c
--- linux-2.4.21-pre5/arch/x86_64/mm/k8topology.c	Wed Mar 26 16:11:28 2003
+++ linux-2.4.21-pre6/arch/x86_64/mm/k8topology.c	Wed Mar 26 16:11:35 2003
@@ -6,7 +6,7 @@
  * Instead the northbridge registers are read directly. 
  * 
  * Copyright 2002 Andi Kleen, SuSE Labs.
- * $Id: k8topology.c,v 1.4 2003/02/11 04:52:33 ak Exp $
+ * $Id: k8topology.c,v 1.5 2003/03/10 14:33:40 ak Exp $
  */
 #include <linux/kernel.h>
 #include <linux/init.h>
@@ -23,7 +23,7 @@
 int memnode_shift;
 u8  memnodemap[NODEMAPSIZE];
 
-static int find_northbridge(void)
+static __init int find_northbridge(void)
 {
 	int num; 
 
@@ -53,7 +53,7 @@
 #define for_all_nodes(n) \
 	for (n=0; n<MAXNODE;n++) if (nodes[n].start!=nodes[n].end)
 
-static int compute_hash_shift(struct node *nodes, int numnodes, u64 maxmem)
+static __init int compute_hash_shift(struct node *nodes, int numnodes, u64 maxmem)
 {
 	int i; 
 	int shift = 24;
diff -urN linux-2.4.21-pre5/arch/x86_64/mm/pageattr.c linux-2.4.21-pre6/arch/x86_64/mm/pageattr.c
--- linux-2.4.21-pre5/arch/x86_64/mm/pageattr.c	Thu Nov 28 15:53:12 2002
+++ linux-2.4.21-pre6/arch/x86_64/mm/pageattr.c	Wed Mar 26 16:11:35 2003
@@ -48,18 +48,17 @@
 {
 	struct cpuinfo_x86 *cpu = &cpu_data[smp_processor_id()]; 
 	wmb(); 
-	/* Disabled for now because there seem to be some problems with CLFLUSH */
 	if (0 && test_bit(X86_FEATURE_CLFLSH, &cpu->x86_capability)) { 
 		/* is this worth it? */ 
 		int i;
 		for (i = 0; i < PAGE_SIZE; i += cpu->x86_clflush_size) 
-			asm volatile("clflush %0" :: "m" (__pa(address) + i)); 
+			asm volatile("clflush %0" :: "m" (address + i)); 
 	} else
 		asm volatile("wbinvd":::"memory"); 
 	__flush_tlb_one(address);
 }
 
-/* no more special protections in this 2/4MB area - revert to a
+/* no more special protections in this 2MB area - revert to a
    large page again. */
 static inline void revert_page(struct page *kpte_page, unsigned long address)
 {
diff -urN linux-2.4.21-pre5/drivers/acpi/acpi_ksyms.c linux-2.4.21-pre6/drivers/acpi/acpi_ksyms.c
--- linux-2.4.21-pre5/drivers/acpi/acpi_ksyms.c	Fri Aug  2 17:39:43 2002
+++ linux-2.4.21-pre6/drivers/acpi/acpi_ksyms.c	Wed Mar 26 16:11:35 2003
@@ -68,6 +68,7 @@
 EXPORT_SYMBOL(acpi_get_next_object);
 EXPORT_SYMBOL(acpi_evaluate_object);
 EXPORT_SYMBOL(acpi_get_table);
+EXPORT_SYMBOL(acpi_get_firmware_table);
 
 EXPORT_SYMBOL(acpi_install_notify_handler);
 EXPORT_SYMBOL(acpi_remove_notify_handler);
diff -urN linux-2.4.21-pre5/drivers/atm/fore200e.c linux-2.4.21-pre6/drivers/atm/fore200e.c
--- linux-2.4.21-pre5/drivers/atm/fore200e.c	Fri Aug  2 17:39:43 2002
+++ linux-2.4.21-pre6/drivers/atm/fore200e.c	Wed Mar 26 16:11:35 2003
@@ -1135,7 +1135,7 @@
 	return;
     } 
 
-    skb->stamp = vcc->timestamp = xtime;
+    skb->stamp = xtime;
     
 #ifdef FORE200E_52BYTE_AAL0_SDU
     if (cell_header) {
diff -urN linux-2.4.21-pre5/drivers/atm/suni.c linux-2.4.21-pre6/drivers/atm/suni.c
--- linux-2.4.21-pre5/drivers/atm/suni.c	Tue Nov 13 09:19:41 2001
+++ linux-2.4.21-pre6/drivers/atm/suni.c	Wed Mar 26 16:11:35 2003
@@ -233,8 +233,6 @@
 	if (!(PRIV(dev) = kmalloc(sizeof(struct suni_priv),GFP_KERNEL)))
 		return -ENOMEM;
 
-	MOD_INC_USE_COUNT;
-
 	PRIV(dev)->dev = dev;
 	spin_lock_irqsave(&sunis_lock,flags);
 	first = !sunis;
@@ -280,7 +278,6 @@
 	spin_unlock_irqrestore(&sunis_lock,flags);
 	kfree(PRIV(dev));
 
-	MOD_DEC_USE_COUNT;
 	return 0;
 }
 
@@ -293,7 +290,7 @@
 };
 
 
-int __init suni_init(struct atm_dev *dev)
+int suni_init(struct atm_dev *dev)
 {
 	unsigned char mri;
 
diff -urN linux-2.4.21-pre5/drivers/block/amiflop.c linux-2.4.21-pre6/drivers/block/amiflop.c
--- linux-2.4.21-pre5/drivers/block/amiflop.c	Wed Mar 26 16:11:28 2003
+++ linux-2.4.21-pre6/drivers/block/amiflop.c	Wed Mar 26 16:11:35 2003
@@ -431,7 +431,7 @@
 	motor_off_timer[drive].expires = jiffies + 3*HZ;
 	/* called this way it is always from interrupt */
 	motor_off_timer[drive].data = nr | 0x80000000;
-	add_timer(motor_off_timer + nr);
+	add_timer(motor_off_timer + drive);
 }
 
 static int fd_calibrate(int drive)
diff -urN linux-2.4.21-pre5/drivers/block/umem.c linux-2.4.21-pre6/drivers/block/umem.c
--- linux-2.4.21-pre5/drivers/block/umem.c	Wed Mar 26 16:11:28 2003
+++ linux-2.4.21-pre6/drivers/block/umem.c	Wed Mar 26 16:11:35 2003
@@ -1314,6 +1314,9 @@
 static const struct pci_device_id __devinitdata mm_pci_ids[] = { {
 	vendor:		PCI_VENDOR_ID_MICRO_MEMORY,
 	device:		PCI_DEVICE_ID_MICRO_MEMORY_5415CN,
+	}, {
+	vendor:		PCI_VENDOR_ID_MICRO_MEMORY,
+	device:		PCI_DEVICE_ID_MICRO_MEMORY_5425CN,
 	}, { /* end: all zeroes */ }
 };
 
diff -urN linux-2.4.21-pre5/drivers/char/Makefile linux-2.4.21-pre6/drivers/char/Makefile
--- linux-2.4.21-pre5/drivers/char/Makefile	Wed Mar 26 16:11:28 2003
+++ linux-2.4.21-pre6/drivers/char/Makefile	Wed Mar 26 16:11:35 2003
@@ -237,6 +237,7 @@
 obj-$(CONFIG_PC110_PAD) += pc110pad.o
 obj-$(CONFIG_MK712_MOUSE) += mk712.o
 obj-$(CONFIG_RTC) += rtc.o
+obj-$(CONFIG_GEN_RTC) += genrtc.o
 obj-$(CONFIG_EFI_RTC) += efirtc.o
 ifeq ($(CONFIG_PPC),)
   obj-$(CONFIG_NVRAM) += nvram.o
diff -urN linux-2.4.21-pre5/drivers/char/epca.c linux-2.4.21-pre6/drivers/char/epca.c
--- linux-2.4.21-pre5/drivers/char/epca.c	Wed Mar 26 16:11:28 2003
+++ linux-2.4.21-pre6/drivers/char/epca.c	Wed Mar 26 16:11:35 2003
@@ -3755,7 +3755,7 @@
 
 			case 5:
 				board.port = (unsigned char *)ints[index];
-				if (board.port <= 0)
+				if (ints[index] <= 0)
 				{
 					printk(KERN_ERR "<Error> - epca_setup: Invalid io port 0x%x\n", (unsigned int)board.port);
 					invalid_lilo_config = 1;
@@ -3767,7 +3767,7 @@
 
 			case 6:
 				board.membase = (unsigned char *)ints[index];
-				if (board.membase <= 0)
+				if (ints[index] <= 0)
 				{
 					printk(KERN_ERR "<Error> - epca_setup: Invalid memory base 0x%x\n",(unsigned int)board.membase);
 					invalid_lilo_config = 1;
diff -urN linux-2.4.21-pre5/drivers/char/genrtc.c linux-2.4.21-pre6/drivers/char/genrtc.c
--- linux-2.4.21-pre5/drivers/char/genrtc.c	Wed Dec 31 16:00:00 1969
+++ linux-2.4.21-pre6/drivers/char/genrtc.c	Wed Mar 26 16:11:35 2003
@@ -0,0 +1,533 @@
+/*
+ *	Real Time Clock interface for
+ *		- q40 and other m68k machines,
+ *		- HP PARISC machines
+ *		- PowerPC machines
+ *      emulate some RTC irq capabilities in software
+ *
+ *      Copyright (C) 1999 Richard Zidlicky
+ *
+ *	based on Paul Gortmaker's rtc.c device and
+ *           Sam Creasey Generic rtc driver
+ *
+ *	This driver allows use of the real time clock (built into
+ *	nearly all computers) from user space. It exports the /dev/rtc
+ *	interface supporting various ioctl() and also the /proc/dev/rtc
+ *	pseudo-file for status information.
+ *
+ *	The ioctls can be used to set the interrupt behaviour where
+ *	supported.
+ *
+ *	The /dev/rtc interface will block on reads until an interrupt
+ *	has been received. If a RTC interrupt has already happened,
+ *	it will output an unsigned long and then block. The output value
+ *	contains the interrupt status in the low byte and the number of
+ *	interrupts since the last read in the remaining high bytes. The
+ *	/dev/rtc interface can also be used with the select(2) call.
+ *
+ *	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.
+ *
+
+ *      1.01 fix for 2.3.X                    rz@linux-m68k.org
+ *      1.02 merged with code from genrtc.c   rz@linux-m68k.org
+ *      1.03 make it more portable            zippel@linux-m68k.org
+ *      1.04 removed useless timer code       rz@linux-m68k.org
+ *      1.05 portable RTC_UIE emulation       rz@linux-m68k.org
+ *      1.06 set_rtc_time can return an error trini@kernel.crashing.org
+ *      1.07 ported to HP PARISC (hppa)	      Helge Deller <deller@gmx.de>
+ */
+
+#define RTC_VERSION	"1.07"
+
+#include <linux/module.h>
+#include <linux/config.h>
+#include <linux/errno.h>
+#include <linux/miscdevice.h>
+#include <linux/fcntl.h>
+
+#include <linux/rtc.h>
+#include <linux/init.h>
+#include <linux/poll.h>
+#include <linux/proc_fs.h>
+
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <asm/rtc.h>
+
+/*
+ *	We sponge a minor off of the misc major. No need slurping
+ *	up another valuable major dev number for this. If you add
+ *	an ioctl, make sure you don't conflict with SPARC's RTC
+ *	ioctls.
+ */
+
+static DECLARE_WAIT_QUEUE_HEAD(gen_rtc_wait);
+
+/*
+ *	Bits in gen_rtc_status.
+ */
+
+#define RTC_IS_OPEN		0x01	/* means /dev/rtc is in use	*/
+
+static unsigned char gen_rtc_status;	/* bitmapped status byte.	*/
+static unsigned long gen_rtc_irq_data;	/* our output to the world	*/
+
+/* months start at 0 now */
+static unsigned char days_in_mo[] =
+{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
+
+static int irq_active;
+
+#ifdef CONFIG_GEN_RTC_X
+struct tq_struct genrtc_task;
+static struct timer_list timer_task;
+
+static unsigned int oldsecs;
+static int lostint;
+static int tt_exp;
+
+static void gen_rtc_timer(unsigned long data);
+
+static volatile int stask_active;              /* schedule_task */
+static volatile int ttask_active;              /* timer_task */
+static int stop_rtc_timers;                    /* don't requeue tasks */
+static spinlock_t gen_rtc_lock = SPIN_LOCK_UNLOCKED;
+
+static void gen_rtc_interrupt(unsigned long arg);
+
+/*
+ * Routine to poll RTC seconds field for change as often as posible,
+ * after first RTC_UIE use timer to reduce polling
+ */
+static void genrtc_troutine(void *data)
+{
+	unsigned int tmp = get_rtc_ss();
+	
+	if (stop_rtc_timers) {
+		stask_active = 0;
+		return;
+	}
+
+	if (oldsecs != tmp){
+		oldsecs = tmp;
+
+		timer_task.function = gen_rtc_timer;
+		timer_task.expires = jiffies + HZ - (HZ/10);
+		tt_exp=timer_task.expires;
+		ttask_active=1;
+		stask_active=0;
+		add_timer(&timer_task);
+
+		gen_rtc_interrupt(0);
+	} else if (schedule_task(&genrtc_task) == 0)
+		stask_active = 0;
+}
+
+static void gen_rtc_timer(unsigned long data)
+{
+	lostint = get_rtc_ss() - oldsecs ;
+	if (lostint<0) 
+		lostint = 60 - lostint;
+	if (time_after(jiffies, tt_exp))
+		printk(KERN_INFO "genrtc: timer task delayed by %ld jiffies\n",
+		       jiffies-tt_exp);
+	ttask_active=0;
+	stask_active=1;
+	if ((schedule_task(&genrtc_task) == 0))
+		stask_active = 0;
+}
+
+/* 
+ * call gen_rtc_interrupt function to signal an RTC_UIE,
+ * arg is unused.
+ * Could be invoked either from a real interrupt handler or
+ * from some routine that periodically (eg 100HZ) monitors
+ * whether RTC_SECS changed
+ */
+static void gen_rtc_interrupt(unsigned long arg)
+{
+	/*  We store the status in the low byte and the number of
+	 *	interrupts received since the last read in the remainder
+	 *	of rtc_irq_data.  */
+
+	gen_rtc_irq_data += 0x100;
+	gen_rtc_irq_data &= ~0xff;
+	gen_rtc_irq_data |= RTC_UIE;
+
+	if (lostint){
+		printk("genrtc: system delaying clock ticks?\n");
+		/* increment count so that userspace knows something is wrong */
+		gen_rtc_irq_data += ((lostint-1)<<8);
+		lostint = 0;
+	}
+
+	wake_up_interruptible(&gen_rtc_wait);
+}
+
+/*
+ *	Now all the various file operations that we export.
+ */
+static ssize_t gen_rtc_read(struct file *file, char *buf,
+			size_t count, loff_t *ppos)
+{
+	DECLARE_WAITQUEUE(wait, current);
+	unsigned long data;
+	ssize_t retval;
+
+	if (count != sizeof (unsigned int) && count != sizeof (unsigned long))
+		return -EINVAL;
+
+	if (file->f_flags & O_NONBLOCK && !gen_rtc_irq_data)
+		return -EAGAIN;
+
+	add_wait_queue(&gen_rtc_wait, &wait);
+	retval = -ERESTARTSYS;
+
+	while (1) {
+		set_current_state(TASK_INTERRUPTIBLE);
+		data = xchg(&gen_rtc_irq_data, 0);
+		if (data)
+			break;
+		if (signal_pending(current))
+			goto out;
+		schedule();
+	}
+
+	/* first test allows optimizer to nuke this case for 32-bit machines */
+	if (sizeof (int) != sizeof (long) && count == sizeof (unsigned int)) {
+		unsigned int uidata = data;
+		retval = put_user(uidata, (unsigned long *)buf);
+	}
+	else {
+		retval = put_user(data, (unsigned long *)buf);
+	}
+	if (!retval)
+		retval = sizeof(unsigned long);
+ out:
+	current->state = TASK_RUNNING;
+	remove_wait_queue(&gen_rtc_wait, &wait);
+
+	return retval;
+}
+
+static unsigned int gen_rtc_poll(struct file *file,
+				 struct poll_table_struct *wait)
+{
+	poll_wait(file, &gen_rtc_wait, wait);
+	if (gen_rtc_irq_data != 0)
+		return POLLIN | POLLRDNORM;
+	return 0;
+}
+
+#endif
+
+/*
+ * Used to disable/enable interrupts, only RTC_UIE supported
+ * We also clear out any old irq data after an ioctl() that
+ * meddles with the interrupt enable/disable bits.
+ */
+
+static inline void gen_clear_rtc_irq_bit(unsigned char bit)
+{
+#ifdef CONFIG_GEN_RTC_X
+	stop_rtc_timers = 1;
+	if (ttask_active){
+		del_timer_sync(&timer_task);
+		ttask_active = 0;
+	}
+	while (stask_active)
+		schedule();
+
+	spin_lock(&gen_rtc_lock);
+	irq_active = 0;
+	spin_unlock(&gen_rtc_lock);
+#endif
+}
+
+static inline int gen_set_rtc_irq_bit(unsigned char bit)
+{
+#ifdef CONFIG_GEN_RTC_X
+	spin_lock(&gen_rtc_lock);
+	if ( !irq_active ) {
+		irq_active = 1;
+		stop_rtc_timers = 0;
+		lostint = 0;
+		genrtc_task.routine = genrtc_troutine;
+		oldsecs = get_rtc_ss();
+		init_timer(&timer_task);
+
+		stask_active = 1;
+		if (schedule_task(&genrtc_task) == 0){
+			stask_active = 0;
+		}
+	}
+	spin_unlock(&gen_rtc_lock);
+	gen_rtc_irq_data = 0;
+	return 0;
+#else
+	return -EINVAL;
+#endif
+}
+
+static int gen_rtc_ioctl(struct inode *inode, struct file *file,
+			 unsigned int cmd, unsigned long arg)
+{
+	struct rtc_time wtime;
+	struct rtc_pll_info pll;
+
+	switch (cmd) {
+
+	case RTC_PLL_GET:
+	    if (get_rtc_pll(&pll))
+	 	    return -EINVAL;
+	    else
+		    return copy_to_user((void *)arg, &pll, sizeof pll) ? -EFAULT : 0;
+
+	case RTC_PLL_SET:
+		if (!capable(CAP_SYS_TIME))
+			return -EACCES;
+		if (copy_from_user(&pll, (struct rtc_pll_info*)arg,
+				   sizeof(pll)))
+			return -EFAULT;
+	    return set_rtc_pll(&pll);
+
+	case RTC_UIE_OFF:	/* disable ints from RTC updates.	*/
+		gen_clear_rtc_irq_bit(RTC_UIE);
+		return 0;
+
+	case RTC_UIE_ON:	/* enable ints for RTC updates.	*/
+	        return gen_set_rtc_irq_bit(RTC_UIE);
+
+	case RTC_RD_TIME:	/* Read the time/date from RTC	*/
+		/* this doesn't get week-day, who cares */
+		memset(&wtime, 0, sizeof(wtime));
+		get_rtc_time(&wtime);
+
+		return copy_to_user((void *)arg, &wtime, sizeof(wtime)) ? -EFAULT : 0;
+
+	case RTC_SET_TIME:	/* Set the RTC */
+	    {
+		int year;
+		unsigned char leap_yr;
+
+		if (!capable(CAP_SYS_TIME))
+			return -EACCES;
+
+		if (copy_from_user(&wtime, (struct rtc_time *)arg,
+				   sizeof(wtime)))
+			return -EFAULT;
+
+		year = wtime.tm_year + 1900;
+		leap_yr = ((!(year % 4) && (year % 100)) ||
+			   !(year % 400));
+
+		if ((wtime.tm_mon < 0 || wtime.tm_mon > 11) || (wtime.tm_mday < 1))
+			return -EINVAL;
+
+		if (wtime.tm_mday < 0 || wtime.tm_mday >
+		    (days_in_mo[wtime.tm_mon] + ((wtime.tm_mon == 1) && leap_yr)))
+			return -EINVAL;
+
+		if (wtime.tm_hour < 0 || wtime.tm_hour >= 24 ||
+		    wtime.tm_min < 0 || wtime.tm_min >= 60 ||
+		    wtime.tm_sec < 0 || wtime.tm_sec >= 60)
+			return -EINVAL;
+
+		return set_rtc_time(&wtime);
+	    }
+	}
+
+	return -EINVAL;
+}
+
+/*
+ *	We enforce only one user at a time here with the open/close.
+ *	Also clear the previous interrupt data on an open, and clean
+ *	up things on a close.
+ */
+
+static int gen_rtc_open(struct inode *inode, struct file *file)
+{
+	if (gen_rtc_status & RTC_IS_OPEN)
+		return -EBUSY;
+
+	MOD_INC_USE_COUNT;
+
+	gen_rtc_status |= RTC_IS_OPEN;
+	gen_rtc_irq_data = 0;
+	irq_active = 0;
+
+	return 0;
+}
+
+static int gen_rtc_release(struct inode *inode, struct file *file)
+{
+	/*
+	 * Turn off all interrupts once the device is no longer
+	 * in use and clear the data.
+	 */
+
+	gen_clear_rtc_irq_bit(RTC_PIE|RTC_AIE|RTC_UIE);
+
+	gen_rtc_status &= ~RTC_IS_OPEN;
+	MOD_DEC_USE_COUNT;
+
+	return 0;
+}
+
+static int gen_rtc_read_proc(char *page, char **start, off_t off,
+			     int count, int *eof, void *data);
+
+
+/*
+ *	The various file operations we support.
+ */
+
+static struct file_operations gen_rtc_fops = {
+	.owner		= THIS_MODULE,
+#ifdef CONFIG_GEN_RTC_X
+	.read		= gen_rtc_read,
+	.poll		= gen_rtc_poll,
+#endif
+	.ioctl		= gen_rtc_ioctl,
+	.open		= gen_rtc_open,
+	.release	= gen_rtc_release,
+};
+
+static struct miscdevice rtc_gen_dev =
+{
+	.minor		= RTC_MINOR,
+	.name		= "rtc",
+	.fops		= &gen_rtc_fops,
+};
+
+static int __init rtc_generic_init(void)
+{
+	int retval;
+
+	printk(KERN_INFO "Generic RTC Driver v%s\n", RTC_VERSION);
+
+	retval = misc_register(&rtc_gen_dev);
+	if(retval < 0)
+		return retval;
+
+#ifdef CONFIG_PROC_FS
+	if((create_proc_read_entry ("driver/rtc", 0, 0, gen_rtc_read_proc, NULL)) == NULL){
+		misc_deregister(&rtc_gen_dev);
+		return -ENOMEM;
+	}
+#endif
+
+	return 0;
+}
+
+static void __exit rtc_generic_exit(void)
+{
+	remove_proc_entry ("driver/rtc", NULL);
+	misc_deregister(&rtc_gen_dev);
+}
+
+module_init(rtc_generic_init);
+module_exit(rtc_generic_exit);
+EXPORT_NO_SYMBOLS;
+
+
+/*
+ *	Info exported via "/proc/rtc".
+ */
+
+#ifdef CONFIG_PROC_FS
+
+static int gen_rtc_proc_output(char *buf)
+{
+	char *p;
+	struct rtc_time tm;
+	unsigned int flags;
+	struct rtc_pll_info pll;
+
+	p = buf;
+
+	flags = get_rtc_time(&tm);
+
+	p += sprintf(p,
+		     "rtc_time\t: %02d:%02d:%02d\n"
+		     "rtc_date\t: %04d-%02d-%02d\n"
+		     "rtc_epoch\t: %04u\n",
+		     tm.tm_hour, tm.tm_min, tm.tm_sec,
+		     tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, 1900);
+
+	tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
+
+	p += sprintf(p, "alarm\t\t: ");
+	if (tm.tm_hour <= 24)
+		p += sprintf(p, "%02d:", tm.tm_hour);
+	else
+		p += sprintf(p, "**:");
+
+	if (tm.tm_min <= 59)
+		p += sprintf(p, "%02d:", tm.tm_min);
+	else
+		p += sprintf(p, "**:");
+
+	if (tm.tm_sec <= 59)
+		p += sprintf(p, "%02d\n", tm.tm_sec);
+	else
+		p += sprintf(p, "**\n");
+
+	p += sprintf(p,
+		     "DST_enable\t: %s\n"
+		     "BCD\t\t: %s\n"
+		     "24hr\t\t: %s\n"
+		     "square_wave\t: %s\n"
+		     "alarm_IRQ\t: %s\n"
+		     "update_IRQ\t: %s\n"
+		     "periodic_IRQ\t: %s\n"
+		     "periodic_freq\t: %ld\n"
+		     "batt_status\t: %s\n",
+		     (flags & RTC_DST_EN) ? "yes" : "no",
+		     (flags & RTC_DM_BINARY) ? "no" : "yes",
+		     (flags & RTC_24H) ? "yes" : "no",
+		     (flags & RTC_SQWE) ? "yes" : "no",
+		     (flags & RTC_AIE) ? "yes" : "no",
+		     irq_active ? "yes" : "no",
+		     (flags & RTC_PIE) ? "yes" : "no",
+		     0L /* freq */,
+		     (flags & RTC_BATT_BAD) ? "bad" : "okay");
+	if (!get_rtc_pll(&pll))
+	    p += sprintf(p,
+			 "PLL adjustment\t: %d\n"
+			 "PLL max +ve adjustment\t: %d\n"
+			 "PLL max -ve adjustment\t: %d\n"
+			 "PLL +ve adjustment factor\t: %d\n"
+			 "PLL -ve adjustment factor\t: %d\n"
+			 "PLL frequency\t: %ld\n",
+			 pll.pll_value,
+			 pll.pll_max,
+			 pll.pll_min,
+			 pll.pll_posmult,
+			 pll.pll_negmult,
+			 pll.pll_clock);
+	return p - buf;
+}
+
+static int gen_rtc_read_proc(char *page, char **start, off_t off,
+			     int count, int *eof, void *data)
+{
+	int len = gen_rtc_proc_output (page);
+        if (len <= off+count) *eof = 1;
+	*start = page + off;
+	len -= off;
+        if (len>count) len = count;
+        if (len<0) len = 0;
+	return len;
+}
+
+#endif /* CONFIG_PROC_FS */
+
+
+MODULE_AUTHOR("Richard Zidlicky");
+MODULE_LICENSE("GPL");
+
diff -urN linux-2.4.21-pre5/drivers/char/ipmi/ipmi_kcs_sm.c linux-2.4.21-pre6/drivers/char/ipmi/ipmi_kcs_sm.c
--- linux-2.4.21-pre5/drivers/char/ipmi/ipmi_kcs_sm.c	Wed Mar 26 16:11:28 2003
+++ linux-2.4.21-pre6/drivers/char/ipmi/ipmi_kcs_sm.c	Wed Mar 26 16:11:35 2003
@@ -220,6 +220,7 @@
 		kcs->ibf_timeout -= time;
 		if (kcs->ibf_timeout < 0) {
 			start_error_recovery(kcs, "IBF not ready in time");
+			kcs->ibf_timeout = IBF_RETRY_TIMEOUT;
 			return 1;
 		}
 		return 0;
@@ -331,6 +332,9 @@
 
 	switch (kcs->state) {
 	case KCS_IDLE:
+		/* If there's and interrupt source, turn it off. */
+		clear_obf(kcs, status);
+
 		if (GET_STATUS_ATN(status))
 			return KCS_ATTN;
 		else
@@ -398,13 +402,20 @@
 				"Not in read or idle in read state");
 			break;
 		}
-		if (! check_obf(kcs, status, time))
-			return KCS_CALL_WITH_DELAY;
 
 		if (state == KCS_READ_STATE) {
+			if (! check_obf(kcs, status, time))
+				return KCS_CALL_WITH_DELAY;
 			read_next_byte(kcs);
 		} else {
-			read_data(kcs);
+			/* We don't implement this exactly like the state
+			   machine in the spec.  Some broken hardware
+			   does not write the final dummy byte to the
+			   read register.  Thus obf will never go high
+			   here.  We just go straight to idle, and we
+			   handle clearing out obf in idle state if it
+			   happens to come in. */
+			clear_obf(kcs, status);
 			kcs->orig_write_count = 0;
 			kcs->state = KCS_IDLE;
 			return KCS_TRANSACTION_COMPLETE;
diff -urN linux-2.4.21-pre5/drivers/char/ipmi/ipmi_msghandler.c linux-2.4.21-pre6/drivers/char/ipmi/ipmi_msghandler.c
--- linux-2.4.21-pre5/drivers/char/ipmi/ipmi_msghandler.c	Wed Mar 26 16:11:28 2003
+++ linux-2.4.21-pre6/drivers/char/ipmi/ipmi_msghandler.c	Wed Mar 26 16:11:35 2003
@@ -52,6 +52,10 @@
 
 #define MAX_EVENTS_IN_QUEUE	25
 
+/* Don't let a message sit in a queue forever, always time it with at lest
+   the max message timer. */
+#define MAX_MSG_TIMEOUT		60000
+
 struct ipmi_user
 {
 	struct list_head link;
@@ -76,6 +80,38 @@
 	unsigned char cmd;
 };
 
+struct seq_table
+{
+	int                  inuse : 1;
+
+	unsigned long        timeout;
+	unsigned long        orig_timeout;
+	unsigned int         retries_left;
+
+	/* To verify on an incoming send message response that this is
+           the message that the response is for, we keep a sequence id
+           and increment it every time we send a message. */
+	long                 seqid;
+
+	/* This is held so we can properly respond to the message on a
+           timeout, and it is used to hold the temporary data for
+           retransmission, too. */
+	struct ipmi_recv_msg *recv_msg;
+};
+
+/* Store the information in a msgid (long) to allow us to find a
+   sequence table entry from the msgid. */
+#define STORE_SEQ_IN_MSGID(seq, seqid) (((seq&0xff)<<26) | (seqid&0x3ffffff))
+
+#define GET_SEQ_FROM_MSGID(msgid, seq, seqid) \
+	do {								\
+		seq = ((msgid >> 26) & 0x3f);				\
+		seqid = (msgid & 0x3fffff);				\
+        } while(0)
+
+#define NEXT_SEQID(seqid) (((seqid) + 1) & 0x3fffff)
+
+
 #define IPMI_IPMB_NUM_SEQ	64
 struct ipmi_smi
 {
@@ -99,12 +135,8 @@
            sequence numbers for IPMB messages that go out of the
            interface to match them up with their responses.  A routine
            is called periodically to time the items in this list. */
-	spinlock_t              seq_lock;
-	struct {
-		unsigned long   timeout;
-		int             inuse;
-		struct ipmi_recv_msg *recv_msg;
-	} seq_table[IPMI_IPMB_NUM_SEQ];
+	spinlock_t       seq_lock;
+	struct seq_table seq_table[IPMI_IPMB_NUM_SEQ];
 	int curr_seq;
 
 	/* Messages that were delayed for some reason (out of memory,
@@ -300,19 +332,20 @@
 }
 
 /* Find the next sequence number not being used and add the given
-   message with the given timeout to the sequence table. */
+   message with the given timeout to the sequence table.  This must be
+   called with the interface's seq_lock held. */
 static int intf_next_seq(ipmi_smi_t           intf,
 			 struct ipmi_recv_msg *recv_msg,
 			 unsigned long        timeout,
-			 unsigned char        *seq)
+			 int                  retries,
+			 unsigned char        *seq,
+			 long                 *seqid)
 {
-	int           rv = 0;
-	unsigned long flags;
-	unsigned int  i;
+	int          rv = 0;
+	unsigned int i;
 
-	spin_lock_irqsave(&(intf->seq_lock), flags);
 	for (i=intf->curr_seq;
-	     i!=(intf->curr_seq-1);
+	     (i+1)%IPMI_IPMB_NUM_SEQ != intf->curr_seq;
 	     i=(i+1)%IPMI_IPMB_NUM_SEQ)
 	{
 		if (! intf->seq_table[i].inuse)
@@ -321,16 +354,21 @@
 
 	if (! intf->seq_table[i].inuse) {
 		intf->seq_table[i].recv_msg = recv_msg;
-		intf->seq_table[i].timeout = timeout;
+
+		/* Start with the maximum timeout, when the send response
+		   comes in we will start the real timer. */
+		intf->seq_table[i].timeout = MAX_MSG_TIMEOUT;
+		intf->seq_table[i].orig_timeout = timeout;
+		intf->seq_table[i].retries_left = retries;
 		intf->seq_table[i].inuse = 1;
+		intf->seq_table[i].seqid = NEXT_SEQID(intf->seq_table[i].seqid);
 		*seq = i;
+		*seqid = intf->seq_table[i].seqid;
 		intf->curr_seq = (i+1)%IPMI_IPMB_NUM_SEQ;
 	} else {
 		rv = -EAGAIN;
 	}
 	
-	spin_unlock_irqrestore(&(intf->seq_lock), flags);
-
 	return rv;
 }
 
@@ -373,6 +411,33 @@
 }
 
 
+/* Start the timer for a specific sequence table entry. */
+static int intf_start_seq_timer(ipmi_smi_t           intf,
+				long                 msgid)
+{
+	int           rv = -ENODEV;
+	unsigned long flags;
+	unsigned char seq;
+	unsigned long seqid;
+
+
+	GET_SEQ_FROM_MSGID(msgid, seq, seqid);
+
+	spin_lock_irqsave(&(intf->seq_lock), flags);
+	/* We do this verification because the user can be deleted
+           while a message is outstanding. */
+	if ((intf->seq_table[seq].inuse)
+	    && (intf->seq_table[seq].seqid == seqid))
+	{
+		struct seq_table *ent = &(intf->seq_table[seq]);
+		ent->timeout = ent->orig_timeout;
+	}
+	spin_unlock_irqrestore(&(intf->seq_lock), flags);
+
+	return rv;
+}
+
+
 int ipmi_create_user(unsigned int          if_num,
 		     struct ipmi_user_hndl *handler,
 		     void                  *handler_data,
@@ -649,6 +714,48 @@
 	return -csum;
 }
 
+static inline void format_ipmb_msg(struct ipmi_smi_msg   *smi_msg,
+				   struct ipmi_msg       *msg,
+				   struct ipmi_ipmb_addr *ipmb_addr,
+				   long                  msgid,
+				   unsigned char         ipmb_seq,
+				   int                   broadcast,
+				   unsigned char         source_address,
+				   unsigned char         source_lun)
+{
+	int i = broadcast;
+
+	/* Format the IPMB header data. */
+	smi_msg->data[0] = (IPMI_NETFN_APP_REQUEST << 2);
+	smi_msg->data[1] = IPMI_SEND_MSG_CMD;
+	smi_msg->data[2] = ipmb_addr->channel;
+	if (broadcast)
+		smi_msg->data[3] = 0;
+	smi_msg->data[i+3] = ipmb_addr->slave_addr;
+	smi_msg->data[i+4] = (msg->netfn << 2) | (ipmb_addr->lun & 0x3);
+	smi_msg->data[i+5] = ipmb_checksum(&(smi_msg->data[i+3]), 2);
+	smi_msg->data[i+6] = source_address;
+	smi_msg->data[i+7] = (ipmb_seq << 2) | source_lun;
+	smi_msg->data[i+8] = msg->cmd;
+
+	/* Now tack on the data to the message. */
+	if (msg->data_len > 0)
+		memcpy(&(smi_msg->data[i+9]), msg->data,
+		       msg->data_len);
+	smi_msg->data_size = msg->data_len + 9;
+
+	/* Now calculate the checksum and tack it on. */
+	smi_msg->data[i+smi_msg->data_size]
+		= ipmb_checksum(&(smi_msg->data[i+6]),
+				smi_msg->data_size-6);
+
+	/* Add on the checksum size and the offset from the
+	   broadcast. */
+	smi_msg->data_size += 1 + i;
+
+	smi_msg->msgid = msgid;
+}
+
 /* Separate from ipmi_request so that the user does not have to be
    supplied in certain circumstances (mainly at panic time).  If
    messages are supplied, they will be freed, even if an error
@@ -667,6 +774,7 @@
 	int                  rv = 0;
 	struct ipmi_smi_msg  *smi_msg;
 	struct ipmi_recv_msg *recv_msg;
+	unsigned long        flags;
 
 
 	if (supplied_recv) {
@@ -693,13 +801,22 @@
 	    goto out_err;
 	}
 
+	recv_msg->user = user;
+	recv_msg->msgid = msgid;
+	/* Store the message to send in the receive message so timeout
+	   responses can get the proper response data. */
+	recv_msg->msg = *msg;
+
 	if (addr->addr_type == IPMI_SYSTEM_INTERFACE_ADDR_TYPE) {
 		struct ipmi_system_interface_addr *smi_addr;
 
+
 		smi_addr = (struct ipmi_system_interface_addr *) addr;
 		if (smi_addr->lun > 3)
 			return -EINVAL;
 
+		memcpy(&recv_msg->addr, smi_addr, sizeof(*smi_addr));
+
 		if ((msg->netfn == IPMI_NETFN_APP_REQUEST)
 		    && ((msg->cmd == IPMI_SEND_MSG_CMD)
 			|| (msg->cmd == IPMI_GET_MSG_CMD)
@@ -716,11 +833,6 @@
 			goto out_err;
 		}
 
-		recv_msg->user = user;
-		recv_msg->addr = *addr;
-		recv_msg->msgid = msgid;
-		recv_msg->msg = *msg;
-
 		smi_msg->data[0] = (msg->netfn << 2) | (smi_addr->lun & 0x3);
 		smi_msg->data[1] = msg->cmd;
 		smi_msg->msgid = msgid;
@@ -733,7 +845,9 @@
 	{
 		struct ipmi_ipmb_addr *ipmb_addr;
 		unsigned char         ipmb_seq;
-		int                   i;
+		long                  seqid;
+		int                   broadcast;
+		int                   retries;
 
 		if (addr == NULL) {
 			rv = -EINVAL;
@@ -744,75 +858,80 @@
 		    /* Broadcasts add a zero at the beginning of the
 		       message, but otherwise is the same as an IPMB
 		       address. */
-		    smi_msg->data[3] = 0;
 		    addr->addr_type = IPMI_IPMB_ADDR_TYPE;
-		    i = 1;
+		    broadcast = 1;
+		    retries = 0; /* Don't retry broadcasts. */
 		} else {
-		    i = 0;
+		    broadcast = 0;
+		    retries = 4;
 		}
 
 		/* 9 for the header and 1 for the checksum, plus
                    possibly one for the broadcast. */
-		if ((msg->data_len + 10 + i) > IPMI_MAX_MSG_LENGTH) {
+		if ((msg->data_len + 10 + broadcast) > IPMI_MAX_MSG_LENGTH) {
 			rv = -EMSGSIZE;
 			goto out_err;
 		}
 
 		ipmb_addr = (struct ipmi_ipmb_addr *) addr;
-		if (ipmb_addr->lun > 3)
-			return -EINVAL;
-
-		memcpy(&(recv_msg->addr), ipmb_addr, sizeof(*ipmb_addr));
+		if (ipmb_addr->lun > 3) {
+			rv = -EINVAL;
+			goto out_err;
+		}
 
-		recv_msg->user = user;
-		recv_msg->msgid = msgid;
-		recv_msg->msg = *msg;
+		memcpy(&recv_msg->addr, ipmb_addr, sizeof(*ipmb_addr));
 
 		if (recv_msg->msg.netfn & 0x1) {
-			/* It's a response, so use the user's sequence. */
-			ipmb_seq = msgid;
+			/* It's a response, so use the user's sequence
+                           from msgid. */
+			format_ipmb_msg(smi_msg, msg, ipmb_addr, msgid,
+					msgid, broadcast,
+					source_address, source_lun);
 		} else {
 			/* It's a command, so get a sequence for it. */
-			/* Create a sequence number with a 5 second timeout. */
+
+			spin_lock_irqsave(&(intf->seq_lock), flags);
+
+			/* Create a sequence number with a 1 second
+                           timeout and 4 retries. */
 			/* FIXME - magic number for the timeout. */
 			rv = intf_next_seq(intf,
 					   recv_msg,
-					   5000,
-					   &ipmb_seq);
+					   1000,
+					   retries,
+					   &ipmb_seq,
+					   &seqid);
 			if (rv) {
 				/* We have used up all the sequence numbers,
 				   probably, so abort. */
-				ipmi_free_recv_msg(recv_msg);
-				smi_msg->done(smi_msg);
+				spin_unlock_irqrestore(&(intf->seq_lock),
+						       flags);
 				goto out_err;
 			}
-		}
-
-		/* Format the IPMB header data. */
-		smi_msg->data[0] = (IPMI_NETFN_APP_REQUEST << 2);
-		smi_msg->data[1] = IPMI_SEND_MSG_CMD;
-		smi_msg->data[2] = addr->channel;
-		smi_msg->data[i+3] = ipmb_addr->slave_addr;
-		smi_msg->data[i+4] = (msg->netfn << 2) | (ipmb_addr->lun & 0x3);
-		smi_msg->data[i+5] = ipmb_checksum(&(smi_msg->data[i+3]), 2);
-		smi_msg->data[i+6] = source_address;
-		smi_msg->data[i+7] = (ipmb_seq << 2) | source_lun;
-		smi_msg->data[i+8] = msg->cmd;
 
-		/* Now tack on the data to the message. */
-		if (msg->data_len > 0)
-			memcpy(&(smi_msg->data[i+9]), msg->data, msg->data_len);
-		smi_msg->data_size = msg->data_len + 9;
+			/* Store the sequence number in the message,
+                           so that when the send message response
+                           comes back we can start the timer. */
+			format_ipmb_msg(smi_msg, msg, ipmb_addr,
+					STORE_SEQ_IN_MSGID(ipmb_seq, seqid),
+					ipmb_seq, broadcast,
+					source_address, source_lun);
+
+			/* Copy the message into the recv message data, so we
+			   can retransmit it later if necessary. */
+			memcpy(recv_msg->msg_data, smi_msg->data,
+			       smi_msg->data_size);
+			recv_msg->msg.data = recv_msg->msg_data;
+			recv_msg->msg.data_len = smi_msg->data_size;
 
-		/* Now calculate the checksum and tack it on. */
-		smi_msg->data[i+smi_msg->data_size]
-		    = ipmb_checksum(&(smi_msg->data[i+6]), smi_msg->data_size-6);
-
-		/* Add on the checksum size and the offset from the
-                   broadcast. */
-		smi_msg->data_size += 1 + i;
-		
-		smi_msg->msgid = msgid;
+			/* We don't unlock until here, because we need
+                           to copy the completed message into the
+                           recv_msg before we release the lock.
+                           Otherwise, race conditions may bite us.  I
+                           know that's pretty paranoid, but I prefer
+                           to be correct. */
+			spin_unlock_irqrestore(&(intf->seq_lock), flags);
+		}
 	} else {
 	    /* Unknown address type. */
 	    rv = -EINVAL;
@@ -832,8 +951,8 @@
 	return 0;
 
  out_err:
-	smi_msg->done(smi_msg);
-	recv_msg->done(recv_msg);
+	ipmi_free_smi_msg(smi_msg);
+	ipmi_free_recv_msg(recv_msg);
 	return rv;
 }
 
@@ -936,8 +1055,10 @@
 			new_intf->handlers = handlers;
 			new_intf->send_info = send_info;
 			spin_lock_init(&(new_intf->seq_lock));
-			for (j=0; j<IPMI_IPMB_NUM_SEQ; j++)
-			    new_intf->seq_table[j].inuse = 0;
+			for (j=0; j<IPMI_IPMB_NUM_SEQ; j++) {
+				new_intf->seq_table[j].inuse = 0;
+				new_intf->seq_table[j].seqid = 0;
+			}
 			new_intf->curr_seq = 0;
 			spin_lock_init(&(new_intf->waiting_msgs_lock));
 			INIT_LIST_HEAD(&(new_intf->waiting_msgs));
@@ -1426,24 +1547,25 @@
 	int           rv;
 
 
-	if ((msg->data_size >= 2) && (msg->data[1] == IPMI_SEND_MSG_CMD)) {
-		/* This is the local response to a send, we just
-                   ignore these. */
-		msg->done(msg);
-		return;
-	}
-
 	/* Lock the user lock so the user can't go away while we are
 	   working on it. */
 	read_lock(&(intf->users_lock));
 
+	if ((msg->data_size >= 2) && (msg->data[1] == IPMI_SEND_MSG_CMD)) {
+		/* This is the local response to a send, start the
+                   timer for these. */
+		intf_start_seq_timer(intf, msg->msgid);
+		ipmi_free_smi_msg(msg);
+		goto out_unlock;
+	}
+
 	/* To preserve message order, if the list is not empty, we
            tack this message onto the end of the list. */
 	spin_lock_irqsave(&(intf->waiting_msgs_lock), flags);
 	if (!list_empty(&(intf->waiting_msgs))) {
 		list_add_tail(&(msg->link), &(intf->waiting_msgs));
 		spin_unlock(&(intf->waiting_msgs_lock));
-		return;
+		goto out_unlock;
 	}
 	spin_unlock_irqrestore(&(intf->waiting_msgs_lock), flags);
 		
@@ -1455,9 +1577,10 @@
 		list_add_tail(&(msg->link), &(intf->waiting_msgs));
 		spin_unlock(&(intf->waiting_msgs_lock));
 	} else if (rv == 0) {
-		msg->done(msg);
+		ipmi_free_smi_msg(msg);
 	}
 
+ out_unlock:
 	read_unlock(&(intf->users_lock));
 }
 
@@ -1490,6 +1613,39 @@
 }
 
 static void
+send_from_recv_msg(ipmi_smi_t intf, struct ipmi_recv_msg *recv_msg,
+		   struct ipmi_smi_msg *smi_msg,
+		   unsigned char seq, long seqid)
+{
+	if (!smi_msg)
+		smi_msg = ipmi_alloc_smi_msg();
+	if (!smi_msg)
+		/* If we can't allocate the message, then just return, we
+		   get 4 retries, so this should be ok. */
+		return;
+
+	memcpy(smi_msg->data, recv_msg->msg.data, recv_msg->msg.data_len);
+	smi_msg->data_size = recv_msg->msg.data_len;
+	smi_msg->msgid = STORE_SEQ_IN_MSGID(seq, seqid);
+		
+	/* Send the new message.  We send with a zero priority.  It
+	   timed out, I doubt time is that critical now, and high
+	   priority messages are really only for messages to the local
+	   MC, which don't get resent. */
+	intf->handlers->sender(intf->send_info, smi_msg, 0);
+
+#if DEBUG_MSGING
+	{
+		int m;
+		printk("Resend: ");
+		for (m=0; m<smi_msg->data_size; m++)
+			printk(" %2.2x", smi_msg->data[m]);
+		printk("\n");
+	}
+#endif
+}
+
+static void
 ipmi_timeout_handler(long timeout_period)
 {
 	ipmi_smi_t           intf;
@@ -1516,7 +1672,7 @@
 			smi_msg = list_entry(entry, struct ipmi_smi_msg, link);
 			if (! handle_new_recv_msg(intf, smi_msg)) {
 				list_del(entry);
-				smi_msg->done(smi_msg);
+				ipmi_free_smi_msg(smi_msg);
 			} else {
 				/* To preserve message order, quit if we
 				   can't handle a message. */
@@ -1530,13 +1686,28 @@
 		   list. */
 		spin_lock_irqsave(&(intf->seq_lock), flags);
 		for (j=0; j<IPMI_IPMB_NUM_SEQ; j++) {
-			if (intf->seq_table[j].inuse) {
-				intf->seq_table[j].timeout -= timeout_period;
-				if (intf->seq_table[j].timeout <= 0) {
-					intf->seq_table[j].inuse = 0;
-					msg = intf->seq_table[j].recv_msg;
-					list_add_tail(&(msg->link), &timeouts);
-				}
+			struct seq_table *ent = &(intf->seq_table[j]);
+			if (!ent->inuse)
+				continue;
+
+			ent->timeout -= timeout_period;
+			if (ent->timeout > 0)
+				continue;
+
+			if (ent->retries_left == 0) {
+				/* The message has used all its retries. */
+				ent->inuse = 0;
+				msg = ent->recv_msg;
+				list_add_tail(&(msg->link), &timeouts);
+			} else {
+				/* More retries, send again. */
+
+				/* Start with the max timer, set to normal
+				   timer after the message is sent. */
+				ent->timeout = MAX_MSG_TIMEOUT;
+				ent->retries_left--;
+				send_from_recv_msg(intf, ent->recv_msg, NULL,
+						   j, ent->seqid);
 			}
 		}
 		spin_unlock_irqrestore(&(intf->seq_lock), flags);
diff -urN linux-2.4.21-pre5/drivers/char/mwave/mwavedd.c linux-2.4.21-pre6/drivers/char/mwave/mwavedd.c
--- linux-2.4.21-pre5/drivers/char/mwave/mwavedd.c	Mon Feb 25 11:37:57 2002
+++ linux-2.4.21-pre6/drivers/char/mwave/mwavedd.c	Wed Mar 26 16:11:35 2003
@@ -500,7 +500,7 @@
 {
 	int i;
 	int retval = 0;
-	unsigned int resultMiscRegister;
+	int resultMiscRegister;
 	pMWAVE_DEVICE_DATA pDrvData = &mwave_s_mdd;
 
 	memset(&mwave_s_mdd, 0, sizeof(MWAVE_DEVICE_DATA));
diff -urN linux-2.4.21-pre5/drivers/char/random.c linux-2.4.21-pre6/drivers/char/random.c
--- linux-2.4.21-pre5/drivers/char/random.c	Thu Nov 28 15:53:12 2002
+++ linux-2.4.21-pre6/drivers/char/random.c	Wed Mar 26 16:11:35 2003
@@ -2035,56 +2035,102 @@
 
 /* This should not be decreased so low that ISNs wrap too fast. */
 #define REKEY_INTERVAL	300
-#define HASH_BITS 24
+/*
+ * Bit layout of the tcp sequence numbers (before adding current time):
+ * bit 24-31: increased after every key exchange
+ * bit 0-23: hash(source,dest)
+ *
+ * The implementation is similar to the algorithm described
+ * in the Appendix of RFC 1185, except that
+ * - it uses a 1 MHz clock instead of a 250 kHz clock
+ * - it performs a rekey every 5 minutes, which is equivalent
+ * 	to a (source,dest) tulple dependent forward jump of the
+ * 	clock by 0..2^(HASH_BITS+1)
+ *
+ * Thus the average ISN wraparound time is 68 minutes instead of
+ * 4.55 hours.
+ *
+ * SMP cleanup and lock avoidance with poor man's RCU.
+ * 			Manfred Spraul <manfred@colorfullife.com>
+ * 		
+ */
+#define COUNT_BITS	8
+#define COUNT_MASK	( (1<<COUNT_BITS)-1)
+#define HASH_BITS	24
+#define HASH_MASK	( (1<<HASH_BITS)-1 )
+
+static struct keydata {
+	time_t rekey_time;
+	__u32	count;		// already shifted to the final position
+	__u32	secret[12];
+} ____cacheline_aligned ip_keydata[2];
+
+static spinlock_t ip_lock = SPIN_LOCK_UNLOCKED;
+static unsigned int ip_cnt;
+
+static struct keydata *__check_and_rekey(time_t time)
+{
+	struct keydata *keyptr;
+	spin_lock(&ip_lock);
+	keyptr = &ip_keydata[ip_cnt&1];
+	if (!keyptr->rekey_time || (time - keyptr->rekey_time) > REKEY_INTERVAL) {
+		keyptr = &ip_keydata[1^(ip_cnt&1)];
+		keyptr->rekey_time = time;
+		get_random_bytes(keyptr->secret, sizeof(keyptr->secret));
+		keyptr->count = (ip_cnt&COUNT_MASK)<<HASH_BITS;
+		mb();
+		ip_cnt++;
+	}
+	spin_unlock(&ip_lock);
+	return keyptr;
+}
+
+static inline struct keydata *check_and_rekey(time_t time)
+{
+	struct keydata *keyptr = &ip_keydata[ip_cnt&1];
+
+	rmb();
+	if (!keyptr->rekey_time || (time - keyptr->rekey_time) > REKEY_INTERVAL) {
+		keyptr = __check_and_rekey(time);
+	}
+
+	return keyptr;
+}
 
 #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
 __u32 secure_tcpv6_sequence_number(__u32 *saddr, __u32 *daddr,
 				   __u16 sport, __u16 dport)
 {
-	static __u32	rekey_time;
-	static __u32	count;
-	static __u32	secret[12];
 	struct timeval 	tv;
 	__u32		seq;
+	__u32		hash[12];
+	struct keydata *keyptr;
 
-	/* The procedure is the same as for IPv4, but addresses are longer. */
+	/* The procedure is the same as for IPv4, but addresses are longer.
+	 * Thus we must use twothirdsMD4Transform.
+	 */
 
 	do_gettimeofday(&tv);	/* We need the usecs below... */
+	keyptr = check_and_rekey(tv.tv_sec);
 
-	if (!rekey_time || (tv.tv_sec - rekey_time) > REKEY_INTERVAL) {
-		rekey_time = tv.tv_sec;
-		/* First five words are overwritten below. */
-		get_random_bytes(&secret[5], sizeof(secret)-5*4);
-		count = (tv.tv_sec/REKEY_INTERVAL) << HASH_BITS;
-	}
-
-	memcpy(secret, saddr, 16);
-	secret[4]=(sport << 16) + dport;
-
-	seq = (twothirdsMD4Transform(daddr, secret) &
-	       ((1<<HASH_BITS)-1)) + count;
+	memcpy(hash, saddr, 16);
+	hash[4]=(sport << 16) + dport;
+	memcpy(&hash[5],keyptr->secret,sizeof(__u32)*7);
 
+	seq = twothirdsMD4Transform(daddr, hash) & HASH_MASK;
+	seq += keyptr->count;
 	seq += tv.tv_usec + tv.tv_sec*1000000;
+
 	return seq;
 }
 
 __u32 secure_ipv6_id(__u32 *daddr)
 {
-	static time_t	rekey_time;
-	static __u32	secret[12];
-	time_t		t;
+	struct keydata *keyptr;
 
-	/*
-	 * Pick a random secret every REKEY_INTERVAL seconds.
-	 */
-	t = CURRENT_TIME;
-	if (!rekey_time || (t - rekey_time) > REKEY_INTERVAL) {
-		rekey_time = t;
-		/* First word is overwritten below. */
-		get_random_bytes(secret, sizeof(secret));
-	}
+	keyptr = check_and_rekey(CURRENT_TIME);
 
-	return twothirdsMD4Transform(daddr, secret);
+	return halfMD4Transform(daddr, keyptr->secret);
 }
 
 #endif
@@ -2093,40 +2139,30 @@
 __u32 secure_tcp_sequence_number(__u32 saddr, __u32 daddr,
 				 __u16 sport, __u16 dport)
 {
-	static __u32	rekey_time;
-	static __u32	count;
-	static __u32	secret[12];
 	struct timeval 	tv;
 	__u32		seq;
+	__u32	hash[4];
+	struct keydata *keyptr;
 
 	/*
 	 * Pick a random secret every REKEY_INTERVAL seconds.
 	 */
 	do_gettimeofday(&tv);	/* We need the usecs below... */
-
-	if (!rekey_time || (tv.tv_sec - rekey_time) > REKEY_INTERVAL) {
-		rekey_time = tv.tv_sec;
-		/* First three words are overwritten below. */
-		get_random_bytes(&secret[3], sizeof(secret)-12);
-		count = (tv.tv_sec/REKEY_INTERVAL) << HASH_BITS;
-	}
+	keyptr = check_and_rekey(tv.tv_sec);
 
 	/*
 	 *  Pick a unique starting offset for each TCP connection endpoints
 	 *  (saddr, daddr, sport, dport).
-	 *  Note that the words are placed into the first words to be
-	 *  mixed in with the halfMD4.  This is because the starting
-	 *  vector is also a random secret (at secret+8), and further
-	 *  hashing fixed data into it isn't going to improve anything,
-	 *  so we should get started with the variable data.
+	 *  Note that the words are placed into the starting vector, which is 
+	 *  then mixed with a partial MD4 over random data.
 	 */
-	secret[0]=saddr;
-	secret[1]=daddr;
-	secret[2]=(sport << 16) + dport;
-
-	seq = (halfMD4Transform(secret+8, secret) &
-	       ((1<<HASH_BITS)-1)) + count;
+	hash[0]=saddr;
+	hash[1]=daddr;
+	hash[2]=(sport << 16) + dport;
+	hash[3]=keyptr->secret[11];
 
+	seq = halfMD4Transform(hash, keyptr->secret) & HASH_MASK;
+	seq += keyptr->count;
 	/*
 	 *	As close as possible to RFC 793, which
 	 *	suggests using a 250 kHz clock.
@@ -2148,31 +2184,22 @@
  */
 __u32 secure_ip_id(__u32 daddr)
 {
-	static time_t	rekey_time;
-	static __u32	secret[12];
-	time_t		t;
+	struct keydata *keyptr;
+	__u32 hash[4];
 
-	/*
-	 * Pick a random secret every REKEY_INTERVAL seconds.
-	 */
-	t = CURRENT_TIME;
-	if (!rekey_time || (t - rekey_time) > REKEY_INTERVAL) {
-		rekey_time = t;
-		/* First word is overwritten below. */
-		get_random_bytes(secret+1, sizeof(secret)-4);
-	}
+	keyptr = check_and_rekey(CURRENT_TIME);
 
 	/*
 	 *  Pick a unique starting offset for each IP destination.
-	 *  Note that the words are placed into the first words to be
-	 *  mixed in with the halfMD4.  This is because the starting
-	 *  vector is also a random secret (at secret+8), and further
-	 *  hashing fixed data into it isn't going to improve anything,
-	 *  so we should get started with the variable data.
+	 *  The dest ip address is placed in the starting vector,
+	 *  which is then hashed with random data.
 	 */
-	secret[0]=daddr;
+	hash[0] = daddr;
+	hash[1] = keyptr->secret[9];
+	hash[2] = keyptr->secret[10];
+	hash[3] = keyptr->secret[11];
 
-	return halfMD4Transform(secret+8, secret);
+	return halfMD4Transform(hash, keyptr->secret);
 }
 
 #ifdef CONFIG_SYN_COOKIES
diff -urN linux-2.4.21-pre5/drivers/char/rtc.c linux-2.4.21-pre6/drivers/char/rtc.c
--- linux-2.4.21-pre5/drivers/char/rtc.c	Wed Mar 26 16:11:28 2003
+++ linux-2.4.21-pre6/drivers/char/rtc.c	Wed Mar 26 16:11:35 2003
@@ -74,6 +74,7 @@
 #include <asm/system.h>
 
 #ifdef __sparc__
+#include <linux/pci.h>
 #include <asm/ebus.h>
 #ifdef __sparc_v9__
 #include <asm/isa.h>
diff -urN linux-2.4.21-pre5/drivers/i2c/i2c-proc.c linux-2.4.21-pre6/drivers/i2c/i2c-proc.c
--- linux-2.4.21-pre5/drivers/i2c/i2c-proc.c	Mon Feb 25 11:37:57 2002
+++ linux-2.4.21-pre6/drivers/i2c/i2c-proc.c	Wed Mar 26 16:11:35 2003
@@ -729,7 +729,7 @@
 			     ||
 			     ((address_data->
 			       ignore_range[i] ==
-			       SENSORS_ANY_I2C_BUS) & !is_isa))
+			       SENSORS_ANY_I2C_BUS) && !is_isa))
 			    && (addr >= address_data->ignore_range[i + 1])
 			    && (addr <= address_data->ignore_range[i + 2])) {
 #ifdef DEBUG
@@ -818,7 +818,7 @@
 		     i += 2) {
 			if (((adapter_id == address_data->probe[i]) ||
 			     ((address_data->
-			       probe[i] == SENSORS_ANY_I2C_BUS) & !is_isa))
+			       probe[i] == SENSORS_ANY_I2C_BUS) && !is_isa))
 			    && (addr == address_data->probe[i + 1])) {
 #ifdef DEBUG
 				printk
@@ -835,7 +835,7 @@
 			    ((adapter_id == address_data->probe_range[i])
 			     ||
 			     ((address_data->probe_range[i] ==
-			       SENSORS_ANY_I2C_BUS) & !is_isa))
+			       SENSORS_ANY_I2C_BUS) && !is_isa))
 			    && (addr >= address_data->probe_range[i + 1])
 			    && (addr <= address_data->probe_range[i + 2])) {
 				found = 1;
diff -urN linux-2.4.21-pre5/drivers/ide/Config.in linux-2.4.21-pre6/drivers/ide/Config.in
--- linux-2.4.21-pre5/drivers/ide/Config.in	Wed Mar 26 16:11:28 2003
+++ linux-2.4.21-pre6/drivers/ide/Config.in	Wed Mar 26 16:11:35 2003
@@ -61,7 +61,6 @@
 	    if [ "$CONFIG_MIPS_ITE8172" = "y" -o "$CONFIG_MIPS_IVR" = "y" ]; then
 	       dep_mbool '    IT8172 IDE support' CONFIG_BLK_DEV_IT8172 $CONFIG_BLK_DEV_IDEDMA_PCI
 	    fi
-	    dep_tristate '    nVidia NFORCE support' CONFIG_BLK_DEV_NFORCE $CONFIG_BLK_DEV_IDEDMA_PCI
 	    dep_tristate '    NS87415 chipset support' CONFIG_BLK_DEV_NS87415 $CONFIG_BLK_DEV_IDEDMA_PCI
 	    dep_tristate '    OPTi 82C621 chipset enhanced support (EXPERIMENTAL)' CONFIG_BLK_DEV_OPTI621 $CONFIG_EXPERIMENTAL
 	    dep_tristate '    PROMISE PDC202{46|62|65|67} support' CONFIG_BLK_DEV_PDC202XX_OLD $CONFIG_BLK_DEV_IDEDMA_PCI
diff -urN linux-2.4.21-pre5/drivers/ide/pci/Makefile linux-2.4.21-pre6/drivers/ide/pci/Makefile
--- linux-2.4.21-pre5/drivers/ide/pci/Makefile	Wed Mar 26 16:11:28 2003
+++ linux-2.4.21-pre6/drivers/ide/pci/Makefile	Wed Mar 26 16:11:35 2003
@@ -18,7 +18,6 @@
 obj-$(CONFIG_BLK_DEV_IDE_ICSIDE)	+= icside.o
 obj-$(CONFIG_BLK_DEV_IT8172)		+= it8172.o
 obj-$(CONFIG_BLK_DEV_NS87415)		+= ns87415.o
-obj-$(CONFIG_BLK_DEV_NFORCE)		+= nvidia.o
 obj-$(CONFIG_BLK_DEV_OPTI621)		+= opti621.o
 obj-$(CONFIG_BLK_DEV_PDC202XX_OLD)	+= pdc202xx_old.o
 obj-$(CONFIG_BLK_DEV_PDC202XX_NEW)	+= pdc202xx_new.o
diff -urN linux-2.4.21-pre5/drivers/ide/pci/amd74xx.c linux-2.4.21-pre6/drivers/ide/pci/amd74xx.c
--- linux-2.4.21-pre5/drivers/ide/pci/amd74xx.c	Wed Mar 26 16:11:28 2003
+++ linux-2.4.21-pre6/drivers/ide/pci/amd74xx.c	Wed Mar 26 16:11:36 2003
@@ -1,411 +1,441 @@
 /*
- * linux/drivers/ide/pci/amd74xx.c		Version 0.05	June 9, 2000
+ * Version 2.10
  *
- * Copyright (C) 1999-2000		Andre Hedrick <andre@linux-ide.org>
- * May be copied or modified under the terms of the GNU General Public License
+ * AMD 755/756/766/8111 and nVidia nForce IDE driver for Linux.
  *
- * Documentation
- *	Publically available from AMD
+ * Copyright (c) 2000-2002 Vojtech Pavlik
  *
- * Errata
- *	AMD 756: Errata #1
- *	Single word DMA is not supported by old AMD IDE devices
- *
- *	AMD 766: Errata #15
- *	IDE may hang if prefetch is enabled. The BIOS does not enable
- *	prefetch.
- *
- *	AMD 766: Errata #19
- *	Poor UDMA100 performance
- *
- *	AMD 761: Errata #55
- *	AMD 762: Errata #56
- *	A problem exists with IDE prefetch and the last page before the
- *	640K boundary. Users hitting this problem can add a PS/2 mouse
- *	to the system, causing that page to become an EBDA page and not
- *	used by the OS. A full work around is under investigation.
- *	BIOS updates should also avoid this.
+ * Based on the work of:
+ *      Andre Hedrick
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
  */
 
 #include <linux/config.h>
 #include <linux/module.h>
-#include <linux/types.h>
 #include <linux/kernel.h>
-#include <linux/delay.h>
-#include <linux/timer.h>
-#include <linux/mm.h>
 #include <linux/ioport.h>
 #include <linux/blkdev.h>
-#include <linux/hdreg.h>
-
-#include <linux/interrupt.h>
-#include <linux/init.h>
 #include <linux/pci.h>
+#include <linux/init.h>
 #include <linux/ide.h>
-
 #include <asm/io.h>
-#include <asm/irq.h>
 
-#include "ide_modes.h"
+#include "ide-timing.h"
 #include "amd74xx.h"
 
-#if defined(DISPLAY_VIPER_TIMINGS) && defined(CONFIG_PROC_FS)
+#define AMD_IDE_ENABLE		(0x00 + amd_config->base)
+#define AMD_IDE_CONFIG		(0x01 + amd_config->base)
+#define AMD_CABLE_DETECT	(0x02 + amd_config->base)
+#define AMD_DRIVE_TIMING	(0x08 + amd_config->base)
+#define AMD_8BIT_TIMING		(0x0e + amd_config->base)
+#define AMD_ADDRESS_SETUP	(0x0c + amd_config->base)
+#define AMD_UDMA_TIMING		(0x10 + amd_config->base)
+
+#define AMD_UDMA		0x07
+#define AMD_UDMA_33		0x01
+#define AMD_UDMA_66		0x02
+#define AMD_UDMA_100		0x03
+#define AMD_CHECK_SWDMA		0x08
+#define AMD_BAD_SWDMA		0x10
+#define AMD_BAD_FIFO		0x20
+
+/*
+ * AMD SouthBridge chips.
+ */
+
+static struct amd_ide_chip {
+	unsigned short id;
+	unsigned char rev;
+	unsigned long base;
+	unsigned char flags;
+} amd_ide_chips[] = {
+	{ PCI_DEVICE_ID_AMD_COBRA_7401, 0x00, 0x40, AMD_UDMA_33 | AMD_BAD_SWDMA },	/* AMD-755 Cobra */
+	{ PCI_DEVICE_ID_AMD_VIPER_7409, 0x00, 0x40, AMD_UDMA_66 | AMD_CHECK_SWDMA },	/* AMD-756 Viper */
+	{ PCI_DEVICE_ID_AMD_VIPER_7411, 0x00, 0x40, AMD_UDMA_100 | AMD_BAD_FIFO },	/* AMD-766 Viper */
+	{ PCI_DEVICE_ID_AMD_OPUS_7441, 0x00, 0x40, AMD_UDMA_100 },			/* AMD-768 Opus */
+	{ PCI_DEVICE_ID_AMD_8111_IDE,  0x00, 0x40, AMD_UDMA_100 },			/* AMD-8111 */
+        { PCI_DEVICE_ID_NVIDIA_NFORCE_IDE, 0x00, 0x50, AMD_UDMA_100 },                  /* nVidia nForce */
+        { PCI_DEVICE_ID_NVIDIA_NFORCE2_IDE, 0x00, 0x50, AMD_UDMA_100 },                  /* nVidia nForce */
+
+	{ 0 }
+};
+
+static struct amd_ide_chip *amd_config;
+static unsigned char amd_enabled;
+static unsigned int amd_80w;
+static unsigned int amd_clock;
+
+static unsigned char amd_cyc2udma[] = { 6, 6, 5, 4, 0, 1, 1, 2, 2, 3, 3 };
+static unsigned char amd_udma2cyc[] = { 4, 6, 8, 10, 3, 2, 1, 1 };
+static char *amd_dma[] = { "MWDMA16", "UDMA33", "UDMA66", "UDMA100" };
+
+/*
+ * AMD /proc entry.
+ */
+
+#ifdef CONFIG_PROC_FS
+
 #include <linux/stat.h>
 #include <linux/proc_fs.h>
 
-static u8 amd74xx_proc = 0;
-
+static unsigned long amd_base;
 static struct pci_dev *bmide_dev;
+extern int (*amd74xx_display_info)(char *, char **, off_t, int); /* ide-proc.c */
 
-static int amd74xx_get_info (char *buffer, char **addr, off_t offset, int count)
-{
+#define amd_print(format, arg...) p += sprintf(p, format "\n" , ## arg)
+#define amd_print_drive(name, format, arg...)\
+	p += sprintf(p, name); for (i = 0; i < 4; i++) p += sprintf(p, format, ## arg); p += sprintf(p, "\n");
+
+static int amd74xx_get_info(char *buffer, char **addr, off_t offset, int count)
+{
+	int speed[4], cycle[4], setup[4], active[4], recover[4], den[4],
+		 uen[4], udma[4], active8b[4], recover8b[4];
+	struct pci_dev *dev = bmide_dev;
+	unsigned int v, u, i;
+	unsigned short c, w;
+	unsigned char t;
 	char *p = buffer;
-	u32 bibma = pci_resource_start(bmide_dev, 4);
-	u8 c0 = 0, c1 = 0;
 
-	/*
-	 * at that point bibma+0x2 et bibma+0xa are byte registers
-	 * to investigate:
-	 */
-	c0 = inb((unsigned short)bibma + 0x02);
-	c1 = inb((unsigned short)bibma + 0x0a);
-
-	p += sprintf(p, "\n                                "
-			"AMD %04X VIPER Chipset.\n", bmide_dev->device);
-	p += sprintf(p, "--------------- Primary Channel "
-			"---------------- Secondary Channel "
-			"-------------\n");
-	p += sprintf(p, "                %sabled "
-			"                        %sabled\n",
-			(c0&0x80) ? "dis" : " en",
-			(c1&0x80) ? "dis" : " en");
-	p += sprintf(p, "--------------- drive0 --------- drive1 "
-			"-------- drive0 ---------- drive1 ------\n");
-	p += sprintf(p, "DMA enabled:    %s              %s "
-			"            %s               %s\n",
-			(c0&0x20) ? "yes" : "no ", (c0&0x40) ? "yes" : "no ",
-			(c1&0x20) ? "yes" : "no ", (c1&0x40) ? "yes" : "no " );
-	p += sprintf(p, "UDMA\n");
-	p += sprintf(p, "DMA\n");
-	p += sprintf(p, "PIO\n");
-
-	return p-buffer;	/* => must be less than 4k! */
-}
-#endif  /* defined(DISPLAY_VIPER_TIMINGS) && defined(CONFIG_PROC_FS) */
-
-static int amd74xx_mode5_check (struct pci_dev *dev)
-{
-	switch(dev->device) {
-		case PCI_DEVICE_ID_AMD_VIPER_7411:
-		case PCI_DEVICE_ID_AMD_VIPER_7441:
-		case PCI_DEVICE_ID_AMD_8111_IDE:
-			return 1;
-		default:
-			return 0;
+	amd_print("----------AMD BusMastering IDE Configuration----------------");
+
+	amd_print("Driver Version:                     2.10");
+	amd_print("South Bridge:                       %s", bmide_dev->name);
+
+	pci_read_config_byte(dev, PCI_REVISION_ID, &t);
+	amd_print("Revision:                           IDE %#x", t);
+	amd_print("Highest DMA rate:                   %s", amd_dma[amd_config->flags & AMD_UDMA]);
+
+	amd_print("BM-DMA base:                        %#lx", amd_base);
+	amd_print("PCI clock:                          %d.%dMHz", amd_clock / 1000, amd_clock / 100 % 10);
+	
+	amd_print("-----------------------Primary IDE-------Secondary IDE------");
+
+	pci_read_config_byte(dev, AMD_IDE_CONFIG, &t);
+	amd_print("Prefetch Buffer:       %10s%20s", (t & 0x80) ? "yes" : "no", (t & 0x20) ? "yes" : "no");
+	amd_print("Post Write Buffer:     %10s%20s", (t & 0x40) ? "yes" : "no", (t & 0x10) ? "yes" : "no");
+
+	pci_read_config_byte(dev, AMD_IDE_ENABLE, &t);
+	amd_print("Enabled:               %10s%20s", (t & 0x02) ? "yes" : "no", (t & 0x01) ? "yes" : "no");
+
+	c = inb(amd_base + 0x02) | (inb(amd_base + 0x0a) << 8);
+	amd_print("Simplex only:          %10s%20s", (c & 0x80) ? "yes" : "no", (c & 0x8000) ? "yes" : "no");
+
+	amd_print("Cable Type:            %10s%20s", (amd_80w & 1) ? "80w" : "40w", (amd_80w & 2) ? "80w" : "40w");
+
+	if (!amd_clock)
+                return p - buffer;
+
+	amd_print("-------------------drive0----drive1----drive2----drive3-----");
+
+	pci_read_config_byte(dev, AMD_ADDRESS_SETUP, &t);
+	pci_read_config_dword(dev, AMD_DRIVE_TIMING, &v);
+	pci_read_config_word(dev, AMD_8BIT_TIMING, &w);
+	pci_read_config_dword(dev, AMD_UDMA_TIMING, &u);
+
+	for (i = 0; i < 4; i++) {
+		setup[i]     = ((t >> ((3 - i) << 1)) & 0x3) + 1;
+		recover8b[i] = ((w >> ((1 - (i >> 1)) << 3)) & 0xf) + 1;
+		active8b[i]  = ((w >> (((1 - (i >> 1)) << 3) + 4)) & 0xf) + 1;
+		active[i]    = ((v >> (((3 - i) << 3) + 4)) & 0xf) + 1;
+		recover[i]   = ((v >> ((3 - i) << 3)) & 0xf) + 1;
+
+		udma[i] = amd_udma2cyc[((u >> ((3 - i) << 3)) & 0x7)];
+		uen[i]  = ((u >> ((3 - i) << 3)) & 0x40) ? 1 : 0;
+		den[i]  = (c & ((i & 1) ? 0x40 : 0x20) << ((i & 2) << 2));
+
+		if (den[i] && uen[i] && udma[i] == 1) {
+			speed[i] = amd_clock * 3;
+			cycle[i] = 666666 / amd_clock;
+			continue;
+		}
+
+		speed[i] = 4 * amd_clock / ((den[i] && uen[i]) ? udma[i] : (active[i] + recover[i]) * 2);
+		cycle[i] = 1000000 * ((den[i] && uen[i]) ? udma[i] : (active[i] + recover[i]) * 2) / amd_clock / 2;
 	}
-}
 
-static unsigned int amd74xx_swdma_check (struct pci_dev *dev)
-{
-	unsigned int class_rev;
+	amd_print_drive("Transfer Mode: ", "%10s", den[i] ? (uen[i] ? "UDMA" : "DMA") : "PIO");
 
-	if (amd74xx_mode5_check(dev))
-		return 1;
+	amd_print_drive("Address Setup: ", "%8dns", 1000000 * setup[i] / amd_clock);
+	amd_print_drive("Cmd Active:    ", "%8dns", 1000000 * active8b[i] / amd_clock);
+	amd_print_drive("Cmd Recovery:  ", "%8dns", 1000000 * recover8b[i] / amd_clock);
+	amd_print_drive("Data Active:   ", "%8dns", 1000000 * active[i] / amd_clock);
+	amd_print_drive("Data Recovery: ", "%8dns", 1000000 * recover[i] / amd_clock);
+	amd_print_drive("Cycle Time:    ", "%8dns", cycle[i]);
+	amd_print_drive("Transfer Rate: ", "%4d.%dMB/s", speed[i] / 1000, speed[i] / 100 % 10);
 
-	pci_read_config_dword(dev, PCI_CLASS_REVISION, &class_rev);
-	class_rev &= 0xff;
-	return ((int) (class_rev >= 7) ? 1 : 0);
+	return p - buffer;	/* hoping it is less than 4K... */
 }
 
-static u8 amd74xx_ratemask (ide_drive_t *drive)
+#endif
+
+/*
+ * amd_set_speed() writes timing values to the chipset registers
+ */
+
+static void amd_set_speed(struct pci_dev *dev, unsigned char dn, struct ide_timing *timing)
 {
-	u8 mode;
+	unsigned char t;
 
-        switch(HWIF(drive)->pci_dev->device) {
-		case PCI_DEVICE_ID_AMD_8111_IDE:
-		case PCI_DEVICE_ID_AMD_VIPER_7441:
-		case PCI_DEVICE_ID_AMD_VIPER_7411:
-			mode = 3;
-			break;
-		case PCI_DEVICE_ID_AMD_VIPER_7409:
-			mode = 2;
-			break;
-		case PCI_DEVICE_ID_AMD_COBRA_7401:
-			return 1;
-		default:
-			return 0;
-	}
+	pci_read_config_byte(dev, AMD_ADDRESS_SETUP, &t);
+	t = (t & ~(3 << ((3 - dn) << 1))) | ((FIT(timing->setup, 1, 4) - 1) << ((3 - dn) << 1));
+	pci_write_config_byte(dev, AMD_ADDRESS_SETUP, t);
+
+	pci_write_config_byte(dev, AMD_8BIT_TIMING + (1 - (dn >> 1)),
+		((FIT(timing->act8b, 1, 16) - 1) << 4) | (FIT(timing->rec8b, 1, 16) - 1));
 
-	if (!eighty_ninty_three(drive))
-		mode = min(mode, (u8)1);
+	pci_write_config_byte(dev, AMD_DRIVE_TIMING + (3 - dn),
+		((FIT(timing->active, 1, 16) - 1) << 4) | (FIT(timing->recover, 1, 16) - 1));
+
+	switch (amd_config->flags & AMD_UDMA) {
+		case AMD_UDMA_33:  t = timing->udma ? (0xc0 | (FIT(timing->udma, 2, 5) - 2)) : 0x03; break;
+		case AMD_UDMA_66:  t = timing->udma ? (0xc0 | amd_cyc2udma[FIT(timing->udma, 2, 10)]) : 0x03; break;
+		case AMD_UDMA_100: t = timing->udma ? (0xc0 | amd_cyc2udma[FIT(timing->udma, 1, 10)]) : 0x03; break;
+		default: return;
+	}
 
-	return mode;
+	pci_write_config_byte(dev, AMD_UDMA_TIMING + (3 - dn), t);
 }
 
 /*
- * Here is where all the hard work goes to program the chipset.
+ * amd_set_drive() computes timing values configures the drive and
+ * the chipset to a desired transfer mode. It also can be called
+ * by upper layers.
  */
-static int amd74xx_tune_chipset (ide_drive_t *drive, u8 xferspeed)
+
+static int amd_set_drive(ide_drive_t *drive, u8 speed)
 {
-	u8 drive_pci[]		= { 0x53, 0x52, 0x51, 0x50 };
-	u8 drive_pci2[]		= { 0x4b, 0x4a, 0x49, 0x48 };
-#if 0
-	u8 ultra_rate[]		= { 0x42, 0x41, 0x40, 0x44, 0x45, 0x46 };
-	u8 mwdma_rate[]		= { 0x77, 0x21, 0x20 };
-	u8 swdma_rate[]		= { 0xA8, 0x65, 0x42 };
-	u8 pio_rate[]		= { 0xA8, 0x65, 0x42, 0x22, 0x20};
-#endif
-	ide_hwif_t *hwif	= HWIF(drive);
-	struct pci_dev *dev	= hwif->pci_dev;
-	u8 speed	= ide_rate_filter(amd74xx_ratemask(drive), xferspeed);
-	u8 ultra_timing	= 0, dma_pio_timing = 0, pio_timing = 0;
-
-	pci_read_config_byte(dev, drive_pci[drive->dn], &ultra_timing);
-	pci_read_config_byte(dev, drive_pci2[drive->dn], &dma_pio_timing);
-	pci_read_config_byte(dev, 0x4c, &pio_timing);
-
-	ultra_timing	&= ~0xC7;
-	dma_pio_timing	&= ~0xFF;
-	pio_timing	&= ~(0x03 << drive->dn);
-
-	switch(speed) {
-		case XFER_UDMA_7:
-		case XFER_UDMA_6:
-			speed = XFER_UDMA_5;
-		case XFER_UDMA_5:
-			ultra_timing |= 0x46;
-			dma_pio_timing |= 0x20;
-			break;
-		case XFER_UDMA_4:
-			ultra_timing |= 0x45;
-			dma_pio_timing |= 0x20;
-			break;
-		case XFER_UDMA_3:
-			ultra_timing |= 0x44;
-			dma_pio_timing |= 0x20;
-			break;
-		case XFER_UDMA_2:
-			ultra_timing |= 0x40;
-			dma_pio_timing |= 0x20;
-			break;
-		case XFER_UDMA_1:
-			ultra_timing |= 0x41;
-			dma_pio_timing |= 0x20;
-			break;
-		case XFER_UDMA_0:
-			ultra_timing |= 0x42;
-			dma_pio_timing |= 0x20;
-			break;
-		case XFER_MW_DMA_2:
-			dma_pio_timing |= 0x20;
-			break;
-		case XFER_MW_DMA_1:
-			dma_pio_timing |= 0x21;
-			break;
-		case XFER_MW_DMA_0:
-			dma_pio_timing |= 0x77;
-			break;
-		case XFER_SW_DMA_2:
-			dma_pio_timing |= 0x42;
-			break;
-		case XFER_SW_DMA_1:
-			dma_pio_timing |= 0x65;
-			break;
-		case XFER_SW_DMA_0:
-			dma_pio_timing |= 0xA8;
-			break;
-		case XFER_PIO_4:
-			dma_pio_timing |= 0x20;
-			break;
-		case XFER_PIO_3:
-			dma_pio_timing |= 0x22;
-			break;
-		case XFER_PIO_2:
-			dma_pio_timing |= 0x42;
-			break;
-		case XFER_PIO_1:
-			dma_pio_timing |= 0x65;
-			break;
-		case XFER_PIO_0:
-		default:
-			dma_pio_timing |= 0xA8;
-			break;
-        }
+	ide_drive_t *peer = HWIF(drive)->drives + (~drive->dn & 1);
+	struct ide_timing t, p;
+	int T, UT;
+
+	if (speed != XFER_PIO_SLOW && speed != drive->current_speed)
+		if (ide_config_drive_speed(drive, speed))
+			printk(KERN_WARNING "ide%d: Drive %d didn't accept speed setting. Oh, well.\n",
+				drive->dn >> 1, drive->dn & 1);
+
+	T = 1000000000 / amd_clock;
+	UT = T / min_t(int, max_t(int, amd_config->flags & AMD_UDMA, 1), 2);
+
+	ide_timing_compute(drive, speed, &t, T, UT);
+
+	if (peer->present) {
+		ide_timing_compute(peer, peer->current_speed, &p, T, UT);
+		ide_timing_merge(&p, &t, &t, IDE_TIMING_8BIT);
+	}
+
+	if (speed == XFER_UDMA_5 && amd_clock <= 33333) t.udma = 1;
 
-	pio_timing |= (0x03 << drive->dn);
+	amd_set_speed(HWIF(drive)->pci_dev, drive->dn, &t);
 
-	pci_write_config_byte(dev, drive_pci[drive->dn], ultra_timing);
-	pci_write_config_byte(dev, drive_pci2[drive->dn], dma_pio_timing);
-	pci_write_config_byte(dev, 0x4c, pio_timing);
+	if (!drive->init_speed)	
+		drive->init_speed = speed;
+	drive->current_speed = speed;
 
-	return (ide_config_drive_speed(drive, speed));
+	return 0;
 }
 
-static void amd74xx_tune_drive (ide_drive_t *drive, u8 pio)
+/*
+ * amd74xx_tune_drive() is a callback from upper layers for
+ * PIO-only tuning.
+ */
+
+static void amd74xx_tune_drive(ide_drive_t *drive, u8 pio)
 {
-	pio = ide_get_best_pio_mode(drive, pio, 5, NULL);
-	(void) amd74xx_tune_chipset(drive, (XFER_PIO_0 + pio));
+	if (!((amd_enabled >> HWIF(drive)->channel) & 1))
+		return;
+
+	if (pio == 255) {
+		amd_set_drive(drive, ide_find_best_mode(drive, XFER_PIO | XFER_EPIO));
+		return;
+	}
+
+	amd_set_drive(drive, XFER_PIO_0 + min_t(byte, pio, 5));
 }
 
 /*
- * This allows the configuration of ide_pci chipset registers
- * for cards that learn about the drive's UDMA, DMA, PIO capabilities
- * after the drive is reported by the OS.
+ * amd74xx_dmaproc() is a callback from upper layers that can do
+ * a lot, but we use it for DMA/PIO tuning only, delegating everything
+ * else to the default ide_dmaproc().
  */
-static int config_chipset_for_dma (ide_drive_t *drive)
+
+static int amd74xx_ide_dma_check(ide_drive_t *drive)
 {
-	u8 speed = ide_dma_speed(drive, amd74xx_ratemask(drive));
+	int w80 = HWIF(drive)->udma_four;
+
+	u8 speed = ide_find_best_mode(drive,
+		XFER_PIO | XFER_EPIO | XFER_MWDMA | XFER_UDMA |
+		((amd_config->flags & AMD_BAD_SWDMA) ? 0 : XFER_SWDMA) |
+		(w80 && (amd_config->flags & AMD_UDMA) >= AMD_UDMA_66 ? XFER_UDMA_66 : 0) |
+		(w80 && (amd_config->flags & AMD_UDMA) >= AMD_UDMA_100 ? XFER_UDMA_100 : 0));
 
-	if (!(speed))
-		return 0;
+	amd_set_drive(drive, speed);
 
-	(void) amd74xx_tune_chipset(drive, speed);
-	return ide_dma_enable(drive);
+	if (drive->autodma && (speed & XFER_MODE) != XFER_PIO)
+		return HWIF(drive)->ide_dma_on(drive);
+	return HWIF(drive)->ide_dma_off_quietly(drive);
 }
 
-static int amd74xx_config_drive_xfer_rate (ide_drive_t *drive)
+/*
+ * The initialization callback. Here we determine the IDE chip type
+ * and initialize its drive independent registers.
+ */
+
+static unsigned int __init init_chipset_amd74xx(struct pci_dev *dev, const char *name)
 {
-	ide_hwif_t *hwif	= HWIF(drive);
-	struct hd_driveid *id	= drive->id;
+	unsigned char t;
+	unsigned int u;
+	int i;
 
-	drive->init_speed = 0;
+/*
+ * Check for bad SWDMA.
+ */
 
-	if (id && (id->capability & 1) && drive->autodma) {
-		/* Consult the list of known "bad" drives */
-		if (hwif->ide_dma_bad_drive(drive))
-			goto fast_ata_pio;
-		if (id->field_valid & 4) {
-			if (id->dma_ultra & hwif->ultra_mask) {
-				/* Force if Capable UltraDMA */
-				int dma = config_chipset_for_dma(drive);
-				if ((id->field_valid & 2) && dma)
-					goto try_dma_modes;
-			}
-		} else if (id->field_valid & 2) {
-try_dma_modes:
-			if ((id->dma_mword & hwif->mwdma_mask) ||
-			    (id->dma_1word & hwif->swdma_mask)) {
-				/* Force if Capable regular DMA modes */
-				if (!config_chipset_for_dma(drive))
-					goto no_dma_set;
-			}
+	if (amd_config->flags & AMD_CHECK_SWDMA) {
+		pci_read_config_byte(dev, PCI_REVISION_ID, &t);
+		if (t <= 7)
+			amd_config->flags |= AMD_BAD_SWDMA;
+	}
 
-		} else if (hwif->ide_dma_good_drive(drive) &&
-			   (id->eide_dma_time < 150)) {
-			/* Consult the list of known "good" drives */
-			if (!config_chipset_for_dma(drive))
-				goto no_dma_set;
-		} else {
-			goto fast_ata_pio;
-		}
-	} else if ((id->capability & 8) || (id->field_valid & 2)) {
-fast_ata_pio:
-no_dma_set:
-		amd74xx_tune_drive(drive, 5);
-		return hwif->ide_dma_off_quietly(drive);
+/*
+ * Check 80-wire cable presence.
+ */
+
+	switch (amd_config->flags & AMD_UDMA) {
+
+		case AMD_UDMA_100:
+			pci_read_config_byte(dev, AMD_CABLE_DETECT, &t);
+			pci_read_config_dword(dev, AMD_UDMA_TIMING, &u);
+			amd_80w = ((t & 0x3) ? 1 : 0) | ((t & 0xc) ? 2 : 0);
+			for (i = 24; i >= 0; i -= 8)
+				if (((u >> i) & 4) && !(amd_80w & (1 << (1 - (i >> 4))))) {
+					printk(KERN_WARNING "AMD_IDE: Bios didn't set cable bits corectly. Enabling workaround.\n");
+					amd_80w |= (1 << (1 - (i >> 4)));
+				}
+			break;
+
+		case AMD_UDMA_66:
+			pci_read_config_dword(dev, AMD_UDMA_TIMING, &u);
+			for (i = 24; i >= 0; i -= 8)
+				if ((u >> i) & 4)
+					amd_80w |= (1 << (1 - (i >> 4)));
+			break;
 	}
-	return hwif->ide_dma_on(drive);
-}
 
-static unsigned int __init init_chipset_amd74xx (struct pci_dev *dev, const char *name)
-{
-	if (!amd74xx_swdma_check(dev))
-		printk("%s: disabling single-word DMA support (revision < C4)\n", name);
+	pci_read_config_dword(dev, AMD_IDE_ENABLE, &u);
+	amd_enabled = ((u & 1) ? 2 : 0) | ((u & 2) ? 1 : 0);
 
-#if defined(DISPLAY_VIPER_TIMINGS) && defined(CONFIG_PROC_FS)
-	if (!amd74xx_proc) {
-		amd74xx_proc = 1;
-		bmide_dev = dev;
-		ide_pci_register_host_proc(&amd74xx_procs[0]);
+/*
+ * Take care of prefetch & postwrite.
+ */
+
+	pci_read_config_byte(dev, AMD_IDE_CONFIG, &t);
+	pci_write_config_byte(dev, AMD_IDE_CONFIG,
+		(amd_config->flags & AMD_BAD_FIFO) ? (t & 0x0f) : (t | 0xf0));
+
+/*
+ * Determine the system bus clock.
+ */
+
+	amd_clock = system_bus_clock() * 1000;
+
+	switch (amd_clock) {
+		case 33000: amd_clock = 33333; break;
+		case 37000: amd_clock = 37500; break;
+		case 41000: amd_clock = 41666; break;
 	}
-#endif /* DISPLAY_VIPER_TIMINGS && CONFIG_PROC_FS */
+
+	if (amd_clock < 20000 || amd_clock > 50000) {
+		printk(KERN_WARNING "AMD_IDE: User given PCI clock speed impossible (%d), using 33 MHz instead.\n", amd_clock);
+		printk(KERN_WARNING "AMD_IDE: Use ide0=ata66 if you want to assume 80-wire cable\n");
+		amd_clock = 33333;
+	}
+
+/*
+ * Print the boot message.
+ */
+
+	pci_read_config_byte(dev, PCI_REVISION_ID, &t);
+	printk(KERN_INFO "AMD_IDE: %s (rev %02x) %s controller on pci%s\n",
+		dev->name, t, amd_dma[amd_config->flags & AMD_UDMA], dev->slot_name);
+
+/*
+ * Register /proc/ide/amd74xx entry
+ */
+
+#if defined(DISPLAY_AMD_TIMINGS) && defined(CONFIG_PROC_FS)
+        if (!amd74xx_proc) {
+                amd_base = pci_resource_start(dev, 4);
+                bmide_dev = dev;
+                ide_pci_register_host_proc(&amd74xx_procs[0]);
+                amd74xx_proc = 1;
+        }
+#endif /* DISPLAY_AMD_TIMINGS && CONFIG_PROC_FS */
+
 
 	return 0;
 }
 
-static unsigned int __init ata66_amd74xx (ide_hwif_t *hwif)
+static unsigned int __init ata66_amd74xx(ide_hwif_t *hwif)
 {
-	struct pci_dev *dev	= hwif->pci_dev;
-	u8 cable_80_pin[2]	= { 0, 0 };
-	u8 ata66		= 0;
-	u8 tmpbyte;
-
-	/*
-	 * Ultra66 cable detection (from Host View)
-	 * 7411, 7441, 0x42, bit0: primary, bit2: secondary 80 pin
-	 */
-	pci_read_config_byte(dev, 0x42, &tmpbyte);
-
-	/*
-	 * 0x42, bit0 is 1 => primary channel
-	 * has 80-pin (from host view)
-	 */
-	if (tmpbyte & 0x01) cable_80_pin[0] = 1;
-
-	/*
-	 * 0x42, bit2 is 1 => secondary channel
-	 * has 80-pin (from host view)
-	 */
-	if (tmpbyte & 0x04) cable_80_pin[1] = 1;
-
-	switch(dev->device) {
-		case PCI_DEVICE_ID_AMD_COBRA_7401:
-			cable_80_pin[hwif->channel] = 0;
-			return 0;
-		case PCI_DEVICE_ID_AMD_8111_IDE:
-		case PCI_DEVICE_ID_AMD_VIPER_7441:
-		case PCI_DEVICE_ID_AMD_VIPER_7411:
-			ata66 = (hwif->channel) ?
-				cable_80_pin[1] :
-				cable_80_pin[0];
-		default:
-			break;
-	}
-	return (unsigned int) ata66;
+	return ((amd_enabled & amd_80w) >> hwif->channel) & 1;
 }
 
-static void __init init_hwif_amd74xx (ide_hwif_t *hwif)
+static void __init init_hwif_amd74xx(ide_hwif_t *hwif)
 {
+	int i;
+
 	hwif->autodma = 0;
+
 	hwif->tuneproc = &amd74xx_tune_drive;
-	hwif->speedproc = &amd74xx_tune_chipset;
+	hwif->speedproc = &amd_set_drive;
 
-	if (!hwif->dma_base) {
-		hwif->drives[0].autotune = 1;
-		hwif->drives[1].autotune = 1;
-		return;
+	for (i = 0; i < 2; i++) {
+		hwif->drives[i].io_32bit = 1;
+		hwif->drives[i].unmask = 1;
+		hwif->drives[i].autotune = 1;
+		hwif->drives[i].dn = hwif->channel * 2 + i;
 	}
 
-	hwif->atapi_dma = 1;
-	hwif->ultra_mask = 0x3f;
-	hwif->mwdma_mask = 0x07;
-	if (amd74xx_swdma_check(hwif->pci_dev))
-		hwif->swdma_mask = 0x07;
-
-	if (!(hwif->udma_four))
-		hwif->udma_four = ata66_amd74xx(hwif);
-	hwif->ide_dma_check = &amd74xx_config_drive_xfer_rate;
-	if (!noautodma)
-		hwif->autodma = 1;
-	hwif->drives[0].autodma = hwif->autodma;
-	hwif->drives[1].autodma = hwif->autodma;
-}
-
-static void __init init_dma_amd74xx (ide_hwif_t *hwif, unsigned long dmabase)
-{
-	if (!(hwif->channel))
-		hwif->OUTB(hwif->INB(dmabase+2) & 0x60, dmabase+2);
-	ide_setup_dma(hwif, dmabase, 8);
+	if (!hwif->dma_base)
+		return;
+
+        hwif->atapi_dma = 1;
+        hwif->ultra_mask = 0x7f;
+        hwif->mwdma_mask = 0x07;
+        hwif->swdma_mask = 0x07;
+
+        if (!(hwif->udma_four))
+                hwif->udma_four = ((amd_enabled & amd_80w) >> hwif->channel) & 1;
+        hwif->ide_dma_check = &amd74xx_ide_dma_check;
+        if (!noautodma)
+                hwif->autodma = 1;
+        hwif->drives[0].autodma = hwif->autodma;
+        hwif->drives[1].autodma = hwif->autodma;
 }
 
-extern void ide_setup_pci_device(struct pci_dev *, ide_pci_device_t *);
+/*
+ * We allow the BM-DMA driver only work on enabled interfaces.
+ */
 
+static void __init init_dma_amd74xx(ide_hwif_t *hwif, unsigned long dmabase)
+{
+	if ((amd_enabled >> hwif->channel) & 1)
+		ide_setup_dma(hwif, dmabase, 8);
+}
+
+extern void ide_setup_pci_device(struct pci_dev *, ide_pci_device_t *);
 
-static int __devinit amd74xx_init_one(struct pci_dev *dev, const struct pci_device_id *id)
+static int __devinit amd74xx_probe(struct pci_dev *dev, const struct pci_device_id *id)
 {
-	ide_pci_device_t *d = &amd74xx_chipsets[id->driver_data];
-	if (dev->device != d->device)
-		BUG();
+	ide_pci_device_t *d = amd74xx_chipsets + id->driver_data;
+	amd_config = amd_ide_chips + id->driver_data;
+	if (dev->device != d->device) BUG();
+	if (dev->device != amd_config->id) BUG();
 	ide_setup_pci_device(dev, d);
 	MOD_INC_USE_COUNT;
 	return 0;
@@ -417,13 +447,15 @@
 	{ PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_VIPER_7411,	PCI_ANY_ID, PCI_ANY_ID, 0, 0, 2},
 	{ PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_OPUS_7441,	PCI_ANY_ID, PCI_ANY_ID, 0, 0, 3},
 	{ PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8111_IDE, 	PCI_ANY_ID, PCI_ANY_ID, 0, 0, 4},
+	{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 5},
+	{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE2_IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 6},
 	{ 0, },
 };
 
 static struct pci_driver driver = {
 	.name		= "AMD IDE",
 	.id_table	= amd74xx_pci_tbl,
-	.probe		= amd74xx_init_one,
+	.probe		= amd74xx_probe,
 };
 
 static int amd74xx_ide_init(void)
@@ -439,8 +471,8 @@
 module_init(amd74xx_ide_init);
 module_exit(amd74xx_ide_exit);
 
-MODULE_AUTHOR("Andre Hedrick");
-MODULE_DESCRIPTION("PCI driver module for AMD IDE");
+MODULE_AUTHOR("Vojtech Pavlik");
+MODULE_DESCRIPTION("AMD PCI IDE driver");
 MODULE_LICENSE("GPL");
 
 EXPORT_NO_SYMBOLS;
diff -urN linux-2.4.21-pre5/drivers/ide/pci/amd74xx.h linux-2.4.21-pre6/drivers/ide/pci/amd74xx.h
--- linux-2.4.21-pre5/drivers/ide/pci/amd74xx.h	Wed Mar 26 16:11:28 2003
+++ linux-2.4.21-pre6/drivers/ide/pci/amd74xx.h	Wed Mar 26 16:11:36 2003
@@ -5,9 +5,9 @@
 #include <linux/pci.h>
 #include <linux/ide.h>
 
-#define DISPLAY_VIPER_TIMINGS
+#define DISPLAY_AMD_TIMINGS
 
-#if defined(DISPLAY_VIPER_TIMINGS) && defined(CONFIG_PROC_FS)
+#if defined(DISPLAY_AMD_TIMINGS) && defined(CONFIG_PROC_FS)
 #include <linux/stat.h>
 #include <linux/proc_fs.h>
 
@@ -17,13 +17,13 @@
 
 static ide_pci_host_proc_t amd74xx_procs[] __initdata = {
 	{
-		name:		"amd74xx",
-		set:		1,
-		get_info:	amd74xx_get_info,
-		parent:		NULL,
+		.name		= "amd74xx",
+		.set		= 1,
+		.get_info	= amd74xx_get_info,
+		.parent		= NULL,
 	},
 };
-#endif  /* defined(DISPLAY_VIPER_TIMINGS) && defined(CONFIG_PROC_FS) */
+#endif  /* defined(DISPLAY_AMD_TIMINGS) && defined(CONFIG_PROC_FS) */
 
 static unsigned int init_chipset_amd74xx(struct pci_dev *, const char *);
 static void init_hwif_amd74xx(ide_hwif_t *);
@@ -31,75 +31,104 @@
 
 static ide_pci_device_t amd74xx_chipsets[] __devinitdata = {
 	{	/* 0 */
-		vendor:		PCI_VENDOR_ID_AMD,
-		device:		PCI_DEVICE_ID_AMD_COBRA_7401,
-		name:		"AMD7401",
-		init_chipset:	init_chipset_amd74xx,
-		init_iops:	NULL,
-		init_hwif:	init_hwif_amd74xx,
-		init_dma:	init_dma_amd74xx,
-		channels:	2,
-		autodma:	AUTODMA,
-		enablebits:	{{0x40,0x01,0x01}, {0x40,0x02,0x02}},
-		bootable:	ON_BOARD,
-		extra:		0
+		.vendor		= PCI_VENDOR_ID_AMD,
+		.device		= PCI_DEVICE_ID_AMD_COBRA_7401,
+		.name		= "AMD7401",
+		.init_chipset	= init_chipset_amd74xx,
+		.init_iops	= NULL,
+		.init_hwif	= init_hwif_amd74xx,
+		.init_dma	= init_dma_amd74xx,
+		.channels	= 2,
+		.autodma	= AUTODMA,
+		.enablebits	= {{0x40,0x01,0x01}, {0x40,0x02,0x02}},
+		.bootable	= ON_BOARD,
+		.extra		= 0
 	},{	/* 1 */
-		vendor:		PCI_VENDOR_ID_AMD,
-		device:		PCI_DEVICE_ID_AMD_VIPER_7409,
-		name:		"AMD7409",
-		init_chipset:	init_chipset_amd74xx,
-		init_iops:	NULL,
-		init_hwif:	init_hwif_amd74xx,
-		init_dma:	init_dma_amd74xx,
-		channels:	2,
-		autodma:	AUTODMA,
-		enablebits:	{{0x40,0x01,0x01}, {0x40,0x02,0x02}},
-		bootable:	ON_BOARD,
-		extra:		0
+		.vendor		= PCI_VENDOR_ID_AMD,
+		.device		= PCI_DEVICE_ID_AMD_VIPER_7409,
+		.name		= "AMD7409",
+		.init_chipset	= init_chipset_amd74xx,
+		.init_iops	= NULL,
+		.init_hwif	= init_hwif_amd74xx,
+		.init_dma	= init_dma_amd74xx,
+		.channels	= 2,
+		.autodma	= AUTODMA,
+		.enablebits	= {{0x40,0x01,0x01}, {0x40,0x02,0x02}},
+		.bootable	= ON_BOARD,
+		.extra		= 0
 	},{	/* 2 */
-		vendor:		PCI_VENDOR_ID_AMD,
-		device:		PCI_DEVICE_ID_AMD_VIPER_7411,
-		name:		"AMD7411",
-		init_chipset:	init_chipset_amd74xx,
-		init_iops:	NULL,
-		init_hwif:	init_hwif_amd74xx,
-		init_dma:	init_dma_amd74xx,
-		channels:	2,
-		autodma:	AUTODMA,
-		enablebits:	{{0x40,0x01,0x01}, {0x40,0x02,0x02}},
-		bootable:	ON_BOARD,
-		extra:		0
+		.vendor		= PCI_VENDOR_ID_AMD,
+		.device		= PCI_DEVICE_ID_AMD_VIPER_7411,
+		.name		= "AMD7411",
+		.init_chipset	= init_chipset_amd74xx,
+		.init_iops	= NULL,
+		.init_hwif	= init_hwif_amd74xx,
+		.init_dma	= init_dma_amd74xx,
+		.channels	= 2,
+		.autodma	= AUTODMA,
+		.enablebits	= {{0x40,0x01,0x01}, {0x40,0x02,0x02}},
+		.bootable	= ON_BOARD,
+		.extra		= 0
 	},{	/* 3 */
-		vendor:		PCI_VENDOR_ID_AMD,
-		device:		PCI_DEVICE_ID_AMD_OPUS_7441,
-		name:		"AMD7441",
-		init_chipset:	init_chipset_amd74xx,
-		init_iops:	NULL,
-		init_hwif:	init_hwif_amd74xx,
-		init_dma:	init_dma_amd74xx,
-		channels:	2,
-		autodma:	AUTODMA,
-		enablebits:	{{0x40,0x01,0x01}, {0x40,0x02,0x02}},
-		bootable:	ON_BOARD,
-		extra:		0
+		.vendor		= PCI_VENDOR_ID_AMD,
+		.device		= PCI_DEVICE_ID_AMD_OPUS_7441,
+		.name		= "AMD7441",
+		.init_chipset	= init_chipset_amd74xx,
+		.init_iops	= NULL,
+		.init_hwif	= init_hwif_amd74xx,
+		.init_dma	= init_dma_amd74xx,
+		.channels	= 2,
+		.autodma	= AUTODMA,
+		.enablebits	= {{0x40,0x01,0x01}, {0x40,0x02,0x02}},
+		.bootable	= ON_BOARD,
+		.extra		= 0
 	},{	/* 4 */
-		vendor:		PCI_VENDOR_ID_AMD,
-		device:		PCI_DEVICE_ID_AMD_8111_IDE,
-		name:		"AMD8111",
-		init_chipset:	init_chipset_amd74xx,
-		init_iops:	NULL,
-		init_hwif:	init_hwif_amd74xx,
-		init_dma:	init_dma_amd74xx,
-		autodma:	AUTODMA,
-		channels:	2,
-		enablebits:	{{0x40,0x01,0x01}, {0x40,0x02,0x02}},
-		bootable:	ON_BOARD,
-		extra:		0
-	},{
-		vendor:		0,
-		device:		0,
-		channels:	0,
-		bootable:	EOL,
+		.vendor		= PCI_VENDOR_ID_AMD,
+		.device		= PCI_DEVICE_ID_AMD_8111_IDE,
+		.name		= "AMD8111",
+		.init_chipset	= init_chipset_amd74xx,
+		.init_iops	= NULL,
+		.init_hwif	= init_hwif_amd74xx,
+		.init_dma	= init_dma_amd74xx,
+		.autodma	= AUTODMA,
+		.channels	= 2,
+		.enablebits	= {{0x40,0x01,0x01}, {0x40,0x02,0x02}},
+		.bootable	= ON_BOARD,
+		.extra		= 0
+	},
+	{	/* 5 */
+		.vendor		= PCI_VENDOR_ID_NVIDIA,
+		.device		= PCI_DEVICE_ID_NVIDIA_NFORCE_IDE,
+		.name		= "NFORCE",
+		.init_chipset	= init_chipset_amd74xx,
+		.init_iops	= NULL,
+		.init_hwif	= init_hwif_amd74xx,
+		.init_dma	= init_dma_amd74xx,
+		.channels	= 2,
+		.autodma	= AUTODMA,
+		.enablebits	= {{0x50,0x01,0x01}, {0x50,0x02,0x02}},
+		.bootable	= ON_BOARD,
+		.extra		= 0,
+	},
+	{	/* 6 */
+		.vendor		= PCI_VENDOR_ID_NVIDIA,
+		.device		= PCI_DEVICE_ID_NVIDIA_NFORCE2_IDE,
+		.name		= "NFORCE2",
+		.init_chipset	= init_chipset_amd74xx,
+		.init_iops	= NULL,
+		.init_hwif	= init_hwif_amd74xx,
+		.init_dma	= init_dma_amd74xx,
+		.channels	= 2,
+		.autodma	= AUTODMA,
+		.enablebits	= {{0x50,0x01,0x01}, {0x50,0x02,0x02}},
+		.bootable	= ON_BOARD,
+		.extra		= 0,
+	},
+	{
+		.vendor		= 0,
+		.device		= 0,
+		.channels	= 0,
+		.bootable	= EOL,
 	}
 };
 
diff -urN linux-2.4.21-pre5/drivers/ide/pci/nvidia.c linux-2.4.21-pre6/drivers/ide/pci/nvidia.c
--- linux-2.4.21-pre5/drivers/ide/pci/nvidia.c	Wed Mar 26 16:11:28 2003
+++ linux-2.4.21-pre6/drivers/ide/pci/nvidia.c	Wed Dec 31 16:00:00 1969
@@ -1,373 +0,0 @@
-/*
- * linux/drivers/ide/pci/nvidia.c		Version 0.01
- *
- * Copyright (C) 2002-2002		Andre Hedrick <andre@linux-ide.org>
- * May be copied or modified under the terms of the GNU General Public License
- *
- */
-
-#include <linux/config.h>
-#include <linux/module.h>
-#include <linux/types.h>
-#include <linux/kernel.h>
-#include <linux/delay.h>
-#include <linux/timer.h>
-#include <linux/mm.h>
-#include <linux/ioport.h>
-#include <linux/blkdev.h>
-#include <linux/hdreg.h>
-
-#include <linux/interrupt.h>
-#include <linux/init.h>
-#include <linux/pci.h>
-#include <linux/ide.h>
-
-#include <asm/io.h>
-#include <asm/irq.h>
-
-#include "ide_modes.h"
-#include "nvidia.h"
-
-#if defined(DISPLAY_NFORCE_TIMINGS) && defined(CONFIG_PROC_FS)
-#include <linux/stat.h>
-#include <linux/proc_fs.h>
-
-static u8 nforce_proc = 0;
-static struct pci_dev *bmide_dev;
-
-static int nforce_get_info (char *buffer, char **addr, off_t offset, int count)
-{
-	char *p = buffer;
-	u32 bibma = pci_resource_start(bmide_dev, 4);
-	u8 c0 = 0, c1 = 0;
-
-	/*
-	 * at that point bibma+0x2 et bibma+0xa are byte registers
-	 * to investigate:
-	 */
-	c0 = inb((unsigned short)bibma + 0x02);
-	c1 = inb((unsigned short)bibma + 0x0a);
-
-	p += sprintf(p, "\n                                "
-			"nVidia %04X Chipset.\n", bmide_dev->device);
-	p += sprintf(p, "--------------- Primary Channel "
-			"---------------- Secondary Channel "
-			"-------------\n");
-	p += sprintf(p, "                %sabled "
-			"                        %sabled\n",
-			(c0&0x80) ? "dis" : " en",
-			(c1&0x80) ? "dis" : " en");
-	p += sprintf(p, "--------------- drive0 --------- drive1 "
-			"-------- drive0 ---------- drive1 ------\n");
-	p += sprintf(p, "DMA enabled:    %s              %s "
-			"            %s               %s\n",
-			(c0&0x20) ? "yes" : "no ", (c0&0x40) ? "yes" : "no ",
-			(c1&0x20) ? "yes" : "no ", (c1&0x40) ? "yes" : "no " );
-	p += sprintf(p, "UDMA\n");
-	p += sprintf(p, "DMA\n");
-	p += sprintf(p, "PIO\n");
-
-	return p-buffer;	/* => must be less than 4k! */
-}
-#endif  /* defined(DISPLAY_NFORCE_TIMINGS) && defined(CONFIG_PROC_FS) */
-
-static u8 nforce_ratemask (ide_drive_t *drive)
-{
-	u8 mode;
-
-        switch(HWIF(drive)->pci_dev->device) {
-		case PCI_DEVICE_ID_NVIDIA_NFORCE_IDE:
-		case PCI_DEVICE_ID_NVIDIA_NFORCE2_IDE:
-			mode = 3;
-			break;
-		default:
-			return 0;
-	}
-	if (!eighty_ninty_three(drive))
-		mode = min(mode, (u8)1);
-	return mode;
-}
-
-/*
- * Here is where all the hard work goes to program the chipset.
- */
-static int nforce_tune_chipset (ide_drive_t *drive, u8 xferspeed)
-{
-	static const u8 drive_pci[] = { 0x63, 0x62, 0x61, 0x60 };
-	static const u8 drive_pci2[] = { 0x5b, 0x5a, 0x59, 0x58 };
-
-	ide_hwif_t *hwif	= HWIF(drive);
-	struct pci_dev *dev	= hwif->pci_dev;
-	u8 speed	= ide_rate_filter(nforce_ratemask(drive), xferspeed);
-	u8 ultra_timing	= 0, dma_pio_timing = 0, pio_timing = 0;
-
-	pci_read_config_byte(dev, drive_pci[drive->dn], &ultra_timing);
-	pci_read_config_byte(dev, drive_pci2[drive->dn], &dma_pio_timing);
-	pci_read_config_byte(dev, 0x5c, &pio_timing);
-
-	ultra_timing	&= ~0xC7;
-	dma_pio_timing	&= ~0xFF;
-	pio_timing	&= ~(0x03 << drive->dn);
-
-	switch(speed) {
-		case XFER_UDMA_7:
-		case XFER_UDMA_6:
-			speed = XFER_UDMA_5;
-		case XFER_UDMA_5:
-			ultra_timing |= 0x46;
-			dma_pio_timing |= 0x20;
-			break;
-		case XFER_UDMA_4:
-			ultra_timing |= 0x45;
-			dma_pio_timing |= 0x20;
-			break;
-		case XFER_UDMA_3:
-			ultra_timing |= 0x44;
-			dma_pio_timing |= 0x20;
-			break;
-		case XFER_UDMA_2:
-			ultra_timing |= 0x40;
-			dma_pio_timing |= 0x20;
-			break;
-		case XFER_UDMA_1:
-			ultra_timing |= 0x41;
-			dma_pio_timing |= 0x20;
-			break;
-		case XFER_UDMA_0:
-			ultra_timing |= 0x42;
-			dma_pio_timing |= 0x20;
-			break;
-		case XFER_MW_DMA_2:
-			dma_pio_timing |= 0x20;
-			break;
-		case XFER_MW_DMA_1:
-			dma_pio_timing |= 0x21;
-			break;
-		case XFER_MW_DMA_0:
-			dma_pio_timing |= 0x77;
-			break;
-		case XFER_SW_DMA_2:
-			dma_pio_timing |= 0x42;
-			break;
-		case XFER_SW_DMA_1:
-			dma_pio_timing |= 0x65;
-			break;
-		case XFER_SW_DMA_0:
-			dma_pio_timing |= 0xA8;
-			break;
-		case XFER_PIO_4:
-			dma_pio_timing |= 0x20;
-			break;
-		case XFER_PIO_3:
-			dma_pio_timing |= 0x22;
-			break;
-		case XFER_PIO_2:
-			dma_pio_timing |= 0x42;
-			break;
-		case XFER_PIO_1:
-			dma_pio_timing |= 0x65;
-			break;
-		case XFER_PIO_0:
-		default:
-			dma_pio_timing |= 0xA8;
-			break;
-        }
-
-	pio_timing |= (0x03 << drive->dn);
-
-	pci_write_config_byte(dev, drive_pci[drive->dn], ultra_timing);
-	pci_write_config_byte(dev, drive_pci2[drive->dn], dma_pio_timing);
-	pci_write_config_byte(dev, 0x5c, pio_timing);
-
-	return (ide_config_drive_speed(drive, speed));
-}
-
-static void nforce_tune_drive (ide_drive_t *drive, u8 pio)
-{
-	pio = ide_get_best_pio_mode(drive, pio, 5, NULL);
-	(void) nforce_tune_chipset(drive, (XFER_PIO_0 + pio));
-}
-
-/*
- * This allows the configuration of ide_pci chipset registers
- * for cards that learn about the drive's UDMA, DMA, PIO capabilities
- * after the drive is reported by the OS.
- */
-static int config_chipset_for_dma (ide_drive_t *drive)
-{
-	u8 speed = ide_dma_speed(drive, nforce_ratemask(drive));
-
-	if (!(speed))
-		return 0;
-
-	(void) nforce_tune_chipset(drive, speed);
-	return ide_dma_enable(drive);
-}
-
-static int nforce_config_drive_xfer_rate (ide_drive_t *drive)
-{
-	ide_hwif_t *hwif	= HWIF(drive);
-	struct hd_driveid *id	= drive->id;
-
-	drive->init_speed = 0;
-
-	if (id && (id->capability & 1) && drive->autodma) {
-		/* Consult the list of known "bad" drives */
-		if (hwif->ide_dma_bad_drive(drive))
-			goto fast_ata_pio;
-		if (id->field_valid & 4) {
-			if (id->dma_ultra & hwif->ultra_mask) {
-				/* Force if Capable UltraDMA */
-				int dma = config_chipset_for_dma(drive);
-				if ((id->field_valid & 2) && !dma)
-					goto try_dma_modes;
-			}
-		} else if (id->field_valid & 2) {
-try_dma_modes:
-			if ((id->dma_mword & hwif->mwdma_mask) ||
-			    (id->dma_1word & hwif->swdma_mask)) {
-				/* Force if Capable regular DMA modes */
-				if (!config_chipset_for_dma(drive))
-					goto no_dma_set;
-			}
-
-		} else if (hwif->ide_dma_good_drive(drive) &&
-			   (id->eide_dma_time < 150)) {
-			/* Consult the list of known "good" drives */
-			if (!config_chipset_for_dma(drive))
-				goto no_dma_set;
-		} else {
-			goto fast_ata_pio;
-		}
-	} else if ((id->capability & 8) || (id->field_valid & 2)) {
-fast_ata_pio:
-no_dma_set:
-		nforce_tune_drive(drive, 5);
-		return hwif->ide_dma_off_quietly(drive);
-	}
-	return hwif->ide_dma_on(drive);
-}
-
-static unsigned int __init init_chipset_nforce (struct pci_dev *dev, const char *name)
-{
-#if defined(DISPLAY_NFORCE_TIMINGS) && defined(CONFIG_PROC_FS)
-	if (!nforce_proc) {
-		nforce_proc = 1;
-		bmide_dev = dev;
-		ide_pci_register_host_proc(&nforce_procs[0]);
-	}
-#endif /* DISPLAY_NFORCE_TIMINGS && CONFIG_PROC_FS */
-	return 0;
-}
-
-static unsigned int __init ata66_nforce (ide_hwif_t *hwif)
-{
-	struct pci_dev *dev	= hwif->pci_dev;
-	u8 cable_80_pin[2]	= { 0, 0 };
-	u8 ata66		= 0;
-	u8 tmpbyte;
-
-	/*
-	 * Ultra66 cable detection (from Host View)
-	 * 7411, 7441, 0x52, bit0: primary, bit2: secondary 80 pin
-	 */
-	pci_read_config_byte(dev, 0x52, &tmpbyte);
-
-	/*
-	 * 0x52, bit0 is 1 => primary channel
-	 * has 80-pin (from host view)
-	 */
-	if (tmpbyte & 0x01) cable_80_pin[0] = 1;
-
-	/*
-	 * 0x52, bit2 is 1 => secondary channel
-	 * has 80-pin (from host view)
-	 */
-	if (tmpbyte & 0x04) cable_80_pin[1] = 1;
-
-	switch(dev->device) {
-		case PCI_DEVICE_ID_NVIDIA_NFORCE_IDE:
-		case PCI_DEVICE_ID_NVIDIA_NFORCE2_IDE:
-			ata66 = (hwif->channel) ?
-				cable_80_pin[1] :
-				cable_80_pin[0];
-		default:
-			break;
-	}
-	return (unsigned int) ata66;
-}
-
-static void __init init_hwif_nforce (ide_hwif_t *hwif)
-{
-	hwif->tuneproc = &nforce_tune_drive;
-	hwif->speedproc = &nforce_tune_chipset;
-	hwif->autodma = 0;
-
-	if (!hwif->dma_base) {
-		hwif->drives[0].autotune = 1;
-		hwif->drives[1].autotune = 1;
-		return;
-	}
-
-	hwif->atapi_dma = 1;
-	hwif->ultra_mask = 0x3f;
-	hwif->mwdma_mask = 0x07;
-	hwif->swdma_mask = 0x07;
-
-	if (!(hwif->udma_four))
-		hwif->udma_four = ata66_nforce(hwif);
-	hwif->ide_dma_check = &nforce_config_drive_xfer_rate;
-	if (!noautodma)
-		hwif->autodma = 1;
-	hwif->drives[0].autodma = hwif->autodma;
-	hwif->drives[1].autodma = hwif->autodma;
-}
-
-/* FIXME - not needed */
-static void __init init_dma_nforce (ide_hwif_t *hwif, unsigned long dmabase)
-{
-	ide_setup_dma(hwif, dmabase, 8);
-}
-
-extern void ide_setup_pci_device(struct pci_dev *, ide_pci_device_t *);
-
-static int __devinit nforce_init_one(struct pci_dev *dev, const struct pci_device_id *id)
-{
-	ide_pci_device_t *d = &nvidia_chipsets[id->driver_data];
-	if (dev->device != d->device)
-		BUG();
-	ide_setup_pci_device(dev, d);
-	MOD_INC_USE_COUNT;
-	return 0;
-}
-
-static struct pci_device_id nforce_pci_tbl[] __devinitdata = {
-	{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
-	{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE2_IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 1},
-	{ 0, },
-};
-
-static struct pci_driver driver = {
-	.name		= "nForce IDE",
-	.id_table	= nforce_pci_tbl,
-	.probe		= nforce_init_one,
-};
-
-static int nforce_ide_init(void)
-{
-	return ide_pci_register_driver(&driver);
-}
-
-static void nforce_ide_exit(void)
-{
-	ide_pci_unregister_driver(&driver);
-}
-
-module_init(nforce_ide_init);
-module_exit(nforce_ide_exit);
-
-MODULE_AUTHOR("Andre Hedrick");
-MODULE_DESCRIPTION("PCI driver module for nVidia nForce IDE");
-MODULE_LICENSE("GPL");
-
-EXPORT_NO_SYMBOLS;
diff -urN linux-2.4.21-pre5/drivers/ide/pci/nvidia.h linux-2.4.21-pre6/drivers/ide/pci/nvidia.h
--- linux-2.4.21-pre5/drivers/ide/pci/nvidia.h	Wed Mar 26 16:11:28 2003
+++ linux-2.4.21-pre6/drivers/ide/pci/nvidia.h	Wed Dec 31 16:00:00 1969
@@ -1,65 +0,0 @@
-#ifndef NFORCE_H
-#define NFORCE_H
-
-#include <linux/config.h>
-#include <linux/pci.h>
-#include <linux/ide.h>
-
-#define DISPLAY_NFORCE_TIMINGS
-
-#if defined(DISPLAY_NFORCE_TIMINGS) && defined(CONFIG_PROC_FS)
-#include <linux/stat.h>
-#include <linux/proc_fs.h>
-
-static u8 nforce_proc;
-
-static int nforce_get_info(char *, char **, off_t, int);
-
-static ide_pci_host_proc_t nforce_procs[] __initdata = {
-	{
-		name:		"nforce",
-		set:		1,
-		get_info:	nforce_get_info,
-		parent:		NULL,
-	},
-};
-#endif  /* defined(DISPLAY_NFORCE_TIMINGS) && defined(CONFIG_PROC_FS) */
-
-static unsigned int init_chipset_nforce(struct pci_dev *, const char *);
-static void init_hwif_nforce(ide_hwif_t *);
-static void init_dma_nforce(ide_hwif_t *, unsigned long);
-
-static ide_pci_device_t nvidia_chipsets[] __devinitdata = {
-	{
-		vendor:		PCI_VENDOR_ID_NVIDIA,
-		device:		PCI_DEVICE_ID_NVIDIA_NFORCE_IDE,
-		name:		"NFORCE",
-		init_chipset:	init_chipset_nforce,
-		init_iops:	NULL,
-		init_hwif:	init_hwif_nforce,
-		init_dma:	init_dma_nforce,
-		channels:	2,
-		autodma:	AUTODMA,
-		enablebits:	{{0x50,0x01,0x01}, {0x50,0x02,0x02}},
-		bootable:	ON_BOARD,
-		extra:		0,
-	},
-
-	{
-		vendor:		PCI_VENDOR_ID_NVIDIA,
-		device:		PCI_DEVICE_ID_NVIDIA_NFORCE2_IDE,
-		name:		"NFORCE2",
-		init_chipset:	init_chipset_nforce,
-		init_iops:	NULL,
-		init_hwif:	init_hwif_nforce,
-		init_dma:	init_dma_nforce,
-		channels:	2,
-		autodma:	AUTODMA,
-		enablebits:	{{0x50,0x01,0x01}, {0x50,0x02,0x02}},
-		bootable:	ON_BOARD,
-		extra:		0,
-	}
-};
-
-
-#endif /* NFORCE_H */
diff -urN linux-2.4.21-pre5/drivers/ieee1394/Makefile linux-2.4.21-pre6/drivers/ieee1394/Makefile
--- linux-2.4.21-pre5/drivers/ieee1394/Makefile	Wed Mar 26 16:11:28 2003
+++ linux-2.4.21-pre6/drivers/ieee1394/Makefile	Wed Mar 26 16:11:36 2003
@@ -4,11 +4,11 @@
 
 O_TARGET := ieee1394drv.o
 
-export-objs := ieee1394_core.o ohci1394.o cmp.o
+export-objs := ieee1394_core.o ohci1394.o cmp.o iso.o
 
 list-multi := ieee1394.o
 ieee1394-objs := ieee1394_core.o ieee1394_transactions.o hosts.o \
-		 highlevel.o csr.o nodemgr.o dma.o
+		 highlevel.o csr.o nodemgr.o dma.o iso.o
 
 obj-$(CONFIG_IEEE1394) += ieee1394.o
 obj-$(CONFIG_IEEE1394_PCILYNX) += pcilynx.o
diff -urN linux-2.4.21-pre5/drivers/ieee1394/dv1394-private.h linux-2.4.21-pre6/drivers/ieee1394/dv1394-private.h
--- linux-2.4.21-pre5/drivers/ieee1394/dv1394-private.h	Wed Mar 26 16:11:28 2003
+++ linux-2.4.21-pre6/drivers/ieee1394/dv1394-private.h	Wed Mar 26 16:11:36 2003
@@ -458,6 +458,10 @@
 	 */
 	spinlock_t spinlock;
 
+	/* flag to prevent spurious interrupts (which OHCI seems to
+	   generate a lot :) from accessing the struct */
+	int dma_running;
+	
 	/*
 	  3) the sleeping semaphore 'sem' - this is used from process context only,
 	  to serialize various operations on the video_card. Even though only one
@@ -571,7 +575,7 @@
 
 static int do_dv1394_init(struct video_card *video, struct dv1394_init *init);
 static int do_dv1394_init_default(struct video_card *video);
-static int do_dv1394_shutdown(struct video_card *video, int free_user_buf);
+static void do_dv1394_shutdown(struct video_card *video, int free_user_buf);
 
 
 /* NTSC empty packet rate accurate to within 0.01%, 
diff -urN linux-2.4.21-pre5/drivers/ieee1394/dv1394.c linux-2.4.21-pre6/drivers/ieee1394/dv1394.c
--- linux-2.4.21-pre5/drivers/ieee1394/dv1394.c	Wed Mar 26 16:11:28 2003
+++ linux-2.4.21-pre6/drivers/ieee1394/dv1394.c	Wed Mar 26 16:11:36 2003
@@ -138,7 +138,7 @@
        (will cause undeflows if your machine is too slow!)
 */
 
-#define DV1394_DEBUG_LEVEL 1
+#define DV1394_DEBUG_LEVEL 0
 
 /* for debugging use ONLY: allow more than one open() of the device */
 /* #define DV1394_ALLOW_MORE_THAN_ONE_OPEN 1 */
@@ -695,7 +695,7 @@
 		wmb();
 #endif
 
-		
+		video->dma_running = 1;
 
 		/* set the 'run' bit */
 		reg_write(video->ohci, video->ohci_IsoXmitContextControlSet, 0x8000);
@@ -817,7 +817,9 @@
 		reg_write(video->ohci, video->ohci_IsoRcvCommandPtr,             
 			  video->frames[0]->descriptor_pool_dma | 1); /* Z=1 */
 		wmb();
-		
+
+		video->dma_running = 1;
+
 		/* run */
 		reg_write(video->ohci, video->ohci_IsoRcvContextControlSet, 0x8000);
 		flush_pci_write(video->ohci);
@@ -918,16 +920,16 @@
 	u64 chan_mask;
 	int retval = -EINVAL;
 
-	debug_printk( "dv1394: initialising %d\n", video->id );
+	debug_printk("dv1394: initialising %d\n", video->id);
 	if(init->api_version != DV1394_API_VERSION)
-		goto err;
+		return -EINVAL;
 	
 	/* first sanitize all the parameters */
 	if( (init->n_frames < 2) || (init->n_frames > DV1394_MAX_FRAMES) )
-		goto err;
+		return -EINVAL;
 
 	if( (init->format != DV1394_NTSC) && (init->format != DV1394_PAL) )
-		goto err;
+		return -EINVAL;
 
 	if( (init->syt_offset == 0) || (init->syt_offset > 50) )
 		/* default SYT offset is 3 cycles */
@@ -948,15 +950,15 @@
 	if(new_buf_size % PAGE_SIZE) new_buf_size += PAGE_SIZE - (new_buf_size % PAGE_SIZE);
 
 	/* don't allow the user to allocate the DMA buffer more than once */
-	if(video->dv_buf.kvirt && video->dv_buf_size != new_buf_size)
-		goto err;
-	
+	if(video->dv_buf.kvirt && video->dv_buf_size != new_buf_size) {
+		printk("dv1394: re-sizing the DMA buffer is not allowed\n");
+		return -EINVAL;
+	}
+
 	/* shutdown the card if it's currently active */
 	/* (the card should not be reset if the parameters are screwy) */
-	if( video_card_initialized(video) )
-		do_dv1394_shutdown(video, 0);
-
 
+	do_dv1394_shutdown(video, 0);
 	
 	/* try to claim the ISO channel */
 	spin_lock_irqsave(&video->ohci->IR_channel_lock, flags);
@@ -973,7 +975,6 @@
 	/* initialize misc. fields of video */
 	video->n_frames = init->n_frames;
 	video->pal_or_ntsc = init->format;
-	
 
 	video->cip_accum = 0;
 	video->continuity_counter = 0;
@@ -989,7 +990,6 @@
 	video->current_packet = -1;
 	video->first_frame = 0;
 
-
 	if(video->pal_or_ntsc == DV1394_NTSC) {
 		video->cip_n = init->cip_n != 0 ? init->cip_n : CIP_N_NTSC;
 		video->cip_d = init->cip_d != 0 ? init->cip_d : CIP_D_NTSC;
@@ -1002,14 +1002,8 @@
 
 	video->syt_offset = init->syt_offset;
 	
-	
 	/* find and claim DMA contexts on the OHCI card */
 
-	/* XXX this should be the last step of initialization, since the interrupt
-	   handler uses ohci_i*_ctx to indicate whether or not it is safe to touch
-	   frames. I'm not making this change quite yet, since it would be better
-	   to clean up the init/shutdown process first.*/
-
 	if(video->ohci_it_ctx == -1) {
 		ohci1394_init_iso_tasklet(&video->it_tasklet, OHCI_ISO_TRANSMIT,
 					  it_tasklet_func, (unsigned long) video);
@@ -1017,14 +1011,12 @@
 		if (ohci1394_register_iso_tasklet(video->ohci, &video->it_tasklet) < 0) {	
 			printk(KERN_ERR "dv1394: could not find an available IT DMA context\n");
 			retval = -EBUSY;
-			goto err_ctx;
-		}
-		else {
-			video->ohci_it_ctx = video->it_tasklet.context;
-			debug_printk("dv1394: claimed IT DMA context %d\n", video->ohci_it_ctx);
+			goto err;
 		}
+		
+		video->ohci_it_ctx = video->it_tasklet.context;
+		debug_printk("dv1394: claimed IT DMA context %d\n", video->ohci_it_ctx);
 	}
-	
 
 	if(video->ohci_ir_ctx == -1) {
 		ohci1394_init_iso_tasklet(&video->ir_tasklet, OHCI_ISO_RECEIVE,
@@ -1033,14 +1025,11 @@
 		if (ohci1394_register_iso_tasklet(video->ohci, &video->ir_tasklet) < 0) {
 			printk(KERN_ERR "dv1394: could not find an available IR DMA context\n");
 			retval = -EBUSY;
-			goto err_ctx;
-		}
-		else {
-			video->ohci_ir_ctx = video->ir_tasklet.context;
-			debug_printk("dv1394: claimed IR DMA context %d\n", video->ohci_ir_ctx);
+			goto err;
 		}
+		video->ohci_ir_ctx = video->ir_tasklet.context;
+		debug_printk("dv1394: claimed IR DMA context %d\n", video->ohci_ir_ctx);
 	}
-
 	
 	/* allocate struct frames */
 	for(i = 0; i < init->n_frames; i++) {
@@ -1049,7 +1038,7 @@
 		if(!video->frames[i]) {
 			printk(KERN_ERR "dv1394: Cannot allocate frame structs\n");
 			retval = -ENOMEM;
-			goto err_frames;
+			goto err;
 		}
 	}
 
@@ -1057,7 +1046,7 @@
 		/* allocate the ringbuffer */
 		retval = dma_region_alloc(&video->dv_buf, new_buf_size, video->ohci->dev, PCI_DMA_TODEVICE);
 		if(retval)
-			goto err_frames;
+			goto err;
 					  
 		video->dv_buf_size = new_buf_size;
 
@@ -1079,7 +1068,7 @@
 		retval = dma_region_alloc(&video->packet_buf, video->packet_buf_size,
 					  video->ohci->dev, PCI_DMA_FROMDEVICE);
 		if(retval)
-			goto err_dv_buf;
+			goto err;
 		
 		debug_printk("dv1394: Allocated %d packets in buffer, total %u pages (%u DMA pages), %lu bytes\n", 
 				 video->n_frames*MAX_PACKETS, video->packet_buf.n_pages,
@@ -1109,30 +1098,8 @@
 	
 	return 0;
 
- err_dv_buf:
-	dma_region_free(&video->dv_buf);
-	
- err_frames:
-	for(i = 0; i < DV1394_MAX_FRAMES; i++) {
-		if(video->frames[i])
-			frame_delete(video->frames[i]);
-	}	
-	video->n_frames = 0;
-
- err_ctx:
-	if(video->ohci_it_ctx != -1) {
-		ohci1394_unregister_iso_tasklet(video->ohci, &video->it_tasklet);
-		video->ohci_it_ctx = -1;
-	}
-	if(video->ohci_ir_ctx != -1) {
-		ohci1394_unregister_iso_tasklet(video->ohci, &video->ir_tasklet);
-		video->ohci_ir_ctx = -1;
-	}
-	
-	spin_lock_irqsave(&video->ohci->IR_channel_lock, flags);
-	video->ohci->ISO_channel_usage &= ~chan_mask;
-	spin_unlock_irqrestore(&video->ohci->IR_channel_lock, flags);
- err:
+err:
+	do_dv1394_shutdown(video, 1);
 	return retval;
 }
 
@@ -1160,10 +1127,15 @@
 {
 	unsigned long flags;
 	int i;
-	
+
 	/* no interrupts */
 	spin_lock_irqsave(&video->spinlock, flags);
 
+	video->dma_running = 0;
+
+	if( (video->ohci_it_ctx == -1) && (video->ohci_ir_ctx == -1) )
+		goto out;
+
 	/* stop DMA if in progress */
 	if( (video->active_frame != -1) ||
 	    (reg_read(video->ohci, video->ohci_IsoXmitContextControlClear) & (1 << 10)) ||
@@ -1203,24 +1175,22 @@
 	}
 	else
 		debug_printk("dv1394: stop_dma: already stopped.\n");
-		
+
+out:
 	spin_unlock_irqrestore(&video->spinlock, flags);
 }
 
 
 
-static int do_dv1394_shutdown(struct video_card *video, int free_dv_buf)
+static void do_dv1394_shutdown(struct video_card *video, int free_dv_buf)
 {
 	int i;
-	unsigned long flags;
 	
 	debug_printk("dv1394: shutdown...\n");
 
 	/* stop DMA if in progress */
 	stop_dma(video);
 	
-	spin_lock_irqsave(&video->spinlock, flags);
-
 	/* release the DMA contexts */
 	if(video->ohci_it_ctx != -1) {
 		video->ohci_IsoXmitContextControlSet = 0;
@@ -1251,8 +1221,6 @@
 		video->ohci_ir_ctx = -1;
 	}
 
-	spin_unlock_irqrestore(&video->spinlock, flags);
-
 	/* release the ISO channel */
 	if(video->channel != -1) {
 		u64 chan_mask;
@@ -1288,9 +1256,7 @@
 	dma_region_free(&video->packet_buf);
 	video->packet_buf_size = 0;
 
-	debug_printk("dv1394: shutdown complete\n");
-
-	return 0;
+	debug_printk("dv1394: shutdown OK\n");
 }
 
 /*
@@ -1777,7 +1743,8 @@
 	}
 	case DV1394_SHUTDOWN:
 	case DV1394_IOC_SHUTDOWN:
-		ret = do_dv1394_shutdown(video, 0);
+		do_dv1394_shutdown(video, 0);
+		ret = 0;
 		break;
 
 	case DV1394_GET_STATUS:
@@ -2147,6 +2114,9 @@
 
 	spin_lock(&video->spinlock);
 
+	if(!video->dma_running)
+		goto out;
+
 	irq_printk("ContextControl = %08x, CommandPtr = %08x\n", 
 	       reg_read(video->ohci, video->ohci_IsoXmitContextControlSet),
 	       reg_read(video->ohci, video->ohci_IsoXmitCommandPtr)
@@ -2273,14 +2243,15 @@
 		} /* for(each frame) */
 	}
 
-	spin_unlock(&video->spinlock);
-
 	if(wake) {
 		kill_fasync(&video->fasync, SIGIO, POLL_OUT);
 		
 		/* wake readers/writers/ioctl'ers */
 		wake_up_interruptible(&video->waitq);
 	}
+
+out:
+	spin_unlock(&video->spinlock);
 }
 
 static void ir_tasklet_func(unsigned long data)
@@ -2290,8 +2261,11 @@
 
 	spin_lock(&video->spinlock);
 
-	if( (video->ohci_ir_ctx != -1) 
-	    && (reg_read(video->ohci, video->ohci_IsoRcvContextControlSet) & (1 << 10)) ) 
+	if(!video->dma_running)
+		goto out;
+	
+	if( (video->ohci_ir_ctx != -1) &&
+	    (reg_read(video->ohci, video->ohci_IsoRcvContextControlSet) & (1 << 10)) )
 	{ 
 
 		int sof=0; /* start-of-frame flag */
@@ -2431,14 +2405,15 @@
 		
 	} /* receive interrupt */
 	
-	spin_unlock(&video->spinlock);
-	
 	if(wake) {
 		kill_fasync(&video->fasync, SIGIO, POLL_IN);
 
 		/* wake readers/writers/ioctl'ers */
 		wake_up_interruptible(&video->waitq);
 	}
+	
+out:
+	spin_unlock(&video->spinlock);
 }
 
 static struct file_operations dv1394_fops=
@@ -2652,6 +2627,7 @@
 
 	clear_bit(0, &video->open);
 	spin_lock_init(&video->spinlock);
+	video->dma_running = 0;
 	init_MUTEX(&video->sem);
 	init_waitqueue_head(&video->waitq);
 	video->fasync = NULL;
@@ -2702,7 +2678,7 @@
 	struct ti_ohci *ohci;
 	struct video_card *video = NULL;
 	unsigned long flags;
-	struct list_head *lh;
+	struct list_head *lh, *templh;
 	char buf[32];
 	int	n;
 	
@@ -2716,7 +2692,7 @@
 	/* find the corresponding video_cards */
 	spin_lock_irqsave(&dv1394_cards_lock, flags);
 	if(!list_empty(&dv1394_cards)) {
-		list_for_each(lh, &dv1394_cards) {
+		list_for_each_safe(lh, templh, &dv1394_cards) {
 			video = list_entry(lh, struct video_card, list);
 			if((video->id >> 2) == ohci->id)
 				dv1394_un_init(video);
@@ -2823,6 +2799,9 @@
 	
 	spin_lock_irqsave(&video->spinlock, flags);
 
+	if(!video->dma_running)
+		goto out;
+
 	/* check IT context */
 	if(video->ohci_it_ctx != -1) {
 		u32 ctx;
@@ -2895,7 +2874,8 @@
 				   reg_read(video->ohci, video->ohci_IsoRcvCommandPtr));
 		}
 	}
-	
+
+out:
 	spin_unlock_irqrestore(&video->spinlock, flags);
 	
 	/* wake readers/writers/ioctl'ers */
diff -urN linux-2.4.21-pre5/drivers/ieee1394/dv1394.h linux-2.4.21-pre6/drivers/ieee1394/dv1394.h
--- linux-2.4.21-pre5/drivers/ieee1394/dv1394.h	Wed Mar 26 16:11:28 2003
+++ linux-2.4.21-pre6/drivers/ieee1394/dv1394.h	Wed Mar 26 16:11:36 2003
@@ -175,7 +175,7 @@
    where copy_DV_frame() reads or writes on the dv1394 file descriptor
    (read/write mode) or copies data to/from the mmap ringbuffer and
    then calls ioctl(DV1394_SUBMIT_FRAMES) to notify dv1394 that new
-   frames are available (mmap mode).
+   frames are availble (mmap mode).
 
    reset_dv1394() is called in the event of a buffer
    underflow/overflow or a halt in the DV stream (e.g. due to a 1394
diff -urN linux-2.4.21-pre5/drivers/ieee1394/eth1394.c linux-2.4.21-pre6/drivers/ieee1394/eth1394.c
--- linux-2.4.21-pre5/drivers/ieee1394/eth1394.c	Wed Mar 26 16:11:28 2003
+++ linux-2.4.21-pre6/drivers/ieee1394/eth1394.c	Wed Mar 26 16:11:36 2003
@@ -77,7 +77,7 @@
 	printk(KERN_ERR fmt, ## args)
 
 static char version[] __devinitdata =
-	"$Rev: 771 $ Ben Collins <bcollins@debian.org>";
+	"$Rev: 807 $ Ben Collins <bcollins@debian.org>";
 
 /* Our ieee1394 highlevel driver */
 #define ETHER1394_DRIVER_NAME "ether1394"
@@ -206,7 +206,7 @@
 	unsigned char arp_data[2*(dev->addr_len+4)];
 
 	/* Copy the main data that we need */
-	arp_ptr = memcpy (arp_data, arp_ptr + sizeof(struct arphdr), sizeof (arp_data));
+	memcpy (arp_data, arp_ptr + sizeof(struct arphdr), sizeof (arp_data));
 
 	/* Extend the buffer enough for our new header */
 	skb_put (skb, sizeof (struct eth1394_arp) -
@@ -214,7 +214,7 @@
 
 #define PROCESS_MEMBER(ptr,val,len) \
   memcpy (val, ptr, len); ptr += len
-	arp_ptr += arp1394->hw_addr_len;
+	arp_ptr = arp_data + arp1394->hw_addr_len;
 	PROCESS_MEMBER (arp_ptr, &arp1394->sip, arp1394->ip_addr_len);
 	arp_ptr += arp1394->hw_addr_len;
 	PROCESS_MEMBER (arp_ptr, &arp1394->tip, arp1394->ip_addr_len);
@@ -498,7 +498,7 @@
 		spin_unlock_irqrestore (&priv->lock, flags);
 
 #define PROCESS_MEMBER(ptr,val,len) \
-  ptr = memcpy (ptr, val, len) + len
+  memcpy (ptr, val, len); ptr += len
                 PROCESS_MEMBER (arp_ptr, src_hw, dev->addr_len);
                 PROCESS_MEMBER (arp_ptr, &arp1394.sip, 4);
                 PROCESS_MEMBER (arp_ptr, dest_hw, dev->addr_len);
diff -urN linux-2.4.21-pre5/drivers/ieee1394/hosts.h linux-2.4.21-pre6/drivers/ieee1394/hosts.h
--- linux-2.4.21-pre5/drivers/ieee1394/hosts.h	Wed Mar 26 16:11:28 2003
+++ linux-2.4.21-pre6/drivers/ieee1394/hosts.h	Wed Mar 26 16:11:36 2003
@@ -19,6 +19,7 @@
 #define CSR_CONFIG_ROM_SIZE       0x100
 
 struct hpsb_packet;
+struct hpsb_iso;
 
 struct hpsb_host {
         struct list_head host_list;
@@ -109,6 +110,31 @@
         ISO_UNLISTEN_CHANNEL
 };
 
+enum isoctl_cmd {
+	/* rawiso API - see iso.h for the meanings of these commands
+	 * INIT = allocate resources
+	 * START = begin transmission/reception
+	 * STOP = halt transmission/reception
+	 * QUEUE/RELEASE = produce/consume packets
+	 * SHUTDOWN = deallocate resources
+	 */
+
+	XMIT_INIT,
+	XMIT_START,
+	XMIT_STOP,
+	XMIT_QUEUE,
+	XMIT_SHUTDOWN,
+
+	RECV_INIT,
+	RECV_LISTEN_CHANNEL,   /* multi-channel only */
+	RECV_UNLISTEN_CHANNEL, /* multi-channel only */
+	RECV_SET_CHANNEL_MASK, /* multi-channel only; arg is a *u64 */
+	RECV_START,
+	RECV_STOP,
+	RECV_RELEASE,
+	RECV_SHUTDOWN,
+};
+
 enum reset_types {
         /* 166 microsecond reset -- only type of reset available on
            non-1394a capable IEEE 1394 controllers */
@@ -146,6 +172,12 @@
          */
         int (*devctl) (struct hpsb_host *host, enum devctl_cmd command, int arg);
 
+	 /* rawiso transmission/reception functions. Return 0 on success, -1
+	  * (or -EXXX errno code) on failure. If the low-level driver does not
+	  * support the rawiso API, set isoctl to NULL.
+	  */
+	int (*isoctl) (struct hpsb_iso *iso, enum isoctl_cmd command, unsigned long arg);
+
         /* This function is mainly to redirect local CSR reads/locks to the iso
          * management registers (bus manager id, bandwidth available, channels
          * available) to the hardware registers in OHCI.  reg is 0,1,2,3 for bus
diff -urN linux-2.4.21-pre5/drivers/ieee1394/ieee1394-ioctl.h linux-2.4.21-pre6/drivers/ieee1394/ieee1394-ioctl.h
--- linux-2.4.21-pre5/drivers/ieee1394/ieee1394-ioctl.h	Wed Mar 26 16:11:28 2003
+++ linux-2.4.21-pre6/drivers/ieee1394/ieee1394-ioctl.h	Wed Mar 26 16:11:36 2003
@@ -68,4 +68,38 @@
 #define VIDEO1394_IOC_LISTEN_POLL_BUFFER	\
 	_IOWR('#', 0x18, struct video1394_wait)
 
+
+/* Raw1394's ISO interface */
+#define RAW1394_IOC_ISO_XMIT_INIT		\
+	_IOW ('#', 0x1a, struct raw1394_iso_status)
+#define RAW1394_IOC_ISO_RECV_INIT		\
+	_IOWR('#', 0x1b, struct raw1394_iso_status)
+#define RAW1394_IOC_ISO_RECV_START		\
+	_IOC (_IOC_WRITE, '#', 0x1c, sizeof(int) * 3)
+#define RAW1394_IOC_ISO_XMIT_START		\
+	_IOC (_IOC_WRITE, '#', 0x1d, sizeof(int) * 2)
+#define RAW1394_IOC_ISO_XMIT_RECV_STOP		\
+	_IO  ('#', 0x1e)
+#define RAW1394_IOC_ISO_GET_STATUS		\
+	_IOR ('#', 0x1f, struct raw1394_iso_status)
+#define RAW1394_IOC_ISO_SHUTDOWN		\
+	_IO  ('#', 0x20)
+#define RAW1394_IOC_ISO_QUEUE_ACTIVITY		\
+	_IO  ('#', 0x21)
+#define RAW1394_IOC_ISO_RECV_LISTEN_CHANNEL	\
+	_IOW ('#', 0x22, unsigned char)
+#define RAW1394_IOC_ISO_RECV_UNLISTEN_CHANNEL	\
+	_IOW ('#', 0x23, unsigned char)
+#define RAW1394_IOC_ISO_RECV_SET_CHANNEL_MASK	\
+	_IOW ('#', 0x24, __u64)
+#define RAW1394_IOC_ISO_RECV_PACKETS		\
+	_IOW ('#', 0x25, struct raw1394_iso_packets)
+#define RAW1394_IOC_ISO_RECV_RELEASE_PACKETS	\
+	_IOW ('#', 0x26, unsigned int)
+#define RAW1394_IOC_ISO_XMIT_PACKETS		\
+	_IOW ('#', 0x27, struct raw1394_iso_packets)
+#define RAW1394_IOC_ISO_XMIT_SYNC		\
+	_IO  ('#', 0x28)
+
+
 #endif /* __IEEE1394_IOCTL_H */
diff -urN linux-2.4.21-pre5/drivers/ieee1394/ieee1394.h linux-2.4.21-pre6/drivers/ieee1394/ieee1394.h
--- linux-2.4.21-pre5/drivers/ieee1394/ieee1394.h	Fri Aug  2 17:39:44 2002
+++ linux-2.4.21-pre6/drivers/ieee1394/ieee1394.h	Wed Mar 26 16:11:36 2003
@@ -15,6 +15,7 @@
 #define TCODE_CYCLE_START        0x8
 #define TCODE_LOCK_REQUEST       0x9
 #define TCODE_ISO_DATA           0xa
+#define TCODE_STREAM_DATA        0xa
 #define TCODE_LOCK_RESPONSE      0xb
 
 #define RCODE_COMPLETE           0x0
diff -urN linux-2.4.21-pre5/drivers/ieee1394/ieee1394_core.c linux-2.4.21-pre6/drivers/ieee1394/ieee1394_core.c
--- linux-2.4.21-pre5/drivers/ieee1394/ieee1394_core.c	Wed Mar 26 16:11:28 2003
+++ linux-2.4.21-pre6/drivers/ieee1394/ieee1394_core.c	Wed Mar 26 16:11:36 2003
@@ -43,6 +43,7 @@
 #include "nodemgr.h"
 #include "ieee1394_hotplug.h"
 #include "dma.h"
+#include "iso.h"
 
 /*
  * Disable the nodemgr detection and config rom reading functionality.
@@ -1144,14 +1145,17 @@
 module_exit(ieee1394_cleanup);
 
 /* Exported symbols */
+
+/** hosts.c **/
 EXPORT_SYMBOL(hpsb_alloc_host);
 EXPORT_SYMBOL(hpsb_add_host);
 EXPORT_SYMBOL(hpsb_remove_host);
 EXPORT_SYMBOL(hpsb_ref_host);
 EXPORT_SYMBOL(hpsb_unref_host);
+
+/** ieee1394_core.c **/
 EXPORT_SYMBOL(hpsb_speedto_str);
 EXPORT_SYMBOL(hpsb_add_packet_complete_task);
-
 EXPORT_SYMBOL(alloc_hpsb_packet);
 EXPORT_SYMBOL(free_hpsb_packet);
 EXPORT_SYMBOL(hpsb_send_packet);
@@ -1161,7 +1165,12 @@
 EXPORT_SYMBOL(hpsb_selfid_complete);
 EXPORT_SYMBOL(hpsb_packet_sent);
 EXPORT_SYMBOL(hpsb_packet_received);
+EXPORT_SYMBOL(ieee1394_register_chardev);
+EXPORT_SYMBOL(ieee1394_unregister_chardev);
+EXPORT_SYMBOL(ieee1394_devfs_handle);
+EXPORT_SYMBOL(ieee1394_procfs_entry);
 
+/** ieee1394_transactions.c **/
 EXPORT_SYMBOL(get_tlabel);
 EXPORT_SYMBOL(free_tlabel);
 EXPORT_SYMBOL(fill_async_readquad);
@@ -1188,6 +1197,7 @@
 EXPORT_SYMBOL(hpsb_write);
 EXPORT_SYMBOL(hpsb_lock);
 
+/** highlevel.c **/
 EXPORT_SYMBOL(hpsb_register_highlevel);
 EXPORT_SYMBOL(hpsb_unregister_highlevel);
 EXPORT_SYMBOL(hpsb_register_addrspace);
@@ -1202,6 +1212,7 @@
 EXPORT_SYMBOL(highlevel_remove_host);
 EXPORT_SYMBOL(highlevel_host_reset);
 
+/** nodemgr.c **/
 EXPORT_SYMBOL(hpsb_guid_get_entry);
 EXPORT_SYMBOL(hpsb_nodeid_get_entry);
 EXPORT_SYMBOL(hpsb_node_fill_packet);
@@ -1214,12 +1225,6 @@
 EXPORT_SYMBOL(hpsb_unregister_protocol);
 EXPORT_SYMBOL(hpsb_release_unit_directory);
 
-EXPORT_SYMBOL(ieee1394_register_chardev);
-EXPORT_SYMBOL(ieee1394_unregister_chardev);
-EXPORT_SYMBOL(ieee1394_devfs_handle);
-
-EXPORT_SYMBOL(ieee1394_procfs_entry);
-
 /** dma.c **/
 EXPORT_SYMBOL(dma_prog_region_init);
 EXPORT_SYMBOL(dma_prog_region_alloc);
@@ -1231,3 +1236,21 @@
 EXPORT_SYMBOL(dma_region_mmap);
 EXPORT_SYMBOL(dma_region_offset_to_bus);
 
+/** iso.c **/
+EXPORT_SYMBOL(hpsb_iso_xmit_init);
+EXPORT_SYMBOL(hpsb_iso_recv_init);
+EXPORT_SYMBOL(hpsb_iso_xmit_start);
+EXPORT_SYMBOL(hpsb_iso_recv_start);
+EXPORT_SYMBOL(hpsb_iso_recv_listen_channel);
+EXPORT_SYMBOL(hpsb_iso_recv_unlisten_channel);
+EXPORT_SYMBOL(hpsb_iso_recv_set_channel_mask);
+EXPORT_SYMBOL(hpsb_iso_stop);
+EXPORT_SYMBOL(hpsb_iso_shutdown);
+EXPORT_SYMBOL(hpsb_iso_xmit_queue_packet);
+EXPORT_SYMBOL(hpsb_iso_xmit_sync);
+EXPORT_SYMBOL(hpsb_iso_recv_release_packets);
+EXPORT_SYMBOL(hpsb_iso_n_ready);
+EXPORT_SYMBOL(hpsb_iso_packet_sent);
+EXPORT_SYMBOL(hpsb_iso_packet_received);
+EXPORT_SYMBOL(hpsb_iso_wake);
+
diff -urN linux-2.4.21-pre5/drivers/ieee1394/iso.c linux-2.4.21-pre6/drivers/ieee1394/iso.c
--- linux-2.4.21-pre5/drivers/ieee1394/iso.c	Wed Dec 31 16:00:00 1969
+++ linux-2.4.21-pre6/drivers/ieee1394/iso.c	Wed Mar 26 16:11:36 2003
@@ -0,0 +1,439 @@
+/*
+ * IEEE 1394 for Linux
+ *
+ * kernel ISO transmission/reception
+ *
+ * Copyright (C) 2002 Maas Digital LLC
+ *
+ * This code is licensed under the GPL.  See the file COPYING in the root
+ * directory of the kernel sources for details.
+ */
+
+#include <linux/slab.h>
+#include "iso.h"
+
+void hpsb_iso_stop(struct hpsb_iso *iso)
+{
+	if(!iso->flags & HPSB_ISO_DRIVER_STARTED)
+		return;
+
+	iso->host->driver->isoctl(iso, iso->type == HPSB_ISO_XMIT ?
+				  XMIT_STOP : RECV_STOP, 0);
+	iso->flags &= ~HPSB_ISO_DRIVER_STARTED;
+}
+
+void hpsb_iso_shutdown(struct hpsb_iso *iso)
+{
+	if(iso->flags & HPSB_ISO_DRIVER_INIT) {
+		hpsb_iso_stop(iso);
+		iso->host->driver->isoctl(iso, iso->type == HPSB_ISO_XMIT ?
+					  XMIT_SHUTDOWN : RECV_SHUTDOWN, 0);
+		iso->flags &= ~HPSB_ISO_DRIVER_INIT;
+	}
+
+	dma_region_free(&iso->data_buf);
+	kfree(iso->infos);
+	kfree(iso);
+}
+
+static struct hpsb_iso* hpsb_iso_common_init(struct hpsb_host *host, enum hpsb_iso_type type,
+					     unsigned int data_buf_size,
+					     unsigned int buf_packets,
+					     int channel,
+					     int irq_interval,
+					     void (*callback)(struct hpsb_iso*))
+{
+	struct hpsb_iso *iso;
+	int dma_direction;
+
+	/* make sure driver supports the ISO API */
+	if(!host->driver->isoctl) {
+		printk(KERN_INFO "ieee1394: host driver '%s' does not support the rawiso API\n",
+		       host->driver->name);
+		return NULL;
+	}
+
+	/* sanitize parameters */
+
+	if(buf_packets < 2)
+		buf_packets = 2;
+
+	if(irq_interval < 1 || irq_interval > buf_packets / 2)
+		irq_interval = buf_packets / 2;
+
+	if(channel < -1 || channel >= 64)
+		return NULL;
+
+	/* channel = -1 is OK for multi-channel recv but not for xmit */
+	if(type == HPSB_ISO_XMIT && channel < 0)
+		return NULL;
+
+	/* allocate and write the struct hpsb_iso */
+
+	iso = kmalloc(sizeof(*iso), SLAB_KERNEL);
+	if(!iso)
+		return NULL;
+
+	/* allocate ringbuffer of packet descriptors */
+	iso->infos = kmalloc(buf_packets * sizeof(struct hpsb_iso_packet_info), SLAB_KERNEL);
+	if(!iso->infos)
+		return NULL;
+
+	iso->type = type;
+	iso->host = host;
+	iso->hostdata = NULL;
+	iso->callback = callback;
+	init_waitqueue_head(&iso->waitq);
+	iso->channel = channel;
+	iso->irq_interval = irq_interval;
+	dma_region_init(&iso->data_buf);
+	iso->buf_size = round_up_to_page(data_buf_size);
+	iso->buf_packets = buf_packets;
+	iso->pkt_dma = 0;
+	iso->first_packet = 0;
+	spin_lock_init(&iso->lock);
+
+	if(iso->type == HPSB_ISO_XMIT) {
+		iso->n_ready_packets = iso->buf_packets;
+		dma_direction = PCI_DMA_TODEVICE;
+	} else {
+		iso->n_ready_packets = 0;
+		dma_direction = PCI_DMA_FROMDEVICE;
+	}
+
+	atomic_set(&iso->overflows, 0);
+	iso->flags = 0;
+	iso->prebuffer = 0;
+
+	/* allocate the packet buffer */
+	if(dma_region_alloc(&iso->data_buf, iso->buf_size, host->pdev, dma_direction))
+		goto err;
+
+	return iso;
+
+err:
+	hpsb_iso_shutdown(iso);
+	return NULL;
+}
+
+int hpsb_iso_n_ready(struct hpsb_iso* iso)
+{
+	unsigned long flags;
+	int val;
+
+	spin_lock_irqsave(&iso->lock, flags);
+	val = iso->n_ready_packets;
+	spin_unlock_irqrestore(&iso->lock, flags);
+
+	return val;
+}
+
+
+struct hpsb_iso* hpsb_iso_xmit_init(struct hpsb_host *host,
+				    unsigned int data_buf_size,
+				    unsigned int buf_packets,
+				    int channel,
+				    int speed,
+				    int irq_interval,
+				    void (*callback)(struct hpsb_iso*))
+{
+	struct hpsb_iso *iso = hpsb_iso_common_init(host, HPSB_ISO_XMIT,
+						    data_buf_size, buf_packets,
+						    channel, irq_interval, callback);
+	if(!iso)
+		return NULL;
+
+	iso->speed = speed;
+
+	/* tell the driver to start working */
+	if(host->driver->isoctl(iso, XMIT_INIT, 0))
+		goto err;
+
+	iso->flags |= HPSB_ISO_DRIVER_INIT;
+	return iso;
+
+err:
+	hpsb_iso_shutdown(iso);
+	return NULL;
+}
+
+struct hpsb_iso* hpsb_iso_recv_init(struct hpsb_host *host,
+				    unsigned int data_buf_size,
+				    unsigned int buf_packets,
+				    int channel,
+				    int irq_interval,
+				    void (*callback)(struct hpsb_iso*))
+{
+	struct hpsb_iso *iso = hpsb_iso_common_init(host, HPSB_ISO_RECV,
+						    data_buf_size, buf_packets,
+						    channel, irq_interval, callback);
+	if(!iso)
+		return NULL;
+
+	/* tell the driver to start working */
+	if(host->driver->isoctl(iso, RECV_INIT, 0))
+		goto err;
+
+	iso->flags |= HPSB_ISO_DRIVER_INIT;
+	return iso;
+
+err:
+	hpsb_iso_shutdown(iso);
+	return NULL;
+}
+
+int hpsb_iso_recv_listen_channel(struct hpsb_iso *iso, unsigned char channel)
+{
+	if(iso->type != HPSB_ISO_RECV || iso->channel != -1 || channel >= 64)
+		return -EINVAL;
+	return iso->host->driver->isoctl(iso, RECV_LISTEN_CHANNEL, channel);
+}
+
+int hpsb_iso_recv_unlisten_channel(struct hpsb_iso *iso, unsigned char channel)
+{
+       if(iso->type != HPSB_ISO_RECV || iso->channel != -1 || channel >= 64)
+               return -EINVAL;
+       return iso->host->driver->isoctl(iso, RECV_UNLISTEN_CHANNEL, channel);
+}
+
+int hpsb_iso_recv_set_channel_mask(struct hpsb_iso *iso, u64 mask)
+{
+	if(iso->type != HPSB_ISO_RECV || iso->channel != -1)
+		return -EINVAL;
+	return iso->host->driver->isoctl(iso, RECV_SET_CHANNEL_MASK, (unsigned long) &mask);
+}
+
+static int do_iso_xmit_start(struct hpsb_iso *iso, int cycle)
+{
+	int retval = iso->host->driver->isoctl(iso, XMIT_START, cycle);
+	if(retval)
+		return retval;
+
+	iso->flags |= HPSB_ISO_DRIVER_STARTED;
+	return retval;
+}
+
+int hpsb_iso_xmit_start(struct hpsb_iso *iso, int cycle, int prebuffer)
+{
+	if(iso->type != HPSB_ISO_XMIT)
+		return -1;
+
+	if(iso->flags & HPSB_ISO_DRIVER_STARTED)
+		return 0;
+
+	if(cycle < -1)
+		cycle = -1;
+	else if(cycle >= 8000)
+		cycle %= 8000;
+
+	iso->xmit_cycle = cycle;
+
+	if(prebuffer < 0)
+		prebuffer = iso->buf_packets;
+	else if(prebuffer == 0)
+		prebuffer = 1;
+
+	if(prebuffer > iso->buf_packets)
+		prebuffer = iso->buf_packets;
+
+	iso->prebuffer = prebuffer;
+
+	/* remember the starting cycle; DMA will commence from xmit_queue_packets()
+	   once enough packets have been buffered */
+	iso->start_cycle = cycle;
+
+	return 0;
+}
+
+int hpsb_iso_recv_start(struct hpsb_iso *iso, int cycle, int tag_mask, int sync)
+{
+	int retval = 0;
+	int isoctl_args[3];
+
+	if(iso->type != HPSB_ISO_RECV)
+		return -1;
+
+	if(iso->flags & HPSB_ISO_DRIVER_STARTED)
+		return 0;
+
+	if(cycle < -1)
+		cycle = -1;
+	else if(cycle >= 8000)
+		cycle %= 8000;
+
+	isoctl_args[0] = cycle;
+	
+	if(tag_mask < 0)
+		/* match all tags */
+		tag_mask = 0xF;
+	isoctl_args[1] = tag_mask;
+
+	isoctl_args[2] = sync;
+
+	retval = iso->host->driver->isoctl(iso, RECV_START, (unsigned long) &isoctl_args[0]);
+	if(retval)
+		return retval;
+
+	iso->flags |= HPSB_ISO_DRIVER_STARTED;
+	return retval;
+}
+
+/* check to make sure the user has not supplied bogus values of offset/len
+   that would cause the kernel to access memory outside the buffer */
+
+static int hpsb_iso_check_offset_len(struct hpsb_iso *iso,
+				     unsigned int offset, unsigned short len,
+				     unsigned int *out_offset, unsigned short *out_len)
+{
+	if(offset >= iso->buf_size)
+		return -EFAULT;
+
+	/* make sure the packet does not go beyond the end of the buffer */
+	if(offset + len > iso->buf_size)
+		return -EFAULT;
+
+	/* check for wrap-around */
+	if(offset + len < offset)
+		return -EFAULT;
+
+	/* now we can trust 'offset' and 'length' */
+	*out_offset = offset;
+	*out_len = len;
+
+	return 0;
+}
+
+
+int hpsb_iso_xmit_queue_packet(struct hpsb_iso *iso, u32 offset, u16 len, u8 tag, u8 sy)
+{
+	struct hpsb_iso_packet_info *info;
+	unsigned long flags;
+	int rv;
+
+	if(iso->type != HPSB_ISO_XMIT)
+		return -EINVAL;
+
+	/* is there space in the buffer? */
+	if(iso->n_ready_packets <= 0) {
+		return -EBUSY;
+	}
+
+	info = &iso->infos[iso->first_packet];
+
+	/* check for bogus offset/length */
+	if(hpsb_iso_check_offset_len(iso, offset, len, &info->offset, &info->len))
+		return -EFAULT;
+
+	info->tag = tag;
+	info->sy = sy;
+
+	spin_lock_irqsave(&iso->lock, flags);
+
+	rv = iso->host->driver->isoctl(iso, XMIT_QUEUE, (unsigned long) info);
+	if(rv)
+		goto out;
+
+	/* increment cursors */
+	iso->first_packet = (iso->first_packet+1) % iso->buf_packets;
+	iso->xmit_cycle = (iso->xmit_cycle+1) % 8000;
+	iso->n_ready_packets--;
+
+	if(iso->prebuffer != 0) {
+		iso->prebuffer--;
+		if(iso->prebuffer <= 0) {
+			iso->prebuffer = 0;
+			rv = do_iso_xmit_start(iso, iso->start_cycle);
+		}
+	}
+
+out:	
+	spin_unlock_irqrestore(&iso->lock, flags);
+	return rv;
+}
+
+int hpsb_iso_xmit_sync(struct hpsb_iso *iso)
+{
+	if(iso->type != HPSB_ISO_XMIT)
+		return -EINVAL;
+
+	return wait_event_interruptible(iso->waitq, hpsb_iso_n_ready(iso) == iso->buf_packets);
+}
+
+void hpsb_iso_packet_sent(struct hpsb_iso *iso, int cycle, int error)
+{
+	unsigned long flags;
+	spin_lock_irqsave(&iso->lock, flags);
+
+	/* predict the cycle of the next packet to be queued */
+
+	/* jump ahead by the number of packets that are already buffered */
+	cycle += iso->buf_packets - iso->n_ready_packets;
+	cycle %= 8000;
+
+	iso->xmit_cycle = cycle;
+	iso->n_ready_packets++;
+	iso->pkt_dma = (iso->pkt_dma + 1) % iso->buf_packets;
+
+	if(iso->n_ready_packets == iso->buf_packets || error != 0) {
+		/* the buffer has run empty! */
+		atomic_inc(&iso->overflows);
+	}
+
+	spin_unlock_irqrestore(&iso->lock, flags);
+}
+
+void hpsb_iso_packet_received(struct hpsb_iso *iso, u32 offset, u16 len,
+			      u16 cycle, u8 channel, u8 tag, u8 sy)
+{
+	unsigned long flags;
+	spin_lock_irqsave(&iso->lock, flags);
+
+	if(iso->n_ready_packets == iso->buf_packets) {
+		/* overflow! */
+		atomic_inc(&iso->overflows);
+	} else {
+		struct hpsb_iso_packet_info *info = &iso->infos[iso->pkt_dma];
+		info->offset = offset;
+		info->len = len;
+		info->cycle = cycle;
+		info->channel = channel;
+		info->tag = tag;
+		info->sy = sy;
+
+		iso->pkt_dma = (iso->pkt_dma+1) % iso->buf_packets;
+		iso->n_ready_packets++;
+	}
+
+	spin_unlock_irqrestore(&iso->lock, flags);
+}
+
+int hpsb_iso_recv_release_packets(struct hpsb_iso *iso, unsigned int n_packets)
+{
+	unsigned long flags;
+	unsigned int i;
+	int rv = 0;
+
+	if(iso->type != HPSB_ISO_RECV)
+		return -1;
+
+	spin_lock_irqsave(&iso->lock, flags);
+	for(i = 0; i < n_packets; i++) {
+		rv = iso->host->driver->isoctl(iso, RECV_RELEASE,
+					       (unsigned long) &iso->infos[iso->first_packet]);
+		if(rv)
+			break;
+
+		iso->first_packet = (iso->first_packet+1) % iso->buf_packets;
+		iso->n_ready_packets--;
+	}
+	spin_unlock_irqrestore(&iso->lock, flags);
+	return rv;
+}
+
+void hpsb_iso_wake(struct hpsb_iso *iso)
+{
+	wake_up_interruptible(&iso->waitq);
+
+	if(iso->callback)
+		iso->callback(iso);
+}
diff -urN linux-2.4.21-pre5/drivers/ieee1394/iso.h linux-2.4.21-pre6/drivers/ieee1394/iso.h
--- linux-2.4.21-pre5/drivers/ieee1394/iso.h	Wed Dec 31 16:00:00 1969
+++ linux-2.4.21-pre6/drivers/ieee1394/iso.h	Wed Mar 26 16:11:36 2003
@@ -0,0 +1,184 @@
+/*
+ * IEEE 1394 for Linux
+ *
+ * kernel ISO transmission/reception
+ *
+ * Copyright (C) 2002 Maas Digital LLC
+ *
+ * This code is licensed under the GPL.  See the file COPYING in the root
+ * directory of the kernel sources for details.
+ */
+
+#ifndef IEEE1394_ISO_H
+#define IEEE1394_ISO_H
+
+#include "hosts.h"
+#include "dma.h"
+
+/* high-level ISO interface */
+
+/* This API sends and receives isochronous packets on a large,
+   virtually-contiguous kernel memory buffer. The buffer may be mapped
+   into a user-space process for zero-copy transmission and reception.
+
+   There are no explicit boundaries between packets in the buffer. A
+   packet may be transmitted or received at any location. However,
+   low-level drivers may impose certain restrictions on alignment or
+   size of packets. (e.g. in OHCI no packet may cross a page boundary,
+   and packets should be quadlet-aligned)
+*/
+
+/* Packet descriptor - the API maintains a ring buffer of these packet
+   descriptors in kernel memory (hpsb_iso.infos[]).  */
+
+struct hpsb_iso_packet_info {
+	/* offset of data payload relative to the first byte of the buffer */
+	__u32 offset;
+
+	/* length of the data payload, in bytes (not including the isochronous header) */
+	__u16 len;
+
+	/* (recv only) the cycle number (mod 8000) on which the packet was received */
+	__u16 cycle;
+
+	/* (recv only) channel on which the packet was received */
+	__u8 channel;
+
+	/* 2-bit 'tag' and 4-bit 'sy' fields of the isochronous header */
+	__u8 tag;
+	__u8 sy;
+};
+
+enum hpsb_iso_type { HPSB_ISO_RECV = 0, HPSB_ISO_XMIT = 1 };
+
+struct hpsb_iso {
+	enum hpsb_iso_type type;
+
+	/* pointer to low-level driver and its private data */
+	struct hpsb_host *host;
+	void *hostdata;
+
+	/* a function to be called (from interrupt context) after
+           outgoing packets have been sent, or incoming packets have
+           arrived */
+	void (*callback)(struct hpsb_iso*);
+
+	/* wait for buffer space */
+	wait_queue_head_t waitq;
+
+	int speed; /* SPEED_100, 200, or 400 */
+	int channel; /* -1 if multichannel */
+
+	/* greatest # of packets between interrupts - controls
+	   the maximum latency of the buffer */
+	int irq_interval;
+
+	/* the buffer for packet data payloads */
+	struct dma_region data_buf;
+
+	/* size of data_buf, in bytes (always a multiple of PAGE_SIZE) */
+	unsigned int buf_size;
+
+	/* ringbuffer of packet descriptors in regular kernel memory */
+	struct hpsb_iso_packet_info *infos;
+
+	/* # of packets in the ringbuffer */
+	unsigned int buf_packets;
+
+	/* protects packet cursors */
+	spinlock_t lock;
+
+	/* the index of the next packet that will be produced
+	   or consumed by the user */
+	int first_packet;
+
+	/* the index of the next packet that will be transmitted
+	   or received by the 1394 hardware */
+	int pkt_dma;
+
+	/* how many packets, starting at first_packet:
+	   (transmit) are ready to be filled with data
+	   (receive)  contain received data */
+	int n_ready_packets;
+
+	/* how many times the buffer has overflowed or underflowed */
+	atomic_t overflows;
+
+	/* private flags to track initialization progress */
+#define HPSB_ISO_DRIVER_INIT     (1<<0)
+#define HPSB_ISO_DRIVER_STARTED  (1<<1)
+	unsigned int flags;
+
+	/* # of packets left to prebuffer (xmit only) */
+	int prebuffer;
+
+	/* starting cycle for DMA (xmit only) */
+	int start_cycle;
+
+	/* cycle at which next packet will be transmitted,
+	   -1 if not known */
+	int xmit_cycle;
+};
+
+/* functions available to high-level drivers (e.g. raw1394) */
+
+/* allocate the buffer and DMA context */
+
+struct hpsb_iso* hpsb_iso_xmit_init(struct hpsb_host *host,
+				    unsigned int data_buf_size,
+				    unsigned int buf_packets,
+				    int channel,
+				    int speed,
+				    int irq_interval,
+				    void (*callback)(struct hpsb_iso*));
+
+/* note: if channel = -1, multi-channel receive is enabled */
+struct hpsb_iso* hpsb_iso_recv_init(struct hpsb_host *host,
+				    unsigned int data_buf_size,
+				    unsigned int buf_packets,
+				    int channel,
+				    int irq_interval,
+				    void (*callback)(struct hpsb_iso*));
+
+/* multi-channel only */
+int hpsb_iso_recv_listen_channel(struct hpsb_iso *iso, unsigned char channel);
+int hpsb_iso_recv_unlisten_channel(struct hpsb_iso *iso, unsigned char channel);
+int hpsb_iso_recv_set_channel_mask(struct hpsb_iso *iso, u64 mask);
+
+/* start/stop DMA */
+int hpsb_iso_xmit_start(struct hpsb_iso *iso, int start_on_cycle, int prebuffer);
+int hpsb_iso_recv_start(struct hpsb_iso *iso, int start_on_cycle, int tag_mask, int sync);
+void hpsb_iso_stop(struct hpsb_iso *iso);
+
+/* deallocate buffer and DMA context */
+void hpsb_iso_shutdown(struct hpsb_iso *iso);
+
+/* queue a packet for transmission. 'offset' is relative to the beginning of the
+   DMA buffer, where the packet's data payload should already have been placed */
+int hpsb_iso_xmit_queue_packet(struct hpsb_iso *iso, u32 offset, u16 len, u8 tag, u8 sy);
+
+/* wait until all queued packets have been transmitted to the bus */
+int hpsb_iso_xmit_sync(struct hpsb_iso *iso);
+
+/* N packets have been read out of the buffer, re-use the buffer space */
+int  hpsb_iso_recv_release_packets(struct hpsb_iso *recv, unsigned int n_packets);
+
+/* returns # of packets ready to send or receive */
+int hpsb_iso_n_ready(struct hpsb_iso *iso);
+
+/* the following are callbacks available to low-level drivers */
+
+/* call after a packet has been transmitted to the bus (interrupt context is OK)
+   'cycle' is the _exact_ cycle the packet was sent on
+   'error' should be non-zero if some sort of error occurred when sending the packet
+*/
+void hpsb_iso_packet_sent(struct hpsb_iso *iso, int cycle, int error);
+
+/* call after a packet has been received (interrupt context OK) */
+void hpsb_iso_packet_received(struct hpsb_iso *iso, u32 offset, u16 len,
+			      u16 cycle, u8 channel, u8 tag, u8 sy);
+
+/* call to wake waiting processes after buffer space has opened up. */
+void hpsb_iso_wake(struct hpsb_iso *iso);
+
+#endif /* IEEE1394_ISO_H */
diff -urN linux-2.4.21-pre5/drivers/ieee1394/ohci1394.c linux-2.4.21-pre6/drivers/ieee1394/ohci1394.c
--- linux-2.4.21-pre5/drivers/ieee1394/ohci1394.c	Wed Mar 26 16:11:28 2003
+++ linux-2.4.21-pre6/drivers/ieee1394/ohci1394.c	Wed Mar 26 16:11:36 2003
@@ -26,12 +26,10 @@
  * . Async Request Receive
  * . Async Response Transmit
  * . Iso Receive
+ * . Iso Transmit
  * . DMA mmap for iso receive
  * . Config ROM generation
  *
- * Things implemented, but still in test phase:
- * . Iso Transmit
- * 
  * Things not implemented:
  * . Async Stream Packets
  * . DMA error recovery
@@ -75,6 +73,9 @@
  *  . Updated to 2.4.x module scheme (PCI aswell)
  *  . Removed procfs support since it trashes random mem
  *  . Config ROM generation
+ *
+ * Dan Maas <dmaas@maasdigital.com>
+ *  . New isochronous API (rawiso)
  */
 
 #include <linux/config.h>
@@ -112,6 +113,8 @@
 #include "ieee1394.h"
 #include "ieee1394_types.h"
 #include "hosts.h"
+#include "dma.h"
+#include "iso.h"
 #include "ieee1394_core.h"
 #include "highlevel.h"
 #include "ohci1394.h"
@@ -153,7 +156,7 @@
 printk(level "%s_%d: " fmt "\n" , OHCI1394_DRIVER_NAME, card , ## args)
 
 static char version[] __devinitdata =
-	"$Rev: 758 $ Ben Collins <bcollins@debian.org>";
+	"$Rev: 810 $ Ben Collins <bcollins@debian.org>";
 
 /* Module Parameters */
 MODULE_PARM(attempt_root,"i");
@@ -167,15 +170,18 @@
 static void dma_trm_tasklet(unsigned long data);
 static void dma_trm_reset(struct dma_trm_ctx *d);
 
+static int alloc_dma_rcv_ctx(struct ti_ohci *ohci, struct dma_rcv_ctx *d,
+			     enum context_type type, int ctx, int num_desc,
+			     int buf_size, int split_buf_size, int context_base);
+static void free_dma_rcv_ctx(struct dma_rcv_ctx *d);
+
+static int alloc_dma_trm_ctx(struct ti_ohci *ohci, struct dma_trm_ctx *d,
+			     enum context_type type, int ctx, int num_desc,
+			     int context_base);
+
 static void ohci1394_pci_remove(struct pci_dev *pdev);
 
 #ifndef __LITTLE_ENDIAN
-/* Swap a series of quads inplace. */
-static __inline__ void block_swab32(quadlet_t *data, size_t size) {
-	while (size--)
-		data[size] = swab32(data[size]);
-}
-
 static unsigned hdr_sizes[] = 
 {
 	3,	/* TCODE_WRITEQ */
@@ -193,16 +199,19 @@
 };
 
 /* Swap headers */
-static inline void packet_swab(quadlet_t *data, int tcode, int len)
+static inline void packet_swab(quadlet_t *data, int tcode)
 {
+	size_t size = hdr_sizes[tcode];
+
 	if (tcode > TCODE_LOCK_RESPONSE || hdr_sizes[tcode] == 0)
 		return;
-	block_swab32(data, hdr_sizes[tcode]);
+
+	while (size--)
+		data[size] = swab32(data[size]);
 }
 #else
 /* Don't waste cycles on same sex byte swaps */
-#define packet_swab(w,x,y)
-#define block_swab32(x,y)
+#define packet_swab(w,x)
 #endif /* !LITTLE_ENDIAN */
 
 /***********************************
@@ -311,7 +320,7 @@
 
 	/* SelfID Ok, reset error counter. */
 	ohci->self_id_errors = 0;
-	
+
 	size = ((self_id_count & 0x00001FFC) >> 2) - 1;
 	q++;
 
@@ -375,12 +384,12 @@
 		      "Running dma failed because Node ID == 63");
 		return -1;
 	}
-	
+
 	/* Run the dma context */
 	reg_write(ohci, reg, 0x8000);
-	
+
 	if (msg) PRINT(KERN_DEBUG, ohci->id, "%s", msg);
-	
+
 	return 0;
 }
 
@@ -394,11 +403,11 @@
 
 	for (i=0; i<d->num_desc; i++) {
 		u32 c;
-		
+
 		c = DMA_CTL_INPUT_MORE | DMA_CTL_UPDATE | DMA_CTL_BRANCH;
 		if (generate_irq)
 			c |= DMA_CTL_IRQ;
-				
+
 		d->prg_cpu[i]->control = cpu_to_le32(c | d->buf_size);
 
 		/* End of descriptor list? */
@@ -417,10 +426,28 @@
         d->buf_ind = 0;
         d->buf_offset = 0;
 
+	if(d->type == DMA_CTX_ISO) {
+		/* Clear contextControl */
+		reg_write(ohci, d->ctrlClear, 0xffffffff);
+
+		/* Set bufferFill, isochHeader, multichannel for IR context */
+		reg_write(ohci, d->ctrlSet, 0xd0000000);
+			
+		/* Set the context match register to match on all tags */
+		reg_write(ohci, d->ctxtMatch, 0xf0000000);
+
+		/* Clear the multi channel mask high and low registers */
+		reg_write(ohci, OHCI1394_IRMultiChanMaskHiClear, 0xffffffff);
+		reg_write(ohci, OHCI1394_IRMultiChanMaskLoClear, 0xffffffff);
+
+		/* Set up isoRecvIntMask to generate interrupts */
+		reg_write(ohci, OHCI1394_IsoRecvIntMaskSet, 1 << d->ctx);
+	}
+
 	/* Tell the controller where the first AR program is */
 	reg_write(ohci, d->cmdPtr, d->prg_bus[0] | 0x1);
 
-	/* Run AR context */
+	/* Run context */
 	reg_write(ohci, d->ctrlSet, 0x00008000);
 
 	DBGMSG(ohci->id, "Receive DMA ctx=%d initialized", d->ctx);
@@ -441,6 +468,11 @@
 	INIT_LIST_HEAD(&d->fifo_list);
 	INIT_LIST_HEAD(&d->pending_list);
 
+	if(d->type == DMA_CTX_ISO) {
+		/* enable interrupts */
+		reg_write(ohci, OHCI1394_IsoXmitIntMaskSet, 1 << d->ctx);
+	}
+
 	DBGMSG(ohci->id, "Transmit DMA ctx=%d initialized", d->ctx);
 }
 
@@ -452,7 +484,7 @@
 
 	reg_write(ohci, reg, 0xffffffff);
 	tmp = reg_read(ohci, reg);
-	
+
 	DBGMSG(ohci->id,"Iso contexts reg: %08x implemented: %08x", reg, tmp);
 
 	/* Count the number of contexts */
@@ -517,12 +549,6 @@
 	/* Don't accept phy packets into AR request context */ 
 	reg_write(ohci, OHCI1394_LinkControlClear, 0x00000400);
 
-	/* Set bufferFill, isochHeader, multichannel for IR context */
-	reg_write(ohci, OHCI1394_IsoRcvContextControlSet, 0xd0000000);
-			
-	/* Set the context match register to match on all tags */
-	reg_write(ohci, OHCI1394_IsoRcvContextMatch, 0xf0000000);
-
 	/* Clear the interrupt mask */
 	reg_write(ohci, OHCI1394_IsoRecvIntMaskClear, 0xffffffff);
 	reg_write(ohci, OHCI1394_IsoRecvIntEventClear, 0xffffffff);
@@ -531,10 +557,6 @@
 	reg_write(ohci, OHCI1394_IsoXmitIntMaskClear, 0xffffffff);
 	reg_write(ohci, OHCI1394_IsoXmitIntEventClear, 0xffffffff);
 
-	/* Clear the multi channel mask high and low registers */
-	reg_write(ohci, OHCI1394_IRMultiChanMaskHiClear, 0xffffffff);
-	reg_write(ohci, OHCI1394_IRMultiChanMaskLoClear, 0xffffffff);
-
 	/* Initialize AR dma */
 	initialize_dma_rcv_ctx(&ohci->ar_req_context, 0);
 	initialize_dma_rcv_ctx(&ohci->ar_resp_context, 0);
@@ -543,19 +565,6 @@
 	initialize_dma_trm_ctx(&ohci->at_req_context);
 	initialize_dma_trm_ctx(&ohci->at_resp_context);
 
-	/* Initialize IR dma */
-	initialize_dma_rcv_ctx(&ohci->ir_context, 1);
-
-        /* Initialize IT dma */
-        initialize_dma_trm_ctx(&ohci->it_context);
-
-	/* Set up isoRecvIntMask to generate interrupts for context 0
-	   (thanks to Michael Greger for seeing that I forgot this) */
-	reg_write(ohci, OHCI1394_IsoRecvIntMaskSet, 0x00000001);
-
-	/* Set up isoXmitIntMask to generate interrupts for context 0 */
-	reg_write(ohci, OHCI1394_IsoXmitIntMaskSet, 0x00000001);
-
 	/* 
 	 * Accept AT requests from all nodes. This probably 
 	 * will have to be controlled from the subsystem
@@ -573,7 +582,8 @@
 	reg_write(ohci, OHCI1394_HCControlClear, 0x40000000);
 
 	/* Enable interrupts */
-	reg_write(ohci, OHCI1394_IntMaskSet, 
+	reg_write(ohci, OHCI1394_IntMaskSet,
+		  OHCI1394_unrecoverableError |
 		  OHCI1394_masterIntEnable | 
 		  OHCI1394_busReset | 
 		  OHCI1394_selfIDComplete |
@@ -599,7 +609,7 @@
 }
 
 /* 
- * Insert a packet in the AT DMA fifo and generate the DMA prg
+ * Insert a packet in the DMA fifo and generate the DMA prg
  * FIXME: rewrite the program in order to accept packets crossing
  *        page boundaries.
  *        check also that a single dma descriptor doesn't cross a 
@@ -641,19 +651,31 @@
                 } else {
                         d->prg_cpu[idx]->data[0] = packet->speed_code<<16 |
                                 (packet->header[0] & 0xFFFF);
-                        d->prg_cpu[idx]->data[1] =
-                                (packet->header[1] & 0xFFFF) | 
-                                (packet->header[0] & 0xFFFF0000);
-                        d->prg_cpu[idx]->data[2] = packet->header[2];
-                        d->prg_cpu[idx]->data[3] = packet->header[3];
-			packet_swab(d->prg_cpu[idx]->data, packet->tcode,
-					packet->header_size>>2);
+
+			if (packet->tcode == TCODE_ISO_DATA) {
+				/* Sending an async stream packet */
+				d->prg_cpu[idx]->data[1] = packet->header[0] & 0xFFFF0000;
+			} else {
+				/* Sending a normal async request or response */
+				d->prg_cpu[idx]->data[1] =
+					(packet->header[1] & 0xFFFF) | 
+					(packet->header[0] & 0xFFFF0000);
+				d->prg_cpu[idx]->data[2] = packet->header[2];
+				d->prg_cpu[idx]->data[3] = packet->header[3];
+			}
+			packet_swab(d->prg_cpu[idx]->data, packet->tcode);
                 }
 
                 if (packet->data_size) { /* block transmit */
-                        d->prg_cpu[idx]->begin.control =
-                                cpu_to_le32(DMA_CTL_OUTPUT_MORE |
-					    DMA_CTL_IMMEDIATE | 0x10);
+			if (packet->tcode == TCODE_STREAM_DATA){
+				d->prg_cpu[idx]->begin.control =
+					cpu_to_le32(DMA_CTL_OUTPUT_MORE |
+						    DMA_CTL_IMMEDIATE | 0x8);
+			} else {
+				d->prg_cpu[idx]->begin.control =
+					cpu_to_le32(DMA_CTL_OUTPUT_MORE |
+						    DMA_CTL_IMMEDIATE | 0x10);
+			}
                         d->prg_cpu[idx]->end.control =
                                 cpu_to_le32(DMA_CTL_OUTPUT_LAST |
 					    DMA_CTL_IRQ | 
@@ -712,7 +734,7 @@
                 d->prg_cpu[idx]->data[0] = packet->speed_code<<16 |
                         (packet->header[0] & 0xFFFF);
                 d->prg_cpu[idx]->data[1] = packet->header[0] & 0xFFFF0000;
-		packet_swab(d->prg_cpu[idx]->data, packet->tcode, packet->header_size>>2);
+		packet_swab(d->prg_cpu[idx]->data, packet->tcode);
   
                 d->prg_cpu[idx]->begin.control = 
 			cpu_to_le32(DMA_CTL_OUTPUT_MORE | 
@@ -759,8 +781,8 @@
 }
 
 /*
- * This function fills the AT FIFO with the (eventual) pending packets
- * and runs or wakes up the AT DMA prg if necessary.
+ * This function fills the FIFO with the (eventual) pending packets
+ * and runs or wakes up the DMA prg if necessary.
  *
  * The function MUST be called with the d->lock held.
  */ 
@@ -776,7 +798,7 @@
 	idx = d->prg_ind;
 	z = (p->data_size) ? 3 : 2;
 
-	/* insert the packets into the at dma fifo */
+	/* insert the packets into the dma fifo */
 	while (d->free_prgs > 0 && !list_empty(&d->pending_list)) {
 		struct hpsb_packet *p = driver_packet(d->pending_list.next);
 		list_del(&p->driver_list);
@@ -798,13 +820,15 @@
 		/* Wake up the dma context if necessary */
 		if (!(reg_read(ohci, d->ctrlSet) & 0x400)) {
 			DBGMSG(ohci->id,"Waking transmit DMA ctx=%d",d->ctx);
-			reg_write(ohci, d->ctrlSet, 0x1000);
 		}
+
+		/* do this always, to avoid race condition */
+		reg_write(ohci, d->ctrlSet, 0x1000);
 	}
 	return 1;
 }
 
-/* Transmission of an async packet */
+/* Transmission of an async or iso packet */
 static int ohci_transmit(struct hpsb_host *host, struct hpsb_packet *packet)
 {
 	struct ti_ohci *ohci = host->hostdata;
@@ -818,12 +842,36 @@
 		return 0;
 	}
 
-	/* Decide wether we have an iso, a request, or a response packet */
+	/* Decide whether we have an iso, a request, or a response packet */
 	if (packet->type == hpsb_raw)
 		d = &ohci->at_req_context;
-	else if (packet->tcode == TCODE_ISO_DATA)
-		d = &ohci->it_context;
-	else if (packet->tcode & 0x02)
+	else if ((packet->tcode == TCODE_ISO_DATA) && (packet->type == hpsb_iso)) {
+		/* The legacy IT DMA context is initialized on first
+		 * use.  However, the alloc cannot be run from
+		 * interrupt context, so we bail out if that is the
+		 * case. I don't see anyone sending ISO packets from
+		 * interrupt context anyway... */
+		
+		if(ohci->it_legacy_context.ohci == NULL) {
+			if(in_interrupt()) {
+				PRINT(KERN_ERR, ohci->id, 
+				      "legacy IT context cannot be initialized during interrupt");
+				return 0;
+			}
+
+			if (alloc_dma_trm_ctx(ohci, &ohci->it_legacy_context,
+					      DMA_CTX_ISO, 0, IT_NUM_DESC,
+					      OHCI1394_IsoXmitContextBase) < 0) {
+				PRINT(KERN_ERR, ohci->id, 
+				      "error initializing legacy IT context");
+				return 0;
+			}
+
+			initialize_dma_trm_ctx(&ohci->it_legacy_context);
+		}
+		
+		d = &ohci->it_legacy_context;
+	} else if ((packet->tcode & 0x02) && (packet->tcode != TCODE_ISO_DATA))
 		d = &ohci->at_resp_context;
 	else 
 		d = &ohci->at_req_context;
@@ -831,7 +879,7 @@
 	spin_lock_irqsave(&d->lock,flags);
 
 	list_add_tail(&packet->driver_list, &d->pending_list);
-	
+
 	dma_trm_flush(ohci, d);
 
 	spin_unlock_irqrestore(&d->lock,flags);
@@ -855,11 +903,11 @@
 	case GET_CYCLE_COUNTER:
 		retval = reg_read(ohci, OHCI1394_IsochronousCycleTimer);
 		break;
-	
+
 	case SET_CYCLE_COUNTER:
 		reg_write(ohci, OHCI1394_IsochronousCycleTimer, arg);
 		break;
-	
+
 	case SET_BUS_ID:
 		PRINT(KERN_ERR, ohci->id, "devctl command SET_BUS_ID err");
 		break;
@@ -919,8 +967,26 @@
 			spin_unlock_irqrestore(&ohci->IR_channel_lock, flags);
 			return -EFAULT;
 		}
-		
+
+		/* activate the legacy IR context */
+		if(ohci->ir_legacy_context.ohci == NULL) {
+			if(alloc_dma_rcv_ctx(ohci, &ohci->ir_legacy_context,
+					     DMA_CTX_ISO, 0, IR_NUM_DESC,
+					     IR_BUF_SIZE, IR_SPLIT_BUF_SIZE,
+					     OHCI1394_IsoRcvContextBase) < 0) {
+				PRINT(KERN_ERR, ohci->id,
+				      "%s: failed to allocate an IR context", 
+				      __FUNCTION__);
+				return -ENOMEM;
+			}
+			ohci->ir_legacy_channels = 0;
+			initialize_dma_rcv_ctx(&ohci->ir_legacy_context, 1);
+
+			DBGMSG(ohci->id, "ISO receive legacy context activated");
+		}
+
 		ohci->ISO_channel_usage |= mask;
+		ohci->ir_legacy_channels |= mask;
 
 		if (arg>31) 
 			reg_write(ohci, OHCI1394_IRMultiChanMaskHiSet, 
@@ -957,6 +1023,7 @@
 		}
 		
 		ohci->ISO_channel_usage &= ~mask;
+		ohci->ir_legacy_channels &= ~mask;
 
 		if (arg>31) 
 			reg_write(ohci, OHCI1394_IRMultiChanMaskHiClear, 
@@ -967,6 +1034,11 @@
 
                 spin_unlock_irqrestore(&ohci->IR_channel_lock, flags);
                 DBGMSG(ohci->id, "Listening disabled on channel %d", arg);
+
+		if(ohci->ir_legacy_channels == 0) {
+			free_dma_rcv_ctx(&ohci->ir_legacy_context);
+			DBGMSG(ohci->id, "ISO receive legacy context deactivated");
+		}
                 break;
         }
 	default:
@@ -977,6 +1049,1063 @@
 	return retval;
 }
 
+/***********************************
+ * rawiso ISO reception            *
+ ***********************************/
+
+/*
+  We use either buffer-fill or packet-per-buffer DMA mode. The DMA
+  buffer is split into "blocks" (regions described by one DMA
+  descriptor). Each block must be one page or less in size, and
+  must not cross a page boundary.
+
+  There is one little wrinkle with buffer-fill mode: a packet that
+  starts in the final block may wrap around into the first block. But
+  the user API expects all packets to be contiguous. Our solution is
+  to keep the very last page of the DMA buffer in reserve - if a
+  packet spans the gap, we copy its tail into this page.
+*/
+
+struct ohci_iso_recv {
+	struct ti_ohci *ohci;
+
+	struct ohci1394_iso_tasklet task;
+	int task_active;
+
+	enum { BUFFER_FILL_MODE,
+	       PACKET_PER_BUFFER_MODE } dma_mode;
+
+	/* memory and PCI mapping for the DMA descriptors */
+	struct dma_prog_region prog;
+	struct dma_cmd *block; /* = (struct dma_cmd*) prog.virt */
+
+	/* how many DMA blocks fit in the buffer */
+	unsigned int nblocks;
+
+	/* stride of DMA blocks */
+	unsigned int buf_stride;
+
+	/* number of blocks to batch between interrupts */
+	int block_irq_interval;
+
+	/* block that DMA will finish next */
+	int block_dma;
+
+	/* (buffer-fill only) block that the reader will release next */
+	int block_reader;
+
+	/* (buffer-fill only) bytes of buffer the reader has released,
+	   less than one block */
+	int released_bytes;
+
+	/* (buffer-fill only) buffer offset at which the next packet will appear */
+	int dma_offset;
+
+	/* OHCI DMA context control registers */
+	u32 ContextControlSet;
+	u32 ContextControlClear;
+	u32 CommandPtr;
+	u32 ContextMatch;
+};
+
+static void ohci_iso_recv_bufferfill_task(unsigned long data);
+static void ohci_iso_recv_packetperbuf_task(unsigned long data);
+static void ohci_iso_recv_stop(struct hpsb_iso *iso);
+static void ohci_iso_recv_shutdown(struct hpsb_iso *iso);
+static int  ohci_iso_recv_start(struct hpsb_iso *iso, int cycle, int tag_mask, int sync);
+static void ohci_iso_recv_program(struct hpsb_iso *iso);
+
+static int ohci_iso_recv_init(struct hpsb_iso *iso)
+{
+	struct ti_ohci *ohci = iso->host->hostdata;
+	struct ohci_iso_recv *recv;
+	int ctx;
+	int ret = -ENOMEM;
+
+	recv = kmalloc(sizeof(*recv), SLAB_KERNEL);
+	if(!recv)
+		return -ENOMEM;
+
+	iso->hostdata = recv;
+	recv->ohci = ohci;
+	recv->task_active = 0;
+	dma_prog_region_init(&recv->prog);
+	recv->block = NULL;
+
+	/* use buffer-fill mode, unless irq_interval is 1
+	   (note: multichannel requires buffer-fill) */
+
+	if(iso->irq_interval == 1 && iso->channel != -1) {
+		recv->dma_mode = PACKET_PER_BUFFER_MODE;
+	} else {
+		recv->dma_mode = BUFFER_FILL_MODE;
+	}
+
+	/* set nblocks, buf_stride, block_irq_interval */
+
+	if(recv->dma_mode == BUFFER_FILL_MODE) {
+		recv->buf_stride = PAGE_SIZE;
+
+		/* one block per page of data in the DMA buffer, minus the final guard page */
+		recv->nblocks = iso->buf_size/PAGE_SIZE - 1;
+		if(recv->nblocks < 3) {
+			DBGMSG(ohci->id, "ohci_iso_recv_init: DMA buffer too small");
+			goto err;
+		}
+
+		/* iso->irq_interval is in packets - translate that to blocks */
+		/* (err, sort of... 1 is always the safest value) */
+		recv->block_irq_interval = iso->irq_interval / recv->nblocks;
+		if(recv->block_irq_interval < 1)
+			recv->block_irq_interval = 1;
+		else if(recv->block_irq_interval*4 > recv->nblocks)
+			recv->block_irq_interval = recv->nblocks/4;
+	} else {
+		int max_packet_size;
+
+		recv->nblocks = iso->buf_packets;
+		recv->block_irq_interval = 1;
+
+		/* choose a buffer stride */
+		/* must be a power of 2, and <= PAGE_SIZE */
+
+		max_packet_size = iso->buf_size / iso->buf_packets;
+
+		for(recv->buf_stride = 8; recv->buf_stride < max_packet_size;
+		    recv->buf_stride *= 2);
+	
+		if(recv->buf_stride*iso->buf_packets > iso->buf_size ||
+		   recv->buf_stride > PAGE_SIZE) {
+			/* this shouldn't happen, but anyway... */
+			DBGMSG(ohci->id, "ohci_iso_recv_init: problem choosing a buffer stride");
+			goto err;
+		}
+	}
+
+	recv->block_reader = 0;
+	recv->released_bytes = 0;
+	recv->block_dma = 0;
+	recv->dma_offset = 0;
+
+	/* size of DMA program = one descriptor per block */
+	if (dma_prog_region_alloc(&recv->prog,
+				 sizeof(struct dma_cmd) * recv->nblocks,
+				 recv->ohci->dev))
+		goto err;
+
+	recv->block = (struct dma_cmd*) recv->prog.kvirt;
+
+	ohci1394_init_iso_tasklet(&recv->task,
+				  iso->channel == -1 ? OHCI_ISO_MULTICHANNEL_RECEIVE :
+				                       OHCI_ISO_RECEIVE,
+				  recv->dma_mode == BUFFER_FILL_MODE ?
+				  ohci_iso_recv_bufferfill_task :
+				  ohci_iso_recv_packetperbuf_task,
+				  (unsigned long) iso);
+
+	if(ohci1394_register_iso_tasklet(recv->ohci, &recv->task) < 0)
+		goto err;
+
+	recv->task_active = 1;
+
+	/* recv context registers are spaced 32 bytes apart */
+	ctx = recv->task.context;
+	recv->ContextControlSet = OHCI1394_IsoRcvContextControlSet + 32 * ctx;
+	recv->ContextControlClear = OHCI1394_IsoRcvContextControlClear + 32 * ctx;
+	recv->CommandPtr = OHCI1394_IsoRcvCommandPtr + 32 * ctx;
+	recv->ContextMatch = OHCI1394_IsoRcvContextMatch + 32 * ctx;
+
+	if(iso->channel == -1) {
+		/* clear multi-channel selection mask */
+		reg_write(recv->ohci, OHCI1394_IRMultiChanMaskHiClear, 0xFFFFFFFF);
+		reg_write(recv->ohci, OHCI1394_IRMultiChanMaskLoClear, 0xFFFFFFFF);
+	}
+		
+	/* write the DMA program */
+	ohci_iso_recv_program(iso);
+
+	DBGMSG(ohci->id, "ohci_iso_recv_init: %s mode, DMA buffer is %lu pages"
+	       " (%u bytes), using %u blocks, buf_stride %u, block_irq_interval %d",
+	       recv->dma_mode == BUFFER_FILL_MODE ?
+	       "buffer-fill" : "packet-per-buffer",
+	       iso->buf_size/PAGE_SIZE, iso->buf_size, 
+	       recv->nblocks, recv->buf_stride, recv->block_irq_interval);
+
+	return 0;
+
+err:
+	ohci_iso_recv_shutdown(iso);
+	return ret;
+}
+
+static void ohci_iso_recv_stop(struct hpsb_iso *iso)
+{
+	struct ohci_iso_recv *recv = iso->hostdata;
+
+	/* disable interrupts */
+	reg_write(recv->ohci, OHCI1394_IsoRecvIntMaskClear, 1 << recv->task.context);
+		
+	/* halt DMA */
+	ohci1394_stop_context(recv->ohci, recv->ContextControlClear, NULL);
+}
+
+static void ohci_iso_recv_shutdown(struct hpsb_iso *iso)
+{
+	struct ohci_iso_recv *recv = iso->hostdata;
+
+	if(recv->task_active) {
+		ohci_iso_recv_stop(iso);
+		ohci1394_unregister_iso_tasklet(recv->ohci, &recv->task);
+		recv->task_active = 0;
+	}
+
+	dma_prog_region_free(&recv->prog);
+	kfree(recv);
+	iso->hostdata = NULL;
+}
+
+/* set up a "gapped" ring buffer DMA program */
+static void ohci_iso_recv_program(struct hpsb_iso *iso)
+{
+	struct ohci_iso_recv *recv = iso->hostdata;
+	int blk;
+
+	/* address of 'branch' field in previous DMA descriptor */
+	u32 *prev_branch = NULL;
+
+	for (blk = 0; blk < recv->nblocks; blk++) {
+		u32 control;
+
+		/* the DMA descriptor */
+		struct dma_cmd *cmd = &recv->block[blk];
+
+		/* offset of the DMA descriptor relative to the DMA prog buffer */
+		unsigned long prog_offset = blk * sizeof(struct dma_cmd);
+
+		/* offset of this packet's data within the DMA buffer */
+		unsigned long buf_offset = blk * recv->buf_stride;
+
+		if (recv->dma_mode == BUFFER_FILL_MODE) {
+			control = 2 << 28; /* INPUT_MORE */
+		} else {
+			control = 3 << 28; /* INPUT_LAST */
+		}
+
+		control |= 8 << 24; /* s = 1, update xferStatus and resCount */
+
+		/* interrupt on last block, and at intervals */
+		if (blk == recv->nblocks-1 || (blk % recv->block_irq_interval) == 0) {
+			control |= 3 << 20; /* want interrupt */
+		}
+
+		control |= 3 << 18; /* enable branch to address */
+		control |= recv->buf_stride;
+
+		cmd->control = cpu_to_le32(control);
+		cmd->address = cpu_to_le32(dma_region_offset_to_bus(&iso->data_buf, buf_offset));
+		cmd->branchAddress = 0; /* filled in on next loop */
+		cmd->status = cpu_to_le32(recv->buf_stride);
+
+		/* link the previous descriptor to this one */
+		if (prev_branch) {
+			*prev_branch = cpu_to_le32(dma_prog_region_offset_to_bus(&recv->prog, prog_offset) | 1);
+		}
+
+		prev_branch = &cmd->branchAddress;
+	}
+
+	/* the final descriptor's branch address and Z should be left at 0 */
+}
+
+/* listen or unlisten to a specific channel (multi-channel mode only) */
+static void ohci_iso_recv_change_channel(struct hpsb_iso *iso, unsigned char channel, int listen)
+{
+	struct ohci_iso_recv *recv = iso->hostdata;
+	int reg, i;
+
+	if(channel < 32) {
+		reg = listen ? OHCI1394_IRMultiChanMaskLoSet : OHCI1394_IRMultiChanMaskLoClear;
+		i = channel;
+	} else {
+		reg = listen ? OHCI1394_IRMultiChanMaskHiSet : OHCI1394_IRMultiChanMaskHiClear;
+		i = channel - 32;
+	}
+
+	reg_write(recv->ohci, reg, (1 << i));
+
+	/* issue a dummy read to force all PCI writes to be posted immediately */
+	mb();
+	reg_read(recv->ohci, OHCI1394_IsochronousCycleTimer);
+}
+
+static void ohci_iso_recv_set_channel_mask(struct hpsb_iso *iso, u64 mask)
+{
+	struct ohci_iso_recv *recv = iso->hostdata;
+	int i;
+
+	for(i = 0; i < 64; i++) {
+		if(mask & (1ULL << i)) {
+			if(i < 32)
+				reg_write(recv->ohci, OHCI1394_IRMultiChanMaskLoSet, (1 << i));
+			else
+				reg_write(recv->ohci, OHCI1394_IRMultiChanMaskHiSet, (1 << (i-32)));
+		} else {
+			if(i < 32)
+				reg_write(recv->ohci, OHCI1394_IRMultiChanMaskLoClear, (1 << i));
+			else
+				reg_write(recv->ohci, OHCI1394_IRMultiChanMaskHiClear, (1 << (i-32)));
+		}
+	}
+
+	/* issue a dummy read to force all PCI writes to be posted immediately */
+	mb();
+	reg_read(recv->ohci, OHCI1394_IsochronousCycleTimer);
+}
+
+static int ohci_iso_recv_start(struct hpsb_iso *iso, int cycle, int tag_mask, int sync)
+{
+	struct ohci_iso_recv *recv = iso->hostdata;
+	u32 command, contextMatch;
+
+	reg_write(recv->ohci, recv->ContextControlClear, 0xFFFFFFFF);
+	wmb();
+
+	/* always keep ISO headers */
+	command = (1 << 30);
+
+	if(recv->dma_mode == BUFFER_FILL_MODE)
+		command |= (1 << 31);
+
+	reg_write(recv->ohci, recv->ContextControlSet, command);
+
+	/* match on specified tags */
+	contextMatch = tag_mask << 28;
+
+	if(iso->channel == -1) {
+		/* enable multichannel reception */
+		reg_write(recv->ohci, recv->ContextControlSet, (1 << 28));
+	} else {
+		/* listen on channel */
+		contextMatch |= iso->channel;
+	}
+
+	if(cycle != -1) {
+		u32 seconds;
+		
+		/* enable cycleMatch */
+		reg_write(recv->ohci, recv->ContextControlSet, (1 << 29));
+
+		/* set starting cycle */
+		cycle &= 0x1FFF;
+		
+		/* 'cycle' is only mod 8000, but we also need two 'seconds' bits -
+		   just snarf them from the current time */
+		seconds = reg_read(recv->ohci, OHCI1394_IsochronousCycleTimer) >> 25;
+
+		/* advance one second to give some extra time for DMA to start */
+		seconds += 1;
+		
+		cycle |= (seconds & 3) << 13;
+
+		contextMatch |= cycle << 12;
+	}
+
+	if(sync != -1) {
+		/* set sync flag on first DMA descriptor */
+		struct dma_cmd *cmd = &recv->block[recv->block_dma];
+		cmd->control |= DMA_CTL_WAIT;
+
+		/* match sync field */
+		contextMatch |= (sync&0xf)<<8;
+	}
+
+	reg_write(recv->ohci, recv->ContextMatch, contextMatch);
+
+	/* address of first descriptor block */
+	command = dma_prog_region_offset_to_bus(&recv->prog,
+						recv->block_dma * sizeof(struct dma_cmd));
+	command |= 1; /* Z=1 */
+
+	reg_write(recv->ohci, recv->CommandPtr, command);
+
+	/* enable interrupts */
+	reg_write(recv->ohci, OHCI1394_IsoRecvIntMaskSet, 1 << recv->task.context);
+
+	wmb();
+
+	/* run */
+	reg_write(recv->ohci, recv->ContextControlSet, 0x8000);
+
+	/* issue a dummy read of the cycle timer register to force
+	   all PCI writes to be posted immediately */
+	mb();
+	reg_read(recv->ohci, OHCI1394_IsochronousCycleTimer);
+
+	/* check RUN */
+	if(!(reg_read(recv->ohci, recv->ContextControlSet) & 0x8000)) {
+		PRINT(KERN_ERR, recv->ohci->id,
+		      "Error starting IR DMA (ContextControl 0x%08x)\n",
+		      reg_read(recv->ohci, recv->ContextControlSet));
+		return -1;
+	}
+
+	return 0;
+}
+
+static void ohci_iso_recv_release_block(struct ohci_iso_recv *recv, int block)
+{
+	/* re-use the DMA descriptor for the block */
+	/* by linking the previous descriptor to it */
+
+	int next_i = block;
+	int prev_i = (next_i == 0) ? (recv->nblocks - 1) : (next_i - 1);
+
+	struct dma_cmd *next = &recv->block[next_i];
+	struct dma_cmd *prev = &recv->block[prev_i];
+
+	/* 'next' becomes the new end of the DMA chain,
+	   so disable branch and enable interrupt */
+	next->branchAddress = 0;
+	next->control |= cpu_to_le32(3 << 20);
+
+	/* link prev to next */	
+	prev->branchAddress = cpu_to_le32(dma_prog_region_offset_to_bus(&recv->prog,
+									sizeof(struct dma_cmd) * next_i)
+					  | 1); /* Z=1 */
+
+	/* disable interrupt on previous DMA descriptor, except at intervals */
+	if((prev_i % recv->block_irq_interval) == 0) {
+		prev->control |= cpu_to_le32(3 << 20); /* enable interrupt */
+	} else {
+		prev->control &= cpu_to_le32(~(3<<20)); /* disable interrupt */
+	}
+	wmb();
+
+	/* wake up DMA in case it fell asleep */
+	reg_write(recv->ohci, recv->ContextControlSet, (1 << 12));
+}
+
+static void ohci_iso_recv_bufferfill_release(struct ohci_iso_recv *recv,
+					     struct hpsb_iso_packet_info *info)
+{
+	int len;
+
+	/* release the memory where the packet was */
+	len = info->len;
+
+	/* add the wasted space for padding to 4 bytes */
+	if(len % 4)
+		len += 4 - (len % 4);
+
+	/* add 8 bytes for the OHCI DMA data format overhead */
+	len += 8;
+
+	recv->released_bytes += len;
+
+	/* have we released enough memory for one block? */
+	while(recv->released_bytes > recv->buf_stride) {
+		ohci_iso_recv_release_block(recv, recv->block_reader);
+		recv->block_reader = (recv->block_reader + 1) % recv->nblocks;
+		recv->released_bytes -= recv->buf_stride;
+	}
+}
+
+static inline void ohci_iso_recv_release(struct hpsb_iso *iso, struct hpsb_iso_packet_info *info)
+{
+	struct ohci_iso_recv *recv = iso->hostdata;
+	if(recv->dma_mode == BUFFER_FILL_MODE) {
+		ohci_iso_recv_bufferfill_release(recv, info);
+	} else {
+		ohci_iso_recv_release_block(recv, info - iso->infos);
+	}
+}
+
+/* parse all packets from blocks that have been fully received */
+static void ohci_iso_recv_bufferfill_parse(struct hpsb_iso *iso, struct ohci_iso_recv *recv)
+{
+	int wake = 0;
+	int runaway = 0;
+		
+	while(1) {
+		/* we expect the next parsable packet to begin at recv->dma_offset */
+		/* note: packet layout is as shown in section 10.6.1.1 of the OHCI spec */
+		
+		unsigned int offset;
+		unsigned short len, cycle;
+		unsigned char channel, tag, sy;
+		
+		unsigned char *p = iso->data_buf.kvirt;
+
+		unsigned int this_block = recv->dma_offset/recv->buf_stride;
+
+		/* don't loop indefinitely */
+		if(runaway++ > 100000) {
+			atomic_inc(&iso->overflows);
+			PRINT(KERN_ERR, recv->ohci->id,
+			      "IR DMA error - Runaway during buffer parsing!\n");
+			break;
+		}
+
+		/* stop parsing once we arrive at block_dma (i.e. don't get ahead of DMA) */
+		if(this_block == recv->block_dma)
+			break;
+
+		wake = 1;
+		
+		/* parse data length, tag, channel, and sy */
+		
+		/* note: we keep our own local copies of 'len' and 'offset'
+		   so the user can't mess with them by poking in the mmap area */
+		
+		len = p[recv->dma_offset+2] | (p[recv->dma_offset+3] << 8);
+
+		if(len > 4096) {
+			PRINT(KERN_ERR, recv->ohci->id,
+			      "IR DMA error - bogus 'len' value %u\n", len);
+		}
+		
+		channel = p[recv->dma_offset+1] & 0x3F;
+		tag = p[recv->dma_offset+1] >> 6;
+		sy = p[recv->dma_offset+0] & 0xF;
+
+		/* advance to data payload */
+		recv->dma_offset += 4;
+		
+		/* check for wrap-around */
+		if(recv->dma_offset >= recv->buf_stride*recv->nblocks) {
+			recv->dma_offset -= recv->buf_stride*recv->nblocks;
+		}
+
+		/* dma_offset now points to the first byte of the data payload */
+		offset = recv->dma_offset;
+
+		/* advance to xferStatus/timeStamp */
+		recv->dma_offset += len;
+
+		/* payload is padded to 4 bytes */
+		if(len % 4) { 
+			recv->dma_offset += 4 - (len%4);
+		}
+
+		/* check for wrap-around */
+		if(recv->dma_offset >= recv->buf_stride*recv->nblocks) {
+			/* uh oh, the packet data wraps from the last
+                           to the first DMA block - make the packet
+                           contiguous by copying its "tail" into the
+                           guard page */
+
+			int guard_off = recv->buf_stride*recv->nblocks;
+			int tail_len = len - (guard_off - offset);
+
+			if(tail_len > 0  && tail_len < recv->buf_stride) {
+				memcpy(iso->data_buf.kvirt + guard_off,
+				       iso->data_buf.kvirt,
+				       tail_len);
+			}
+
+			recv->dma_offset -= recv->buf_stride*recv->nblocks;
+		}
+
+		/* parse timestamp */
+		cycle = p[recv->dma_offset+0] | (p[recv->dma_offset+1]<<8);
+		cycle &= 0x1FFF;
+
+		/* advance to next packet */
+		recv->dma_offset += 4;
+
+		/* check for wrap-around */
+		if(recv->dma_offset >= recv->buf_stride*recv->nblocks) {
+			recv->dma_offset -= recv->buf_stride*recv->nblocks;
+		}
+
+		hpsb_iso_packet_received(iso, offset, len, cycle, channel, tag, sy);
+	}
+
+	if(wake)
+		hpsb_iso_wake(iso);
+}
+
+static void ohci_iso_recv_bufferfill_task(unsigned long data)
+{
+	struct hpsb_iso *iso = (struct hpsb_iso*) data;
+	struct ohci_iso_recv *recv = iso->hostdata;
+
+	int loop;
+
+	/* loop over all blocks */
+	for(loop = 0; loop < recv->nblocks; loop++) {
+		
+		/* check block_dma to see if it's done */
+		
+		struct dma_cmd *im = &recv->block[recv->block_dma];
+		
+		/* check the DMA descriptor for new writes to xferStatus */
+		u16 xferstatus = im->status >> 16;
+		
+		/* rescount is the number of bytes *remaining to be written* in the block */
+		u16 rescount = im->status & 0xFFFF;
+
+		unsigned char event = xferstatus & 0x1F;
+
+		if(!event) {
+			/* nothing has happened to this block yet */
+			break;
+		}
+
+		if(event != 0x11) {
+			atomic_inc(&iso->overflows);
+			PRINT(KERN_ERR, recv->ohci->id,
+			      "IR DMA error - OHCI error code 0x%02x\n", event);
+		}
+
+		if(rescount != 0) {
+			/* the card is still writing to this block;
+			   we can't touch it until it's done */
+			break;
+		}
+		
+		/* OK, the block is finished... */
+		
+		/* sync our view of the block */
+		dma_region_sync(&iso->data_buf, recv->block_dma*recv->buf_stride, recv->buf_stride);
+		
+		/* reset the DMA descriptor */
+		im->status = recv->buf_stride;
+
+		/* advance block_dma */
+		recv->block_dma = (recv->block_dma + 1) % recv->nblocks;
+
+		if((recv->block_dma+1) % recv->nblocks == recv->block_reader) {
+			atomic_inc(&iso->overflows);
+			DBGMSG(recv->ohci->id, "ISO reception overflow - "
+			       "ran out of DMA blocks");
+		}
+	}
+
+	/* parse any packets that have arrived */
+	ohci_iso_recv_bufferfill_parse(iso, recv);
+}
+
+static void ohci_iso_recv_packetperbuf_task(unsigned long data)
+{
+	struct hpsb_iso *iso = (struct hpsb_iso*) data;
+	struct ohci_iso_recv *recv = iso->hostdata;
+	int count;
+	int wake = 0;
+	
+	/* loop over the entire buffer */
+	for(count = 0; count < recv->nblocks; count++) {
+		u32 packet_len = 0;
+		
+		/* pointer to the DMA descriptor */
+		struct dma_cmd *il = ((struct dma_cmd*) recv->prog.kvirt) + iso->pkt_dma;
+
+		/* check the DMA descriptor for new writes to xferStatus */
+		u16 xferstatus = le32_to_cpu(il->status) >> 16;
+		u16 rescount = le32_to_cpu(il->status) & 0xFFFF;
+
+		unsigned char event = xferstatus & 0x1F;
+
+		if(!event) {
+			/* this packet hasn't come in yet; we are done for now */
+			goto out;
+		}
+		
+		if(event == 0x11) {
+			/* packet received successfully! */
+			
+			/* rescount is the number of bytes *remaining* in the packet buffer,
+			   after the packet was written */
+			packet_len = recv->buf_stride - rescount;
+
+		} else if(event == 0x02) {
+			PRINT(KERN_ERR, recv->ohci->id, "IR DMA error - packet too long for buffer\n");
+		} else if(event) {
+			PRINT(KERN_ERR, recv->ohci->id, "IR DMA error - OHCI error code 0x%02x\n", event);
+		}
+
+		/* sync our view of the buffer */
+		dma_region_sync(&iso->data_buf, iso->pkt_dma * recv->buf_stride, recv->buf_stride);
+			
+		/* record the per-packet info */
+		{
+			/* iso header is 8 bytes ahead of the data payload */
+			unsigned char *hdr;
+
+			unsigned int offset;
+			unsigned short cycle;
+			unsigned char channel, tag, sy;
+
+			offset = iso->pkt_dma * recv->buf_stride;
+			hdr = iso->data_buf.kvirt + offset;
+
+			/* skip iso header */
+			offset += 8;
+			packet_len -= 8;
+			
+			cycle = (hdr[0] | (hdr[1] << 8)) & 0x1FFF;
+			channel = hdr[5] & 0x3F;
+			tag = hdr[5] >> 6;
+			sy = hdr[4] & 0xF;
+
+			hpsb_iso_packet_received(iso, offset, packet_len, cycle, channel, tag, sy);
+		}
+		
+		/* reset the DMA descriptor */
+		il->status = recv->buf_stride;
+
+		wake = 1;
+		recv->block_dma = iso->pkt_dma;
+	}
+
+out:
+	if(wake)
+		hpsb_iso_wake(iso);
+}
+
+
+/***********************************
+ * rawiso ISO transmission         *
+ ***********************************/
+
+struct ohci_iso_xmit {
+	struct ti_ohci *ohci;
+	struct dma_prog_region prog;
+	struct ohci1394_iso_tasklet task;
+	int task_active;
+
+	u32 ContextControlSet;
+	u32 ContextControlClear;
+	u32 CommandPtr;
+};
+
+/* transmission DMA program:
+   one OUTPUT_MORE_IMMEDIATE for the IT header
+   one OUTPUT_LAST for the buffer data */
+
+struct iso_xmit_cmd {
+	struct dma_cmd output_more_immediate;
+	u8 iso_hdr[8];
+	u32 unused[2];
+	struct dma_cmd output_last;
+};
+
+static int ohci_iso_xmit_init(struct hpsb_iso *iso);
+static int ohci_iso_xmit_start(struct hpsb_iso *iso, int cycle);
+static void ohci_iso_xmit_shutdown(struct hpsb_iso *iso);
+static void ohci_iso_xmit_task(unsigned long data);
+
+static int ohci_iso_xmit_init(struct hpsb_iso *iso)
+{
+	struct ohci_iso_xmit *xmit;
+	unsigned int prog_size;
+	int ctx;
+	int ret = -ENOMEM;
+
+	xmit = kmalloc(sizeof(*xmit), SLAB_KERNEL);
+	if(!xmit)
+		return -ENOMEM;
+
+	iso->hostdata = xmit;
+	xmit->ohci = iso->host->hostdata;
+	xmit->task_active = 0;
+
+	dma_prog_region_init(&xmit->prog);
+
+	prog_size = sizeof(struct iso_xmit_cmd) * iso->buf_packets;
+
+	if(dma_prog_region_alloc(&xmit->prog, prog_size, xmit->ohci->dev))
+		goto err;
+
+	ohci1394_init_iso_tasklet(&xmit->task, OHCI_ISO_TRANSMIT,
+				  ohci_iso_xmit_task, (unsigned long) iso);
+
+	if(ohci1394_register_iso_tasklet(xmit->ohci, &xmit->task) < 0)
+		goto err;
+
+	xmit->task_active = 1;
+
+	/* xmit context registers are spaced 16 bytes apart */
+	ctx = xmit->task.context;
+	xmit->ContextControlSet = OHCI1394_IsoXmitContextControlSet + 16 * ctx;
+	xmit->ContextControlClear = OHCI1394_IsoXmitContextControlClear + 16 * ctx;
+	xmit->CommandPtr = OHCI1394_IsoXmitCommandPtr + 16 * ctx;
+
+	return 0;
+
+err:
+	ohci_iso_xmit_shutdown(iso);
+	return ret;
+}
+
+static void ohci_iso_xmit_stop(struct hpsb_iso *iso)
+{
+	struct ohci_iso_xmit *xmit = iso->hostdata;
+
+	/* disable interrupts */
+	reg_write(xmit->ohci, OHCI1394_IsoXmitIntMaskClear, 1 << xmit->task.context);
+
+	/* halt DMA */
+	if(ohci1394_stop_context(xmit->ohci, xmit->ContextControlClear, NULL)) {
+		/* XXX the DMA context will lock up if you try to send too much data! */
+		PRINT(KERN_ERR, xmit->ohci->id,
+		      "you probably exceeded the OHCI card's bandwidth limit - "
+		      "reload the module and reduce xmit bandwidth");
+	}
+}
+
+static void ohci_iso_xmit_shutdown(struct hpsb_iso *iso)
+{
+	struct ohci_iso_xmit *xmit = iso->hostdata;
+
+	if(xmit->task_active) {
+		ohci_iso_xmit_stop(iso);
+		ohci1394_unregister_iso_tasklet(xmit->ohci, &xmit->task);
+		xmit->task_active = 0;
+	}
+
+	dma_prog_region_free(&xmit->prog);
+	kfree(xmit);
+	iso->hostdata = NULL;
+}
+
+static void ohci_iso_xmit_task(unsigned long data)
+{
+	struct hpsb_iso *iso = (struct hpsb_iso*) data;
+	struct ohci_iso_xmit *xmit = iso->hostdata;
+	int wake = 0;
+	int count;
+
+	/* check the whole buffer if necessary, starting at pkt_dma */
+	for(count = 0; count < iso->buf_packets; count++) {
+		int cycle;
+
+		/* DMA descriptor */
+		struct iso_xmit_cmd *cmd = dma_region_i(&xmit->prog, struct iso_xmit_cmd, iso->pkt_dma);
+		
+		/* check for new writes to xferStatus */
+		u16 xferstatus = le32_to_cpu(cmd->output_last.status) >> 16;
+		u8  event = xferstatus & 0x1F;
+
+		if(!event) {
+			/* packet hasn't been sent yet; we are done for now */
+			break;
+		}
+
+		if(event != 0x11)
+			PRINT(KERN_ERR, xmit->ohci->id,
+			      "IT DMA error - OHCI error code 0x%02x\n", event);
+		
+		/* at least one packet went out, so wake up the writer */
+		wake = 1;
+		
+		/* parse cycle */
+		cycle = le32_to_cpu(cmd->output_last.status) & 0x1FFF;
+
+		/* tell the subsystem the packet has gone out */
+		hpsb_iso_packet_sent(iso, cycle, event != 0x11);
+		
+		/* reset the DMA descriptor for next time */
+		cmd->output_last.status = 0;
+	}
+
+	if(wake)
+		hpsb_iso_wake(iso);
+}
+
+static int ohci_iso_xmit_queue(struct hpsb_iso *iso, struct hpsb_iso_packet_info *info)
+{
+	struct ohci_iso_xmit *xmit = iso->hostdata;
+
+	int next_i, prev_i;
+	struct iso_xmit_cmd *next, *prev;
+
+	unsigned int offset;
+	unsigned short len;
+	unsigned char tag, sy;
+
+	/* check that the packet doesn't cross a page boundary
+	   (we could allow this if we added OUTPUT_MORE descriptor support) */
+	if(cross_bound(info->offset, info->len)) {
+		PRINT(KERN_ERR, xmit->ohci->id,
+		      "rawiso xmit: packet %u crosses a page boundary",
+		      iso->first_packet);
+		return -EINVAL;
+	}
+
+	offset = info->offset;
+	len = info->len;
+	tag = info->tag;
+	sy = info->sy;
+
+	/* sync up the card's view of the buffer */
+	dma_region_sync(&iso->data_buf, offset, len);
+
+	/* append first_packet to the DMA chain */
+	/* by linking the previous descriptor to it */
+	/* (next will become the new end of the DMA chain) */
+
+	next_i = iso->first_packet;
+	prev_i = (next_i == 0) ? (iso->buf_packets - 1) : (next_i - 1);
+
+	next = dma_region_i(&xmit->prog, struct iso_xmit_cmd, next_i);
+	prev = dma_region_i(&xmit->prog, struct iso_xmit_cmd, prev_i);
+
+	/* set up the OUTPUT_MORE_IMMEDIATE descriptor */
+	memset(next, 0, sizeof(struct iso_xmit_cmd));
+	next->output_more_immediate.control = cpu_to_le32(0x02000008);
+
+	/* ISO packet header is embedded in the OUTPUT_MORE_IMMEDIATE */
+
+	/* tcode = 0xA, and sy */
+	next->iso_hdr[0] = 0xA0 | (sy & 0xF);
+
+	/* tag and channel number */
+	next->iso_hdr[1] = (tag << 6) | (iso->channel & 0x3F);
+
+	/* transmission speed */
+	next->iso_hdr[2] = iso->speed & 0x7;
+
+	/* payload size */
+	next->iso_hdr[6] = len & 0xFF;
+	next->iso_hdr[7] = len >> 8;
+
+	/* set up the OUTPUT_LAST */
+	next->output_last.control = cpu_to_le32(1 << 28);
+	next->output_last.control |= cpu_to_le32(1 << 27); /* update timeStamp */
+	next->output_last.control |= cpu_to_le32(3 << 20); /* want interrupt */
+	next->output_last.control |= cpu_to_le32(3 << 18); /* enable branch */
+	next->output_last.control |= cpu_to_le32(len);
+
+	/* payload bus address */
+	next->output_last.address = cpu_to_le32(dma_region_offset_to_bus(&iso->data_buf, offset));
+
+	/* leave branchAddress at zero for now */
+
+	/* re-write the previous DMA descriptor to chain to this one */
+
+	/* set prev branch address to point to next (Z=3) */
+	prev->output_last.branchAddress = cpu_to_le32(
+		dma_prog_region_offset_to_bus(&xmit->prog, sizeof(struct iso_xmit_cmd) * next_i) | 3);
+
+	/* disable interrupt, unless required by the IRQ interval */
+	if(prev_i % iso->irq_interval) {
+		prev->output_last.control &= cpu_to_le32(~(3 << 20)); /* no interrupt */
+	} else {
+		prev->output_last.control |= cpu_to_le32(3 << 20); /* enable interrupt */
+	}
+
+	wmb();
+
+	/* wake DMA in case it is sleeping */
+	reg_write(xmit->ohci, xmit->ContextControlSet, 1 << 12);
+
+	/* issue a dummy read of the cycle timer to force all PCI
+	   writes to be posted immediately */
+	mb();
+	reg_read(xmit->ohci, OHCI1394_IsochronousCycleTimer);
+
+	return 0;
+}
+
+static int ohci_iso_xmit_start(struct hpsb_iso *iso, int cycle)
+{
+	struct ohci_iso_xmit *xmit = iso->hostdata;
+
+	/* clear out the control register */
+	reg_write(xmit->ohci, xmit->ContextControlClear, 0xFFFFFFFF);
+	wmb();
+
+	/* address and length of first descriptor block (Z=3) */
+	reg_write(xmit->ohci, xmit->CommandPtr,
+		  dma_prog_region_offset_to_bus(&xmit->prog, iso->pkt_dma * sizeof(struct iso_xmit_cmd)) | 3);
+
+	/* cycle match */
+	if(cycle != -1) {
+		u32 start = cycle & 0x1FFF;
+		
+		/* 'cycle' is only mod 8000, but we also need two 'seconds' bits -
+		   just snarf them from the current time */
+		u32 seconds = reg_read(xmit->ohci, OHCI1394_IsochronousCycleTimer) >> 25;
+
+		/* advance one second to give some extra time for DMA to start */
+		seconds += 1;
+		
+		start |= (seconds & 3) << 13;
+
+		reg_write(xmit->ohci, xmit->ContextControlSet, 0x80000000 | (start << 16));
+	}
+
+	/* enable interrupts */
+	reg_write(xmit->ohci, OHCI1394_IsoXmitIntMaskSet, 1 << xmit->task.context);
+
+	/* run */
+	reg_write(xmit->ohci, xmit->ContextControlSet, 0x8000);
+	mb();
+
+	/* wait 100 usec to give the card time to go active */
+	udelay(100);
+
+	/* check the RUN bit */
+	if(!(reg_read(xmit->ohci, xmit->ContextControlSet) & 0x8000)) {
+		PRINT(KERN_ERR, xmit->ohci->id, "Error starting IT DMA (ContextControl 0x%08x)\n",
+		      reg_read(xmit->ohci, xmit->ContextControlSet));
+		return -1;
+	}
+
+	return 0;
+}
+
+static int ohci_isoctl(struct hpsb_iso *iso, enum isoctl_cmd cmd, unsigned long arg)
+{
+
+	switch(cmd) {
+	case XMIT_INIT:
+		return ohci_iso_xmit_init(iso);
+	case XMIT_START:
+		return ohci_iso_xmit_start(iso, arg);
+	case XMIT_STOP:
+		ohci_iso_xmit_stop(iso);
+		return 0;
+	case XMIT_QUEUE:
+		return ohci_iso_xmit_queue(iso, (struct hpsb_iso_packet_info*) arg);
+	case XMIT_SHUTDOWN:
+		ohci_iso_xmit_shutdown(iso);
+		return 0;
+
+	case RECV_INIT:
+		return ohci_iso_recv_init(iso);
+	case RECV_START: {
+		int *args = (int*) arg;
+		return ohci_iso_recv_start(iso, args[0], args[1], args[2]);
+	}
+	case RECV_STOP:
+		ohci_iso_recv_stop(iso);
+		return 0;
+	case RECV_RELEASE:
+		ohci_iso_recv_release(iso, (struct hpsb_iso_packet_info*) arg);
+		return 0;
+	case RECV_SHUTDOWN:
+		ohci_iso_recv_shutdown(iso);
+		return 0;
+	case RECV_LISTEN_CHANNEL:
+		ohci_iso_recv_change_channel(iso, arg, 1);
+		return 0;
+	case RECV_UNLISTEN_CHANNEL:
+		ohci_iso_recv_change_channel(iso, arg, 0);
+		return 0;
+	case RECV_SET_CHANNEL_MASK:
+		ohci_iso_recv_set_channel_mask(iso, *((u64*) arg));
+		return 0;
+
+	default:
+		PRINT_G(KERN_ERR, "ohci_isoctl cmd %d not implemented yet",
+			cmd);
+		break;
+	}
+	return -EINVAL;
+}
+
 /***************************************
  * IEEE-1394 functionality section END *
  ***************************************/
@@ -1038,7 +2167,7 @@
 
 		if (t->type == OHCI_ISO_TRANSMIT && tx_event & mask)
 			tasklet_schedule(&t->tasklet);
-		if (t->type == OHCI_ISO_RECEIVE && rx_event & mask)
+		else if (rx_event & mask)
 			tasklet_schedule(&t->tasklet);
 	}
 
@@ -1056,7 +2185,7 @@
 	unsigned long flags;
 
 	/* Read and clear the interrupt event register.  Don't clear
-	 * the busReset event, though, this is done when we get the
+	 * the busReset event, though. This is done when we get the
 	 * selfIDComplete interrupt. */
 	spin_lock_irqsave(&ohci->event_lock, flags);
 	event = reg_read(ohci, OHCI1394_IntEventClear);
@@ -1067,37 +2196,85 @@
 
 	DBGMSG(ohci->id, "IntEvent: %08x", event);
 
-	/* Die right here an now */
 	if (event & OHCI1394_unrecoverableError) {
-		PRINT(KERN_ERR, ohci->id, "Unrecoverable error, shutting down card!");
-		return;
+		int ctx;
+		PRINT(KERN_ERR, ohci->id, "Unrecoverable error!");
+
+		if (reg_read(ohci, OHCI1394_AsReqTrContextControlSet) & 0x800)
+			PRINT(KERN_ERR, ohci->id, "Async Req Tx Context died: "
+				"ctrl[%08x] cmdptr[%08x]",
+				reg_read(ohci, OHCI1394_AsReqTrContextControlSet),
+				reg_read(ohci, OHCI1394_AsReqTrCommandPtr));
+
+		if (reg_read(ohci, OHCI1394_AsRspTrContextControlSet) & 0x800)
+			PRINT(KERN_ERR, ohci->id, "Async Rsp Tx Context died: "
+				"ctrl[%08x] cmdptr[%08x]",
+				reg_read(ohci, OHCI1394_AsRspTrContextControlSet),
+				reg_read(ohci, OHCI1394_AsRspTrCommandPtr));
+
+		if (reg_read(ohci, OHCI1394_AsReqRcvContextControlSet) & 0x800)
+			PRINT(KERN_ERR, ohci->id, "Async Req Rcv Context died: "
+				"ctrl[%08x] cmdptr[%08x]",
+				reg_read(ohci, OHCI1394_AsReqRcvContextControlSet),
+				reg_read(ohci, OHCI1394_AsReqRcvCommandPtr));
+
+		if (reg_read(ohci, OHCI1394_AsRspRcvContextControlSet) & 0x800)
+			PRINT(KERN_ERR, ohci->id, "Async Rsp Rcv Context died: "
+				"ctrl[%08x] cmdptr[%08x]",
+				reg_read(ohci, OHCI1394_AsRspRcvContextControlSet),
+				reg_read(ohci, OHCI1394_AsRspRcvCommandPtr));
+
+		for (ctx = 0; ctx < ohci->nb_iso_xmit_ctx; ctx++) {
+			if (reg_read(ohci, OHCI1394_IsoXmitContextControlSet + (16 * ctx)) & 0x800)
+				PRINT(KERN_ERR, ohci->id, "Iso Xmit %d Context died: "
+					"ctrl[%08x] cmdptr[%08x]", ctx,
+					reg_read(ohci, OHCI1394_IsoXmitContextControlSet + (16 * ctx)),
+					reg_read(ohci, OHCI1394_IsoXmitCommandPtr + (16 * ctx)));
+		}
+
+		for (ctx = 0; ctx < ohci->nb_iso_rcv_ctx; ctx++) {
+			if (reg_read(ohci, OHCI1394_IsoRcvContextControlSet + (32 * ctx)) & 0x800)
+				PRINT(KERN_ERR, ohci->id, "Iso Recv %d Context died: "
+					"ctrl[%08x] cmdptr[%08x] match[%08x]", ctx,
+					reg_read(ohci, OHCI1394_IsoRcvContextControlSet + (32 * ctx)),
+					reg_read(ohci, OHCI1394_IsoRcvCommandPtr + (32 * ctx)),
+					reg_read(ohci, OHCI1394_IsoRcvContextMatch + (32 * ctx)));
+		}
+
+		event &= ~OHCI1394_unrecoverableError;
 	}
 
 	if (event & OHCI1394_cycleInconsistent) {
 		/* We subscribe to the cycleInconsistent event only to
 		 * clear the corresponding event bit... otherwise,
-		 * isochronous cycleMatch DMA wont work. */
+		 * isochronous cycleMatch DMA won't work. */
 		DBGMSG(ohci->id, "OHCI1394_cycleInconsistent");
 		event &= ~OHCI1394_cycleInconsistent;
 	}
 
 	if (event & OHCI1394_busReset) {
+		int loops = 0;
+
 		/* The busReset event bit can't be cleared during the
 		 * selfID phase, so we disable busReset interrupts, to
 		 * avoid burying the cpu in interrupt requests. */
 		spin_lock_irqsave(&ohci->event_lock, flags);
   		reg_write(ohci, OHCI1394_IntMaskClear, OHCI1394_busReset);
-		if (ohci->dev->vendor == PCI_VENDOR_ID_APPLE && 
-		    ohci->dev->device == PCI_DEVICE_ID_APPLE_UNI_N_FW) {
-  			udelay(10);
-  			while(reg_read(ohci, OHCI1394_IntEventSet) & OHCI1394_busReset) {
-  				reg_write(ohci, OHCI1394_IntEventClear, OHCI1394_busReset);
-				spin_unlock_irqrestore(&ohci->event_lock, flags);
-	  			udelay(10);
-				spin_lock_irqsave(&ohci->event_lock, flags);
-  			}
+		udelay(10);
+		while (reg_read(ohci, OHCI1394_IntEventSet) & OHCI1394_busReset) {
+			if (loops++ > 10000) {
+				hpsb_reset_bus(host, 1);
+				loops = 0;
+				DBGMSG(ohci->id, "Out of control loop, resetting");
+			}
+
+			reg_write(ohci, OHCI1394_IntEventClear, OHCI1394_busReset);
+			spin_unlock_irqrestore(&ohci->event_lock, flags);
+			udelay(10);
+			spin_lock_irqsave(&ohci->event_lock, flags);
   		}
 		spin_unlock_irqrestore(&ohci->event_lock, flags);
+
 		if (!host->in_bus_reset) {
 			DBGMSG(ohci->id, "irq_handler: Bus reset requested%s",
 			      (attempt_root) ? " and attempting to become root"
@@ -1178,7 +2355,7 @@
 			if (!(node_id & 0x80000000)) {
 				PRINT(KERN_ERR, ohci->id,
 				      "SelfID received, but NodeID invalid "
-				      "(probably new bus reset occured): %08X",
+				      "(probably new bus reset occurred): %08X",
 				      node_id);
 				goto selfid_not_valid;
 			}
@@ -1187,9 +2364,9 @@
 			isroot = (node_id & 0x40000000) != 0;
 
 			DBGMSG(ohci->id,
-			       "SelfID interrupt received "
-			       "(phyid %d, %s)", phyid,
-			       (isroot ? "root" : "not root"));
+			      "SelfID interrupt received "
+			      "(phyid %d, %s)", phyid, 
+			      (isroot ? "root" : "not root"));
 
 			handle_selfid(ohci, host, phyid, isroot);
 
@@ -1216,13 +2393,14 @@
 				reg_write(ohci,OHCI1394_PhyReqFilterHiSet, 0x00000000);
 				reg_write(ohci,OHCI1394_PhyReqFilterLoSet, 0x00000000);
 			}
+
 			DBGMSG(ohci->id, "PhyReqFilter=%08x%08x\n",
 			       reg_read(ohci,OHCI1394_PhyReqFilterHiSet),
 			       reg_read(ohci,OHCI1394_PhyReqFilterLoSet));
 
 			hpsb_selfid_complete(host, phyid, isroot);
 		} else
-			PRINT(KERN_ERR, ohci->id,
+			PRINT(KERN_ERR, ohci->id, 
 			      "SelfID received outside of bus reset sequence");
 
 selfid_not_valid:
@@ -1252,8 +2430,10 @@
 		PRINT(KERN_INFO, ohci->id, 
 		      "Waking dma ctx=%d ... processing is probably too slow",
 		      d->ctx);
-		reg_write(ohci, d->ctrlSet, 0x1000);
 	}
+
+	/* do this always, to avoid race condition */
+	reg_write(ohci, d->ctrlSet, 0x1000);
 }
 
 #define cond_le32_to_cpu(data, noswap) \
@@ -1398,7 +2578,7 @@
 		 * bus reset. We always ignore it.  */
 		if (tcode != OHCI1394_TCODE_PHY) {
 			if (!ohci->no_swap_incoming)
-				packet_swab(d->spb, tcode, (length - 4) >> 2);
+				packet_swab(d->spb, tcode);
 			DBGMSG(ohci->id, "Packet received from node"
 				" %d ack=0x%02X spd=%d tcode=0x%X"
 				" length=%d ctx=%d tlabel=%d",
@@ -1566,13 +2746,18 @@
 		return;
 
 	DBGMSG(d->ohci->id, "Freeing dma_rcv_ctx %d", d->ctx);
-	
-	ohci1394_stop_context(d->ohci, d->ctrlClear, NULL);
 
-	if (d->type == DMA_CTX_ISO)
-		ohci1394_unregister_iso_tasklet(d->ohci, &d->ohci->ir_tasklet);
-	else
-		tasklet_kill(&d->task);
+	if(d->ctrlClear) {
+		ohci1394_stop_context(d->ohci, d->ctrlClear, NULL);
+
+		if (d->type == DMA_CTX_ISO) {
+			/* disable interrupts */
+			reg_write(d->ohci, OHCI1394_IsoRecvIntMaskClear, 1 << d->ctx);
+			ohci1394_unregister_iso_tasklet(d->ohci, &d->ohci->ir_legacy_tasklet);
+		} else {
+			tasklet_kill(&d->task);
+		}
+	}
 
 	if (d->buf_cpu) {
 		for (i=0; i<d->num_desc; i++)
@@ -1588,11 +2773,11 @@
 	if (d->prg_cpu) {
 		for (i=0; i<d->num_desc; i++) 
 			if (d->prg_cpu[i] && d->prg_bus[i]) {
-				pci_free_consistent(
-					d->ohci->dev, sizeof(struct dma_cmd), 
-					d->prg_cpu[i], d->prg_bus[i]);
+				pci_pool_free(d->prg_pool, d->prg_cpu[i], d->prg_bus[i]);
 				OHCI_DMA_FREE("consistent dma_rcv prg[%d]", i);
 			}
+		pci_pool_destroy(d->prg_pool);
+		OHCI_DMA_FREE("dma_rcv prg pool");
 		kfree(d->prg_cpu);
 		kfree(d->prg_bus);
 	}
@@ -1617,9 +2802,9 @@
 	d->buf_size = buf_size;
 	d->split_buf_size = split_buf_size;
 
-	d->ctrlSet = context_base + OHCI1394_ContextControlSet;
-	d->ctrlClear = context_base + OHCI1394_ContextControlClear;
-	d->cmdPtr = context_base + OHCI1394_ContextCommandPtr;
+	d->ctrlSet = 0;
+	d->ctrlClear = 0;
+	d->cmdPtr = 0;
 
 	d->buf_cpu = kmalloc(d->num_desc * sizeof(quadlet_t*), GFP_KERNEL);
 	d->buf_bus = kmalloc(d->num_desc * sizeof(dma_addr_t), GFP_KERNEL);
@@ -1652,6 +2837,10 @@
 		return -ENOMEM;
 	}
 
+	d->prg_pool = pci_pool_create("ohci1394 rcv prg", ohci->dev,
+				      sizeof(struct dma_cmd), 4, 0, SLAB_KERNEL);
+	OHCI_DMA_ALLOC("dma_rcv prg pool");
+
 	for (i=0; i<d->num_desc; i++) {
 		d->buf_cpu[i] = pci_alloc_consistent(ohci->dev, 
 						     d->buf_size,
@@ -1667,11 +2856,8 @@
 			return -ENOMEM;
 		}
 
-		
-                d->prg_cpu[i] = pci_alloc_consistent(ohci->dev, 
-						     sizeof(struct dma_cmd),
-						     d->prg_bus+i);
-		OHCI_DMA_ALLOC("consistent dma_rcv prg[%d]", i);
+		d->prg_cpu[i] = pci_pool_alloc(d->prg_pool, SLAB_KERNEL, d->prg_bus+i);
+		OHCI_DMA_ALLOC("pool dma_rcv prg[%d]", i);
 
                 if (d->prg_cpu[i] != NULL) {
                         memset(d->prg_cpu[i], 0, sizeof(struct dma_cmd));
@@ -1686,17 +2872,30 @@
         spin_lock_init(&d->lock);
 
 	if (type == DMA_CTX_ISO) {
-		ohci1394_init_iso_tasklet(&ohci->ir_tasklet, OHCI_ISO_RECEIVE,
+		ohci1394_init_iso_tasklet(&ohci->ir_legacy_tasklet,
+					  OHCI_ISO_MULTICHANNEL_RECEIVE,
 					  dma_rcv_tasklet, (unsigned long) d);
 		if (ohci1394_register_iso_tasklet(ohci,
-						  &ohci->ir_tasklet) < 0) {
+						  &ohci->ir_legacy_tasklet) < 0) {
 			PRINT(KERN_ERR, ohci->id, "No IR DMA context available");
 			free_dma_rcv_ctx(d);
 			return -EBUSY;
 		}
-	}
-	else
+
+		/* the IR context can be assigned to any DMA context
+		 * by ohci1394_register_iso_tasklet */
+		d->ctx = ohci->ir_legacy_tasklet.context;
+		d->ctrlSet = OHCI1394_IsoRcvContextControlSet + 32*d->ctx;
+		d->ctrlClear = OHCI1394_IsoRcvContextControlClear + 32*d->ctx;
+		d->cmdPtr = OHCI1394_IsoRcvCommandPtr + 32*d->ctx;
+		d->ctxtMatch = OHCI1394_IsoRcvContextMatch + 32*d->ctx;
+	} else {
+		d->ctrlSet = context_base + OHCI1394_ContextControlSet;
+		d->ctrlClear = context_base + OHCI1394_ContextControlClear;
+		d->cmdPtr = context_base + OHCI1394_ContextCommandPtr;
+
 		tasklet_init (&d->task, dma_rcv_tasklet, (unsigned long) d);
+	}
 
 	return 0;
 }
@@ -1710,21 +2909,27 @@
 
 	DBGMSG(d->ohci->id, "Freeing dma_trm_ctx %d", d->ctx);
 
-	ohci1394_stop_context(d->ohci, d->ctrlClear, NULL);
+	if(d->ctrlClear) {
+		ohci1394_stop_context(d->ohci, d->ctrlClear, NULL);
 
-	if (d->type == DMA_CTX_ISO)
-		ohci1394_unregister_iso_tasklet(d->ohci, &d->ohci->it_tasklet);
-	else
-		tasklet_kill(&d->task);
+		if (d->type == DMA_CTX_ISO) {
+			/* disable interrupts */
+			reg_write(d->ohci, OHCI1394_IsoXmitIntMaskClear, 1 << d->ctx);
+			ohci1394_unregister_iso_tasklet(d->ohci,
+							&d->ohci->it_legacy_tasklet);
+		} else {
+			tasklet_kill(&d->task);
+		}
+	}
 
 	if (d->prg_cpu) {
 		for (i=0; i<d->num_desc; i++) 
 			if (d->prg_cpu[i] && d->prg_bus[i]) {
-				pci_free_consistent(
-					d->ohci->dev, sizeof(struct at_dma_prg), 
-					d->prg_cpu[i], d->prg_bus[i]);
-				OHCI_DMA_FREE("consistent dma_trm prg[%d]", i);
+				pci_pool_free(d->prg_pool, d->prg_cpu[i], d->prg_bus[i]);
+				OHCI_DMA_FREE("pool dma_trm prg[%d]", i);
 			}
+		pci_pool_destroy(d->prg_pool);
+		OHCI_DMA_FREE("dma_trm prg pool");
 		kfree(d->prg_cpu);
 		kfree(d->prg_bus);
 	}
@@ -1744,9 +2949,9 @@
 	d->type = type;
 	d->ctx = ctx;
 	d->num_desc = num_desc;
-	d->ctrlSet = context_base + OHCI1394_ContextControlSet;
-	d->ctrlClear = context_base + OHCI1394_ContextControlClear;
-	d->cmdPtr = context_base + OHCI1394_ContextCommandPtr;
+	d->ctrlSet = 0;
+	d->ctrlClear = 0;
+	d->cmdPtr = 0;
 
 	d->prg_cpu = kmalloc(d->num_desc * sizeof(struct at_dma_prg*), 
 			     GFP_KERNEL);
@@ -1760,11 +2965,13 @@
 	memset(d->prg_cpu, 0, d->num_desc * sizeof(struct at_dma_prg*));
 	memset(d->prg_bus, 0, d->num_desc * sizeof(dma_addr_t));
 
+	d->prg_pool = pci_pool_create("ohci1394 trm prg", ohci->dev,
+				      sizeof(struct at_dma_prg), 4, 0, SLAB_KERNEL);
+	OHCI_DMA_ALLOC("dma_rcv prg pool");
+
 	for (i = 0; i < d->num_desc; i++) {
-                d->prg_cpu[i] = pci_alloc_consistent(ohci->dev, 
-						     sizeof(struct at_dma_prg),
-						     d->prg_bus+i);
-		OHCI_DMA_ALLOC("consistent dma_trm prg[%d]", i);
+		d->prg_cpu[i] = pci_pool_alloc(d->prg_pool, SLAB_KERNEL, d->prg_bus+i);
+		OHCI_DMA_ALLOC("pool dma_trm prg[%d]", i);
 
                 if (d->prg_cpu[i] != NULL) {
                         memset(d->prg_cpu[i], 0, sizeof(struct at_dma_prg));
@@ -1780,17 +2987,26 @@
 
 	/* initialize tasklet */
 	if (type == DMA_CTX_ISO) {
-		ohci1394_init_iso_tasklet(&ohci->it_tasklet, OHCI_ISO_TRANSMIT,
+		ohci1394_init_iso_tasklet(&ohci->it_legacy_tasklet, OHCI_ISO_TRANSMIT,
 					  dma_trm_tasklet, (unsigned long) d);
 		if (ohci1394_register_iso_tasklet(ohci,
-						  &ohci->it_tasklet) < 0) {
+						  &ohci->it_legacy_tasklet) < 0) {
 			PRINT(KERN_ERR, ohci->id, "No IT DMA context available");
 			free_dma_trm_ctx(d);
 			return -EBUSY;
 		}
-	}
-	else
+
+		/* IT can be assigned to any context by register_iso_tasklet */
+		d->ctx = ohci->it_legacy_tasklet.context;
+		d->ctrlSet = OHCI1394_IsoXmitContextControlSet + 16 * d->ctx;
+		d->ctrlClear = OHCI1394_IsoXmitContextControlClear + 16 * d->ctx;
+		d->cmdPtr = OHCI1394_IsoXmitCommandPtr + 16 * d->ctx;
+	} else {
+		d->ctrlSet = context_base + OHCI1394_ContextControlSet;
+		d->ctrlClear = context_base + OHCI1394_ContextControlClear;
+		d->cmdPtr = context_base + OHCI1394_ContextCommandPtr;
 		tasklet_init (&d->task, dma_trm_tasklet, (unsigned long)d);
+	}
 
 	return 0;
 }
@@ -1969,6 +3185,7 @@
 	.get_rom =		ohci_get_rom,
 	.transmit_packet =	ohci_transmit,
 	.devctl =		ohci_devctl,
+	.isoctl =               ohci_isoctl,
 	.hw_csr_reg =		ohci_hw_csr_reg,
 };
 
@@ -1994,7 +3211,7 @@
 	struct hpsb_host *host;
 	struct ti_ohci *ohci;	/* shortcut to currently handled device */
 	unsigned long ohci_base;
-	
+
 	if (version_printed++ == 0)
 		PRINT_G(KERN_INFO, "%s", version);
 
@@ -2070,7 +3287,7 @@
 		pci_alloc_consistent(ohci->dev, OHCI1394_SI_DMA_BUF_SIZE,
                       &ohci->selfid_buf_bus);
 	OHCI_DMA_ALLOC("consistent selfid_buf");
-	
+
 	if (ohci->selfid_buf_cpu == NULL)
 		FAIL(-ENOMEM, "Failed to allocate DMA buffer for self-id packets");
 	ohci->init_state = OHCI_INIT_HAVE_SELFID_BUFFER;
@@ -2143,19 +3360,11 @@
 	ohci->ISO_channel_usage = 0;
         spin_lock_init(&ohci->IR_channel_lock);
 
-	/* IR DMA context */
-	if (alloc_dma_rcv_ctx(ohci, &ohci->ir_context,
-			      DMA_CTX_ISO, 0, IR_NUM_DESC,
-			      IR_BUF_SIZE, IR_SPLIT_BUF_SIZE,
-			      OHCI1394_IsoRcvContextBase) < 0)
-		FAIL(-ENOMEM, "Failed to allocate IR context");
+	/* the IR DMA context is allocated on-demand; mark it inactive */
+	ohci->ir_legacy_context.ohci = NULL;
 
-	
-	/* IT DMA context allocation */
-	if (alloc_dma_trm_ctx(ohci, &ohci->it_context,
-			      DMA_CTX_ISO, 0, IT_NUM_DESC,
-			      OHCI1394_IsoXmitContextBase) < 0)
-		FAIL(-ENOMEM, "Failed to allocate IT context");
+	/* same for the IT DMA context */
+	ohci->it_legacy_context.ohci = NULL;
 
 	if (request_irq(dev->irq, ohci_irq_handler, SA_SHIRQ,
 			 OHCI1394_DRIVER_NAME, ohci))
@@ -2200,11 +3409,11 @@
 		free_dma_trm_ctx(&ohci->at_resp_context);
 
 		/* Free IR dma */
-		free_dma_rcv_ctx(&ohci->ir_context);
-		
+		free_dma_rcv_ctx(&ohci->ir_legacy_context);
+
 		/* Free IT dma */
-		free_dma_trm_ctx(&ohci->it_context);
-	
+		free_dma_trm_ctx(&ohci->it_legacy_context);
+
 	case OHCI_INIT_HAVE_SELFID_BUFFER:
 		pci_free_consistent(ohci->dev, OHCI1394_SI_DMA_BUF_SIZE, 
 				    ohci->selfid_buf_cpu,
@@ -2252,10 +3461,10 @@
 
 static struct pci_device_id ohci1394_pci_tbl[] __devinitdata = {
 	{
-		.class = 		PCI_CLASS_FIREWIRE_OHCI,
-		.class_mask = 	0x00ffffff,
-		.vendor =		PCI_ANY_ID,
-		.device =		PCI_ANY_ID,
+		.class = 	PCI_CLASS_FIREWIRE_OHCI,
+		.class_mask = 	~0,
+		.vendor =	PCI_ANY_ID,
+		.device =	PCI_ANY_ID,
 		.subvendor =	PCI_ANY_ID,
 		.subdevice =	PCI_ANY_ID,
 	},
@@ -2280,7 +3489,7 @@
 /* essentially the only purpose of this code is to allow another
    module to hook into ohci's interrupt handler */
 
-void ohci1394_stop_context(struct ti_ohci *ohci, int reg, char *msg)
+int ohci1394_stop_context(struct ti_ohci *ohci, int reg, char *msg)
 {
 	int i=0;
 
@@ -2292,11 +3501,15 @@
 		i++;
 		if (i>5000) {
 			PRINT(KERN_ERR, ohci->id, 
-			      "Runaway loop while stopping context...");
-			break;
+			      "Runaway loop while stopping context: %s...", msg ? msg : "");
+			return 1;
 		}
+
+		mb();
+		udelay(10);
 	}
 	if (msg) PRINT(KERN_ERR, ohci->id, "%s: dma prg stopped", msg);
+	return 0;
 }
 
 void ohci1394_init_iso_tasklet(struct ohci1394_iso_tasklet *tasklet, int type,
@@ -2305,7 +3518,7 @@
 	tasklet_init(&tasklet->tasklet, func, data);
 	tasklet->type = type;
 	/* We init the tasklet->link field, so we can list_del() it
-	 * without worrying wether it was added to the list or not. */
+	 * without worrying whether it was added to the list or not. */
 	INIT_LIST_HEAD(&tasklet->link);
 }
 
@@ -2322,6 +3535,13 @@
 	else {
 		n = ohci->nb_iso_rcv_ctx;
 		usage = &ohci->ir_ctx_usage;
+
+		/* only one receive context can be multichannel (OHCI sec 10.4.1) */
+		if(tasklet->type == OHCI_ISO_MULTICHANNEL_RECEIVE) {
+			if(test_and_set_bit(0, &ohci->ir_multichannel_used)) {
+				return r;
+			}
+		}
 	}
 
 	spin_lock_irqsave(&ohci->iso_tasklet_list_lock, flags);
@@ -2335,7 +3555,7 @@
 		}
 
 	spin_unlock_irqrestore(&ohci->iso_tasklet_list_lock, flags);
-	
+
 	return r;
 }
 
@@ -2350,9 +3570,14 @@
 
 	if (tasklet->type == OHCI_ISO_TRANSMIT)
 		clear_bit(tasklet->context, &ohci->it_ctx_usage);
-	else
+	else {
 		clear_bit(tasklet->context, &ohci->ir_ctx_usage);
 
+		if(tasklet->type == OHCI_ISO_MULTICHANNEL_RECEIVE) {
+			clear_bit(0, &ohci->ir_multichannel_used);
+		}
+	}
+
 	list_del(&tasklet->link);
 
 	spin_unlock_irqrestore(&ohci->iso_tasklet_list_lock, flags);
diff -urN linux-2.4.21-pre5/drivers/ieee1394/ohci1394.h linux-2.4.21-pre6/drivers/ieee1394/ohci1394.h
--- linux-2.4.21-pre5/drivers/ieee1394/ohci1394.h	Wed Mar 26 16:11:28 2003
+++ linux-2.4.21-pre6/drivers/ieee1394/ohci1394.h	Wed Mar 26 16:11:36 2003
@@ -95,6 +95,7 @@
 	/* dma block descriptors */
         struct dma_cmd **prg_cpu;
         dma_addr_t *prg_bus;
+	struct pci_pool *prg_pool;
 
 	/* dma buffers */
         quadlet_t **buf_cpu;
@@ -108,6 +109,7 @@
 	int ctrlClear;
 	int ctrlSet;
 	int cmdPtr;
+	int ctxtMatch;
 };
 
 /* DMA transmit context */	
@@ -120,6 +122,7 @@
 	/* dma block descriptors */
         struct at_dma_prg **prg_cpu;
 	dma_addr_t *prg_bus;
+	struct pci_pool *prg_pool;
 
         unsigned int prg_ind;
         unsigned int sent_ind;
@@ -143,7 +146,8 @@
 	struct tasklet_struct tasklet;
 	struct list_head link;
 	int context;
-	enum { OHCI_ISO_TRANSMIT, OHCI_ISO_RECEIVE } type;
+	enum { OHCI_ISO_TRANSMIT, OHCI_ISO_RECEIVE,
+	       OHCI_ISO_MULTICHANNEL_RECEIVE } type;
 };
 
 struct ti_ohci {
@@ -168,7 +172,7 @@
 	/* dma buffer for self-id packets */
         quadlet_t *selfid_buf_cpu;
         dma_addr_t selfid_buf_bus;
-	
+
 	/* buffer for csr config rom */
         quadlet_t *csr_config_rom_cpu; 
         dma_addr_t csr_config_rom_bus; 
@@ -185,18 +189,28 @@
 	struct dma_trm_ctx at_req_context;
 
         /* iso receive */
-	struct dma_rcv_ctx ir_context;
-	struct ohci1394_iso_tasklet ir_tasklet;
-        spinlock_t IR_channel_lock;
 	int nb_iso_rcv_ctx;
 	unsigned long ir_ctx_usage; /* use test_and_set_bit() for atomicity */
+	unsigned long ir_multichannel_used; /* ditto */
+        spinlock_t IR_channel_lock;
+
+	/* iso receive (legacy API) */
+	u64 ir_legacy_channels; /* note: this differs from ISO_channel_usage;
+				   it only accounts for channels listened to
+				   by the legacy API, so that we can know when
+				   it is safe to free the legacy API context */
+
+	struct dma_rcv_ctx ir_legacy_context;
+	struct ohci1394_iso_tasklet ir_legacy_tasklet;
 	
         /* iso transmit */
-	struct dma_trm_ctx it_context;
-	struct ohci1394_iso_tasklet it_tasklet;
 	int nb_iso_xmit_ctx;
 	unsigned long it_ctx_usage; /* use test_and_set_bit() for atomicity */
-	
+
+	/* iso transmit (legacy API) */
+	struct dma_trm_ctx it_legacy_context;
+	struct ohci1394_iso_tasklet it_legacy_tasklet;
+
         u64 ISO_channel_usage;
 
         /* IEEE-1394 part follows */
@@ -211,10 +225,10 @@
 
 	/* Tasklets for iso receive and transmit, used by video1394,
 	 * amdtp and dv1394 */
-	
+
 	struct list_head iso_tasklet_list;
 	spinlock_t iso_tasklet_list_lock;
-	
+
 	/* Swap the selfid buffer? */
 	unsigned int selfid_swap:1;
 	/* Some Apple chipset seem to swap incoming headers for us */
@@ -292,6 +306,9 @@
 #define OHCI1394_IsoRecvIntEventClear         0x0A4
 #define OHCI1394_IsoRecvIntMaskSet            0x0A8
 #define OHCI1394_IsoRecvIntMaskClear          0x0AC
+#define OHCI1394_InitialBandwidthAvailable    0x0B0
+#define OHCI1394_InitialChannelsAvailableHi   0x0B4
+#define OHCI1394_InitialChannelsAvailableLo   0x0B8
 #define OHCI1394_FairnessControl              0x0DC
 #define OHCI1394_LinkControlSet               0x0E0
 #define OHCI1394_LinkControlClear             0x0E4
@@ -388,12 +405,12 @@
 					   truncated */
 #define EVT_OVERRUN		0x5	/* A recv FIFO overflowed on reception of ISO
 					   packet */
-#define EVT_DESCRIPTOR_READ	0x6	/* An unrecoverable error occured while host was
+#define EVT_DESCRIPTOR_READ	0x6	/* An unrecoverable error occurred while host was
 					   reading a descriptor block */
-#define EVT_DATA_READ		0x7	/* An error occured while host controller was
+#define EVT_DATA_READ		0x7	/* An error occurred while host controller was
 					   attempting to read from host memory in the data
 					   stage of descriptor processing */
-#define EVT_DATA_WRITE		0x8	/* An error occured while host controller was
+#define EVT_DATA_WRITE		0x8	/* An error occurred while host controller was
 					   attempting to write either during the data stage
 					   of descriptor processing, or when processing a single
 					   16-bit host memory write */
@@ -422,8 +439,8 @@
 void ohci1394_unregister_iso_tasklet(struct ti_ohci *ohci,
 				     struct ohci1394_iso_tasklet *tasklet);
 
-void ohci1394_stop_context      (struct ti_ohci *ohci, int reg, char *msg);
+/* returns zero if successful, one if DMA context is locked up */
+int ohci1394_stop_context      (struct ti_ohci *ohci, int reg, char *msg);
 struct ti_ohci *ohci1394_get_struct(int card_num);
 
 #endif
-
diff -urN linux-2.4.21-pre5/drivers/ieee1394/pcilynx.c linux-2.4.21-pre6/drivers/ieee1394/pcilynx.c
--- linux-2.4.21-pre5/drivers/ieee1394/pcilynx.c	Wed Mar 26 16:11:28 2003
+++ linux-2.4.21-pre6/drivers/ieee1394/pcilynx.c	Wed Mar 26 16:11:36 2003
@@ -19,6 +19,17 @@
  * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  */
 
+/*
+ * Contributions:
+ *
+ * Manfred Weihs <weihs@ict.tuwien.ac.at>
+ *        reading bus info block (containing GUID) from serial 
+ *            eeprom via i2c and storing it in config ROM
+ *        Reworked code for initiating bus resets
+ *            (long, short, with or without hold-off)
+ *        Enhancements in async and iso send code
+ */
+
 #include <linux/config.h>
 #include <linux/kernel.h>
 #include <linux/slab.h>
@@ -450,7 +461,7 @@
 
         if (host->in_bus_reset) return; /* in bus reset again */
 
-        if (isroot) reg_set_bits(lynx, LINK_CONTROL, LINK_CONTROL_CYCMASTER);
+        if (isroot) reg_set_bits(lynx, LINK_CONTROL, LINK_CONTROL_CYCMASTER); //FIXME: I do not think, we need this here
         reg_set_bits(lynx, LINK_CONTROL,
                      LINK_CONTROL_RCV_CMP_VALID | LINK_CONTROL_TX_ASYNC_EN
                      | LINK_CONTROL_RX_ASYNC_EN | LINK_CONTROL_CYCTIMEREN);
@@ -466,7 +477,14 @@
         struct hpsb_packet *packet;
 
         d = (what == hpsb_iso ? &lynx->iso_send : &lynx->async);
+        if (!list_empty(&d->pcl_queue)) {
+                PRINT(KERN_ERR, lynx->id, "trying to queue a new packet in nonempty fifo");
+                BUG();
+        }
+
         packet = driver_packet(d->queue.next);
+        list_del(&packet->driver_list);
+        list_add_tail(&packet->driver_list, &d->pcl_queue);
 
         d->header_dma = pci_map_single(lynx->dev, packet->header,
                                        packet->header_size, PCI_DMA_TODEVICE);
@@ -480,6 +498,7 @@
 
         pcl.next = PCL_NEXT_INVALID;
         pcl.async_error_next = PCL_NEXT_INVALID;
+        pcl.pcl_status = 0;
 #ifdef __BIG_ENDIAN
         pcl.buffer[0].control = packet->speed_code << 14 | packet->header_size;
 #else
@@ -546,7 +565,7 @@
         spin_lock_irqsave(&d->queue_lock, flags);
 
 	list_add_tail(&packet->driver_list, &d->queue);
-	if (d->queue.next == &packet->driver_list)
+	if (list_empty(&d->pcl_queue))
                 send_next(lynx, packet->type);
 
         spin_unlock_irqrestore(&d->queue_lock, flags);
@@ -563,6 +582,7 @@
         struct hpsb_packet *packet;
 	LIST_HEAD(packet_list);
         unsigned long flags;
+	int phy_reg;
 
         switch (cmd) {
         case RESET_BUS:
@@ -571,21 +591,47 @@
                         break;
                 }
 
-                if (arg) {
-                        arg = 3 << 6;
-                } else {
-                        arg = 1 << 6;
-                }
-
-                retval = get_phy_reg(lynx, 1);
-                arg |= (retval == -1 ? 63 : retval);
-                retval = 0;
-
-                PRINT(KERN_INFO, lynx->id, "resetting bus on request");
+		switch (arg) {
+		case SHORT_RESET:
+			if (lynx->phyic.reg_1394a) {
+				phy_reg = get_phy_reg(lynx, 5);
+				if (phy_reg == -1) {
+					PRINT(KERN_ERR, lynx->id, "cannot reset bus, because read phy reg failed");
+					retval = -1;
+					break;
+				}
+				phy_reg |= 0x40;
+
+				PRINT(KERN_INFO, lynx->id, "resetting bus (short bus reset) on request");
+
+				lynx->selfid_size = -1;
+				lynx->phy_reg0 = -1;
+				set_phy_reg(lynx, 5, phy_reg); /* set ISBR */
+				break;
+			} else {
+				PRINT(KERN_INFO, lynx->id, "cannot do short bus reset, because of old phy");
+				/* fall through to long bus reset */
+			}
+		case LONG_RESET:
+			phy_reg = get_phy_reg(lynx, 1);
+			if (phy_reg == -1) {
+				PRINT(KERN_ERR, lynx->id, "cannot reset bus, because read phy reg failed");
+				retval = -1;
+				break;
+			}
+			phy_reg |= 0x40;
+
+			PRINT(KERN_INFO, lynx->id, "resetting bus (long bus reset) on request");
+
+			lynx->selfid_size = -1;
+			lynx->phy_reg0 = -1;
+			set_phy_reg(lynx, 1, phy_reg); /* clear RHB, set IBR */
+			break;
+		default:
+			PRINT(KERN_ERR, lynx->id, "unknown argument for reset_bus command %d", arg);
+			retval = -1;
+		}
 
-                lynx->selfid_size = -1;
-                lynx->phy_reg0 = -1;
-                set_phy_reg(lynx, 1, arg);
                 break;
 
         case GET_CYCLE_COUNTER:
@@ -618,7 +664,44 @@
 		list_splice(&lynx->async.queue, &packet_list);
 		INIT_LIST_HEAD(&lynx->async.queue);
 
-                spin_unlock_irqrestore(&lynx->async.queue_lock, flags);
+                if (list_empty(&lynx->async.pcl_queue)) {
+                        spin_unlock_irqrestore(&lynx->async.queue_lock, flags);
+                        PRINTD(KERN_DEBUG, lynx->id, "no async packet in PCL to cancel");
+                } else {
+                        struct ti_pcl pcl;
+                        u32 ack;
+                        struct hpsb_packet *packet;
+
+                        PRINT(KERN_INFO, lynx->id, "cancelling async packet, that was already in PCL");
+
+                        get_pcl(lynx, lynx->async.pcl, &pcl);
+
+                        packet = driver_packet(lynx->async.pcl_queue.next);
+                        list_del(&packet->driver_list);
+
+                        pci_unmap_single(lynx->dev, lynx->async.header_dma,
+                                         packet->header_size, PCI_DMA_TODEVICE);
+                        if (packet->data_size) {
+                                pci_unmap_single(lynx->dev, lynx->async.data_dma,
+                                                 packet->data_size, PCI_DMA_TODEVICE);
+                        }
+
+                        spin_unlock_irqrestore(&lynx->async.queue_lock, flags);
+
+                        if (pcl.pcl_status & DMA_CHAN_STAT_PKTCMPL) {
+                                if (pcl.pcl_status & DMA_CHAN_STAT_SPECIALACK) {
+                                        ack = (pcl.pcl_status >> 15) & 0xf;
+                                        PRINTD(KERN_INFO, lynx->id, "special ack %d", ack);
+                                        ack = (ack == 1 ? ACKX_TIMEOUT : ACKX_SEND_ERROR);
+                                } else {
+                                        ack = (pcl.pcl_status >> 15) & 0xf;
+                                }
+                        } else {
+                                PRINT(KERN_INFO, lynx->id, "async packet was not completed");
+                                ack = ACKX_ABORTED;
+                        }
+                        hpsb_packet_sent(host, packet, ack);
+                }
 
 		while (!list_empty(&packet_list)) {
 			packet = driver_packet(packet_list.next);
@@ -709,7 +792,7 @@
 
 static int mem_open(struct inode *inode, struct file *file)
 {
-        int cid = minor(inode->i_rdev);
+        int cid = MINOR(inode->i_rdev);
         enum { t_rom, t_aux, t_ram } type;
         struct memdata *md;
         
@@ -1007,11 +1090,11 @@
         linkint = reg_read(lynx, LINK_INT_STATUS);
         intmask = reg_read(lynx, PCI_INT_STATUS);
 
+        if (!(intmask & PCI_INT_INT_PEND)) return;
+
         PRINTD(KERN_DEBUG, lynx->id, "interrupt: 0x%08x / 0x%08x", intmask,
                linkint);
 
-        if (!(intmask & PCI_INT_INT_PEND)) return;
-
         reg_write(lynx, LINK_INT_STATUS, linkint);
         reg_write(lynx, PCI_INT_STATUS, intmask);
 
@@ -1110,62 +1193,96 @@
         }
 
         if (intmask & PCI_INT_DMA_HLT(CHANNEL_ASYNC_SEND)) {
-                u32 ack;
-                struct hpsb_packet *packet;
-                
+                PRINTD(KERN_DEBUG, lynx->id, "async sent");
                 spin_lock(&lynx->async.queue_lock);
 
-                ack = reg_read(lynx, DMA_CHAN_STAT(CHANNEL_ASYNC_SEND));
-
-		packet = driver_packet(lynx->async.queue.next);
-		list_del(&packet->driver_list);
-
-                pci_unmap_single(lynx->dev, lynx->async.header_dma,
-                                 packet->header_size, PCI_DMA_TODEVICE);
-                if (packet->data_size) {
-                        pci_unmap_single(lynx->dev, lynx->async.data_dma,
-                                         packet->data_size, PCI_DMA_TODEVICE);
-                }
+                if (list_empty(&lynx->async.pcl_queue)) {
+                        spin_unlock(&lynx->async.queue_lock);
+                        PRINT(KERN_WARNING, lynx->id, "async dma halted, but no queued packet (maybe it was cancelled)");
+                } else {
+                        struct ti_pcl pcl;
+                        u32 ack;
+                        struct hpsb_packet *packet;
+
+                        get_pcl(lynx, lynx->async.pcl, &pcl);
+
+                        packet = driver_packet(lynx->async.pcl_queue.next);
+                        list_del(&packet->driver_list);
+
+                        pci_unmap_single(lynx->dev, lynx->async.header_dma,
+                                         packet->header_size, PCI_DMA_TODEVICE);
+                        if (packet->data_size) {
+                                pci_unmap_single(lynx->dev, lynx->async.data_dma,
+                                                 packet->data_size, PCI_DMA_TODEVICE);
+                        }
 
-                if (!list_empty(&lynx->async.queue)) {
-                        send_next(lynx, hpsb_async);
-                }
+                        if (!list_empty(&lynx->async.queue)) {
+                                send_next(lynx, hpsb_async);
+                        }
 
-                spin_unlock(&lynx->async.queue_lock);
+                        spin_unlock(&lynx->async.queue_lock);
 
-                if (ack & DMA_CHAN_STAT_SPECIALACK) {
-                        ack = (ack >> 15) & 0xf;
-                        PRINTD(KERN_INFO, lynx->id, "special ack %d", ack);
-                        ack = (ack == 1 ? ACKX_TIMEOUT : ACKX_SEND_ERROR);
-                } else {
-                        ack = (ack >> 15) & 0xf;
+                        if (pcl.pcl_status & DMA_CHAN_STAT_PKTCMPL) {
+                                if (pcl.pcl_status & DMA_CHAN_STAT_SPECIALACK) {
+                                        ack = (pcl.pcl_status >> 15) & 0xf;
+                                        PRINTD(KERN_INFO, lynx->id, "special ack %d", ack);
+                                        ack = (ack == 1 ? ACKX_TIMEOUT : ACKX_SEND_ERROR);
+                                } else {
+                                        ack = (pcl.pcl_status >> 15) & 0xf;
+                                }
+                        } else {
+                                PRINT(KERN_INFO, lynx->id, "async packet was not completed");
+                                ack = ACKX_SEND_ERROR;
+                        }
+                        hpsb_packet_sent(host, packet, ack);
                 }
-                
-                hpsb_packet_sent(host, packet, ack);
         }
 
         if (intmask & PCI_INT_DMA_HLT(CHANNEL_ISO_SEND)) {
-                struct hpsb_packet *packet;
-
+                PRINTD(KERN_DEBUG, lynx->id, "iso sent");
                 spin_lock(&lynx->iso_send.queue_lock);
 
-		packet = driver_packet(lynx->iso_send.queue.next);
-		list_del(&packet->driver_list);
+                if (list_empty(&lynx->iso_send.pcl_queue)) {
+                        spin_unlock(&lynx->iso_send.queue_lock);
+                        PRINT(KERN_ERR, lynx->id, "iso send dma halted, but no queued packet");
+                } else {
+                        struct ti_pcl pcl;
+                        u32 ack;
+                        struct hpsb_packet *packet;
+
+                        get_pcl(lynx, lynx->iso_send.pcl, &pcl);
+
+                        packet = driver_packet(lynx->iso_send.pcl_queue.next);
+                        list_del(&packet->driver_list);
+
+                        pci_unmap_single(lynx->dev, lynx->iso_send.header_dma,
+                                         packet->header_size, PCI_DMA_TODEVICE);
+                        if (packet->data_size) {
+                                pci_unmap_single(lynx->dev, lynx->iso_send.data_dma,
+                                                 packet->data_size, PCI_DMA_TODEVICE);
+                        }
 
-                pci_unmap_single(lynx->dev, lynx->iso_send.header_dma,
-                                 packet->header_size, PCI_DMA_TODEVICE);
-                if (packet->data_size) {
-                        pci_unmap_single(lynx->dev, lynx->iso_send.data_dma,
-                                         packet->data_size, PCI_DMA_TODEVICE);
-                }
+                        if (!list_empty(&lynx->iso_send.queue)) {
+                                send_next(lynx, hpsb_iso);
+                        }
 
-                if (!list_empty(&lynx->iso_send.queue)) {
-                        send_next(lynx, hpsb_iso);
-                }
+                        spin_unlock(&lynx->iso_send.queue_lock);
 
-                spin_unlock(&lynx->iso_send.queue_lock);
+                        if (pcl.pcl_status & DMA_CHAN_STAT_PKTCMPL) {
+                                if (pcl.pcl_status & DMA_CHAN_STAT_SPECIALACK) {
+                                        ack = (pcl.pcl_status >> 15) & 0xf;
+                                        PRINTD(KERN_INFO, lynx->id, "special ack %d", ack);
+                                        ack = (ack == 1 ? ACKX_TIMEOUT : ACKX_SEND_ERROR);
+                                } else {
+                                        ack = (pcl.pcl_status >> 15) & 0xf;
+                                }
+                        } else {
+                                PRINT(KERN_INFO, lynx->id, "iso send packet was not completed");
+                                ack = ACKX_SEND_ERROR;
+                        }
 
-                hpsb_packet_sent(host, packet, ACK_COMPLETE);
+                        hpsb_packet_sent(host, packet, ack); //FIXME: maybe we should just use ACK_COMPLETE and ACKX_SEND_ERROR
+                }
         }
 
         if (intmask & PCI_INT_DMA_HLT(CHANNEL_ASYNC_RCV)) {
@@ -1467,7 +1584,9 @@
         lynx->phy_reg0 = -1;
 
 	INIT_LIST_HEAD(&lynx->async.queue);
+	INIT_LIST_HEAD(&lynx->async.pcl_queue);
 	INIT_LIST_HEAD(&lynx->iso_send.queue);
+	INIT_LIST_HEAD(&lynx->iso_send.pcl_queue);
 
         pcl.next = pcl_bus(lynx, lynx->rcv_pcl);
         put_pcl(lynx, lynx->rcv_pcl_start, &pcl);
@@ -1690,7 +1809,7 @@
         .name =     PCILYNX_DRIVER_NAME,
         .id_table = pci_table,
         .probe =    add_card,
-        .remove =   __devexit_p(remove_card),
+        .remove =   remove_card,
 };
 
 static struct hpsb_host_driver lynx_driver = {
diff -urN linux-2.4.21-pre5/drivers/ieee1394/pcilynx.h linux-2.4.21-pre6/drivers/ieee1394/pcilynx.h
--- linux-2.4.21-pre5/drivers/ieee1394/pcilynx.h	Thu Nov 28 15:53:13 2002
+++ linux-2.4.21-pre6/drivers/ieee1394/pcilynx.h	Wed Mar 26 16:11:36 2003
@@ -95,6 +95,7 @@
         struct lynx_send_data {
                 pcl_t pcl_start, pcl;
                 struct list_head queue;
+                struct list_head pcl_queue; /* this queue contains at most one packet */
                 spinlock_t queue_lock;
                 dma_addr_t header_dma, data_dma;
                 int channel;
@@ -514,13 +515,13 @@
 
 static quadlet_t lynx_csr_rom[] = {
 /* bus info block     offset (hex) */
-        _(0x04040000), /* info/CRC length, CRC              400  */
+        _(0x04046aaf), /* info/CRC length, CRC              400  */
         _(0x31333934), /* 1394 magic number                 404  */
         _(0xf064a000), /* misc. settings                    408  */
         _(0x08002850), /* vendor ID, chip ID high           40c  */
         _(0x0000ffff), /* chip ID low                       410  */
 /* root directory */
-        _(0x00090000), /* directory length, CRC             414  */
+        _(0x00095778), /* directory length, CRC             414  */
         _(0x03080028), /* vendor ID (Texas Instr.)          418  */
         _(0x81000008), /* offset to textual ID              41c  */
         _(0x0c000200), /* node capabilities                 420  */
@@ -530,8 +531,8 @@
         _(0x81000014), /* offset to textual ID              430  */
         _(0x09000000), /* node hardware version             434  */
         _(0x81000018), /* offset to textual ID              438  */
-  /* module vendor ID textual */
-        _(0x00070000), /* CRC length, CRC                   43c  */
+/* module vendor ID textual */
+        _(0x00070812), /* CRC length, CRC                   43c  */
         _(0x00000000), /*                                   440  */
         _(0x00000000), /*                                   444  */
         _(0x54455841), /* "Texas Instruments"               448  */
@@ -540,25 +541,25 @@
         _(0x4d454e54), /*                                   454  */
         _(0x53000000), /*                                   458  */
 /* node unique ID leaf */
-        _(0x00020000), /* CRC length, CRC                   45c  */
+        _(0x00022ead), /* CRC length, CRC                   45c  */
         _(0x08002850), /* vendor ID, chip ID high           460  */
         _(0x0000ffff), /* chip ID low                       464  */
 /* module dependent info */
-        _(0x00050000), /* CRC length, CRC                   468  */
+        _(0x0005d837), /* CRC length, CRC                   468  */
         _(0x81000012), /* offset to module textual ID       46c  */
         _(0x81000017), /* textual descriptor                470  */
         _(0x39010000), /* SRAM size                         474  */
         _(0x3a010000), /* AUXRAM size                       478  */
         _(0x3b000000), /* AUX device                        47c  */
 /* module textual ID */
-        _(0x00050000), /* CRC length, CRC                   480  */
+        _(0x000594df), /* CRC length, CRC                   480  */
         _(0x00000000), /*                                   484  */
         _(0x00000000), /*                                   488  */
         _(0x54534231), /* "TSB12LV21"                       48c  */
         _(0x324c5632), /*                                   490  */
         _(0x31000000), /*                                   494  */
 /* part number */
-        _(0x00060000), /* CRC length, CRC                   498  */
+        _(0x00068405), /* CRC length, CRC                   498  */
         _(0x00000000), /*                                   49c  */
         _(0x00000000), /*                                   4a0  */
         _(0x39383036), /* "9806000-0001"                    4a4  */
@@ -566,14 +567,14 @@
         _(0x30303031), /*                                   4ac  */
         _(0x20000001), /*                                   4b0  */
 /* module hardware version textual */
-        _(0x00050000), /* CRC length, CRC                   4b4  */
+        _(0x00056501), /* CRC length, CRC                   4b4  */
         _(0x00000000), /*                                   4b8  */
         _(0x00000000), /*                                   4bc  */
         _(0x5453424b), /* "TSBKPCITST"                      4c0  */
         _(0x50434954), /*                                   4c4  */
         _(0x53540000), /*                                   4c8  */
 /* node hardware version textual */
-        _(0x00050000), /* CRC length, CRC                   4d0  */
+        _(0x0005d805), /* CRC length, CRC                   4d0  */
         _(0x00000000), /*                                   4d4  */
         _(0x00000000), /*                                   4d8  */
         _(0x54534232), /* "TSB21LV03"                       4dc  */
diff -urN linux-2.4.21-pre5/drivers/ieee1394/raw1394-private.h linux-2.4.21-pre6/drivers/ieee1394/raw1394-private.h
--- linux-2.4.21-pre5/drivers/ieee1394/raw1394-private.h	Wed Dec 31 16:00:00 1969
+++ linux-2.4.21-pre6/drivers/ieee1394/raw1394-private.h	Wed Mar 26 16:11:36 2003
@@ -0,0 +1,79 @@
+#ifndef IEEE1394_RAW1394_PRIVATE_H
+#define IEEE1394_RAW1394_PRIVATE_H
+
+/* header for definitions that are private to the raw1394 driver
+   and not visible to user-space */
+
+#define RAW1394_DEVICE_MAJOR      171
+#define RAW1394_DEVICE_NAME       "raw1394"
+
+struct iso_block_store {
+        atomic_t refcount;
+        size_t data_size;
+        quadlet_t data[0];
+};
+
+enum raw1394_iso_state { RAW1394_ISO_INACTIVE = 0,
+			 RAW1394_ISO_RECV = 1,
+			 RAW1394_ISO_XMIT = 2 };
+
+struct file_info {
+        struct list_head list;
+
+        enum { opened, initialized, connected } state;
+        unsigned int protocol_version;
+
+        struct hpsb_host *host;
+
+        struct list_head req_pending;
+        struct list_head req_complete;
+        struct semaphore complete_sem;
+        spinlock_t reqlists_lock;
+        wait_queue_head_t poll_wait_complete;
+
+        struct list_head addr_list;           
+
+        u8 *fcp_buffer;
+
+	/* old ISO API */
+        u64 listen_channels;
+        quadlet_t *iso_buffer;
+        size_t iso_buffer_length;
+
+        u8 notification; /* (busreset-notification) RAW1394_NOTIFY_OFF/ON */
+
+	/* new rawiso API */
+	enum raw1394_iso_state iso_state;
+	struct hpsb_iso *iso_handle;
+};
+
+struct arm_addr {
+        struct list_head addr_list; /* file_info list */
+        u64    start, end;
+        u64    arm_tag;
+        u8     access_rights;
+        u8     notification_options;
+        u8     client_transactions;
+        u64    recvb;
+        u16    rec_length;
+        u8     *addr_space_buffer; /* accessed by read/write/lock */
+};
+
+struct pending_request {
+        struct list_head list;
+        struct file_info *file_info;
+        struct hpsb_packet *packet;
+	struct tq_struct tq;
+        struct iso_block_store *ibs;
+        quadlet_t *data;
+        int free_data;
+        struct raw1394_request req;
+};
+
+struct host_info {
+        struct list_head list;
+        struct hpsb_host *host;
+        struct list_head file_info_list;
+};
+
+#endif  /* IEEE1394_RAW1394_PRIVATE_H */
diff -urN linux-2.4.21-pre5/drivers/ieee1394/raw1394.c linux-2.4.21-pre6/drivers/ieee1394/raw1394.c
--- linux-2.4.21-pre5/drivers/ieee1394/raw1394.c	Wed Mar 26 16:11:28 2003
+++ linux-2.4.21-pre6/drivers/ieee1394/raw1394.c	Wed Mar 26 16:11:36 2003
@@ -46,9 +46,10 @@
 #include "ieee1394_core.h"
 #include "hosts.h"
 #include "highlevel.h"
+#include "iso.h"
 #include "ieee1394_transactions.h"
 #include "raw1394.h"
-
+#include "raw1394-private.h"
 
 #if BITS_PER_LONG == 64
 #define int2ptr(x) ((void *)x)
@@ -90,10 +91,10 @@
 static int arm_lock64 (struct hpsb_host *host, int nodeid, octlet_t *store,
                u64 addr, octlet_t data, octlet_t arg, int ext_tcode, u16 flags);
 static struct hpsb_address_ops arm_ops = {
-        read:     arm_read,
-        write:    arm_write,
-        lock:     arm_lock,
-        lock64:   arm_lock64,
+	.read	= arm_read,
+	.write	= arm_write,
+	.lock	= arm_lock,
+	.lock64	= arm_lock64,
 };
 
 static void queue_complete_cb(struct pending_request *req);
@@ -107,7 +108,7 @@
         if (req != NULL) {
                 memset(req, 0, sizeof(struct pending_request));
                 INIT_LIST_HEAD(&req->list);
-                HPSB_INIT_WORK(&req->tq, (void(*)(void*))queue_complete_cb, NULL);
+                INIT_TQUEUE(&req->tq, (void(*)(void*))queue_complete_cb, NULL);
         }
 
         return req;
@@ -132,18 +133,25 @@
         kfree(req);
 }
 
+/* fi->reqlists_lock must be taken */
+static void __queue_complete_req(struct pending_request *req)
+{
+	struct file_info *fi = req->file_info;
+	list_del(&req->list);
+        list_add_tail(&req->list, &fi->req_complete);
+
+	up(&fi->complete_sem);
+        wake_up_interruptible(&fi->poll_wait_complete);
+}
+
 static void queue_complete_req(struct pending_request *req)
 {
         unsigned long flags;
         struct file_info *fi = req->file_info;
 
         spin_lock_irqsave(&fi->reqlists_lock, flags);
-        list_del(&req->list);
-        list_add_tail(&req->list, &fi->req_complete);
+	__queue_complete_req(req);
         spin_unlock_irqrestore(&fi->reqlists_lock, flags);
-
-        up(&fi->complete_sem);
-        wake_up_interruptible(&fi->poll_wait_complete);
 }
 
 static void queue_complete_cb(struct pending_request *req)
@@ -797,7 +805,7 @@
                 return sizeof(struct raw1394_request);
         }
 
-	HPSB_PREPARE_WORK(&req->tq, (void (*)(void*))queue_complete_req, req);
+	PREPARE_TQUEUE(&req->tq, (void (*)(void*))queue_complete_req, req);
         req->req.length = 0;
 	hpsb_add_packet_complete_task(packet, &req->tq);
 
@@ -860,7 +868,7 @@
         packet->header_size=header_length;
         packet->data_size=req->req.length-header_length;
 
-	HPSB_PREPARE_WORK(&req->tq, (void (*)(void*))queue_complete_req, req);
+	PREPARE_TQUEUE(&req->tq, (void (*)(void*))queue_complete_req, req);
         req->req.length = 0;
         hpsb_add_packet_complete_task(packet, &req->tq);
 
@@ -2000,6 +2008,323 @@
         return retval;
 }
 
+/* rawiso operations */
+
+/* check if any RAW1394_REQ_RAWISO_ACTIVITY event is already in the
+ * completion queue (reqlists_lock must be taken) */
+static inline int __rawiso_event_in_queue(struct file_info *fi)
+{
+	struct list_head *lh;
+	struct pending_request *req;
+
+	list_for_each(lh, &fi->req_complete) {
+		req = list_entry(lh, struct pending_request, list);
+		if(req->req.type == RAW1394_REQ_RAWISO_ACTIVITY) {
+			return 1;
+		}
+	}
+
+	return 0;
+}
+
+/* put a RAWISO_ACTIVITY event in the queue, if one isn't there already */
+static void queue_rawiso_event(struct file_info *fi)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&fi->reqlists_lock, flags);
+
+	/* only one ISO activity event may be in the queue */
+	if(!__rawiso_event_in_queue(fi)) {
+		struct pending_request *req = __alloc_pending_request(SLAB_ATOMIC);
+
+		if(req) {
+			req->file_info = fi;
+			req->req.type = RAW1394_REQ_RAWISO_ACTIVITY;
+			req->req.generation = get_hpsb_generation(fi->host);
+			__queue_complete_req(req);
+		} else {
+			/* on allocation failure, signal an overflow */
+			if(fi->iso_handle) {
+				atomic_inc(&fi->iso_handle->overflows);
+			}
+		}
+	}
+	spin_unlock_irqrestore(&fi->reqlists_lock, flags);
+}
+
+static void rawiso_activity_cb(struct hpsb_iso *iso)
+{
+	unsigned long flags;
+        struct list_head *lh;
+        struct host_info *hi;
+
+        spin_lock_irqsave(&host_info_lock, flags);
+        hi = find_host_info(iso->host);
+
+	if (hi != NULL) {
+		list_for_each(lh, &hi->file_info_list) {
+			struct file_info *fi = list_entry(lh, struct file_info, list);
+			if(fi->iso_handle == iso)
+				queue_rawiso_event(fi);
+		}
+	}
+
+	spin_unlock_irqrestore(&host_info_lock, flags);
+}
+
+/* helper function - gather all the kernel iso status bits for returning to user-space */
+static void raw1394_iso_fill_status(struct hpsb_iso *iso, struct raw1394_iso_status *stat)
+{
+	stat->config.data_buf_size = iso->buf_size;
+	stat->config.buf_packets = iso->buf_packets;
+	stat->config.channel = iso->channel;
+	stat->config.speed = iso->speed;
+	stat->config.irq_interval = iso->irq_interval;
+	stat->n_packets = hpsb_iso_n_ready(iso);
+	stat->overflows = atomic_read(&iso->overflows);
+	stat->xmit_cycle = iso->xmit_cycle;
+}
+
+static int raw1394_iso_xmit_init(struct file_info *fi, void *uaddr)
+{
+	struct raw1394_iso_status stat;
+
+	if(copy_from_user(&stat, uaddr, sizeof(stat)))
+		return -EFAULT;
+
+	fi->iso_handle = hpsb_iso_xmit_init(fi->host,
+					    stat.config.data_buf_size,
+					    stat.config.buf_packets,
+					    stat.config.channel,
+					    stat.config.speed,
+					    stat.config.irq_interval,
+					    rawiso_activity_cb);
+	if(!fi->iso_handle)
+		return -ENOMEM;
+
+	fi->iso_state = RAW1394_ISO_XMIT;
+
+	raw1394_iso_fill_status(fi->iso_handle, &stat);
+	if(copy_to_user(uaddr, &stat, sizeof(stat)))
+		return -EFAULT;
+
+	/* queue an event to get things started */
+	rawiso_activity_cb(fi->iso_handle);
+
+	return 0;
+}
+
+static int raw1394_iso_recv_init(struct file_info *fi, void *uaddr)
+{
+	struct raw1394_iso_status stat;
+
+	if(copy_from_user(&stat, uaddr, sizeof(stat)))
+		return -EFAULT;
+
+	fi->iso_handle = hpsb_iso_recv_init(fi->host,
+					    stat.config.data_buf_size,
+					    stat.config.buf_packets,
+					    stat.config.channel,
+					    stat.config.irq_interval,
+					    rawiso_activity_cb);
+	if(!fi->iso_handle)
+		return -ENOMEM;
+
+	fi->iso_state = RAW1394_ISO_RECV;
+
+	raw1394_iso_fill_status(fi->iso_handle, &stat);
+	if(copy_to_user(uaddr, &stat, sizeof(stat)))
+		return -EFAULT;
+	return 0;
+}
+
+static int raw1394_iso_get_status(struct file_info *fi, void *uaddr)
+{
+	struct raw1394_iso_status stat;
+	struct hpsb_iso *iso = fi->iso_handle;
+
+	raw1394_iso_fill_status(fi->iso_handle, &stat);
+	if(copy_to_user(uaddr, &stat, sizeof(stat)))
+		return -EFAULT;
+
+	/* reset overflow counter */
+	atomic_set(&iso->overflows, 0);
+
+	return 0;
+}
+
+/* copy N packet_infos out of the ringbuffer into user-supplied array */
+static int raw1394_iso_recv_packets(struct file_info *fi, void *uaddr)
+{
+	struct raw1394_iso_packets upackets;
+	unsigned int packet = fi->iso_handle->first_packet;
+	int i;
+
+	if(copy_from_user(&upackets, uaddr, sizeof(upackets)))
+		return -EFAULT;
+
+	if(upackets.n_packets > hpsb_iso_n_ready(fi->iso_handle))
+		return -EINVAL;
+
+	/* ensure user-supplied buffer is accessible and big enough */
+	if(verify_area(VERIFY_WRITE, upackets.infos,
+		       upackets.n_packets * sizeof(struct raw1394_iso_packet_info)))
+		return -EFAULT;
+
+	/* copy the packet_infos out */
+	for(i = 0; i < upackets.n_packets; i++) {
+		if(__copy_to_user(&upackets.infos[i],
+				  &fi->iso_handle->infos[packet],
+				  sizeof(struct raw1394_iso_packet_info)))
+			return -EFAULT;
+		
+		packet = (packet + 1) % fi->iso_handle->buf_packets;
+	}
+
+	return 0;
+}
+
+/* copy N packet_infos from user to ringbuffer, and queue them for transmission */
+static int raw1394_iso_send_packets(struct file_info *fi, void *uaddr)
+{
+	struct raw1394_iso_packets upackets;
+	int i, rv;
+
+	if(copy_from_user(&upackets, uaddr, sizeof(upackets)))
+		return -EFAULT;
+
+	if(upackets.n_packets > hpsb_iso_n_ready(fi->iso_handle))
+		return -EINVAL;
+
+	/* ensure user-supplied buffer is accessible and big enough */
+	if(verify_area(VERIFY_READ, upackets.infos,
+		       upackets.n_packets * sizeof(struct raw1394_iso_packet_info)))
+		return -EFAULT;
+
+	/* copy the infos structs in and queue the packets */
+	for(i = 0; i < upackets.n_packets; i++) {
+		struct raw1394_iso_packet_info info;
+
+		if(__copy_from_user(&info, &upackets.infos[i],
+				    sizeof(struct raw1394_iso_packet_info)))
+			return -EFAULT;
+
+		rv = hpsb_iso_xmit_queue_packet(fi->iso_handle, info.offset,
+						info.len, info.tag, info.sy);
+		if(rv)
+			return rv;
+	}
+
+	return 0;
+}
+
+static void raw1394_iso_shutdown(struct file_info *fi)
+{
+	if(fi->iso_handle)
+		hpsb_iso_shutdown(fi->iso_handle);
+
+	fi->iso_handle = NULL;
+	fi->iso_state = RAW1394_ISO_INACTIVE;
+}
+
+/* mmap the rawiso xmit/recv buffer */
+static int raw1394_mmap(struct file *file, struct vm_area_struct *vma)
+{
+	struct file_info *fi = file->private_data;
+
+	if(fi->iso_state == RAW1394_ISO_INACTIVE)
+		return -EINVAL;
+
+	return dma_region_mmap(&fi->iso_handle->data_buf, file, vma);
+}
+
+/* ioctl is only used for rawiso operations */
+static int raw1394_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+{
+	struct file_info *fi = file->private_data;
+
+	switch(fi->iso_state) {
+	case RAW1394_ISO_INACTIVE:
+		switch(cmd) {
+		case RAW1394_IOC_ISO_XMIT_INIT:
+			return raw1394_iso_xmit_init(fi, (void*) arg);
+		case RAW1394_IOC_ISO_RECV_INIT:
+			return raw1394_iso_recv_init(fi, (void*) arg);
+		default:
+			break;
+		}
+		break;
+	case RAW1394_ISO_RECV:
+		switch(cmd) {
+		case RAW1394_IOC_ISO_RECV_START: {
+			/* copy args from user-space */
+			int args[3];
+			if(copy_from_user(&args[0], (void*) arg, sizeof(args)))
+				return -EFAULT;
+			return hpsb_iso_recv_start(fi->iso_handle, args[0], args[1], args[2]);
+		}
+		case RAW1394_IOC_ISO_XMIT_RECV_STOP:
+			hpsb_iso_stop(fi->iso_handle);
+			return 0;
+		case RAW1394_IOC_ISO_RECV_LISTEN_CHANNEL:
+			return hpsb_iso_recv_listen_channel(fi->iso_handle, arg);
+		case RAW1394_IOC_ISO_RECV_UNLISTEN_CHANNEL:
+			return hpsb_iso_recv_unlisten_channel(fi->iso_handle, arg);
+		case RAW1394_IOC_ISO_RECV_SET_CHANNEL_MASK: {
+			/* copy the u64 from user-space */
+			u64 mask;
+			if(copy_from_user(&mask, (void*) arg, sizeof(mask)))
+				return -EFAULT;
+			return hpsb_iso_recv_set_channel_mask(fi->iso_handle, mask);
+		}
+		case RAW1394_IOC_ISO_GET_STATUS:
+			return raw1394_iso_get_status(fi, (void*) arg);
+		case RAW1394_IOC_ISO_RECV_PACKETS:
+			return raw1394_iso_recv_packets(fi, (void*) arg);
+		case RAW1394_IOC_ISO_RECV_RELEASE_PACKETS:
+			return hpsb_iso_recv_release_packets(fi->iso_handle, arg);
+		case RAW1394_IOC_ISO_SHUTDOWN:
+			raw1394_iso_shutdown(fi);
+			return 0;
+		case RAW1394_IOC_ISO_QUEUE_ACTIVITY:
+			queue_rawiso_event(fi);
+			return 0;
+		}
+		break;
+	case RAW1394_ISO_XMIT:
+		switch(cmd) {
+		case RAW1394_IOC_ISO_XMIT_START: {
+			/* copy two ints from user-space */
+			int args[2];
+			if(copy_from_user(&args[0], (void*) arg, sizeof(args)))
+				return -EFAULT;
+			return hpsb_iso_xmit_start(fi->iso_handle, args[0], args[1]);
+		}
+		case RAW1394_IOC_ISO_XMIT_SYNC:
+			return hpsb_iso_xmit_sync(fi->iso_handle);
+		case RAW1394_IOC_ISO_XMIT_RECV_STOP:
+			hpsb_iso_stop(fi->iso_handle);
+			return 0;
+		case RAW1394_IOC_ISO_GET_STATUS:
+			return raw1394_iso_get_status(fi, (void*) arg);
+		case RAW1394_IOC_ISO_XMIT_PACKETS:
+			return raw1394_iso_send_packets(fi, (void*) arg);
+		case RAW1394_IOC_ISO_SHUTDOWN:
+			raw1394_iso_shutdown(fi);
+			return 0;
+		case RAW1394_IOC_ISO_QUEUE_ACTIVITY:
+			queue_rawiso_event(fi);
+			return 0;
+		}
+		break;
+	default:
+		break;
+	}
+
+	return -EINVAL;
+}
+
 static unsigned int raw1394_poll(struct file *file, poll_table *pt)
 {
         struct file_info *fi = file->private_data;
@@ -2060,6 +2385,9 @@
         struct arm_addr  *arm_addr = NULL;
         int another_host;
 
+	if(fi->iso_state != RAW1394_ISO_INACTIVE)
+		raw1394_iso_shutdown(fi);
+
         for (i = 0; i < 64; i++) {
                 if (fi->listen_channels & (1ULL << i)) {
                         hpsb_unlisten_channel(hl_handle, fi->host, i);
@@ -2121,7 +2449,7 @@
         spin_unlock_irq(&host_info_lock);
         if (fail > 0) {
                 printk(KERN_ERR "raw1394: during addr_list-release "
-                        "error(s) occured \n");
+                        "error(s) occurred \n");
         }
 
         while (!done) {
@@ -2167,7 +2495,9 @@
 static struct file_operations file_ops = {
 	.owner =	THIS_MODULE,
         .read =		raw1394_read, 
-        .write =	raw1394_write, 
+        .write =	raw1394_write,
+	.mmap =         raw1394_mmap,
+	.ioctl =        raw1394_ioctl,
         .poll =		raw1394_poll, 
         .open =		raw1394_open, 
         .release =	raw1394_release, 
diff -urN linux-2.4.21-pre5/drivers/ieee1394/raw1394.h linux-2.4.21-pre6/drivers/ieee1394/raw1394.h
--- linux-2.4.21-pre5/drivers/ieee1394/raw1394.h	Wed Mar 26 16:11:28 2003
+++ linux-2.4.21-pre6/drivers/ieee1394/raw1394.h	Wed Mar 26 16:11:36 2003
@@ -1,8 +1,7 @@
 #ifndef IEEE1394_RAW1394_H
 #define IEEE1394_RAW1394_H
 
-#define RAW1394_DEVICE_MAJOR      171
-#define RAW1394_DEVICE_NAME       "raw1394"
+/* header for the raw1394 API that is exported to user-space */
 
 #define RAW1394_KERNELAPI_VERSION 4
 
@@ -36,10 +35,11 @@
 #define RAW1394_REQ_PHYPACKET       500
 
 /* kernel to user */
-#define RAW1394_REQ_BUS_RESET     10000
-#define RAW1394_REQ_ISO_RECEIVE   10001
-#define RAW1394_REQ_FCP_REQUEST   10002
-#define RAW1394_REQ_ARM           10003
+#define RAW1394_REQ_BUS_RESET        10000
+#define RAW1394_REQ_ISO_RECEIVE      10001
+#define RAW1394_REQ_FCP_REQUEST      10002
+#define RAW1394_REQ_ARM              10003
+#define RAW1394_REQ_RAWISO_ACTIVITY  10004
 
 /* error codes */
 #define RAW1394_ERROR_NONE        0
@@ -93,21 +93,21 @@
 };
 
 typedef struct arm_request {
-        nodeid_t        destination_nodeid;
-        nodeid_t        source_nodeid;
-        nodeaddr_t      destination_offset;
-        u8              tlabel;
-        u8              tcode;
-        u_int8_t        extended_transaction_code;
-        u_int32_t       generation;
-        arm_length_t    buffer_length;
-        byte_t          *buffer;
+        __u16           destination_nodeid;
+        __u16           source_nodeid;
+        __u64           destination_offset;
+        __u8            tlabel;
+        __u8            tcode;
+        __u8            extended_transaction_code;
+        __u32           generation;
+        __u16           buffer_length;
+        __u8            *buffer;
 } *arm_request_t;
 
 typedef struct arm_response {
-        int             response_code;
-        arm_length_t    buffer_length;
-        byte_t          *buffer;
+        __s32           response_code;
+        __u16           buffer_length;
+        __u8            *buffer;
 } *arm_response_t;
 
 typedef struct arm_request_response {
@@ -115,68 +115,60 @@
         struct arm_response *response;
 } *arm_request_response_t;
 
-#ifdef __KERNEL__
+/* rawiso API */
+#include "ieee1394-ioctl.h"
 
-struct iso_block_store {
-        atomic_t refcount;
-        size_t data_size;
-        quadlet_t data[0];
+/* per-packet metadata embedded in the ringbuffer */
+/* must be identical to hpsb_iso_packet_info in iso.h! */
+struct raw1394_iso_packet_info {
+	__u32 offset;
+	__u16 len;
+	__u16 cycle;   /* recv only */
+	__u8  channel; /* recv only */
+	__u8  tag;
+	__u8  sy;
 };
 
-struct file_info {
-        struct list_head list;
-
-        enum { opened, initialized, connected } state;
-        unsigned int protocol_version;
-
-        struct hpsb_host *host;
+/* argument for RAW1394_ISO_RECV/XMIT_PACKETS ioctls */
+struct raw1394_iso_packets {
+	__u32 n_packets;
+	struct raw1394_iso_packet_info *infos;
+};
 
-        struct list_head req_pending;
-        struct list_head req_complete;
-        struct semaphore complete_sem;
-        spinlock_t reqlists_lock;
-        wait_queue_head_t poll_wait_complete;
+struct raw1394_iso_config {
+	/* size of packet data buffer, in bytes (will be rounded up to PAGE_SIZE) */
+	__u32 data_buf_size;
 
-        struct list_head addr_list;           
+	/* # of packets to buffer */
+	__u32 buf_packets;
 
-        u8 *fcp_buffer;
+	/* iso channel (set to -1 for multi-channel recv) */
+	__s32 channel;
 
-        u64 listen_channels;
-        quadlet_t *iso_buffer;
-        size_t iso_buffer_length;
+	/* xmit only - iso transmission speed */
+	__u8 speed;
 
-        u8 notification; /* (busreset-notification) RAW1394_NOTIFY_OFF/ON */
+	/* max. latency of buffer, in packets (-1 if you don't care) */
+	__s32 irq_interval;
 };
 
-struct arm_addr {
-        struct list_head addr_list; /* file_info list */
-        u64    start, end;
-        u64    arm_tag;
-        u8     access_rights;
-        u8     notification_options;
-        u8     client_transactions;
-        u64    recvb;
-        u16    rec_length;
-        u8     *addr_space_buffer; /* accessed by read/write/lock */
-};
+/* argument to RAW1394_ISO_XMIT/RECV_INIT and RAW1394_ISO_GET_STATUS */
+struct raw1394_iso_status {
+	/* current settings */
+	struct raw1394_iso_config config;
 
-struct pending_request {
-        struct list_head list;
-        struct file_info *file_info;
-        struct hpsb_packet *packet;
-        struct hpsb_queue_struct tq;
-        struct iso_block_store *ibs;
-        quadlet_t *data;
-        int free_data;
-        struct raw1394_request req;
-};
+	/* number of packets waiting to be filled with data (ISO transmission)
+	   or containing data received (ISO reception) */
+	__u32 n_packets;
 
-struct host_info {
-        struct list_head list;
-        struct hpsb_host *host;
-        struct list_head file_info_list;
-};
+	/* approximate number of packets dropped due to overflow or
+	   underflow of the packet buffer (a value of zero guarantees
+	   that no packets have been dropped) */
+	__u32 overflows;
 
-#endif /* __KERNEL__ */
+	/* cycle number at which next packet will be transmitted;
+	   -1 if not known */
+	__s16 xmit_cycle;
+};
 
 #endif /* IEEE1394_RAW1394_H */
diff -urN linux-2.4.21-pre5/drivers/ieee1394/sbp2.c linux-2.4.21-pre6/drivers/ieee1394/sbp2.c
--- linux-2.4.21-pre5/drivers/ieee1394/sbp2.c	Wed Mar 26 16:11:28 2003
+++ linux-2.4.21-pre6/drivers/ieee1394/sbp2.c	Wed Mar 26 16:11:36 2003
@@ -84,7 +84,7 @@
  * sbp2_serialize_io		- Serialize all I/O coming down from the scsi drivers 
  *				  (0 = deserialized, 1 = serialized, default = 0)
  * sbp2_max_sectors, 		- Change max sectors per I/O supported (default = 255)
- * sbp2_max_outstanding_cmds	- Change max outstanding concurrent commands (default = 8)
+ * sbp2_max_outstanding_cmds	- Change max outstanding concurrent commands (default = 64)
  * sbp2_max_cmds_per_lun	- Change max concurrent commands per sbp2 device (default = 1)
  * sbp2_exclusive_login		- Set to zero if you'd like to allow multiple hosts the ability
  *				  to log in at the same time. Sbp2 device must support this,
@@ -298,6 +298,9 @@
  *		   returns the dead bit in status. Thanks to Chandan (chandan@toad.net) for this one.
  *	04/27/02 - Fix sbp2 login problem on SMP systems, enable real spinlocks by default. (JSG)
  *	06/09/02 - Don't force 36-bute SCSI inquiry, but leave in a define for badly behaved devices. (JSG)   
+ *	02/04/03 - Fixed a SMP deadlock (don't hold sbp2_command_lock while calling sbp2scsi_complete_command).
+ *		   Also save/restore irq flags in sbp2scsi_complete_command  - Sancho Dauskardt <sda@bdit.de>
+ *
  */
 
 
@@ -350,7 +353,7 @@
 #include "sbp2.h"
 
 static char version[] __devinitdata =
-	"$Rev: 707 $ James Goodwin <jamesg@filanet.com>";
+	"$Rev: 792 $ James Goodwin <jamesg@filanet.com>";
 
 /*
  * Module load parameter definitions
@@ -399,7 +402,7 @@
  * sbp2 devices attached (or if you need to do some debugging).
  */
 MODULE_PARM(sbp2_max_outstanding_cmds,"i");
-MODULE_PARM_DESC(sbp2_max_outstanding_cmds, "Change max outstanding concurrent commands (default = 8)");
+MODULE_PARM_DESC(sbp2_max_outstanding_cmds, "Change max outstanding concurrent commands (default = 64)");
 static int sbp2_max_outstanding_cmds = SBP2SCSI_MAX_OUTSTANDING_CMDS;
 
 /*
@@ -513,22 +516,10 @@
 #define SBP2_ERR(fmt, args...)		HPSB_ERR("sbp2: "fmt, ## args)
 
 /*
- * Spinlock debugging stuff.
+ * Globals
  */
-#define SBP2_USE_REAL_SPINLOCKS
 
-#ifdef SBP2_USE_REAL_SPINLOCKS
-#define sbp2_spin_lock(lock, flags)	spin_lock_irqsave(lock, flags)	
-#define sbp2_spin_unlock(lock, flags)	spin_unlock_irqrestore(lock, flags);
 static spinlock_t sbp2_host_info_lock = SPIN_LOCK_UNLOCKED;
-#else
-#define sbp2_spin_lock(lock, flags)	do {save_flags(flags); cli();} while (0)	
-#define sbp2_spin_unlock(lock, flags)	do {restore_flags(flags);} while (0)
-#endif
-
-/*
- * Globals
- */
 
 static Scsi_Host_Template scsi_driver_template;
 
@@ -724,7 +715,7 @@
 	/* 
 	 * Go through free list releasing packets
 	 */
-	sbp2_spin_lock(&hi->sbp2_request_packet_lock, flags);
+	spin_lock_irqsave(&hi->sbp2_request_packet_lock, flags);
 	while (!list_empty(&hi->sbp2_req_free)) {
 
 		lh = hi->sbp2_req_free.next;
@@ -741,7 +732,7 @@
 
 	}
 	kfree(hi->request_packet);
-	sbp2_spin_unlock(&hi->sbp2_request_packet_lock, flags);
+	spin_unlock_irqrestore(&hi->sbp2_request_packet_lock, flags);
 
 	return;
 }
@@ -763,7 +754,7 @@
 	struct hpsb_packet *packet;
 	unsigned long flags;
 
-	sbp2_spin_lock(&hi->sbp2_request_packet_lock, flags);
+	spin_lock_irqsave(&hi->sbp2_request_packet_lock, flags);
 	if (!list_empty(&hi->sbp2_req_free)) {
 
 		/*
@@ -800,7 +791,7 @@
 		 * Set up a task queue completion routine, which returns
 		 * the packet to the free list and releases the tlabel.
 		 */
-		HPSB_PREPARE_WORK(&request_packet->tq,
+		PREPARE_TQUEUE(&request_packet->tq,
 				  (void (*)(void*))sbp2util_free_request_packet,
 				  request_packet);
 		request_packet->hi_context = hi;
@@ -814,7 +805,7 @@
 	} else {
 		SBP2_ERR("sbp2util_allocate_request_packet - no packets available!");
 	}
-	sbp2_spin_unlock(&hi->sbp2_request_packet_lock, flags);
+	spin_unlock_irqrestore(&hi->sbp2_request_packet_lock, flags);
 
 	return(request_packet);
 }
@@ -831,12 +822,12 @@
 	/*
 	 * Free the tlabel, and return the packet to the free pool.
 	 */
-	sbp2_spin_lock(&hi->sbp2_request_packet_lock, flags);
+	spin_lock_irqsave(&hi->sbp2_request_packet_lock, flags);
 	free_tlabel(hi->host, LOCAL_BUS | request_packet->packet->node_id,
 		    request_packet->packet->tlabel);
 	list_del(&request_packet->list);
 	list_add_tail(&request_packet->list, &hi->sbp2_req_free);
-	sbp2_spin_unlock(&hi->sbp2_request_packet_lock, flags);
+	spin_unlock_irqrestore(&hi->sbp2_request_packet_lock, flags);
 
 	return;
 }
@@ -852,12 +843,12 @@
 	unsigned long flags;
 	struct sbp2_command_info *command;
         
-	sbp2_spin_lock(&scsi_id->sbp2_command_orb_lock, flags);
+	spin_lock_irqsave(&scsi_id->sbp2_command_orb_lock, flags);
 	for (i = 0; i < scsi_id->sbp2_total_command_orbs; i++) {
 		command = (struct sbp2_command_info *)
 		    kmalloc(sizeof(struct sbp2_command_info), GFP_KERNEL);
 		if (!command) {
-			sbp2_spin_unlock(&scsi_id->sbp2_command_orb_lock, flags);
+			spin_unlock_irqrestore(&scsi_id->sbp2_command_orb_lock, flags);
 			return(-ENOMEM);
 		}
 		memset(command, '\0', sizeof(struct sbp2_command_info));
@@ -874,7 +865,7 @@
 		INIT_LIST_HEAD(&command->list);
 		list_add_tail(&command->list, &scsi_id->sbp2_command_orb_completed);
 	}
-	sbp2_spin_unlock(&scsi_id->sbp2_command_orb_lock, flags);
+	spin_unlock_irqrestore(&scsi_id->sbp2_command_orb_lock, flags);
 	return 0;
 }
 
@@ -888,7 +879,7 @@
 	struct sbp2_command_info *command;
 	unsigned long flags;
         
-	sbp2_spin_lock(&scsi_id->sbp2_command_orb_lock, flags);
+	spin_lock_irqsave(&scsi_id->sbp2_command_orb_lock, flags);
 	if (!list_empty(&scsi_id->sbp2_command_orb_completed)) {
 		list_for_each_safe(lh, next, &scsi_id->sbp2_command_orb_completed) {
 			command = list_entry(lh, struct sbp2_command_info, list);
@@ -906,7 +897,7 @@
 			kfree(command);
 		}
 	}
-	sbp2_spin_unlock(&scsi_id->sbp2_command_orb_lock, flags);
+	spin_unlock_irqrestore(&scsi_id->sbp2_command_orb_lock, flags);
 	return;
 }
 
@@ -921,17 +912,17 @@
 	struct sbp2_command_info *command;
 	unsigned long flags;
 
-	sbp2_spin_lock(&scsi_id->sbp2_command_orb_lock, flags);
+	spin_lock_irqsave(&scsi_id->sbp2_command_orb_lock, flags);
 	if (!list_empty(&scsi_id->sbp2_command_orb_inuse)) {
 		list_for_each(lh, &scsi_id->sbp2_command_orb_inuse) {
 			command = list_entry(lh, struct sbp2_command_info, list);
 			if (command->command_orb_dma == orb) {
-				sbp2_spin_unlock(&scsi_id->sbp2_command_orb_lock, flags);
+				spin_unlock_irqrestore(&scsi_id->sbp2_command_orb_lock, flags);
 				return (command);
 			}
 		}
 	}
-	sbp2_spin_unlock(&scsi_id->sbp2_command_orb_lock, flags);
+	spin_unlock_irqrestore(&scsi_id->sbp2_command_orb_lock, flags);
 
 	SBP2_ORB_DEBUG("could not match command orb %x", (unsigned int)orb);
 
@@ -948,17 +939,17 @@
 	struct sbp2_command_info *command;
 	unsigned long flags;
 
-	sbp2_spin_lock(&scsi_id->sbp2_command_orb_lock, flags);
+	spin_lock_irqsave(&scsi_id->sbp2_command_orb_lock, flags);
 	if (!list_empty(&scsi_id->sbp2_command_orb_inuse)) {
 		list_for_each(lh, &scsi_id->sbp2_command_orb_inuse) {
 			command = list_entry(lh, struct sbp2_command_info, list);
 			if (command->Current_SCpnt == SCpnt) {
-				sbp2_spin_unlock(&scsi_id->sbp2_command_orb_lock, flags);
+				spin_unlock_irqrestore(&scsi_id->sbp2_command_orb_lock, flags);
 				return (command);
 			}
 		}
 	}
-	sbp2_spin_unlock(&scsi_id->sbp2_command_orb_lock, flags);
+	spin_unlock_irqrestore(&scsi_id->sbp2_command_orb_lock, flags);
 	return(NULL);
 }
 
@@ -975,7 +966,7 @@
 	struct sbp2_command_info *command = NULL;
 	unsigned long flags;
 
-	sbp2_spin_lock(&scsi_id->sbp2_command_orb_lock, flags);
+	spin_lock_irqsave(&scsi_id->sbp2_command_orb_lock, flags);
 	if (!list_empty(&scsi_id->sbp2_command_orb_completed)) {
 		lh = scsi_id->sbp2_command_orb_completed.next;
 		list_del(lh);
@@ -986,7 +977,7 @@
 	} else {
 		SBP2_ERR("sbp2util_allocate_command_orb - No orbs available!");
 	}
-	sbp2_spin_unlock(&scsi_id->sbp2_command_orb_lock, flags);
+	spin_unlock_irqrestore(&scsi_id->sbp2_command_orb_lock, flags);
 	return (command);
 }
 
@@ -1031,11 +1022,11 @@
 {
 	unsigned long flags;
 
-	sbp2_spin_lock(&scsi_id->sbp2_command_orb_lock, flags);
+	spin_lock_irqsave(&scsi_id->sbp2_command_orb_lock, flags);
 	list_del(&command->list);
 	sbp2util_free_command_dma(command);
 	list_add_tail(&command->list, &scsi_id->sbp2_command_orb_completed);
-	sbp2_spin_unlock(&scsi_id->sbp2_command_orb_lock, flags);
+	spin_unlock_irqrestore(&scsi_id->sbp2_command_orb_lock, flags);
 }
 
 
@@ -1158,9 +1149,9 @@
 	/* Complete any pending commands with busy (so they get
 	 * retried) and remove them from our queue
 	 */
-	sbp2_spin_lock(&hi->sbp2_command_lock, flags);
+	spin_lock_irqsave(&hi->sbp2_command_lock, flags);
 	sbp2scsi_complete_all_commands(hi, scsi_id, DID_BUS_BUSY);
-	sbp2_spin_unlock(&hi->sbp2_command_lock, flags);
+	spin_unlock_irqrestore(&hi->sbp2_command_lock, flags);
 }
 
 /*
@@ -1199,9 +1190,9 @@
 		return;
 	}
 
-	sbp2_spin_lock(&sbp2_host_info_lock, flags);
+	spin_lock_irqsave(&sbp2_host_info_lock, flags);
 	list_add_tail(&hi->list, &sbp2_host_info_list);
-	sbp2_spin_unlock(&sbp2_host_info_lock, flags);
+	spin_unlock_irqrestore(&sbp2_host_info_lock, flags);
 
 	/* Register our host with the SCSI stack. */
 	hi->scsi_host = scsi_register (&scsi_driver_template, sizeof(void *));
@@ -1260,7 +1251,7 @@
 
 	SBP2_DEBUG("sbp2_remove_host");
 
-	sbp2_spin_lock(&sbp2_host_info_lock, flags);
+	spin_lock_irqsave(&sbp2_host_info_lock, flags);
 
 	hi = sbp2_find_host_info(host);
 	if (hi != NULL) {
@@ -1271,7 +1262,7 @@
 	else
 		SBP2_ERR("attempt to remove unknown host %p", host);
 
-	sbp2_spin_unlock(&sbp2_host_info_lock, flags);
+	spin_unlock_irqrestore(&sbp2_host_info_lock, flags);
 }
 
 /*
@@ -2711,16 +2702,16 @@
 		return(RCODE_ADDRESS_ERROR);
 	}
 
-	sbp2_spin_lock(&sbp2_host_info_lock, flags);
+	spin_lock_irqsave(&sbp2_host_info_lock, flags);
 	hi = sbp2_find_host_info(host);
-	sbp2_spin_unlock(&sbp2_host_info_lock, flags);
+	spin_unlock_irqrestore(&sbp2_host_info_lock, flags);
 
 	if (!hi) {
 		SBP2_ERR("host info is NULL - this is bad!");
 		return(RCODE_ADDRESS_ERROR);
 	}
 
-	sbp2_spin_lock(&hi->sbp2_command_lock, flags);
+	spin_lock_irqsave(&hi->sbp2_command_lock, flags);
 
 	/*
 	 * Find our scsi_id structure by looking at the status fifo address written to by
@@ -2731,7 +2722,7 @@
 
 	if (!scsi_id) {
 		SBP2_ERR("scsi_id is NULL - device is gone?");
-		sbp2_spin_unlock(&hi->sbp2_command_lock, flags);
+		spin_unlock_irqrestore(&hi->sbp2_command_lock, flags);
 		return(RCODE_ADDRESS_ERROR);
 	}
 
@@ -2795,13 +2786,6 @@
 			}
 
 			SBP2_ORB_DEBUG("completing command orb %p", &command->command_orb);
-
-			/*
-			 * Complete the SCSI command
-			 */
-			SBP2_DEBUG("Completing SCSI command");
-			sbp2scsi_complete_command(hi, scsi_id, scsi_status, SCpnt, command->Current_done);
-			SBP2_ORB_DEBUG("command orb completed");
 		}
 
 		/*
@@ -2825,7 +2809,23 @@
 		}
 	}
 
-	sbp2_spin_unlock(&hi->sbp2_command_lock, flags);
+	spin_unlock_irqrestore(&hi->sbp2_command_lock, flags);
+
+
+	if (SCpnt) {
+
+		/*
+		 * Complete the SCSI command.
+		 *
+		 * Only do it after we've released the sbp2_command_lock,
+		 * as it might otherwise deadlock with the 
+		 * io_request_lock (in sbp2scsi_queuecommand).
+		 */
+		SBP2_DEBUG("Completing SCSI command");
+		sbp2scsi_complete_command(hi, scsi_id, scsi_status, SCpnt, command->Current_done);
+		SBP2_ORB_DEBUG("command orb completed");
+	}
+
 	return(RCODE_COMPLETE);
 }
 
@@ -2905,12 +2905,12 @@
 	/*
 	 * Try and send our SCSI command
 	 */
-	sbp2_spin_lock(&hi->sbp2_command_lock, flags);
+	spin_lock_irqsave(&hi->sbp2_command_lock, flags);
 	if (sbp2_send_command(hi, scsi_id, SCpnt, done)) {
 		SBP2_ERR("Error sending SCSI command");
 		sbp2scsi_complete_command(hi, scsi_id, SBP2_SCSI_STATUS_SELECTION_TIMEOUT, SCpnt, done);
 	}
-	sbp2_spin_unlock(&hi->sbp2_command_lock, flags);
+	spin_unlock_irqrestore(&hi->sbp2_command_lock, flags);
 
 	return(0);
 }
@@ -2951,10 +2951,14 @@
 
 /*
  * This function is called in order to complete a regular SBP-2 command.
+ *
+ * This can be called in interrupt context.
  */
 static void sbp2scsi_complete_command(struct sbp2scsi_host_info *hi, struct scsi_id_instance_data *scsi_id, u32 scsi_status,
 				      Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *))
 {
+	unsigned long flags;
+
 	SBP2_DEBUG("sbp2scsi_complete_command");
 
 	/*
@@ -3053,9 +3057,9 @@
 	/*
 	 * Tell scsi stack that we're done with this command
 	 */
-	spin_lock_irq(&io_request_lock);
+	spin_lock_irqsave(&io_request_lock, flags);
 	done (SCpnt);
-	spin_unlock_irq(&io_request_lock);
+	spin_unlock_irqrestore(&io_request_lock, flags);
 
 	return;
 }
@@ -3080,7 +3084,7 @@
 		 * Right now, just return any matching command structures
 		 * to the free pool.
 		 */
-		sbp2_spin_lock(&hi->sbp2_command_lock, flags);
+		spin_lock_irqsave(&hi->sbp2_command_lock, flags);
 		command = sbp2util_find_command_for_SCpnt(scsi_id, SCpnt);
 		if (command) {
 			SBP2_DEBUG("Found command to abort");
@@ -3105,7 +3109,7 @@
 		 */
 		sbp2_agent_reset(hi, scsi_id, SBP2_SEND_NO_WAIT);
 		sbp2scsi_complete_all_commands(hi, scsi_id, DID_BUS_BUSY);		
-		sbp2_spin_unlock(&hi->sbp2_command_lock, flags);
+		spin_unlock_irqrestore(&hi->sbp2_command_lock, flags);
 	}
 
 	return(SUCCESS);
diff -urN linux-2.4.21-pre5/drivers/ieee1394/sbp2.h linux-2.4.21-pre6/drivers/ieee1394/sbp2.h
--- linux-2.4.21-pre5/drivers/ieee1394/sbp2.h	Wed Mar 26 16:11:28 2003
+++ linux-2.4.21-pre6/drivers/ieee1394/sbp2.h	Wed Mar 26 16:11:36 2003
@@ -265,7 +265,7 @@
 #define SBP2_CLUSTERING			ENABLE_CLUSTERING
 #define SBP2_MAX_SG_ELEMENT_LENGTH	0xf000
 #define SBP2SCSI_MAX_SCSI_IDS		16	/* Max sbp2 device instances supported */
-#define SBP2SCSI_MAX_OUTSTANDING_CMDS	8	/* Max total outstanding sbp2 commands allowed at a time! */
+#define SBP2SCSI_MAX_OUTSTANDING_CMDS	64	/* Max total outstanding sbp2 commands allowed at a time! */
 #define SBP2SCSI_MAX_CMDS_PER_LUN	1 	/* Max outstanding sbp2 commands per device - tune as needed */
 #define SBP2_MAX_SECTORS		255	/* Max sectors supported */
 
@@ -319,7 +319,7 @@
 
 	struct list_head list;
 	struct hpsb_packet *packet;
-	struct hpsb_queue_struct tq;
+	struct tq_struct tq;
 	void *hi_context;
 
 };
diff -urN linux-2.4.21-pre5/drivers/ieee1394/video1394.c linux-2.4.21-pre6/drivers/ieee1394/video1394.c
--- linux-2.4.21-pre5/drivers/ieee1394/video1394.c	Wed Mar 26 16:11:28 2003
+++ linux-2.4.21-pre6/drivers/ieee1394/video1394.c	Wed Mar 26 16:11:36 2003
@@ -51,6 +51,7 @@
 #include "ieee1394_core.h"
 #include "highlevel.h"
 #include "video1394.h"
+#include "dma.h"
 
 #include "ohci1394.h"
 
@@ -90,9 +91,14 @@
 	unsigned int packet_size;
 	unsigned int left_size;
 	unsigned int nb_cmd;
-	unsigned char *buf;
+
+	struct dma_region dma;
+
+	struct dma_prog_region *prg_reg;
+
         struct dma_cmd **ir_prg;
 	struct it_dma_prg **it_prg;
+
 	unsigned int *buffer_status;
         struct timeval *buffer_time; /* time when the buffer was received */
 	unsigned int *last_used_cmd; /* For ISO Transmit with 
@@ -155,74 +161,6 @@
 static devfs_handle_t devfs_handle;
 static struct hpsb_highlevel *hl_handle = NULL;
 
-/* Code taken from bttv.c */
-
-/*******************************/
-/* Memory management functions */
-/*******************************/
-
-static inline unsigned long kvirt_to_bus(unsigned long adr) 
-{
-	unsigned long kva, ret;
-
-	kva = (unsigned long) page_address(vmalloc_to_page((void *)adr));
-	kva |= adr & (PAGE_SIZE-1); /* restore the offset */
-	ret = virt_to_bus((void *)kva);
-	return ret;
-}
-
-/* Here we want the physical address of the memory.
- * This is used when initializing the contents of the area.
- */
-static inline unsigned long kvirt_to_pa(unsigned long adr) 
-{
-        unsigned long kva, ret;
-
-	kva = (unsigned long) page_address(vmalloc_to_page((void *)adr));
-	kva |= adr & (PAGE_SIZE-1); /* restore the offset */
-	ret = __pa(kva);
-        return ret;
-}
-
-static void * rvmalloc(unsigned long size)
-{
-	void * mem;
-	unsigned long adr;
-
-	size=PAGE_ALIGN(size);
-	mem=vmalloc_32(size);
-	if (mem) 
-	{
-		memset(mem, 0, size); /* Clear the ram out, 
-					 no junk to the user */
-	        adr=(unsigned long) mem;
-		while (size > 0) 
-                {
-			mem_map_reserve(vmalloc_to_page((void *)adr));
-			adr+=PAGE_SIZE;
-			size-=PAGE_SIZE;
-		}
-	}
-	return mem;
-}
-
-static void rvfree(void * mem, unsigned long size)
-{
-        unsigned long adr;
-
-	if (mem) 
-	{
-	        adr=(unsigned long) mem;
-		while ((long) size > 0) 
-                {
-			mem_map_unreserve(vmalloc_to_page((void *)adr));
-			adr+=PAGE_SIZE;
-			size-=PAGE_SIZE;
-		}
-		vfree(mem);
-	}
-}
-/* End of code taken from bttv.c */
 
 static int free_dma_iso_ctx(struct dma_iso_ctx *d)
 {
@@ -234,20 +172,19 @@
 	if (d->iso_tasklet.link.next != NULL)
 		ohci1394_unregister_iso_tasklet(d->ohci, &d->iso_tasklet);
 
-	if (d->buf)
-		rvfree((void *)d->buf, d->num_desc * d->buf_size);
+	dma_region_free(&d->dma);
 
-	if (d->ir_prg) {
-		for (i=0;i<d->num_desc;i++) 
-			if (d->ir_prg[i]) kfree(d->ir_prg[i]);
-		kfree(d->ir_prg);
+	if (d->prg_reg) {
+		for (i = 0; i < d->num_desc; i++)
+			dma_prog_region_free(&d->prg_reg[i]);
+		kfree(d->prg_reg);
 	}
 
-	if (d->it_prg) {
-		for (i=0;i<d->num_desc;i++) 
-			if (d->it_prg[i]) kfree(d->it_prg[i]);
+	if (d->ir_prg)
+		kfree(d->ir_prg);
+
+	if (d->it_prg)
 		kfree(d->it_prg);
-	}
 
 	if (d->buffer_status)
 		kfree(d->buffer_status);
@@ -287,18 +224,17 @@
 	d->frame_size = buf_size;
 	d->buf_size = PAGE_ALIGN(buf_size);
 	d->last_buffer = -1;
-	d->buf = NULL;
-	d->ir_prg = NULL;
 	init_waitqueue_head(&d->waitq);
 
-	d->buf = rvmalloc(d->num_desc * d->buf_size);
+	/* Init the regions for easy cleanup */
+	dma_region_init(&d->dma);
 
-	if (d->buf == NULL) {
+	if (dma_region_alloc(&d->dma, d->num_desc * d->buf_size, ohci->dev,
+			     PCI_DMA_BIDIRECTIONAL)) {
 		PRINT(KERN_ERR, ohci->id, "Failed to allocate dma buffer");
 		free_dma_iso_ctx(d);
 		return NULL;
 	}
-	memset(d->buf, 0, d->num_desc * d->buf_size);
 
 	if (type == OHCI_ISO_RECEIVE)
 		ohci1394_init_iso_tasklet(&d->iso_tasklet, type,
@@ -317,6 +253,17 @@
 	}
 	d->ctx = d->iso_tasklet.context;
 
+	d->prg_reg = kmalloc(d->num_desc * sizeof(struct dma_prog_region),
+			GFP_KERNEL);
+	if (d->prg_reg == NULL) {
+		PRINT(KERN_ERR, ohci->id, "Failed to allocate ir prg regs");
+		free_dma_iso_ctx(d);
+		return NULL;
+	}
+	/* Makes for easier cleanup */
+	for (i = 0; i < d->num_desc; i++)
+		dma_prog_region_init(&d->prg_reg[i]);
+
 	if (type == OHCI_ISO_RECEIVE) {
 		d->ctrlSet = OHCI1394_IsoRcvContextControlSet+32*d->ctx;
 		d->ctrlClear = OHCI1394_IsoRcvContextControlClear+32*d->ctx;
@@ -327,31 +274,27 @@
 				    GFP_KERNEL);
 
 		if (d->ir_prg == NULL) {
-			PRINT(KERN_ERR, ohci->id, 
-			      "Failed to allocate dma ir prg");
+			PRINT(KERN_ERR, ohci->id, "Failed to allocate dma ir prg");
 			free_dma_iso_ctx(d);
 			return NULL;
 		}
 		memset(d->ir_prg, 0, d->num_desc * sizeof(struct dma_cmd *));
-	
+
 		d->nb_cmd = d->buf_size / PAGE_SIZE + 1;
 		d->left_size = (d->frame_size % PAGE_SIZE) ?
 			d->frame_size % PAGE_SIZE : PAGE_SIZE;
  
-		for (i=0;i<d->num_desc;i++) {
-			d->ir_prg[i] = kmalloc(d->nb_cmd * 
-					       sizeof(struct dma_cmd), 
-					       GFP_KERNEL);
-			if (d->ir_prg[i] == NULL) {
-				PRINT(KERN_ERR, ohci->id, 
-				      "Failed to allocate dma ir prg");
+		for (i = 0;i < d->num_desc; i++) {
+			if (dma_prog_region_alloc(&d->prg_reg[i], d->nb_cmd *
+						  sizeof(struct dma_cmd), ohci->dev)) {
+				PRINT(KERN_ERR, ohci->id, "Failed to allocate dma ir prg");
 				free_dma_iso_ctx(d);
 				return NULL;
 			}
+			d->ir_prg[i] = (struct dma_cmd *)d->prg_reg[i].kvirt;
 		}
 
-	}
-	else {  /* OHCI_ISO_TRANSMIT */
+	} else {  /* OHCI_ISO_TRANSMIT */
 		d->ctrlSet = OHCI1394_IsoXmitContextControlSet+16*d->ctx;
 		d->ctrlClear = OHCI1394_IsoXmitContextControlClear+16*d->ctx;
 		d->cmdPtr = OHCI1394_IsoXmitCommandPtr+16*d->ctx;
@@ -382,20 +325,17 @@
 		if (d->frame_size % d->packet_size) {
 			d->nb_cmd++;
 			d->left_size = d->frame_size % d->packet_size;
-		}
-		else
+		} else
 			d->left_size = d->packet_size;
 
-		for (i=0;i<d->num_desc;i++) {
-			d->it_prg[i] = kmalloc(d->nb_cmd * 
-					       sizeof(struct it_dma_prg), 
-					       GFP_KERNEL);
-			if (d->it_prg[i] == NULL) {
-				PRINT(KERN_ERR, ohci->id, 
-				      "Failed to allocate dma it prg");
+		for (i = 0; i < d->num_desc; i++) {
+			if (dma_prog_region_alloc(&d->prg_reg[i], d->nb_cmd *
+						sizeof(struct it_dma_prg), ohci->dev)) {
+				PRINT(KERN_ERR, ohci->id, "Failed to allocate dma it prg");
 				free_dma_iso_ctx(d);
 				return NULL;
 			}
+			d->it_prg[i] = (struct it_dma_prg *)d->prg_reg[i].kvirt;
 		}
 	}
 
@@ -448,7 +388,7 @@
 	int i;
 	d->ir_prg[n][0].status = cpu_to_le32(4);
 	d->ir_prg[n][1].status = cpu_to_le32(PAGE_SIZE-4);
-	for (i=2;i<d->nb_cmd-1;i++)
+	for (i = 2; i < d->nb_cmd - 1; i++)
 		d->ir_prg[n][i].status = cpu_to_le32(PAGE_SIZE);
 	d->ir_prg[n][i].status = cpu_to_le32(d->left_size);
 }
@@ -456,7 +396,8 @@
 static void initialize_dma_ir_prg(struct dma_iso_ctx *d, int n, int flags)
 {
 	struct dma_cmd *ir_prg = d->ir_prg[n];
-	unsigned long buf = (unsigned long)d->buf+n*d->buf_size;
+	struct dma_prog_region *ir_reg = &d->prg_reg[n];
+	unsigned long buf = (unsigned long)d->dma.kvirt + n * d->buf_size;
 	int i;
 
 	/* the first descriptor will read only 4 bytes */
@@ -467,31 +408,37 @@
 	if (flags & VIDEO1394_SYNC_FRAMES)
 		ir_prg[0].control |= cpu_to_le32(DMA_CTL_WAIT);
 
-	ir_prg[0].address = cpu_to_le32(kvirt_to_bus(buf));
-	ir_prg[0].branchAddress =  cpu_to_le32((virt_to_bus(&(ir_prg[1].control)) 
-				    & 0xfffffff0) | 0x1);
+	ir_prg[0].address = cpu_to_le32(dma_region_offset_to_bus(&d->dma, buf -
+				(unsigned long)d->dma.kvirt));
+	ir_prg[0].branchAddress = cpu_to_le32((dma_prog_region_offset_to_bus(ir_reg,
+					1 * sizeof(struct dma_cmd)) & 0xfffffff0) | 0x1);
 
 	/* the second descriptor will read PAGE_SIZE-4 bytes */
 	ir_prg[1].control = cpu_to_le32(DMA_CTL_INPUT_MORE | DMA_CTL_UPDATE |
 		DMA_CTL_BRANCH | (PAGE_SIZE-4));
-	ir_prg[1].address = cpu_to_le32(kvirt_to_bus(buf+4));
-	ir_prg[1].branchAddress =  cpu_to_le32((virt_to_bus(&(ir_prg[2].control)) 
-				    & 0xfffffff0) | 0x1);
+	ir_prg[1].address = cpu_to_le32(dma_region_offset_to_bus(&d->dma, (buf + 4) -
+					(unsigned long)d->dma.kvirt));
+	ir_prg[1].branchAddress = cpu_to_le32((dma_prog_region_offset_to_bus(ir_reg,
+					2 * sizeof(struct dma_cmd)) & 0xfffffff0) | 0x1);
 	
 	for (i=2;i<d->nb_cmd-1;i++) {
 		ir_prg[i].control = cpu_to_le32(DMA_CTL_INPUT_MORE | DMA_CTL_UPDATE | 
 			DMA_CTL_BRANCH | PAGE_SIZE);
-		ir_prg[i].address = cpu_to_le32(kvirt_to_bus(buf+(i-1)*PAGE_SIZE));
-
-		ir_prg[i].branchAddress =  
-			cpu_to_le32((virt_to_bus(&(ir_prg[i+1].control)) 
-			 & 0xfffffff0) | 0x1);
+		ir_prg[i].address = cpu_to_le32(dma_region_offset_to_bus(&d->dma,
+					        (buf+(i-1)*PAGE_SIZE) -
+						(unsigned long)d->dma.kvirt));
+
+		ir_prg[i].branchAddress =
+			cpu_to_le32((dma_prog_region_offset_to_bus(ir_reg,
+				(i + 1) * sizeof(struct dma_cmd)) & 0xfffffff0) | 0x1);
 	}
 
 	/* the last descriptor will generate an interrupt */
 	ir_prg[i].control = cpu_to_le32(DMA_CTL_INPUT_MORE | DMA_CTL_UPDATE | 
 		DMA_CTL_IRQ | DMA_CTL_BRANCH | d->left_size);
-	ir_prg[i].address = cpu_to_le32(kvirt_to_bus(buf+(i-1)*PAGE_SIZE));
+	ir_prg[i].address = cpu_to_le32(dma_region_offset_to_bus(&d->dma,
+					(buf+(i-1)*PAGE_SIZE) -
+					(unsigned long)d->dma.kvirt));
 }
 	
 static void initialize_dma_ir_ctx(struct dma_iso_ctx *d, int tag, int flags)
@@ -566,7 +513,7 @@
 static inline void put_timestamp(struct ti_ohci *ohci, struct dma_iso_ctx * d,
 				 int n)
 {
-	unsigned char* buf = d->buf + n * d->buf_size;
+	unsigned char* buf = d->dma.kvirt + n * d->buf_size;
 	u32 cycleTimer;
 	u32 timeStamp;
 
@@ -595,7 +542,7 @@
 	if (n == -1) {
 	  return;
 	}
-	buf = d->buf + n * d->buf_size;
+	buf = d->dma.kvirt + n * d->buf_size;
 
 	timeStamp += (d->last_used_cmd[n] << 12) & 0xffff;
 
@@ -642,7 +589,8 @@
 static void initialize_dma_it_prg(struct dma_iso_ctx *d, int n, int sync_tag)
 {
 	struct it_dma_prg *it_prg = d->it_prg[n];
-	unsigned long buf = (unsigned long)d->buf+n*d->buf_size;
+	struct dma_prog_region *it_reg = &d->prg_reg[n];
+	unsigned long buf = (unsigned long)d->dma.kvirt + n * d->buf_size;
 	int i;
 	d->last_used_cmd[n] = d->nb_cmd - 1;
 	for (i=0;i<d->nb_cmd;i++) {
@@ -666,18 +614,18 @@
 		it_prg[i].end.control = cpu_to_le32(DMA_CTL_OUTPUT_LAST |
 			    	    	     DMA_CTL_BRANCH);
 		it_prg[i].end.address =
-			cpu_to_le32(kvirt_to_bus(buf+i*d->packet_size));
+			cpu_to_le32(dma_region_offset_to_bus(&d->dma, (buf+i*d->packet_size) -
+						(unsigned long)d->dma.kvirt));
 
 		if (i<d->nb_cmd-1) {
 			it_prg[i].end.control |= cpu_to_le32(d->packet_size);
 			it_prg[i].begin.branchAddress = 
-				cpu_to_le32((virt_to_bus(&(it_prg[i+1].begin.control)) 
-				 & 0xfffffff0) | 0x3);
+				cpu_to_le32((dma_prog_region_offset_to_bus(it_reg, (i + 1) *
+					sizeof(struct it_dma_prg)) & 0xfffffff0) | 0x3);
 			it_prg[i].end.branchAddress = 
-				cpu_to_le32((virt_to_bus(&(it_prg[i+1].begin.control)) 
-				 & 0xfffffff0) | 0x3);
-		}
-		else {
+				cpu_to_le32((dma_prog_region_offset_to_bus(it_reg, (i + 1) *
+					sizeof(struct it_dma_prg)) & 0xfffffff0) | 0x3);
+		} else {
 			/* the last prg generates an interrupt */
 			it_prg[i].end.control |= cpu_to_le32(DMA_CTL_UPDATE | 
 				DMA_CTL_IRQ | d->left_size);
@@ -686,15 +634,6 @@
 			it_prg[i].end.branchAddress = 0;
 		}
 		it_prg[i].end.status = 0;
-
-#if 0
-		printk("%d:%d: %08x-%08x ctrl %08x brch %08x d0 %08x d1 %08x\n",n,i,
-		       virt_to_bus(&(it_prg[i].begin.control)),
-		       virt_to_bus(&(it_prg[i].end.control)),
-		       it_prg[i].end.control,
-		       it_prg[i].end.branchAddress,
-		       it_prg[i].data[0], it_prg[i].data[1]);
-#endif
 	}
 }
 
@@ -703,6 +642,7 @@
 	struct ti_ohci *ohci)
 {
 	struct it_dma_prg *it_prg = d->it_prg[n];
+	struct dma_prog_region *it_reg = &d->prg_reg[n];
 	int i;
 
 #if 0
@@ -724,12 +664,12 @@
 
 		if (i < d->nb_cmd-1 && packet_sizes[i+1] != 0) {
 			it_prg[i].end.control |= cpu_to_le32(size);
-			it_prg[i].begin.branchAddress = 
-				cpu_to_le32((virt_to_bus(&(it_prg[i+1].begin.control)) 
-				 & 0xfffffff0) | 0x3);
-			it_prg[i].end.branchAddress = 
-				cpu_to_le32((virt_to_bus(&(it_prg[i+1].begin.control)) 
-				 & 0xfffffff0) | 0x3);
+			it_prg[i].begin.branchAddress =
+				cpu_to_le32((dma_prog_region_offset_to_bus(it_reg, (i + 1) *
+					sizeof(struct it_dma_prg)) & 0xfffffff0) | 0x3);
+			it_prg[i].end.branchAddress =
+				cpu_to_le32((dma_prog_region_offset_to_bus(it_reg, (i + 1) *
+					sizeof(struct it_dma_prg)) & 0xfffffff0) | 0x3);
 		} else {
 			/* the last prg generates an interrupt */
 			it_prg[i].end.control |= cpu_to_le32(DMA_CTL_UPDATE | 
@@ -761,37 +701,6 @@
 	reg_write(ohci, OHCI1394_IsoXmitIntMaskSet, 1<<d->ctx);
 }
 
-static int do_iso_mmap(struct ti_ohci *ohci, struct dma_iso_ctx *d,
-		       struct vm_area_struct *vma)
-{
-        unsigned long start = vma->vm_start;
-	unsigned long size = vma->vm_end - vma->vm_start;
-        unsigned long page, pos;
-
-        if (size > d->num_desc * d->buf_size) {
-		PRINT(KERN_ERR, ohci->id, 
-		      "iso context %d buf size is different from mmap size", 
-		      d->ctx);
-                return -EINVAL;
-	}
-        if (!d->buf) {
-		PRINT(KERN_ERR, ohci->id, 
-		      "iso context %d is not allocated", d->ctx);
-		return -EINVAL;
-	}
-
-	pos = (unsigned long) d->buf;
-        while (size > 0) {
-                page = kvirt_to_pa(pos);
-                if (remap_page_range_1394(vma, start, page, PAGE_SIZE, PAGE_SHARED))
-                        return -EAGAIN;
-                start += PAGE_SIZE;
-                pos += PAGE_SIZE;
-                size -= PAGE_SIZE;
-        }
-        return 0;
-}
-
 static int video1394_ioctl(struct inode *inode, struct file *file,
 			   unsigned int cmd, unsigned long arg)
 {
@@ -804,6 +713,8 @@
 	{
 	case VIDEO1394_LISTEN_CHANNEL:
 	case VIDEO1394_TALK_CHANNEL:
+	case VIDEO1394_IOC_LISTEN_CHANNEL:
+	case VIDEO1394_IOC_TALK_CHANNEL:
 	{
 		struct video1394_mmap v;
 		u64 mask;
@@ -862,7 +773,7 @@
 			return -EFAULT;
 		}
 
-		if (cmd == VIDEO1394_LISTEN_CHANNEL) {
+		if (cmd == VIDEO1394_IOC_LISTEN_CHANNEL || cmd == VIDEO1394_LISTEN_CHANNEL) {
 			d = alloc_dma_iso_ctx(ohci, OHCI_ISO_RECEIVE,
 					      v.nb_buffers, v.buf_size, 
 					      v.channel, 0);
@@ -912,8 +823,10 @@
 
 		return 0;
 	}
-	case VIDEO1394_UNLISTEN_CHANNEL: 
+	case VIDEO1394_UNLISTEN_CHANNEL:
 	case VIDEO1394_UNTALK_CHANNEL:
+	case VIDEO1394_IOC_UNLISTEN_CHANNEL: 
+	case VIDEO1394_IOC_UNTALK_CHANNEL:
 	{
 		int channel;
 		u64 mask;
@@ -937,7 +850,7 @@
 		/* Mark this channel as unused */
 		ohci->ISO_channel_usage &= ~mask;
 
-		if (cmd == VIDEO1394_UNLISTEN_CHANNEL)
+		if (cmd == VIDEO1394_IOC_UNLISTEN_CHANNEL || cmd == VIDEO1394_UNLISTEN_CHANNEL)
 			d = find_ctx(&ctx->context_list, OHCI_ISO_RECEIVE, channel);
 		else
 			d = find_ctx(&ctx->context_list, OHCI_ISO_TRANSMIT, channel);
@@ -950,6 +863,7 @@
 		return 0;
 	}
 	case VIDEO1394_LISTEN_QUEUE_BUFFER:
+	case VIDEO1394_IOC_LISTEN_QUEUE_BUFFER:
 	{
 		struct video1394_wait v;
 		struct dma_iso_ctx *d;
@@ -976,10 +890,10 @@
 		
 		d->buffer_status[v.buffer]=VIDEO1394_BUFFER_QUEUED;
 
-		if (d->last_buffer>=0) 
-			d->ir_prg[d->last_buffer][d->nb_cmd-1].branchAddress = 
-				cpu_to_le32((virt_to_bus(&(d->ir_prg[v.buffer][0].control)) 
-				 & 0xfffffff0) | 0x1);
+		if (d->last_buffer>=0)
+			d->ir_prg[d->last_buffer][d->nb_cmd-1].branchAddress =
+				cpu_to_le32((dma_prog_region_offset_to_bus(&d->prg_reg[v.buffer], 0)
+					& 0xfffffff0) | 0x1);
 
 		d->last_buffer = v.buffer;
 
@@ -992,9 +906,9 @@
 			DBGMSG(ohci->id, "Starting iso DMA ctx=%d",d->ctx);
 
 			/* Tell the controller where the first program is */
-			reg_write(ohci, d->cmdPtr, 
-				  virt_to_bus(&(d->ir_prg[v.buffer][0]))|0x1);
-			
+			reg_write(ohci, d->cmdPtr,
+				dma_prog_region_offset_to_bus(&d->prg_reg[v.buffer], 0) | 0x1);
+
 			/* Run IR context */
 			reg_write(ohci, d->ctrlSet, 0x8000);
 		}
@@ -1011,6 +925,8 @@
 	}
 	case VIDEO1394_LISTEN_WAIT_BUFFER:
 	case VIDEO1394_LISTEN_POLL_BUFFER:
+	case VIDEO1394_IOC_LISTEN_WAIT_BUFFER:
+	case VIDEO1394_IOC_LISTEN_POLL_BUFFER:
 	{
 		struct video1394_wait v;
 		struct dma_iso_ctx *d;
@@ -1037,7 +953,8 @@
 			d->buffer_status[v.buffer]=VIDEO1394_BUFFER_FREE;
 			break;
 		case VIDEO1394_BUFFER_QUEUED:
-			if (cmd == VIDEO1394_LISTEN_POLL_BUFFER) {
+			if (cmd == VIDEO1394_IOC_LISTEN_POLL_BUFFER ||
+			    cmd == VIDEO1394_LISTEN_POLL_BUFFER) {
 			    /* for polling, return error code EINTR */
 			    spin_unlock_irqrestore(&d->lock, flags);
 			    return -EINTR;
@@ -1092,6 +1009,7 @@
 		return 0;
 	}
 	case VIDEO1394_TALK_QUEUE_BUFFER:
+	case VIDEO1394_IOC_TALK_QUEUE_BUFFER:
 	{
 		struct video1394_wait v;
 		struct video1394_queue_variable qv;
@@ -1134,18 +1052,16 @@
 
 		d->buffer_status[v.buffer]=VIDEO1394_BUFFER_QUEUED;
 
-		if (d->last_buffer>=0) {
+		if (d->last_buffer >= 0) {
 			d->it_prg[d->last_buffer]
-				[ d->last_used_cmd[d->last_buffer]
-					].end.branchAddress = 
-				cpu_to_le32((virt_to_bus(&(d->it_prg[v.buffer][0].begin.control)) 
-				 & 0xfffffff0) | 0x3);
+				[ d->last_used_cmd[d->last_buffer] ].end.branchAddress = 
+					cpu_to_le32((dma_prog_region_offset_to_bus(&d->prg_reg[v.buffer],
+						0) & 0xfffffff0) | 0x3);
 
 			d->it_prg[d->last_buffer]
-				[d->last_used_cmd[d->last_buffer]
-					].begin.branchAddress = 
-				cpu_to_le32((virt_to_bus(&(d->it_prg[v.buffer][0].begin.control)) 
-				 & 0xfffffff0) | 0x3);
+				[ d->last_used_cmd[d->last_buffer] ].begin.branchAddress =
+					cpu_to_le32((dma_prog_region_offset_to_bus(&d->prg_reg[v.buffer],
+						0) & 0xfffffff0) | 0x3);
 			d->next_buffer[d->last_buffer] = v.buffer;
 		}
 		d->last_buffer = v.buffer;
@@ -1162,9 +1078,9 @@
 			put_timestamp(ohci, d, d->last_buffer);
 
 			/* Tell the controller where the first program is */
-			reg_write(ohci, d->cmdPtr, 
-				  virt_to_bus(&(d->it_prg[v.buffer][0]))|0x3);
-			
+			reg_write(ohci, d->cmdPtr,
+				dma_prog_region_offset_to_bus(&d->prg_reg[v.buffer], 0) | 0x3);
+
 			/* Run IT context */
 			reg_write(ohci, d->ctrlSet, 0x8000);
 		}
@@ -1182,6 +1098,7 @@
 		
 	}
 	case VIDEO1394_TALK_WAIT_BUFFER:
+	case VIDEO1394_IOC_TALK_WAIT_BUFFER:
 	{
 		struct video1394_wait v;
 		struct dma_iso_ctx *d;
@@ -1250,7 +1167,7 @@
 	if (ctx->current_ctx == NULL) {
 		PRINT(KERN_ERR, ohci->id, "Current iso context not set");
 	} else
-		res = do_iso_mmap(ohci, ctx->current_ctx, vma);
+		res = dma_region_mmap(&ctx->current_ctx->dma, file, vma);
 	unlock_kernel();
 	return res;
 }
diff -urN linux-2.4.21-pre5/drivers/ieee1394/video1394.h linux-2.4.21-pre6/drivers/ieee1394/video1394.h
--- linux-2.4.21-pre5/drivers/ieee1394/video1394.h	Fri Dec 21 09:41:54 2001
+++ linux-2.4.21-pre6/drivers/ieee1394/video1394.h	Wed Mar 26 16:11:36 2003
@@ -21,6 +21,8 @@
 #ifndef _VIDEO_1394_H
 #define _VIDEO_1394_H
 
+#include "ieee1394-ioctl.h"
+
 #define VIDEO1394_DRIVER_NAME "video1394"
 
 #define VIDEO1394_MAX_SIZE 0x4000000
diff -urN linux-2.4.21-pre5/drivers/input/joydev.c linux-2.4.21-pre6/drivers/input/joydev.c
--- linux-2.4.21-pre5/drivers/input/joydev.c	Wed Mar 26 16:11:28 2003
+++ linux-2.4.21-pre6/drivers/input/joydev.c	Wed Mar 26 16:11:36 2003
@@ -388,7 +388,7 @@
 		case JSIOCSBTNMAP:
 			if (copy_from_user(joydev->keypam, (__u16 *) arg, sizeof(__u16) * (KEY_MAX - BTN_MISC)))
 				return -EFAULT;
-			for (i = 0; i < joydev->nkey; i++); {
+			for (i = 0; i < joydev->nkey; i++) {
 				if (joydev->keypam[i] > KEY_MAX || joydev->keypam[i] < BTN_MISC) return -EINVAL;
 				joydev->keymap[joydev->keypam[i] - BTN_MISC] = i;
 			}
diff -urN linux-2.4.21-pre5/drivers/isdn/hysdn/hysdn_boot.c linux-2.4.21-pre6/drivers/isdn/hysdn/hysdn_boot.c
--- linux-2.4.21-pre5/drivers/isdn/hysdn/hysdn_boot.c	Fri Dec 21 09:41:54 2001
+++ linux-2.4.21-pre6/drivers/isdn/hysdn/hysdn_boot.c	Wed Mar 26 16:11:36 2003
@@ -143,7 +143,7 @@
 					     (boot->pof_recid == TAG_CABSDATA) ? "CABSDATA" : "ABSDATA",
 					     datlen, boot->pof_recoffset);
 
-			if ((boot->last_error = card->writebootseq(card, boot->buf.BootBuf, datlen) < 0))
+			if ((boot->last_error = card->writebootseq(card, boot->buf.BootBuf, datlen)) < 0)
 				return (boot->last_error);	/* error writing data */
 
 			if (boot->pof_recoffset + datlen >= boot->pof_reclen)
diff -urN linux-2.4.21-pre5/drivers/md/linear.c linux-2.4.21-pre6/drivers/md/linear.c
--- linux-2.4.21-pre5/drivers/md/linear.c	Sun Sep 30 12:26:06 2001
+++ linux-2.4.21-pre6/drivers/md/linear.c	Wed Mar 26 16:11:36 2003
@@ -153,31 +153,29 @@
 	return 1;
 }
 
-static int linear_status (char *page, mddev_t *mddev)
+static void linear_status (struct seq_file *seq, mddev_t *mddev)
 {
-	int sz = 0;
 
 #undef MD_DEBUG
 #ifdef MD_DEBUG
 	int j;
 	linear_conf_t *conf = mddev_to_conf(mddev);
   
-	sz += sprintf(page+sz, "      ");
+	seq_printf(seq, "      ");
 	for (j = 0; j < conf->nr_zones; j++)
 	{
-		sz += sprintf(page+sz, "[%s",
+		seq_printf(seq, "[%s",
 			partition_name(conf->hash_table[j].dev0->dev));
 
 		if (conf->hash_table[j].dev1)
-			sz += sprintf(page+sz, "/%s] ",
+			seq_printf(seq, "/%s] ",
 			  partition_name(conf->hash_table[j].dev1->dev));
 		else
-			sz += sprintf(page+sz, "] ");
+			seq_printf(seq, "] ");
 	}
-	sz += sprintf(page+sz, "\n");
+	seq_printf(seq, "\n");
 #endif
-	sz += sprintf(page+sz, " %dk rounding", mddev->param.chunk_size/1024);
-	return sz;
+	seq_printf(seq, " %dk rounding", mddev->param.chunk_size/1024);
 }
 
 
diff -urN linux-2.4.21-pre5/drivers/md/md.c linux-2.4.21-pre6/drivers/md/md.c
--- linux-2.4.21-pre5/drivers/md/md.c	Wed Mar 26 16:11:28 2003
+++ linux-2.4.21-pre6/drivers/md/md.c	Wed Mar 26 16:11:36 2003
@@ -1048,7 +1048,7 @@
 			printk("(skipping faulty ");
 		if (rdev->alias_device)
 			printk("(skipping alias ");
-		if (disk_faulty(&rdev->sb->this_disk)) {
+		if (!rdev->faulty && disk_faulty(&rdev->sb->this_disk)) {
 			printk("(skipping new-faulty %s )\n",
 			       partition_name(rdev->dev));
 			continue;
@@ -2395,13 +2395,6 @@
 	}
 
 	persistent = !mddev->sb->not_persistent;
-	size = calc_dev_size(dev, mddev, persistent);
-
-	if (size < mddev->sb->size) {
-		printk(KERN_WARNING "md%d: disk size %d blocks < array size %d\n",
-				mdidx(mddev), size, mddev->sb->size);
-		return -ENOSPC;
-	}
 
 	rdev = find_rdev(mddev, dev);
 	if (rdev)
@@ -2423,6 +2416,14 @@
 		err = -EINVAL;
 		goto abort_export;
 	}
+	size = calc_dev_size(dev, mddev, persistent);
+
+	if (size < mddev->sb->size) {
+		printk(KERN_WARNING "md%d: disk size %d blocks < array size %d\n",
+				mdidx(mddev), size, mddev->sb->size);
+		err = -ENOSPC;
+		goto abort_export;
+	}
 	bind_rdev_to_array(rdev, mddev);
 
 	/*
@@ -3065,13 +3066,13 @@
 	return 0;
 }
 
-static int status_unused(char * page)
+static void status_unused(struct seq_file *seq)
 {
-	int sz = 0, i = 0;
+	int i = 0;
 	mdk_rdev_t *rdev;
 	struct md_list_head *tmp;
 
-	sz += sprintf(page + sz, "unused devices: ");
+	seq_printf(seq, "unused devices: ");
 
 	ITERATE_RDEV_ALL(rdev,tmp) {
 		if (!rdev->same_set.next && !rdev->same_set.prev) {
@@ -3079,21 +3080,19 @@
 			 * The device is not yet used by any array.
 			 */
 			i++;
-			sz += sprintf(page + sz, "%s ",
+			seq_printf(seq, "%s ",
 				partition_name(rdev->dev));
 		}
 	}
 	if (!i)
-		sz += sprintf(page + sz, "<none>");
+		seq_printf(seq, "<none>");
 
-	sz += sprintf(page + sz, "\n");
-	return sz;
+	seq_printf(seq, "\n");
 }
 
 
-static int status_resync(char * page, mddev_t * mddev)
+static void status_resync(struct seq_file *seq, mddev_t * mddev)
 {
-	int sz = 0;
 	unsigned long max_blocks, resync, res, dt, db, rt;
 
 	resync = (mddev->curr_resync - atomic_read(&mddev->recovery_active))/2;
@@ -3102,32 +3101,31 @@
 	/*
 	 * Should not happen.
 	 */
-	if (!max_blocks) {
+	if (!max_blocks)
 		MD_BUG();
-		return 0;
-	}
+
 	res = (resync/1024)*1000/(max_blocks/1024 + 1);
 	{
 		int i, x = res/50, y = 20-x;
-		sz += sprintf(page + sz, "[");
+		seq_printf(seq, "[");
 		for (i = 0; i < x; i++)
-			sz += sprintf(page + sz, "=");
-		sz += sprintf(page + sz, ">");
+			seq_printf(seq, "=");
+		seq_printf(seq, ">");
 		for (i = 0; i < y; i++)
-			sz += sprintf(page + sz, ".");
-		sz += sprintf(page + sz, "] ");
+			seq_printf(seq, ".");
+		seq_printf(seq, "] ");
 	}
 	if (!mddev->recovery_running)
 		/*
 		 * true resync
 		 */
-		sz += sprintf(page + sz, " resync =%3lu.%lu%% (%lu/%lu)",
+		seq_printf(seq, " resync =%3lu.%lu%% (%lu/%lu)",
 				res/10, res % 10, resync, max_blocks);
 	else
 		/*
 		 * recovery ...
 		 */
-		sz += sprintf(page + sz, " recovery =%3lu.%lu%% (%lu/%lu)",
+		seq_printf(seq, " recovery =%3lu.%lu%% (%lu/%lu)",
 				res/10, res % 10, resync, max_blocks);
 
 	/*
@@ -3144,84 +3142,156 @@
 	db = resync - (mddev->resync_mark_cnt/2);
 	rt = (dt * ((max_blocks-resync) / (db/100+1)))/100;
 
-	sz += sprintf(page + sz, " finish=%lu.%lumin", rt / 60, (rt % 60)/6);
+	seq_printf(seq, " finish=%lu.%lumin", rt / 60, (rt % 60)/6);
 
-	sz += sprintf(page + sz, " speed=%ldK/sec", db/dt);
+	seq_printf(seq, " speed=%ldK/sec", db/dt);
 
-	return sz;
 }
 
-static int md_status_read_proc(char *page, char **start, off_t off,
-			int count, int *eof, void *data)
+
+static void *md_seq_start(struct seq_file *seq, loff_t *pos)
 {
-	int sz = 0, j, size;
-	struct md_list_head *tmp, *tmp2;
-	mdk_rdev_t *rdev;
+	struct list_head *tmp;
+	loff_t l = *pos;
 	mddev_t *mddev;
 
-	sz += sprintf(page + sz, "Personalities : ");
-	for (j = 0; j < MAX_PERSONALITY; j++)
-	if (pers[j])
-		sz += sprintf(page+sz, "[%s] ", pers[j]->name);
+	if (l > 0x10000)
+		return NULL;
+	if (!l--)
+		/* header */
+		return (void*)1;
 
-	sz += sprintf(page+sz, "\n");
+	list_for_each(tmp,&all_mddevs)
+		if (!l--) {
+			mddev = list_entry(tmp, mddev_t, all_mddevs);
+			return mddev;
+		}
+	return (void*)2;/* tail */
+}
 
+static void *md_seq_next(struct seq_file *seq, void *v, loff_t *pos)
+{
+	struct list_head *tmp;
+	mddev_t *next_mddev, *mddev = v;
+	
+	++*pos;
+	if (v == (void*)2)
+		return NULL;
 
-	sz += sprintf(page+sz, "read_ahead ");
-	if (read_ahead[MD_MAJOR] == INT_MAX)
-		sz += sprintf(page+sz, "not set\n");
+	if (v == (void*)1)
+		tmp = all_mddevs.next;
 	else
-		sz += sprintf(page+sz, "%d sectors\n", read_ahead[MD_MAJOR]);
+		tmp = mddev->all_mddevs.next;
+	if (tmp != &all_mddevs)
+		next_mddev = list_entry(tmp,mddev_t,all_mddevs);
+	else {
+		next_mddev = (void*)2;
+		*pos = 0x10000;
+	}		
 
-	ITERATE_MDDEV(mddev,tmp) {
-		sz += sprintf(page + sz, "md%d : %sactive", mdidx(mddev),
-						mddev->pers ? "" : "in");
-		if (mddev->pers) {
-			if (mddev->ro)
-				sz += sprintf(page + sz, " (read-only)");
-			sz += sprintf(page + sz, " %s", mddev->pers->name);
-		}
+	return next_mddev;
 
-		size = 0;
-		ITERATE_RDEV(mddev,rdev,tmp2) {
-			sz += sprintf(page + sz, " %s[%d]",
-				partition_name(rdev->dev), rdev->desc_nr);
-			if (rdev->faulty) {
-				sz += sprintf(page + sz, "(F)");
-				continue;
-			}
-			size += rdev->size;
-		}
+}
 
-		if (mddev->nb_dev) {
-			if (mddev->pers)
-				sz += sprintf(page + sz, "\n      %d blocks",
-						 md_size[mdidx(mddev)]);
-			else
-				sz += sprintf(page + sz, "\n      %d blocks", size);
-		}
+static void md_seq_stop(struct seq_file *seq, void *v)
+{
+
+}
+
+static int md_seq_show(struct seq_file *seq, void *v)
+{
+	int j, size;
+	struct md_list_head *tmp2;
+	mdk_rdev_t *rdev;
+	mddev_t *mddev = v;
+
+	if (v == (void*)1) {
+		seq_printf(seq, "Personalities : ");
+		for (j = 0; j < MAX_PERSONALITY; j++)
+			if (pers[j])
+				seq_printf(seq, "[%s] ", pers[j]->name);
+
+		seq_printf(seq, "\n");
+		seq_printf(seq, "read_ahead ");
+		if (read_ahead[MD_MAJOR] == INT_MAX)
+			seq_printf(seq, "not set\n");
+		else
+			seq_printf(seq, "%d sectors\n", read_ahead[MD_MAJOR]);
+		return 0;
+	}
+	if (v == (void*)2) {
+		status_unused(seq);
+		return 0;
+	}
 
-		if (!mddev->pers) {
-			sz += sprintf(page+sz, "\n");
+	seq_printf(seq, "md%d : %sactive", mdidx(mddev),
+		   mddev->pers ? "" : "in");
+	if (mddev->pers) {
+		if (mddev->ro)
+			seq_printf(seq, " (read-only)");
+		seq_printf(seq, " %s", mddev->pers->name);
+	}
+	
+	size = 0;
+	ITERATE_RDEV(mddev,rdev,tmp2) {
+		seq_printf(seq, " %s[%d]",
+			   partition_name(rdev->dev), rdev->desc_nr);
+		if (rdev->faulty) {
+			seq_printf(seq, "(F)");
 			continue;
 		}
+		size += rdev->size;
+	}
+
+	if (mddev->nb_dev) {
+		if (mddev->pers)
+			seq_printf(seq, "\n      %d blocks",
+				   md_size[mdidx(mddev)]);
+		else
+			seq_printf(seq, "\n      %d blocks", size);
+	}
+
+	if (mddev->pers) {
 
-		sz += mddev->pers->status (page+sz, mddev);
+		mddev->pers->status (seq, mddev);
 
-		sz += sprintf(page+sz, "\n      ");
+		seq_printf(seq, "\n      ");
 		if (mddev->curr_resync) {
-			sz += status_resync (page+sz, mddev);
+			status_resync (seq, mddev);
 		} else {
 			if (sem_getcount(&mddev->resync_sem) != 1)
-				sz += sprintf(page + sz, "	resync=DELAYED");
+				seq_printf(seq, "	resync=DELAYED");
 		}
-		sz += sprintf(page + sz, "\n");
 	}
-	sz += status_unused(page + sz);
+	seq_printf(seq, "\n");
 
-	return sz;
+	return 0;
 }
 
+  
+static struct seq_operations md_seq_ops = {
+	.start  = md_seq_start,
+	.next   = md_seq_next,
+	.stop   = md_seq_stop,
+	.show   = md_seq_show,
+};
+
+static int md_seq_open(struct inode *inode, struct file *file)
+{
+	int error;
+
+	error = seq_open(file, &md_seq_ops);
+	return error;
+}
+
+static struct file_operations md_seq_fops = {
+	.open           = md_seq_open,
+	.read           = seq_read,
+	.llseek         = seq_lseek,
+	.release	= seq_release,
+};
+
+
 int register_md_personality(int pnum, mdk_personality_t *p)
 {
 	if (pnum >= MAX_PERSONALITY) {
@@ -3621,6 +3691,7 @@
 
 static void md_geninit(void)
 {
+	struct proc_dir_entry *p;
 	int i;
 
 	for(i = 0; i < MAX_MD_DEVS; i++) {
@@ -3637,7 +3708,9 @@
 	dprintk("md: sizeof(mdp_super_t) = %d\n", (int)sizeof(mdp_super_t));
 
 #ifdef CONFIG_PROC_FS
-	create_proc_read_entry("mdstat", 0, NULL, md_status_read_proc, NULL);
+	p = create_proc_entry("mdstat", S_IRUGO, NULL);
+	if (p)
+		p->proc_fops = &md_seq_fops;
 #endif
 }
 
diff -urN linux-2.4.21-pre5/drivers/md/multipath.c linux-2.4.21-pre6/drivers/md/multipath.c
--- linux-2.4.21-pre5/drivers/md/multipath.c	Mon Feb 25 11:37:58 2002
+++ linux-2.4.21-pre6/drivers/md/multipath.c	Wed Mar 26 16:11:36 2003
@@ -281,18 +281,17 @@
 	return 0;
 }
 
-static int multipath_status (char *page, mddev_t *mddev)
+static void multipath_status (struct seq_file *seq, mddev_t *mddev)
 {
 	multipath_conf_t *conf = mddev_to_conf(mddev);
-	int sz = 0, i;
+	int i;
 	
-	sz += sprintf (page+sz, " [%d/%d] [", conf->raid_disks,
+	seq_printf (seq, " [%d/%d] [", conf->raid_disks,
 						 conf->working_disks);
 	for (i = 0; i < conf->raid_disks; i++)
-		sz += sprintf (page+sz, "%s",
+		seq_printf (seq, "%s",
 			conf->multipaths[i].operational ? "U" : "_");
-	sz += sprintf (page+sz, "]");
-	return sz;
+	seq_printf (seq, "]");
 }
 
 #define LAST_DISK KERN_ALERT \
diff -urN linux-2.4.21-pre5/drivers/md/raid0.c linux-2.4.21-pre6/drivers/md/raid0.c
--- linux-2.4.21-pre5/drivers/md/raid0.c	Sun Sep 30 12:26:06 2001
+++ linux-2.4.21-pre6/drivers/md/raid0.c	Wed Mar 26 16:11:36 2003
@@ -289,41 +289,40 @@
 	return 0;
 }
 			   
-static int raid0_status (char *page, mddev_t *mddev)
+static void raid0_status (struct seq_file *seq, mddev_t *mddev)
 {
-	int sz = 0;
 #undef MD_DEBUG
 #ifdef MD_DEBUG
 	int j, k;
 	raid0_conf_t *conf = mddev_to_conf(mddev);
   
-	sz += sprintf(page + sz, "      ");
+	seq_printf(seq, "      ");
 	for (j = 0; j < conf->nr_zones; j++) {
-		sz += sprintf(page + sz, "[z%d",
+		seq_printf(seq, "[z%d",
 				conf->hash_table[j].zone0 - conf->strip_zone);
 		if (conf->hash_table[j].zone1)
-			sz += sprintf(page+sz, "/z%d] ",
+			seq_printf(seq, "/z%d] ",
 				conf->hash_table[j].zone1 - conf->strip_zone);
 		else
-			sz += sprintf(page+sz, "] ");
+			seq_printf(seq, "] ");
 	}
   
-	sz += sprintf(page + sz, "\n");
+	seq_printf(seq, "\n");
   
 	for (j = 0; j < conf->nr_strip_zones; j++) {
-		sz += sprintf(page + sz, "      z%d=[", j);
+		seq_printf(seq, "      z%d=[", j);
 		for (k = 0; k < conf->strip_zone[j].nb_dev; k++)
-			sz += sprintf (page+sz, "%s/", partition_name(
+			seq_printf (seq, "%s/", partition_name(
 				conf->strip_zone[j].dev[k]->dev));
-		sz--;
-		sz += sprintf (page+sz, "] zo=%d do=%d s=%d\n",
+
+		seq_printf (seq, "] zo=%d do=%d s=%d\n",
 				conf->strip_zone[j].zone_offset,
 				conf->strip_zone[j].dev_offset,
 				conf->strip_zone[j].size);
 	}
 #endif
-	sz += sprintf(page + sz, " %dk chunks", mddev->param.chunk_size/1024);
-	return sz;
+	seq_printf(seq, " %dk chunks", mddev->param.chunk_size/1024);
+	return;
 }
 
 static mdk_personality_t raid0_personality=
diff -urN linux-2.4.21-pre5/drivers/md/raid1.c linux-2.4.21-pre6/drivers/md/raid1.c
--- linux-2.4.21-pre5/drivers/md/raid1.c	Thu Nov 28 15:53:13 2002
+++ linux-2.4.21-pre6/drivers/md/raid1.c	Wed Mar 26 16:11:36 2003
@@ -719,18 +719,17 @@
 	return (0);
 }
 
-static int raid1_status (char *page, mddev_t *mddev)
+static void raid1_status(struct seq_file *seq, mddev_t *mddev)
 {
 	raid1_conf_t *conf = mddev_to_conf(mddev);
-	int sz = 0, i;
+	int i;
 	
-	sz += sprintf (page+sz, " [%d/%d] [", conf->raid_disks,
+	seq_printf(seq, " [%d/%d] [", conf->raid_disks,
 						 conf->working_disks);
 	for (i = 0; i < conf->raid_disks; i++)
-		sz += sprintf (page+sz, "%s",
+		seq_printf(seq, "%s",
 			conf->mirrors[i].operational ? "U" : "_");
-	sz += sprintf (page+sz, "]");
-	return sz;
+	seq_printf(seq, "]");
 }
 
 #define LAST_DISK KERN_ALERT \
diff -urN linux-2.4.21-pre5/drivers/md/raid5.c linux-2.4.21-pre6/drivers/md/raid5.c
--- linux-2.4.21-pre5/drivers/md/raid5.c	Mon Feb 25 11:37:58 2002
+++ linux-2.4.21-pre6/drivers/md/raid5.c	Wed Mar 26 16:11:36 2003
@@ -1681,23 +1681,23 @@
 }
 #endif
 
-static int raid5_status (char *page, mddev_t *mddev)
+static void raid5_status (struct seq_file *seq, mddev_t *mddev)
 {
 	raid5_conf_t *conf = (raid5_conf_t *) mddev->private;
 	mdp_super_t *sb = mddev->sb;
-	int sz = 0, i;
+	int i;
 
-	sz += sprintf (page+sz, " level %d, %dk chunk, algorithm %d", sb->level, sb->chunk_size >> 10, sb->layout);
-	sz += sprintf (page+sz, " [%d/%d] [", conf->raid_disks, conf->working_disks);
+	seq_printf (seq, " level %d, %dk chunk, algorithm %d", sb->level, sb->chunk_size >> 10, sb->layout);
+	seq_printf (seq, " [%d/%d] [", conf->raid_disks, conf->working_disks);
 	for (i = 0; i < conf->raid_disks; i++)
-		sz += sprintf (page+sz, "%s", conf->disks[i].operational ? "U" : "_");
-	sz += sprintf (page+sz, "]");
+		seq_printf (seq, "%s", conf->disks[i].operational ? "U" : "_");
+	seq_printf (seq, "]");
 #if RAID5_DEBUG
 #define D(x) \
-	sz += sprintf (page+sz, "<"#x":%d>", atomic_read(&conf->x))
+	seq_printf (seq, "<"#x":%d>", atomic_read(&conf->x))
 	printall(conf);
 #endif
-	return sz;
+
 }
 
 static void print_raid5_conf (raid5_conf_t *conf)
diff -urN linux-2.4.21-pre5/drivers/media/radio/radio-aimslab.c linux-2.4.21-pre6/drivers/media/radio/radio-aimslab.c
--- linux-2.4.21-pre5/drivers/media/radio/radio-aimslab.c	Sun Sep 30 12:26:06 2001
+++ linux-2.4.21-pre6/drivers/media/radio/radio-aimslab.c	Wed Mar 26 16:11:36 2003
@@ -1,6 +1,6 @@
 /* radiotrack (radioreveal) driver for Linux radio support
  * (c) 1997 M. Kirkwood
- * Coverted to new API by Alan Cox <Alan.Cox@linux.org>
+ * Converted to new API by Alan Cox <Alan.Cox@linux.org>
  * Various bugfixes and enhancements by Russell Kroll <rkroll@exploits.org>
  *
  * History:
diff -urN linux-2.4.21-pre5/drivers/media/radio/radio-cadet.c linux-2.4.21-pre6/drivers/media/radio/radio-cadet.c
--- linux-2.4.21-pre5/drivers/media/radio/radio-cadet.c	Wed Mar 26 16:11:28 2003
+++ linux-2.4.21-pre6/drivers/media/radio/radio-cadet.c	Wed Mar 26 16:11:36 2003
@@ -538,7 +538,7 @@
 
 	for(i=0;i<8;i++) {
 	        io=iovals[i];
-	        if(request_region(io,2, "cadet-probe")>=0) {
+	        if(request_region(io,2, "cadet-probe") != NULL) {
 		        cadet_setfreq(1410);
 			if(cadet_getfreq()==1410) {
 				release_region(io, 2);
diff -urN linux-2.4.21-pre5/drivers/media/radio/radio-gemtek.c linux-2.4.21-pre6/drivers/media/radio/radio-gemtek.c
--- linux-2.4.21-pre5/drivers/media/radio/radio-gemtek.c	Thu Nov 28 15:53:13 2002
+++ linux-2.4.21-pre6/drivers/media/radio/radio-gemtek.c	Wed Mar 26 16:11:36 2003
@@ -8,7 +8,7 @@
  *    RadioTrack II driver for Linux radio support (C) 1998 Ben Pfaff
  * 
  *    Based on RadioTrack I/RadioReveal (C) 1997 M. Kirkwood
- *    Coverted to new API by Alan Cox <Alan.Cox@linux.org>
+ *    Converted to new API by Alan Cox <Alan.Cox@linux.org>
  *    Various bugfixes and enhancements by Russell Kroll <rkroll@exploits.org>
  *
  * TODO: Allow for more than one of these foolish entities :-)
diff -urN linux-2.4.21-pre5/drivers/media/radio/radio-maxiradio.c linux-2.4.21-pre6/drivers/media/radio/radio-maxiradio.c
--- linux-2.4.21-pre5/drivers/media/radio/radio-maxiradio.c	Fri Dec 21 09:41:54 2001
+++ linux-2.4.21-pre6/drivers/media/radio/radio-maxiradio.c	Wed Mar 26 16:11:36 2003
@@ -54,7 +54,7 @@
 
 
 /* TEA5757 pin mappings */
-const int clk = 1, data = 2, wren = 4, mo_st = 8, power = 16 ;
+static const int clk = 1, data = 2, wren = 4, mo_st = 8, power = 16 ;
 
 static int radio_nr = -1;
 MODULE_PARM(radio_nr, "i");
diff -urN linux-2.4.21-pre5/drivers/media/radio/radio-rtrack2.c linux-2.4.21-pre6/drivers/media/radio/radio-rtrack2.c
--- linux-2.4.21-pre5/drivers/media/radio/radio-rtrack2.c	Sun Sep 30 12:26:06 2001
+++ linux-2.4.21-pre6/drivers/media/radio/radio-rtrack2.c	Wed Mar 26 16:11:36 2003
@@ -1,7 +1,7 @@
 /* RadioTrack II driver for Linux radio support (C) 1998 Ben Pfaff
  * 
  * Based on RadioTrack I/RadioReveal (C) 1997 M. Kirkwood
- * Coverted to new API by Alan Cox <Alan.Cox@linux.org>
+ * Converted to new API by Alan Cox <Alan.Cox@linux.org>
  * Various bugfixes and enhancements by Russell Kroll <rkroll@exploits.org>
  *
  * TODO: Allow for more than one of these foolish entities :-)
diff -urN linux-2.4.21-pre5/drivers/media/video/cpia.c linux-2.4.21-pre6/drivers/media/video/cpia.c
--- linux-2.4.21-pre5/drivers/media/video/cpia.c	Thu Nov 28 15:53:13 2002
+++ linux-2.4.21-pre6/drivers/media/video/cpia.c	Wed Mar 26 16:11:36 2003
@@ -6,6 +6,7 @@
  * (C) Copyright 1999-2000 Peter Pregler
  * (C) Copyright 1999-2000 Scott J. Bertin
  * (C) Copyright 1999-2000 Johannes Erdfelt <johannes@erdfelt.com>
+ * (C) Copyright 2000 STMicroelectronics
  *
  * 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
@@ -22,7 +23,9 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
-/* #define _CPIA_DEBUG_		define for verbose debug output */
+/* define _CPIA_DEBUG_ for verbose debug output (see cpia.h) */
+/* #define _CPIA_DEBUG_  1 */  
+
 #include <linux/config.h>
 
 #include <linux/module.h>
@@ -512,6 +515,46 @@
 	return len;
 }
 
+static int match(char *checkstr, char **buffer, unsigned long *count,
+                 int *find_colon, int *err)
+{
+	int ret, colon_found = 1;
+	int len = strlen(checkstr);
+	ret = (len <= *count && strncmp(*buffer, checkstr, len) == 0);
+	if (ret) {
+		*buffer += len;
+		*count -= len;
+		if (*find_colon) {
+			colon_found = 0;
+			while (*count && (**buffer == ' ' || **buffer == '\t' ||
+			       (!colon_found && **buffer == ':'))) {
+				if (**buffer == ':')
+					colon_found = 1;
+				--*count;
+				++*buffer;
+			}
+			if (!*count || !colon_found)
+				*err = -EINVAL;
+			*find_colon = 0;
+		}
+	}
+	return ret;
+}
+
+static unsigned long int value(char **buffer, unsigned long *count, int *err)
+{
+	char *p;
+	unsigned long int ret;
+	ret = simple_strtoul(*buffer, &p, 0);
+	if (p == *buffer)
+		*err = -EINVAL;
+	else {
+		*count -= p - *buffer;
+		*buffer = p;
+	}
+	return ret;
+}
+
 static int cpia_write_proc(struct file *file, const char *buf,
                            unsigned long count, void *data)
 {
@@ -566,44 +609,11 @@
 	memcpy(&new_params, &cam->params, sizeof(struct cam_params));
 	new_mains = cam->mainsFreq;
 	
-#define MATCH(x) \
-	({ \
-		int _len = strlen(x), _ret, _colon_found; \
-		_ret = (_len <= count && strncmp(buffer, x, _len) == 0); \
-		if (_ret) { \
-			buffer += _len; \
-			count -= _len; \
-			if (find_colon) { \
-				_colon_found = 0; \
-				while (count && (*buffer == ' ' || *buffer == '\t' || \
-				       (!_colon_found && *buffer == ':'))) { \
-					if (*buffer == ':')  \
-						_colon_found = 1; \
-					--count; \
-					++buffer; \
-				} \
-				if (!count || !_colon_found) \
-					retval = -EINVAL; \
-				find_colon = 0; \
-			} \
-		} \
-		_ret; \
-	})
+#define MATCH(x) (match(x, &buffer, &count, &find_colon, &retval))
+#define VALUE (value(&buffer,&count, &retval))
 #define FIRMWARE_VERSION(x,y) (new_params.version.firmwareVersion == (x) && \
                                new_params.version.firmwareRevision == (y))
-#define VALUE \
-	({ \
-		char *_p; \
-		unsigned long int _ret; \
-		_ret = simple_strtoul(buffer, &_p, 0); \
-		if (_p == buffer) \
-			retval = -EINVAL; \
-		else { \
-			count -= _p - buffer; \
-			buffer = _p; \
-		} \
-		_ret; \
-	})
+
 
 	retval = 0;
 	while (count && !retval) {
@@ -1248,10 +1258,9 @@
 		}
 	}
 #undef MATCH	
-#undef FIRMWARE_VERSION
 #undef VALUE
-#undef FIND_VALUE
-#undef FIND_END
+#undef FIRMWARE_VERSION
+
 	if (!retval) {
 		if (command_flags & COMMAND_SETCOLOURPARAMS) {
 			/* Adjust cam->vp to reflect these changes */
@@ -1560,7 +1569,7 @@
 }
 
 
-static void inline free_frames(struct cpia_frame frame[FRAME_NUM])
+static inline void free_frames(struct cpia_frame frame[FRAME_NUM])
 {
 	int i;
 
@@ -1745,7 +1754,7 @@
 
 	retval = cam->ops->transferCmd(cam->lowlevel_data, cmd, data);
 	if (retval)
-		LOG("%x - failed\n", command);
+		DBG("%x - failed\n", command);
 
 	return retval;
 }
@@ -1970,7 +1979,7 @@
 	while (size > 0) {
 		size -= (ll+2);
 		if (size < 0) {
-			LOG("Insufficient data in buffer\n");
+			DBG("Insufficient data in buffer\n");
 			return -1;
 		}
 
@@ -1985,7 +1994,7 @@
 				int skipsize = skipcount(*ibuf >> 1, out_fmt);
 				obuf += skipsize;
 				if (obuf > end_obuf) {
-					LOG("Insufficient data in buffer\n");
+					DBG("Insufficient data in buffer\n");
 					return -1;
 				}
 				++ibuf;
@@ -1994,7 +2003,7 @@
 		}
 		if (ll == 1) {
 			if (*ibuf != EOL) {
-				LOG("EOL not found giving up after %d/%d"
+				DBG("EOL not found giving up after %d/%d"
 				    " bytes\n", origsize-size, origsize);
 				return -1;
 			}
@@ -2012,7 +2021,7 @@
 				ibuf += 2; /* skip over line length */
 			}
 		} else {
-			LOG("line length was not 1 but %d after %d/%d bytes\n",
+			DBG("line length was not 1 but %d after %d/%d bytes\n",
 			    ll, origsize-size, origsize);
 			return -1;
 		}
@@ -2501,54 +2510,56 @@
 	return 0;
 }
 
+static void put_cam(struct cpia_camera_ops* ops)
+{
+	if (ops->owner)
+		__MOD_DEC_USE_COUNT(ops->owner);
+}
+
 /* ------------------------- V4L interface --------------------- */
-static int cpia_open(struct video_device *dev, int flags)
+static int cpia_open(struct inode *inode, struct file *file)
 {
-	int i;
+	struct video_device *dev = video_devdata(file);
 	struct cam_data *cam = dev->priv;
+	int err;
 
 	if (!cam) {
 		DBG("Internal error, cam_data not found!\n");
-		return -EBUSY;
+		return -ENODEV;
 	}
-	    
+
 	if (cam->open_count > 0) {
 		DBG("Camera already open\n");
 		return -EBUSY;
 	}
-	
+
+	if (!try_inc_mod_count(cam->ops->owner))
+	  return -ENODEV;
+
+	down(&cam->busy_lock);
+
+	err = -ENOMEM;	
 	if (!cam->raw_image) {
 		cam->raw_image = rvmalloc(CPIA_MAX_IMAGE_SIZE);
 		if (!cam->raw_image)
-			return -ENOMEM;
+			goto oops;
 	}
 
 	if (!cam->decompressed_frame.data) {
 		cam->decompressed_frame.data = rvmalloc(CPIA_MAX_FRAME_SIZE);
-		if (!cam->decompressed_frame.data) {
-			rvfree(cam->raw_image, CPIA_MAX_IMAGE_SIZE);
-			cam->raw_image = NULL;
-			return -ENOMEM;
-		}
+		if (!cam->decompressed_frame.data) 
+			goto oops;
 	}
 	
 	/* open cpia */
-	if (cam->ops->open(cam->lowlevel_data)) {
-		rvfree(cam->decompressed_frame.data, CPIA_MAX_FRAME_SIZE);
-		cam->decompressed_frame.data = NULL;
-		rvfree(cam->raw_image, CPIA_MAX_IMAGE_SIZE);
-		cam->raw_image = NULL;
-		return -ENODEV;
-	}
+	err = -ENODEV;
+	if (cam->ops->open(cam->lowlevel_data)) 
+		goto oops;
 	
 	/* reset the camera */
-	if ((i = reset_camera(cam)) != 0) {
+	if ((err = reset_camera(cam)) != 0) {
 		cam->ops->close(cam->lowlevel_data);
-		rvfree(cam->decompressed_frame.data, CPIA_MAX_FRAME_SIZE);
-		cam->decompressed_frame.data = NULL;
-		rvfree(cam->raw_image, CPIA_MAX_IMAGE_SIZE);
-		cam->raw_image = NULL;
-		return i;
+		goto oops;
 	}
 	
 	/* Set ownership of /proc/cpia/videoX to current user */
@@ -2562,14 +2573,28 @@
 	cam->mmap_kludge = 0;
 	
 	++cam->open_count;
+	file->private_data = dev;
+	up(&cam->busy_lock);
 	return 0;
+
+ oops:
+	if (cam->decompressed_frame.data) {
+		rvfree(cam->decompressed_frame.data, CPIA_MAX_FRAME_SIZE);
+		cam->decompressed_frame.data = NULL;
+	}
+	if (cam->raw_image) {
+		rvfree(cam->raw_image, CPIA_MAX_IMAGE_SIZE);
+		cam->raw_image = NULL;
+	}
+	up(&cam->busy_lock);
+	put_cam(cam->ops);
+	return err;
 }
 
-static void cpia_close(struct video_device *dev)
+static int cpia_close(struct inode *inode, struct file *file)
 {
-	struct cam_data *cam;
-
-	cam = dev->priv;
+	struct  video_device *dev = file->private_data;
+	struct cam_data *cam = dev->priv;
 
 	if (cam->ops) {
 	        /* Return ownership of /proc/cpia/videoX to root */
@@ -2590,6 +2615,7 @@
 
 		/* close cpia */
 		cam->ops->close(cam->lowlevel_data);
+		put_cam(cam->ops);
 	}
 
 	if (--cam->open_count == 0) {
@@ -2607,20 +2633,20 @@
 		if (cam->frame_buf)
 			free_frame_buf(cam);
 
-		if (!cam->ops) {
-			video_unregister_device(dev);
+		if (!cam->ops) 
 			kfree(cam);
-		}
 	}
 	
+	file->private_data = NULL;
 
-	return;
+	return 0;
 }
 
-static long cpia_read(struct video_device *dev, char *buf,
-                      unsigned long count, int noblock)
+static int cpia_read(struct file *file, char *buf,
+		     size_t count, loff_t *ppos)
 {
-	struct cam_data *cam = dev->priv;
+	struct video_device *dev = file->private_data;
+ 	struct cam_data *cam = dev->priv;
 
 	/* make this _really_ smp and multithread-safe */
 	if (down_interruptible(&cam->busy_lock))
@@ -2659,7 +2685,7 @@
 	/* copy data to user space */
 	if (cam->decompressed_frame.count > count) {
 		DBG("count wrong: %d, %lu\n", cam->decompressed_frame.count,
-		    count);
+		    (unsigned long) count);
 		up(&cam->busy_lock);
 		return -EFAULT;
 	}
@@ -2674,8 +2700,10 @@
 	return cam->decompressed_frame.count;
 }
 
-static int cpia_ioctl(struct video_device *dev, unsigned int ioctlnr, void *arg)
+static int cpia_do_ioctl(struct inode *inode, struct file *file,
+			 unsigned int ioctlnr, void *arg)
 {
+	struct video_device *dev = file->private_data;
 	struct cam_data *cam = dev->priv;
 	int retval = 0;
 
@@ -2692,20 +2720,17 @@
 	/* query capabilites */
 	case VIDIOCGCAP:
 	{
-		struct video_capability b;
+		struct video_capability *b = arg;
 
 		DBG("VIDIOCGCAP\n");
-		strcpy(b.name, "CPiA Camera");
-		b.type = VID_TYPE_CAPTURE;
-		b.channels = 1;
-		b.audios = 0;
-		b.maxwidth = 352;	/* VIDEOSIZE_CIF */
-		b.maxheight = 288;
-		b.minwidth = 48;	/* VIDEOSIZE_48_48 */
-		b.minheight = 48;
-
-		if (copy_to_user(arg, &b, sizeof(b)))
-			retval = -EFAULT;
+		strcpy(b->name, "CPiA Camera");
+		b->type = VID_TYPE_CAPTURE;
+		b->channels = 1;
+		b->audios = 0;
+		b->maxwidth = 352;	/* VIDEOSIZE_CIF */
+		b->maxheight = 288;
+		b->minwidth = 48;	/* VIDEOSIZE_48_48 */
+		b->minheight = 48;
 
 		break;
 	}
@@ -2713,39 +2738,30 @@
 	/* get/set video source - we are a camera and nothing else */
 	case VIDIOCGCHAN:
 	{
-		struct video_channel v;
+		struct video_channel *v = arg;
 
 		DBG("VIDIOCGCHAN\n");
-		if (copy_from_user(&v, arg, sizeof(v))) {
-			retval = -EFAULT;
-			break;
-		}
-		if (v.channel != 0) {
+		if (v->channel != 0) {
 			retval = -EINVAL;
 			break;
 		}
 
-		v.channel = 0;
-		strcpy(v.name, "Camera");
-		v.tuners = 0;
-		v.flags = 0;
-		v.type = VIDEO_TYPE_CAMERA;
-		v.norm = 0;
+		v->channel = 0;
+		strcpy(v->name, "Camera");
+		v->tuners = 0;
+		v->flags = 0;
+		v->type = VIDEO_TYPE_CAMERA;
+		v->norm = 0;
 
-		if (copy_to_user(arg, &v, sizeof(v)))
-			retval = -EFAULT;
 		break;
 	}
 	
 	case VIDIOCSCHAN:
 	{
-		int v;
+		struct video_channel *v = arg;
 
 		DBG("VIDIOCSCHAN\n");
-		if (copy_from_user(&v, arg, sizeof(v)))
-			retval = -EFAULT;
-
-		if (retval == 0 && v != 0)
+		if (v->channel != 0)
 			retval = -EINVAL;
 
 		break;
@@ -2753,38 +2769,35 @@
 
 	/* image properties */
 	case VIDIOCGPICT:
+	{
+ 		struct video_picture *pic = arg;
+
 		DBG("VIDIOCGPICT\n");
-		if (copy_to_user(arg, &cam->vp, sizeof(struct video_picture)))
-			retval = -EFAULT;
+ 		*pic = cam->vp;
 		break;
-	
+	}
+
 	case VIDIOCSPICT:
 	{
-		struct video_picture vp;
+		struct video_picture *vp = arg;
 
 		DBG("VIDIOCSPICT\n");
 
-		/* copy_from_user */
-		if (copy_from_user(&vp, arg, sizeof(vp))) {
-			retval = -EFAULT;
-			break;
-		}
-
 		/* check validity */
-		DBG("palette: %d\n", vp.palette);
-		DBG("depth: %d\n", vp.depth);
-		if (!valid_mode(vp.palette, vp.depth)) {
+		DBG("palette: %d\n", vp->palette);
+		DBG("depth: %d\n", vp->depth);
+		if (!valid_mode(vp->palette, vp->depth)) {
 			retval = -EINVAL;
 			break;
 		}
 
 		down(&cam->param_lock);
 		/* brightness, colour, contrast need no check 0-65535 */
-		memcpy( &cam->vp, &vp, sizeof(vp) );
+		cam->vp = *vp;
 		/* update cam->params.colourParams */
-		cam->params.colourParams.brightness = vp.brightness*100/65535;
-		cam->params.colourParams.contrast = vp.contrast*100/65535;
-		cam->params.colourParams.saturation = vp.colour*100/65535;
+		cam->params.colourParams.brightness = vp->brightness*100/65535;
+		cam->params.colourParams.contrast = vp->contrast*100/65535;
+		cam->params.colourParams.saturation = vp->colour*100/65535;
 		/* contrast is in steps of 8, so round */
 		cam->params.colourParams.contrast =
 			((cam->params.colourParams.contrast + 3) / 8) * 8;
@@ -2799,34 +2812,33 @@
 		cam->cmd_queue |= COMMAND_SETCOLOURPARAMS;
 		up(&cam->param_lock);
 		DBG("VIDIOCSPICT: %d / %d // %d / %d / %d / %d\n",
-		    vp.depth, vp.palette, vp.brightness, vp.hue, vp.colour,
-		    vp.contrast);
+		    vp->depth, vp->palette, vp->brightness, vp->hue, 
+		    vp->colour, vp->contrast);
 		break;
 	}
 
 	/* get/set capture window */
 	case VIDIOCGWIN:
+	{
+		struct video_window *vw = arg;
+
 		DBG("VIDIOCGWIN\n");
 
-		if (copy_to_user(arg, &cam->vw, sizeof(struct video_window)))
-			retval = -EFAULT;
+		*vw = cam->vw;
 		break;
-	
+	}	
+
 	case VIDIOCSWIN:
 	{
 		/* copy_from_user, check validity, copy to internal structure */
-		struct video_window vw;
+		struct video_window *vw = arg;
 		DBG("VIDIOCSWIN\n");
-		if (copy_from_user(&vw, arg, sizeof(vw))) {
-			retval = -EFAULT;
-			break;
-		}
 
-		if (vw.clipcount != 0) {    /* clipping not supported */
+		if (vw->clipcount != 0) {    /* clipping not supported */
 			retval = -EINVAL;
 			break;
 		}
-		if (vw.clips != NULL) {     /* clipping not supported */
+		if (vw->clips != NULL) {     /* clipping not supported */
 			retval = -EINVAL;
 			break;
 		}
@@ -2835,8 +2847,8 @@
 		* is requested by the user???
 		*/
 		down(&cam->param_lock);
-		if (vw.width != cam->vw.width || vw.height != cam->vw.height) {
-			int video_size = match_videosize(vw.width, vw.height);
+		if (vw->width != cam->vw.width || vw->height != cam->vw.height) {
+			int video_size = match_videosize(vw->width, vw->height);
 
 			if (video_size < 0) {
 				retval = -EINVAL;
@@ -2865,41 +2877,34 @@
 	/* mmap interface */
 	case VIDIOCGMBUF:
 	{
-		struct video_mbuf vm;
+		struct video_mbuf *vm = arg;
 		int i;
 
 		DBG("VIDIOCGMBUF\n");
-		memset(&vm, 0, sizeof(vm));
-		vm.size = CPIA_MAX_FRAME_SIZE*FRAME_NUM;
-		vm.frames = FRAME_NUM;
+		memset(vm, 0, sizeof(*vm));
+		vm->size = CPIA_MAX_FRAME_SIZE*FRAME_NUM;
+		vm->frames = FRAME_NUM;
 		for (i = 0; i < FRAME_NUM; i++)
-			vm.offsets[i] = CPIA_MAX_FRAME_SIZE * i;
-
-		if (copy_to_user((void *)arg, (void *)&vm, sizeof(vm)))
-			retval = -EFAULT;
+			vm->offsets[i] = CPIA_MAX_FRAME_SIZE * i;
 
 		break;
 	}
 	
 	case VIDIOCMCAPTURE:
 	{
-		struct video_mmap vm;
+		struct video_mmap *vm = arg;
 		int video_size;
 
-		if (copy_from_user((void *)&vm, (void *)arg, sizeof(vm))) {
-			retval = -EFAULT;
-			break;
-		}
-		DBG("VIDIOCMCAPTURE: %d / %d / %dx%d\n", vm.format, vm.frame,
-		    vm.width, vm.height);
-		if (vm.frame<0||vm.frame>=FRAME_NUM) {
+		DBG("VIDIOCMCAPTURE: %d / %d / %dx%d\n", vm->format, vm->frame,
+		    vm->width, vm->height);
+		if (vm->frame<0||vm->frame>=FRAME_NUM) {
 			retval = -EINVAL;
 			break;
 		}
 
 		/* set video format */
-		cam->vp.palette = vm.format;
-		switch(vm.format) {
+		cam->vp.palette = vm->format;
+		switch(vm->format) {
 		case VIDEO_PALETTE_GREY:
 			cam->vp.depth=8;
 			break;
@@ -2924,7 +2929,7 @@
 			break;
 
 		/* set video size */
-		video_size = match_videosize(vm.width, vm.height);
+		video_size = match_videosize(vm->width, vm->height);
 		if (video_size < 0) {
 			retval = -EINVAL;
 			break;
@@ -2937,37 +2942,33 @@
 		}
 		/* according to v4l-spec we must start streaming here */
 		cam->mmap_kludge = 1;
-		retval = capture_frame(cam, &vm);
+		retval = capture_frame(cam, vm);
 
 		break;
 	}
 	
 	case VIDIOCSYNC:
 	{
-		int frame;
+		int *frame = arg;
 
-		if (copy_from_user((void *)&frame, arg, sizeof(int))) {
-			retval = -EFAULT;
-			break;
-		}
-		//DBG("VIDIOCSYNC: %d\n", frame);
+		//DBG("VIDIOCSYNC: %d\n", *frame);
 
-		if (frame<0 || frame >= FRAME_NUM) {
+		if (*frame<0 || *frame >= FRAME_NUM) {
 			retval = -EINVAL;
 			break;
 		}
 
-		switch (cam->frame[frame].state) {
+		switch (cam->frame[*frame].state) {
 		case FRAME_UNUSED:
 		case FRAME_READY:
 		case FRAME_GRABBING:
-			DBG("sync to unused frame %d\n", frame);
+			DBG("sync to unused frame %d\n", *frame);
 			retval = -EINVAL;
 			break;
 
 		case FRAME_DONE:
-			cam->frame[frame].state = FRAME_UNUSED;
-			//DBG("VIDIOCSYNC: %d synced\n", frame);
+			cam->frame[*frame].state = FRAME_UNUSED;
+			//DBG("VIDIOCSYNC: %d synced\n", *frame);
 			break;
 		}
 		if (retval == -EINTR) {
@@ -2979,36 +2980,22 @@
 
 	/* pointless to implement overlay with this camera */
 	case VIDIOCCAPTURE:
-		retval = -EINVAL;
-		break;
 	case VIDIOCGFBUF:
-		retval = -EINVAL;
-		break;
 	case VIDIOCSFBUF:
-		retval = -EINVAL;
-		break;
 	case VIDIOCKEY:
 		retval = -EINVAL;
 		break;
 
 	/* tuner interface - we have none */
 	case VIDIOCGTUNER:
-		retval = -EINVAL;
-		break;
 	case VIDIOCSTUNER:
-		retval = -EINVAL;
-		break;
 	case VIDIOCGFREQ:
-		retval = -EINVAL;
-		break;
 	case VIDIOCSFREQ:
 		retval = -EINVAL;
 		break;
 
 	/* audio interface - we have none */
 	case VIDIOCGAUDIO:
-		retval = -EINVAL;
-		break;
 	case VIDIOCSAUDIO:
 		retval = -EINVAL;
 		break;
@@ -3017,16 +3004,23 @@
 		break;
 	}
 
-	up(&cam->param_lock);
 	up(&cam->busy_lock);
 	return retval;
 } 
 
+static int cpia_ioctl(struct inode *inode, struct file *file,
+		     unsigned int cmd, unsigned long arg)
+{
+	return video_usercopy(inode, file, cmd, arg, cpia_do_ioctl);
+}
+
+
 /* FIXME */
-static int cpia_mmap(struct video_device *dev, const char *adr,
-                     unsigned long size)
+static int cpia_mmap(struct file *file, struct vm_area_struct *vma)
 {
-	unsigned long start = (unsigned long)adr;
+	struct video_device *dev = file->private_data;
+	unsigned long start = vma->vm_start;
+	unsigned long size  = vma->vm_end - vma->vm_start;
 	unsigned long page, pos;
 	struct cam_data *cam = dev->priv;
 	int retval;
@@ -3074,25 +3068,22 @@
 	return 0;
 }
 
-int cpia_video_init(struct video_device *vdev)
-{
-#ifdef CONFIG_PROC_FS
-	create_proc_cpia_cam(vdev->priv);
-#endif
-	return 0;
-}
+static struct file_operations cpia_fops = {
+	owner:		THIS_MODULE,
+	open:		cpia_open,
+	release:	cpia_close,
+	read:		cpia_read,
+	mmap:		cpia_mmap,
+	ioctl:		cpia_ioctl,
+	llseek:         no_llseek,
+};
 
 static struct video_device cpia_template = {
 	owner:		THIS_MODULE,
 	name:		"CPiA Camera",
 	type:		VID_TYPE_CAPTURE,
 	hardware:	VID_HARDWARE_CPIA,
-	open:		cpia_open,
-	close:		cpia_close,
-	read:		cpia_read,
-	ioctl:		cpia_ioctl,
-	mmap:		cpia_mmap,
-	initialize:	cpia_video_init,
+	fops:           &cpia_fops,
 	minor:		-1,
 };
 
@@ -3229,16 +3220,10 @@
 
 struct cam_data *cpia_register_camera(struct cpia_camera_ops *ops, void *lowlevel)
 {
-        struct cam_data *camera;
-	
-	/* Need a lock when adding/removing cameras.  This doesn't happen
-	 * often and doesn't take very long, so grabbing the kernel lock
-	 * should be OK. */
+	struct cam_data *camera;
 	
-	if ((camera = kmalloc(sizeof(struct cam_data), GFP_KERNEL)) == NULL) {
-		unlock_kernel();
+	if ((camera = kmalloc(sizeof(struct cam_data), GFP_KERNEL)) == NULL)
 		return NULL;
-	}
 	
 	init_camera_struct( camera, ops );
 	camera->lowlevel_data = lowlevel;
@@ -3246,7 +3231,6 @@
 	/* register v4l device */
 	if (video_register_device(&camera->vdev, VFL_TYPE_GRABBER, video_nr) == -1) {
 		kfree(camera);
-		unlock_kernel();
 		printk(KERN_DEBUG "video_register_device failed\n");
 		return NULL;
 	}
@@ -3266,6 +3250,10 @@
 	/* close cpia */
 	camera->ops->close(camera->lowlevel_data);
 
+#ifdef CONFIG_PROC_FS
+	create_proc_cpia_cam(camera);
+#endif
+
 	printk(KERN_INFO "  CPiA Version: %d.%02d (%d.%d)\n",
 	       camera->params.version.firmwareVersion,
 	       camera->params.version.firmwareRevision,
@@ -3285,12 +3273,10 @@
 
 void cpia_unregister_camera(struct cam_data *cam)
 {
-	if (!cam->open_count) {
-		DBG("unregistering video\n");
-		video_unregister_device(&cam->vdev);
-	} else {
-		LOG("/dev/video%d removed while open, "
-		    "deferring video_unregister_device\n", cam->vdev.minor);
+	DBG("unregistering video\n");
+	video_unregister_device(&cam->vdev);
+	if (cam->open_count) {
+		put_cam(cam->ops);
 		DBG("camera open -- setting ops to NULL\n");
 		cam->ops = NULL;
 	}
@@ -3313,9 +3299,6 @@
 	proc_cpia_create();
 #endif
 
-#ifdef CONFIG_VIDEO_CPIA_PP
-	cpia_pp_init();
-#endif
 #ifdef CONFIG_KMOD
 #ifdef CONFIG_VIDEO_CPIA_PP_MODULE
 	request_module("cpia_pp");
@@ -3325,6 +3308,10 @@
 	request_module("cpia_usb");
 #endif
 #endif	/* CONFIG_KMOD */
+
+#ifdef CONFIG_VIDEO_CPIA_PP
+	cpia_pp_init();
+#endif
 #ifdef CONFIG_VIDEO_CPIA_USB
 	cpia_usb_init();
 #endif
diff -urN linux-2.4.21-pre5/drivers/media/video/cpia.h linux-2.4.21-pre6/drivers/media/video/cpia.h
--- linux-2.4.21-pre5/drivers/media/video/cpia.h	Thu Nov 28 15:53:13 2002
+++ linux-2.4.21-pre6/drivers/media/video/cpia.h	Wed Mar 26 16:11:36 2003
@@ -27,12 +27,16 @@
  */
 
 #define CPIA_MAJ_VER	0
-#define CPIA_MIN_VER    8
-#define CPIA_PATCH_VER	1
+#define CPIA_MIN_VER   8
+#define CPIA_PATCH_VER	5
 
-#define CPIA_PP_MAJ_VER       0
-#define CPIA_PP_MIN_VER       8
-#define CPIA_PP_PATCH_VER     1
+#define CPIA_PP_MAJ_VER       CPIA_MAJ_VER
+#define CPIA_PP_MIN_VER       CPIA_MIN_VER
+#define CPIA_PP_PATCH_VER     CPIA_PATCH_VER
+
+#define CPIA_USB_MAJ_VER      CPIA_MAJ_VER
+#define CPIA_USB_MIN_VER      CPIA_MIN_VER
+#define CPIA_USB_PATCH_VER    CPIA_PATCH_VER
 
 #define CPIA_MAX_FRAME_SIZE_UNALIGNED	(352 * 288 * 4)   /* CIF at RGB32 */
 #define CPIA_MAX_FRAME_SIZE	((CPIA_MAX_FRAME_SIZE_UNALIGNED + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1)) /* align above to PAGE_SIZE */
@@ -41,6 +45,7 @@
 
 #include <asm/uaccess.h>
 #include <linux/videodev.h>
+#include <linux/list.h>
 #include <linux/smp_lock.h>
 
 struct cpia_camera_ops
@@ -93,6 +98,10 @@
 	 * is STREAM_READY before calling streamRead.
 	 */
 	int wait_for_stream_ready;
+	/*
+	 * Used to maintain lowlevel module usage counts
+	 */
+	struct module *owner;
 };
 
 struct cpia_frame {
@@ -233,8 +242,7 @@
 #define FRAME_NUM	2	/* double buffering for now */
 
 struct cam_data {
-	struct cam_data **previous;
-	struct cam_data *next;
+	struct list_head cam_data_list;
 
         struct semaphore busy_lock;     /* guard against SMP multithreading */
 	struct cpia_camera_ops *ops;	/* lowlevel driver operations */
@@ -400,24 +408,6 @@
       (p)&0x80?1:0, (p)&0x40?1:0, (p)&0x20?1:0, (p)&0x10?1:0,\
         (p)&0x08?1:0, (p)&0x04?1:0, (p)&0x02?1:0, (p)&0x01?1:0);
 
-static inline void cpia_add_to_list(struct cam_data* l, struct cam_data* drv)
-{
-	drv->next = l;
-	drv->previous = &l;
-	l = drv;
-}
-
-static inline void cpia_remove_from_list(struct cam_data* drv)
-{
-	if (drv->previous != NULL) {
-		if (drv->next != NULL)
-			drv->next->previous = drv->previous;
-		*(drv->previous) = drv->next;
-		drv->previous = NULL;
-		drv->next = NULL;
-	}
-}
-
 #endif /* __KERNEL__ */
 
 #endif /* cpia_h */
diff -urN linux-2.4.21-pre5/drivers/media/video/cpia_pp.c linux-2.4.21-pre6/drivers/media/video/cpia_pp.c
--- linux-2.4.21-pre5/drivers/media/video/cpia_pp.c	Thu Nov 28 15:53:13 2002
+++ linux-2.4.21-pre6/drivers/media/video/cpia_pp.c	Wed Mar 26 16:11:36 2003
@@ -22,6 +22,9 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+/* define _CPIA_DEBUG_ for verbose debug output (see cpia.h) */
+/* #define _CPIA_DEBUG_  1 */  
+
 #include <linux/config.h>
 #include <linux/version.h>
 
@@ -33,6 +36,7 @@
 #include <linux/interrupt.h>
 #include <linux/delay.h>
 #include <linux/smp_lock.h>
+#include <linux/sched.h>
 
 #include <linux/kmod.h>
 
@@ -50,65 +54,6 @@
 
 #define ABOUT "Parallel port driver for Vision CPiA based cameras"
 
-/* IEEE 1284 Compatiblity Mode signal names 	*/
-#define nStrobe		PARPORT_CONTROL_STROBE	  /* inverted */
-#define nAutoFd		PARPORT_CONTROL_AUTOFD	  /* inverted */
-#define nInit		PARPORT_CONTROL_INIT
-#define nSelectIn	PARPORT_CONTROL_SELECT
-#define IntrEnable	PARPORT_CONTROL_INTEN	  /* normally zero for no IRQ */
-#define DirBit		PARPORT_CONTROL_DIRECTION /* 0 = Forward, 1 = Reverse	*/
-
-#define nFault		PARPORT_STATUS_ERROR
-#define Select		PARPORT_STATUS_SELECT
-#define PError		PARPORT_STATUS_PAPEROUT
-#define nAck		PARPORT_STATUS_ACK
-#define Busy		PARPORT_STATUS_BUSY	  /* inverted */	
-
-/* some more */
-#define HostClk		nStrobe
-#define HostAck		nAutoFd
-#define nReverseRequest	nInit
-#define Active_1284	nSelectIn
-#define nPeriphRequest	nFault
-#define XFlag		Select
-#define nAckReverse	PError
-#define PeriphClk	nAck
-#define PeriphAck	Busy
-
-/* these can be used to correct for the inversion on some bits */
-#define STATUS_INVERSION_MASK	(Busy)
-#define CONTROL_INVERSION_MASK	(nStrobe|nAutoFd|nSelectIn)
-
-#define ECR_empty	0x01
-#define ECR_full	0x02
-#define ECR_serviceIntr 0x04
-#define ECR_dmaEn	0x08
-#define ECR_nErrIntrEn	0x10
-
-#define ECR_mode_mask	0xE0
-#define ECR_SPP_mode	0x00
-#define ECR_PS2_mode	0x20
-#define ECR_FIFO_mode	0x40
-#define ECR_ECP_mode	0x60
-
-#define ECP_FIFO_SIZE	16
-#define DMA_BUFFER_SIZE               PAGE_SIZE
-	/* for 16bit DMA make sure DMA_BUFFER_SIZE is 16 bit aligned */
-#define PARPORT_CHUNK_SIZE	PAGE_SIZE/* >=2.3.x */
-				/* we read this many bytes at once */
-
-#define GetECRMasked(port,mask)	(parport_read_econtrol(port) & (mask))
-#define GetStatus(port)		((parport_read_status(port)^STATUS_INVERSION_MASK)&(0xf8))
-#define SetStatus(port,val)	parport_write_status(port,(val)^STATUS_INVERSION_MASK)
-#define GetControl(port)	((parport_read_control(port)^CONTROL_INVERSION_MASK)&(0x3f))
-#define SetControl(port,val)	parport_write_control(port,(val)^CONTROL_INVERSION_MASK)
-
-#define GetStatusMasked(port,mask)	(GetStatus(port) & (mask))
-#define GetControlMasked(port,mask)	(GetControl(port) & (mask))
-#define SetControlMasked(port,mask)	SetControl(port,GetControl(port) | (mask));
-#define ClearControlMasked(port,mask)	SetControl(port,GetControl(port)&~(mask));
-#define FrobControlBit(port,mask,value)	SetControl(port,(GetControl(port)&~(mask))|((value)&(mask)));
-
 #define PACKET_LENGTH 	8
 
 /* Magic numbers for defining port-device mappings */
@@ -156,35 +101,13 @@
 	cpia_pp_streamStop,
 	cpia_pp_streamRead,
 	cpia_pp_close,
-	1
+	1,
+	THIS_MODULE
 };
 
-static struct cam_data *cam_list;
+static LIST_HEAD(cam_list);
 static spinlock_t cam_list_lock_pp;
 
-#ifdef _CPIA_DEBUG_
-#define DEB_PORT(port) { \
-u8 controll = GetControl(port); \
-u8 statusss = GetStatus(port); \
-DBG("nsel %c per %c naut %c nstrob %c nak %c busy %c nfaul %c sel %c init %c dir %c\n",\
-((controll & nSelectIn)	? 'U' : 'D'), \
-((statusss & PError)	? 'U' : 'D'), \
-((controll & nAutoFd)	? 'U' : 'D'), \
-((controll & nStrobe)	? 'U' : 'D'), \
-((statusss & nAck)	? 'U' : 'D'), \
-((statusss & Busy)	? 'U' : 'D'), \
-((statusss & nFault)	? 'U' : 'D'), \
-((statusss & Select)	? 'U' : 'D'), \
-((controll & nInit)	? 'U' : 'D'), \
-((controll & DirBit)	? 'R' : 'F')  \
-); }
-#else
-#define DEB_PORT(port) {}
-#endif
-
-#define WHILE_OUT_TIMEOUT (HZ/10)
-#define DMA_TIMEOUT 10*HZ
-
 /* FIXME */
 static void cpia_parport_enable_irq( struct parport *port ) {
 	parport_enable_irq(port);
@@ -198,6 +121,14 @@
 	return;
 }
 
+/* Special CPiA PPC modes: These are invoked by using the 1284 Extensibility
+ * Link Flag during negotiation */  
+#define UPLOAD_FLAG  0x08
+#define ECP_TRANSFER 0x03
+
+#define PARPORT_CHUNK_SIZE	PAGE_SIZE
+
+
 /****************************************************************************
  *
  *  EndTransferMode
@@ -240,7 +171,7 @@
 {
 	int retry;
 	int mode = IEEE1284_MODE_ECP;
-	if(extensibility) mode = 8|3|IEEE1284_EXT_LINK;
+	if(extensibility) mode = UPLOAD_FLAG|ECP_TRANSFER|IEEE1284_EXT_LINK;
 
 	/* After some commands the camera needs extra time before
 	 * it will respond again, so we try up to 3 times */
@@ -345,11 +276,22 @@
  *  cpia_pp_streamRead
  *
  ***************************************************************************/
+static int cpia_pp_read(struct parport *port, u8 *buffer, int len)
+{
+	int bytes_read, new_bytes;
+	for(bytes_read=0; bytes_read<len; bytes_read += new_bytes) {
+		new_bytes = parport_read(port, buffer+bytes_read,
+			                 len-bytes_read);
+		if(new_bytes < 0) break;
+	}
+	return bytes_read;
+}
+
 static int cpia_pp_streamRead(void *privdata, u8 *buffer, int noblock)
 {
 	struct pp_cam_entry *cam = privdata;
 	int read_bytes = 0;
-	int i, endseen;
+	int i, endseen, block_size, new_bytes;
 
 	if(cam == NULL) {
 		DBG("Internal driver error: cam is NULL\n");
@@ -378,25 +320,34 @@
 			return -EIO;
 		}
 	}
-	read_bytes = parport_read(cam->port, buffer, CPIA_MAX_IMAGE_SIZE );
-
-	EndTransferMode(cam);
-	DBG("read %d bytes\n", read_bytes);
-	if( read_bytes<0) return -EIO;
 	endseen = 0;
-	endseen = 0;
-	for( i=0; i<read_bytes && endseen<4; i++ ) {
-	  if( *buffer==EOI ) {
-	    endseen++;
-	  } else {
-	    endseen=0;
-	  }
-	  buffer++;
-	}
-	if( endseen>3 ) {
-	  cam->image_complete=1;
-	  DBG("endseen at %d bytes\n", i);
+	block_size = PARPORT_CHUNK_SIZE;
+	while( !cam->image_complete ) {
+		if(current->need_resched)  schedule();
+		
+		new_bytes = cpia_pp_read(cam->port, buffer, block_size );
+		if( new_bytes <= 0 ) {
+			break;
+		}
+		i=-1;
+		while(++i<new_bytes && endseen<4) {
+	        	if(*buffer==EOI) {
+	                	endseen++;
+	                } else {
+	                	endseen=0;
+	                }
+			buffer++;
+		}
+		read_bytes += i;
+		if( endseen==4 ) {
+			cam->image_complete=1;
+			break;
+		}
+		if( CPIA_MAX_IMAGE_SIZE-read_bytes <= PARPORT_CHUNK_SIZE ) {
+			block_size=CPIA_MAX_IMAGE_SIZE-read_bytes;
+		}
 	}
+	EndTransferMode(cam);
 	return cam->image_complete ? read_bytes : -EIO;
 }
 
@@ -566,7 +517,7 @@
 		return -ENXIO;
 	}
 	spin_lock( &cam_list_lock_pp );
-	cpia_add_to_list(cam_list, cpia);
+	list_add( &cpia->cam_data_list, &cam_list );
 	spin_unlock( &cam_list_lock_pp );
 
 	return 0;
@@ -574,27 +525,34 @@
 
 static void cpia_pp_detach (struct parport *port)
 {
-	struct cam_data *cpia;
+	struct list_head *tmp;
+	struct cam_data *cpia = NULL;
+	struct pp_cam_entry *cam;
 
 	spin_lock( &cam_list_lock_pp );
-	for(cpia = cam_list; cpia != NULL; cpia = cpia->next) {
-		struct pp_cam_entry *cam = cpia->lowlevel_data;
+	list_for_each (tmp, &cam_list) {
+		cpia = list_entry(tmp, struct cam_data, cam_data_list);
+		cam = (struct pp_cam_entry *) cpia->lowlevel_data;
 		if (cam && cam->port->number == port->number) {
-			cpia_remove_from_list(cpia);
-			spin_unlock( &cam_list_lock_pp );			
-			cpia_unregister_camera(cpia);
-			
-			if(cam->open_count > 0) {
-				cpia_pp_close(cam);
-			}
-
-			parport_unregister_device(cam->pdev);
-		
-			kfree(cam);
-			cpia->lowlevel_data = NULL;
+			list_del(&cpia->cam_data_list);
 			break;
 		}
+		cpia = NULL;
+	}
+	spin_unlock( &cam_list_lock_pp );			
+
+	if (!cpia) {
+		DBG("cpia_pp_detach failed to find cam_data in cam_list\n");
+		return;
 	}
+	
+	cam = (struct pp_cam_entry *) cpia->lowlevel_data;	
+	cpia_unregister_camera(cpia);
+	if(cam->open_count > 0) 
+		cpia_pp_close(cam);
+	parport_unregister_device(cam->pdev);
+	cpia->lowlevel_data = NULL;	
+	kfree(cam);
 }
 
 static void cpia_pp_attach (struct parport *port)
@@ -642,14 +600,12 @@
 		return 0;
 	}
 	
+	spin_lock_init( &cam_list_lock_pp );
+
 	if (parport_register_driver (&cpia_pp_driver)) {
 		LOG ("unable to register with parport\n");
 		return -EIO;
 	}
-	
-	cam_list = NULL;
-	spin_lock_init( &cam_list_lock_pp );
-
 	return 0;
 }
 
diff -urN linux-2.4.21-pre5/drivers/media/video/cpia_usb.c linux-2.4.21-pre6/drivers/media/video/cpia_usb.c
--- linux-2.4.21-pre5/drivers/media/video/cpia_usb.c	Thu Nov 28 15:53:13 2002
+++ linux-2.4.21-pre6/drivers/media/video/cpia_usb.c	Wed Mar 26 16:11:36 2003
@@ -21,11 +21,13 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+/* define _CPIA_DEBUG_ for verbose debug output (see cpia.h) */
+/* #define _CPIA_DEBUG_  1 */  
+
 #include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/init.h>
 #include <linux/wait.h>
-#include <linux/sched.h>
 #include <linux/list.h>
 #include <linux/slab.h>
 #include <linux/vmalloc.h>
@@ -100,10 +102,11 @@
 	cpia_usb_streamStop,
 	cpia_usb_streamRead,
 	cpia_usb_close,
-	0
+	0,
+	THIS_MODULE
 };
 
-static struct cam_data *cam_list;
+static LIST_HEAD(cam_list);
 static spinlock_t cam_list_lock_usb;
 
 static void cpia_usb_complete(struct urb *urb)
@@ -456,12 +459,14 @@
 {
 	struct usb_cpia *ucpia = (struct usb_cpia *) privdata;
 
-	ucpia->open = 0;
+	if(!ucpia)
+		return -ENODEV;
 
-	cpia_usb_free_resources(ucpia, 1);
+	ucpia->open = 0;
 
-	if (!ucpia->present)
-		kfree(ucpia);
+	/* ucpia->present = 0 protects against trying to reset the
+	 * alt setting if camera is physically disconnected while open */
+	cpia_usb_free_resources(ucpia, ucpia->present);
 
 	return 0;
 }
@@ -540,7 +545,7 @@
 	}
 
 	spin_lock( &cam_list_lock_usb );
-	cpia_add_to_list(cam_list, cam);
+	list_add( &cam->cam_data_list, &cam_list );
 	spin_unlock( &cam_list_lock_usb );
 
 	return cam;
@@ -555,6 +560,7 @@
 	vfree(ucpia->buffers[0]);
 	ucpia->buffers[0] = NULL;
 fail_alloc_0:
+	kfree(ucpia);
 
 	return NULL;
 }
@@ -578,76 +584,57 @@
 	id_table:	cpia_id_table,
 };
 
-/* don't use dev, it may be NULL! (see usb_cpia_cleanup) */
-/* _disconnect from usb_cpia_cleanup is not necessary since usb_deregister */
-/* will do it for us as well as passing a udev structure - jerdfelt */
 static void cpia_disconnect(struct usb_device *udev, void *ptr)
 {
 	struct cam_data *cam = (struct cam_data *) ptr;
 	struct usb_cpia *ucpia = (struct usb_cpia *) cam->lowlevel_data;
   
 	spin_lock( &cam_list_lock_usb );
-	cpia_remove_from_list(cam);
+	list_del(&cam->cam_data_list);
 	spin_unlock( &cam_list_lock_usb );
 	
-	/* Don't even try to reset the altsetting if we're disconnected */
-	cpia_usb_free_resources(ucpia, 0);
-
 	ucpia->present = 0;
-
 	cpia_unregister_camera(cam);
+	if(ucpia->open)
+		cpia_usb_close(cam->lowlevel_data);
 
 	ucpia->curbuff->status = FRAME_ERROR;
-
 	if (waitqueue_active(&ucpia->wq_stream))
 		wake_up_interruptible(&ucpia->wq_stream);
-
 	usb_driver_release_interface(&cpia_driver,
 				     &udev->actconfig->interface[0]);
-
 	ucpia->curbuff = ucpia->workbuff = NULL;
-
 	if (ucpia->buffers[2]) {
 		vfree(ucpia->buffers[2]);
 		ucpia->buffers[2] = NULL;
 	}
-
 	if (ucpia->buffers[1]) {
 		vfree(ucpia->buffers[1]);
 		ucpia->buffers[1] = NULL;
 	}
-
 	if (ucpia->buffers[0]) {
 		vfree(ucpia->buffers[0]);
 		ucpia->buffers[0] = NULL;
 	}
 
-	if (!ucpia->open) {
-		kfree(ucpia);
-		cam->lowlevel_data = NULL;
-	}
+	cam->lowlevel_data = NULL;
+	kfree(ucpia);
 }
 
 static int __init usb_cpia_init(void)
 {
-	cam_list = NULL;
+	printk(KERN_INFO "%s v%d.%d.%d\n",ABOUT, 
+	       CPIA_USB_MAJ_VER,CPIA_USB_MIN_VER,CPIA_USB_PATCH_VER);
+
 	spin_lock_init(&cam_list_lock_usb);
 	return usb_register(&cpia_driver);
 }
 
 static void __exit usb_cpia_cleanup(void)
 {
-/*
-	struct cam_data *cam;
-
-	while ((cam = cam_list) != NULL)
-		cpia_disconnect(NULL, cam);
-*/
-
 	usb_deregister(&cpia_driver);
 }
 
-
 module_init (usb_cpia_init);
 module_exit (usb_cpia_cleanup);
 
diff -urN linux-2.4.21-pre5/drivers/media/video/w9966.c linux-2.4.21-pre6/drivers/media/video/w9966.c
--- linux-2.4.21-pre5/drivers/media/video/w9966.c	Fri Aug  2 17:39:44 2002
+++ linux-2.4.21-pre6/drivers/media/video/w9966.c	Wed Mar 26 16:11:36 2003
@@ -931,7 +931,7 @@
 		if(copy_from_user(&vtune, arg, sizeof(vtune)) != 0)
 			return -EFAULT;
 
-		if(vtune.tuner != 0);
+		if(vtune.tuner != 0)
 			return -EINVAL;
 
 		strcpy(vtune.name, "no tuner");
diff -urN linux-2.4.21-pre5/drivers/message/fusion/mptbase.c linux-2.4.21-pre6/drivers/message/fusion/mptbase.c
--- linux-2.4.21-pre5/drivers/message/fusion/mptbase.c	Wed Mar 26 16:11:28 2003
+++ linux-2.4.21-pre6/drivers/message/fusion/mptbase.c	Wed Mar 26 16:11:36 2003
@@ -1802,6 +1802,7 @@
 	if (this != NULL) {
 		int sz;
 		u32 state;
+		int ret;
 
 		/* Disable the FW */
 		state = mpt_GetIocState(this, 1);
@@ -1812,9 +1813,9 @@
 		if (this->cached_fw != NULL) {
 			ddlprintk((KERN_INFO MYNAM ": Pushing FW onto adapter\n"));
 
-			if ((state = mpt_downloadboot(this, NO_SLEEP)) < 0) {
+			if ((ret = mpt_downloadboot(this, NO_SLEEP)) < 0) {
 				printk(KERN_WARNING MYNAM 
-					": firmware downloadboot failure (%d)!\n", state);
+					": firmware downloadboot failure (%d)!\n", ret);
 			}
 		}
 
diff -urN linux-2.4.21-pre5/drivers/mtd/chips/Makefile linux-2.4.21-pre6/drivers/mtd/chips/Makefile
--- linux-2.4.21-pre5/drivers/mtd/chips/Makefile	Fri Aug  2 17:39:44 2002
+++ linux-2.4.21-pre6/drivers/mtd/chips/Makefile	Wed Mar 26 16:11:37 2003
@@ -17,6 +17,7 @@
 obj-$(CONFIG_MTD)		+= chipreg.o
 obj-$(CONFIG_MTD_AMDSTD)	+= amd_flash.o 
 obj-$(CONFIG_MTD_CFI)		+= cfi_probe.o
+obj-$(CONFIG_MTD_CFI_STAA)	+= cfi_cmdset_0020.o
 obj-$(CONFIG_MTD_CFI_AMDSTD)	+= cfi_cmdset_0002.o
 obj-$(CONFIG_MTD_CFI_INTELEXT)	+= cfi_cmdset_0001.o
 obj-$(CONFIG_MTD_GEN_PROBE)	+= gen_probe.o
diff -urN linux-2.4.21-pre5/drivers/mtd/nftlmount.c linux-2.4.21-pre6/drivers/mtd/nftlmount.c
--- linux-2.4.21-pre5/drivers/mtd/nftlmount.c	Wed Mar 26 16:11:28 2003
+++ linux-2.4.21-pre6/drivers/mtd/nftlmount.c	Wed Mar 26 16:11:37 2003
@@ -94,7 +94,7 @@
 
 		/* To be safer with BIOS, also use erase mark as discriminant */
 		if ((ret = MTD_READOOB(nftl->mtd, block * nftl->EraseSize + SECTORSIZE + 8,
-				8, &retlen, (char *)&h1) < 0)) {
+				8, &retlen, (char *)&h1)) < 0) {
 			printk(KERN_WARNING "ANAND header found at 0x%x in mtd%d, but OOB data read failed (err %d)\n",
 			       block * nftl->EraseSize, nftl->mtd->index, ret);
 			continue;
@@ -113,7 +113,7 @@
 
 		/* Finally reread to check ECC */
 		if ((ret = MTD_READECC(nftl->mtd, block * nftl->EraseSize, SECTORSIZE,
-				&retlen, buf, (char *)&oob, NAND_ECC_DISKONCHIP) < 0)) {
+				&retlen, buf, (char *)&oob, NAND_ECC_DISKONCHIP)) < 0) {
 			printk(KERN_NOTICE "ANAND header found at 0x%x in mtd%d, but ECC read failed (err %d)\n",
 			       block * nftl->EraseSize, nftl->mtd->index, ret);
 			continue;
diff -urN linux-2.4.21-pre5/drivers/net/82596.c linux-2.4.21-pre6/drivers/net/82596.c
--- linux-2.4.21-pre5/drivers/net/82596.c	Wed Mar 26 16:11:28 2003
+++ linux-2.4.21-pre6/drivers/net/82596.c	Wed Mar 26 16:11:37 2003
@@ -649,7 +649,7 @@
 
 	/* change the scp address */
 
-	MPU_PORT(dev, PORT_ALTSCP, (void *)virt_to_bus(&lp->scp));
+	MPU_PORT(dev, PORT_ALTSCP, (void *)virt_to_bus((void *)&lp->scp));
 
 #elif defined(ENABLE_APRICOT)
 
@@ -680,8 +680,8 @@
 		lp->scp.sysbus = 0x00440000;
 #endif
 
-	lp->scp.iscp = WSWAPiscp(virt_to_bus(&(lp->iscp)));
-	lp->iscp.scb = WSWAPscb(virt_to_bus(&(lp->scb)));
+	lp->scp.iscp = WSWAPiscp(virt_to_bus((void *)&lp->iscp));
+	lp->iscp.scb = WSWAPscb(virt_to_bus((void *)&lp->scb));
 	lp->iscp.stat = ISCP_BUSY;
 	lp->cmd_backlog = 0;
 
diff -urN linux-2.4.21-pre5/drivers/net/apne.c linux-2.4.21-pre6/drivers/net/apne.c
--- linux-2.4.21-pre5/drivers/net/apne.c	Fri Aug  2 17:39:44 2002
+++ linux-2.4.21-pre6/drivers/net/apne.c	Wed Mar 26 16:11:37 2003
@@ -113,9 +113,6 @@
 #define MANUAL_HWADDR5 0x9a
 */
 
-#define WORDSWAP(a) ( (((a)>>8)&0xff) | ((a)<<8) )
-
-
 static const char version[] =
     "apne.c:v1.1 7/10/98 Alain Malek (Alain.Malek@cryogen.ch)\n";
 
@@ -403,10 +400,9 @@
     }
 
     outb(ENISR_RDC, nic_base + NE_EN0_ISR);	/* Ack intr. */
-
-    hdr->count = WORDSWAP(hdr->count);
-
     ei_status.dmaing &= ~0x01;
+
+    le16_to_cpus(&hdr->count);
 }
 
 /* Block input and output, similar to the Crynwr packet driver.  If you
diff -urN linux-2.4.21-pre5/drivers/net/dmfe.c linux-2.4.21-pre6/drivers/net/dmfe.c
--- linux-2.4.21-pre5/drivers/net/dmfe.c	Wed Mar 26 16:11:28 2003
+++ linux-2.4.21-pre6/drivers/net/dmfe.c	Wed Mar 26 16:11:37 2003
@@ -48,6 +48,10 @@
     Use time_after for jiffies calculation.  Added ethtool
     support.  Updated PCI resource allocation.  Do not
     forget to unmap PCI mapped skbs.
+    
+    Alan Cox <alan@redhat.com>
+    Added new PCI identifiers provided by Clear Zhang at ALi 
+    for their 1563 ethernet device.
 
     TODO
 
@@ -1977,6 +1981,7 @@
 	{ 0x1282, 0x9102, PCI_ANY_ID, PCI_ANY_ID, 0, 0, PCI_DM9102_ID },
 	{ 0x1282, 0x9100, PCI_ANY_ID, PCI_ANY_ID, 0, 0, PCI_DM9100_ID },
 	{ 0x1282, 0x9009, PCI_ANY_ID, PCI_ANY_ID, 0, 0, PCI_DM9009_ID },
+	{ 0x10B9, 0x5261, PCI_ANY_ID, PCI_ANY_ID, 0, 0, PCI_DM9102_ID },
 	{ 0, }
 };
 MODULE_DEVICE_TABLE(pci, dmfe_pci_tbl);
diff -urN linux-2.4.21-pre5/drivers/net/e100/e100_main.c linux-2.4.21-pre6/drivers/net/e100/e100_main.c
--- linux-2.4.21-pre5/drivers/net/e100/e100_main.c	Wed Mar 26 16:11:28 2003
+++ linux-2.4.21-pre6/drivers/net/e100/e100_main.c	Wed Mar 26 16:11:37 2003
@@ -607,7 +607,9 @@
 	}
 
 	if (((bdp->pdev->device > 0x1030)
-	     && (bdp->pdev->device < 0x103F))
+	       && (bdp->pdev->device < 0x103F))
+	    || ((bdp->pdev->device >= 0x1050)
+	       && (bdp->pdev->device <= 0x1057))
 	    || (bdp->pdev->device == 0x2449)
 	    || (bdp->pdev->device == 0x2459)
 	    || (bdp->pdev->device == 0x245D)) {
diff -urN linux-2.4.21-pre5/drivers/net/e100/e100_test.c linux-2.4.21-pre6/drivers/net/e100/e100_test.c
--- linux-2.4.21-pre5/drivers/net/e100/e100_test.c	Wed Mar 26 16:11:28 2003
+++ linux-2.4.21-pre6/drivers/net/e100/e100_test.c	Wed Mar 26 16:11:37 2003
@@ -242,6 +242,9 @@
 		 *dynamic_tbd = e100_config_dynamic_tbd(bdp,*dynamic_tbd);
 
 	if (set_loopback) {
+		/* ICH PHY loopback is broken */
+		if (bdp->flags & IS_ICH && loopback_mode == PHY_LOOPBACK)
+			loopback_mode = MAC_LOOPBACK;
 		/* Configure loopback on MAC */
 		e100_config_loopback_mode(bdp,loopback_mode);
 	} else {
diff -urN linux-2.4.21-pre5/drivers/net/e100/e100_vendor.h linux-2.4.21-pre6/drivers/net/e100/e100_vendor.h
--- linux-2.4.21-pre5/drivers/net/e100/e100_vendor.h	Thu Nov 28 15:53:13 2002
+++ linux-2.4.21-pre6/drivers/net/e100/e100_vendor.h	Wed Mar 26 16:11:37 2003
@@ -285,6 +285,13 @@
 	{0x8086, 0x2449, 0x1014, PCI_ANY_ID, 0, 0, E100_PROVE_D},	
 	{0x8086, 0x2449, PCI_ANY_ID, PCI_ANY_ID, 0, 0, E100_ALL_BOARDS},
 	
+	{0x8086, 0x1050, PCI_ANY_ID, PCI_ANY_ID, 0, 0, E100_PROVE_NET},
+	{0x8086, 0x1051, PCI_ANY_ID, PCI_ANY_ID, 0, 0, E100_PROVE_NET},
+	{0x8086, 0x1052, PCI_ANY_ID, PCI_ANY_ID, 0, 0, E100_PROVM_NET},
+	{0x8086, 0x1053, PCI_ANY_ID, PCI_ANY_ID, 0, 0, E100_PROVM_NET},
+	{0x8086, 0x1054, PCI_ANY_ID, PCI_ANY_ID, 0, 0, E100_PROVE_NET},
+	{0x8086, 0x1055, PCI_ANY_ID, PCI_ANY_ID, 0, 0, E100_PROVM_NET},
+
 	{0x8086, 0x1059, 0x1179, 0x0005, 0, 0, E100_82551QM},
 	{0x8086, 0x1059, 0x1033, 0x8191, 0, 0, E100_82551QM},
 	{0x8086, 0x1059, PCI_ANY_ID, PCI_ANY_ID, 0, 0, E100_82551QM},
@@ -303,6 +310,7 @@
 	{0x8086, 0x103C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, E100_PROVM_NET},
 	{0x8086, 0x103D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, E100_PROVE_NET},
 	{0x8086, 0x103E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, E100_PROVM_NET},
+	{0x8086, 0x1050, PCI_ANY_ID, PCI_ANY_ID, 0, 0, E100_PROVM_NET},
 	{0x8086, 0x2459, PCI_ANY_ID, PCI_ANY_ID, 0, 0, E100_82562},
 	{0x8086, 0x245D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, E100_82562},
 	{0,} /* This has to be the last entry*/
diff -urN linux-2.4.21-pre5/drivers/net/eepro100.c linux-2.4.21-pre6/drivers/net/eepro100.c
--- linux-2.4.21-pre5/drivers/net/eepro100.c	Wed Mar 26 16:11:28 2003
+++ linux-2.4.21-pre6/drivers/net/eepro100.c	Wed Mar 26 16:11:37 2003
@@ -842,6 +842,7 @@
 	sp->lstats = (struct speedo_stats *)(sp->tx_ring + TX_RING_SIZE);
 	sp->lstats_dma = TX_RING_ELEM_DMA(sp, TX_RING_SIZE);
 	init_timer(&sp->timer); /* used in ioctl() */
+	spin_lock_init(&sp->lock);
 
 	sp->mii_if.full_duplex = option >= 0 && (option & 0x10) ? 1 : 0;
 	if (card_idx >= 0) {
@@ -993,7 +994,6 @@
 	sp->dirty_tx = 0;
 	sp->last_cmd = 0;
 	sp->tx_full = 0;
-	spin_lock_init(&sp->lock);
 	sp->in_interrupt = 0;
 
 	/* .. we can safely take handler calls during init. */
diff -urN linux-2.4.21-pre5/drivers/net/macmace.c linux-2.4.21-pre6/drivers/net/macmace.c
--- linux-2.4.21-pre5/drivers/net/macmace.c	Thu Nov 28 15:53:14 2002
+++ linux-2.4.21-pre6/drivers/net/macmace.c	Wed Mar 26 16:11:37 2003
@@ -332,8 +332,8 @@
 		return -ENOMEM;
 	}
 
-	mp->rx_ring_phys = (unsigned char *) virt_to_bus(mp->rx_ring);
-	mp->tx_ring_phys = (unsigned char *) virt_to_bus(mp->tx_ring);
+	mp->rx_ring_phys = (unsigned char *) virt_to_bus((void *)mp->rx_ring);
+	mp->tx_ring_phys = (unsigned char *) virt_to_bus((void *)mp->tx_ring);
 
 	/* We want the Rx buffer to be uncached and the Tx buffer to be writethrough */
 
diff -urN linux-2.4.21-pre5/drivers/net/pci-skeleton.c linux-2.4.21-pre6/drivers/net/pci-skeleton.c
--- linux-2.4.21-pre5/drivers/net/pci-skeleton.c	Wed Mar 26 16:11:28 2003
+++ linux-2.4.21-pre6/drivers/net/pci-skeleton.c	Wed Mar 26 16:11:37 2003
@@ -1348,6 +1348,16 @@
 	void *ioaddr = tp->mmio_addr;
 	int entry;
 
+	/* If we don't have auto-pad remember not to send random
+	   memory! */
+	   
+	if (skb->len < ETH_ZLEN)
+	{
+		skb = skb_padto(skb, ETH_ZLEN);
+		if(skb == NULL)
+			return 0;
+	}
+	
 	/* Calculate the next Tx descriptor entry. */
 	entry = atomic_read (&tp->cur_tx) % NUM_TX_DESC;
 
@@ -1358,9 +1368,8 @@
 	/* tp->tx_info[entry].mapping = 0; */
 	memcpy (tp->tx_buf[entry], skb->data, skb->len);
 
-	/* Note: the chip doesn't have auto-pad! */
 	NETDRV_W32 (TxStatus0 + (entry * sizeof(u32)),
-		 tp->tx_flag | (skb->len >= ETH_ZLEN ? skb->len : ETH_ZLEN));
+		 tp->tx_flag | skb->len);
 
 	dev->trans_start = jiffies;
 	atomic_inc (&tp->cur_tx);
diff -urN linux-2.4.21-pre5/drivers/net/r8169.c linux-2.4.21-pre6/drivers/net/r8169.c
--- linux-2.4.21-pre5/drivers/net/r8169.c	Wed Mar 26 16:11:28 2003
+++ linux-2.4.21-pre6/drivers/net/r8169.c	Wed Mar 26 16:11:37 2003
@@ -41,6 +41,8 @@
 #include <linux/etherdevice.h>
 #include <linux/delay.h>
 #include <linux/crc32.h>
+#include <linux/init.h>
+#include <asm/io.h>
 
 #define RTL8169_VERSION "1.2"
 #define MODULENAME "r8169"
diff -urN linux-2.4.21-pre5/drivers/net/sun3_82586.c linux-2.4.21-pre6/drivers/net/sun3_82586.c
--- linux-2.4.21-pre5/drivers/net/sun3_82586.c	Wed Mar 26 16:11:28 2003
+++ linux-2.4.21-pre6/drivers/net/sun3_82586.c	Wed Mar 26 16:11:37 2003
@@ -279,7 +279,7 @@
 
 int __init sun3_82586_probe(struct net_device *dev)
 {
-	unsigned long ioaddr, iopte;
+ 	unsigned long ioaddr;
 	static int found = 0;
 	
 	/* check that this machine has an onboard 82586 */
@@ -296,22 +296,8 @@
 	if(found)
 		return -ENODEV;
 	
-	for(ioaddr = 0xfe00000; ioaddr < (0xfe00000 +
-	    SUN3_PMEG_SIZE); ioaddr += SUN3_PTE_SIZE) {
-
-		iopte = sun3_get_pte(ioaddr);
-		if(!(iopte & SUN3_PAGE_TYPE_IO)) /* this an io page? */
-			continue;
-
-		if(((iopte & SUN3_PAGE_PGNUM_MASK) << PAGE_SHIFT) ==
-		   IE_OBIO) {
-			found = 1;
-			break;
-		}
-	}
-	
-	if(!found)
-		return 0;
+	ioaddr = (unsigned long)ioremap(IE_OBIO, PAGE_SIZE);
+	found = 1;
 	
 	SET_MODULE_OWNER(dev);
 
diff -urN linux-2.4.21-pre5/drivers/net/sun3lance.c linux-2.4.21-pre6/drivers/net/sun3lance.c
--- linux-2.4.21-pre5/drivers/net/sun3lance.c	Wed Mar 26 16:11:28 2003
+++ linux-2.4.21-pre6/drivers/net/sun3lance.c	Wed Mar 26 16:11:37 2003
@@ -280,7 +280,7 @@
 
 static int __init lance_probe( struct net_device *dev)
 {	
-	unsigned long ioaddr;
+ 	unsigned long ioaddr;
 	struct lance_private	*lp;
 	int 			i;
 	static int 		did_version;
@@ -288,26 +288,7 @@
 	unsigned short tmp1, tmp2;
 
 #ifdef CONFIG_SUN3
-	unsigned long iopte;
-	int found = 0;
-
-	/* LANCE_OBIO can be found within the IO pmeg with some effort */
-	for(ioaddr = 0xfe00000; ioaddr < (0xfe00000 +
-	    SUN3_PMEG_SIZE); ioaddr += SUN3_PTE_SIZE) {
-
-		iopte = sun3_get_pte(ioaddr);
-		if(!(iopte & SUN3_PAGE_TYPE_IO)) /* this an io page? */
-			continue;
-
-		if(((iopte & SUN3_PAGE_PGNUM_MASK) << PAGE_SHIFT) ==
-		   LANCE_OBIO) {
-			found = 1;
-			break;
-		}
-	}
-	
-	if(!found)
-		return 0;
+ 	ioaddr = (unsigned long)ioremap(LANCE_OBIO, PAGE_SIZE);
 #else
 	ioaddr = SUN3X_LANCE;
 #endif
diff -urN linux-2.4.21-pre5/drivers/net/sunhme.c linux-2.4.21-pre6/drivers/net/sunhme.c
--- linux-2.4.21-pre5/drivers/net/sunhme.c	Wed Mar 26 16:11:28 2003
+++ linux-2.4.21-pre6/drivers/net/sunhme.c	Wed Mar 26 16:11:37 2003
@@ -523,7 +523,7 @@
 	ASD(("happy_meal_tcvr_write: reg=0x%02x value=%04x\n", reg, value));
 
 	/* Welcome to Sun Microsystems, can I take your order please? */
-	if (!hp->happy_flags & HFLAG_FENABLE)
+	if (!(hp->happy_flags & HFLAG_FENABLE))
 		return happy_meal_bb_write(hp, tregs, reg, value);
 
 	/* Would you like fries with that? */
diff -urN linux-2.4.21-pre5/drivers/net/wan/8253x/8253xtty.c linux-2.4.21-pre6/drivers/net/wan/8253x/8253xtty.c
--- linux-2.4.21-pre5/drivers/net/wan/8253x/8253xtty.c	Fri Aug  2 17:39:44 2002
+++ linux-2.4.21-pre6/drivers/net/wan/8253x/8253xtty.c	Wed Mar 26 16:11:37 2003
@@ -2131,7 +2131,7 @@
 	/* Check whether or not the port is open in SYNC mode */
 	if(port->open_type == OPEN_SYNC_NET)
 	{
-		if(port->dev && netif_carrier_ok(port->dev));
+		if(port->dev && netif_carrier_ok(port->dev))
 		{
 			port->tty= NULL;	/* Don't bother with open counting here
 						   but make sure the tty field is NULL*/
diff -urN linux-2.4.21-pre5/drivers/net/wan/comx-hw-mixcom.c linux-2.4.21-pre6/drivers/net/wan/comx-hw-mixcom.c
--- linux-2.4.21-pre5/drivers/net/wan/comx-hw-mixcom.c	Fri Dec 21 09:41:54 2001
+++ linux-2.4.21-pre6/drivers/net/wan/comx-hw-mixcom.c	Wed Mar 26 16:11:37 2003
@@ -102,7 +102,7 @@
 	unsigned char cec;
 	unsigned delay = 0;
 
-	while ((cec = (rd_hscx(dev, HSCX_STAR) & HSCX_CEC) != 0) && 
+	while ((cec = (rd_hscx(dev, HSCX_STAR) & HSCX_CEC)) != 0 && 
 	    (jiffs + HZ > jiffies)) {
 		udelay(1);
 		if (++delay > (100000 / HZ)) break;
diff -urN linux-2.4.21-pre5/drivers/pci/quirks.c linux-2.4.21-pre6/drivers/pci/quirks.c
--- linux-2.4.21-pre5/drivers/pci/quirks.c	Wed Mar 26 16:11:29 2003
+++ linux-2.4.21-pre6/drivers/pci/quirks.c	Wed Mar 26 16:11:37 2003
@@ -698,6 +698,7 @@
 	 * instead of 0x01.
 	 */
 	{ PCI_FIXUP_HEADER,	PCI_VENDOR_ID_INTEL,	PCI_DEVICE_ID_INTEL_82380FB,	quirk_transparent_bridge },
+	{ PCI_FIXUP_HEADER,	PCI_VENDOR_ID_TOSHIBA,	0x605,				quirk_transparent_bridge },
 
 	{ PCI_FIXUP_FINAL,	PCI_VENDOR_ID_CYRIX,	PCI_DEVICE_ID_CYRIX_PCI_MASTER, quirk_mediagx_master },
 
diff -urN linux-2.4.21-pre5/drivers/scsi/3w-xxxx.c linux-2.4.21-pre6/drivers/scsi/3w-xxxx.c
--- linux-2.4.21-pre5/drivers/scsi/3w-xxxx.c	Thu Nov 28 15:53:14 2002
+++ linux-2.4.21-pre6/drivers/scsi/3w-xxxx.c	Wed Mar 26 16:11:37 2003
@@ -6,7 +6,7 @@
    		     Arnaldo Carvalho de Melo <acme@conectiva.com.br>
                      Brad Strand <linux@3ware.com>
 
-   Copyright (C) 1999-2002 3ware Inc.
+   Copyright (C) 1999-2003 3ware Inc.
 
    Kernel compatablity By: 	Andre Hedrick <andre@suse.com>
    Non-Copyright (C) 2000	Andre Hedrick <andre@suse.com>
@@ -160,6 +160,11 @@
                  Add support for mode sense opcode.
                  Add support for cache mode page.
                  Add support for synchronize cache opcode.
+   1.02.00.032 - Fix small multicard rollcall bug.
+                 Make driver stay loaded with no units for hot add/swap.
+                 Add support for "twe" character device for ioctls.
+                 Clean up request_id queueing code.
+                 Fix tw_scsi_queue() spinlocks.
 */
 
 #include <linux/module.h>
@@ -202,6 +207,9 @@
 MODULE_LICENSE("GPL");
 #endif
 
+static int tw_chrdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg);
+static int tw_chrdev_open(struct inode *inode, struct file *file);
+static int tw_chrdev_release(struct inode *inode, struct file *file);
 static int tw_copy_info(TW_Info *info, char *fmt, ...);
 static void tw_copy_mem_info(TW_Info *info, char *data, int len);
 static void tw_interrupt(int irq, void *dev_instance, struct pt_regs *regs);
@@ -215,10 +223,19 @@
 	tw_halt, NULL, 0
 };
 
+/* File operations struct for character device */
+static struct file_operations tw_fops = {
+	owner: THIS_MODULE,
+	ioctl: tw_chrdev_ioctl,
+	open: tw_chrdev_open,
+	release: tw_chrdev_release
+};
+
 /* Globals */
-char *tw_driver_version="1.02.00.031";
+char *tw_driver_version="1.02.00.032";
 TW_Device_Extension *tw_device_extension_list[TW_MAX_SLOT];
 int tw_device_extension_count = 0;
+static int twe_major = -1;
 
 /* Functions */
 
@@ -608,6 +625,209 @@
 	return 0;
 } /* End tw_check_errors() */
 
+/* This function handles ioctl for the character device */
+static int tw_chrdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+{
+	int error, request_id;
+	dma_addr_t dma_handle;
+	unsigned short tw_aen_code;
+	struct timeval before, timeout;
+	unsigned long flags = 0x0;
+	unsigned int data_buffer_length = 0;
+	unsigned long data_buffer_length_adjusted = 0;
+	unsigned long *cpu_addr;
+	TW_New_Ioctl *tw_ioctl;
+	TW_Passthru *passthru;
+	TW_Device_Extension *tw_dev = tw_device_extension_list[MINOR(inode->i_rdev)];
+
+	dprintk(KERN_WARNING "3w-xxxx: tw_chrdev_ioctl()\n");
+
+	/* Only let one of these through at a time */
+	if (test_and_set_bit(TW_IN_CHRDEV_IOCTL, &tw_dev->flags)) {
+		return -EBUSY;
+	}
+
+	/* First copy down the buffer length */
+	error = copy_from_user(&data_buffer_length, (void *)arg, sizeof(unsigned int));
+	if (error) {
+		printk(KERN_WARNING "3w-xxxx: tw_chrdev_ioctl(): Error copying buffer length from userspace.\n");
+		clear_bit(TW_IN_CHRDEV_IOCTL, &tw_dev->flags);
+		return -EFAULT;
+	}
+
+	/* Check size */
+	if (data_buffer_length > TW_MAX_SECTORS * 512) {
+		printk(KERN_WARNING "3w-xxxx: tw_chrdev_ioctl(): Invalid buffer size (%d).\n", data_buffer_length);
+		clear_bit(TW_IN_CHRDEV_IOCTL, &tw_dev->flags);
+		return -EFAULT;
+	}
+
+	/* Hardware can only do multiple of 512 byte transfers */
+	if (data_buffer_length % 512)
+		data_buffer_length_adjusted = data_buffer_length + 512 - (data_buffer_length % 512);
+	else
+		data_buffer_length_adjusted = data_buffer_length;
+
+	/* Now allocate ioctl buf memory */
+	cpu_addr = pci_alloc_consistent(tw_dev->tw_pci_dev, data_buffer_length_adjusted+sizeof(TW_New_Ioctl) - 1, &dma_handle);
+	if (cpu_addr == NULL) {
+		printk(KERN_WARNING "3w-xxxx: tw_chrdev_ioctl(): Error allocating memory.\n");
+		clear_bit(TW_IN_CHRDEV_IOCTL, &tw_dev->flags);
+		return -ENOMEM;
+	}
+
+	tw_ioctl = (TW_New_Ioctl *)cpu_addr;
+
+	/* Now copy down the entire ioctl */
+	error = copy_from_user(tw_ioctl, (void *)arg, data_buffer_length + sizeof(TW_New_Ioctl) - 1);
+	if (error) {
+		printk(KERN_WARNING "3w-xxxx: tw_chrdev_ioctl(): Error copying data from userspace.\n");
+		pci_free_consistent(tw_dev->tw_pci_dev, data_buffer_length_adjusted+sizeof(TW_New_Ioctl) - 1, cpu_addr, dma_handle);
+		clear_bit(TW_IN_CHRDEV_IOCTL, &tw_dev->flags);
+		return -EFAULT;
+	}
+
+	passthru = (TW_Passthru *)&tw_ioctl->firmware_command;
+
+	/* See which ioctl we are doing */
+	switch (cmd) {
+		case TW_OP_NOP:
+			dprintk(KERN_WARNING "3w-xxxx: tw_chrdev_ioctl(): caught TW_OP_NOP.\n");
+			break;
+		case TW_OP_AEN_LISTEN:
+			dprintk(KERN_WARNING "3w-xxxx: tw_chrdev_ioctl(): caught TW_AEN_LISTEN.\n");
+			memset(tw_ioctl->data_buffer, 0, tw_ioctl->data_buffer_length);
+			if (tw_dev->aen_head == tw_dev->aen_tail) {
+				tw_aen_code = TW_AEN_QUEUE_EMPTY;
+			} else {
+				tw_aen_code = tw_dev->aen_queue[tw_dev->aen_head];
+				if (tw_dev->aen_head == TW_Q_LENGTH - 1) {
+					tw_dev->aen_head = TW_Q_START;
+				} else {
+					tw_dev->aen_head = tw_dev->aen_head + 1;
+				}
+			}
+			memcpy(tw_ioctl->data_buffer, &tw_aen_code, sizeof(tw_aen_code));
+			break;
+		case TW_CMD_PACKET_WITH_DATA:
+			dprintk(KERN_WARNING "3w-xxxx: tw_chrdev_ioctl(): caught TW_CMD_PACKET_WITH_DATA.\n");
+			spin_lock_irqsave(&tw_dev->tw_lock, flags);
+
+			tw_state_request_start(tw_dev, &request_id);
+
+			/* Flag internal command */
+			tw_dev->srb[request_id] = 0;
+
+			/* Flag chrdev ioctl */
+			tw_dev->chrdev_request_id = request_id;
+
+			tw_ioctl->firmware_command.request_id = request_id;
+
+			/* Load the sg list */
+			switch (tw_ioctl->firmware_command.byte0.sgl_offset) {
+			case 2:
+				tw_ioctl->firmware_command.byte8.param.sgl[0].address = dma_handle + sizeof(TW_New_Ioctl) - 1;
+				tw_ioctl->firmware_command.byte8.param.sgl[0].length = data_buffer_length_adjusted;
+				break;
+			case 3:
+				tw_ioctl->firmware_command.byte8.io.sgl[0].address = dma_handle + sizeof(TW_New_Ioctl) - 1;
+				tw_ioctl->firmware_command.byte8.io.sgl[0].length = data_buffer_length_adjusted;
+				break;
+			case 5:
+				passthru->sg_list[0].address = dma_handle + sizeof(TW_New_Ioctl) - 1;
+				passthru->sg_list[0].length = data_buffer_length_adjusted;
+				break;
+			}
+
+			memcpy(tw_dev->command_packet_virtual_address[request_id], &(tw_ioctl->firmware_command), sizeof(TW_Command));
+
+			/* Now post the command packet to the controller */
+			tw_post_command_packet(tw_dev, request_id);
+			spin_unlock_irqrestore(&tw_dev->tw_lock, flags);
+
+			/* Now wait for the command to complete */
+			do_gettimeofday(&before);
+
+	tw_ioctl_chrdev_retry:
+
+			if (tw_dev->chrdev_request_id != TW_IOCTL_CHRDEV_FREE) {
+				interruptible_sleep_on_timeout(&tw_dev->ioctl_wqueue, 1);
+				do_gettimeofday(&timeout);
+				if (before.tv_sec + TW_IOCTL_CHRDEV_TIMEOUT < timeout.tv_sec) {
+					/* Now we need to reset the board */
+					printk(KERN_WARNING "3w-xxxx: scsi%d: Character ioctl (0x%x) timed out, resetting card.\n", tw_dev->host->host_no, cmd);
+					spin_lock_irqsave(&tw_dev->tw_lock, flags);
+					tw_dev->state[request_id] = TW_S_COMPLETED;
+					tw_state_request_finish(tw_dev, request_id);
+					pci_free_consistent(tw_dev->tw_pci_dev, data_buffer_length_adjusted+sizeof(TW_New_Ioctl) - 1, cpu_addr, dma_handle);
+					tw_dev->posted_request_count--;
+					if (tw_reset_device_extension(tw_dev)) {
+						printk(KERN_WARNING "3w-xxxx: tw_chrdev_ioctl(): Reset failed for card %d.\n", tw_dev->host->host_no);
+					}
+					spin_unlock_irqrestore(&tw_dev->tw_lock, flags);
+					clear_bit(TW_IN_CHRDEV_IOCTL, &tw_dev->flags);
+					if (signal_pending(current))
+						return -EINTR;
+					else
+						return -EIO;
+				} else {
+					goto tw_ioctl_chrdev_retry;
+				}
+			}
+
+			/* Now copy in the command packet response */
+			memcpy(&(tw_ioctl->firmware_command), tw_dev->command_packet_virtual_address[request_id], sizeof(TW_Command));
+
+			/* Now complete the io */
+			spin_lock_irqsave(&tw_dev->tw_lock, flags);
+			tw_dev->posted_request_count--;
+			tw_dev->state[request_id] = TW_S_COMPLETED;
+			tw_state_request_finish(tw_dev, request_id);
+			spin_unlock_irqrestore(&tw_dev->tw_lock, flags);
+			break;
+		default:
+			printk(KERN_WARNING "3w-xxxx: Unknown chrdev ioctl 0x%x.\n", cmd);
+	}
+
+	/* Now copy the entire response to userspace */
+	error = copy_to_user((void *)arg, tw_ioctl, sizeof(TW_New_Ioctl) + tw_ioctl->data_buffer_length - 1);
+	if (error) {
+		printk(KERN_WARNING "3w-xxxx: tw_chrdev_ioctl(): Error copying data to userspace.\n");
+		pci_free_consistent(tw_dev->tw_pci_dev, data_buffer_length_adjusted+sizeof(TW_New_Ioctl) - 1, cpu_addr, dma_handle);
+		clear_bit(TW_IN_CHRDEV_IOCTL, &tw_dev->flags);
+		return -EFAULT;
+	}
+
+	/* Now free ioctl buf memory */
+	pci_free_consistent(tw_dev->tw_pci_dev, data_buffer_length_adjusted+sizeof(TW_New_Ioctl) - 1, cpu_addr, dma_handle);
+
+	clear_bit(TW_IN_CHRDEV_IOCTL, &tw_dev->flags);
+
+	return 0;
+} /* End tw_chrdev_ioctl() */
+
+/* This function handles open for the character device */
+static int tw_chrdev_open(struct inode *inode, struct file *file)
+{
+	unsigned int minor_number;
+
+	dprintk(KERN_WARNING "3w-xxxx: tw_ioctl_open()\n");
+
+	minor_number = MINOR(inode->i_rdev);
+	if (minor_number >= tw_device_extension_count)
+		return -ENODEV;
+
+	return 0;
+} /* End tw_chrdev_open() */
+
+/* This function handles close for the character device */
+static int tw_chrdev_release(struct inode *inode, struct file *file)
+{
+	dprintk(KERN_WARNING "3w-xxxx: tw_ioctl_release()\n");
+
+	return 0;
+} /* End tw_chrdev_release() */
+
 /* This function will clear all interrupts on the controller */
 void tw_clear_all_interrupts(TW_Device_Extension *tw_dev)
 {
@@ -878,7 +1098,9 @@
 
 			/* Disable interrupts on the card */
 			tw_disable_interrupts(tw_dev);
-			
+
+			tries = 0;
+
 			while (tries < TW_MAX_RESET_TRIES) {
 				/* Do soft reset */
 				tw_soft_reset(tw_dev);
@@ -924,10 +1146,6 @@
 			error = tw_initialize_units(tw_dev);
 			if (error) {
 				printk(KERN_WARNING "3w-xxxx: No valid units for card %d.\n", j);
-				release_region((tw_dev->tw_pci_dev->resource[0].start), TW_IO_ADDRESS_RANGE);
-				tw_free_device_extension(tw_dev);
-				kfree(tw_dev);
-				continue;
 			}
 
 			error = tw_initconnection(tw_dev, TW_INIT_MESSAGE_CREDITS);
@@ -945,14 +1163,15 @@
 			/* Calculate max cmds per lun, and setup queues */
 			if (tw_dev->num_units > 0) {
 				if ((tw_dev->num_raid_five > 0) && (tw_dev->tw_pci_dev->device == TW_DEVICE_ID)) {
-					tw_host->cmd_per_lun = (TW_MAX_BOUNCEBUF-1)/tw_dev->num_units;
+					tw_host->cmd_per_lun = (TW_MAX_BOUNCEBUF-2)/tw_dev->num_units;
 					tw_dev->free_head = TW_Q_START;
-					tw_dev->free_tail = TW_MAX_BOUNCEBUF - 1;
+					tw_dev->free_tail = TW_Q_START;
 					tw_dev->free_wrap = TW_MAX_BOUNCEBUF - 1;
 				} else {
 					/* Use SHT cmd_per_lun here */
+					tw_host->cmd_per_lun = TW_MAX_CMDS_PER_LUN;
 					tw_dev->free_head = TW_Q_START;
-					tw_dev->free_tail = TW_Q_LENGTH - 1;
+					tw_dev->free_tail = TW_Q_START;
 					tw_dev->free_wrap = TW_Q_LENGTH - 1;
 				}
 			}
@@ -1013,13 +1232,7 @@
 			/* Tell the firmware we support shutdown notification*/
 			error = tw_setfeature(tw_dev2, 2, 1, &c);
 			if (error) {
-				printk(KERN_WARNING "3w-xxxx: tw_setfeature(): Error setting features for card %d.\n", j);
-				scsi_unregister(host);
-				release_region((tw_dev->tw_pci_dev->resource[0].start), TW_IO_ADDRESS_RANGE);
-				tw_free_device_extension(tw_dev);
-				kfree(tw_dev);
-				numcards--;
-				continue;
+				printk(KERN_WARNING "3w-xxxx: Unable to set features for card %d, old firmware or card.\n", j);
 			}
 
 			/* Now setup the interrupt handler */
@@ -1044,10 +1257,14 @@
 		}
 	}
 
-	if (numcards == 0) 
-		printk(KERN_WARNING "3w-xxxx: No cards with valid units found.\n");
-	else
-	  register_reboot_notifier(&tw_notifier);
+	if (numcards == 0) {
+		printk(KERN_WARNING "3w-xxxx: No cards found.\n");
+	} else {
+		register_reboot_notifier(&tw_notifier);
+		if ((twe_major = register_chrdev (0, "twe", &tw_fops)) < 0) {
+			printk(KERN_WARNING "3w-xxxx: Unable to register \"twe\" character device, error = %d.\n", twe_major);
+		}
+	}
 
 	return numcards;
 } /* End tw_findcards() */
@@ -1179,6 +1396,8 @@
 	tw_dev->pending_head = TW_Q_START;
 	tw_dev->pending_tail = TW_Q_START;
 	spin_lock_init(&tw_dev->tw_lock);
+	tw_dev->chrdev_request_id = TW_IOCTL_CHRDEV_FREE;
+	init_waitqueue_head(&tw_dev->ioctl_wqueue);
 
 	return 0;
 } /* End tw_initialize_device_extension() */
@@ -1510,9 +1729,14 @@
 				/* Check for internal command completion */
 				if (tw_dev->srb[request_id] == 0) {
 					dprintk(KERN_WARNING "3w-xxxx: tw_interrupt(): Found internally posted command.\n");
-					retval = tw_aen_complete(tw_dev, request_id);
-					if (retval) {
-						printk(KERN_WARNING "3w-xxxx: scsi%d: Error completing aen.\n", tw_dev->host->host_no);
+					/* Check for chrdev ioctl completion */
+					if (request_id != tw_dev->chrdev_request_id) {
+						retval = tw_aen_complete(tw_dev, request_id);
+						if (retval) {
+							printk(KERN_WARNING "3w-xxxx: scsi%d: Error completing aen.\n", tw_dev->host->host_no);
+						}
+					} else {
+						tw_dev->chrdev_request_id = TW_IOCTL_CHRDEV_FREE;
 					}
 				} else {
 				switch (tw_dev->srb[request_id]->cmnd[0]) {
@@ -1524,6 +1748,10 @@
 					case WRITE_6:
 						dprintk(KERN_NOTICE "3w-xxxx: tw_interrupt(): caught WRITE_10/WRITE_6\n");
 						break;
+					case TEST_UNIT_READY:
+						dprintk(KERN_NOTICE "3w-xxxx: tw_interrupt(): caught TEST_UNIT_READY\n");
+						error = tw_scsiop_test_unit_ready_complete(tw_dev, request_id);
+						break;
 					case INQUIRY:
 						dprintk(KERN_NOTICE "3w-xxxx: tw_interrupt(): caught INQUIRY\n");
 						error = tw_scsiop_inquiry_complete(tw_dev, request_id);
@@ -2187,12 +2415,13 @@
 		tw_dev->state[i] = TW_S_INITIAL;
 	}
 	tw_dev->free_head = TW_Q_START;
-	tw_dev->free_tail = TW_Q_LENGTH - 1;
+	tw_dev->free_tail = TW_Q_START;
 	tw_dev->posted_request_count = 0;
 	tw_dev->pending_request_count = 0;
 	tw_dev->pending_head = TW_Q_START;
 	tw_dev->pending_tail = TW_Q_START;
 	tw_dev->reset_print = 0;
+	tw_dev->chrdev_request_id = TW_IOCTL_CHRDEV_FREE;
 
 	return 0;
 } /* End tw_reset_device_extension() */
@@ -2475,7 +2704,6 @@
 	unsigned char *command = SCpnt->cmnd;
 	int request_id = 0;
 	int error = 0;
-	unsigned long flags = 0;
 	TW_Device_Extension *tw_dev = (TW_Device_Extension *)SCpnt->host->hostdata;
 
 	if (tw_dev == NULL) {
@@ -2485,14 +2713,14 @@
 		return 0;
 	}
 
-	spin_lock_irqsave(&tw_dev->tw_lock, flags);
+	spin_lock(&tw_dev->tw_lock);
 	dprintk(KERN_NOTICE "3w-xxxx: tw_scsi_queue()\n");
 
 	/* Skip scsi command if it isn't for us */
-	if ((tw_dev->is_unit_present[SCpnt->target] == FALSE) || (SCpnt->lun != 0)) {
+	if ((SCpnt->channel != 0) || (SCpnt->lun != 0)) {
 		SCpnt->result = (DID_BAD_TARGET << 16);
 		done(SCpnt);
-		spin_unlock_irqrestore(&tw_dev->tw_lock, flags);
+		spin_unlock(&tw_dev->tw_lock);
 		return 0;
 	}
 	
@@ -2505,6 +2733,9 @@
 	/* Save the scsi command for use by the ISR */
 	tw_dev->srb[request_id] = SCpnt;
 
+	/* Initialize phase to zero */
+	SCpnt->SCp.phase = 0;
+
 	switch (*command) {
 		case READ_10:
 		case READ_6:
@@ -2554,7 +2785,7 @@
 		SCpnt->result = (DID_ERROR << 16);
 		done(SCpnt);
 	}
-	spin_unlock_irqrestore(&tw_dev->tw_lock, flags);
+	spin_unlock(&tw_dev->tw_lock);
 
 	return 0;
 } /* End tw_scsi_queue() */
@@ -2578,6 +2809,12 @@
 	/* Free up the IRQ */
 	free_irq(tw_dev->tw_pci_dev->irq, tw_dev);
 
+	/* Unregister character device */
+	if (twe_major >= 0) {
+		unregister_chrdev(twe_major, "twe");
+		twe_major = -1;
+	}
+
 	/* Free up device extension resources */
 	tw_free_device_extension(tw_dev);
 
@@ -2651,7 +2888,6 @@
 {
 	unsigned char *is_unit_present;
 	unsigned char *request_buffer;
-	int i;
 	TW_Param *param;
 
 	dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_inquiry_complete()\n");
@@ -2663,12 +2899,12 @@
 	}
 	request_buffer = tw_dev->srb[request_id]->request_buffer;
 	memset(request_buffer, 0, tw_dev->srb[request_id]->request_bufflen);
-	request_buffer[0] = TYPE_DISK;									 /* Peripheral device type */
-	request_buffer[1] = 0;													 /* Device type modifier */
-	request_buffer[2] = 0;													 /* No ansi/iso compliance */
-	request_buffer[4] = 31;													/* Additional length */
+	request_buffer[0] = TYPE_DISK; /* Peripheral device type */
+	request_buffer[1] = 0;	       /* Device type modifier */
+	request_buffer[2] = 0;	       /* No ansi/iso compliance */
+	request_buffer[4] = 31;	       /* Additional length */
 	memcpy(&request_buffer[8], "3ware   ", 8);	 /* Vendor ID */
-	memcpy(&request_buffer[16], "3w-xxxx         ", 16); /* Product ID */
+	sprintf(&request_buffer[16], "Logical Disk %-2d ", tw_dev->srb[request_id]->target);
 	memcpy(&request_buffer[32], tw_driver_version, 3);
 
 	param = (TW_Param *)tw_dev->alignment_virtual_address[request_id];
@@ -2678,15 +2914,12 @@
 	}
 	is_unit_present = &(param->data[0]);
 
-	for (i=0 ; i<TW_MAX_UNITS; i++) {
-		if (is_unit_present[i] == 0) {
-			tw_dev->is_unit_present[i] = FALSE;
-		} else {
-		  if (is_unit_present[i] & TW_UNIT_ONLINE) {
-			tw_dev->is_unit_present[i] = TRUE;
-			dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_inquiry_complete: Unit %d found.\n", i);
-		  }
-		}
+	if (is_unit_present[tw_dev->srb[request_id]->target] & TW_UNIT_ONLINE) {
+		tw_dev->is_unit_present[tw_dev->srb[request_id]->target] = TRUE;
+	} else {
+		tw_dev->is_unit_present[tw_dev->srb[request_id]->target] = FALSE;
+		tw_dev->srb[request_id]->result = (DID_BAD_TARGET << 16);
+		return TW_ISR_DONT_RESULT;
 	}
 
 	return 0;
@@ -2993,7 +3226,11 @@
                 /* Do this if there are no sg list entries for raid 5 */
                 if (tw_dev->srb[request_id]->use_sg == 0) {
 			dprintk(KERN_WARNING "doing raid 5 write use_sg = 0, bounce_buffer[%d] = 0x%p\n", request_id, tw_dev->bounce_buffer[request_id]);
-			memcpy(tw_dev->bounce_buffer[request_id], tw_dev->srb[request_id]->request_buffer, tw_dev->srb[request_id]->request_bufflen);
+			buffaddr = tw_map_scsi_single_data(tw_dev->tw_pci_dev, tw_dev->srb[request_id]);
+			if (buffaddr == 0)
+				return 1;
+
+			memcpy(tw_dev->bounce_buffer[request_id], bus_to_virt(buffaddr), tw_dev->srb[request_id]->request_bufflen);
 			command_packet->byte8.io.sgl[0].address = tw_dev->bounce_buffer_phys[request_id];
 			command_packet->byte8.io.sgl[0].length = tw_dev->srb[request_id]->request_bufflen;
 			command_packet->size+=2;
@@ -3002,9 +3239,13 @@
                 /* Do this if we have multiple sg list entries for raid 5 */
                 if (tw_dev->srb[request_id]->use_sg > 0) {
                         dprintk(KERN_WARNING "doing raid 5 write use_sg = %d, sglist[0].length = %d\n", tw_dev->srb[request_id]->use_sg, sglist[0].length);
-                        for (i=0;i<tw_dev->srb[request_id]->use_sg; i++) {
-                                memcpy((char *)(tw_dev->bounce_buffer[request_id])+count, sglist[i].address, sglist[i].length);
-				count+=sglist[i].length;
+			use_sg = tw_map_scsi_sg_data(tw_dev->tw_pci_dev, tw_dev->srb[request_id]);
+			if (use_sg == 0)
+				return 1;
+
+			for (i=0;i<use_sg; i++) {
+				memcpy((char *)tw_dev->bounce_buffer[request_id]+count, bus_to_virt(sg_dma_address(&sglist[i])), sg_dma_len(&sglist[i]));
+				count+=sg_dma_len(&sglist[i]);
                         }
                         command_packet->byte8.io.sgl[0].address = tw_dev->bounce_buffer_phys[request_id];
                         command_packet->byte8.io.sgl[0].length = count;
@@ -3088,17 +3329,89 @@
 /* This function will handle test unit ready scsi command */
 int tw_scsiop_test_unit_ready(TW_Device_Extension *tw_dev, int request_id)
 {
+
+	TW_Param *param;
+	TW_Command *command_packet;
+	unsigned long command_que_value;
+	u32 command_que_addr;
+	unsigned long param_value;
+
 	dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_test_unit_ready()\n");
 
-	/* Tell the scsi layer were done */
-	tw_dev->state[request_id] = TW_S_COMPLETED;
-	tw_state_request_finish(tw_dev, request_id);
-	tw_dev->srb[request_id]->result = (DID_OK << 16);
-	tw_dev->srb[request_id]->scsi_done(tw_dev->srb[request_id]);
+	/* Initialize command packet */
+	command_que_addr = tw_dev->registers.command_que_addr;
+	command_packet = (TW_Command *)tw_dev->command_packet_virtual_address[request_id];
+	if (command_packet == NULL) {
+		printk(KERN_WARNING "3w-xxxx: tw_scsiop_test_unit_ready(): Bad command packet virtual address.\n");
+		return 1;
+	}
+	memset(command_packet, 0, sizeof(TW_Sector));
+	command_packet->byte0.opcode = TW_OP_GET_PARAM;
+	command_packet->byte0.sgl_offset = 2;
+	command_packet->size = 4;
+	command_packet->request_id = request_id;
+	command_packet->byte3.unit = 0;
+	command_packet->byte3.host_id = 0;
+	command_packet->status = 0;
+	command_packet->flags = 0;
+	command_packet->byte6.parameter_count = 1;
+
+	/* Now setup the param */
+	if (tw_dev->alignment_virtual_address[request_id] == NULL) {
+		printk(KERN_WARNING "3w-xxxx: tw_scsiop_test_unit_ready(): Bad alignment virtual address.\n");
+		return 1;
+	}
+	param = (TW_Param *)tw_dev->alignment_virtual_address[request_id];
+	memset(param, 0, sizeof(TW_Sector));
+	param->table_id = 3;	 /* unit summary table */
+	param->parameter_id = 3; /* unitsstatus parameter */
+	param->parameter_size_bytes = TW_MAX_UNITS;
+	param_value = tw_dev->alignment_physical_address[request_id];
+	if (param_value == 0) {
+		printk(KERN_WARNING "3w-xxxx: tw_scsiop_test_unit_ready(): Bad alignment physical address.\n");
+		return 1;
+	}
+
+	command_packet->byte8.param.sgl[0].address = param_value;
+	command_packet->byte8.param.sgl[0].length = sizeof(TW_Sector);
+	command_que_value = tw_dev->command_packet_physical_address[request_id];
+	if (command_que_value == 0) {
+		printk(KERN_WARNING "3w-xxxx: tw_scsiop_test_unit_ready(): Bad command packet physical address.\n");
+		return 1;
+	}
+
+	/* Now try to post the command packet */
+	tw_post_command_packet(tw_dev, request_id);
 
 	return 0;
 } /* End tw_scsiop_test_unit_ready() */
 
+/* This function is called by the isr to complete a testunitready command */
+int tw_scsiop_test_unit_ready_complete(TW_Device_Extension *tw_dev, int request_id)
+{
+	unsigned char *is_unit_present;
+	TW_Param *param;
+
+	dprintk(KERN_WARNING "3w-xxxx: tw_scsiop_test_unit_ready_complete()\n");
+
+	param = (TW_Param *)tw_dev->alignment_virtual_address[request_id];
+	if (param == NULL) {
+		printk(KERN_WARNING "3w-xxxx: tw_scsiop_test_unit_ready_complete(): Bad alignment virtual address.\n");
+		return 1;
+	}
+	is_unit_present = &(param->data[0]);
+
+	if (is_unit_present[tw_dev->srb[request_id]->target] & TW_UNIT_ONLINE) {
+		tw_dev->is_unit_present[tw_dev->srb[request_id]->target] = TRUE;
+	} else {
+		tw_dev->is_unit_present[tw_dev->srb[request_id]->target] = FALSE;
+		tw_dev->srb[request_id]->result = (DID_BAD_TARGET << 16);
+		return TW_ISR_DONT_RESULT;
+	}
+
+	return 0;
+} /* End tw_scsiop_test_unit_ready_complete() */
+
 /* Set a value in the features table */
 int tw_setfeature(TW_Device_Extension *tw_dev, int parm, int param_size,
                   unsigned char *val)
@@ -3233,17 +3546,13 @@
 {
 	dprintk(KERN_NOTICE "3w-xxxx: tw_state_request_finish()\n");
   
-	do {    
-		if (tw_dev->free_tail == tw_dev->free_wrap) {
-			tw_dev->free_tail = TW_Q_START;
-		} else {
-			tw_dev->free_tail = tw_dev->free_tail + 1;
-		}
-	} while ((tw_dev->state[tw_dev->free_queue[tw_dev->free_tail]] != TW_S_COMPLETED));
-
 	tw_dev->free_queue[tw_dev->free_tail] = request_id;
-
 	tw_dev->state[request_id] = TW_S_FINISHED;
+	if (tw_dev->free_tail == tw_dev->free_wrap)
+		tw_dev->free_tail = TW_Q_START;
+	else
+		tw_dev->free_tail++;
+
 	dprintk(KERN_NOTICE "3w-xxxx: tw_state_request_finish(): Freeing request_id %d\n", request_id);
 
 	return 0;
@@ -3257,20 +3566,16 @@
 	dprintk(KERN_NOTICE "3w-xxxx: tw_state_request_start()\n");
 	
 	/* Obtain next free request_id */
-	do {
-		if (tw_dev->free_head == tw_dev->free_wrap) {
-			tw_dev->free_head = TW_Q_START;
-		} else {
-			tw_dev->free_head = tw_dev->free_head + 1;
-		}
-	} while (tw_dev->state[tw_dev->free_queue[tw_dev->free_head]] & TW_START_MASK);
-
 	id = tw_dev->free_queue[tw_dev->free_head];
+	if (tw_dev->free_head == tw_dev->free_wrap)
+		tw_dev->free_head = TW_Q_START;
+	else
+		tw_dev->free_head++;
 
-	dprintk(KERN_NOTICE "3w-xxxx: tw_state_request_start(): id = %d.\n", id);
 	*request_id = id;
 	tw_dev->state[id] = TW_S_STARTED;
 
+	dprintk(KERN_NOTICE "3w-xxxx: tw_state_request_start(): id = %d.\n", id);
 	return 0;
 } /* End tw_state_request_start() */
 
diff -urN linux-2.4.21-pre5/drivers/scsi/3w-xxxx.h linux-2.4.21-pre6/drivers/scsi/3w-xxxx.h
--- linux-2.4.21-pre5/drivers/scsi/3w-xxxx.h	Thu Nov 28 15:53:14 2002
+++ linux-2.4.21-pre6/drivers/scsi/3w-xxxx.h	Wed Mar 26 16:11:37 2003
@@ -6,7 +6,7 @@
    		     Arnaldo Carvalho de Melo <acme@conectiva.com.br>
                      Brad Strand <linux@3ware.com>
 
-   Copyright (C) 1999-2002 3ware Inc.
+   Copyright (C) 1999-2003 3ware Inc.
 
    Kernel compatablity By:	Andre Hedrick <andre@suse.com>
    Non-Copyright (C) 2000	Andre Hedrick <andre@suse.com>
@@ -114,11 +114,11 @@
   {0x84, 0x0b, 0x47, 0x00}, // Data CRC error               SCSI parity error
   {0xd0, 0x0b, 0x00, 0x00}, // Device busy                  Aborted command
   {0xd1, 0x0b, 0x00, 0x00}, // Device busy                  Aborted command
+  {0x37, 0x02, 0x04, 0x00}, // Unit offline                 Not ready
 
   /* Codes for older firmware */
                             // 3ware Error                  SCSI Error
   {0x09, 0x0b, 0x00, 0x00}, // Unrecovered disk error       Aborted command
-  {0x37, 0x0b, 0x04, 0x00}, // Unit offline                 Logical unit not ready
   {0x51, 0x0b, 0x00, 0x00}  // Unspecified                  Aborted command
 };
 
@@ -221,12 +221,15 @@
 #define TW_MAX_PCI_BUSES		      255
 #define TW_MAX_RESET_TRIES		      3
 #define TW_UNIT_INFORMATION_TABLE_BASE	      0x300
-#define TW_MAX_CMDS_PER_LUN		      255
+#define TW_MAX_CMDS_PER_LUN		      254 /* 254 for io, 1 for 
+                                                     chrdev ioctl, one for
+                                                     internal aen post */
 #define TW_BLOCK_SIZE			      0x200 /* 512-byte blocks */
 #define TW_IOCTL                              0x80
 #define TW_UNIT_ONLINE                        1
 #define TW_IN_INTR                            1
 #define TW_IN_IOCTL                           2
+#define TW_IN_CHRDEV_IOCTL                    3
 #define TW_MAX_SECTORS                        256
 #define TW_MAX_BOUNCE_SECTORS                 128
 #define TW_AEN_WAIT_TIME                      1000
@@ -235,6 +238,8 @@
 #define TW_ISR_DONT_COMPLETE                  2
 #define TW_ISR_DONT_RESULT                    3
 #define TW_IOCTL_TIMEOUT                      25 /* 25 seconds */
+#define TW_IOCTL_CHRDEV_TIMEOUT               25 /* 25 seconds */
+#define TW_IOCTL_CHRDEV_FREE                  -1
 
 /* Macros */
 #define TW_STATUS_ERRORS(x) \
@@ -250,6 +255,8 @@
 #define dprintk(msg...) do { } while(0)
 #endif
 
+#pragma pack(1)
+
 /* Scatter Gather List Entry */
 typedef struct TAG_TW_SG_Entry {
 	u32 address;
@@ -299,6 +306,8 @@
 	} byte8;
 } TW_Command;
 
+#pragma pack()
+
 typedef struct TAG_TW_Ioctl {
 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,15)
 	int buffer;
@@ -311,6 +320,16 @@
 	unsigned char data[1];
 } TW_Ioctl;
 
+#pragma pack(1)
+
+/* Structure for new chardev ioctls */
+typedef struct TAG_TW_New_Ioctl {
+	unsigned int data_buffer_length;
+	unsigned char padding [508];
+	TW_Command firmware_command;
+	char data_buffer[1];
+} TW_New_Ioctl;
+
 /* GetParam descriptor */
 typedef struct {
 	unsigned short	table_id;
@@ -425,8 +444,12 @@
 	unsigned long		*ioctl_data[TW_Q_LENGTH];
 	int			reset_print;
 	char                    online;
+	volatile int		chrdev_request_id;
+	wait_queue_head_t       ioctl_wqueue;
 } TW_Device_Extension;
 
+#pragma pack()
+
 /* Function prototypes */
 int tw_aen_complete(TW_Device_Extension *tw_dev, int request_id);
 int tw_aen_drain_queue(TW_Device_Extension *tw_dev);
@@ -473,6 +496,7 @@
 int tw_scsiop_request_sense(TW_Device_Extension *tw_dev, int request_id);
 int tw_scsiop_synchronize_cache(TW_Device_Extension *tw_dev, int request_id);
 int tw_scsiop_test_unit_ready(TW_Device_Extension *tw_dev, int request_id);
+int tw_scsiop_test_unit_ready_complete(TW_Device_Extension *tw_dev, int request_id);
 int tw_setfeature(TW_Device_Extension *tw_dev, int parm, int param_size, 
 		  unsigned char *val);
 int tw_setup_irq(TW_Device_Extension *tw_dev);
@@ -505,7 +529,7 @@
 	reset : NULL,					\
 	slave_attach : NULL,				\
 	bios_param : tw_scsi_biosparam,			\
-	can_queue : TW_Q_LENGTH-1,			\
+	can_queue : TW_Q_LENGTH-2,			\
 	this_id: -1,					\
 	sg_tablesize : TW_MAX_SGL_LENGTH,		\
 	cmd_per_lun: TW_MAX_CMDS_PER_LUN,		\
@@ -538,7 +562,7 @@
 	reset : NULL,					\
 	slave_attach : NULL,				\
 	bios_param : tw_scsi_biosparam,			\
-	can_queue : TW_Q_LENGTH-1,			\
+	can_queue : TW_Q_LENGTH-2,			\
 	this_id: -1,					\
 	sg_tablesize : TW_MAX_SGL_LENGTH,		\
 	cmd_per_lun: TW_MAX_CMDS_PER_LUN,		\
diff -urN linux-2.4.21-pre5/drivers/scsi/53c7xx.c linux-2.4.21-pre6/drivers/scsi/53c7xx.c
--- linux-2.4.21-pre5/drivers/scsi/53c7xx.c	Thu Nov 28 15:53:14 2002
+++ linux-2.4.21-pre6/drivers/scsi/53c7xx.c	Wed Mar 26 16:11:37 2003
@@ -1466,9 +1466,9 @@
     patch_abs_32 (hostdata->script, 0, test_src, 
 	virt_to_bus(&hostdata->test_source));
     patch_abs_32 (hostdata->script, 0, saved_dsa,
-	virt_to_bus(&hostdata->saved2_dsa));
+	virt_to_bus((void *)&hostdata->saved2_dsa));
     patch_abs_32 (hostdata->script, 0, emulfly,
-	virt_to_bus(&hostdata->emulated_intfly));
+	virt_to_bus((void *)&hostdata->emulated_intfly));
 
     patch_abs_rwri_data (hostdata->script, 0, dsa_check_reselect, 
 	(unsigned char)(Ent_dsa_code_check_reselect - Ent_dsa_zero));
diff -urN linux-2.4.21-pre5/drivers/scsi/ChangeLog.ips linux-2.4.21-pre6/drivers/scsi/ChangeLog.ips
--- linux-2.4.21-pre5/drivers/scsi/ChangeLog.ips	Thu Nov 28 15:53:14 2002
+++ linux-2.4.21-pre6/drivers/scsi/ChangeLog.ips	Wed Mar 26 16:11:37 2003
@@ -1,6 +1,14 @@
 IBM ServeRAID driver Change Log
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
+        6.00.00  - Add 6x Adapters and Battery Flash 
+        
+        5.30.00  - use __devexit_p()
+	
+        5.10.15  - remove unused code (sem, macros, etc.)                         
+
+        5.10.12  - use pci_dma interfaces, update for 2.5 kernel changes  
+	      
         5.00.01  - Sarasota ( 5i ) adapters must always be scanned first  
                  - Get rid on IOCTL_NEW_COMMAND code  
                  - Add Extended DCDB Commands for Tape Support in 5I                  
diff -urN linux-2.4.21-pre5/drivers/scsi/Makefile linux-2.4.21-pre6/drivers/scsi/Makefile
--- linux-2.4.21-pre5/drivers/scsi/Makefile	Wed Mar 26 16:11:29 2003
+++ linux-2.4.21-pre6/drivers/scsi/Makefile	Wed Mar 26 16:11:37 2003
@@ -50,7 +50,7 @@
 obj-$(CONFIG_ATARI_SCSI)	+= atari_scsi.o
 obj-$(CONFIG_MAC_SCSI)		+= mac_scsi.o
 obj-$(CONFIG_SCSI_MAC_ESP)	+= mac_esp.o	NCR53C9x.o
-obj-$(CONFIG_SUN3_SCSI)		+= sun3_scsi.o
+obj-$(CONFIG_SUN3_SCSI)		+= sun3_scsi.o  sun3_scsi_vme.o
 obj-$(CONFIG_MVME16x_SCSI)	+= mvme16x.o	53c7xx.o
 obj-$(CONFIG_BVME6000_SCSI)	+= bvme6000.o	53c7xx.o
 obj-$(CONFIG_SCSI_SIM710)	+= sim710.o
diff -urN linux-2.4.21-pre5/drivers/scsi/amiga7xx.c linux-2.4.21-pre6/drivers/scsi/amiga7xx.c
--- linux-2.4.21-pre5/drivers/scsi/amiga7xx.c	Thu Oct 25 13:53:49 2001
+++ linux-2.4.21-pre6/drivers/scsi/amiga7xx.c	Wed Mar 26 16:11:38 2003
@@ -86,7 +86,7 @@
 #ifdef CONFIG_WARPENGINE_SCSI
     	    case ZORRO_PROD_MACROSYSTEMS_WARP_ENGINE_40xx:
 		if (request_mem_region(address+0x40000, 0x1000, "ncr53c710")) {
-		    address = (unsigned long)ioremap(address, size);
+		    address = (unsigned long)z_ioremap(address, size);
 		    options = OPTION_MEMORY_MAPPED | OPTION_DEBUG_TEST1 |
 			      OPTION_INTFLY | OPTION_SYNCHRONOUS |
 			      OPTION_ALWAYS_SYNCHRONOUS | OPTION_DISCONNECT;
@@ -102,7 +102,7 @@
 	    case ZORRO_PROD_CBM_A4091_1:
 	    case ZORRO_PROD_CBM_A4091_2:
 		if (request_mem_region(address+0x800000, 0x1000, "ncr53c710")) {
-		    address = (unsigned long)ioremap(address, size);
+		    address = (unsigned long)z_ioremap(address, size);
 		    options = OPTION_MEMORY_MAPPED | OPTION_DEBUG_TEST1 |
 			      OPTION_INTFLY | OPTION_SYNCHRONOUS |
 			      OPTION_ALWAYS_SYNCHRONOUS | OPTION_DISCONNECT;
diff -urN linux-2.4.21-pre5/drivers/scsi/atari_NCR5380.c linux-2.4.21-pre6/drivers/scsi/atari_NCR5380.c
--- linux-2.4.21-pre5/drivers/scsi/atari_NCR5380.c	Fri Aug  2 17:39:44 2002
+++ linux-2.4.21-pre6/drivers/scsi/atari_NCR5380.c	Wed Mar 26 16:11:38 2003
@@ -309,13 +309,8 @@
 #undef TAG_NONE
 #define TAG_NONE 0xff
 
-/* For the m68k, the number of bits in 'allocated' must be a multiple of 32! */
-#if (MAX_TAGS % 32) != 0
-#error "MAX_TAGS must be a multiple of 32!"
-#endif
-
 typedef struct {
-    char	allocated[MAX_TAGS/8];
+    DECLARE_BITMAP(allocated, MAX_TAGS);
     int		nr_allocated;
     int		queue_size;
 } TAG_ALLOC;
@@ -334,7 +329,7 @@
     for( target = 0; target < 8; ++target ) {
 	for( lun = 0; lun < 8; ++lun ) {
 	    ta = &TagAlloc[target][lun];
-	    memset( &ta->allocated, 0, MAX_TAGS/8 );
+	    CLEAR_BITMAP( ta->allocated, MAX_TAGS );
 	    ta->nr_allocated = 0;
 	    /* At the beginning, assume the maximum queue size we could
 	     * support (MAX_TAGS). This value will be decreased if the target
@@ -394,8 +389,8 @@
     else {
 	TAG_ALLOC *ta = &TagAlloc[cmd->target][cmd->lun];
 
-	cmd->tag = find_first_zero_bit( &ta->allocated, MAX_TAGS );
-	set_bit( cmd->tag, &ta->allocated );
+	cmd->tag = find_first_zero_bit( ta->allocated, MAX_TAGS );
+	set_bit( cmd->tag, ta->allocated );
 	ta->nr_allocated++;
 	TAG_PRINTK( "scsi%d: using tag %d for target %d lun %d "
 		    "(now %d tags in use)\n",
@@ -424,7 +419,7 @@
     }
     else {
 	TAG_ALLOC *ta = &TagAlloc[cmd->target][cmd->lun];
-	clear_bit( cmd->tag, &ta->allocated );
+	clear_bit( cmd->tag, ta->allocated );
 	ta->nr_allocated--;
 	TAG_PRINTK( "scsi%d: freed tag %d for target %d lun %d\n",
 		    H_NO(cmd), cmd->tag, cmd->target, cmd->lun );
@@ -443,7 +438,7 @@
     for( target = 0; target < 8; ++target ) {
 	for( lun = 0; lun < 8; ++lun ) {
 	    ta = &TagAlloc[target][lun];
-	    memset( &ta->allocated, 0, MAX_TAGS/8 );
+	    CLEAR_BITMAP( ta->allocated, MAX_TAGS );
 	    ta->nr_allocated = 0;
 	}
     }
diff -urN linux-2.4.21-pre5/drivers/scsi/blz1230.c linux-2.4.21-pre6/drivers/scsi/blz1230.c
--- linux-2.4.21-pre5/drivers/scsi/blz1230.c	Fri Aug  2 17:39:44 2002
+++ linux-2.4.21-pre6/drivers/scsi/blz1230.c	Wed Mar 26 16:11:38 2003
@@ -131,8 +131,8 @@
 		esp->eregs = eregs;
 
 		/* Set the command buffer */
-		esp->esp_command = (volatile unsigned char*) cmd_buffer;
-		esp->esp_command_dvma = virt_to_bus(cmd_buffer);
+		esp->esp_command = cmd_buffer;
+		esp->esp_command_dvma = virt_to_bus((void *)cmd_buffer);
 
 		esp->irq = IRQ_AMIGA_PORTS;
 		esp->slot = board+REAL_BLZ1230_ESP_ADDR;
diff -urN linux-2.4.21-pre5/drivers/scsi/blz2060.c linux-2.4.21-pre6/drivers/scsi/blz2060.c
--- linux-2.4.21-pre5/drivers/scsi/blz2060.c	Fri Aug  2 17:39:44 2002
+++ linux-2.4.21-pre6/drivers/scsi/blz2060.c	Wed Mar 26 16:11:38 2003
@@ -110,8 +110,8 @@
 		esp->eregs = (struct ESP_regs *)(address + BLZ2060_ESP_ADDR);
 		
 		/* Set the command buffer */
-		esp->esp_command = (volatile unsigned char*) cmd_buffer;
-		esp->esp_command_dvma = virt_to_bus(cmd_buffer);
+		esp->esp_command = cmd_buffer;
+		esp->esp_command_dvma = virt_to_bus((void *)cmd_buffer);
 
 		esp->irq = IRQ_AMIGA_PORTS;
 		request_irq(IRQ_AMIGA_PORTS, esp_intr, SA_SHIRQ,
diff -urN linux-2.4.21-pre5/drivers/scsi/cpqfcTSinit.c linux-2.4.21-pre6/drivers/scsi/cpqfcTSinit.c
--- linux-2.4.21-pre5/drivers/scsi/cpqfcTSinit.c	Wed Mar 26 16:11:29 2003
+++ linux-2.4.21-pre6/drivers/scsi/cpqfcTSinit.c	Wed Mar 26 16:11:38 2003
@@ -41,6 +41,7 @@
 #include <linux/slab.h>
 #include <linux/vmalloc.h>	// ioremap()
 #include <linux/completion.h>
+#include <linux/init.h>
 #ifdef __alpha__
 #define __KERNEL_SYSCALLS__
 #endif
diff -urN linux-2.4.21-pre5/drivers/scsi/cyberstorm.c linux-2.4.21-pre6/drivers/scsi/cyberstorm.c
--- linux-2.4.21-pre5/drivers/scsi/cyberstorm.c	Fri Aug  2 17:39:44 2002
+++ linux-2.4.21-pre6/drivers/scsi/cyberstorm.c	Wed Mar 26 16:11:38 2003
@@ -131,8 +131,8 @@
 		esp->eregs = (struct ESP_regs *)(address + CYBER_ESP_ADDR);
 		
 		/* Set the command buffer */
-		esp->esp_command = (volatile unsigned char*) cmd_buffer;
-		esp->esp_command_dvma = virt_to_bus(cmd_buffer);
+		esp->esp_command = cmd_buffer;
+		esp->esp_command_dvma = virt_to_bus((void *)cmd_buffer);
 
 		esp->irq = IRQ_AMIGA_PORTS;
 		request_irq(IRQ_AMIGA_PORTS, esp_intr, SA_SHIRQ,
diff -urN linux-2.4.21-pre5/drivers/scsi/cyberstormII.c linux-2.4.21-pre6/drivers/scsi/cyberstormII.c
--- linux-2.4.21-pre5/drivers/scsi/cyberstormII.c	Fri Aug  2 17:39:44 2002
+++ linux-2.4.21-pre6/drivers/scsi/cyberstormII.c	Wed Mar 26 16:11:38 2003
@@ -125,8 +125,8 @@
 		esp->eregs = eregs;
 		
 		/* Set the command buffer */
-		esp->esp_command = (volatile unsigned char*) cmd_buffer;
-		esp->esp_command_dvma = virt_to_bus(cmd_buffer);
+		esp->esp_command = cmd_buffer;
+		esp->esp_command_dvma = virt_to_bus((void *)cmd_buffer);
 
 		esp->irq = IRQ_AMIGA_PORTS;
 		request_irq(IRQ_AMIGA_PORTS, esp_intr, SA_SHIRQ,
diff -urN linux-2.4.21-pre5/drivers/scsi/dpt_i2o.c linux-2.4.21-pre6/drivers/scsi/dpt_i2o.c
--- linux-2.4.21-pre5/drivers/scsi/dpt_i2o.c	Wed Mar 26 16:11:29 2003
+++ linux-2.4.21-pre6/drivers/scsi/dpt_i2o.c	Wed Mar 26 16:11:38 2003
@@ -2560,7 +2560,7 @@
 
 	if(pHba->initialized ) {
 		if (adpt_i2o_status_get(pHba) < 0) {
-			if((rcode = adpt_i2o_reset_hba(pHba) != 0)){
+			if((rcode = adpt_i2o_reset_hba(pHba)) != 0){
 				printk(KERN_WARNING"%s: Could NOT reset.\n", pHba->name);
 				return rcode;
 			}
@@ -2586,7 +2586,7 @@
 			}
 		}
 	} else {
-		if((rcode = adpt_i2o_reset_hba(pHba) != 0)){
+		if((rcode = adpt_i2o_reset_hba(pHba)) != 0){
 			printk(KERN_WARNING"%s: Could NOT reset.\n", pHba->name);
 			return rcode;
 		}
diff -urN linux-2.4.21-pre5/drivers/scsi/fastlane.c linux-2.4.21-pre6/drivers/scsi/fastlane.c
--- linux-2.4.21-pre5/drivers/scsi/fastlane.c	Fri Aug  2 17:39:44 2002
+++ linux-2.4.21-pre6/drivers/scsi/fastlane.c	Wed Mar 26 16:11:38 2003
@@ -140,7 +140,7 @@
 
 		/* Map the physical address space into virtual kernel space */
 		address = (unsigned long)
-			ioremap_nocache(board, z->resource.end-board+1);
+			z_ioremap(board, z->resource.end-board+1);
 
 		if(!address){
 			printk("Could not remap Fastlane controller memory!");
@@ -161,8 +161,8 @@
 		esp->edev = (void *) address;
 		
 		/* Set the command buffer */
-		esp->esp_command = (volatile unsigned char*) cmd_buffer;
-		esp->esp_command_dvma = virt_to_bus(cmd_buffer);
+		esp->esp_command = cmd_buffer;
+		esp->esp_command_dvma = virt_to_bus((void *)cmd_buffer);
 
 		esp->irq = IRQ_AMIGA_PORTS;
 		esp->slot = board+FASTLANE_ESP_ADDR;
@@ -189,7 +189,7 @@
 	return 0;
 
  err_unmap:
-	iounmap((void *)address);
+	z_iounmap((void *)address);
  err_unregister:
 	scsi_unregister (esp->ehost);
  err_release:
diff -urN linux-2.4.21-pre5/drivers/scsi/ide-scsi.c linux-2.4.21-pre6/drivers/scsi/ide-scsi.c
--- linux-2.4.21-pre5/drivers/scsi/ide-scsi.c	Wed Mar 26 16:11:29 2003
+++ linux-2.4.21-pre6/drivers/scsi/ide-scsi.c	Wed Mar 26 16:11:38 2003
@@ -1107,7 +1107,12 @@
 {
 	ide_drive_t *drive	= idescsi_drives[cmd->target];
 
+	/* We cannot reset the interface holding the lock. We can
+	   drop the lock here however */
+	   
+	spin_unlock(&io_request_lock);
 	(void) ide_do_reset(drive);
+	spin_lock(&io_request_lock);
 	return SCSI_RESET_SUCCESS;
 }
 
diff -urN linux-2.4.21-pre5/drivers/scsi/ips.c linux-2.4.21-pre6/drivers/scsi/ips.c
--- linux-2.4.21-pre5/drivers/scsi/ips.c	Wed Mar 26 16:11:29 2003
+++ linux-2.4.21-pre6/drivers/scsi/ips.c	Wed Mar 26 16:11:38 2003
@@ -1,11 +1,12 @@
 /*****************************************************************************/
-/* ips.c -- driver for the IBM ServeRAID controller                          */
+/* ips.c -- driver for the Adaptec / IBM ServeRAID controller                */
 /*                                                                           */
 /* Written By: Keith Mitchell, IBM Corporation                               */
 /*             Jack Hammer, Adaptec, Inc.                                    */
 /*             David Jeffery, Adaptec, Inc.                                  */
 /*                                                                           */
-/* Copyright (C) 2000 IBM Corporation                                        */
+/* Copyright (C) 2000 IBM Corporation                                        */ 
+/* Copyright (C) 2003 Adaptec, Inc.                                          */ 
 /*                                                                           */
 /* 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      */
@@ -42,7 +43,7 @@
 /* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
 /*                                                                           */
 /* Bugs/Comments/Suggestions about this driver should be mailed to:          */
-/*      ipslinux@adaptec.com     	                                         */
+/*      ipslinux@adaptec.com        	                                     */
 /*                                                                           */
 /* For system support issues, contact your local IBM Customer support.       */
 /* Directions to find IBM Customer Support for each country can be found at: */
@@ -66,9 +67,8 @@
 /* 0.99.05  - Fix an oops when we get certain passthru commands              */
 /* 1.00.00  - Initial Public Release                                         */
 /*            Functionally equivalent to 0.99.05                             */
-/* 3.60.00  - Bump max commands to 128 for use with ServeRAID firmware 3.60  */
-/*          - Change version to 3.60 to coincide with ServeRAID release      */
-/*            numbering.                                                     */
+/* 3.60.00  - Bump max commands to 128 for use with firmware 3.60            */
+/*          - Change version to 3.60 to coincide with release numbering.     */
 /* 3.60.01  - Remove bogus error check in passthru routine                   */
 /* 3.60.02  - Make DCDB direction based on lookup table                      */
 /*          - Only allow one DCDB command to a SCSI ID at a time             */
@@ -76,7 +76,7 @@
 /* 4.00.01  - Add support for First Failure Data Capture                     */
 /* 4.00.02  - Fix problem with PT DCDB with no buffer                        */
 /* 4.00.03  - Add alternative passthru interface                             */
-/*          - Add ability to flash ServeRAID BIOS                            */
+/*          - Add ability to flash BIOS                                      */
 /* 4.00.04  - Rename structures/constants to be prefixed with IPS_           */
 /* 4.00.05  - Remove wish_block from init routine                            */
 /*          - Use linux/spinlock.h instead of asm/spinlock.h for kernels     */
@@ -126,6 +126,10 @@
 /* 5.00.01  - Sarasota ( 5i ) adapters must always be scanned first          */
 /*          - Get rid on IOCTL_NEW_COMMAND code                              */
 /*          - Add Extended DCDB Commands for Tape Support in 5I              */
+/* 5.10.12  - use pci_dma interfaces, update for 2.5 kernel changes          */
+/* 5.10.15  - remove unused code (sem, macros, etc.)                         */
+/* 5.30.00  - use __devexit_p()                                              */
+/* 6.00.00  - Add 6x Adapters and Battery Flash                              */
 /*****************************************************************************/
 
 /*
@@ -157,13 +161,10 @@
 #include <linux/kernel.h>
 #include <linux/ioport.h>
 #include <linux/slab.h>
-#include <linux/vmalloc.h>
 #include <linux/delay.h>
-#include <linux/sched.h>
 #include <linux/pci.h>
 #include <linux/proc_fs.h>
 #include <linux/reboot.h>
-#include <linux/tqueue.h>
 #include <linux/interrupt.h>
 
 #include <linux/blk.h>
@@ -198,8 +199,8 @@
 /*
  * DRIVER_VER
  */
-#define IPS_VERSION_HIGH        "5.10"
-#define IPS_VERSION_LOW         ".21 "
+#define IPS_VERSION_HIGH        "6.00"
+#define IPS_VERSION_LOW         ".26 "
 
 
 #if LINUX_VERSION_CODE < LinuxVersionCode(2,4,0)
@@ -223,7 +224,7 @@
                                              dma_addr_t *dmahandle) {
        void * ptr = kmalloc(size, GFP_ATOMIC); 
        if(ptr){     
-          *dmahandle = VIRT_TO_BUS(ptr);
+          *dmahandle = (uint32_t)virt_to_bus(ptr);
        }
        return ptr;
     }
@@ -232,15 +233,34 @@
     
     #define pci_map_sg(a,b,n,z)       (n)
     #define pci_unmap_sg(a,b,c,d)     
-    #define pci_map_single(a,b,c,d)   (VIRT_TO_BUS(b))
+    #define pci_map_single(a,b,c,d)   ((uint32_t)virt_to_bus(b))
     #define pci_unmap_single(a,b,c,d) 
     #ifndef sg_dma_address
-      #define sg_dma_address(x)         (VIRT_TO_BUS((x)->address))
+      #define sg_dma_address(x)         ((uint32_t)virt_to_bus((x)->address))
       #define sg_dma_len(x)             ((x)->length)
     #endif
     #define pci_unregister_driver(x)
 #endif
 
+#if LINUX_VERSION_CODE <= LinuxVersionCode(2,5,0)
+    #define IPS_SG_ADDRESS(sg)       ((sg)->address)
+    #define IPS_LOCK_SAVE(lock,flags) spin_lock_irqsave(&io_request_lock,flags)
+    #define IPS_UNLOCK_RESTORE(lock,flags) spin_unlock_irqrestore(&io_request_lock,flags)
+    #ifndef __devexit_p
+        #define __devexit_p(x) x
+    #endif
+#else
+    #define IPS_SG_ADDRESS(sg)      (page_address((sg)->page) ? \
+                                     page_address((sg)->page)+(sg)->offset : 0)
+    #define IPS_LOCK_SAVE(lock,flags) spin_lock(lock)
+    #define IPS_UNLOCK_RESTORE(lock,flags) spin_unlock(lock)
+#endif
+
+#define IPS_DMA_DIR(scb) ((!scb->scsi_cmd || ips_is_passthru(scb->scsi_cmd) || \
+                         SCSI_DATA_NONE == scb->scsi_cmd->sc_data_direction) ? \
+                         PCI_DMA_BIDIRECTIONAL : \
+                         scsi_to_pci_dma_dir(scb->scsi_cmd->sc_data_direction))
+
 #ifdef IPS_DEBUG
    #define METHOD_TRACE(s, i)    if (ips_debug >= (i+10)) printk(KERN_NOTICE s "\n");
    #define DEBUG(i, s)           if (ips_debug >= i) printk(KERN_NOTICE s "\n");
@@ -265,19 +285,26 @@
 static int          ips_force_memio = 1;             /* Always use Memory Mapped I/O    */
 static int          ips_force_i2o = 1;               /* Always use I2O command delivery */
 static int          ips_ioctlsize = IPS_IOCTL_SIZE;  /* Size of the ioctl buffer        */
-static int          ips_cd_boot = 0;                 /* Booting from ServeRAID Manager CD */
+static int          ips_cd_boot = 0;                 /* Booting from Manager CD         */
 static char        *ips_FlashData = NULL;            /* CD Boot - Flash Data Buffer      */
-static int          ips_FlashDataInUse = 0;          /* CD Boot - Flash Data In Use Flag */
+static long         ips_FlashDataInUse = 0;          /* CD Boot - Flash Data In Use Flag */
 static uint32_t     MaxLiteCmds = 32;                /* Max Active Cmds for a Lite Adapter */  
 
 IPS_DEFINE_COMPAT_TABLE( Compatable );               /* Version Compatability Table      */
 
 
 #if LINUX_VERSION_CODE >= LinuxVersionCode(2,4,0)
-   /* This table describes any / all ServeRAID Adapters */
+   /* This table describes all ServeRAID Adapters */
    static struct  pci_device_id  ips_pci_table[]  __devinitdata = {
            { 0x1014, 0x002E, PCI_ANY_ID, PCI_ANY_ID, 0, 0 },
            { 0x1014, 0x01BD, PCI_ANY_ID, PCI_ANY_ID, 0, 0 },
+           { 0x9005, 0x0250, PCI_ANY_ID, PCI_ANY_ID, 0, 0 },
+           { 0, }
+   };
+
+   /* This table describes only Anaconda Family Adapters */
+   static struct  pci_device_id  ips_pci_table_anaconda[]  __devinitdata = {
+           { 0x1014, 0x002E, PCI_ANY_ID, PCI_ANY_ID, 0, 0 },
            { 0, }
    };
 
@@ -287,13 +314,25 @@
                    { 0x1014, 0x01BD, PCI_ANY_ID, 0x258, 0, 0 },
                    { 0, }
                    };   
+
+   /* This table describes only Sebring ( ServeRAID 6i ) Adapters */
+   static struct  pci_device_id  ips_pci_table_6i[]  __devinitdata = {
+                   { 0x9005, 0x0250, PCI_ANY_ID, 0x28C, 0, 0 },
+                   { 0, }
+                   };   
     
-   /* This table describes all i960 Adapters */
+   /* This table describes all i960 ( 4M, 4Mx, 4L, 4Lx ) Adapters */
    static struct  pci_device_id  ips_pci_table_i960[]  __devinitdata = {
                    { 0x1014, 0x01BD, PCI_ANY_ID, PCI_ANY_ID, 0, 0 },
                    { 0, }
                    };   
 
+   /* This table describes all Adaptec ( 6M ) Adapters */
+   static struct  pci_device_id  ips_pci_table_adaptec[]  __devinitdata = {
+                   { 0x9005, 0x0250, PCI_ANY_ID, PCI_ANY_ID, 0, 0 },
+                   { 0, }
+                   };   
+
    MODULE_DEVICE_TABLE( pci, ips_pci_table );
 
    static char ips_hot_plug_name[] = "ips";
@@ -308,12 +347,26 @@
        remove:		__devexit_p(ips_remove_device),
    }; 
            
+   struct pci_driver ips_pci_driver_anaconda = {
+       name:		ips_hot_plug_name,
+       id_table:	ips_pci_table_anaconda,
+       probe:		ips_insert_device,
+       remove:		__devexit_p(ips_remove_device),
+   }; 
+
    struct pci_driver ips_pci_driver_5i = {
        name:		ips_hot_plug_name,
        id_table:	ips_pci_table_5i,
        probe:		ips_insert_device,
        remove:		__devexit_p(ips_remove_device),
    };
+           
+   struct pci_driver ips_pci_driver_6i = {
+       name:		ips_hot_plug_name,
+       id_table:	ips_pci_table_6i,
+       probe:		ips_insert_device,
+       remove:		__devexit_p(ips_remove_device),
+   };
 
    struct pci_driver ips_pci_driver_i960 = {
        name:		ips_hot_plug_name,
@@ -322,6 +375,13 @@
        remove:		__devexit_p(ips_remove_device),
    };
 
+   struct pci_driver ips_pci_driver_adaptec = {
+       name:		ips_hot_plug_name,
+       id_table:	ips_pci_table_adaptec,
+       probe:		ips_insert_device,
+       remove:		__devexit_p(ips_remove_device),
+   };
+
 #endif
 
 /*
@@ -344,9 +404,17 @@
    "ServeRAID 4Mx",
    "ServeRAID 4Lx",
    "ServeRAID 5i",
-   "ServeRAID 5i"
+   "ServeRAID 5i",
+   "ServeRAID 6M",
+   "ServeRAID 6i"
 };
 
+/* Init State 0 means we're only looking for a device to provide us the BIOS Adapter Ordering Table */
+/* Init State 1 is when we are actually enumerating the devices.                                    */
+static  int      InitState;
+/* IF BIOS wants to tell us the enumeration order, it puts a table in NVRAM Page 5 */
+static  uint8_t  AdapterOrder[16] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+
 static struct notifier_block ips_notifier = {
    ips_halt, NULL, 0
 };
@@ -479,6 +547,7 @@
 static void ips_free(ips_ha_t *);
 static void ips_init_scb(ips_ha_t *, ips_scb_t *);
 static void ips_freescb(ips_ha_t *, ips_scb_t *);
+static void ips_setup_funclist(ips_ha_t *);
 static void ips_statinit(ips_ha_t *);
 static void ips_statinit_memio(ips_ha_t *);
 static void ips_fix_ffdc_time(ips_ha_t *, ips_scb_t *, time_t);
@@ -512,10 +581,13 @@
 static int copy_info(IPS_INFOSTR *, char *, ...);
 static int ips_get_version_info(ips_ha_t *ha, IPS_VERSION_DATA *Buffer, int intr );
 static void ips_version_check(ips_ha_t *ha, int intr);
+static int ips_abort_init(ips_ha_t *ha, struct Scsi_Host *sh, int index);
 static int ips_init_phase2( int index );
 
 #if LINUX_VERSION_CODE >= LinuxVersionCode(2,4,0)
 static int ips_init_phase1( struct pci_dev *pci_dev, int *indexPtr );
+#else
+static int ips_init_oldphase1(Scsi_Host_Template *SHT);
 #endif
 
 /*--------------------------------------------------------------------------*/
@@ -578,8 +650,8 @@
 
 #else
 
-  char      *p;
-   char       tokens[3] = {',', '.', 0};
+   char   *p;
+   char    tokens[3] = {',', '.', 0};
 
    for (key = strtok(ips_str, tokens); key; key = strtok(NULL, tokens)) {
       p = key;
@@ -593,7 +665,7 @@
          value = p+1;
       } else
          value = NULL;
-
+                          
       /*
        * We now have key/value pairs.
        * Update the variables
@@ -631,33 +703,9 @@
 /****************************************************************************/
 int
 ips_detect(Scsi_Host_Template *SHT) {
-   struct Scsi_Host *sh;
-   ips_ha_t         *ha;
-   uint32_t          io_addr;
-   uint32_t          mem_addr;
-   uint32_t          io_len;
-   uint32_t          mem_len;
-   uint16_t          planer;
-   uint8_t           revision_id;
-   uint8_t           bus;
-   uint8_t           func;
-   uint8_t           irq;
-   uint16_t          deviceID[2];
-   uint16_t          subdevice_id;
-   int               i;
-   int               j;
-   uint32_t          count;
-   char             *ioremap_ptr;
-   char             *mem_ptr;
-   struct pci_dev   *dev[2];
-   struct pci_dev   *morpheus = NULL;
-   struct pci_dev   *trombone = NULL;
-#if LINUX_VERSION_CODE < LinuxVersionCode(2,4,0)
-   uint32_t          currbar;
-   uint32_t          maskbar;
-   uint8_t           barnum;
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,4,0)
+   int  i;
 #endif
-   uint32_t          IsDead;
 
    METHOD_TRACE("ips_detect", 1);
 
@@ -670,8 +718,8 @@
 #endif
 #endif
 
-   /* If Booting from the ServeRAID Manager CD, Allocate a large Flash  */
-   /* Buffer ( so we won't need to allocate one for each adapter ).     */
+   /* If Booting from the Manager CD, Allocate a large Flash        */
+   /* Buffer ( so we won't need to allocate one for each adapter ). */
     if ( ips_cd_boot ) {            
       ips_FlashData = ( char * ) __get_free_pages( GFP_KERNEL, 7 );   
       if (ips_FlashData == NULL) {
@@ -679,16 +727,6 @@
          printk( KERN_WARNING "ERROR: Can't Allocate Large Buffer for Flashing\n" );
       }
    }                                                                               
-
-   SHT->proc_info = ips_proc_info;
-#if LINUX_VERSION_CODE < LinuxVersionCode(2,4,0)
-   SHT->proc_dir = &proc_scsi_ips;
-#else
-   SHT->proc_name = "ips";
-#endif
-
-#if defined(CONFIG_PCI)
-
    /* initalize number of controllers */
    ips_num_controllers = 0;
    ips_next_controller = 0;
@@ -697,539 +735,569 @@
    if (!pci_present())
       return (0);
 
-   morpheus = pci_find_device(IPS_VENDORID, IPS_DEVICEID_MORPHEUS, morpheus);
-   trombone = pci_find_device(IPS_VENDORID, IPS_DEVICEID_COPPERHEAD, trombone);
-
-   /* determine which controller to probe first */
-   if (!morpheus) {
-      /* we only have trombone */
-      dev[0] = trombone;
-      dev[1] = NULL;
-      deviceID[0] = IPS_DEVICEID_COPPERHEAD;
-   } else if (!trombone) {
-      /* we only have morpheus */
-      dev[0] = morpheus;
-      dev[1] = NULL;
-      deviceID[0] = IPS_DEVICEID_MORPHEUS;
-   } else {
-      /* we have both in the system */
-      if (trombone->bus->number < morpheus->bus->number) {
-         dev[0] = trombone;
-         dev[1] = morpheus;
-         deviceID[0] = IPS_DEVICEID_COPPERHEAD;
-         deviceID[1] = IPS_DEVICEID_MORPHEUS;
-      } else if (trombone->bus->number > morpheus->bus->number) {
-         dev[0] = morpheus;
-         dev[1] = trombone;
-         deviceID[0] = IPS_DEVICEID_MORPHEUS;
-         deviceID[1] = IPS_DEVICEID_COPPERHEAD;
-      } else {
-         /* further detection required */
-         if (trombone->devfn < morpheus->devfn) {
-            dev[0] = trombone;
-            dev[1] = morpheus;
-            deviceID[0] = IPS_DEVICEID_COPPERHEAD;
-            deviceID[1] = IPS_DEVICEID_MORPHEUS;
-         } else {
-            dev[0] = morpheus;
-            dev[1] = trombone;
-            deviceID[0] = IPS_DEVICEID_MORPHEUS;
-            deviceID[1] = IPS_DEVICEID_COPPERHEAD;
-         }
-      }
-   }
-
 /**********************************************************************************/
 /* For Kernel Versions 2.4 or greater, use new PCI ( Hot Pluggable ) architecture */
 /**********************************************************************************/
 
 #if LINUX_VERSION_CODE >= LinuxVersionCode(2,4,0)
+ #if LINUX_VERSION_CODE < LinuxVersionCode(2,5,0)
    spin_unlock_irq(&io_request_lock);
+ #endif
+   SHT->proc_info = ips_proc_info;
+   SHT->proc_name = "ips";
 
-   /* By definition, a Sarasota ( 5i ) Adapter MUST be enumerated first or the */
-   /* server may not boot properly. The adapters must be enumerated in exactly */
-   /* the same order as ServeRAID BIOS for the machine to come up properly.    */
-
-   pci_module_init(&ips_pci_driver_5i);          /* Ask for 5i Adapters First  */
-   if (ips_num_controllers)                      /* If there is a 5i Adapter   */
-      pci_module_init(&ips_pci_driver_i960);     /*    Get all i960's next     */
-   pci_module_init(&ips_pci_driver);             /* Get all remaining Adapters */
-                                                 /*  ( in normal BUS order )   */
-   spin_lock_irq(&io_request_lock);
-   if (ips_num_controllers > 0) 
-      register_reboot_notifier(&ips_notifier);
-
-   return (ips_num_controllers);
+   /* There are several special cases ( which are too complicated to enumerate here ) where, due */
+   /* to System BIOS rules, the adapters must be enumerated in a certain order.  If ServeRAID    */
+   /* BIOS tells us the order, then we will follow it.  The first pass at init is simply to be   */
+   /* able to communicate with the first adapter to see if BIOS is telling us the order.         */ 
+   /* This does not apply to ia64 EFI BIOS.                                                      */
+
+#if !defined(__ia64__)
+   InitState = 0;
+   pci_module_init(&ips_pci_driver); /* Look for Any Adapter, to fill in the Adapter Order Table */
 #endif
 
-   /* Now scan the controllers */
-   for (i = 0; i < 2; i++) {
-      if (!dev[i])
-         break;
+   InitState = 1;       
+   
+   if ( AdapterOrder[0] ) {  
+      /* BIOS has dictated the order that we should enumerate Adapters */
+      for ( i = 1; i <= AdapterOrder[0]; i++ ) {
+          switch (AdapterOrder[i]) {
+          case 'M':
+              pci_module_init(&ips_pci_driver_adaptec);     /* Ask for Adaptec Adapters */
+              break;
+          case 'S':
+              pci_module_init(&ips_pci_driver_5i);          /* Ask for 5i Adapters      */
+              pci_module_init(&ips_pci_driver_6i);          /* Ask for 6i Adapters      */
+              break;
+          case 'N':
+              pci_module_init(&ips_pci_driver_i960);        /* Ask for i960 Adapters    */
+              break;
+          case 'A':
+              pci_module_init(&ips_pci_driver_anaconda);    /* Ask for Anaconda Family Adapters */
+              break;
+          default:
+              i = AdapterOrder[0] + 1;                      /* Premature End of List - Ensure Loop Ends */
+              break;
+          }
+      }
+   }
+   else { 
+      /* No Adapter Order Table from BIOS, so sort things the old-fashioned way */
 
-      do {
-         if (ips_next_controller >= IPS_MAX_ADAPTERS)
-            break;
+      /* By definition, an Internal ( 5i or 6i ) Adapter MUST be enumerated first */
+      /* or the server may not boot properly. The adapters must be enumerated in  */
+      /* exactly the same order as BIOS for the machine to come up properly.      */
+      /*    NOTE: There will never be both a 5i and a 6i in the same machine.     */
 
-#if LINUX_VERSION_CODE >= LinuxVersionCode(2,4,0)
-         if (pci_enable_device(dev[i]))
-            break;
-#endif
+      pci_module_init(&ips_pci_driver_5i);          /* Ask for 5i Adapters First  */
+      if (ips_num_controllers) {                    /* If there is a 5i Adapter   */
+         pci_module_init(&ips_pci_driver_i960);     /*    Get all i960's next     */
+      }
+      else {
+         pci_module_init(&ips_pci_driver_6i);         /* Ask if any 6i Adapters   */
+         if (ips_num_controllers)                     /* If there is a 6i Adapter */
+            pci_module_init(&ips_pci_driver_adaptec); /*    Get all Adaptecs next */
+      }
 
-         /* stuff that we get in dev */
-         irq = dev[i]->irq;
-         bus = dev[i]->bus->number;
-         func = dev[i]->devfn;
-
-         /* Init MEM/IO addresses to 0 */
-         mem_addr = 0;
-         io_addr = 0;
-         mem_len = 0;
-         io_len = 0;
+      pci_module_init(&ips_pci_driver);             /* Get all remaining Adapters */
+                                                    /*  ( in normal BUS order )   */
+   }
 
-         for (j = 0; j < 2; j++) {
-#if LINUX_VERSION_CODE >= LinuxVersionCode(2,4,0)
-            if (!pci_resource_start(dev[i], j))
-               break;
+ #if LINUX_VERSION_CODE < LinuxVersionCode(2,5,0)
+   spin_lock_irq(&io_request_lock);
+ #endif
+   if (ips_num_controllers > 0) 
+      register_reboot_notifier(&ips_notifier);
 
-            if (pci_resource_flags(dev[i], j) & IORESOURCE_IO) {
-               io_addr = pci_resource_start(dev[i], j);
-               io_len = pci_resource_len(dev[i], j);
-            } else {
-               mem_addr = pci_resource_start(dev[i], j);
-               mem_len = pci_resource_len(dev[i], j);
-            }
+   return (ips_num_controllers);
 #else
-            if (!dev[i]->base_address[j])
-               break;
-
-            if ((dev[i]->base_address[j] & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_IO) {
-               barnum = PCI_BASE_ADDRESS_0 + (j * 4);
-               io_addr = dev[i]->base_address[j] & PCI_BASE_ADDRESS_IO_MASK;
-
-               /* Get Size */
-               pci_read_config_dword(dev[i], barnum, &currbar);
-               pci_write_config_dword(dev[i], barnum, ~0);
-               pci_read_config_dword(dev[i], barnum, &maskbar);
-               pci_write_config_dword(dev[i], barnum, currbar);
+   InitState = 1;
+   SHT->proc_info = ips_proc_info;
+   SHT->proc_dir = &proc_scsi_ips;
+   return ips_init_oldphase1(SHT);
+#endif /* LINUX_VERSION_CODE >= LinuxVersionCode(2,4,0) */
 
-               io_len = ~(maskbar & PCI_BASE_ADDRESS_IO_MASK) + 1;
-            } else {
-               barnum = PCI_BASE_ADDRESS_0 + (j * 4);
-               mem_addr = dev[i]->base_address[j] & PCI_BASE_ADDRESS_MEM_MASK;
+}
 
-               /* Get Size */
-               pci_read_config_dword(dev[i], barnum, &currbar);
-               pci_write_config_dword(dev[i], barnum, ~0);
-               pci_read_config_dword(dev[i], barnum, &maskbar);
-               pci_write_config_dword(dev[i], barnum, currbar);
+#if LINUX_VERSION_CODE < LinuxVersionCode(2,4,0)
 
-               mem_len = ~(maskbar & PCI_BASE_ADDRESS_MEM_MASK) + 1;
+/***********************************************************************************/
+/*   Sort the Device Structures                                                    */
+/*   Devices are sorted by groups ( type ) and then PCI address within each group  */  
+/*   This also results in the same ordering that results when using the 2.4 kernel */
+/*   architecture for initialization.                                              */
+/***********************************************************************************/
+
+static void 
+ips_sort_controllers(struct pci_dev *dev[]) {
+   struct pci_dev *tempdev[IPS_MAX_ADAPTERS];
+   struct pci_dev *lowestdev;
+   int             i, j;
+   int             temp_index = 0;    /* Index into tempdev[] array */    
+   int             lowIndex = 0;
+   int             newlowflag = 0;    /* Flag to indicate when a new low address has been found */
+   uint16_t        subdevice_id;
+   unsigned char   BusNumber;
+
+   /* Clear the Temporary Dev Structure */
+   for (i = 0; i < IPS_MAX_ADAPTERS; i++) 
+       tempdev[i] = NULL;
+
+   /* The Outer Loop goes thru each Adapter Type Supported */
+   for (j = 0; j < IPS_MAX_ADAPTER_TYPES; j++) {
+      lowestdev = NULL;
+      /* The Inner Loop Checks each Device still in the List and */
+      /* Finds the lowset adapter left ( by PCI boot order )     */
+      for (i = 0; i < IPS_MAX_ADAPTERS; i++) {
+         if (dev[i]) {
+            if (lowestdev == NULL) {  /* If this is the first one found, it must be the lowest ! */
+               lowestdev = dev[i];
+               lowIndex = i;
             }
-#endif
-         }
-
-         /* setup memory mapped area (if applicable) */
-         if (mem_addr) {
-            uint32_t base;
-            uint32_t offs;
-
-            DEBUG_VAR(1, "(%s%d) detect, Memory region %x, size: %d",
-                      ips_name, ips_next_controller, mem_addr, mem_len);
 
-#if LINUX_VERSION_CODE >= LinuxVersionCode(2,4,0)
-            if (check_mem_region(mem_addr, mem_len)) {
-               /* Couldn't allocate io space */
-               printk(KERN_WARNING "(%s%d) couldn't allocate IO space %x len %d.\n",
-                      ips_name, ips_next_controller, io_addr, io_len);
+            /* If you find a Sarasota ( 5i ), it must always be treated as the first adapter */
+            if (dev[i]->device == IPS_DEVICEID_MORPHEUS) {
+                pci_read_config_word(dev[i], PCI_SUBSYSTEM_ID, &subdevice_id);
+                if ((subdevice_id == IPS_SUBDEVICEID_5I1) || 
+                    (subdevice_id == IPS_SUBDEVICEID_5I2)) {
+                     lowestdev = dev[i];
+                     lowIndex = i;
+                     break;
+                }
+            } 
 
-               ips_next_controller++;
+            /* If you find a Sebring ( 6i ), it must always be treated as the first adapter */
+            if (dev[i]->device == IPS_DEVICEID_MARCO) {
+                pci_read_config_word(dev[i], PCI_SUBSYSTEM_ID, &subdevice_id);
+                if (subdevice_id == IPS_SUBDEVICEID_6I) {
+                    lowestdev = dev[i];
+                    lowIndex = i;
+                    break;
+                }
+            }    
 
-               continue;
-            }
+            /* Determine if this device is at a lower PCI address than the current lowest device */
+            newlowflag = 0;
 
-            request_mem_region(mem_addr, mem_len, "ips");
-#endif
+            if (dev[i]->device == IPS_DEVICEID_MARCO)     /* System BIOS adds 1 to Marco Bus Number */
+                BusNumber = ( dev[i]->bus->number ) - 1;  /*        because of Bridge Chip          */
+            else
+                BusNumber = dev[i]->bus->number;
 
-            base = mem_addr & PAGE_MASK;
-            offs = mem_addr - base;
+            if (BusNumber < lowestdev->bus->number)        /* IF a lower BUS # */
+                newlowflag = i;
 
-            ioremap_ptr = ioremap(base, PAGE_SIZE);
-            mem_ptr = ioremap_ptr + offs;
-         } else {
-            ioremap_ptr = NULL;
-            mem_ptr = NULL;
+            if ((BusNumber == lowestdev->bus->number) &&   /* If Same Bus #, but a lower device # */
+                (dev[i]->devfn < lowestdev->devfn)) 
+                newlowflag = i;
+
+            if ( newlowflag ) {
+                lowestdev = dev[i];
+                lowIndex = i;
+            }
          }
-
-         /* setup I/O mapped area (if applicable) */
-         if (io_addr) {
-            DEBUG_VAR(1, "(%s%d) detect, IO region %x, size: %d",
-                      ips_name, ips_next_controller, io_addr, io_len);
-
-            if (check_region(io_addr, io_len)) {
-               /* Couldn't allocate io space */
-               printk(KERN_WARNING "(%s%d) couldn't allocate IO space %x len %d.\n",
-                      ips_name, ips_next_controller, io_addr, io_len);
-
-               ips_next_controller++;
-
-               continue;
+      }
+                                                                                     
+      if (lowestdev) {                       /* If we found another adapter */
+         tempdev[temp_index] = lowestdev;    /*    Add it in the list       */
+         dev[lowIndex] = NULL;               /*    Null it out so we don't find it again */
+         temp_index++;
+         /* Now get all the adapters that are the same type as the low one      . */
+         /* They will already be in order, so they don't need any further sorting.*/
+         for (i = 0; i < IPS_MAX_ADAPTERS; i++) {
+            if (dev[i]) {
+               if (dev[i]->device == lowestdev->device) {
+                  tempdev[temp_index] = dev[i];  /* Add the same type adapter to the list */
+                  temp_index++;                    
+                  dev[i] = NULL;                 /* Null it out so we don't find it again */
+               }                           
             }
-
-            request_region(io_addr, io_len, "ips");
          }
+      }
+   }
 
-         /* get planer status */
-         if (pci_read_config_word(dev[i], 0x04, &planer)) {
-            printk(KERN_WARNING "(%s%d) can't get planer status.\n",
-                   ips_name, ips_next_controller);
+   /* Copy the Sorted Adapter Pointers ( tempdev[] ) to the Original Structure */
+   for (i = 0; i < IPS_MAX_ADAPTERS; i++) 
+       dev[i] = tempdev[i];
 
-            ips_next_controller++;
+}
 
-            continue;
-         }
+/****************************************************************************/
+/*   Detect and initialize the driver for 2.2 kernels                       */
+/*                                                                          */
+/* NOTE: this routine is called under the io_request_lock spinlock          */
+/****************************************************************************/
+static int ips_init_oldphase1(Scsi_Host_Template *SHT){
+   struct Scsi_Host *sh;
+   ips_ha_t         *ha;
+   uint32_t          io_addr;
+   uint32_t          mem_addr;
+   uint32_t          io_len;
+   uint32_t          mem_len;
+   uint16_t          planer;
+   uint8_t           revision_id;
+   uint8_t           bus;
+   uint8_t           func;
+   uint8_t           irq;
+   uint16_t          subdevice_id;
+   int               i;
+   int               j;
+   uint32_t          count;
+   char             *ioremap_ptr;
+   char             *mem_ptr;
+   struct pci_dev   *dev[IPS_MAX_ADAPTERS];
+   dma_addr_t        dma_address;
+   uint32_t          currbar;
+   uint32_t          maskbar;
+   uint8_t           barnum;
+   uint32_t          IsDead;
 
-         /* check to see if an onboard planer controller is disabled */
-         if (!(planer & 0x000C)) {
+   METHOD_TRACE("ips_init_oldphase1", 1);
+   
+   for ( i = 0; i < IPS_MAX_ADAPTERS; i++ ) 
+       dev[i] = NULL;
+   
+   /* Find all the adapters that we support and save them in the dev[] structure */
+   i = 0;
+   dev[i] = pci_find_device(IPS_VENDORID_IBM, IPS_DEVICEID_MORPHEUS, NULL);
+   while ( dev[i] ) {
+      i++;
+      dev[i] = pci_find_device(IPS_VENDORID_IBM, IPS_DEVICEID_MORPHEUS, dev[i-1]);
+   }
+
+   dev[i] = pci_find_device(IPS_VENDORID_IBM, IPS_DEVICEID_COPPERHEAD, NULL);
+   while ( dev[i] ) {
+       i++;
+       dev[i] = pci_find_device(IPS_VENDORID_IBM, IPS_DEVICEID_COPPERHEAD, dev[i-1]);
+   }
+
+   dev[i] = pci_find_device(IPS_VENDORID_ADAPTEC, IPS_DEVICEID_MARCO, NULL);
+   while ( dev[i] ) {
+       i++;
+       dev[i] = pci_find_device(IPS_VENDORID_IBM, IPS_DEVICEID_MARCO, dev[i-1]);
+   }
+     
+   /* Sort the Adapters */
+   if ( dev[0] ) 
+      ips_sort_controllers( dev );
+   else
+      return (0);    
 
-            DEBUG_VAR(1, "(%s%d) detect, Onboard ServeRAID disabled by BIOS",
-                      ips_name, ips_next_controller);
+   /* Now scan and Initialize the controllers */
+   for ( i = 0; i < IPS_MAX_ADAPTERS; i++ ) {
+      if (!dev[i])
+         break;
 
-            ips_next_controller++;
+      if (ips_next_controller >= IPS_MAX_ADAPTERS)
+         break;
 
-            continue;
-         }
+      /* stuff that we get in dev */
+      irq = dev[i]->irq;
+      bus = dev[i]->bus->number;
+      func = dev[i]->devfn;
+
+      /* Init MEM/IO addresses to 0 */
+      mem_addr = 0;
+      io_addr = 0;
+      mem_len = 0;
+      io_len = 0;
 
-         DEBUG_VAR(1, "(%s%d) detect bus %d, func %x, irq %d, io %x, mem: %x, ptr: %p",
-                   ips_name, ips_next_controller, bus, func, irq, io_addr, mem_addr, mem_ptr);
+      for (j = 0; j < 2; j++) {
+         if (!dev[i]->base_address[j])
+            break;
 
-         /* get the revision ID */
-         if (pci_read_config_byte(dev[i], PCI_REVISION_ID, &revision_id)) {
-            printk(KERN_WARNING "(%s%d) can't get revision id.\n",
-                   ips_name, ips_next_controller);
+         if ((dev[i]->base_address[j] & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_IO) {
+            barnum = PCI_BASE_ADDRESS_0 + (j * 4);
+            io_addr = dev[i]->base_address[j] & PCI_BASE_ADDRESS_IO_MASK;
 
-            ips_next_controller++;
+            /* Get Size */
+            pci_read_config_dword(dev[i], barnum, &currbar);
+            pci_write_config_dword(dev[i], barnum, ~0);
+            pci_read_config_dword(dev[i], barnum, &maskbar);
+            pci_write_config_dword(dev[i], barnum, currbar);
 
-            continue;
-         }
+            io_len = ~(maskbar & PCI_BASE_ADDRESS_IO_MASK) + 1;
+        } else {
+            barnum = PCI_BASE_ADDRESS_0 + (j * 4);
+            mem_addr = dev[i]->base_address[j] & PCI_BASE_ADDRESS_MEM_MASK;
 
-#if LINUX_VERSION_CODE < LinuxVersionCode(2,4,0)
-         /* get the subdevice id */
-         if (pci_read_config_word(dev[i], PCI_SUBSYSTEM_ID, &subdevice_id)) {
-            printk(KERN_WARNING "(%s%d) can't get subdevice id.\n",
-                   ips_name, ips_next_controller);
+            /* Get Size */
+            pci_read_config_dword(dev[i], barnum, &currbar);
+            pci_write_config_dword(dev[i], barnum, ~0);
+            pci_read_config_dword(dev[i], barnum, &maskbar);
+            pci_write_config_dword(dev[i], barnum, currbar);
 
-            ips_next_controller++;
-
-            continue;
+            mem_len = ~(maskbar & PCI_BASE_ADDRESS_MEM_MASK) + 1;
          }
-#else
-         subdevice_id = dev[i]->subsystem_device;
-#endif
-
-         /* found a controller */
-         sh = scsi_register(SHT, sizeof(ips_ha_t));
-
-         if (sh == NULL) {
-            printk(KERN_WARNING "(%s%d) Unable to register controller with SCSI subsystem - skipping controller\n",
-                   ips_name, ips_next_controller);
-
-            ips_next_controller++;
+      }
 
-            continue;
-         }
+      /* setup memory mapped area (if applicable) */
+      if (mem_addr) {
+         uint32_t base;
+         uint32_t offs;
 
-         ha = IPS_HA(sh);
-         memset(ha, 0, sizeof(ips_ha_t));
+         DEBUG_VAR(1, "(%s%d) detect, Memory region %x, size: %d",
+                   ips_name, ips_next_controller, mem_addr, mem_len);
 
-         /* Initialize spin lock */
-         spin_lock_init(&ha->scb_lock);
-         spin_lock_init(&ha->copp_lock);
-         spin_lock_init(&ha->ips_lock);
-         spin_lock_init(&ha->copp_waitlist.lock);
-         spin_lock_init(&ha->scb_waitlist.lock);
-         spin_lock_init(&ha->scb_activelist.lock);
-
-         ips_sh[ips_next_controller] = sh;
-         ips_ha[ips_next_controller] = ha;
-         ips_num_controllers++;
-         ha->active = 1;
+         base = mem_addr & PAGE_MASK;
+         offs = mem_addr - base;
 
-         ha->enq = kmalloc(sizeof(IPS_ENQ), GFP_KERNEL);
+         ioremap_ptr = ioremap(base, PAGE_SIZE);
+         mem_ptr = ioremap_ptr + offs;
+      } else {
+         ioremap_ptr = NULL;
+         mem_ptr = NULL;
+      }
 
-         if (!ha->enq) {
-            printk(KERN_WARNING "(%s%d) Unable to allocate host inquiry structure - skipping contoller\n",
-                   ips_name, ips_next_controller);
+      /* setup I/O mapped area (if applicable) */
+      if (io_addr) {
+         DEBUG_VAR(1, "(%s%d) detect, IO region %x, size: %d",
+                   ips_name, ips_next_controller, io_addr, io_len);
+
+         if (check_region(io_addr, io_len)) {
+             /* Couldn't allocate io space */
+            printk(KERN_WARNING "(%s%d) couldn't allocate IO space %x len %d.\n",
+                   ips_name, ips_next_controller, io_addr, io_len);
 
-            ha->active = 0;
-            ips_free(ha);
-            scsi_unregister(sh);
-            ips_ha[ips_next_controller] = 0;
-            ips_sh[ips_next_controller] = 0;
             ips_next_controller++;
-            ips_num_controllers--;
 
             continue;
          }
 
-         ha->adapt = kmalloc(sizeof(IPS_ADAPTER), GFP_KERNEL);
+         request_region(io_addr, io_len, "ips");
+      }
 
-         if (!ha->adapt) {
-            printk(KERN_WARNING "(%s%d) Unable to allocate host adapt structure - skipping controller\n",
-                   ips_name, ips_next_controller);
+      /* get planer status */
+      if (pci_read_config_word(dev[i], 0x04, &planer)) {
+         printk(KERN_WARNING "(%s%d) can't get planer status.\n",
+                ips_name, ips_next_controller);
 
-            ha->active = 0;
-            ips_free(ha);
-            scsi_unregister(sh);
-            ips_ha[ips_next_controller] = 0;
-            ips_sh[ips_next_controller] = 0;
-            ips_next_controller++;
-            ips_num_controllers--;
+         ips_next_controller++;
 
-            continue;
-         }
+         continue;
+      }
 
-         ha->conf = kmalloc(sizeof(IPS_CONF), GFP_KERNEL);
+      /* check to see if an onboard planer controller is disabled */
+      if (!(planer & 0x000C)) {
 
-         if (!ha->conf) {
-            printk(KERN_WARNING "(%s%d) Unable to allocate host conf structure - skipping controller\n",
+         DEBUG_VAR(1, "(%s%d) detect, Onboard controller disabled by BIOS",
                    ips_name, ips_next_controller);
 
-            ha->active = 0;
-            ips_free(ha);
-            scsi_unregister(sh);
-            ips_ha[ips_next_controller] = 0;
-            ips_sh[ips_next_controller] = 0;
-            ips_next_controller++;
-            ips_num_controllers--;
+         ips_next_controller++;
 
-            continue;
-         }
+         continue;
+      }
 
-         ha->nvram = kmalloc(sizeof(IPS_NVRAM_P5), GFP_KERNEL);
+      DEBUG_VAR(1, "(%s%d) detect bus %d, func %x, irq %d, io %x, mem: %x, ptr: %p",
+                ips_name, ips_next_controller, bus, func, irq, io_addr, mem_addr, mem_ptr);
 
-         if (!ha->nvram) {
-            printk(KERN_WARNING "(%s%d) Unable to allocate host nvram structure - skipping controller\n",
-                   ips_name, ips_next_controller);
+      /* get the revision ID */
+      if (pci_read_config_byte(dev[i], PCI_REVISION_ID, &revision_id)) {
+          printk(KERN_WARNING "(%s%d) can't get revision id.\n",
+                 ips_name, ips_next_controller);
+
+        ips_next_controller++;
+        continue;
+      }
+
+      /* get the subdevice id */
+      if (pci_read_config_word(dev[i], PCI_SUBSYSTEM_ID, &subdevice_id)) {
+         printk(KERN_WARNING "(%s%d) can't get subdevice id.\n",
+                ips_name, ips_next_controller);
 
-            ha->active = 0;
-            ips_free(ha);
-            scsi_unregister(sh);
-            ips_ha[ips_next_controller] = 0;
-            ips_sh[ips_next_controller] = 0;
-            ips_next_controller++;
-            ips_num_controllers--;
+         ips_next_controller++;
 
-            continue;
-         }
+         continue;
+      }
 
-         ha->subsys = kmalloc(sizeof(IPS_SUBSYS), GFP_KERNEL);
+      /* found a controller */
+      sh = scsi_register(SHT, sizeof(ips_ha_t));
 
-         if (!ha->subsys) {
-            printk(KERN_WARNING "(%s%d) Unable to allocate host subsystem structure - skipping controller\n",
-                   ips_name, ips_next_controller);
+      if (sh == NULL) {
+         printk(KERN_WARNING "(%s%d) Unable to register controller with SCSI subsystem - skipping controller\n",
+                ips_name, ips_next_controller);
 
-            ha->active = 0;
-            ips_free(ha);
-            scsi_unregister(sh);
-            ips_ha[ips_next_controller] = 0;
-            ips_sh[ips_next_controller] = 0;
-            ips_next_controller++;
-            ips_num_controllers--;
+         ips_next_controller++;
 
-            continue;
-         }
+         continue;
+      }
 
-         ha->dummy = kmalloc(sizeof(IPS_IO_CMD), GFP_KERNEL);
+      ha = IPS_HA(sh);
+      memset(ha, 0, sizeof(ips_ha_t));
 
-         if (!ha->dummy) {
-            printk(KERN_WARNING "(%s%d) Unable to allocate host dummy structure - skipping controller\n",
-                   ips_name, ips_next_controller);
+      ips_sh[ips_next_controller] = sh;
+      ips_ha[ips_next_controller] = ha;
+      ips_num_controllers++;
+      ha->active = 1;
+
+      ha->enq = kmalloc(sizeof(IPS_ENQ), GFP_KERNEL);
+
+      if (!ha->enq) {
+         printk(KERN_WARNING "(%s%d) Unable to allocate host inquiry structure - skipping contoller\n",
+                ips_name, ips_next_controller);
+         ips_abort_init(ha, sh, ips_next_controller);
+         ips_next_controller++;
+         ips_num_controllers--;
 
-            ha->active = 0;
-            ips_free(ha);
-            scsi_unregister(sh);
-            ips_ha[ips_next_controller] = 0;
-            ips_sh[ips_next_controller] = 0;
-            ips_next_controller++;
-            ips_num_controllers--;
+         continue;
+      }
 
-            continue;
-         }
+      ha->adapt = pci_alloc_consistent(dev[i], sizeof(IPS_ADAPTER) + sizeof(IPS_IO_CMD), &dma_address);
 
-         for (count = PAGE_SIZE, ha->ioctl_order = 0;
-              count < ips_ioctlsize;
-              ha->ioctl_order++, count <<= 1);
+      if (!ha->adapt) {
+         printk(KERN_WARNING "(%s%d) Unable to allocate host adapt and dummy structure - skipping controller\n",
+                ips_name, ips_next_controller);
+         ips_abort_init(ha, sh, ips_next_controller);
+         ips_next_controller++;
+         ips_num_controllers--;
 
-         ha->ioctl_data = (char *) __get_free_pages(GFP_KERNEL, ha->ioctl_order);
-         ha->ioctl_datasize = count;
+         continue;
+      }
+      ha->adapt->hw_status_start = dma_address;
+      ha->dummy = (void *)ha->adapt + 1;
 
-         if (!ha->ioctl_data) {
-            printk(KERN_WARNING "(%s%d) Unable to allocate ioctl data\n",
-                   ips_name, ips_next_controller);
+      ha->conf = kmalloc(sizeof(IPS_CONF), GFP_KERNEL);
 
-            ha->ioctl_data = NULL;
-            ha->ioctl_order = 0;
-            ha->ioctl_datasize = 0;
-         }
+      if (!ha->conf) {
+         printk(KERN_WARNING "(%s%d) Unable to allocate host conf structure - skipping controller\n",
+                ips_name, ips_next_controller);
+         ips_abort_init(ha, sh, ips_next_controller);
+         ips_next_controller++;
+         ips_num_controllers--;
 
-         /* Store away needed values for later use */
-         sh->io_port = io_addr;
-         sh->n_io_port = io_addr ? 255 : 0;
-         sh->unique_id = (io_addr) ? io_addr : mem_addr;
-         sh->irq = irq;
-         sh->select_queue_depths = ips_select_queue_depth;
-         sh->sg_tablesize = sh->hostt->sg_tablesize;
-         sh->can_queue = sh->hostt->can_queue;
-         sh->cmd_per_lun = sh->hostt->cmd_per_lun;
-         sh->unchecked_isa_dma = sh->hostt->unchecked_isa_dma;
-         sh->use_clustering = sh->hostt->use_clustering;
+         continue;
+      }
 
-#if LINUX_VERSION_CODE >= LinuxVersionCode(2,4,7)
-         sh->max_sectors = 128;
-#endif                      
+      ha->nvram = kmalloc(sizeof(IPS_NVRAM_P5), GFP_KERNEL);
 
-#if LINUX_VERSION_CODE < LinuxVersionCode(2,4,0)
-         sh->wish_block = FALSE;
-#endif
+      if (!ha->nvram) {
+         printk(KERN_WARNING "(%s%d) Unable to allocate host nvram structure - skipping controller\n",
+                ips_name, ips_next_controller);
+         ips_abort_init(ha, sh, ips_next_controller);
+         ips_next_controller++;
+         ips_num_controllers--;
 
-         /* Store info in HA structure */
-         ha->irq = irq;
-         ha->io_addr = io_addr;
-         ha->io_len = io_len;
-         ha->mem_addr = mem_addr;
-         ha->mem_len = mem_len;
-         ha->mem_ptr = mem_ptr;
-         ha->ioremap_ptr = ioremap_ptr;
-         ha->host_num = ips_next_controller;
-         ha->revision_id = revision_id;
-         ha->slot_num = PCI_SLOT(dev[i]->devfn);
-         ha->device_id = deviceID[i];
-         ha->subdevice_id = subdevice_id;
-         ha->pcidev = dev[i];
+         continue;
+      }
 
-         /*
-          * Setup Functions
-          */
-         if (IPS_IS_MORPHEUS(ha)) {
-            /* morpheus */
-            ha->func.isintr = ips_isintr_morpheus;
-            ha->func.isinit = ips_isinit_morpheus;
-            ha->func.issue = ips_issue_i2o_memio;
-            ha->func.init = ips_init_morpheus;
-            ha->func.statupd = ips_statupd_morpheus;
-            ha->func.reset = ips_reset_morpheus;
-            ha->func.intr = ips_intr_morpheus;
-            ha->func.enableint = ips_enable_int_morpheus;
-         } else if (IPS_USE_MEMIO(ha)) {
-            /* copperhead w/MEMIO */
-            ha->func.isintr = ips_isintr_copperhead_memio;
-            ha->func.isinit = ips_isinit_copperhead_memio;
-            ha->func.init = ips_init_copperhead_memio;
-            ha->func.statupd = ips_statupd_copperhead_memio;
-            ha->func.statinit = ips_statinit_memio;
-            ha->func.reset = ips_reset_copperhead_memio;
-            ha->func.intr = ips_intr_copperhead;
-            ha->func.erasebios = ips_erase_bios_memio;
-            ha->func.programbios = ips_program_bios_memio;
-            ha->func.verifybios = ips_verify_bios_memio;
-            ha->func.enableint = ips_enable_int_copperhead_memio;
+      ha->subsys = kmalloc(sizeof(IPS_SUBSYS), GFP_KERNEL);
 
-            if (IPS_USE_I2O_DELIVER(ha))
-               ha->func.issue = ips_issue_i2o_memio;
-            else
-               ha->func.issue = ips_issue_copperhead_memio;
-         } else {
-            /* copperhead */
-            ha->func.isintr = ips_isintr_copperhead;
-            ha->func.isinit = ips_isinit_copperhead;
-            ha->func.init = ips_init_copperhead;
-            ha->func.statupd = ips_statupd_copperhead;
-            ha->func.statinit = ips_statinit;
-            ha->func.reset = ips_reset_copperhead;
-            ha->func.intr = ips_intr_copperhead;
-            ha->func.erasebios = ips_erase_bios;
-            ha->func.programbios = ips_program_bios;
-            ha->func.verifybios = ips_verify_bios;
-            ha->func.enableint = ips_enable_int_copperhead;
+      if (!ha->subsys) {
+         printk(KERN_WARNING "(%s%d) Unable to allocate host subsystem structure - skipping controller\n",
+                ips_name, ips_next_controller);
+         ips_abort_init(ha, sh, ips_next_controller);
+         ips_next_controller++;
+         ips_num_controllers--;
 
-            if (IPS_USE_I2O_DELIVER(ha))
-               ha->func.issue = ips_issue_i2o;
-            else
-               ha->func.issue = ips_issue_copperhead;
-         }
-            
-         /* If Morpheus appears dead, reset it */
-         if ( IPS_IS_MORPHEUS( ha ) ) {
-             IsDead = readl( ha->mem_ptr + IPS_REG_I960_MSG1 );
-             if ( IsDead == 0xDEADBEEF ) {
-                 ips_reset_morpheus( ha );
-             }
-         }
+         continue;
+      }
 
-         /*
-          * Initialize the card if it isn't already
-          */
+      for (count = PAGE_SIZE, ha->ioctl_order = 0;
+           count < ips_ioctlsize;
+           ha->ioctl_order++, count <<= 1);
+
+      ha->ioctl_data = (char *) __get_free_pages(GFP_KERNEL, ha->ioctl_order);
+      ha->ioctl_datasize = count;
+
+      if (!ha->ioctl_data) {
+         printk(KERN_WARNING "(%s%d) Unable to allocate ioctl data\n",
+                ips_name, ips_next_controller);
 
-         if (!(*ha->func.isinit)(ha)) {
-            if (!(*ha->func.init)(ha)) {
-               /*
-                * Initialization failed
-                */
-               printk(KERN_WARNING "(%s%d) unable to initialize controller - skipping controller\n",
-                      ips_name, ips_next_controller);
+         ha->ioctl_data = NULL;
+         ha->ioctl_order = 0;
+         ha->ioctl_datasize = 0;
+      }
 
-               ha->active = 0;
-               ips_free(ha);
-               scsi_unregister(sh);
-               ips_ha[ips_next_controller] = 0;
-               ips_sh[ips_next_controller] = 0;
-               ips_next_controller++;
-               ips_num_controllers--;
+      /* Store away needed values for later use */
+      sh->io_port = io_addr;
+      sh->n_io_port = io_addr ? 255 : 0;
+      sh->unique_id = (io_addr) ? io_addr : mem_addr;
+      sh->irq = irq;
+      sh->select_queue_depths = ips_select_queue_depth;
+      sh->sg_tablesize = sh->hostt->sg_tablesize;
+      sh->can_queue = sh->hostt->can_queue;
+      sh->cmd_per_lun = sh->hostt->cmd_per_lun;
+      sh->unchecked_isa_dma = sh->hostt->unchecked_isa_dma;
+      sh->use_clustering = sh->hostt->use_clustering;
+
+      sh->wish_block = FALSE;
+
+      /* Store info in HA structure */
+      ha->irq = irq;
+      ha->io_addr = io_addr;
+      ha->io_len = io_len;
+      ha->mem_addr = mem_addr;
+      ha->mem_len = mem_len;
+      ha->mem_ptr = mem_ptr;
+      ha->ioremap_ptr = ioremap_ptr;
+      ha->host_num = ips_next_controller;
+      ha->revision_id = revision_id;
+      ha->slot_num = PCI_SLOT(dev[i]->devfn);
+      ha->device_id = dev[i]->device;
+      ha->subdevice_id = subdevice_id;
+      ha->pcidev = dev[i];
 
-               continue;
-            }
-         }
+      /*
+       * Setup Functions
+       */
+      ips_setup_funclist(ha);
+            
+      /* If Morpheus appears dead, reset it */
+      if ( ( IPS_IS_MORPHEUS( ha ) ) || ( IPS_IS_MARCO( ha ) ) ) {
+          IsDead = readl( ha->mem_ptr + IPS_REG_I960_MSG1 );
+          if ( IsDead == 0xDEADBEEF ) {
+              ips_reset_morpheus( ha );
+          }
+      }
 
-         /* install the interrupt handler */
-         if (request_irq(irq, do_ipsintr, SA_SHIRQ, ips_name, ha)) {
-            printk(KERN_WARNING "(%s%d) unable to install interrupt handler - skipping controller\n",
-                   ips_name, ips_next_controller);
+      /*
+       * Initialize the card if it isn't already
+       */
 
-            ha->active = 0;
-            ips_free(ha);
-            scsi_unregister(sh);
-            ips_ha[ips_next_controller] = 0;
-            ips_sh[ips_next_controller] = 0;
+      if (!(*ha->func.isinit)(ha)) {
+         if (!(*ha->func.init)(ha)) {
+            /*
+             * Initialization failed
+             */
+            printk(KERN_WARNING "(%s%d) unable to initialize controller - skipping controller\n",
+                   ips_name, ips_next_controller);
+            ips_abort_init(ha, sh, ips_next_controller);
             ips_next_controller++;
             ips_num_controllers--;
 
             continue;
          }
+      }
 
-         /*
-          * Allocate a temporary SCB for initialization
-          */
-         ha->max_cmds = 1;
-         if (!ips_allocatescbs(ha)) {
-            /* couldn't allocate a temp SCB */
-            printk(KERN_WARNING "(%s%d) unable to allocate CCBs - skipping contoller\n",
-                   ips_name, ips_next_controller);
-
-            ha->active = 0;
-            ips_free(ha);
-            scsi_unregister(sh);
-            ips_ha[ips_next_controller] = 0;
-            ips_sh[ips_next_controller] = 0;
-            free_irq(ha->irq, ha);
-            ips_next_controller++;
-            ips_num_controllers--;
+      /* install the interrupt handler */
+      if (request_irq(irq, do_ipsintr, SA_SHIRQ, ips_name, ha)) {
+         printk(KERN_WARNING "(%s%d) unable to install interrupt handler - skipping controller\n",
+                ips_name, ips_next_controller);
+         ips_abort_init(ha, sh, ips_next_controller);
+         ips_next_controller++;
+         ips_num_controllers--;
 
-            continue;
-         }
+         continue;
+      }
 
+      /*
+       * Allocate a temporary SCB for initialization
+       */
+      ha->max_cmds = 1;
+      if (!ips_allocatescbs(ha)) {
+         /* couldn't allocate a temp SCB */
+         printk(KERN_WARNING "(%s%d) unable to allocate CCBs - skipping contoller\n",
+                ips_name, ips_next_controller);
+         free_irq(ha->irq, ha);
+         ips_abort_init(ha, sh, ips_next_controller);
          ips_next_controller++;
-      } while ((dev[i] = pci_find_device(IPS_VENDORID, deviceID[i], dev[i])));
+         ips_num_controllers--;
+
+         continue;
+      }
+
+      ips_next_controller++;
    }
 
    /*
@@ -1252,12 +1320,64 @@
       register_reboot_notifier(&ips_notifier);
 
    return (ips_num_controllers);
+}
+#endif
 
-#else
+/****************************************************************************/
+/*   configure the function pointers to use the functions that will work    */
+/*   with the found version of the adapter                                  */
+/****************************************************************************/
+static void ips_setup_funclist(ips_ha_t *ha){
 
-   /* No PCI -- No ServeRAID */
-   return (0);
-#endif /* CONFIG_PCI */
+   /*                                
+    * Setup Functions
+    */
+   if (IPS_IS_MORPHEUS(ha) || IPS_IS_MARCO(ha)) {
+      /* morpheus / marco / sebring */
+      ha->func.isintr = ips_isintr_morpheus;
+      ha->func.isinit = ips_isinit_morpheus;
+      ha->func.issue = ips_issue_i2o_memio;
+      ha->func.init = ips_init_morpheus;
+      ha->func.statupd = ips_statupd_morpheus;
+      ha->func.reset = ips_reset_morpheus;
+      ha->func.intr = ips_intr_morpheus;
+      ha->func.enableint = ips_enable_int_morpheus;
+   } else if (IPS_USE_MEMIO(ha)) {
+      /* copperhead w/MEMIO */
+      ha->func.isintr = ips_isintr_copperhead_memio;
+      ha->func.isinit = ips_isinit_copperhead_memio;
+      ha->func.init = ips_init_copperhead_memio;
+      ha->func.statupd = ips_statupd_copperhead_memio;
+      ha->func.statinit = ips_statinit_memio;
+      ha->func.reset = ips_reset_copperhead_memio;
+      ha->func.intr = ips_intr_copperhead;
+      ha->func.erasebios = ips_erase_bios_memio;
+      ha->func.programbios = ips_program_bios_memio;
+      ha->func.verifybios = ips_verify_bios_memio;
+      ha->func.enableint = ips_enable_int_copperhead_memio;
+      if (IPS_USE_I2O_DELIVER(ha))
+         ha->func.issue = ips_issue_i2o_memio;
+      else
+         ha->func.issue = ips_issue_copperhead_memio;
+   } else {
+      /* copperhead */
+      ha->func.isintr = ips_isintr_copperhead;
+      ha->func.isinit = ips_isinit_copperhead;
+      ha->func.init = ips_init_copperhead;
+      ha->func.statupd = ips_statupd_copperhead;
+      ha->func.statinit = ips_statinit;
+      ha->func.reset = ips_reset_copperhead;
+      ha->func.intr = ips_intr_copperhead;
+      ha->func.erasebios = ips_erase_bios;
+      ha->func.programbios = ips_program_bios;
+      ha->func.verifybios = ips_verify_bios;
+      ha->func.enableint = ips_enable_int_copperhead;
+
+      if (IPS_USE_I2O_DELIVER(ha))
+         ha->func.issue = ips_issue_i2o;
+      else
+         ha->func.issue = ips_issue_copperhead;
+   }
 }
 
 /****************************************************************************/
@@ -1309,13 +1429,15 @@
    scb->cmd.flush_cache.reserved3 = 0;
    scb->cmd.flush_cache.reserved4 = 0;
 
-   printk(KERN_NOTICE "(%s%d) Flushing Cache.\n", ips_name, ha->host_num);
+   if (InitState != 0)    /* If Not just Searching for the Adapter Order Table */
+      printk(KERN_NOTICE "(%s%d) Flushing Cache.\n", ips_name, ha->host_num);
 
    /* send command */
    if (ips_send_wait(ha, scb, ips_cmd_timeout, IPS_INTR_ON) == IPS_FAILURE)
       printk(KERN_NOTICE "(%s%d) Incomplete Flush.\n", ips_name, ha->host_num);
 
-   printk(KERN_NOTICE "(%s%d) Flushing Complete.\n", ips_name, ha->host_num);
+   if (InitState != 0)    /* If Not just Searching for the Adapter Order Table */
+      printk(KERN_NOTICE "(%s%d) Flushing Complete.\n", ips_name, ha->host_num);
 
    ips_sh[i] = NULL;
    ips_ha[i] = NULL;
@@ -1332,12 +1454,14 @@
 
    scsi_unregister(sh);
 
-   ips_released_controllers++;
-
-   if (ips_num_controllers == ips_released_controllers){
-      unregister_reboot_notifier(&ips_notifier);
-      pci_unregister_driver(&ips_pci_driver);
+   if (InitState != 0) {
+      ips_released_controllers++;
+      if (ips_num_controllers == ips_released_controllers){
+         unregister_reboot_notifier(&ips_notifier);
+         pci_unregister_driver(ha->pcidev->driver);
+      }
    }
+
    return (FALSE);
 }
 
@@ -1404,12 +1528,13 @@
 /* Routine Description:                                                     */
 /*                                                                          */
 /*   Abort a command (using the new error code stuff)                       */
-/*                                                                          */
+/* Note: this routine is called under the io_request_lock                   */
 /****************************************************************************/
 int
 ips_eh_abort(Scsi_Cmnd *SC) {
    ips_ha_t         *ha;
    ips_copp_wait_item_t *item;
+   int ret;
 
    METHOD_TRACE("ips_eh_abort", 1);
 
@@ -1431,36 +1556,25 @@
       return (FAILED);
    }
 
-   if (test_and_set_bit(IPS_IN_ABORT, &ha->flags))
-      return (FAILED);
-
    /* See if the command is on the copp queue */
-   IPS_QUEUE_LOCK(&ha->copp_waitlist);
    item = ha->copp_waitlist.head;
    while ((item) && (item->scsi_cmd != SC))
       item = item->next;
-   IPS_QUEUE_UNLOCK(&ha->copp_waitlist);
 
    if (item) {
       /* Found it */
       ips_removeq_copp(&ha->copp_waitlist, item);
-      clear_bit(IPS_IN_ABORT, &ha->flags);
-
-      return (SUCCESS);
-   }
+      ret = (SUCCESS);
 
    /* See if the command is on the wait queue */
-   if (ips_removeq_wait(&ha->scb_waitlist, SC)) {
+   } else if (ips_removeq_wait(&ha->scb_waitlist, SC)) {
       /* command not sent yet */
-      clear_bit(IPS_IN_ABORT, &ha->flags);
-
-      return (SUCCESS);
+      ret = (SUCCESS);
    } else {
       /* command must have already been sent */
-      clear_bit(IPS_IN_ABORT, &ha->flags);
-
-      return (FAILED);
+      ret = (FAILED);
    }
+   return ret;
 }
 
 /****************************************************************************/
@@ -1481,7 +1595,6 @@
    ips_ha_t             *ha;
    ips_scb_t            *scb;
    ips_copp_wait_item_t *item;
-   unsigned long         cpu_flags;
 
    METHOD_TRACE("ips_eh_reset", 1);
 
@@ -1506,36 +1619,27 @@
    if (!ha->active)
       return (FAILED);
 
-   if (test_and_set_bit(IPS_IN_RESET, &ha->flags))
-      return (FAILED);
-
    /* See if the command is on the copp queue */
-   IPS_QUEUE_LOCK(&ha->copp_waitlist);
    item = ha->copp_waitlist.head;
    while ((item) && (item->scsi_cmd != SC))
       item = item->next;
-   IPS_QUEUE_UNLOCK(&ha->copp_waitlist);
 
    if (item) {
       /* Found it */
       ips_removeq_copp(&ha->copp_waitlist, item);
-      clear_bit(IPS_IN_RESET, &ha->flags);
-
       return (SUCCESS);
    }
 
    /* See if the command is on the wait queue */
    if (ips_removeq_wait(&ha->scb_waitlist, SC)) {
       /* command not sent yet */
-      clear_bit(IPS_IN_RESET, &ha->flags);
-
       return (SUCCESS);
    }
 
    /* An explanation for the casual observer:                              */
    /* Part of the function of a RAID controller is automatic error         */
    /* detection and recovery.  As such, the only problem that physically   */
-   /* resetting a ServeRAID adapter will ever fix is when, for some reason,*/
+   /* resetting an adapter will ever fix is when, for some reason,         */
    /* the driver is not successfully communicating with the adapter.       */
    /* Therefore, we will attempt to flush this adapter.  If that succeeds, */
    /* then there's no real purpose in a physical reset. This will complete */
@@ -1562,13 +1666,12 @@
       ret = ips_send_wait(ha, scb, ips_cmd_timeout, IPS_INTR_IORL);
       if (ret == IPS_SUCCESS) {
          printk(KERN_NOTICE "(%s%d) Reset Request - Flushed Cache\n", ips_name, ha->host_num);
-         clear_bit(IPS_IN_RESET, &ha->flags);
          return (SUCCESS);
          }
    }
 
    /* Either we can't communicate with the adapter or it's an IOCTL request */
-   /* from a ServeRAID utility.  A physical reset is needed at this point.  */
+   /* from a utility.  A physical reset is needed at this point.            */
 
    ha->ioctl_reset = 0;             /* Reset the IOCTL Requested Reset Flag */
 
@@ -1607,8 +1710,6 @@
       }
 
       ha->active = FALSE;
-      clear_bit(IPS_IN_RESET, &ha->flags);
-
       return (FAILED);
    }
 
@@ -1639,8 +1740,6 @@
       }
 
       ha->active = FALSE;
-      clear_bit(IPS_IN_RESET, &ha->flags);
-
       return (FAILED);
    }
 
@@ -1649,10 +1748,8 @@
       struct timeval tv;
 
       do_gettimeofday(&tv);
-      IPS_HA_LOCK(cpu_flags);
       ha->last_ffdc = tv.tv_sec;
       ha->reset_count++;
-      IPS_HA_UNLOCK(cpu_flags);
       ips_ffdc_reset(ha, IPS_INTR_IORL);
    }
 
@@ -1671,25 +1768,11 @@
       ha->dcdb_active[i-1] = 0;
 
    /* Reset the number of active IOCTLs */
-   IPS_HA_LOCK(cpu_flags);
    ha->num_ioctl = 0;
-   IPS_HA_UNLOCK(cpu_flags);
-
-   clear_bit(IPS_IN_RESET, &ha->flags);
 
-   if (!test_bit(IPS_IN_INTR, &ha->flags)) {
-      /*
-       * Only execute the next command when
-       * we are not being called from the
-       * interrupt handler.  The interrupt
-       * handler wants to do this and since
-       * interrupts are turned off here....
-       */
-      ips_next(ha, IPS_INTR_IORL);
-   }
+   ips_next(ha, IPS_INTR_IORL);
 
    return (SUCCESS);
-
 #endif /* NO_IPS_RESET */
 
 }
@@ -1709,7 +1792,6 @@
 int
 ips_queue(Scsi_Cmnd *SC, void (*done) (Scsi_Cmnd *)) {
    ips_ha_t         *ha;
-   unsigned long     cpu_flags;
    ips_passthru_t   *pt;                                             
 
    METHOD_TRACE("ips_queue", 1);
@@ -1723,28 +1805,17 @@
       return (DID_ERROR);
 
    if (ips_is_passthru(SC)) {
-      IPS_QUEUE_LOCK(&ha->copp_waitlist);
       if (ha->copp_waitlist.count == IPS_MAX_IOCTL_QUEUE) {
-         IPS_QUEUE_UNLOCK(&ha->copp_waitlist);
          SC->result = DID_BUS_BUSY << 16;
          done(SC);
 
          return (0);
-      } else {
-         IPS_QUEUE_UNLOCK(&ha->copp_waitlist);
-      }
-   } else {
-      IPS_QUEUE_LOCK(&ha->scb_waitlist);
-      if (ha->scb_waitlist.count == IPS_MAX_QUEUE) {
-         IPS_QUEUE_UNLOCK(&ha->scb_waitlist);
-         SC->result = DID_BUS_BUSY << 16;
-         done(SC);
-
-         return (0);
-      } else {
-         IPS_QUEUE_UNLOCK(&ha->scb_waitlist);
       }
+   } else if (ha->scb_waitlist.count == IPS_MAX_QUEUE) {
+      SC->result = DID_BUS_BUSY << 16;
+      done(SC);
 
+      return (0);
    }
 
    SC->scsi_done = done;
@@ -1769,7 +1840,7 @@
 
       ips_copp_wait_item_t *scratch;
       
-      /* A Reset IOCTL is only sent by the ServeRAID boot CD in extreme cases. */
+      /* A Reset IOCTL is only sent by the boot CD in extreme cases.           */
       /* There can never be any system activity ( network or disk ), but check */
       /* anyway just as a good practice.                                       */
       pt = (ips_passthru_t *) SC->request_buffer;                                
@@ -1798,27 +1869,15 @@
       }
 
       scratch->scsi_cmd = SC;
-      sema_init(&ha->ioctl_sem, 0);
-      scratch->sem = &ha->ioctl_sem;
       scratch->next = NULL;
 
       ips_putq_copp_tail(&ha->copp_waitlist, scratch);
    }
-   else
+   else {
       ips_putq_wait_tail(&ha->scb_waitlist, SC);
-
-   if(ha->scb_waitlist.count + ha->scb_activelist.count > 32)
-         mod_timer(&SC->eh_timeout, jiffies + 120 * HZ);
-
-   IPS_HA_LOCK(cpu_flags);
-   if ((!test_bit(IPS_IN_INTR, &ha->flags)) &&
-       (!test_bit(IPS_IN_ABORT, &ha->flags)) &&
-       (!test_bit(IPS_IN_RESET, &ha->flags))) {
-      IPS_HA_UNLOCK(cpu_flags);
-      ips_next(ha, IPS_INTR_IORL);
-   } else {
-      IPS_HA_UNLOCK(cpu_flags);
    }
+
+   ips_next(ha, IPS_INTR_IORL);
    
    /* If We were using the CD Boot Flash Buffer, Restore the Old Values */
    if ( ips_FlashData == ha->ioctl_data ) {                               
@@ -1938,33 +1997,24 @@
 do_ipsintr(int irq, void *dev_id, struct pt_regs *regs) {
    ips_ha_t         *ha;
    unsigned long     cpu_flags;
+   struct Scsi_Host *host;
 
    METHOD_TRACE("do_ipsintr", 2);
 
    ha = (ips_ha_t *) dev_id;
    if (!ha) 
       return;
-   
-   spin_lock_irqsave(&io_request_lock, cpu_flags);
-
-   if (test_and_set_bit(IPS_IN_INTR, &ha->flags)) {
-      spin_unlock_irqrestore(&io_request_lock, cpu_flags);
-
-      return ;
-   }
+   host = ips_sh[ha->host_num];
+   IPS_LOCK_SAVE(host->host_lock, cpu_flags);
 
    if (!ha->active) {
-      clear_bit(IPS_IN_INTR, &ha->flags);
-      spin_unlock_irqrestore(&io_request_lock, cpu_flags);
-
+      IPS_UNLOCK_RESTORE(host->host_lock, cpu_flags);
       return;
    }
 
    (*ha->func.intr)(ha);
 
-   clear_bit(IPS_IN_INTR, &ha->flags);
-
-   spin_unlock_irqrestore(&io_request_lock, cpu_flags);
+   IPS_UNLOCK_RESTORE(host->host_lock, cpu_flags);
 
    /* start the next command */
    ips_next(ha, IPS_INTR_ON);
@@ -1987,7 +2037,6 @@
    ips_scb_t        *scb;
    IPS_STATUS        cstatus;
    int               intrstatus;
-   unsigned long     cpu_flags;
 
    METHOD_TRACE("ips_intr", 2);
 
@@ -1997,15 +2046,12 @@
    if (!ha->active)
       return;
 
-   IPS_HA_LOCK(cpu_flags);
-
    intrstatus = (*ha->func.isintr)(ha);
 
    if (!intrstatus) {
       /*
        * Unexpected/Shared interrupt
        */
-      IPS_HA_UNLOCK(cpu_flags);
 
       return;
    }
@@ -2032,12 +2078,8 @@
        * use the callback function to finish things up
        * NOTE: interrupts are OFF for this
        */
-      IPS_HA_UNLOCK(cpu_flags);
       (*scb->callback) (ha, scb);
-      IPS_HA_LOCK(cpu_flags);
    } /* end while */
-
-   IPS_HA_UNLOCK(cpu_flags);
 }
 
 /****************************************************************************/
@@ -2057,7 +2099,6 @@
    ips_scb_t        *scb;
    IPS_STATUS        cstatus;
    int               intrstatus;
-   unsigned long     cpu_flags;
 
    METHOD_TRACE("ips_intr_morpheus", 2);
 
@@ -2067,15 +2108,12 @@
    if (!ha->active)
       return;
 
-   IPS_HA_LOCK(cpu_flags);
-
    intrstatus = (*ha->func.isintr)(ha);
 
    if (!intrstatus) {
       /*
        * Unexpected/Shared interrupt
        */
-      IPS_HA_UNLOCK(cpu_flags);
 
       return;
    }
@@ -2108,12 +2146,8 @@
        * use the callback function to finish things up
        * NOTE: interrupts are OFF for this
        */
-      IPS_HA_UNLOCK(cpu_flags);
       (*scb->callback) (ha, scb);
-      IPS_HA_LOCK(cpu_flags);
    } /* end while */
-
-   IPS_HA_UNLOCK(cpu_flags);
 }
 
 /****************************************************************************/
@@ -2231,7 +2265,7 @@
          return 1;
       else if(SC->use_sg){
          struct scatterlist *sg = SC->request_buffer;
-         char *buffer = sg[0].address;
+         char *buffer = IPS_SG_ADDRESS(sg);
          if(buffer && buffer[0] == 'C' && buffer[1] == 'O' && 
             buffer[2] == 'P' && buffer[3] == 'P')
             return 1;
@@ -2288,7 +2322,7 @@
             ha->ioctl_order = order;
             ha->ioctl_datasize = count;
          } else {
-             pt = (ips_passthru_t*)sg[0].address;
+             pt = (ips_passthru_t*)IPS_SG_ADDRESS(sg);
              pt->BasicStatus = 0x0B;
              pt->ExtendedStatus = 0x00;
              SC->result = DID_ERROR << 16;
@@ -2298,7 +2332,7 @@
       ha->ioctl_datasize = length;
       length = 0;
       for(i = 0; i < SC->use_sg; i++){
-         memcpy(&ha->ioctl_data[length], sg[i].address, sg[i].length);
+         memcpy(&ha->ioctl_data[length], IPS_SG_ADDRESS(&sg[i]), sg[i].length);
          length += sg[i].length;
       }
       pt = (ips_passthru_t *)ha->ioctl_data;
@@ -2327,13 +2361,6 @@
     * packet we received from the sg driver. In this
     * case the CmdBSize field of the pt structure is
     * used for the size of the buffer.
-    *
-    * IF the scsi op_code == 0x81 then we assume that
-    * we will need our own buffer and we will copy the
-    * data to/from the user buffer passed in the scsi
-    * command.  The data address resides at offset 4
-    * in the scsi command.  The length of the data resides
-    * at offset 8 in the scsi command.
     */
 
    switch (pt->CoppCmd) {
@@ -2499,6 +2526,7 @@
 static int
 ips_flash_firmware(ips_ha_t * ha, ips_passthru_t *pt, ips_scb_t *scb){
    IPS_SG_LIST *sg_list;
+   uint32_t cmd_busaddr;
 
    if(pt->CoppCP.cmd.flashfw.type == IPS_FW_IMAGE &&
       pt->CoppCP.cmd.flashfw.direction == IPS_WRITE_FW ){
@@ -2513,11 +2541,12 @@
    }
    /* Save the S/G list pointer so it doesn't get clobbered */
    sg_list = scb->sg_list;
+   cmd_busaddr = scb->scb_busaddr;
    /* copy in the CP */
    memcpy(&scb->cmd, &pt->CoppCP.cmd, sizeof(IPS_IOCTL_CMD));
    /* FIX stuff that might be wrong */
    scb->sg_list = sg_list;
-   scb->scb_busaddr = VIRT_TO_BUS(scb);
+   scb->scb_busaddr = cmd_busaddr;
    scb->bus = scb->scsi_cmd->channel;
    scb->target_id = scb->scsi_cmd->target;
    scb->lun = scb->scsi_cmd->lun;
@@ -2527,8 +2556,13 @@
    scb->op_code = 0;
    scb->callback = ipsintr_done;
    scb->timeout = ips_cmd_timeout;
+
+   scb->data_len = ha->flash_datasize;
+   scb->data_busaddr = pci_map_single(ha->pcidev, ha->flash_data, scb->data_len,
+                                      IPS_DMA_DIR(scb));
+   scb->flags |= IPS_SCB_MAP_SINGLE;
    scb->cmd.flashfw.command_id = IPS_COMMAND_ID(ha, scb);
-   scb->cmd.flashfw.buffer_addr = VIRT_TO_BUS(ha->flash_data);
+   scb->cmd.flashfw.buffer_addr = scb->data_busaddr;
    if (pt->TimeOut)
       scb->timeout = pt->TimeOut;
    scb->scsi_cmd->result = DID_OK <<16;
@@ -2561,6 +2595,7 @@
 static int
 ips_usrcmd(ips_ha_t *ha, ips_passthru_t *pt, ips_scb_t *scb) {
    IPS_SG_LIST *sg_list;
+   uint32_t cmd_busaddr;
 
    METHOD_TRACE("ips_usrcmd", 1);
 
@@ -2569,14 +2604,14 @@
 
    /* Save the S/G list pointer so it doesn't get clobbered */
    sg_list = scb->sg_list;
-
+   cmd_busaddr = scb->scb_busaddr;
    /* copy in the CP */
    memcpy(&scb->cmd, &pt->CoppCP.cmd, sizeof(IPS_IOCTL_CMD));
    memcpy(&scb->dcdb, &pt->CoppCP.dcdb, sizeof(IPS_DCDB_TABLE));
 
    /* FIX stuff that might be wrong */
    scb->sg_list = sg_list;
-   scb->scb_busaddr = VIRT_TO_BUS(scb);
+   scb->scb_busaddr = cmd_busaddr;
    scb->bus = scb->scsi_cmd->channel;
    scb->target_id = scb->scsi_cmd->target;
    scb->lun = scb->scsi_cmd->lun;
@@ -2595,16 +2630,31 @@
       return (0);
 
    if (pt->CmdBSize) {
-      if(!scb->scsi_cmd->use_sg)
-         scb->data_busaddr = VIRT_TO_BUS(scb->scsi_cmd->request_buffer + sizeof(ips_passthru_t));
-      else
-         scb->data_busaddr = VIRT_TO_BUS(ha->ioctl_data + sizeof(ips_passthru_t));
+      if(!scb->scsi_cmd->use_sg){
+         scb->data_len = pt->CmdBSize;
+         scb->data_busaddr = pci_map_single(ha->pcidev,
+                                            scb->scsi_cmd->request_buffer +
+                                            sizeof(ips_passthru_t),
+                                            pt->CmdBSize,
+                                            IPS_DMA_DIR(scb));
+         scb->flags |= IPS_SCB_MAP_SINGLE;
+      } else {
+         scb->data_len = pt->CmdBSize;
+         scb->data_busaddr = pci_map_single(ha->pcidev,
+                                            ha->ioctl_data +
+                                            sizeof(ips_passthru_t),
+                                            pt->CmdBSize,
+                                            IPS_DMA_DIR(scb));
+         scb->flags |= IPS_SCB_MAP_SINGLE;
+      }
    } else {
       scb->data_busaddr = 0L;
    }
 
    if (scb->cmd.dcdb.op_code == IPS_CMD_DCDB)
-      scb->cmd.dcdb.dcdb_address = cpu_to_le32(VIRT_TO_BUS(&scb->dcdb));
+      scb->cmd.dcdb.dcdb_address = cpu_to_le32(scb->scb_busaddr +
+                                          (unsigned long)&scb->dcdb -
+                                          (unsigned long)scb);
 
    if (pt->CmdBSize) {
       if (scb->cmd.dcdb.op_code == IPS_CMD_DCDB)
@@ -2675,7 +2725,7 @@
       int i, length = 0;
       struct scatterlist *sg = scb->scsi_cmd->request_buffer;
       for(i = 0; i < scb->scsi_cmd->use_sg; i++){
-         memcpy(sg[i].address, &ha->ioctl_data[length], sg[i].length);
+         memcpy(IPS_SG_ADDRESS(&sg[i]), &ha->ioctl_data[length], sg[i].length);
          length += sg[i].length;
       }
    }
@@ -2885,6 +2935,17 @@
       }
 
       break;
+
+   case IPS_DEVICEID_MARCO:
+      switch (ha->subdevice_id) {
+      case IPS_SUBDEVICEID_6M:
+          ha->ad_type = IPS_ADTYPE_SERVERAID6M;
+          break;
+      case IPS_SUBDEVICEID_6I:
+          ha->ad_type = IPS_ADTYPE_SERVERAID6I;
+          break;
+      }
+      break;
    }
 }
 
@@ -3013,9 +3074,13 @@
       scb->cmd.flashfw.type = 1;
       scb->cmd.flashfw.direction = 0;
       scb->cmd.flashfw.count = cpu_to_le32(0x800);
-      scb->cmd.flashfw.buffer_addr = cpu_to_le32(VIRT_TO_BUS(buffer));
       scb->cmd.flashfw.total_packets = 1;
       scb->cmd.flashfw.packet_num = 0;
+      scb->data_len = 0x1000;
+      scb->data_busaddr = pci_map_single(ha->pcidev, buffer, scb->data_len,
+                                         IPS_DMA_DIR(scb));
+      scb->cmd.flashfw.buffer_addr = scb->data_busaddr;
+      scb->flags |= IPS_SCB_MAP_SINGLE;
 
       /* issue the command */
       if (((ret = ips_send_wait(ha, scb, ips_cmd_timeout, intr)) == IPS_FAILURE) ||
@@ -3113,6 +3178,10 @@
       return (0);
    }
 
+   /* If there are Logical Drives and a Reset Occurred, then an EraseStripeLock is Needed */
+   if ( (ha->conf->ucLogDriveCount > 0) && (ha->requires_esl == 1) ) 
+      ips_clear_adapter(ha, IPS_INTR_IORL);
+   
    /* set limits on SID, LUN, BUS */
    ha->ntargets = IPS_MAX_TARGETS + 1;
    ha->nlun = 1;
@@ -3198,39 +3267,31 @@
    Scsi_Cmnd            *q;
    ips_copp_wait_item_t *item;
    int                   ret;
-   unsigned long         cpu_flags;
-   unsigned long         cpu_flags2 = 0;
-
+   unsigned long         cpu_flags = 0;
+   struct Scsi_Host *host;
    METHOD_TRACE("ips_next", 1);
 
    if (!ha)
       return ;
-
+   host = ips_sh[ha->host_num];
    /*
     * Block access to the queue function so
     * this command won't time out
     */
-   if (intr == IPS_INTR_ON) 
-       spin_lock_irqsave(&io_request_lock, cpu_flags2);
+   if(intr == IPS_INTR_ON)
+      IPS_LOCK_SAVE(host->host_lock, cpu_flags);
    
    if ((ha->subsys->param[3] & 0x300000)  && ( ha->scb_activelist.count == 0 )) {
       struct timeval tv;
 
       do_gettimeofday(&tv);
 
-      IPS_HA_LOCK(cpu_flags);
       if (tv.tv_sec - ha->last_ffdc > IPS_SECS_8HOURS) {
          ha->last_ffdc = tv.tv_sec;
-         IPS_HA_UNLOCK(cpu_flags);
          ips_ffdc_time(ha);
-      } else {
-         IPS_HA_UNLOCK(cpu_flags);
       }
    }
 
-   if (intr == IPS_INTR_ON)
-       spin_unlock_irqrestore(&io_request_lock, cpu_flags2);
-
    /*
     * Send passthru commands
     * These have priority over normal I/O
@@ -3238,22 +3299,21 @@
     * since we limit the number that can be active
     * on the card at any one time
     */
-   IPS_HA_LOCK(cpu_flags);
-   IPS_QUEUE_LOCK(&ha->copp_waitlist);
    while ((ha->num_ioctl < IPS_MAX_IOCTL) &&
           (ha->copp_waitlist.head) &&
           (scb = ips_getscb(ha))) {
 
-      IPS_QUEUE_UNLOCK(&ha->copp_waitlist);
       item = ips_removeq_copp_head(&ha->copp_waitlist);
       ha->num_ioctl++;
-      IPS_HA_UNLOCK(cpu_flags);
+      if(intr == IPS_INTR_ON)
+         IPS_UNLOCK_RESTORE(host->host_lock, cpu_flags);
       scb->scsi_cmd = item->scsi_cmd;
-      scb->sem = item->sem;
       kfree(item);
 
       ret = ips_make_passthru(ha, scb->scsi_cmd, scb, intr);
 
+      if(intr == IPS_INTR_ON)
+         IPS_LOCK_SAVE(host->host_lock, cpu_flags);
       switch (ret) {
       case IPS_FAILURE:
          if (scb->scsi_cmd) {
@@ -3276,8 +3336,6 @@
       } /* end case */
 
       if (ret != IPS_SUCCESS) {
-         IPS_HA_LOCK(cpu_flags);
-         IPS_QUEUE_LOCK(&ha->copp_waitlist);
          ha->num_ioctl--;
          continue;
      }
@@ -3304,20 +3362,14 @@
          break;
       } /* end case */
 
-      IPS_HA_LOCK(cpu_flags);
-      IPS_QUEUE_LOCK(&ha->copp_waitlist);
    }
 
-   IPS_QUEUE_UNLOCK(&ha->copp_waitlist);
-   IPS_HA_UNLOCK(cpu_flags);
 
    /*
     * Send "Normal" I/O commands
     */
-   IPS_HA_LOCK(cpu_flags);
-   IPS_QUEUE_LOCK(&ha->scb_waitlist);
+    
    p = ha->scb_waitlist.head;
-   IPS_QUEUE_UNLOCK(&ha->scb_waitlist);
    while ((p) && (scb = ips_getscb(ha))) {
       if ((p->channel > 0) && (ha->dcdb_active[p->channel-1] & (1 << p->target))) {
          ips_freescb(ha, scb);
@@ -3327,10 +3379,9 @@
 
       q = p;
       SC = ips_removeq_wait(&ha->scb_waitlist, q);
-      if (SC == NULL)                    /* Should never happen, but good to check anyway */
-         continue;
 
-      IPS_HA_UNLOCK(cpu_flags);          /* Unlock HA after command is taken off queue */
+      if(intr == IPS_INTR_ON)
+         IPS_UNLOCK_RESTORE(host->host_lock, cpu_flags); /* Unlock HA after command is taken off queue */
 
       SC->result = DID_OK;
       SC->host_scribble = NULL;
@@ -3356,7 +3407,8 @@
          int                 i;
 
          sg = SC->request_buffer;
-         scb->sg_count = pci_map_sg(ha->pcidev, sg, SC->use_sg, PCI_DMA_BIDIRECTIONAL);
+         scb->sg_count = pci_map_sg(ha->pcidev, sg, SC->use_sg,
+                                    scsi_to_pci_dma_dir(SC->sc_data_direction));
          scb->flags |= IPS_SCB_MAP_SG;
          if (scb->sg_count == 1) {
             if (sg_dma_len(sg) > ha->max_xfer) {
@@ -3416,7 +3468,8 @@
 
             scb->dcdb.transfer_length = scb->data_len;
             scb->data_busaddr = pci_map_single(ha->pcidev, SC->request_buffer,
-                                               scb->data_len, PCI_DMA_BIDIRECTIONAL);
+                                               scb->data_len,
+                                               scsi_to_pci_dma_dir(SC->sc_data_direction));
             scb->flags |= IPS_SCB_MAP_SINGLE;
             scb->sg_len = 0;
          } else {
@@ -3437,13 +3490,15 @@
          scb->dcdb.cmd_attribute |= IPS_TRANSFER64K;
          scb->dcdb.transfer_length = 0;
       }
-
+      if(intr == IPS_INTR_ON)
+         IPS_LOCK_SAVE(host->host_lock, cpu_flags);
+      
       ret = ips_send_cmd(ha, scb);
 
-      if (ret == IPS_SUCCESS)
-         ips_putq_scb_head(&ha->scb_activelist, scb);
-
       switch(ret) {
+      case IPS_SUCCESS:
+         ips_putq_scb_head(&ha->scb_activelist, scb);
+         break;
       case IPS_FAILURE:
          if (scb->scsi_cmd) {
             scb->scsi_cmd->result = DID_ERROR << 16;
@@ -3470,10 +3525,10 @@
 
       p = (Scsi_Cmnd *) p->host_scribble;
 
-      IPS_HA_LOCK(cpu_flags);
    } /* end while */
 
-   IPS_HA_UNLOCK(cpu_flags);
+   if(intr == IPS_INTR_ON)
+      IPS_UNLOCK_RESTORE(host->host_lock, cpu_flags);
 }
 
 /****************************************************************************/
@@ -3484,7 +3539,7 @@
 /*                                                                          */
 /*   Add an item to the head of the queue                                   */
 /*                                                                          */
-/* ASSUMED to be called from within a lock                                  */
+/* ASSUMED to be called from within the HA lock                             */
 /*                                                                          */
 /****************************************************************************/
 static inline void
@@ -3494,8 +3549,6 @@
    if (!item)
       return ;
 
-   IPS_QUEUE_LOCK(queue);
-
    item->q_next = queue->head;
    queue->head = item;
 
@@ -3503,8 +3556,6 @@
       queue->tail = item;
 
    queue->count++;
-
-   IPS_QUEUE_UNLOCK(queue);
 }
 
 /****************************************************************************/
@@ -3515,7 +3566,7 @@
 /*                                                                          */
 /*   Add an item to the tail of the queue                                   */
 /*                                                                          */
-/* ASSUMED to be called from within a lock                                  */
+/* ASSUMED to be called from within the HA lock                             */
 /*                                                                          */
 /****************************************************************************/
 static inline void
@@ -3525,8 +3576,6 @@
    if (!item)
       return ;
 
-   IPS_QUEUE_LOCK(queue);
-
    item->q_next = NULL;
 
    if (queue->tail)
@@ -3538,8 +3587,6 @@
       queue->head = item;
 
    queue->count++;
-
-   IPS_QUEUE_UNLOCK(queue);
 }
 
 /****************************************************************************/
@@ -3550,7 +3597,7 @@
 /*                                                                          */
 /*   Remove the head of the queue                                           */
 /*                                                                          */
-/* ASSUMED to be called from within a lock                                  */
+/* ASSUMED to be called from within the HA lock                             */
 /*                                                                          */
 /****************************************************************************/
 static inline ips_scb_t *
@@ -3559,13 +3606,9 @@
 
    METHOD_TRACE("ips_removeq_scb_head", 1);
 
-   IPS_QUEUE_LOCK(queue);
-
    item = queue->head;
 
    if (!item) {
-      IPS_QUEUE_UNLOCK(queue);
-
       return (NULL);
    }
 
@@ -3577,8 +3620,6 @@
 
    queue->count--;
 
-   IPS_QUEUE_UNLOCK(queue);
-
    return (item);
 }
 
@@ -3590,7 +3631,7 @@
 /*                                                                          */
 /*   Remove an item from a queue                                            */
 /*                                                                          */
-/* ASSUMED to be called from within a lock                                  */
+/* ASSUMED to be called from within the HA lock                             */
 /*                                                                          */
 /****************************************************************************/
 static inline ips_scb_t *
@@ -3602,11 +3643,7 @@
    if (!item)
       return (NULL);
 
-   IPS_QUEUE_LOCK(queue);
-
    if (item == queue->head) {
-      IPS_QUEUE_UNLOCK(queue);
-
       return (ips_removeq_scb_head(queue));
    }
 
@@ -3625,13 +3662,9 @@
       item->q_next = NULL;
       queue->count--;
 
-      IPS_QUEUE_UNLOCK(queue);
-
       return (item);
    }
 
-   IPS_QUEUE_UNLOCK(queue);
-
    return (NULL);
 }
 
@@ -3643,7 +3676,7 @@
 /*                                                                          */
 /*   Add an item to the head of the queue                                   */
 /*                                                                          */
-/* ASSUMED to be called from within a lock                                  */
+/* ASSUMED to be called from within the HA lock                             */
 /*                                                                          */
 /****************************************************************************/
 static inline void
@@ -3653,8 +3686,6 @@
    if (!item)
       return ;
 
-   IPS_QUEUE_LOCK(queue);
-
    item->host_scribble = (char *) queue->head;
    queue->head = item;
 
@@ -3662,8 +3693,6 @@
       queue->tail = item;
 
    queue->count++;
-
-   IPS_QUEUE_UNLOCK(queue);
 }
 
 /****************************************************************************/
@@ -3674,7 +3703,7 @@
 /*                                                                          */
 /*   Add an item to the tail of the queue                                   */
 /*                                                                          */
-/* ASSUMED to be called from within a lock                                  */
+/* ASSUMED to be called from within the HA lock                             */
 /*                                                                          */
 /****************************************************************************/
 static inline void
@@ -3684,8 +3713,6 @@
    if (!item)
       return ;
 
-   IPS_QUEUE_LOCK(queue);
-
    item->host_scribble = NULL;
 
    if (queue->tail)
@@ -3697,8 +3724,6 @@
       queue->head = item;
 
    queue->count++;
-
-   IPS_QUEUE_UNLOCK(queue);
 }
 
 /****************************************************************************/
@@ -3709,7 +3734,7 @@
 /*                                                                          */
 /*   Remove the head of the queue                                           */
 /*                                                                          */
-/* ASSUMED to be called from within a lock                                  */
+/* ASSUMED to be called from within the HA lock                             */
 /*                                                                          */
 /****************************************************************************/
 static inline Scsi_Cmnd *
@@ -3718,13 +3743,9 @@
 
    METHOD_TRACE("ips_removeq_wait_head", 1);
 
-   IPS_QUEUE_LOCK(queue);
-
    item = queue->head;
 
    if (!item) {
-      IPS_QUEUE_UNLOCK(queue);
-
       return (NULL);
    }
 
@@ -3736,8 +3757,6 @@
 
    queue->count--;
 
-   IPS_QUEUE_UNLOCK(queue);
-
    return (item);
 }
 
@@ -3749,7 +3768,7 @@
 /*                                                                          */
 /*   Remove an item from a queue                                            */
 /*                                                                          */
-/* ASSUMED to be called from within a lock                                  */
+/* ASSUMED to be called from within the HA lock                             */
 /*                                                                          */
 /****************************************************************************/
 static inline Scsi_Cmnd *
@@ -3761,11 +3780,7 @@
    if (!item)
       return (NULL);
 
-   IPS_QUEUE_LOCK(queue);
-
    if (item == queue->head) {
-      IPS_QUEUE_UNLOCK(queue);
-
       return (ips_removeq_wait_head(queue));
    }
 
@@ -3784,13 +3799,9 @@
       item->host_scribble = NULL;
       queue->count--;
 
-      IPS_QUEUE_UNLOCK(queue);
-
       return (item);
    }
 
-   IPS_QUEUE_UNLOCK(queue);
-
    return (NULL);
 }
 
@@ -3802,7 +3813,7 @@
 /*                                                                          */
 /*   Add an item to the head of the queue                                   */
 /*                                                                          */
-/* ASSUMED to be called from within a lock                                  */
+/* ASSUMED to be called from within the HA lock                             */
 /*                                                                          */
 /****************************************************************************/
 static inline void
@@ -3812,8 +3823,6 @@
    if (!item)
       return ;
 
-   IPS_QUEUE_LOCK(queue);
-
    item->next = queue->head;
    queue->head = item;
 
@@ -3821,8 +3830,6 @@
       queue->tail = item;
 
    queue->count++;
-
-   IPS_QUEUE_UNLOCK(queue);
 }
 
 /****************************************************************************/
@@ -3833,7 +3840,7 @@
 /*                                                                          */
 /*   Add an item to the tail of the queue                                   */
 /*                                                                          */
-/* ASSUMED to be called from within a lock                                  */
+/* ASSUMED to be called from within the HA lock                             */
 /*                                                                          */
 /****************************************************************************/
 static inline void
@@ -3843,8 +3850,6 @@
    if (!item)
       return ;
 
-   IPS_QUEUE_LOCK(queue);
-
    item->next = NULL;
 
    if (queue->tail)
@@ -3856,8 +3861,6 @@
       queue->head = item;
 
    queue->count++;
-
-   IPS_QUEUE_UNLOCK(queue);
 }
 
 /****************************************************************************/
@@ -3868,7 +3871,7 @@
 /*                                                                          */
 /*   Remove the head of the queue                                           */
 /*                                                                          */
-/* ASSUMED to be called from within a lock                                  */
+/* ASSUMED to be called from within the HA lock                             */
 /*                                                                          */
 /****************************************************************************/
 static inline ips_copp_wait_item_t *
@@ -3877,13 +3880,9 @@
 
    METHOD_TRACE("ips_removeq_copp_head", 1);
 
-   IPS_QUEUE_LOCK(queue);
-
    item = queue->head;
 
    if (!item) {
-      IPS_QUEUE_UNLOCK(queue);
-
       return (NULL);
    }
 
@@ -3895,8 +3894,6 @@
 
    queue->count--;
 
-   IPS_QUEUE_UNLOCK(queue);
-
    return (item);
 }
 
@@ -3908,7 +3905,7 @@
 /*                                                                          */
 /*   Remove an item from a queue                                            */
 /*                                                                          */
-/* ASSUMED to be called from within a lock                                  */
+/* ASSUMED to be called from within the HA lock                             */
 /*                                                                          */
 /****************************************************************************/
 static inline ips_copp_wait_item_t *
@@ -3920,11 +3917,7 @@
    if (!item)
       return (NULL);
 
-   IPS_QUEUE_LOCK(queue);
-
    if (item == queue->head) {
-      IPS_QUEUE_UNLOCK(queue);
-
       return (ips_removeq_copp_head(queue));
    }
 
@@ -3943,13 +3936,9 @@
       item->next = NULL;
       queue->count--;
 
-      IPS_QUEUE_UNLOCK(queue);
-
       return (item);
    }
 
-   IPS_QUEUE_UNLOCK(queue);
-
    return (NULL);
 }
 
@@ -3966,6 +3955,7 @@
 ipsintr_blocking(ips_ha_t *ha, ips_scb_t *scb) {
    METHOD_TRACE("ipsintr_blocking", 2);
 
+   ips_freescb(ha, scb);
    if ((ha->waitflag == TRUE) &&
        (ha->cmd_in_progress == scb->cdb[0])) {
       ha->waitflag = FALSE;
@@ -4012,12 +4002,11 @@
 /* Routine Description:                                                     */
 /*                                                                          */
 /*   Do housekeeping on completed commands                                  */
-/*                                                                          */
+/*  ASSUMED to be called form within the request lock                       */
 /****************************************************************************/
 static void
 ips_done(ips_ha_t *ha, ips_scb_t *scb) {
    int ret;
-   unsigned long cpu_flags;
 
    METHOD_TRACE("ips_done", 1);
 
@@ -4026,9 +4015,7 @@
 
    if ((scb->scsi_cmd) && (ips_is_passthru(scb->scsi_cmd))) {
       ips_cleanup_passthru(ha, scb);
-      IPS_HA_LOCK(cpu_flags);
       ha->num_ioctl--;
-      IPS_HA_UNLOCK(cpu_flags);
    } else {
       /*
        * Check to see if this command had too much
@@ -4041,7 +4028,6 @@
 
          bk_save = scb->breakup;
          scb->breakup = 0;
-         mod_timer(&scb->scsi_cmd->eh_timeout, jiffies + 120 * HZ);
 
          if (scb->sg_count) {
             /* S/G request */
@@ -4128,15 +4114,21 @@
          } else {
             /* Non S/G Request */
             pci_unmap_single(ha->pcidev, scb->data_busaddr, scb->data_len,
-                             PCI_DMA_BIDIRECTIONAL);
+                             IPS_DMA_DIR(scb));
             if ((scb->scsi_cmd->request_bufflen - (bk_save * ha->max_xfer)) > ha->max_xfer) {
                /* Further breakup required */
                scb->data_len = ha->max_xfer;
-               scb->data_busaddr = pci_map_single(ha->pcidev, scb->scsi_cmd->request_buffer + (bk_save * ha->max_xfer), scb->data_len, PCI_DMA_BIDIRECTIONAL);
+               scb->data_busaddr = pci_map_single(ha->pcidev,
+                                           scb->scsi_cmd->request_buffer +
+                                           (bk_save * ha->max_xfer),
+                                           scb->data_len, IPS_DMA_DIR(scb));
                scb->breakup = bk_save + 1;
             } else {
                scb->data_len = scb->scsi_cmd->request_bufflen - (bk_save * ha->max_xfer);
-               scb->data_busaddr = pci_map_single(ha->pcidev, scb->scsi_cmd->request_buffer + (bk_save * ha->max_xfer), scb->data_len, PCI_DMA_BIDIRECTIONAL);
+               scb->data_busaddr = pci_map_single(ha->pcidev,
+                                           scb->scsi_cmd->request_buffer +
+                                           (bk_save * ha->max_xfer),
+                                           scb->data_len, IPS_DMA_DIR(scb));
 	    }
 
             scb->dcdb.transfer_length = scb->data_len;
@@ -4181,9 +4173,7 @@
    } /* end if passthru */
 
    if (scb->bus) {
-      IPS_HA_LOCK(cpu_flags);
       ha->dcdb_active[scb->bus-1] &= ~(1 << scb->target_id);
-      IPS_HA_UNLOCK(cpu_flags);
    }
 
    scb->scsi_cmd->scsi_done(scb->scsi_cmd);
@@ -4197,7 +4187,7 @@
 /*                                                                          */
 /* Routine Description:                                                     */
 /*                                                                          */
-/*   Map ServeRAID error codes to Linux Error Codes                         */
+/*   Map Controller Error codes to Linux Error Codes                        */
 /*                                                                          */
 /****************************************************************************/
 static int
@@ -4363,6 +4353,7 @@
    char     *sp;
    int       device_error;
    IPS_DCDB_TABLE_TAPE *tapeDCDB;
+   int       TimeOut;
 
    METHOD_TRACE("ips_send_cmd", 1);
 
@@ -4372,8 +4363,8 @@
       /* internal command */
 
       if (scb->bus > 0) {
-         /* ServeRAID commands can't be issued */
-         /* to real devices -- fail them       */
+         /* Controller commands can't be issued */
+         /* to real devices -- fail them        */
          if ((ha->waitflag == TRUE) &&
              (ha->cmd_in_progress == scb->cdb[0])) {
             ha->waitflag = FALSE;
@@ -4383,6 +4374,8 @@
       }
    } else if ((scb->bus == 0) && (!ips_is_passthru(scb->scsi_cmd))) {
       /* command to logical bus -- interpret */
+      if(ha->scb_waitlist.count + ha->scb_activelist.count > 32)
+         mod_timer(&scb->scsi_cmd->eh_timeout, jiffies + 120 * HZ);
       ret = IPS_SUCCESS_IMM;
 
       switch (scb->scsi_cmd->cmnd[0]) {
@@ -4399,43 +4392,17 @@
 
       case TEST_UNIT_READY:
       case INQUIRY:
-         if (scb->target_id == IPS_ADAPTER_ID) {
-            /*
-             * Either we have a TUR
-             * or we have a SCSI inquiry
-             */
-            if (scb->scsi_cmd->cmnd[0] == TEST_UNIT_READY)
-               scb->scsi_cmd->result = DID_OK << 16;
-
-            if (scb->scsi_cmd->cmnd[0] == INQUIRY) {
-               IPS_SCSI_INQ_DATA inquiry;
-
-               memset(&inquiry, 0, sizeof(IPS_SCSI_INQ_DATA));
-
-               inquiry.DeviceType = IPS_SCSI_INQ_TYPE_PROCESSOR;
-               inquiry.DeviceTypeQualifier = IPS_SCSI_INQ_LU_CONNECTED;
-               inquiry.Version = IPS_SCSI_INQ_REV2;
-               inquiry.ResponseDataFormat = IPS_SCSI_INQ_RD_REV2;
-               inquiry.AdditionalLength = 31;
-               inquiry.Flags[0] = IPS_SCSI_INQ_Address16;
-               inquiry.Flags[1] = IPS_SCSI_INQ_WBus16 | IPS_SCSI_INQ_Sync;
-               strncpy(inquiry.VendorId, "IBM     ", 8);
-               strncpy(inquiry.ProductId, "SERVERAID       ", 16);
-               strncpy(inquiry.ProductRevisionLevel, "1.00", 4);
-
-               memcpy(scb->scsi_cmd->request_buffer, &inquiry, scb->scsi_cmd->request_bufflen);
-
-               scb->scsi_cmd->result = DID_OK << 16;
-            }
-         } else {
-            scb->cmd.logical_info.op_code = IPS_CMD_GET_LD_INFO;
-            scb->cmd.logical_info.command_id = IPS_COMMAND_ID(ha, scb);
-            scb->cmd.logical_info.buffer_addr = cpu_to_le32(VIRT_TO_BUS(&ha->adapt->logical_drive_info));
-            scb->cmd.logical_info.reserved = 0;
-            scb->cmd.logical_info.reserved2 = 0;
-            ret = IPS_SUCCESS;
-         }
-
+         scb->cmd.logical_info.op_code = IPS_CMD_GET_LD_INFO;
+         scb->cmd.logical_info.command_id = IPS_COMMAND_ID(ha, scb);
+         scb->cmd.logical_info.reserved = 0;
+         scb->cmd.logical_info.reserved2 = 0;
+         scb->data_len = sizeof(ha->adapt->logical_drive_info);
+         scb->data_busaddr = pci_map_single(ha->pcidev,
+                                            &ha->adapt->logical_drive_info,
+                                            scb->data_len, IPS_DMA_DIR(scb));
+         scb->flags |= IPS_SCB_MAP_SINGLE;
+         scb->cmd.logical_info.buffer_addr = scb->data_busaddr;
+         ret = IPS_SUCCESS;
          break;
 
       case REQUEST_SENSE:
@@ -4523,17 +4490,26 @@
       case MODE_SENSE:
          scb->cmd.basic_io.op_code = IPS_CMD_ENQUIRY;
          scb->cmd.basic_io.command_id = IPS_COMMAND_ID(ha, scb);
-         scb->cmd.basic_io.sg_addr = cpu_to_le32(VIRT_TO_BUS(ha->enq));
+         scb->data_len = sizeof(*ha->enq);
+         scb->data_busaddr = pci_map_single(ha->pcidev, ha->enq,
+                                            scb->data_len, IPS_DMA_DIR(scb));
+         scb->cmd.basic_io.sg_addr = scb->data_busaddr;
+         scb->flags |= IPS_SCB_MAP_SINGLE;
          ret = IPS_SUCCESS;
          break;
 
       case READ_CAPACITY:
          scb->cmd.logical_info.op_code = IPS_CMD_GET_LD_INFO;
          scb->cmd.logical_info.command_id = IPS_COMMAND_ID(ha, scb);
-         scb->cmd.logical_info.buffer_addr = cpu_to_le32(VIRT_TO_BUS(&ha->adapt->logical_drive_info));
          scb->cmd.logical_info.reserved = 0;
          scb->cmd.logical_info.reserved2 = 0;
          scb->cmd.logical_info.reserved3 = 0;
+         scb->data_len = sizeof(ha->adapt->logical_drive_info);
+         scb->data_busaddr = pci_map_single(ha->pcidev,
+                                            &ha->adapt->logical_drive_info,
+                                            scb->data_len, IPS_DMA_DIR(scb));
+         scb->flags |= IPS_SCB_MAP_SINGLE;
+         scb->cmd.logical_info.buffer_addr = scb->data_busaddr;
          ret = IPS_SUCCESS;
          break;
 
@@ -4586,10 +4562,14 @@
 
       ha->dcdb_active[scb->bus-1] |= (1 << scb->target_id);
       scb->cmd.dcdb.command_id = IPS_COMMAND_ID(ha, scb);
-      scb->cmd.dcdb.dcdb_address = cpu_to_le32(VIRT_TO_BUS(&scb->dcdb));
+      scb->cmd.dcdb.dcdb_address = cpu_to_le32(scb->scb_busaddr +
+                                               (unsigned long)&scb->dcdb -
+                                               (unsigned long)scb);
       scb->cmd.dcdb.reserved = 0;
       scb->cmd.dcdb.reserved2 = 0;
-      scb->cmd.dcdb.reserved3 = 0;
+      scb->cmd.dcdb.reserved3 = 0;   
+
+      TimeOut = scb->scsi_cmd->timeout_per_command;
 
       if (ha->subsys->param[4] & 0x00100000) {          /* If NEW Tape DCDB is Supported */
          if (!scb->sg_len)
@@ -4602,51 +4582,50 @@
          tapeDCDB->cmd_attribute |= IPS_DISCONNECT_ALLOWED;
          tapeDCDB->cmd_attribute &= ~IPS_TRANSFER64K;   /* Always Turn OFF 64K Size Flag */
 
-         if (scb->timeout) {
-           if (scb->timeout <= 10)
-               tapeDCDB->cmd_attribute |= IPS_TIMEOUT10;
-            else if (scb->timeout <= 60)
-               tapeDCDB->cmd_attribute |= IPS_TIMEOUT60;
-            else
-               tapeDCDB->cmd_attribute |= IPS_TIMEOUT20M;
+         if (TimeOut) {
+            if (TimeOut < ( 10 * HZ ))                  
+                tapeDCDB->cmd_attribute |= IPS_TIMEOUT10;     /* TimeOut is 10 Seconds */
+            else if (TimeOut < (60 * HZ)) 
+                tapeDCDB->cmd_attribute |= IPS_TIMEOUT60;     /* TimeOut is 60 Seconds */
+            else if (TimeOut < (1200 * HZ)) 
+                tapeDCDB->cmd_attribute |= IPS_TIMEOUT20M;    /* TimeOut is 20 Minutes */
          }
 
-         if (!(tapeDCDB->cmd_attribute & IPS_TIMEOUT20M))
-            tapeDCDB->cmd_attribute |= IPS_TIMEOUT20M;
-
-         tapeDCDB->sense_length = sizeof(tapeDCDB->sense_info);
+         tapeDCDB->cdb_length = scb->scsi_cmd->cmd_len;
+         tapeDCDB->reserved_for_LUN = 0;
          tapeDCDB->transfer_length = scb->data_len;
          tapeDCDB->buffer_pointer = cpu_to_le32(scb->data_busaddr);
          tapeDCDB->sg_count = scb->sg_len;
-         tapeDCDB->cdb_length = scb->scsi_cmd->cmd_len;
-         tapeDCDB->reserved_for_LUN = 0;
-         tapeDCDB->reserved = 0;
+         tapeDCDB->sense_length = sizeof(tapeDCDB->sense_info);
          tapeDCDB->scsi_status = 0;
+         tapeDCDB->reserved = 0;
          memcpy(tapeDCDB->scsi_cdb, scb->scsi_cmd->cmnd, scb->scsi_cmd->cmd_len);
       } else {
          scb->dcdb.device_address = ((scb->bus - 1) << 4) | scb->target_id;
          scb->dcdb.cmd_attribute |= IPS_DISCONNECT_ALLOWED;
 
-         if (scb->timeout) {
-           if (scb->timeout <= 10)
-               scb->dcdb.cmd_attribute |= IPS_TIMEOUT10;
-            else if (scb->timeout <= 60)
-               scb->dcdb.cmd_attribute |= IPS_TIMEOUT60;
-            else
-               scb->dcdb.cmd_attribute |= IPS_TIMEOUT20M;
+         if (TimeOut) {
+            if (TimeOut < (10 * HZ))                  
+                scb->dcdb.cmd_attribute |= IPS_TIMEOUT10;     /* TimeOut is 10 Seconds */
+            else if (TimeOut < (60 * HZ)) 
+                scb->dcdb.cmd_attribute |= IPS_TIMEOUT60;     /* TimeOut is 60 Seconds */
+            else if (TimeOut < (1200 * HZ)) 
+                scb->dcdb.cmd_attribute |= IPS_TIMEOUT20M;    /* TimeOut is 20 Minutes */
          }
-
-         if (!(scb->dcdb.cmd_attribute & IPS_TIMEOUT20M))
-            scb->dcdb.cmd_attribute |= IPS_TIMEOUT20M;
-
-         scb->dcdb.sense_length = sizeof(scb->dcdb.sense_info);
+         
          scb->dcdb.transfer_length = scb->data_len;
          if ( scb->dcdb.cmd_attribute & IPS_TRANSFER64K ) 
              scb->dcdb.transfer_length = 0;
          scb->dcdb.buffer_pointer = cpu_to_le32(scb->data_busaddr);
-         scb->dcdb.sg_count = scb->sg_len;
          scb->dcdb.cdb_length = scb->scsi_cmd->cmd_len;
+         scb->dcdb.sense_length = sizeof(scb->dcdb.sense_info);
+         scb->dcdb.sg_count = scb->sg_len;
+         scb->dcdb.reserved = 0;
          memcpy(scb->dcdb.scsi_cdb, scb->scsi_cmd->cmnd, scb->scsi_cmd->cmd_len);
+         scb->dcdb.scsi_status = 0;
+         scb->dcdb.reserved2[0] = 0;
+         scb->dcdb.reserved2[1] = 0;
+         scb->dcdb.reserved2[2] = 0;
       }
    }
 
@@ -4660,7 +4639,7 @@
 /* Routine Description:                                                     */
 /*                                                                          */
 /*   Check the status of commands to logical drives                         */
-/*                                                                          */
+/*   Assumed to be called with the HA lock                                  */
 /****************************************************************************/
 static void
 ips_chkstatus(ips_ha_t *ha, IPS_STATUS *pstatus) {
@@ -4725,12 +4704,37 @@
             break;
 
          case TEST_UNIT_READY:
+            if (scb->target_id == IPS_ADAPTER_ID) 
+               break;
+
             if (!ips_online(ha, scb)) {
                errcode = DID_TIME_OUT;
             }
             break;
 
          case INQUIRY:
+            if (scb->target_id == IPS_ADAPTER_ID) {
+               IPS_SCSI_INQ_DATA inquiry;
+
+               memset(&inquiry, 0, sizeof(IPS_SCSI_INQ_DATA));
+
+               inquiry.DeviceType = IPS_SCSI_INQ_TYPE_PROCESSOR;
+               inquiry.DeviceTypeQualifier = IPS_SCSI_INQ_LU_CONNECTED;
+               inquiry.Version = IPS_SCSI_INQ_REV2;
+               inquiry.ResponseDataFormat = IPS_SCSI_INQ_RD_REV2;
+               inquiry.AdditionalLength = 31;
+               inquiry.Flags[0] = IPS_SCSI_INQ_Address16;
+               inquiry.Flags[1] = IPS_SCSI_INQ_WBus16 | IPS_SCSI_INQ_Sync;
+               strncpy(inquiry.VendorId, "IBM     ", 8);
+               strncpy(inquiry.ProductId, "SERVERAID       ", 16);
+               strncpy(inquiry.ProductRevisionLevel, "1.00", 4);
+
+               memcpy(scb->scsi_cmd->request_buffer, &inquiry, scb->scsi_cmd->request_bufflen);
+
+               scb->scsi_cmd->result = DID_OK << 16;
+               break;
+            }
+
             if (ips_online(ha, scb)) {
                ips_inquiry(ha, scb);
             } else {
@@ -5024,7 +5028,8 @@
       }
 
       if (ha->adapt) {
-         kfree(ha->adapt);
+         pci_free_consistent(ha->pcidev,sizeof(IPS_ADAPTER)+ sizeof(IPS_IO_CMD),
+                             ha->adapt, ha->adapt->hw_status_start);
          ha->adapt = NULL;
       }
 
@@ -5038,11 +5043,6 @@
          ha->subsys = NULL;
       }
 
-      if (ha->dummy) {
-         kfree(ha->dummy);
-         ha->dummy = NULL;
-      }
-
       if (ha->ioctl_data) {
          free_pages((unsigned long) ha->ioctl_data, ha->ioctl_order);
          ha->ioctl_data = NULL;
@@ -5164,7 +5164,8 @@
 
    /* Initialize dummy command bucket */
    ha->dummy->op_code = 0xFF;
-   ha->dummy->ccsar = cpu_to_le32(VIRT_TO_BUS(ha->dummy));
+   ha->dummy->ccsar = cpu_to_le32(ha->adapt->hw_status_start
+                                      + sizeof(IPS_ADAPTER));
    ha->dummy->command_id = IPS_MAX_CMDS;
 
    /* set bus address of scb */
@@ -5174,7 +5175,8 @@
 
    /* Neptune Fix */
    scb->cmd.basic_io.cccr = cpu_to_le32((uint32_t) IPS_BIT_ILE);
-   scb->cmd.basic_io.ccsar = cpu_to_le32(VIRT_TO_BUS(ha->dummy));
+   scb->cmd.basic_io.ccsar = cpu_to_le32(ha->adapt->hw_status_start
+                                           + sizeof(IPS_ADAPTER));
 }
 
 /****************************************************************************/
@@ -5191,13 +5193,10 @@
 static ips_scb_t *
 ips_getscb(ips_ha_t *ha) {
    ips_scb_t     *scb;
-   unsigned long  cpu_flags;
 
    METHOD_TRACE("ips_getscb", 1);
 
-   IPS_SCB_LOCK(cpu_flags);
    if ((scb = ha->scb_freelist) == NULL) {
-      IPS_SCB_UNLOCK(cpu_flags);
 
       return (NULL);
    }
@@ -5205,8 +5204,6 @@
    ha->scb_freelist = scb->q_next;
    scb->q_next = NULL;
 
-   IPS_SCB_UNLOCK(cpu_flags);
-
    ips_init_scb(ha, scb);
 
    return (scb);
@@ -5225,20 +5222,20 @@
 /****************************************************************************/
 static void
 ips_freescb(ips_ha_t *ha, ips_scb_t *scb) {
-   unsigned long cpu_flags;
 
    METHOD_TRACE("ips_freescb", 1);
    if(scb->flags & IPS_SCB_MAP_SG)
-      pci_unmap_sg(ha->pcidev,scb->scsi_cmd->request_buffer, scb->scsi_cmd->use_sg, PCI_DMA_BIDIRECTIONAL); 
+      pci_unmap_sg(ha->pcidev, scb->scsi_cmd->request_buffer,
+                   scb->scsi_cmd->use_sg,
+                   IPS_DMA_DIR(scb));
    else if(scb->flags & IPS_SCB_MAP_SINGLE)
-      pci_unmap_single(ha->pcidev,scb->cmd.basic_io.sg_addr, scb->data_len, PCI_DMA_BIDIRECTIONAL);
+      pci_unmap_single(ha->pcidev, scb->data_busaddr, scb->data_len,
+                       IPS_DMA_DIR(scb));
 
    /* check to make sure this is not our "special" scb */
    if (IPS_COMMAND_ID(ha, scb) < (ha->max_cmds - 1)) {
-      IPS_SCB_LOCK(cpu_flags);
       scb->q_next = ha->scb_freelist;
       ha->scb_freelist = scb;
-      IPS_SCB_UNLOCK(cpu_flags);
    }
 }
 
@@ -5593,6 +5590,29 @@
 
    Post = readl(ha->mem_ptr + IPS_REG_I960_MSG0);
 
+   if (Post == 0x4F00) {                          /* If Flashing the Battery PIC         */
+       printk(KERN_WARNING "Flashing Battery PIC, Please wait ...\n" );
+
+       /* Clear the interrupt bit */
+       Isr = (uint32_t) IPS_BIT_I960_MSG0I;
+       writel(Isr, ha->mem_ptr + IPS_REG_I2O_HIR);
+
+       for (i = 0; i < 120; i++) {                /*    Wait Up to 2 Min. for Completion */
+          Post = readl(ha->mem_ptr + IPS_REG_I960_MSG0);
+          if (Post != 0x4F00)
+              break;
+          /* Delay for 1 Second */
+          MDELAY(IPS_ONE_SEC);
+       }
+
+       if (i >= 120) {
+          printk(KERN_WARNING "(%s%d) timeout waiting for Battery PIC Flash\n",
+                 ips_name, ha->host_num);
+          return (0);
+       }
+
+   }
+
    /* Clear the interrupt bit */
    Isr = (uint32_t) IPS_BIT_I960_MSG0I;
    writel(Isr, ha->mem_ptr + IPS_REG_I2O_HIR);
@@ -5635,6 +5655,13 @@
    writel(Oimr, ha->mem_ptr + IPS_REG_I960_OIMR);
 
    /* if we get here then everything went OK */
+
+   /* Since we did a RESET, an EraseStripeLock may be needed */
+   if (Post == 0xEF10) {
+      if ( (Config == 0x000F) || (Config == 0x0009) )
+         ha->requires_esl = 1;
+   }
+
    return (1);
 }
 
@@ -5650,15 +5677,12 @@
 static int
 ips_reset_copperhead(ips_ha_t *ha) {
    int reset_counter;
-   unsigned long cpu_flags;
 
    METHOD_TRACE("ips_reset_copperhead", 1);
 
    DEBUG_VAR(1, "(%s%d) ips_reset_copperhead: io addr: %x, irq: %d",
              ips_name, ha->host_num, ha->io_addr, ha->irq);
 
-   IPS_HA_LOCK(cpu_flags);
-
    reset_counter = 0;
 
    while (reset_counter < 2) {
@@ -5677,14 +5701,11 @@
       if ((*ha->func.init)(ha))
          break;
       else if (reset_counter >= 2) {
-         IPS_HA_UNLOCK(cpu_flags);
 
          return (0);
       }
    }
 
-   IPS_HA_UNLOCK(cpu_flags);
-
    return (1);
 }
 
@@ -5700,15 +5721,12 @@
 static int
 ips_reset_copperhead_memio(ips_ha_t *ha) {
    int reset_counter;
-   unsigned long cpu_flags;
 
    METHOD_TRACE("ips_reset_copperhead_memio", 1);
 
    DEBUG_VAR(1, "(%s%d) ips_reset_copperhead_memio: mem addr: %x, irq: %d",
              ips_name, ha->host_num, ha->mem_addr, ha->irq);
 
-   IPS_HA_LOCK(cpu_flags);
-
    reset_counter = 0;
 
    while (reset_counter < 2) {
@@ -5727,14 +5745,11 @@
       if ((*ha->func.init)(ha))
          break;
       else if (reset_counter >= 2) {
-         IPS_HA_UNLOCK(cpu_flags);
 
          return (0);
       }
    }
 
-   IPS_HA_UNLOCK(cpu_flags);
-
    return (1);
 }
 
@@ -5751,15 +5766,12 @@
 ips_reset_morpheus(ips_ha_t *ha) {
    int           reset_counter;
    uint8_t       junk;
-   unsigned long cpu_flags;
 
    METHOD_TRACE("ips_reset_morpheus", 1);
 
    DEBUG_VAR(1, "(%s%d) ips_reset_morpheus: mem addr: %x, irq: %d",
              ips_name, ha->host_num, ha->mem_addr, ha->irq);
 
-   IPS_HA_LOCK(cpu_flags);
-
    reset_counter = 0;
 
    while (reset_counter < 2) {
@@ -5776,14 +5788,11 @@
       if ((*ha->func.init)(ha))
          break;
       else if (reset_counter >= 2) {
-         IPS_HA_UNLOCK(cpu_flags);
 
          return (0);
       }
    }
 
-   IPS_HA_UNLOCK(cpu_flags);
-
    return (1);
 }
 
@@ -5806,13 +5815,12 @@
    ha->adapt->p_status_end = ha->adapt->status + IPS_MAX_CMDS;
    ha->adapt->p_status_tail = ha->adapt->status;
 
-   phys_status_start = VIRT_TO_BUS(ha->adapt->status);
+   phys_status_start = ha->adapt->hw_status_start;
    outl(cpu_to_le32(phys_status_start), ha->io_addr + IPS_REG_SQSR);
    outl(cpu_to_le32(phys_status_start + IPS_STATUS_Q_SIZE), ha->io_addr + IPS_REG_SQER);
    outl(cpu_to_le32(phys_status_start + IPS_STATUS_SIZE), ha->io_addr + IPS_REG_SQHR);
    outl(cpu_to_le32(phys_status_start), ha->io_addr + IPS_REG_SQTR);
 
-   ha->adapt->hw_status_start = phys_status_start;
    ha->adapt->hw_status_tail = phys_status_start;
 }
 
@@ -5835,13 +5843,12 @@
    ha->adapt->p_status_end = ha->adapt->status + IPS_MAX_CMDS;
    ha->adapt->p_status_tail = ha->adapt->status;
 
-   phys_status_start = VIRT_TO_BUS(ha->adapt->status);
+   phys_status_start = ha->adapt->hw_status_start;
    writel(phys_status_start, ha->mem_ptr + IPS_REG_SQSR);
    writel(phys_status_start + IPS_STATUS_Q_SIZE, ha->mem_ptr + IPS_REG_SQER);
    writel(phys_status_start + IPS_STATUS_SIZE, ha->mem_ptr + IPS_REG_SQHR);
    writel(phys_status_start, ha->mem_ptr + IPS_REG_SQTR);
 
-   ha->adapt->hw_status_start = phys_status_start;
    ha->adapt->hw_status_tail = phys_status_start;
 }
 
@@ -5930,7 +5937,6 @@
 ips_issue_copperhead(ips_ha_t *ha, ips_scb_t *scb) {
    uint32_t TimeOut;
    uint32_t val;
-   unsigned long cpu_flags;
 
    METHOD_TRACE("ips_issue_copperhead", 1);
 
@@ -5950,8 +5956,6 @@
                 scb->cmd.basic_io.command_id);
    }
 
-   IPS_HA_LOCK(cpu_flags);
-
    TimeOut = 0;
 
    while ((val = le32_to_cpu(inl(ha->io_addr + IPS_REG_CCCR))) & IPS_BIT_SEM) {
@@ -5966,8 +5970,6 @@
          printk(KERN_WARNING "(%s%d) ips_issue semaphore chk timeout.\n",
                 ips_name, ha->host_num);
 
-         IPS_HA_UNLOCK(cpu_flags);
-
          return (IPS_FAILURE);
       } /* end if */
    } /* end while */
@@ -5975,8 +5977,6 @@
    outl(cpu_to_le32(scb->scb_busaddr), ha->io_addr + IPS_REG_CCSAR);
    outw(cpu_to_le32(IPS_BIT_START_CMD), ha->io_addr + IPS_REG_CCCR);
 
-   IPS_HA_UNLOCK(cpu_flags);
-
    return (IPS_SUCCESS);
 }
 
@@ -5993,7 +5993,6 @@
 ips_issue_copperhead_memio(ips_ha_t *ha, ips_scb_t *scb) {
    uint32_t      TimeOut;
    uint32_t      val;
-   unsigned long cpu_flags;
 
    METHOD_TRACE("ips_issue_copperhead_memio", 1);
 
@@ -6013,8 +6012,6 @@
                 scb->cmd.basic_io.command_id);
    }
 
-   IPS_HA_LOCK(cpu_flags);
-
    TimeOut = 0;
 
    while ((val = readl(ha->mem_ptr + IPS_REG_CCCR)) & IPS_BIT_SEM) {
@@ -6029,8 +6026,6 @@
          printk(KERN_WARNING "(%s%d) ips_issue semaphore chk timeout.\n",
                 ips_name, ha->host_num);
 
-         IPS_HA_UNLOCK(cpu_flags);
-
          return (IPS_FAILURE);
       } /* end if */
    } /* end while */
@@ -6038,8 +6033,6 @@
    writel(scb->scb_busaddr, ha->mem_ptr + IPS_REG_CCSAR);
    writel(IPS_BIT_START_CMD, ha->mem_ptr + IPS_REG_CCCR);
 
-   IPS_HA_UNLOCK(cpu_flags);
-
    return (IPS_SUCCESS);
 }
 
@@ -6054,7 +6047,6 @@
 /****************************************************************************/
 static int
 ips_issue_i2o(ips_ha_t *ha, ips_scb_t *scb) {
-   unsigned long cpu_flags;
 
    METHOD_TRACE("ips_issue_i2o", 1);
 
@@ -6074,12 +6066,8 @@
                 scb->cmd.basic_io.command_id);
    }
 
-   IPS_HA_LOCK(cpu_flags);
-
    outl(cpu_to_le32(scb->scb_busaddr), ha->io_addr + IPS_REG_I2O_INMSGQ);
 
-   IPS_HA_UNLOCK(cpu_flags);
-
    return (IPS_SUCCESS);
 }
 
@@ -6094,7 +6082,6 @@
 /****************************************************************************/
 static int
 ips_issue_i2o_memio(ips_ha_t *ha, ips_scb_t *scb) {
-   unsigned long cpu_flags;
 
    METHOD_TRACE("ips_issue_i2o_memio", 1);
 
@@ -6114,12 +6101,8 @@
                 scb->cmd.basic_io.command_id);
    }
 
-   IPS_HA_LOCK(cpu_flags);
-
    writel(scb->scb_busaddr, ha->mem_ptr + IPS_REG_I2O_INMSGQ);
 
-   IPS_HA_UNLOCK(cpu_flags);
-
    return (IPS_SUCCESS);
 }
 
@@ -6256,12 +6239,7 @@
           * until after we finish.
           */
 
-         while (test_and_set_bit(IPS_IN_INTR, &ha->flags))
-            udelay(1000);
-
          (*ha->func.intr)(ha);
-
-         clear_bit(IPS_IN_INTR, &ha->flags);
       } 
 
       /* This looks like a very evil loop, but it only does this during start-up */
@@ -6297,8 +6275,7 @@
    if (le32_to_cpu(ha->nvram->signature) != IPS_NVRAM_P5_SIG) {
       DEBUG_VAR(1, "(%s%d) NVRAM page 5 has an invalid signature: %X.",
                 ips_name, ha->host_num, ha->nvram->signature);
-
-      return (1);
+      ha->nvram->signature = IPS_NVRAM_P5_SIG;
    }
 
    DEBUG_VAR(2, "(%s%d) Ad Type: %d, Ad Slot: %d, BIOS: %c%c%c%c %c%c%c%c.",
@@ -6321,6 +6298,10 @@
 
    ips_version_check(ha, intr);                           /* Check BIOS/FW/Driver Versions */
 
+   /* Save the First Copy of the Adapter Order that BIOS put in Page 5 */
+   if ( (InitState == 0) && (AdapterOrder[0] == 0) ) 
+      strncpy((char *) AdapterOrder, (char *) ha->nvram->adapter_order, sizeof(AdapterOrder) );
+
    /* now update the page */
    if (!ips_readwrite_page5(ha, TRUE, intr)) {
       printk(KERN_WARNING "(%s%d) unable to write NVRAM page 5.\n",
@@ -6362,11 +6343,15 @@
    scb->cmd.basic_io.op_code = IPS_CMD_ENQUIRY;
    scb->cmd.basic_io.command_id = IPS_COMMAND_ID(ha, scb);
    scb->cmd.basic_io.sg_count = 0;
-   scb->cmd.basic_io.sg_addr = cpu_to_le32(VIRT_TO_BUS(ha->enq));
    scb->cmd.basic_io.lba = 0;
    scb->cmd.basic_io.sector_count = 0;
    scb->cmd.basic_io.log_drv = 0;
    scb->cmd.basic_io.reserved = 0;
+   scb->data_len = sizeof(*ha->enq);
+   scb->data_busaddr = pci_map_single(ha->pcidev, ha->enq, scb->data_len,
+                                      IPS_DMA_DIR(scb));
+   scb->cmd.basic_io.sg_addr = scb->data_busaddr;
+   scb->flags |= IPS_SCB_MAP_SINGLE;
 
    /* send command */
    if (((ret = ips_send_wait(ha, scb, ips_cmd_timeout, intr)) == IPS_FAILURE) ||
@@ -6403,11 +6388,15 @@
    scb->cmd.basic_io.op_code = IPS_CMD_GET_SUBSYS;
    scb->cmd.basic_io.command_id = IPS_COMMAND_ID(ha, scb);
    scb->cmd.basic_io.sg_count = 0;
-   scb->cmd.basic_io.sg_addr = cpu_to_le32(VIRT_TO_BUS(ha->subsys));
    scb->cmd.basic_io.lba = 0;
    scb->cmd.basic_io.sector_count = 0;
    scb->cmd.basic_io.log_drv = 0;
    scb->cmd.basic_io.reserved = 0;
+   scb->data_len = sizeof(*ha->subsys);
+   scb->data_busaddr = pci_map_single(ha->pcidev, ha->subsys,
+                                      scb->data_len, IPS_DMA_DIR(scb));
+   scb->cmd.basic_io.sg_addr = scb->data_busaddr;
+   scb->flags |= IPS_SCB_MAP_SINGLE;
 
    /* send command */
    if (((ret = ips_send_wait(ha, scb, ips_cmd_timeout, intr)) == IPS_FAILURE) ||
@@ -6448,7 +6437,11 @@
 
    scb->cmd.basic_io.op_code = IPS_CMD_READ_CONF;
    scb->cmd.basic_io.command_id = IPS_COMMAND_ID(ha, scb);
-   scb->cmd.basic_io.sg_addr = cpu_to_le32(VIRT_TO_BUS(ha->conf));
+   scb->data_len = sizeof(*ha->conf);
+   scb->data_busaddr = pci_map_single(ha->pcidev, ha->conf,
+                                      scb->data_len, IPS_DMA_DIR(scb));
+   scb->cmd.basic_io.sg_addr = scb->data_busaddr;
+   scb->flags |= IPS_SCB_MAP_SINGLE;
 
    /* send command */
    if (((ret = ips_send_wait(ha, scb, ips_cmd_timeout, intr)) == IPS_FAILURE) ||
@@ -6498,9 +6491,13 @@
    scb->cmd.nvram.command_id = IPS_COMMAND_ID(ha, scb);
    scb->cmd.nvram.page = 5;
    scb->cmd.nvram.write = write;
-   scb->cmd.nvram.buffer_addr = cpu_to_le32(VIRT_TO_BUS(ha->nvram));
    scb->cmd.nvram.reserved = 0;
    scb->cmd.nvram.reserved2 = 0;
+   scb->data_len = sizeof(*ha->nvram);
+   scb->data_busaddr = pci_map_single(ha->pcidev, ha->nvram,
+                                      scb->data_len, IPS_DMA_DIR(scb));
+   scb->cmd.nvram.buffer_addr = scb->data_busaddr;
+   scb->flags |= IPS_SCB_MAP_SINGLE;
 
    /* issue the command */
    if (((ret = ips_send_wait(ha, scb, ips_cmd_timeout, intr)) == IPS_FAILURE) ||
@@ -7211,7 +7208,7 @@
 /*     Assumes that ips_read_adapter_status() is called first filling in     */
 /*     the data for SubSystem Parameters.                                    */
 /*     Called from ips_write_driver_status() so it also assumes NVRAM Page 5 */
-/*     Data is available.                                                   */
+/*     Data is available.                                                    */
 /*                                                                           */
 /*---------------------------------------------------------------------------*/
 static void ips_version_check(ips_ha_t *ha, int intr) {
@@ -7220,6 +7217,8 @@
  uint8_t  BiosVersion[ IPS_COMPAT_ID_LENGTH + 1];
  int      MatchError;
  int      rc;
+ char     BiosString[10];
+ char     FirmwareString[10];
 
  METHOD_TRACE("ips_version_check", 1);
 
@@ -7252,28 +7251,30 @@
  MatchError = 0;
 
  if  (strncmp(FirmwareVersion, Compatable[ ha->nvram->adapter_type ], IPS_COMPAT_ID_LENGTH) != 0)
- {
-     if (ips_cd_boot == 0)                                                                              
-       printk(KERN_WARNING "Warning: Adapter %d Firmware Compatible Version is %s, but should be %s\n", 
-              ha->host_num, FirmwareVersion, Compatable[ ha->nvram->adapter_type ]);                    
      MatchError = 1;
- }
 
  if  (strncmp(BiosVersion, IPS_COMPAT_BIOS, IPS_COMPAT_ID_LENGTH) != 0)
- {
-     if (ips_cd_boot == 0)                                                                          
-       printk(KERN_WARNING "Warning: Adapter %d BIOS Compatible Version is %s, but should be %s\n", 
-              ha->host_num, BiosVersion, IPS_COMPAT_BIOS);                                          
      MatchError = 1;
- }
 
  ha->nvram->versioning = 1;          /* Indicate the Driver Supports Versioning */
 
  if  (MatchError)
  {
      ha->nvram->version_mismatch = 1;
-     if (ips_cd_boot == 0)                                               
+     if (ips_cd_boot == 0)  
+     {
+       strncpy(&BiosString[0], ha->nvram->bios_high, 4);
+       strncpy(&BiosString[4], ha->nvram->bios_low, 4);
+       BiosString[8] = 0;
+
+       strncpy(&FirmwareString[0], ha->enq->CodeBlkVersion, 8);
+       FirmwareString[8] = 0;
+
        printk(KERN_WARNING "Warning ! ! ! ServeRAID Version Mismatch\n");
+       printk(KERN_WARNING "Bios = %s, Firmware = %s, Device Driver = %s%s\n",
+                            BiosString, FirmwareString, IPS_VERSION_HIGH, IPS_VERSION_LOW );
+       printk(KERN_WARNING "These levels should match to avoid possible compatibility problems.\n" );
+     }
  }
  else
  {
@@ -7287,7 +7288,7 @@
 /*   Routine Name: ips_get_version_info                                      */
 /*                                                                           */
 /*   Routine Description:                                                    */
-/*     Issue an internal GETVERSION ServeRAID Command                        */
+/*     Issue an internal GETVERSION Command                                  */
 /*                                                                           */
 /*   Return Value:                                                           */
 /*     0 if Successful, else non-zero                                        */
@@ -7309,8 +7310,12 @@
    scb->cmd.version_info.command_id = IPS_COMMAND_ID(ha, scb);
    scb->cmd.version_info.reserved = 0;
    scb->cmd.version_info.count = sizeof( IPS_VERSION_DATA);
-   scb->cmd.version_info.buffer_addr = cpu_to_le32(VIRT_TO_BUS(Buffer));
    scb->cmd.version_info.reserved2 = 0;
+   scb->data_len = sizeof(*Buffer);
+   scb->data_busaddr = pci_map_single(ha->pcidev, Buffer,
+                                      scb->data_len, IPS_DMA_DIR(scb));
+   scb->cmd.version_info.buffer_addr = scb->data_busaddr;
+   scb->flags |= IPS_SCB_MAP_SINGLE;
 
    /* issue command */
    rc = ips_send_wait(ha, scb, ips_cmd_timeout, intr);
@@ -7324,6 +7329,14 @@
 #include "scsi_module.c"
 #endif
 
+static int ips_abort_init(ips_ha_t *ha, struct Scsi_Host *sh, int index){
+   ha->active = 0;
+   ips_free(ha);
+   scsi_unregister(sh);
+   ips_ha[index] = 0;
+   ips_sh[index] = 0;
+   return -1;
+}
 
 #if LINUX_VERSION_CODE >= LinuxVersionCode(2,4,0)
 
@@ -7367,12 +7380,25 @@
     int  rc;
 
     METHOD_TRACE("ips_insert_device", 1);
+
+    /* If we're still in Init State 0, and we've already found the Adapter */
+    /* Ordering Table, there is no reason to continue.                     */
+    if ( (InitState == 0) && (AdapterOrder[0]) ) 
+        return -1;
+    
     if (pci_enable_device(pci_dev)) 
 		return -1;
 
     rc = ips_init_phase1(pci_dev, &index);
     if (rc == SUCCESS)
-       rc = ips_init_phase2(index);  
+       rc = ips_init_phase2(index); 
+
+    /* If we're in Init State 0, we're done with the device for now. */
+    /* Release the device and don't count it.                        */
+    if ( InitState == 0 ) {
+        ips_remove_device(pci_dev);
+		return -1;
+    }
 
     if (rc == SUCCESS)
        ips_num_controllers++;
@@ -7407,6 +7433,7 @@
    int               j;
    int               index; 
    uint32_t          count;
+   dma_addr_t        dma_address;
    char             *ioremap_ptr;
    char             *mem_ptr;             
    uint32_t          IsDead
@@ -7486,6 +7513,10 @@
 
     /* found a controller */
     sh = scsi_register(&driver_template, sizeof(ips_ha_t));
+#if LINUX_VERSION_CODE > LinuxVersionCode(2,5,0) 
+    pci_set_dma_mask(pci_dev, (u64)0xffffffff);
+    scsi_set_pci_device(sh, pci_dev);
+#endif
     if (sh == NULL) {
        printk(KERN_WARNING "Unable to register controller with SCSI subsystem\n" );
        return -1;
@@ -7493,14 +7524,7 @@
 
     ha = IPS_HA(sh);
     memset(ha, 0, sizeof(ips_ha_t));
-
-    /* Initialize spin lock */
-    spin_lock_init(&ha->scb_lock);
-    spin_lock_init(&ha->copp_lock);                                                              spin_lock_init(&ha->ips_lock);
-    spin_lock_init(&ha->copp_waitlist.lock);
-    spin_lock_init(&ha->scb_waitlist.lock);
-    spin_lock_init(&ha->scb_activelist.lock);
-
+    
     ips_sh[index] = sh;
     ips_ha[index] = ha;
     ha->active = 1;
@@ -7509,72 +7533,37 @@
 
     if (!ha->enq) {
        printk(KERN_WARNING "Unable to allocate host inquiry structure\n" );
-       ha->active = 0;
-       ips_free(ha);
-       scsi_unregister(sh);
-       ips_ha[index] = 0;
-       ips_sh[index] = 0;
-       return -1;
+       return ips_abort_init(ha, sh, index);
     }
 
-    ha->adapt = kmalloc(sizeof(IPS_ADAPTER), GFP_KERNEL);
-
+    ha->adapt = pci_alloc_consistent(pci_dev, sizeof(IPS_ADAPTER) +
+                                     sizeof(IPS_IO_CMD), &dma_address);
     if (!ha->adapt) {
-       printk(KERN_WARNING "Unable to allocate host adapt structure\n" );
-       ha->active = 0;
-       ips_free(ha);
-       scsi_unregister(sh);
-       ips_ha[index] = 0;
-       ips_sh[index] = 0;
-       return -1;
+       printk(KERN_WARNING "Unable to allocate host adapt & dummy structures\n");
+       return ips_abort_init(ha, sh, index);
     }
+    ha->adapt->hw_status_start = dma_address;
+    ha->dummy = (void *)(ha->adapt + 1);
 
     ha->conf = kmalloc(sizeof(IPS_CONF), GFP_KERNEL);
 
     if (!ha->conf) {
        printk(KERN_WARNING "Unable to allocate host conf structure\n" );
-       ha->active = 0;
-       ips_free(ha);
-       scsi_unregister(sh);
-       ips_ha[index] = 0;
-       ips_sh[index] = 0;
-       return -1;
+       return ips_abort_init(ha, sh, index);
     }
 
     ha->nvram = kmalloc(sizeof(IPS_NVRAM_P5), GFP_KERNEL);
 
     if (!ha->nvram) {
        printk(KERN_WARNING "Unable to allocate host NVRAM structure\n" );
-       ha->active = 0;
-       ips_free(ha);
-       scsi_unregister(sh);
-       ips_ha[index] = 0;
-       ips_sh[index] = 0;
-       return -1;
+       return ips_abort_init(ha, sh, index);
     }
 
     ha->subsys = kmalloc(sizeof(IPS_SUBSYS), GFP_KERNEL);
 
     if (!ha->subsys) {
        printk(KERN_WARNING "Unable to allocate host subsystem structure\n" );
-       ha->active = 0;
-       ips_free(ha);
-       scsi_unregister(sh);
-       ips_ha[index] = 0;
-       ips_sh[index] = 0;
-       return -1;
-    }
-
-    ha->dummy = kmalloc(sizeof(IPS_IO_CMD), GFP_KERNEL);
-
-    if (!ha->dummy) {
-       printk(KERN_WARNING "Unable to allocate host dummy structure\n" );
-       ha->active = 0;
-       ips_free(ha);
-       scsi_unregister(sh);
-       ips_ha[index] = 0;
-       ips_sh[index] = 0;
-       return -1;
+       return ips_abort_init(ha, sh, index);
     }
 
     for (count = PAGE_SIZE, ha->ioctl_order = 0;
@@ -7625,55 +7614,9 @@
     /*
      * Setup Functions
      */
-    if (IPS_IS_MORPHEUS(ha)) {
-       /* morpheus */
-       ha->func.isintr = ips_isintr_morpheus;
-       ha->func.isinit = ips_isinit_morpheus;
-       ha->func.issue = ips_issue_i2o_memio;
-       ha->func.init = ips_init_morpheus;
-       ha->func.statupd = ips_statupd_morpheus;
-       ha->func.reset = ips_reset_morpheus;
-       ha->func.intr = ips_intr_morpheus;
-       ha->func.enableint = ips_enable_int_morpheus;
-    } else if (IPS_USE_MEMIO(ha)) {
-       /* copperhead w/MEMIO */
-       ha->func.isintr = ips_isintr_copperhead_memio;
-       ha->func.isinit = ips_isinit_copperhead_memio;
-       ha->func.init = ips_init_copperhead_memio;
-       ha->func.statupd = ips_statupd_copperhead_memio;
-       ha->func.statinit = ips_statinit_memio;
-       ha->func.reset = ips_reset_copperhead_memio;
-       ha->func.intr = ips_intr_copperhead;
-       ha->func.erasebios = ips_erase_bios_memio;
-       ha->func.programbios = ips_program_bios_memio;
-       ha->func.verifybios = ips_verify_bios_memio;
-       ha->func.enableint = ips_enable_int_copperhead_memio;
-
-       if (IPS_USE_I2O_DELIVER(ha))
-          ha->func.issue = ips_issue_i2o_memio;
-       else
-          ha->func.issue = ips_issue_copperhead_memio;
-    } else {
-       /* copperhead */
-       ha->func.isintr = ips_isintr_copperhead;
-       ha->func.isinit = ips_isinit_copperhead;
-       ha->func.init = ips_init_copperhead;
-       ha->func.statupd = ips_statupd_copperhead;
-       ha->func.statinit = ips_statinit;
-       ha->func.reset = ips_reset_copperhead;
-       ha->func.intr = ips_intr_copperhead;
-       ha->func.erasebios = ips_erase_bios;
-       ha->func.programbios = ips_program_bios;
-       ha->func.verifybios = ips_verify_bios;
-       ha->func.enableint = ips_enable_int_copperhead;
-
-       if (IPS_USE_I2O_DELIVER(ha))
-          ha->func.issue = ips_issue_i2o;
-       else
-          ha->func.issue = ips_issue_copperhead;
-    }
+    ips_setup_funclist(ha);
 
-    if ( IPS_IS_MORPHEUS( ha ) ) {
+    if ( ( IPS_IS_MORPHEUS( ha ) ) || ( IPS_IS_MARCO( ha ) ) ) {
         /* If Morpheus appears dead, reset it */
         IsDead = readl( ha->mem_ptr + IPS_REG_I960_MSG1 );
         if ( IsDead == 0xDEADBEEF ) {
@@ -7691,24 +7634,14 @@
            * Initialization failed
            */
           printk(KERN_WARNING "Unable to initialize controller\n" );
-          ha->active = 0;
-          ips_free(ha);
-          scsi_unregister(sh);
-          ips_ha[index] = 0;
-          ips_sh[index] = 0;
-          return -1;
+          return ips_abort_init(ha, sh, index);
        }
     }
 
     /* Install the interrupt handler */
      if (request_irq(irq, do_ipsintr, SA_SHIRQ, ips_name, ha)) {
        printk(KERN_WARNING "Unable to install interrupt handler\n" );
-       ha->active = 0;
-       ips_free(ha);
-       scsi_unregister(sh);
-       ips_ha[index] = 0;
-       ips_sh[index] = 0;
-       return -1;
+       return ips_abort_init(ha, sh, index);
     }
 
     /*
@@ -7717,13 +7650,8 @@
     ha->max_cmds = 1;
     if (!ips_allocatescbs(ha)) {
        printk(KERN_WARNING "Unable to allocate a CCB\n" );
-       ha->active = 0;
        free_irq(ha->irq, ha);
-       ips_free(ha);
-       scsi_unregister(sh);
-       ips_ha[index] = 0;
-       ips_sh[index] = 0;
-       return -1;
+       return ips_abort_init(ha, sh, index);
     }
 
     *indexPtr = index;
@@ -7759,13 +7687,8 @@
 
     if (!ips_hainit(ha)) {
        printk(KERN_WARNING "Unable to initialize controller\n" );
-       ha->active = 0;
-       ips_free(ha);
        free_irq(ha->irq, ha);
-       scsi_unregister(sh);
-       ips_ha[index] = NULL;
-       ips_sh[index] = NULL;
-       return -1;
+       return ips_abort_init(ha, sh, index);
     }
     /* Free the temporary SCB */
     ips_deallocatescbs(ha, 1);
@@ -7773,13 +7696,8 @@
     /* allocate CCBs */
     if (!ips_allocatescbs(ha)) {
        printk(KERN_WARNING "Unable to allocate CCBs\n" );
-       ha->active = 0;
-       ips_free(ha);
        free_irq(ha->irq, ha);
-       scsi_unregister(sh);
-       ips_ha[index] = NULL;
-       ips_sh[index] = NULL;
-       return -1;
+       return ips_abort_init(ha, sh, index);
     }
 
     /* finish setting values */
diff -urN linux-2.4.21-pre5/drivers/scsi/ips.h linux-2.4.21-pre6/drivers/scsi/ips.h
--- linux-2.4.21-pre5/drivers/scsi/ips.h	Thu Nov 28 15:53:14 2002
+++ linux-2.4.21-pre6/drivers/scsi/ips.h	Wed Mar 26 16:11:38 2003
@@ -1,11 +1,12 @@
 /*****************************************************************************/
-/* ips.h -- driver for the IBM ServeRAID controller                          */
+/* ips.h -- driver for the Adaptec / IBM ServeRAID controller                */
 /*                                                                           */
 /* Written By: Keith Mitchell, IBM Corporation                               */
 /*             Jack Hammer, Adaptec, Inc.                                    */
 /*             David Jeffery, Adaptec, Inc.                                  */
 /*                                                                           */
 /* Copyright (C) 1999 IBM Corporation                                        */
+/* Copyright (C) 2003 Adaptec, Inc.                                          */ 
 /*                                                                           */
 /* 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      */
@@ -42,7 +43,7 @@
 /* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
 /*                                                                           */
 /* Bugs/Comments/Suggestions should be mailed to:                            */
-/*      ipslinux@adaptec.com     	                                         */
+/*      ipslinux@adaptec.com                                                 */
 /*                                                                           */
 /*****************************************************************************/
 
@@ -60,7 +61,6 @@
    extern int ips_queue(Scsi_Cmnd *, void (*) (Scsi_Cmnd *));
    extern int ips_biosparam(Disk *, kdev_t, int *);
    extern const char * ips_info(struct Scsi_Host *);
-   extern void do_ips(int, void *, struct pt_regs *);
 
    /*
     * Some handy macros
@@ -78,41 +78,19 @@
                                          (ha->revision_id >= IPS_REVID_CLARINETP1) && \
                                          (ha->revision_id <= IPS_REVID_CLARINETP3)) ? 1 : 0)
    #define IPS_IS_MORPHEUS(ha)         (ha->device_id == IPS_DEVICEID_MORPHEUS)
+   #define IPS_IS_MARCO(ha)            (ha->device_id == IPS_DEVICEID_MARCO)
    #define IPS_USE_I2O_DELIVER(ha)     ((IPS_IS_MORPHEUS(ha) || \
                                          (IPS_IS_TROMBONE(ha) && \
                                           (ips_force_i2o))) ? 1 : 0)
-   #define IPS_USE_I2O_STATUS(ha)      (IPS_IS_MORPHEUS(ha))
    #define IPS_USE_MEMIO(ha)           ((IPS_IS_MORPHEUS(ha) || \
                                          ((IPS_IS_TROMBONE(ha) || IPS_IS_CLARINET(ha)) && \
                                           (ips_force_memio))) ? 1 : 0)
 
-   #ifndef VIRT_TO_BUS
-      #define VIRT_TO_BUS(x)           (uint32_t) virt_to_bus((void *) x)
-   #endif
-
    #ifndef MDELAY
       #define MDELAY mdelay
    #endif
-
-   #ifndef verify_area_20
-      #define verify_area_20(t,a,sz)   (0) /* success */
-   #endif
-
-   #ifndef DECLARE_MUTEX_LOCKED
-      #define DECLARE_MUTEX_LOCKED(sem) struct semaphore sem = MUTEX_LOCKED;
-   #endif
    
    /*
-    * Lock macros
-    */
-   #define IPS_SCB_LOCK(cpu_flags)      spin_lock_irqsave(&ha->scb_lock, cpu_flags)
-   #define IPS_SCB_UNLOCK(cpu_flags)    spin_unlock_irqrestore(&ha->scb_lock, cpu_flags)
-   #define IPS_QUEUE_LOCK(queue)        spin_lock_irqsave(&(queue)->lock, (queue)->cpu_flags)
-   #define IPS_QUEUE_UNLOCK(queue)      spin_unlock_irqrestore(&(queue)->lock, (queue)->cpu_flags)
-   #define IPS_HA_LOCK(cpu_flags)       spin_lock_irqsave(&ha->ips_lock, cpu_flags)
-   #define IPS_HA_UNLOCK(cpu_flags)     spin_unlock_irqrestore(&ha->ips_lock, cpu_flags)
-
-   /*
     * Adapter address map equates
     */
    #define IPS_REG_HISR                 0x08    /* Host Interrupt Status Reg   */
@@ -171,7 +149,7 @@
    #define IPS_CMD_DCDB                 0x04
    #define IPS_CMD_DCDB_SG              0x84
    #define IPS_CMD_EXTENDED_DCDB 	    0x95
-	#define IPS_CMD_EXTENDED_DCDB_SG		 0x96
+   #define IPS_CMD_EXTENDED_DCDB_SG	    0x96
    #define IPS_CMD_CONFIG_SYNC          0x58
    #define IPS_CMD_ERROR_TABLE          0x17
    #define IPS_CMD_DOWNLOAD             0x20
@@ -185,6 +163,7 @@
    #define IPS_CSL                      0xFF
    #define IPS_POCL                     0x30
    #define IPS_NORM_STATE               0x00
+   #define IPS_MAX_ADAPTER_TYPES        3
    #define IPS_MAX_ADAPTERS             16
    #define IPS_MAX_IOCTL                1
    #define IPS_MAX_IOCTL_QUEUE          8
@@ -207,15 +186,19 @@
    #define IPS_INTR_IORL                1
    #define IPS_FFDC                     99
    #define IPS_ADAPTER_ID               0xF
-   #define IPS_VENDORID                 0x1014
+   #define IPS_VENDORID_IBM             0x1014
+   #define IPS_VENDORID_ADAPTEC         0x9005
    #define IPS_DEVICEID_COPPERHEAD      0x002E
    #define IPS_DEVICEID_MORPHEUS        0x01BD
+   #define IPS_DEVICEID_MARCO           0x0250
    #define IPS_SUBDEVICEID_4M           0x01BE
    #define IPS_SUBDEVICEID_4L           0x01BF
    #define IPS_SUBDEVICEID_4MX          0x0208
    #define IPS_SUBDEVICEID_4LX          0x020E
    #define IPS_SUBDEVICEID_5I2          0x0259
    #define IPS_SUBDEVICEID_5I1          0x0258
+   #define IPS_SUBDEVICEID_6M           0x0279
+   #define IPS_SUBDEVICEID_6I           0x028C
    #define IPS_IOCTL_SIZE               8192
    #define IPS_STATUS_SIZE              4
    #define IPS_STATUS_Q_SIZE            (IPS_MAX_CMDS+1) * IPS_STATUS_SIZE
@@ -298,6 +281,8 @@
    #define IPS_ADTYPE_SERVERAID4LX      0x0B
    #define IPS_ADTYPE_SERVERAID5I2      0x0C
    #define IPS_ADTYPE_SERVERAID5I1      0x0D
+   #define IPS_ADTYPE_SERVERAID6M       0x0E
+   #define IPS_ADTYPE_SERVERAID6I       0x0F
 
    /*
     * Adapter Command/Status Packet Definitions
@@ -369,27 +354,8 @@
    #define IPS_SCSI_MP3_AllocateSurface 0x08
 
    /*
-    * Configuration Structure Flags
-    */
-   #define IPS_CFG_USEROPT_UPDATECOUNT(cfg)   (((cfg)->UserOpt & 0xffff000) >> 16)
-   #define IPS_CFG_USEROPT_CONCURSTART(cfg)   (((cfg)->UserOpt & 0xf000) >> 12)
-   #define IPS_CFG_USEROPT_STARTUPDELAY(cfg)  (((cfg)->UserOpt & 0xf00) >> 8)
-   #define IPS_CFG_USEROPT_REARRANGE(cfg)     ((cfg)->UserOpt & 0x80)
-   #define IPS_CFG_USEROPT_CDBOOT(cfg)        ((cfg)->UserOpt & 0x40)
-   #define IPS_CFG_USEROPT_CLUSTER(cfg)       ((cfg)->UserOpt & 0x20)
-
-   /*
-    * Host adapter Flags (bit numbers)
-    */
-   #define IPS_IN_INTR                  0
-   #define IPS_IN_ABORT                 1
-   #define IPS_IN_RESET                 2
-
-   /*
     * SCB Flags
     */
-   #define IPS_SCB_ACTIVE               0x00001
-   #define IPS_SCB_WAITING              0x00002
    #define IPS_SCB_MAP_SG               0x00008
    #define IPS_SCB_MAP_SINGLE           0X00010
 
@@ -400,7 +366,6 @@
    #define IPS_COPPIOCCMD              (('C'<<8) | 66)
    #define IPS_NUMCTRLS                (('C'<<8) | 68)
    #define IPS_CTRLINFO                (('C'<<8) | 69)
-   #define IPS_FLASHBIOS               (('C'<<8) | 70)
 
    /* flashing defines */
    #define IPS_FW_IMAGE                0x00
@@ -455,7 +420,7 @@
     use_clustering : ENABLE_CLUSTERING,   \
     use_new_eh_code : 1                   \
 }
-#else
+#elif LINUX_VERSION_CODE < LinuxVersionCode(2,5,0)
  #define IPS {                            \
     next : NULL,                          \
     module : NULL,                        \
@@ -484,10 +449,39 @@
     use_clustering : ENABLE_CLUSTERING,   \
     use_new_eh_code : 1                   \
 }
+#else
+ #define IPS {                            \
+    next : NULL,                          \
+    module : NULL,                        \
+    proc_info : NULL,                     \
+    name : NULL,                          \
+    detect : ips_detect,                  \
+    release : ips_release,                \
+    info : ips_info,                      \
+    command : NULL,                       \
+    queuecommand : ips_queue,             \
+    eh_strategy_handler : NULL,           \
+    eh_abort_handler : ips_eh_abort,      \
+    eh_device_reset_handler : NULL,       \
+    eh_bus_reset_handler : NULL,          \
+    eh_host_reset_handler : ips_eh_reset, \
+    abort : NULL,                         \
+    reset : NULL,                         \
+    slave_attach : NULL,                  \
+    bios_param : ips_biosparam,           \
+    can_queue : 0,                        \
+    this_id: -1,                          \
+    sg_tablesize : IPS_MAX_SG,            \
+    cmd_per_lun: 16,                      \
+    present : 0,                          \
+    unchecked_isa_dma : 0,                \
+    use_clustering : ENABLE_CLUSTERING,   \
+    highmem_io : 1                        \
+}
 #endif
 
 /*
- * IBM PCI Raid Command Formats
+ * Raid Command Formats
  */
 typedef struct {
    uint8_t  op_code;
@@ -765,7 +759,7 @@
    uint16_t usConfigUpdateCount;
    uint8_t  ucBlkFlag;
    uint8_t  reserved;
-   uint16_t usAddrDeadDisk[IPS_MAX_CHANNELS * IPS_MAX_TARGETS];
+   uint16_t usAddrDeadDisk[IPS_MAX_CHANNELS * (IPS_MAX_TARGETS + 1)];
 } IPS_ENQ, *PIPS_ENQ;
 
 typedef struct {
@@ -841,7 +835,8 @@
    uint8_t   ReservedForOS2[8];
    uint8_t   bios_high[4];                 /* Adapter's Flashed BIOS Version   */
    uint8_t   bios_low[4];
-   uint8_t   Filler[76];
+   uint8_t   adapter_order[16];            /* BIOS Telling us the Sort Order   */
+   uint8_t   Filler[60];
 } IPS_NVRAM_P5, *PIPS_NVRAM_P5;
 
 /*--------------------------------------------------------------------------*/
@@ -1007,19 +1002,6 @@
    int   option_value;
 } IPS_OPTION;
 
-typedef struct {
-   void             *userbuffer;
-   uint32_t          usersize;
-   void             *kernbuffer;
-   uint32_t          kernsize;
-   void             *ha;
-   void             *SC;
-   void             *pt;
-   struct semaphore *sem;
-   uint32_t          offset;
-   uint32_t          retcode;
-} IPS_FLASH_DATA;
-
 /*
  * Status Info
  */
@@ -1036,8 +1018,6 @@
    struct ips_scb *head;
    struct ips_scb *tail;
    int             count;
-   unsigned long   cpu_flags;
-   spinlock_t      lock;
 } ips_scb_queue_t;
 
 /*
@@ -1047,13 +1027,10 @@
    Scsi_Cmnd      *head;
    Scsi_Cmnd      *tail;
    int             count;
-   unsigned long   cpu_flags;
-   spinlock_t      lock;
 } ips_wait_queue_t;
 
 typedef struct ips_copp_wait_item {
    Scsi_Cmnd                 *scsi_cmd;
-   struct semaphore          *sem;
    struct ips_copp_wait_item *next;
 } ips_copp_wait_item_t;
 
@@ -1061,8 +1038,6 @@
    struct ips_copp_wait_item *head;
    struct ips_copp_wait_item *tail;
    int                        count;
-   unsigned long              cpu_flags;
-   spinlock_t                 lock;
 } ips_copp_queue_t;
 
 /* forward decl for host structure */
@@ -1111,7 +1086,6 @@
    char              *ioctl_data;         /* IOCTL data area            */
    uint32_t           ioctl_datasize;     /* IOCTL data size            */
    uint32_t           cmd_in_progress;    /* Current command in progress*/
-   unsigned long      flags;              /* HA flags                   */
    uint8_t            waitflag;           /* are we waiting for cmd     */
    uint8_t            active;
    int                ioctl_reset;        /* IOCTL Requested Reset Flag */
@@ -1122,7 +1096,6 @@
    uint8_t            slot_num;           /* PCI Slot Number            */
    uint16_t           subdevice_id;       /* Subsystem device ID        */
    uint8_t            ioctl_order;        /* Number of pages in ioctl   */
-   uint8_t            reserved2;          /* Empty                      */
    uint8_t            bios_version[8];    /* BIOS Revision              */
    uint32_t           mem_addr;           /* Memory mapped address      */
    uint32_t           io_len;             /* Size of IO Address         */
@@ -1131,14 +1104,10 @@
    char              *ioremap_ptr;        /* ioremapped memory pointer  */
    ips_hw_func_t      func;               /* hw function pointers       */
    struct pci_dev    *pcidev;             /* PCI device handle          */
-   spinlock_t         scb_lock;
-   spinlock_t         copp_lock;
-   spinlock_t         ips_lock;
-   struct semaphore   ioctl_sem;          /* Semaphore for new IOCTL's  */
-   struct semaphore   flash_ioctl_sem;    /* Semaphore for Flashing     */
    char              *flash_data;         /* Save Area for flash data   */
-   u8                 flash_order;        /* Save Area for flash size order  */
-   u32                flash_datasize;   /* Save Area for flash data size */
+   u8                 flash_order;        /* Save Area for flash size order */
+   u32                flash_datasize;     /* Save Area for flash data size */
+   uint8_t            requires_esl;       /* Requires an EraseStripeLock */
 } ips_ha_t;
 
 typedef void (*ips_scb_callback) (ips_ha_t *, struct ips_scb *);
@@ -1168,7 +1137,6 @@
    Scsi_Cmnd        *scsi_cmd;
    struct ips_scb   *q_next;
    ips_scb_callback  callback;
-   struct semaphore *sem;
    uint32_t          sg_busaddr;
    int               sg_count;
 } ips_scb_t;
@@ -1224,44 +1192,46 @@
 *
 *************************************************************************/
 
-#define IPS_VER_MAJOR 5
-#define IPS_VER_MAJOR_STRING "5"
-#define IPS_VER_MINOR 10
-#define IPS_VER_MINOR_STRING "10"
-#define IPS_VER_BUILD 21
-#define IPS_VER_BUILD_STRING "21"
-#define IPS_VER_STRING "5.10.21"
-#define IPS_LEGALCOPYRIGHT_STRING "(C) Copyright IBM Corp. 1994, 2002. All Rights Reserved."
+#define IPS_VER_MAJOR 6
+#define IPS_VER_MAJOR_STRING "6"
+#define IPS_VER_MINOR 00
+#define IPS_VER_MINOR_STRING "00"
+#define IPS_VER_BUILD 26
+#define IPS_VER_BUILD_STRING "26"
+#define IPS_VER_STRING "6.00.26"
+#define IPS_LEGALCOPYRIGHT_STRING "(C) Copyright IBM Corp. 1994, 2003. All Rights Reserved."
 #define IPS_ADAPTECCOPYRIGHT_STRING "(c) Copyright Adaptec, Inc. 2002 to present. All Rights Reserved."
-#define IPS_NT_LEGALCOPYRIGHT_STRING "(C) Copyright IBM Corp. 1994, 2002."
+#define IPS_NT_LEGALCOPYRIGHT_STRING "(C) Copyright IBM Corp. 1994, 2003."
 
 /* Version numbers for various adapters */
 #define IPS_VER_SERVERAID1 "2.25.01"
 #define IPS_VER_SERVERAID2 "2.88.13"
 #define IPS_VER_NAVAJO "2.88.13"
-#define IPS_VER_SERVERAID3 "5.10.21"
-#define IPS_VER_SERVERAID4H "5.10.21"
-#define IPS_VER_SERVERAID4MLx "5.10.21"
-#define IPS_VER_SARASOTA "5.10.21"
+#define IPS_VER_SERVERAID3 "6.00.26"
+#define IPS_VER_SERVERAID4H "6.00.26"
+#define IPS_VER_SERVERAID4MLx "6.00.26"
+#define IPS_VER_SARASOTA "6.00.26"
+#define IPS_VER_MARCO "6.00.26"
 
 /* Compatability IDs for various adapters */
 #define IPS_COMPAT_UNKNOWN ""
-#define IPS_COMPAT_CURRENT "SA510"
+#define IPS_COMPAT_CURRENT "MR600"
 #define IPS_COMPAT_SERVERAID1 "2.25.01"
 #define IPS_COMPAT_SERVERAID2 "2.88.13"
 #define IPS_COMPAT_NAVAJO  "2.88.13"
 #define IPS_COMPAT_KIOWA "2.88.13"
-#define IPS_COMPAT_SERVERAID3H  "SA510"
-#define IPS_COMPAT_SERVERAID3L  "SA510"
-#define IPS_COMPAT_SERVERAID4H  "SA510"
-#define IPS_COMPAT_SERVERAID4M  "SA510"
-#define IPS_COMPAT_SERVERAID4L  "SA510"
-#define IPS_COMPAT_SERVERAID4Mx "SA510"
-#define IPS_COMPAT_SERVERAID4Lx "SA510"
-#define IPS_COMPAT_SARASOTA     "SA510"
-#define IPS_COMPAT_BIOS "SA510"
+#define IPS_COMPAT_SERVERAID3H  "MR600"
+#define IPS_COMPAT_SERVERAID3L  "MR600"
+#define IPS_COMPAT_SERVERAID4H  "MR600"
+#define IPS_COMPAT_SERVERAID4M  "MR600"
+#define IPS_COMPAT_SERVERAID4L  "MR600"
+#define IPS_COMPAT_SERVERAID4Mx "MR600"
+#define IPS_COMPAT_SERVERAID4Lx "MR600"
+#define IPS_COMPAT_SARASOTA     "MR600"
+#define IPS_COMPAT_MARCO        "MR600"
+#define IPS_COMPAT_BIOS "MR600"
 
-#define IPS_COMPAT_MAX_ADAPTER_TYPE 14
+#define IPS_COMPAT_MAX_ADAPTER_TYPE 15
 #define IPS_COMPAT_ID_LENGTH 8
 
 #define IPS_DEFINE_COMPAT_TABLE(tablename) \
@@ -1278,8 +1248,9 @@
       IPS_COMPAT_SERVERAID4L, \
       IPS_COMPAT_SERVERAID4Mx, \
       IPS_COMPAT_SERVERAID4Lx, \
-      IPS_COMPAT_SARASOTA, \
-      IPS_COMPAT_SARASOTA \
+      IPS_COMPAT_SARASOTA,         /* one-channel variety of SARASOTA */  \
+      IPS_COMPAT_SARASOTA,         /* two-channel variety of SARASOTA */  \
+      IPS_COMPAT_MARCO \
    }
 
 
diff -urN linux-2.4.21-pre5/drivers/scsi/megaraid.c linux-2.4.21-pre6/drivers/scsi/megaraid.c
--- linux-2.4.21-pre5/drivers/scsi/megaraid.c	Wed Mar 26 16:11:29 2003
+++ linux-2.4.21-pre6/drivers/scsi/megaraid.c	Wed Mar 26 16:11:38 2003
@@ -4936,7 +4936,7 @@
 		if( ioc.mbox[0] == MEGA_MBOXCMD_PASSTHRU ) {
 			put_user( scsicmd->result, &uioc->pthru.scsistatus );
 			if (copy_to_user( uioc->pthru.reqsensearea, scsicmd->sense_buffer,
-							  MAX_REQ_SENSE_LEN ));
+							  MAX_REQ_SENSE_LEN ))
 				ret= -EFAULT;
 		} else {
 			put_user(1, &uioc->mbox[16]);	/* numstatus */
diff -urN linux-2.4.21-pre5/drivers/scsi/scsi_scan.c linux-2.4.21-pre6/drivers/scsi/scsi_scan.c
--- linux-2.4.21-pre5/drivers/scsi/scsi_scan.c	Wed Mar 26 16:11:29 2003
+++ linux-2.4.21-pre6/drivers/scsi/scsi_scan.c	Wed Mar 26 16:11:38 2003
@@ -173,7 +173,7 @@
 	{"HP", "NetRAID-4M", "*", BLIST_FORCELUN},
 	{"ADAPTEC", "AACRAID", "*", BLIST_FORCELUN},
 	{"ADAPTEC", "Adaptec 5400S", "*", BLIST_FORCELUN},
-	{"COMPAQ", "MSA1000", "*", BLIST_FORCELUN},
+	{"COMPAQ", "MSA1000", "*", BLIST_SPARSELUN | BLIST_LARGELUN},
 	{"HP", "C1557A", "*", BLIST_FORCELUN},
 	{"IBM", "AuSaV1S2", "*", BLIST_FORCELUN},
 	{"FSC", "CentricStor", "*", BLIST_SPARSELUN | BLIST_LARGELUN},
@@ -182,6 +182,11 @@
 	{"HITACHI", "DF500", "*", BLIST_SPARSELUN},
 	{"HITACHI", "DF600", "*", BLIST_SPARSELUN},
 	{"IBM", "ProFibre 4000R", "*", BLIST_SPARSELUN | BLIST_LARGELUN},
+	{"HITACHI", "OPEN-", "*", BLIST_SPARSELUN},		/* HITACHI XP Arrays */
+	{"WINSYS","FLASHDISK G6", "*", BLIST_SPARSELUN},
+	{"DotHill","SANnet RAID X300", "*", BLIST_SPARSELUN},	
+	{"SUN", "T300", "*", BLIST_SPARSELUN},
+	{"SUN", "T4", "*", BLIST_SPARSELUN},
 
 	/*
 	 * Must be at end of list...
diff -urN linux-2.4.21-pre5/drivers/scsi/sun3_NCR5380.c linux-2.4.21-pre6/drivers/scsi/sun3_NCR5380.c
--- linux-2.4.21-pre5/drivers/scsi/sun3_NCR5380.c	Fri Aug  2 17:39:44 2002
+++ linux-2.4.21-pre6/drivers/scsi/sun3_NCR5380.c	Wed Mar 26 16:11:38 2003
@@ -754,11 +754,8 @@
 static
 char *lprint_Scsi_Cmnd (Scsi_Cmnd *cmd, char *pos, char *buffer, int length);
 
-#ifndef NCR5380_proc_info
-static
-#endif
-int NCR5380_proc_info (char *buffer, char **start, off_t offset,
-		       int length, int hostno, int inout)
+static int NCR5380_proc_info (char *buffer, char **start, off_t offset,
+			      int length, int hostno, int inout)
 {
     char *pos = buffer;
     struct Scsi_Host *instance;
@@ -913,10 +910,7 @@
  */
 
 /* Only make static if a wrapper function is used */
-#ifndef NCR5380_queue_command
-static
-#endif
-int NCR5380_queue_command (Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *))
+static int NCR5380_queue_command (Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *))
 {
     SETUP_HOSTDATA(cmd->host);
     Scsi_Cmnd *tmp;
@@ -1226,8 +1220,8 @@
 					    BASR_ACK)) ==
        (BASR_PHASE_MATCH | BASR_ACK)) {
 	    printk("scsi%d: BASR %02x\n", HOSTNO, NCR5380_read(BUS_AND_STATUS_REG));
-	    printk("scsi%d: bus stuck in data phase -- probably a
- single byte overrun!\n", HOSTNO); 
+	    printk("scsi%d: bus stuck in data phase -- probably a single byte "
+		   "overrun!\n", HOSTNO);
 	    printk("not prepared for this error!\n");
 	    printk("please e-mail sammy@sammy.net with a description of how this\n");
 	    printk("error was produced.\n");
@@ -1318,11 +1312,14 @@
 	    {
 /* MS: Ignore unknown phase mismatch interrupts (caused by EOP interrupt) */
 		if (basr & BASR_PHASE_MATCH)
-		    printk(KERN_NOTICE "scsi%d: unknown interrupt, "
+		   INT_PRINTK("scsi%d: unknown interrupt, "
 			   "BASR 0x%x, MR 0x%x, SR 0x%x\n",
 			   HOSTNO, basr, NCR5380_read(MODE_REG),
 			   NCR5380_read(STATUS_REG));
 		(void) NCR5380_read(RESET_PARITY_INTERRUPT_REG);
+#ifdef SUN3_SCSI_VME
+		dregs->csr |= CSR_DMA_ENABLE;
+#endif
 	    }
 	} /* if !(SELECTION || PARITY) */
     } /* BASR & IRQ */
@@ -1332,6 +1329,9 @@
 	       "BASR 0x%X, MR 0x%X, SR 0x%x\n", HOSTNO, basr,
 	       NCR5380_read(MODE_REG), NCR5380_read(STATUS_REG));
 	(void) NCR5380_read(RESET_PARITY_INTERRUPT_REG);
+#ifdef SUN3_SCSI_VME
+		dregs->csr |= CSR_DMA_ENABLE;
+#endif
     }
     
     if (!done) {
@@ -1694,7 +1694,9 @@
 #ifndef SUPPORT_TAGS
     hostdata->busy[cmd->target] |= (1 << cmd->lun);
 #endif    
-
+#ifdef SUN3_SCSI_VME
+    dregs->csr |= CSR_INTR;
+#endif
     initialize_SCp(cmd);
 
 
@@ -1921,19 +1923,18 @@
 	 printk("scsi%d: transfer_dma without setup!\n", HOSTNO);
 	 BUG();
     }
-
     hostdata->dma_len = c;
 
     DMA_PRINTK("scsi%d: initializing DMA for %s, %d bytes %s %p\n",
 	       HOSTNO, (p & SR_IO) ? "reading" : "writing",
-	       c, (p & SR_IO) ? "to" : "from", d);
+	       c, (p & SR_IO) ? "to" : "from", *data);
 
     /* netbsd turns off ints here, why not be safe and do it too */
     save_flags(flags);
     cli();
     
     /* send start chain */
-    sun3_udc_write(UDC_CHN_START, UDC_CSR);
+    sun3scsi_dma_start(c, *data);
     
     if (p & SR_IO) {
 	    NCR5380_write(TARGET_COMMAND_REG, 1);
@@ -1949,6 +1950,10 @@
 	    NCR5380_write(START_DMA_SEND_REG, 0);
     }
 
+#ifdef SUN3_SCSI_VME
+    dregs->csr |= CSR_DMA_ENABLE;
+#endif
+
     restore_flags(flags);
 
     sun3_dma_active = 1;
@@ -1987,6 +1992,10 @@
     unsigned char phase, tmp, extended_msg[10], old_phase=0xff;
     Scsi_Cmnd *cmd = (Scsi_Cmnd *) hostdata->connected;
 
+#ifdef SUN3_SCSI_VME
+    dregs->csr |= CSR_INTR;
+#endif
+
     while (1) {
 	tmp = NCR5380_read(STATUS_REG);
 	/* We only have a valid SCSI phase when REQ is asserted */
@@ -2020,6 +2029,9 @@
 			}
 		}
 #endif
+#ifdef SUN3_SCSI_VME
+		dregs->csr |= CSR_INTR;
+#endif
 	    }
 
 	    
@@ -2341,6 +2353,9 @@
 		    /* Wait for bus free to avoid nasty timeouts */
 		    while ((NCR5380_read(STATUS_REG) & SR_BSY) && !hostdata->connected)
 		    	barrier();
+#ifdef SUN3_SCSI_VME
+		    dregs->csr |= CSR_DMA_ENABLE;
+#endif
 		    return;
 		/* 
 		 * The SCSI data pointer is *IMPLICITLY* saved on a disconnect
@@ -2678,10 +2693,7 @@
  * 	 called where the loop started in NCR5380_main().
  */
 
-#ifndef NCR5380_abort
-static
-#endif
-int NCR5380_abort (Scsi_Cmnd *cmd)
+static int NCR5380_abort (Scsi_Cmnd *cmd)
 {
     struct Scsi_Host *instance = cmd->host;
     SETUP_HOSTDATA(instance);
@@ -2875,7 +2887,7 @@
  *
  */ 
 
-int NCR5380_reset( Scsi_Cmnd *cmd, unsigned int reset_flags)
+static int NCR5380_reset( Scsi_Cmnd *cmd, unsigned int reset_flags)
 {
     SETUP_HOSTDATA(cmd->host);
     int           i;
diff -urN linux-2.4.21-pre5/drivers/scsi/sun3_scsi.c linux-2.4.21-pre6/drivers/scsi/sun3_scsi.c
--- linux-2.4.21-pre5/drivers/scsi/sun3_scsi.c	Fri Aug  2 17:39:44 2002
+++ linux-2.4.21-pre6/drivers/scsi/sun3_scsi.c	Wed Mar 26 16:11:38 2003
@@ -1,7 +1,7 @@
 /*
  * Sun3 SCSI stuff by Erik Verbruggen (erik@bigmama.xtdnet.nl)
  *
- * Sun3 DMA routines added by Sam Creasey (sammy@oh.verio.com)
+ * Sun3 DMA routines added by Sam Creasey (sammy@sammy.net)
  *
  * Adapted from mac_scsinew.c:
  */
@@ -192,8 +192,7 @@
  
 int sun3scsi_detect(Scsi_Host_Template * tpnt)
 {
-	unsigned long ioaddr, iopte;
-	int count = 0;
+	unsigned long ioaddr;
 	static int called = 0;
 	struct Scsi_Host *instance;
 
@@ -227,28 +226,9 @@
 		tpnt->this_id = 7;
 	}
 
-	/* Taken from Sammy's lance driver: */
-        /* IOBASE_SUN3_SCSI can be found within the IO pmeg with some effort */
-        for(ioaddr = 0xfe00000; ioaddr < (0xfe00000 + SUN3_PMEG_SIZE);
-            ioaddr += SUN3_PTE_SIZE) {
-
-                iopte = sun3_get_pte(ioaddr);
-                if(!(iopte & SUN3_PAGE_TYPE_IO)) /* this an io page? */
-                        continue;
-
-                if(((iopte & SUN3_PAGE_PGNUM_MASK) << PAGE_SHIFT) ==
-                   IOBASE_SUN3_SCSI) {
-                        count = 1;
-                        break;
-                }
-        }
-
-	if(!count) {
-		printk("No Sun3 NCR5380 found!\n");
-		return 0;
-	}
-
+	ioaddr = (unsigned long)ioremap(IOBASE_SUN3_SCSI, PAGE_SIZE);
 	sun3_scsi_regp = (unsigned char *)ioaddr;
+
 	dregs = (struct sun3_dma_regs *)(((unsigned char *)ioaddr) + 8);
 
 	if((udc_regs = dvma_malloc(sizeof(struct sun3_udc_regs)))
@@ -322,13 +302,17 @@
 	return 1;
 }
 
+#ifdef MODULE
 int sun3scsi_release (struct Scsi_Host *shpnt)
 {
 	if (shpnt->irq != IRQ_NONE)
 		free_irq (shpnt->irq, NULL);
 
+	iounmap((void *)sun3_scsi_regp);
+
 	return 0;
 }
+#endif
 
 #ifdef RESET_BOOT
 /*
@@ -537,6 +521,14 @@
 		return 0;
 }
 
+static inline int sun3scsi_dma_start(unsigned long count, unsigned char *data)
+{
+
+    sun3_udc_write(UDC_CHN_START, UDC_CSR);
+    
+    return 0;
+}
+
 /* clean up after our dma is done */
 static int sun3scsi_dma_finish(int write_flag)
 {
diff -urN linux-2.4.21-pre5/drivers/scsi/sun3_scsi.h linux-2.4.21-pre6/drivers/scsi/sun3_scsi.h
--- linux-2.4.21-pre5/drivers/scsi/sun3_scsi.h	Sun Feb 13 11:21:42 2000
+++ linux-2.4.21-pre6/drivers/scsi/sun3_scsi.h	Wed Mar 26 16:11:38 2003
@@ -1,7 +1,7 @@
 /*
  * Sun3 SCSI stuff by Erik Verbruggen (erik@bigmama.xtdnet.nl)
  *
- * Sun3 DMA additions by Sam Creasey (sammy@oh.verio.com)
+ * Sun3 DMA additions by Sam Creasey (sammy@sammy.net)
  *
  * Adapted from mac_scsinew.h:
  */
@@ -36,6 +36,11 @@
 #ifndef SUN3_NCR5380_H
 #define SUN3_NCR5380_H
 
+#ifndef NULL
+#define NULL 0
+#endif
+
+
 #define SUN3SCSI_PUBLIC_RELEASE 1
 
 /*
@@ -45,17 +50,19 @@
 #define IRQ_SUN3_SCSI 2
 #define IOBASE_SUN3_SCSI 0x00140000
 
-int sun3scsi_abort (Scsi_Cmnd *);
-int sun3scsi_detect (Scsi_Host_Template *);
-int sun3scsi_release (struct Scsi_Host *);
-const char *sun3scsi_info (struct Scsi_Host *);
-int sun3scsi_reset(Scsi_Cmnd *, unsigned int);
-int sun3scsi_queue_command (Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
-int sun3scsi_proc_info (char *buffer, char **start, off_t offset,
-			int length, int hostno, int inout);
+#define IOBASE_SUN3_VMESCSI 0xff200000
 
-#ifndef NULL
-#define NULL 0
+static int sun3scsi_abort (Scsi_Cmnd *);
+static int sun3scsi_detect (Scsi_Host_Template *);
+static const char *sun3scsi_info (struct Scsi_Host *);
+static int sun3scsi_reset(Scsi_Cmnd *, unsigned int);
+static int sun3scsi_queue_command (Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
+static int sun3scsi_proc_info (char *buffer, char **start, off_t offset,
+			int length, int hostno, int inout);
+#ifdef MODULE
+static int sun3scsi_release (struct Scsi_Host *);
+#else
+#define sun3scsi_release NULL
 #endif
 
 #ifndef CMD_PER_LUN
@@ -70,27 +77,36 @@
 #define SG_TABLESIZE SG_NONE
 #endif
 
+#ifndef MAX_TAGS
+#define MAX_TAGS 32
+#endif
+
 #ifndef USE_TAGGED_QUEUING
-#define	USE_TAGGED_QUEUING 0
+#define	USE_TAGGED_QUEUING 1
 #endif
 
 #include <scsi/scsicam.h>
 
+#ifdef SUN3_SCSI_VME
+#define SUN3_SCSI_NAME "Sun3 NCR5380 VME SCSI"
+#else
+#define SUN3_SCSI_NAME "Sun3 NCR5380 SCSI"
+#endif
+
 #define SUN3_NCR5380 {							\
-name:			"Sun3 NCR5380 SCSI",				\
-detect:			sun3scsi_detect,				\
-release:		sun3scsi_release,	/* Release */		\
-info:			sun3scsi_info,					\
-queuecommand:		sun3scsi_queue_command,				\
-abort:			sun3scsi_abort,			 		\
-reset:			sun3scsi_reset,					\
-bios_param:		scsicam_bios_param,	/* biosparam */		\
-can_queue:		CAN_QUEUE,		/* can queue */		\
-this_id:		7,			/* id */		\
-sg_tablesize:		SG_ALL,			/* sg_tablesize */	\
-cmd_per_lun:		CMD_PER_LUN,		/* cmd per lun */	\
-unchecked_isa_dma:	0,			/* unchecked_isa_dma */	\
-use_clustering:		DISABLE_CLUSTERING				\
+.name =			SUN3_SCSI_NAME,					\
+.detect =		sun3scsi_detect,				\
+.release =		sun3scsi_release,	/* Release */		\
+.info =			sun3scsi_info,					\
+.queuecommand =		sun3scsi_queue_command,				\
+.abort =		sun3scsi_abort,					\
+.reset =		sun3scsi_reset,					\
+.can_queue =		CAN_QUEUE,		/* can queue */		\
+.this_id =		7,			/* id */		\
+.sg_tablesize =		SG_TABLESIZE,		/* sg_tablesize */	\
+.cmd_per_lun =		CMD_PER_LUN,		/* cmd per lun */	\
+.unchecked_isa_dma =	0,			/* unchecked_isa_dma */	\
+.use_clustering =	DISABLE_CLUSTERING				\
 	}
 
 #ifndef HOSTS_C
@@ -125,13 +141,20 @@
 /* additional registers - mainly DMA control regs */
 /* these start at regbase + 8 -- directly after the NCR regs */
 struct sun3_dma_regs {
-	unsigned short vmeregs[4];  /* unimpl vme stuff */
-	unsigned short udc_data; /* udc dma data reg */
-	unsigned short udc_addr; /* uda dma addr reg */
+	unsigned short dma_addr_hi; /* vme only */
+	unsigned short dma_addr_lo; /* vme only */
+	unsigned short dma_count_hi; /* vme only */
+	unsigned short dma_count_lo; /* vme only */
+	unsigned short udc_data; /* udc dma data reg (obio only) */
+	unsigned short udc_addr; /* uda dma addr reg (obio only) */
 	unsigned short fifo_data; /* fifo data reg, holds extra byte on
 				     odd dma reads */
 	unsigned short fifo_count; 
 	unsigned short csr; /* control/status reg */
+	unsigned short bpack_hi; /* vme only */
+	unsigned short bpack_lo; /* vme only */
+	unsigned short ivect; /* vme only */
+	unsigned short fifo_count_hi; /* vme only */
 };
 
 /* ucd chip specific regs - live in dvma space */
@@ -180,11 +203,21 @@
 #define CSR_SDB_INT 0x200 /* sbc interrupt pending */
 #define CSR_DMA_INT 0x100 /* dma interrupt pending */
 
+#define CSR_LEFT 0xc0
+#define CSR_LEFT_3 0xc0
+#define CSR_LEFT_2 0x80
+#define CSR_LEFT_1 0x40
+#define CSR_PACK_ENABLE 0x20
+
+#define CSR_DMA_ENABLE 0x10
+
 #define CSR_SEND 0x8 /* 1 = send  0 = recv */
 #define CSR_FIFO 0x2 /* reset fifo */
 #define CSR_INTR 0x4 /* interrupt enable */
 #define CSR_SCSI 0x1 
 
+#define VME_DATA24 0x3d00
+
 // debugging printk's, taken from atari_scsi.h 
 /* Debugging printk definitions:
  *
@@ -366,8 +399,6 @@
 #define NCR_PRINT_STATUS(mask) \
 	((NDEBUG & (mask)) ? NCR5380_print_status(instance) : (void)0)
 
-#define NDEBUG_ANY	0xffffffff
-
 
 
 #endif /* ndef HOSTS_C */
diff -urN linux-2.4.21-pre5/drivers/scsi/sun3_scsi_vme.c linux-2.4.21-pre6/drivers/scsi/sun3_scsi_vme.c
--- linux-2.4.21-pre5/drivers/scsi/sun3_scsi_vme.c	Wed Dec 31 16:00:00 1969
+++ linux-2.4.21-pre6/drivers/scsi/sun3_scsi_vme.c	Wed Mar 26 16:11:38 2003
@@ -0,0 +1,568 @@
+/*
+ * Sun3 SCSI stuff by Erik Verbruggen (erik@bigmama.xtdnet.nl)
+ *
+ * Sun3 DMA routines added by Sam Creasey (sammy@sammy.net)
+ *
+ * VME support added by Sam Creasey
+ *
+ * Adapted from sun3_scsi.c -- see there for other headers
+ *
+ * TODO: modify this driver to support multiple Sun3 SCSI VME boards
+ *
+ */
+
+#define AUTOSENSE
+
+#include <linux/types.h>
+#include <linux/stddef.h>
+#include <linux/ctype.h>
+#include <linux/delay.h>
+
+#include <linux/module.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+#include <linux/blk.h>
+
+#include <asm/io.h>
+#include <asm/system.h>
+
+#include <asm/sun3ints.h>
+#include <asm/dvma.h>
+#include <asm/idprom.h>
+#include <asm/machines.h>
+
+#define SUN3_SCSI_VME
+
+#undef SUN3_SCSI_DEBUG
+
+/* dma on! */
+#define REAL_DMA
+
+#include "scsi.h"
+#include "hosts.h"
+#include "sun3_scsi.h"
+#include "NCR5380.h"
+#include "constants.h"
+
+extern int sun3_map_test(unsigned long, char *);
+
+#define USE_WRAPPER
+/*#define RESET_BOOT */
+#define DRIVER_SETUP
+
+#define NDEBUG 0
+
+/*
+ * BUG can be used to trigger a strange code-size related hang on 2.1 kernels
+ */
+#ifdef BUG
+#undef RESET_BOOT
+#undef DRIVER_SETUP
+#endif
+
+/* #define SUPPORT_TAGS */
+
+//#define	ENABLE_IRQ()	enable_irq( SUN3_VEC_VMESCSI0 ); 
+#define ENABLE_IRQ()
+
+
+static void scsi_sun3_intr(int irq, void *dummy, struct pt_regs *fp);
+static inline unsigned char sun3scsi_read(int reg);
+static inline void sun3scsi_write(int reg, int value);
+
+static int setup_can_queue = -1;
+MODULE_PARM(setup_can_queue, "i");
+static int setup_cmd_per_lun = -1;
+MODULE_PARM(setup_cmd_per_lun, "i");
+static int setup_sg_tablesize = -1;
+MODULE_PARM(setup_sg_tablesize, "i");
+#ifdef SUPPORT_TAGS
+static int setup_use_tagged_queuing = -1;
+MODULE_PARM(setup_use_tagged_queuing, "i");
+#endif
+static int setup_hostid = -1;
+MODULE_PARM(setup_hostid, "i");
+
+static Scsi_Cmnd *sun3_dma_setup_done = NULL;
+
+#define	AFTER_RESET_DELAY	(HZ/2)
+
+/* ms to wait after hitting dma regs */
+#define SUN3_DMA_DELAY 10
+
+/* dvma buffer to allocate -- 32k should hopefully be more than sufficient */
+#define SUN3_DVMA_BUFSIZE 0xe000
+
+/* minimum number of bytes to do dma on */
+#define SUN3_DMA_MINSIZE 128
+
+static volatile unsigned char *sun3_scsi_regp;
+static volatile struct sun3_dma_regs *dregs;
+#ifdef OLDDMA
+static unsigned char *dmabuf = NULL; /* dma memory buffer */
+#endif
+static unsigned char *sun3_dma_orig_addr = NULL;
+static unsigned long sun3_dma_orig_count = 0;
+static int sun3_dma_active = 0;
+static unsigned long last_residual = 0;
+
+/*
+ * NCR 5380 register access functions
+ */
+
+static inline unsigned char sun3scsi_read(int reg)
+{
+	return( sun3_scsi_regp[reg] );
+}
+
+static inline void sun3scsi_write(int reg, int value)
+{
+	sun3_scsi_regp[reg] = value;
+}
+
+/*
+ * XXX: status debug
+ */
+static struct Scsi_Host *default_instance;
+
+/*
+ * Function : int sun3scsi_detect(Scsi_Host_Template * tpnt)
+ *
+ * Purpose : initializes mac NCR5380 driver based on the
+ *	command line / compile time port and irq definitions.
+ *
+ * Inputs : tpnt - template for this SCSI adapter.
+ *
+ * Returns : 1 if a host adapter was found, 0 if not.
+ *
+ */
+ 
+static int sun3scsi_detect(Scsi_Host_Template * tpnt)
+{
+	unsigned long ioaddr, irq;
+	static int called = 0;
+	struct Scsi_Host *instance;
+	int i;
+	unsigned long addrs[3] = { IOBASE_SUN3_VMESCSI, 
+				   IOBASE_SUN3_VMESCSI + 0x4000,
+				   0 };
+	unsigned long vecs[3] = { SUN3_VEC_VMESCSI0,
+				  SUN3_VEC_VMESCSI1,
+				  0 };
+	/* check that this machine has an onboard 5380 */
+	switch(idprom->id_machtype) {
+	case SM_SUN3|SM_3_160:
+	case SM_SUN3|SM_3_260:
+		break;
+
+	default:
+		return 0;
+	}
+
+	if(called)
+		return 0;
+
+	tpnt->proc_name = "Sun3 5380 VME SCSI";
+
+	/* setup variables */
+	tpnt->can_queue =
+		(setup_can_queue > 0) ? setup_can_queue : CAN_QUEUE;
+	tpnt->cmd_per_lun =
+		(setup_cmd_per_lun > 0) ? setup_cmd_per_lun : CMD_PER_LUN;
+	tpnt->sg_tablesize = 
+		(setup_sg_tablesize >= 0) ? setup_sg_tablesize : SG_TABLESIZE;
+	
+	if (setup_hostid >= 0)
+		tpnt->this_id = setup_hostid;
+	else {
+		/* use 7 as default */
+		tpnt->this_id = 7;
+	}
+	
+	ioaddr = 0;
+	for(i = 0; addrs[i] != 0; i++) {
+		unsigned char x;
+		
+		ioaddr = (unsigned long)sun3_ioremap(addrs[i], PAGE_SIZE,
+						     SUN3_PAGE_TYPE_VME16);
+		irq = vecs[i];
+		sun3_scsi_regp = (unsigned char *)ioaddr;
+		
+		dregs = (struct sun3_dma_regs *)(((unsigned char *)ioaddr) + 8);
+		
+		if(sun3_map_test((unsigned long)dregs, &x)) {
+			unsigned short oldcsr;
+
+			oldcsr = dregs->csr;
+			dregs->csr = 0;
+			udelay(SUN3_DMA_DELAY);
+			if(dregs->csr == 0x1400)
+				break;
+			
+			dregs->csr = oldcsr;
+		}
+
+		iounmap((void *)ioaddr);
+		ioaddr = 0;
+	}
+
+	if(!ioaddr)
+		return 0;
+	
+#ifdef SUPPORT_TAGS
+	if (setup_use_tagged_queuing < 0)
+		setup_use_tagged_queuing = USE_TAGGED_QUEUING;
+#endif
+
+	instance = scsi_register (tpnt, sizeof(struct NCR5380_hostdata));
+	if(instance == NULL)
+		return 0;
+		
+	default_instance = instance;
+
+        instance->io_port = (unsigned long) ioaddr;
+	instance->irq = irq;
+
+	NCR5380_init(instance, 0);
+
+	instance->n_io_port = 32;
+
+        ((struct NCR5380_hostdata *)instance->hostdata)->ctrl = 0;
+
+	if (request_irq(instance->irq, scsi_sun3_intr,
+			     0, "Sun3SCSI-5380VME", NULL)) {
+#ifndef REAL_DMA
+		printk("scsi%d: IRQ%d not free, interrupts disabled\n",
+		       instance->host_no, instance->irq);
+		instance->irq = IRQ_NONE;
+#else
+		printk("scsi%d: IRQ%d not free, bailing out\n",
+		       instance->host_no, instance->irq);
+		return 0;
+#endif
+	}
+
+	printk("scsi%d: Sun3 5380 VME at port %lX irq", instance->host_no, instance->io_port);
+	if (instance->irq == IRQ_NONE)
+		printk ("s disabled");
+	else
+		printk (" %d", instance->irq);
+	printk(" options CAN_QUEUE=%d CMD_PER_LUN=%d release=%d",
+	       instance->can_queue, instance->cmd_per_lun,
+	       SUN3SCSI_PUBLIC_RELEASE);
+	printk("\nscsi%d:", instance->host_no);
+	NCR5380_print_options(instance);
+	printk("\n");
+
+	dregs->csr = 0;
+	udelay(SUN3_DMA_DELAY);
+	dregs->csr = CSR_SCSI | CSR_FIFO | CSR_INTR;
+	udelay(SUN3_DMA_DELAY);
+	dregs->fifo_count = 0;
+	dregs->fifo_count_hi = 0;
+	dregs->dma_addr_hi = 0;
+	dregs->dma_addr_lo = 0;
+	dregs->dma_count_hi = 0;
+	dregs->dma_count_lo = 0;
+
+	dregs->ivect = VME_DATA24 | (instance->irq & 0xff);
+
+	called = 1;
+
+#ifdef RESET_BOOT
+	sun3_scsi_reset_boot(instance);
+#endif
+
+	return 1;
+}
+
+#ifdef MODULE
+int sun3scsi_release (struct Scsi_Host *shpnt)
+{
+	if (shpnt->irq != IRQ_NONE)
+		free_irq (shpnt->irq, NULL);
+
+	iounmap(sun3_scsi_regp);
+
+	return 0;
+}
+#endif
+
+#ifdef RESET_BOOT
+/*
+ * Our 'bus reset on boot' function
+ */
+
+static void sun3_scsi_reset_boot(struct Scsi_Host *instance)
+{
+	unsigned long end;
+
+	NCR5380_local_declare();
+	NCR5380_setup(instance);
+	
+	/*
+	 * Do a SCSI reset to clean up the bus during initialization. No
+	 * messing with the queues, interrupts, or locks necessary here.
+	 */
+
+	printk( "Sun3 SCSI: resetting the SCSI bus..." );
+
+	/* switch off SCSI IRQ - catch an interrupt without IRQ bit set else */
+//       	sun3_disable_irq( IRQ_SUN3_SCSI );
+
+	/* get in phase */
+	NCR5380_write( TARGET_COMMAND_REG,
+		      PHASE_SR_TO_TCR( NCR5380_read(STATUS_REG) ));
+
+	/* assert RST */
+	NCR5380_write( INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_RST );
+
+	/* The min. reset hold time is 25us, so 40us should be enough */
+	udelay( 50 );
+
+	/* reset RST and interrupt */
+	NCR5380_write( INITIATOR_COMMAND_REG, ICR_BASE );
+	NCR5380_read( RESET_PARITY_INTERRUPT_REG );
+
+	for( end = jiffies + AFTER_RESET_DELAY; time_before(jiffies, end); )
+		barrier();
+
+	/* switch on SCSI IRQ again */
+//       	sun3_enable_irq( IRQ_SUN3_SCSI );
+
+	printk( " done\n" );
+}
+#endif
+
+static const char * sun3scsi_info (struct Scsi_Host *spnt) {
+    return "";
+}
+
+// safe bits for the CSR
+#define CSR_GOOD 0x060f
+
+static void scsi_sun3_intr(int irq, void *dummy, struct pt_regs *fp)
+{
+	unsigned short csr = dregs->csr;
+
+	dregs->csr &= ~CSR_DMA_ENABLE;
+
+
+#ifdef SUN3_SCSI_DEBUG
+	printk("scsi_intr csr %x\n", csr);
+#endif
+
+	if(csr & ~CSR_GOOD) {
+		if(csr & CSR_DMA_BUSERR) {
+			printk("scsi%d: bus error in dma\n", default_instance->host_no);
+#ifdef SUN3_SCSI_DEBUG
+			printk("scsi: residual %x count %x addr %p dmaaddr %x\n", 
+			       dregs->fifo_count,
+			       dregs->dma_count_lo | (dregs->dma_count_hi << 16),
+			       sun3_dma_orig_addr,
+			       dregs->dma_addr_lo | (dregs->dma_addr_hi << 16));
+#endif
+		}
+
+		if(csr & CSR_DMA_CONFLICT) {
+			printk("scsi%d: dma conflict\n", default_instance->host_no);
+		}
+	}
+
+	if(csr & (CSR_SDB_INT | CSR_DMA_INT))
+		NCR5380_intr(irq, dummy, fp);
+}
+
+/*
+ * Debug stuff - to be called on NMI, or sysrq key. Use at your own risk; 
+ * reentering NCR5380_print_status seems to have ugly side effects
+ */
+
+/* this doesn't seem to get used at all -- sam */
+#if 0
+void sun3_sun3_debug (void)
+{
+	unsigned long flags;
+	NCR5380_local_declare();
+
+	if (default_instance) {
+			save_flags(flags);
+			cli();
+			NCR5380_print_status(default_instance);
+			restore_flags(flags);
+	}
+}
+#endif
+
+
+/* sun3scsi_dma_setup() -- initialize the dma controller for a read/write */
+static unsigned long sun3scsi_dma_setup(void *data, unsigned long count, int write_flag)
+{
+	void *addr;
+
+	if(sun3_dma_orig_addr != NULL)
+		dvma_unmap(sun3_dma_orig_addr);
+
+//	addr = sun3_dvma_page((unsigned long)data, (unsigned long)dmabuf);
+	addr = (void *)dvma_map_vme((unsigned long) data, count);
+		
+	sun3_dma_orig_addr = addr;
+	sun3_dma_orig_count = count;
+	
+#ifdef SUN3_SCSI_DEBUG
+	printk("scsi: dma_setup addr %p count %x\n", addr, count);
+#endif
+
+//	dregs->fifo_count = 0;
+#if 0	
+	/* reset fifo */
+	dregs->csr &= ~CSR_FIFO;
+	dregs->csr |= CSR_FIFO;
+#endif	
+	/* set direction */
+	if(write_flag)
+		dregs->csr |= CSR_SEND;
+	else
+		dregs->csr &= ~CSR_SEND;
+	
+	/* reset fifo */
+//	dregs->csr &= ~CSR_FIFO;
+//	dregs->csr |= CSR_FIFO;
+
+	dregs->csr |= CSR_PACK_ENABLE;
+
+	dregs->dma_addr_hi = ((unsigned long)addr >> 16);
+	dregs->dma_addr_lo = ((unsigned long)addr & 0xffff);
+	
+	dregs->dma_count_hi = 0;
+	dregs->dma_count_lo = 0;
+	dregs->fifo_count_hi = 0;
+	dregs->fifo_count = 0;
+		
+#ifdef SUN3_SCSI_DEBUG
+	printk("scsi: dma_setup done csr %x\n", dregs->csr);
+#endif
+       	return count;
+
+}
+
+static inline unsigned long sun3scsi_dma_residual(struct Scsi_Host *instance)
+{
+	return last_residual;
+}
+
+static inline unsigned long sun3scsi_dma_xfer_len(unsigned long wanted, Scsi_Cmnd *cmd,
+				    int write_flag)
+{
+	if((cmd->request.cmd == 0) || (cmd->request.cmd == 1))
+ 		return wanted;
+	else
+		return 0;
+}
+
+static int sun3scsi_dma_start(unsigned long count, char *data)
+{
+	
+	unsigned short csr;
+
+	csr = dregs->csr;
+#ifdef SUN3_SCSI_DEBUG
+	printk("scsi: dma_start data %p count %x csr %x fifo %x\n", data, count, csr, dregs->fifo_count);
+#endif
+	
+	dregs->dma_count_hi = (sun3_dma_orig_count >> 16);
+	dregs->dma_count_lo = (sun3_dma_orig_count & 0xffff);
+
+	dregs->fifo_count_hi = (sun3_dma_orig_count >> 16);
+	dregs->fifo_count = (sun3_dma_orig_count & 0xffff);
+
+//	if(!(csr & CSR_DMA_ENABLE))
+//		dregs->csr |= CSR_DMA_ENABLE;
+
+	return 0;
+}
+
+/* clean up after our dma is done */
+static int sun3scsi_dma_finish(int write_flag)
+{
+	unsigned short fifo;
+	int ret = 0;
+	
+	sun3_dma_active = 0;
+
+	dregs->csr &= ~CSR_DMA_ENABLE;
+	
+	fifo = dregs->fifo_count;
+	if(write_flag) {
+		if((fifo > 0) && (fifo < sun3_dma_orig_count))
+			fifo++;
+	}
+
+	last_residual = fifo;
+#ifdef SUN3_SCSI_DEBUG
+	printk("scsi: residual %x total %x\n", fifo, sun3_dma_orig_count);
+#endif
+	/* empty bytes from the fifo which didn't make it */
+	if((!write_flag) && (dregs->csr & CSR_LEFT)) {
+		unsigned char *vaddr;
+
+#ifdef SUN3_SCSI_DEBUG
+		printk("scsi: got left over bytes\n");
+#endif
+
+		vaddr = (unsigned char *)dvma_vmetov(sun3_dma_orig_addr);
+		
+		vaddr += (sun3_dma_orig_count - fifo);
+		vaddr--;
+		
+		switch(dregs->csr & CSR_LEFT) {
+		case CSR_LEFT_3:
+			*vaddr = (dregs->bpack_lo & 0xff00) >> 8;
+			vaddr--;
+			
+		case CSR_LEFT_2:
+			*vaddr = (dregs->bpack_hi & 0x00ff);
+			vaddr--;
+			
+		case CSR_LEFT_1:
+			*vaddr = (dregs->bpack_hi & 0xff00) >> 8;
+			break;
+		}
+		
+		
+	}
+
+	dvma_unmap(sun3_dma_orig_addr);
+	sun3_dma_orig_addr = NULL;
+
+	dregs->dma_addr_hi = 0;
+	dregs->dma_addr_lo = 0;
+	dregs->dma_count_hi = 0;
+	dregs->dma_count_lo = 0;
+
+	dregs->fifo_count = 0;
+	dregs->fifo_count_hi = 0;
+
+	dregs->csr &= ~CSR_SEND;
+	
+//	dregs->csr |= CSR_DMA_ENABLE;
+	
+#if 0
+	/* reset fifo */
+	dregs->csr &= ~CSR_FIFO;
+	dregs->csr |= CSR_FIFO;
+#endif	
+	sun3_dma_setup_done = NULL;
+
+	return ret;
+
+}
+
+#include "sun3_NCR5380.c"
+
+static Scsi_Host_Template driver_template = SUN3_NCR5380;
+
+#include "scsi_module.c"
+
diff -urN linux-2.4.21-pre5/drivers/scsi/sym53c8xx.c linux-2.4.21-pre6/drivers/scsi/sym53c8xx.c
--- linux-2.4.21-pre5/drivers/scsi/sym53c8xx.c	Thu Nov 28 15:53:14 2002
+++ linux-2.4.21-pre6/drivers/scsi/sym53c8xx.c	Wed Mar 26 16:11:38 2003
@@ -7005,7 +7005,7 @@
 			INW (nc_sist);
 		}
 		else if (istat & DIP) {
-			if (INB (nc_dstat) & ABRT);
+			if (INB (nc_dstat) & ABRT)
 				break;
 		}
 		UDELAY(5);
diff -urN linux-2.4.21-pre5/drivers/scsi/sym53c8xx_2/sym_hipd.c linux-2.4.21-pre6/drivers/scsi/sym53c8xx_2/sym_hipd.c
--- linux-2.4.21-pre5/drivers/scsi/sym53c8xx_2/sym_hipd.c	Fri Aug  2 17:39:44 2002
+++ linux-2.4.21-pre6/drivers/scsi/sym53c8xx_2/sym_hipd.c	Wed Mar 26 16:11:38 2003
@@ -234,7 +234,7 @@
 			INW (nc_sist);
 		}
 		else if (istat & DIP) {
-			if (INB (nc_dstat) & ABRT);
+			if (INB (nc_dstat) & ABRT)
 				break;
 		}
 		UDELAY(5);
diff -urN linux-2.4.21-pre5/drivers/sound/Config.in linux-2.4.21-pre6/drivers/sound/Config.in
--- linux-2.4.21-pre5/drivers/sound/Config.in	Wed Mar 26 16:11:29 2003
+++ linux-2.4.21-pre6/drivers/sound/Config.in	Wed Mar 26 16:11:38 2003
@@ -175,6 +175,7 @@
 
    dep_tristate '    100% Sound Blaster compatibles (SB16/32/64, ESS, Jazz16) support' CONFIG_SOUND_SB $CONFIG_SOUND_OSS
    dep_tristate '    AWE32 synth' CONFIG_SOUND_AWE32_SYNTH $CONFIG_SOUND_OSS
+   dep_tristate '    XpressAudio Sound Blaster emulation' CONFIG_SOUND_KAHLUA $CONFIG_SOUND_SB $CONFIG_SOUND_OSS
    dep_tristate '    Full support for Turtle Beach WaveFront (Tropez Plus, Tropez, Maui) synth/soundcards' CONFIG_SOUND_WAVEFRONT $CONFIG_SOUND_OSS m
    dep_tristate '    Limited support for Turtle Beach Wave Front (Maui, Tropez) synthesizers' CONFIG_SOUND_MAUI $CONFIG_SOUND_OSS
    if [ "$CONFIG_SOUND_MAUI" = "y" ]; then
diff -urN linux-2.4.21-pre5/drivers/sound/Makefile linux-2.4.21-pre6/drivers/sound/Makefile
--- linux-2.4.21-pre5/drivers/sound/Makefile	Wed Mar 26 16:11:29 2003
+++ linux-2.4.21-pre6/drivers/sound/Makefile	Wed Mar 26 16:11:38 2003
@@ -33,6 +33,7 @@
 obj-$(CONFIG_SOUND_MSS)		+= ad1848.o
 obj-$(CONFIG_SOUND_PAS)		+= pas2.o sb.o sb_lib.o uart401.o
 obj-$(CONFIG_SOUND_SB)		+= sb.o sb_lib.o uart401.o
+obj-$(CONFIG_SOUND_KAHLUA)	+= kahlua.o
 obj-$(CONFIG_SOUND_WAVEFRONT)	+= wavefront.o
 obj-$(CONFIG_SOUND_MAUI)	+= maui.o mpu401.o
 obj-$(CONFIG_SOUND_MPU401)	+= mpu401.o
diff -urN linux-2.4.21-pre5/drivers/sound/kahlua.c linux-2.4.21-pre6/drivers/sound/kahlua.c
--- linux-2.4.21-pre5/drivers/sound/kahlua.c	Wed Dec 31 16:00:00 1969
+++ linux-2.4.21-pre6/drivers/sound/kahlua.c	Wed Mar 26 16:11:38 2003
@@ -0,0 +1,230 @@
+/*
+ *	Initialisation code for Cyrix/NatSemi VSA1 softaudio
+ *
+ *	(C) Copyright 2003 Red Hat Inc <alan@redhat.com>
+ *
+ * XpressAudio(tm) is used on the Cyrix MediaGX (now NatSemi Geode) systems.
+ * The older version (VSA1) provides fairly good soundblaster emulation
+ * although there are a couple of bugs: large DMA buffers break record,
+ * and the MPU event handling seems suspect. VSA2 allows the native driver
+ * to control the AC97 audio engine directly and requires a different driver.
+ *
+ * Thanks to National Semiconductor for providing the needed information
+ * on the XpressAudio(tm) internals.
+ *
+ * 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.
+ *
+ * For the avoidance of doubt the "preferred form" of this code is one which
+ * is in an open non patent encumbered format. Where cryptographic key signing
+ * forms part of the process of creating an executable the information
+ * including keys needed to generate an equivalently functional executable
+ * are deemed to be part of the source code.
+ *
+ * TO DO:
+ *	Investigate whether we can portably support Cognac (5520) in the
+ *	same manner.
+ */
+
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+
+#include "sound_config.h"
+
+#include "sb.h"
+
+/*
+ *	Read a soundblaster compatible mixer register.
+ *	In this case we are actually reading an SMI trap
+ *	not real hardware.
+ */
+
+static u8 __devinit mixer_read(unsigned long io, u8 reg)
+{
+	outb(reg, io + 4);
+	udelay(20);
+	reg = inb(io + 5);
+	udelay(20);
+	return reg;
+}
+
+static int __devinit probe_one(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+	struct address_info *hw_config;
+	unsigned long base;
+	void *mem;
+	unsigned long io;
+	u16 map;
+	u8 irq, dma8, dma16;
+	int oldquiet;
+	extern int sb_be_quiet;
+		
+	base = pci_resource_start(pdev, 0);
+	if(base == 0UL)
+		return 1;
+	
+	mem = ioremap(base, 128);
+	if(mem == 0UL)
+		return 1;
+	map = readw(mem + 0x18);	/* Read the SMI enables */
+	iounmap(mem);
+	
+	/* Map bits
+		0:1	* 0x20 + 0x200 = sb base
+		2	sb enable
+		3	adlib enable
+		5	MPU enable 0x330
+		6	MPU enable 0x300
+		
+	   The other bits may be used internally so must be masked */
+
+	io = 0x220 + 0x20 * (map & 3);	   
+	
+	if(map & (1<<2))
+		printk(KERN_INFO "kahlua: XpressAudio at 0x%lx\n", io);
+	else
+		return 1;
+		
+	if(map & (1<<5))
+		printk(KERN_INFO "kahlua: MPU at 0x300\n");
+	else if(map & (1<<6))
+		printk(KERN_INFO "kahlua: MPU at 0x330\n");
+	
+	irq = mixer_read(io, 0x80) & 0x0F;
+	dma8 = mixer_read(io, 0x81);
+
+	// printk("IRQ=%x MAP=%x DMA=%x\n", irq, map, dma8);
+	
+	if(dma8 & 0x20)
+		dma16 = 5;
+	else if(dma8 & 0x40)
+		dma16 = 6;
+	else if(dma8 & 0x80)
+		dma16 = 7;
+	else
+	{
+		printk(KERN_ERR "kahlua: No 16bit DMA enabled.\n");
+		return 1;
+	}
+		
+	if(dma8 & 0x01)
+		dma8 = 0;
+	else if(dma8 & 0x02)
+		dma8 = 1;
+	else if(dma8 & 0x08)
+		dma8 = 3;
+	else
+	{
+		printk(KERN_ERR "kahlua: No 8bit DMA enabled.\n");
+		return 1;
+	}
+	
+	if(irq & 1)
+		irq = 9;
+	else if(irq & 2)
+		irq = 5;
+	else if(irq & 4)
+		irq = 7;
+	else if(irq & 8)
+		irq = 10;
+	else
+	{
+		printk(KERN_ERR "kahlua: SB IRQ not set.\n");
+		return 1;
+	}
+	
+	printk(KERN_INFO "kahlua: XpressAudio on IRQ %d, DMA %d, %d\n",
+		irq, dma8, dma16);
+	
+	hw_config = kmalloc(sizeof(struct address_info), GFP_KERNEL);
+	if(hw_config == NULL)
+	{
+		printk(KERN_ERR "kahlua: out of memory.\n");
+		return 1;
+	}
+	memset(hw_config, 0, sizeof(*hw_config));
+	
+	pci_set_drvdata(pdev, hw_config);
+	
+	hw_config->io_base = io;
+	hw_config->irq = irq;
+	hw_config->dma = dma8;
+	hw_config->dma2 = dma16;
+	hw_config->name = "Cyrix XpressAudio";
+	hw_config->driver_use_1 = SB_NO_MIDI | SB_PCI_IRQ;
+	
+	if(sb_dsp_detect(hw_config, 0, 0, NULL)==0)
+	{
+		printk(KERN_ERR "kahlua: audio not responding.\n");
+		return 1;
+	}
+
+	oldquiet = sb_be_quiet;	
+	sb_be_quiet = 1;
+	if(sb_dsp_init(hw_config, THIS_MODULE))
+	{
+		sb_be_quiet = oldquiet;
+		pci_set_drvdata(pdev, NULL);
+		kfree(hw_config);
+		return 1;
+	}
+	sb_be_quiet = oldquiet;
+	
+	return 0;
+}
+
+static void __exit remove_one(struct pci_dev *pdev)
+{
+	struct address_info *hw_config = pci_get_drvdata(pdev);
+	sb_dsp_unload(hw_config, 0);
+	pci_set_drvdata(pdev, NULL);
+	kfree(hw_config);
+}
+
+MODULE_AUTHOR("Alan Cox");
+MODULE_DESCRIPTION("Kahlua VSA1 PCI Audio");
+MODULE_LICENSE("GPL");
+
+/*
+ *	5530 only. The 5510/5520 decode is different.
+ */
+
+static struct pci_device_id id_tbl[] __devinitdata = {
+	{ PCI_VENDOR_ID_CYRIX, PCI_DEVICE_ID_CYRIX_5530_AUDIO, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
+	{ }
+};
+
+MODULE_DEVICE_TABLE(pci, id_tbl);
+
+static struct pci_driver kahlua_driver = {
+	name:		"kahlua",
+	id_table:	id_tbl,
+	probe:		probe_one,
+	remove:         __devexit_p(remove_one),
+};
+
+
+static int __init kahlua_init_module(void)
+{
+	printk(KERN_INFO "Cyrix Kahlua VSA1 XpressAudio support (c) Copyright 2003 Red Hat Inc\n");
+	return pci_module_init(&kahlua_driver);
+}
+
+static void __exit kahlua_cleanup_module(void)
+{
+	return pci_unregister_driver(&kahlua_driver);
+}
+
+
+module_init(kahlua_init_module);
+module_exit(kahlua_cleanup_module);
+
diff -urN linux-2.4.21-pre5/drivers/sound/maestro.c linux-2.4.21-pre6/drivers/sound/maestro.c
--- linux-2.4.21-pre5/drivers/sound/maestro.c	Fri Aug  2 17:39:44 2002
+++ linux-2.4.21-pre6/drivers/sound/maestro.c	Wed Mar 26 16:11:38 2003
@@ -3359,7 +3359,7 @@
 	/* check to see if we have a capabilities list in
 		the config register */
 	pci_read_config_word(pcidev, PCI_STATUS, &w);
-	if(! w & PCI_STATUS_CAP_LIST) return 0;
+	if(!(w & PCI_STATUS_CAP_LIST)) return 0;
 
 	/* walk the list, starting at the head. */
 	pci_read_config_byte(pcidev,PCI_CAPABILITY_LIST,&next);
diff -urN linux-2.4.21-pre5/drivers/sound/mpu401.c linux-2.4.21-pre6/drivers/sound/mpu401.c
--- linux-2.4.21-pre5/drivers/sound/mpu401.c	Fri Aug  2 17:39:44 2002
+++ linux-2.4.21-pre6/drivers/sound/mpu401.c	Wed Mar 26 16:11:38 2003
@@ -1766,8 +1766,8 @@
 
 static struct address_info cfg;
 
-static int __initdata io = -1;
-static int __initdata irq = -1;
+static int io = -1;
+static int irq = -1;
 
 MODULE_PARM(irq, "i");
 MODULE_PARM(io, "i");
diff -urN linux-2.4.21-pre5/drivers/sound/nec_vrc5477.c linux-2.4.21-pre6/drivers/sound/nec_vrc5477.c
--- linux-2.4.21-pre5/drivers/sound/nec_vrc5477.c	Fri Aug  2 17:39:44 2002
+++ linux-2.4.21-pre6/drivers/sound/nec_vrc5477.c	Wed Mar 26 16:11:38 2003
@@ -1187,7 +1187,7 @@
 #endif
 
 		count -= copyCount;
-		totalCopyCount =+ copyCount;
+		totalCopyCount += copyCount;
 		avail -= copyFragCount;
 		totalCopyFragCount += copyFragCount;
 
diff -urN linux-2.4.21-pre5/drivers/sound/sscape.c linux-2.4.21-pre6/drivers/sound/sscape.c
--- linux-2.4.21-pre5/drivers/sound/sscape.c	Sun Sep 30 12:26:08 2001
+++ linux-2.4.21-pre6/drivers/sound/sscape.c	Wed Mar 26 16:11:39 2003
@@ -668,7 +668,7 @@
 			break;
 		}
 	}
-	if (hw_config->irq > 15 || (regs[4] = irq_bits == 0xff))
+	if (hw_config->irq > 15 || ((regs[4] = irq_bits) == 0xff))
 	{
 		printk(KERN_ERR "Invalid IRQ%d\n", hw_config->irq);
 		return;
diff -urN linux-2.4.21-pre5/drivers/sound/trident.c linux-2.4.21-pre6/drivers/sound/trident.c
--- linux-2.4.21-pre5/drivers/sound/trident.c	Wed Mar 26 16:11:29 2003
+++ linux-2.4.21-pre6/drivers/sound/trident.c	Wed Mar 26 16:11:39 2003
@@ -223,7 +223,7 @@
 #define TRIDENT_STATE_MAGIC	0x63657373 /* "cess" */
 
 #define TRIDENT_DMA_MASK	0x3fffffff /* DMA buffer mask for pci_alloc_consist */
-#define ALI_DMA_MASK		0xffffffff /* ALI Tridents lack the 30-bit limitation */
+#define ALI_DMA_MASK		0x7fffffff /* ALI Tridents have 31-bit DMA. Wow. */
 
 #define NR_HW_CH		32
 
diff -urN linux-2.4.21-pre5/drivers/sound/via82cxxx_audio.c linux-2.4.21-pre6/drivers/sound/via82cxxx_audio.c
--- linux-2.4.21-pre5/drivers/sound/via82cxxx_audio.c	Wed Mar 26 16:11:29 2003
+++ linux-2.4.21-pre6/drivers/sound/via82cxxx_audio.c	Wed Mar 26 16:11:39 2003
@@ -146,6 +146,12 @@
 #define VIA_INTR_FM			((1<<2) |  (1<<6) | (1<<10))
 #define VIA_INTR_MASK		(VIA_INTR_OUT | VIA_INTR_IN | VIA_INTR_FM)
 
+/* Newer VIA we need to monitor the low 3 bits of each channel. This
+   mask covers the channels we don't yet use as well 
+ */
+ 
+#define VIA_NEW_INTR_MASK		0x77077777UL
+
 /* VIA_BASE0_AUDIO_xxx_CHAN_TYPE bits */
 #define VIA_IRQ_ON_FLAG			(1<<0)	/* int on each flagged scatter block */
 #define VIA_IRQ_ON_EOL			(1<<1)	/* int at end of scatter list */
@@ -300,12 +306,20 @@
 	unsigned legacy: 1;	/* Has legacy ports */
 	unsigned intmask: 1;	/* Needs int bits */
 	unsigned sixchannel: 1;	/* 8233/35 with 6 channel support */
+	unsigned volume: 1;
 
 	int locked_rate : 1;
+	
+	int mixer_vol;		/* 8233/35 volume  - not yet implemented */
 
 	struct semaphore syscall_sem;
 	struct semaphore open_sem;
 
+	/* The 8233/8235 have 4 DX audio channels, two record and
+	   one six channel out. We bind ch_in to DX 1, ch_out to multichannel
+	   and ch_fm to DX 2. DX 3 and REC0/REC1 are unused at the
+	   moment */
+	   
 	struct via_channel ch_in;
 	struct via_channel ch_out;
 	struct via_channel ch_fm;
@@ -605,6 +619,9 @@
 {
 	struct via_info *card = ac97->private_data;
 	int rate_reg;
+	u32 dacp;
+	u32 mast_vol, phone_vol, mono_vol, pcm_vol;
+	u32 mute_vol = 0x8000;	/* The mute volume? -- Seems to work! */
 
 	DPRINTK ("ENTER, rate = %d\n", rate);
 
@@ -621,16 +638,32 @@
 	rate_reg = chan->is_record ? AC97_PCM_LR_ADC_RATE :
 			    AC97_PCM_FRONT_DAC_RATE;
 
-	via_ac97_write_reg (ac97, AC97_POWER_CONTROL,
-		(via_ac97_read_reg (ac97, AC97_POWER_CONTROL) & ~0x0200) |
-		0x0200);
+	/* Save current state */
+	dacp=via_ac97_read_reg(ac97, AC97_POWER_CONTROL);
+	mast_vol = via_ac97_read_reg(ac97, AC97_MASTER_VOL_STEREO);
+	mono_vol = via_ac97_read_reg(ac97, AC97_MASTER_VOL_MONO);
+	phone_vol = via_ac97_read_reg(ac97, AC97_HEADPHONE_VOL);
+	pcm_vol = via_ac97_read_reg(ac97, AC97_PCMOUT_VOL);
+	/* Mute - largely reduces popping */
+	via_ac97_write_reg(ac97, AC97_MASTER_VOL_STEREO, mute_vol);
+	via_ac97_write_reg(ac97, AC97_MASTER_VOL_MONO, mute_vol);
+	via_ac97_write_reg(ac97, AC97_HEADPHONE_VOL, mute_vol);
+       	via_ac97_write_reg(ac97, AC97_PCMOUT_VOL, mute_vol);
+	/* Power down the DAC */
+	via_ac97_write_reg(ac97, AC97_POWER_CONTROL, dacp|0x0200);
 
+        /* Set new rate */
 	via_ac97_write_reg (ac97, rate_reg, rate);
 
-	via_ac97_write_reg (ac97, AC97_POWER_CONTROL,
-		via_ac97_read_reg (ac97, AC97_POWER_CONTROL) & ~0x0200);
-
-	udelay (10);
+	/* Power DAC back up */
+	via_ac97_write_reg(ac97, AC97_POWER_CONTROL, dacp);
+	udelay (200); /* reduces popping */
+
+	/* Restore volumes */
+	via_ac97_write_reg(ac97, AC97_MASTER_VOL_STEREO, mast_vol);
+	via_ac97_write_reg(ac97, AC97_MASTER_VOL_MONO, mono_vol);
+	via_ac97_write_reg(ac97, AC97_HEADPHONE_VOL, phone_vol);
+	via_ac97_write_reg(ac97, AC97_PCMOUT_VOL, pcm_vol);
 
 	/* the hardware might return a value different than what we
 	 * passed to it, so read the rate value back from hardware
@@ -1555,7 +1588,30 @@
 
 	rc = via_syscall_down (card, nonblock);
 	if (rc) goto out;
-
+	
+#if 0
+	/*
+	 *	Intercept volume control on 8233 and 8235
+	 */
+	if(card->volume)
+	{
+		switch(cmd)
+		{
+			case SOUND_MIXER_READ_VOLUME:
+				return card->mixer_vol;
+			case SOUND_MIXER_WRITE_VOLUME:
+			{
+				int v;
+				if(get_user(v, (int *)arg))
+				{
+					rc = -EFAULT;
+					goto out;
+				}
+				card->mixer_vol = v;
+			}
+		}
+	}		
+#endif
 	rc = codec->mixer_ioctl(codec, cmd, arg);
 
 	up (&card->syscall_sem);
@@ -1893,7 +1949,16 @@
 static void via_new_interrupt(int irq, void *dev_id, struct pt_regs *regs)
 {
 	struct via_info *card = dev_id;
+	u32 status32;
 
+	/* to minimize interrupt sharing costs, we use the SGD status
+	 * shadow register to check the status of all inputs and
+	 * outputs with a single 32-bit bus read.  If no interrupt
+	 * conditions are flagged, we exit immediately
+	 */
+	status32 = inl (card->baseaddr + VIA_BASE0_SGD_STATUS_SHADOW);
+	if (!(status32 & VIA_NEW_INTR_MASK))
+		return;
 	/*
 	 * goes away completely on UP
 	 */
diff -urN linux-2.4.21-pre5/drivers/usb/CDCEther.c linux-2.4.21-pre6/drivers/usb/CDCEther.c
--- linux-2.4.21-pre5/drivers/usb/CDCEther.c	Wed Mar 26 16:11:29 2003
+++ linux-2.4.21-pre6/drivers/usb/CDCEther.c	Wed Mar 26 16:11:39 2003
@@ -562,7 +562,7 @@
 			MODE_FLAG_DIRECTED |
 			MODE_FLAG_BROADCAST |
 			MODE_FLAG_MULTICAST;
-		buff = kmalloc(6 * net->mc_count, in_interrupt() ? GFP_ATOMIC : GFP_KERNEL);
+		buff = kmalloc(6 * net->mc_count, GFP_ATOMIC);
 		for (i = 0, mclist = net->mc_list;
 			mclist && i < net->mc_count;
 			i++, mclist = mclist->next) {
diff -urN linux-2.4.21-pre5/drivers/usb/Config.in linux-2.4.21-pre6/drivers/usb/Config.in
--- linux-2.4.21-pre5/drivers/usb/Config.in	Wed Mar 26 16:11:29 2003
+++ linux-2.4.21-pre6/drivers/usb/Config.in	Wed Mar 26 16:11:39 2003
@@ -16,17 +16,7 @@
       define_bool CONFIG_USB_BANDWIDTH n
    fi
 
-   comment 'USB Host Controller Drivers'
-   source drivers/usb/hcd/Config.in
-   if [ "$CONFIG_USB_UHCI_ALT" != "y" ]; then
-      dep_tristate '  UHCI (Intel PIIX4, VIA, ...) support' CONFIG_USB_UHCI $CONFIG_USB
-   fi
-   if [ "$CONFIG_USB_UHCI" != "y" ]; then
-      dep_tristate '  UHCI Alternate Driver (JE) support' CONFIG_USB_UHCI_ALT $CONFIG_USB
-   else
-      define_bool CONFIG_USB_UHCI_ALT n
-   fi
-   dep_tristate '  OHCI (Compaq, iMacs, OPTi, SiS, ALi, ...) support' CONFIG_USB_OHCI $CONFIG_USB
+   source drivers/usb/host/Config.in
 
    comment 'USB Device Class drivers'
    dep_tristate '  USB Audio support' CONFIG_USB_AUDIO $CONFIG_USB $CONFIG_SOUND
@@ -68,6 +58,7 @@
    fi
    dep_tristate '  Aiptek 6000U/8000U tablet support' CONFIG_USB_AIPTEK $CONFIG_USB $CONFIG_INPUT
    dep_tristate '  Wacom Intuos/Graphire tablet support' CONFIG_USB_WACOM $CONFIG_USB $CONFIG_INPUT
+   dep_tristate '  KB Gear JamStudio tablet support' CONFIG_USB_KBTAB $CONFIG_USB $CONFIG_INPUT
    dep_tristate '  Griffin Technology PowerMate support' CONFIG_USB_POWERMATE $CONFIG_USB $CONFIG_INPUT
 
    comment 'USB Imaging devices'
diff -urN linux-2.4.21-pre5/drivers/usb/Makefile linux-2.4.21-pre6/drivers/usb/Makefile
--- linux-2.4.21-pre5/drivers/usb/Makefile	Wed Mar 26 16:11:29 2003
+++ linux-2.4.21-pre6/drivers/usb/Makefile	Wed Mar 26 16:11:39 2003
@@ -53,23 +53,24 @@
 
 # EHCI should initialize/link before the other HCDs
 ifeq ($(CONFIG_USB_EHCI_HCD),y)
-	obj-y += hcd/ehci-hcd.o
+	obj-y += host/ehci-hcd.o
 endif
 
-obj-$(CONFIG_USB_UHCI)		+= usb-uhci.o
-obj-$(CONFIG_USB_UHCI_ALT)	+= uhci.o
-obj-$(CONFIG_USB_OHCI)		+= usb-ohci.o
-
 ifneq ($(CONFIG_USB_EHCI_HCD),n)
 	usbcore-objs		+= hcd.o
 endif
-subdir-$(CONFIG_USB_EHCI_HCD)	+= hcd
+
+subdir-$(CONFIG_USB_EHCI_HCD)	+= host
+subdir-$(CONFIG_USB_UHCI_ALT)	+= host
+subdir-$(CONFIG_USB_UHCI)	+= host
+subdir-$(CONFIG_USB_OHCI)	+= host
 
 obj-$(CONFIG_USB_MOUSE)		+= usbmouse.o
 obj-$(CONFIG_USB_HID)		+= hid.o
 obj-$(CONFIG_USB_KBD)		+= usbkbd.o
 obj-$(CONFIG_USB_AIPTEK)	+= aiptek.o
 obj-$(CONFIG_USB_WACOM)		+= wacom.o
+obj-$(CONFIG_USB_KBTAB)		+= kbtab.o
 obj-$(CONFIG_USB_POWERMATE)	+= powermate.o
 
 obj-$(CONFIG_USB_SCANNER)	+= scanner.o
@@ -106,7 +107,7 @@
 obj-$(CONFIG_USB_LCD)		+= usblcd.o
 
 # Object files in subdirectories
-mod-subdirs	:= serial hcd
+mod-subdirs	:= serial host
 
 subdir-$(CONFIG_USB_SERIAL)	+= serial
 subdir-$(CONFIG_USB_STORAGE)	+= storage
diff -urN linux-2.4.21-pre5/drivers/usb/acm.c linux-2.4.21-pre6/drivers/usb/acm.c
--- linux-2.4.21-pre5/drivers/usb/acm.c	Wed Mar 26 16:11:29 2003
+++ linux-2.4.21-pre6/drivers/usb/acm.c	Wed Mar 26 16:11:39 2003
@@ -240,7 +240,7 @@
 	if (urb->status)
 		dbg("nonzero read bulk status received: %d", urb->status);
 
-	if (!urb->status & !acm->throttle)  {
+	if (!urb->status && !acm->throttle)  {
 		for (i = 0; i < urb->actual_length && !acm->throttle; i++) {
 			/* if we insert more than TTY_FLIPBUF_SIZE characters,
 			 * we drop them. */
diff -urN linux-2.4.21-pre5/drivers/usb/auermain.c linux-2.4.21-pre6/drivers/usb/auermain.c
--- linux-2.4.21-pre5/drivers/usb/auermain.c	Wed Mar 26 16:11:29 2003
+++ linux-2.4.21-pre6/drivers/usb/auermain.c	Wed Mar 26 16:11:39 2003
@@ -66,10 +66,10 @@
 extern devfs_handle_t usb_devfs_handle;
 
 /* array of pointers to our devices that are currently connected */
-static struct auerswald *auerdev_table[AUER_MAX_DEVICES];
+struct auerswald *auerdev_table[AUER_MAX_DEVICES];
 
 /* lock to protect the auerdev_table structure */
-static struct semaphore auerdev_table_mutex;
+struct semaphore auerdev_table_mutex;
 
 /*-------------------------------------------------------------------*/
 /* Forwards */
diff -urN linux-2.4.21-pre5/drivers/usb/hcd/Config.in linux-2.4.21-pre6/drivers/usb/hcd/Config.in
--- linux-2.4.21-pre5/drivers/usb/hcd/Config.in	Fri Aug  2 17:39:44 2002
+++ linux-2.4.21-pre6/drivers/usb/hcd/Config.in	Wed Dec 31 16:00:00 1969
@@ -1,7 +0,0 @@
-#
-# USB Host Controller Drivers
-#
-dep_tristate '  EHCI HCD (USB 2.0) support (EXPERIMENTAL)' CONFIG_USB_EHCI_HCD $CONFIG_USB $CONFIG_EXPERIMENTAL
-# dep_tristate '  OHCI HCD support (EXPERIMENTAL)' CONFIG_USB_OHCI_HCD $CONFIG_USB $CONFIG_EXPERIMENTAL
-# dep_tristate '  UHCI HCD (most Intel and VIA) support (EXPERIMENTAL)' CONFIG_USB_UHCI_HCD $CONFIG_USB $CONFIG_EXPERIMENTAL
-
diff -urN linux-2.4.21-pre5/drivers/usb/hcd/Makefile linux-2.4.21-pre6/drivers/usb/hcd/Makefile
--- linux-2.4.21-pre5/drivers/usb/hcd/Makefile	Fri Aug  2 17:39:44 2002
+++ linux-2.4.21-pre6/drivers/usb/hcd/Makefile	Wed Dec 31 16:00:00 1969
@@ -1,27 +0,0 @@
-#
-# Makefile for USB Host Controller Driver
-# framework and drivers
-#
-
-O_TARGET	:=
-
-obj-$(CONFIG_USB_EHCI_HCD)			+= ehci-hcd.o
-# obj-$(CONFIG_USB_OHCI_HCD)			+= ohci-hcd.o
-# obj-$(CONFIG_USB_UHCI_HCD)			+= uhci-hcd.o
-
-# Extract lists of the multi-part drivers.
-# The 'int-*' lists are the intermediate files used to build the multi's.
-multi-y		:= $(filter $(list-multi), $(obj-y))
-multi-m		:= $(filter $(list-multi), $(obj-m))
-int-y		:= $(sort $(foreach m, $(multi-y), $($(basename $(m))-objs)))
-int-m		:= $(sort $(foreach m, $(multi-m), $($(basename $(m))-objs)))
-
-# Take multi-part drivers out of obj-y and put components in.
-obj-y		:= $(filter-out $(list-multi), $(obj-y)) $(int-y)
-
-# Translate to Rules.make lists.
-OX_OBJS		:= $(obj-y)
-MX_OBJS		:= $(obj-m)
-MIX_OBJS	:= $(int-m)
-
-include $(TOPDIR)/Rules.make
diff -urN linux-2.4.21-pre5/drivers/usb/hcd/ehci-dbg.c linux-2.4.21-pre6/drivers/usb/hcd/ehci-dbg.c
--- linux-2.4.21-pre5/drivers/usb/hcd/ehci-dbg.c	Wed Mar 26 16:11:29 2003
+++ linux-2.4.21-pre6/drivers/usb/hcd/ehci-dbg.c	Wed Dec 31 16:00:00 1969
@@ -1,650 +0,0 @@
-/*
- * Copyright (c) 2001-2002 by David Brownell
- * 
- * 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.
- *
- * 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.
- */
-
-/* this file is part of ehci-hcd.c */
-
-#ifdef DEBUG
-#define ehci_dbg(ehci, fmt, args...) \
-	printk(KERN_DEBUG "%s %s: " fmt , hcd_name , \
-		(ehci)->hcd.pdev->slot_name , ## args )
-#else
-#define ehci_dbg(ehci, fmt, args...) do { } while (0)
-#endif
-
-#define ehci_err(ehci, fmt, args...) \
-	printk(KERN_ERR "%s %s: " fmt , hcd_name , \
-		(ehci)->hcd.pdev->slot_name , ## args )
-#define ehci_info(ehci, fmt, args...) \
-	printk(KERN_INFO "%s %s: " fmt , hcd_name , \
-		(ehci)->hcd.pdev->slot_name , ## args )
-#define ehci_warn(ehci, fmt, args...) \
-	printk(KERN_WARNING "%s %s: " fmt , hcd_name , \
-		(ehci)->hcd.pdev->slot_name , ## args )
-
-
-#ifdef EHCI_VERBOSE_DEBUG
-#	define vdbg dbg
-#	define ehci_vdbg ehci_dbg
-#else
-#	define vdbg(fmt,args...) do { } while (0)
-#	define ehci_vdbg(ehci, fmt, args...) do { } while (0)
-#endif
-
-#ifdef	DEBUG
-
-/* check the values in the HCSPARAMS register
- * (host controller _Structural_ parameters)
- * see EHCI spec, Table 2-4 for each value
- */
-static void dbg_hcs_params (struct ehci_hcd *ehci, char *label)
-{
-	u32	params = readl (&ehci->caps->hcs_params);
-
-	ehci_dbg (ehci,
-		"%s hcs_params 0x%x dbg=%d%s cc=%d pcc=%d%s%s ports=%d\n",
-		label, params,
-		HCS_DEBUG_PORT (params),
-		HCS_INDICATOR (params) ? " ind" : "",
-		HCS_N_CC (params),
-		HCS_N_PCC (params),
-	        HCS_PORTROUTED (params) ? "" : " ordered",
-		HCS_PPC (params) ? "" : " !ppc",
-		HCS_N_PORTS (params)
-		);
-	/* Port routing, per EHCI 0.95 Spec, Section 2.2.5 */
-	if (HCS_PORTROUTED (params)) {
-		int i;
-		char buf [46], tmp [7], byte;
-
-		buf[0] = 0;
-		for (i = 0; i < HCS_N_PORTS (params); i++) {
-			byte = readb (&ehci->caps->portroute[(i>>1)]);
-			sprintf(tmp, "%d ", 
-				((i & 0x1) ? ((byte)&0xf) : ((byte>>4)&0xf)));
-			strcat(buf, tmp);
-		}
-		ehci_dbg (ehci, "%s portroute %s\n",
-				label, buf);
-	}
-}
-#else
-
-static inline void dbg_hcs_params (struct ehci_hcd *ehci, char *label) {}
-
-#endif
-
-#ifdef	DEBUG
-
-/* check the values in the HCCPARAMS register
- * (host controller _Capability_ parameters)
- * see EHCI Spec, Table 2-5 for each value
- * */
-static void dbg_hcc_params (struct ehci_hcd *ehci, char *label)
-{
-	u32	params = readl (&ehci->caps->hcc_params);
-
-	if (HCC_ISOC_CACHE (params)) {
-		ehci_dbg (ehci,
-		     "%s hcc_params %04x caching frame %s%s%s\n",
-		     label, params,
-		     HCC_PGM_FRAMELISTLEN (params) ? "256/512/1024" : "1024",
-		     HCC_CANPARK (params) ? " park" : "",
-		     HCC_64BIT_ADDR (params) ? " 64 bit addr" : "");
-	} else {
-		ehci_dbg (ehci,
-		     "%s hcc_params %04x thresh %d uframes %s%s%s\n",
-		     label,
-		     params,
-		     HCC_ISOC_THRES (params),
-		     HCC_PGM_FRAMELISTLEN (params) ? "256/512/1024" : "1024",
-		     HCC_CANPARK (params) ? " park" : "",
-		     HCC_64BIT_ADDR (params) ? " 64 bit addr" : "");
-	}
-}
-#else
-
-static inline void dbg_hcc_params (struct ehci_hcd *ehci, char *label) {}
-
-#endif
-
-#ifdef	DEBUG
-
-static void __attribute__((__unused__))
-dbg_qh (char *label, struct ehci_hcd *ehci, struct ehci_qh *qh)
-{
-	dbg ("%s %p info1 %x info2 %x hw_curr %x qtd_next %x", label,
-		qh, qh->hw_info1, qh->hw_info2,
-		qh->hw_current, qh->hw_qtd_next);
-	dbg ("  alt+errs= %x, token= %x, page0= %x, page1= %x",
-		qh->hw_alt_next, qh->hw_token,
-		qh->hw_buf [0], qh->hw_buf [1]);
-	if (qh->hw_buf [2]) {
-		dbg ("  page2= %x, page3= %x, page4= %x",
-			qh->hw_buf [2], qh->hw_buf [3],
-			qh->hw_buf [4]);
-	}
-}
-
-static int __attribute__((__unused__))
-dbg_status_buf (char *buf, unsigned len, char *label, u32 status)
-{
-	return snprintf (buf, len,
-		"%s%sstatus %04x%s%s%s%s%s%s%s%s%s%s",
-		label, label [0] ? " " : "", status,
-		(status & STS_ASS) ? " Async" : "",
-		(status & STS_PSS) ? " Periodic" : "",
-		(status & STS_RECL) ? " Recl" : "",
-		(status & STS_HALT) ? " Halt" : "",
-		(status & STS_IAA) ? " IAA" : "",
-		(status & STS_FATAL) ? " FATAL" : "",
-		(status & STS_FLR) ? " FLR" : "",
-		(status & STS_PCD) ? " PCD" : "",
-		(status & STS_ERR) ? " ERR" : "",
-		(status & STS_INT) ? " INT" : ""
-		);
-}
-
-static int __attribute__((__unused__))
-dbg_intr_buf (char *buf, unsigned len, char *label, u32 enable)
-{
-	return snprintf (buf, len,
-		"%s%sintrenable %02x%s%s%s%s%s%s",
-		label, label [0] ? " " : "", enable,
-		(enable & STS_IAA) ? " IAA" : "",
-		(enable & STS_FATAL) ? " FATAL" : "",
-		(enable & STS_FLR) ? " FLR" : "",
-		(enable & STS_PCD) ? " PCD" : "",
-		(enable & STS_ERR) ? " ERR" : "",
-		(enable & STS_INT) ? " INT" : ""
-		);
-}
-
-static const char *const fls_strings [] =
-    { "1024", "512", "256", "??" };
-
-static int dbg_command_buf (char *buf, unsigned len, char *label, u32 command)
-{
-	return snprintf (buf, len,
-		"%s%scommand %06x %s=%d ithresh=%d%s%s%s%s period=%s%s %s",
-		label, label [0] ? " " : "", command,
-		(command & CMD_PARK) ? "park" : "(park)",
-		CMD_PARK_CNT (command),
-		(command >> 16) & 0x3f,
-		(command & CMD_LRESET) ? " LReset" : "",
-		(command & CMD_IAAD) ? " IAAD" : "",
-		(command & CMD_ASE) ? " Async" : "",
-		(command & CMD_PSE) ? " Periodic" : "",
-		fls_strings [(command >> 2) & 0x3],
-		(command & CMD_RESET) ? " Reset" : "",
-		(command & CMD_RUN) ? "RUN" : "HALT"
-		);
-}
-
-static int
-dbg_port_buf (char *buf, unsigned len, char *label, int port, u32 status)
-{
-	char	*sig;
-
-	/* signaling state */
-	switch (status & (3 << 10)) {
-	case 0 << 10: sig = "se0"; break;
-	case 1 << 10: sig = "k"; break;		/* low speed */
-	case 2 << 10: sig = "j"; break;
-	default: sig = "?"; break;
-	}
-
-	return snprintf (buf, len,
-		"%s%sport %d status %06x%s%s sig=%s %s%s%s%s%s%s%s%s%s",
-		label, label [0] ? " " : "", port, status,
-		(status & PORT_POWER) ? " POWER" : "",
-		(status & PORT_OWNER) ? " OWNER" : "",
-		sig,
-		(status & PORT_RESET) ? " RESET" : "",
-		(status & PORT_SUSPEND) ? " SUSPEND" : "",
-		(status & PORT_RESUME) ? " RESUME" : "",
-		(status & PORT_OCC) ? " OCC" : "",
-		(status & PORT_OC) ? " OC" : "",
-		(status & PORT_PEC) ? " PEC" : "",
-		(status & PORT_PE) ? " PE" : "",
-		(status & PORT_CSC) ? " CSC" : "",
-		(status & PORT_CONNECT) ? " CONNECT" : ""
-	    );
-}
-
-#else
-static inline void __attribute__((__unused__))
-dbg_qh (char *label, struct ehci_hcd *ehci, struct ehci_qh *qh)
-{}
-
-static inline int __attribute__((__unused__))
-dbg_status_buf (char *buf, unsigned len, char *label, u32 status)
-{ return 0; }
-
-static inline int __attribute__((__unused__))
-dbg_command_buf (char *buf, unsigned len, char *label, u32 command)
-{ return 0; }
-
-static inline int __attribute__((__unused__))
-dbg_intr_buf (char *buf, unsigned len, char *label, u32 enable)
-{ return 0; }
-
-static inline int __attribute__((__unused__))
-dbg_port_buf (char *buf, unsigned len, char *label, int port, u32 status)
-{ return 0; }
-
-#endif	/* DEBUG */
-
-/* functions have the "wrong" filename when they're output... */
-#define dbg_status(ehci, label, status) { \
-	char _buf [80]; \
-	dbg_status_buf (_buf, sizeof _buf, label, status); \
-	ehci_dbg (ehci, "%s\n", _buf); \
-}
-
-#define dbg_cmd(ehci, label, command) { \
-	char _buf [80]; \
-	dbg_command_buf (_buf, sizeof _buf, label, command); \
-	ehci_dbg (ehci, "%s\n", _buf); \
-}
-
-#define dbg_port(ehci, label, port, status) { \
-	char _buf [80]; \
-	dbg_port_buf (_buf, sizeof _buf, label, port, status); \
-	ehci_dbg (ehci, "%s\n", _buf); \
-}
-
-/*-------------------------------------------------------------------------*/
-
-#ifdef STUB_DEBUG_FILES
-
-static inline void create_debug_files (struct ehci_hcd *bus) { }
-static inline void remove_debug_files (struct ehci_hcd *bus) { }
-
-#else
-
-/* troubleshooting help: expose state in driverfs */
-
-#define speed_char(info1) ({ char tmp; \
-		switch (info1 & (3 << 12)) { \
-		case 0 << 12: tmp = 'f'; break; \
-		case 1 << 12: tmp = 'l'; break; \
-		case 2 << 12: tmp = 'h'; break; \
-		default: tmp = '?'; break; \
-		}; tmp; })
-
-static inline char token_mark (u32 token)
-{
-	token = le32_to_cpu (token);
-	if (token & QTD_STS_ACTIVE)
-		return '*';
-	if (token & QTD_STS_HALT)
-		return '-';
-	if (QTD_PID (token) != 1 /* not IN: OUT or SETUP */
-			|| QTD_LENGTH (token) == 0)
-		return ' ';
-	/* tries to advance through hw_alt_next */
-	return '/';
-}
-
-static void qh_lines (
-	struct ehci_hcd *ehci,
-	struct ehci_qh *qh,
-	char **nextp,
-	unsigned *sizep
-)
-{
-	u32			scratch;
-	u32			hw_curr;
-	struct list_head	*entry;
-	struct ehci_qtd		*td;
-	unsigned		temp;
-	unsigned		size = *sizep;
-	char			*next = *nextp;
-	char			mark;
-
-	mark = token_mark (qh->hw_token);
-	if (mark == '/') {	/* qh_alt_next controls qh advance? */
-		if ((qh->hw_alt_next & QTD_MASK) == ehci->async->hw_alt_next)
-			mark = '#';	/* blocked */
-		else if (qh->hw_alt_next & cpu_to_le32 (0x01))
-			mark = '.';	/* use hw_qtd_next */
-		/* else alt_next points to some other qtd */
-	}
-	scratch = cpu_to_le32p (&qh->hw_info1);
-	hw_curr = (mark == '*') ? cpu_to_le32p (&qh->hw_current) : 0;
-	temp = snprintf (next, size,
-			"qh/%p dev%d %cs ep%d %08x %08x (%08x%c %s nak%d)",
-			qh, scratch & 0x007f,
-			speed_char (scratch),
-			(scratch >> 8) & 0x000f,
-			scratch, cpu_to_le32p (&qh->hw_info2),
-			cpu_to_le32p (&qh->hw_token), mark,
-			(cpu_to_le32 (0x8000000) & qh->hw_token)
-				? "data0" : "data1",
-			(cpu_to_le32p (&qh->hw_alt_next) >> 1) & 0x0f);
-	size -= temp;
-	next += temp;
-
-	/* hc may be modifying the list as we read it ... */
-	list_for_each (entry, &qh->qtd_list) {
-		td = list_entry (entry, struct ehci_qtd, qtd_list);
-		scratch = cpu_to_le32p (&td->hw_token);
-		mark = ' ';
-		if (hw_curr == td->qtd_dma)
-			mark = '*';
-		else if (qh->hw_qtd_next == td->qtd_dma)
-			mark = '+';
-		else if (QTD_LENGTH (scratch)) {
-			if (td->hw_alt_next == ehci->async->hw_alt_next)
-				mark = '#';
-			else if (td->hw_alt_next != EHCI_LIST_END)
-				mark = '/';
-		}
-		temp = snprintf (next, size,
-				"\n\t%p%c%s len=%d %08x urb %p",
-				td, mark, ({ char *tmp;
-				 switch ((scratch>>8)&0x03) {
-				 case 0: tmp = "out"; break;
-				 case 1: tmp = "in"; break;
-				 case 2: tmp = "setup"; break;
-				 default: tmp = "?"; break;
-				 } tmp;}),
-				(scratch >> 16) & 0x7fff,
-				scratch,
-				td->urb);
-		if (temp < 0)
-			temp = 0;
-		else if (size < temp)
-			temp = size;
-		size -= temp;
-		next += temp;
-		if (temp == size)
-			goto done;
-	}
-
-	temp = snprintf (next, size, "\n");
-	if (temp < 0)
-		temp = 0;
-	else if (size < temp)
-		temp = size;
-	size -= temp;
-	next += temp;
-
-done:
-	*sizep = size;
-	*nextp = next;
-}
-
-static ssize_t
-show_async (struct device *dev, char *buf)
-{
-	struct pci_dev		*pdev;
-	struct ehci_hcd		*ehci;
-	unsigned long		flags;
-	unsigned		temp, size;
-	char			*next;
-	struct ehci_qh		*qh;
-
-	pdev = container_of (dev, struct pci_dev, dev);
-	ehci = container_of (pci_get_drvdata (pdev), struct ehci_hcd, hcd);
-	next = buf;
-	size = PAGE_SIZE;
-
-	/* dumps a snapshot of the async schedule.
-	 * usually empty except for long-term bulk reads, or head.
-	 * one QH per line, and TDs we know about
-	 */
-	spin_lock_irqsave (&ehci->lock, flags);
-	for (qh = ehci->async->qh_next.qh; size > 0 && qh; qh = qh->qh_next.qh)
-		qh_lines (ehci, qh, &next, &size);
-	if (ehci->reclaim && size > 0) {
-		temp = snprintf (next, size, "\nreclaim =\n");
-		size -= temp;
-		next += temp;
-
-		for (qh = ehci->reclaim; size > 0 && qh; qh = qh->reclaim)
-			qh_lines (ehci, qh, &next, &size);
-	}
-	spin_unlock_irqrestore (&ehci->lock, flags);
-
-	return PAGE_SIZE - size;
-}
-static DEVICE_ATTR (async, S_IRUGO, show_async, NULL);
-
-#define DBG_SCHED_LIMIT 64
-
-static ssize_t
-show_periodic (struct device *dev, char *buf)
-{
-	struct pci_dev		*pdev;
-	struct ehci_hcd		*ehci;
-	unsigned long		flags;
-	union ehci_shadow	p, *seen;
-	unsigned		temp, size, seen_count;
-	char			*next;
-	unsigned		i, tag;
-
-	if (!(seen = kmalloc (DBG_SCHED_LIMIT * sizeof *seen, SLAB_ATOMIC)))
-		return 0;
-	seen_count = 0;
-
-	pdev = container_of (dev, struct pci_dev, dev);
-	ehci = container_of (pci_get_drvdata (pdev), struct ehci_hcd, hcd);
-	next = buf;
-	size = PAGE_SIZE;
-
-	temp = snprintf (next, size, "size = %d\n", ehci->periodic_size);
-	size -= temp;
-	next += temp;
-
-	/* dump a snapshot of the periodic schedule.
-	 * iso changes, interrupt usually doesn't.
-	 */
-	spin_lock_irqsave (&ehci->lock, flags);
-	for (i = 0; i < ehci->periodic_size; i++) {
-		p = ehci->pshadow [i];
-		if (!p.ptr)
-			continue;
-		tag = Q_NEXT_TYPE (ehci->periodic [i]);
-
-		temp = snprintf (next, size, "%4d: ", i);
-		size -= temp;
-		next += temp;
-
-		do {
-			switch (tag) {
-			case Q_TYPE_QH:
-				temp = snprintf (next, size, " qh%d/%p",
-						p.qh->period, p.qh);
-				size -= temp;
-				next += temp;
-				for (temp = 0; temp < seen_count; temp++) {
-					if (seen [temp].ptr == p.ptr)
-						break;
-				}
-				/* show more info the first time around */
-				if (temp == seen_count) {
-					u32	scratch = cpu_to_le32p (
-							&p.qh->hw_info1);
-
-					temp = snprintf (next, size,
-						" (%cs dev%d ep%d [%d/%d] %d)",
-						speed_char (scratch),
-						scratch & 0x007f,
-						(scratch >> 8) & 0x000f,
-						p.qh->usecs, p.qh->c_usecs,
-						0x7ff & (scratch >> 16));
-
-					/* FIXME TD info too */
-
-					if (seen_count < DBG_SCHED_LIMIT)
-						seen [seen_count++].qh = p.qh;
-				} else
-					temp = 0;
-				tag = Q_NEXT_TYPE (p.qh->hw_next);
-				p = p.qh->qh_next;
-				break;
-			case Q_TYPE_FSTN:
-				temp = snprintf (next, size,
-					" fstn-%8x/%p", p.fstn->hw_prev,
-					p.fstn);
-				tag = Q_NEXT_TYPE (p.fstn->hw_next);
-				p = p.fstn->fstn_next;
-				break;
-			case Q_TYPE_ITD:
-				temp = snprintf (next, size,
-					" itd/%p", p.itd);
-				tag = Q_NEXT_TYPE (p.itd->hw_next);
-				p = p.itd->itd_next;
-				break;
-			case Q_TYPE_SITD:
-				temp = snprintf (next, size,
-					" sitd/%p", p.sitd);
-				tag = Q_NEXT_TYPE (p.sitd->hw_next);
-				p = p.sitd->sitd_next;
-				break;
-			}
-			size -= temp;
-			next += temp;
-		} while (p.ptr);
-
-		temp = snprintf (next, size, "\n");
-		size -= temp;
-		next += temp;
-	}
-	spin_unlock_irqrestore (&ehci->lock, flags);
-	kfree (seen);
-
-	return PAGE_SIZE - size;
-}
-static DEVICE_ATTR (periodic, S_IRUGO, show_periodic, NULL);
-
-#undef DBG_SCHED_LIMIT
-
-static ssize_t
-show_registers (struct device *dev, char *buf)
-{
-	struct pci_dev		*pdev;
-	struct ehci_hcd		*ehci;
-	unsigned long		flags;
-	unsigned		temp, size, i;
-	char			*next, scratch [80];
-	static char		fmt [] = "%*s\n";
-	static char		label [] = "";
-
-	pdev = container_of (dev, struct pci_dev, dev);
-	ehci = container_of (pci_get_drvdata (pdev), struct ehci_hcd, hcd);
-
-	next = buf;
-	size = PAGE_SIZE;
-
-	spin_lock_irqsave (&ehci->lock, flags);
-
-	/* Capability Registers */
-	i = readw (&ehci->caps->hci_version);
-	temp = snprintf (next, size,
-		"EHCI %x.%02x, hcd state %d (version " DRIVER_VERSION ")\n",
-		i >> 8, i & 0x0ff, ehci->hcd.state);
-	size -= temp;
-	next += temp;
-
-	// FIXME interpret both types of params
-	i = readl (&ehci->caps->hcs_params);
-	temp = snprintf (next, size, "structural params 0x%08x\n", i);
-	size -= temp;
-	next += temp;
-
-	i = readl (&ehci->caps->hcc_params);
-	temp = snprintf (next, size, "capability params 0x%08x\n", i);
-	size -= temp;
-	next += temp;
-
-	/* Operational Registers */
-	temp = dbg_status_buf (scratch, sizeof scratch, label,
-			readl (&ehci->regs->status));
-	temp = snprintf (next, size, fmt, temp, scratch);
-	size -= temp;
-	next += temp;
-
-	temp = dbg_command_buf (scratch, sizeof scratch, label,
-			readl (&ehci->regs->command));
-	temp = snprintf (next, size, fmt, temp, scratch);
-	size -= temp;
-	next += temp;
-
-	temp = dbg_intr_buf (scratch, sizeof scratch, label,
-			readl (&ehci->regs->intr_enable));
-	temp = snprintf (next, size, fmt, temp, scratch);
-	size -= temp;
-	next += temp;
-
-	temp = snprintf (next, size, "uframe %04x\n",
-			readl (&ehci->regs->frame_index));
-	size -= temp;
-	next += temp;
-
-	for (i = 0; i < HCS_N_PORTS (ehci->hcs_params); i++) {
-		temp = dbg_port_buf (scratch, sizeof scratch, label, i,
-				readl (&ehci->regs->port_status [i]));
-		temp = snprintf (next, size, fmt, temp, scratch);
-		size -= temp;
-		next += temp;
-	}
-
-	if (ehci->reclaim) {
-		temp = snprintf (next, size, "reclaim qh %p%s\n",
-				ehci->reclaim,
-				ehci->reclaim_ready ? " ready" : "");
-		size -= temp;
-		next += temp;
-	}
-
-#ifdef EHCI_STATS
-	temp = snprintf (next, size, "irq normal %ld err %ld reclaim %ld\n",
-		ehci->stats.normal, ehci->stats.error, ehci->stats.reclaim);
-	size -= temp;
-	next += temp;
-
-	temp = snprintf (next, size, "complete %ld unlink %ld\n",
-		ehci->stats.complete, ehci->stats.unlink);
-	size -= temp;
-	next += temp;
-#endif
-
-	spin_unlock_irqrestore (&ehci->lock, flags);
-
-	return PAGE_SIZE - size;
-}
-static DEVICE_ATTR (registers, S_IRUGO, show_registers, NULL);
-
-static inline void create_debug_files (struct ehci_hcd *bus)
-{
-	device_create_file (&bus->hcd.pdev->dev, &dev_attr_async);
-	device_create_file (&bus->hcd.pdev->dev, &dev_attr_periodic);
-	device_create_file (&bus->hcd.pdev->dev, &dev_attr_registers);
-}
-
-static inline void remove_debug_files (struct ehci_hcd *bus)
-{
-	device_remove_file (&bus->hcd.pdev->dev, &dev_attr_async);
-	device_remove_file (&bus->hcd.pdev->dev, &dev_attr_periodic);
-	device_remove_file (&bus->hcd.pdev->dev, &dev_attr_registers);
-}
-
-#endif /* STUB_DEBUG_FILES */
-
diff -urN linux-2.4.21-pre5/drivers/usb/hcd/ehci-hcd.c linux-2.4.21-pre6/drivers/usb/hcd/ehci-hcd.c
--- linux-2.4.21-pre5/drivers/usb/hcd/ehci-hcd.c	Wed Mar 26 16:11:29 2003
+++ linux-2.4.21-pre6/drivers/usb/hcd/ehci-hcd.c	Wed Dec 31 16:00:00 1969
@@ -1,1033 +0,0 @@
-/*
- * Copyright (c) 2000-2002 by David Brownell
- * 
- * 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.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#include <linux/config.h>
-#include <linux/module.h>
-#include <linux/pci.h>
-#include <linux/kernel.h>
-#include <linux/delay.h>
-#include <linux/ioport.h>
-#include <linux/sched.h>
-#include <linux/slab.h>
-#include <linux/smp_lock.h>
-#include <linux/errno.h>
-#include <linux/init.h>
-#include <linux/timer.h>
-#include <linux/list.h>
-#include <linux/interrupt.h>
-
-#ifdef CONFIG_USB_DEBUG
-	#define DEBUG
-#else
-	#undef DEBUG
-#endif
-
-#include <linux/usb.h>
-
-#include <linux/version.h>
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,32)
-#include "../hcd.h"
-#else
-#include "../core/hcd.h"
-#endif
-
-#include <asm/byteorder.h>
-#include <asm/io.h>
-#include <asm/irq.h>
-#include <asm/system.h>
-#include <asm/unaligned.h>
-
-
-/*-------------------------------------------------------------------------*/
-
-/*
- * EHCI hc_driver implementation ... experimental, incomplete.
- * Based on the final 1.0 register interface specification.
- *
- * USB 2.0 shows up in upcoming www.pcmcia.org technology.
- * First was PCMCIA, like ISA; then CardBus, which is PCI.
- * Next comes "CardBay", using USB 2.0 signals.
- *
- * Contains additional contributions by Brad Hards, Rory Bolt, and others.
- * Special thanks to Intel and VIA for providing host controllers to
- * test this driver on, and Cypress (including In-System Design) for
- * providing early devices for those host controllers to talk to!
- *
- * HISTORY:
- *
- * 2002-11-29	Correct handling for hw async_next register.
- * 2002-08-06	Handling for bulk and interrupt transfers is mostly shared;
- *	only scheduling is different, no arbitrary limitations.
- * 2002-07-25	Sanity check PCI reads, mostly for better cardbus support,
- * 	clean up HC run state handshaking.
- * 2002-05-24	Preliminary FS/LS interrupts, using scheduling shortcuts
- * 2002-05-11	Clear TT errors for FS/LS ctrl/bulk.  Fill in some other
- *	missing pieces:  enabling 64bit dma, handoff from BIOS/SMM.
- * 2002-05-07	Some error path cleanups to report better errors; wmb();
- *	use non-CVS version id; better iso bandwidth claim.
- * 2002-04-19	Control/bulk/interrupt submit no longer uses giveback() on
- *	errors in submit path.  Bugfixes to interrupt scheduling/processing.
- * 2002-03-05	Initial high-speed ISO support; reduce ITD memory; shift
- *	more checking to generic hcd framework (db).  Make it work with
- *	Philips EHCI; reduce PCI traffic; shorten IRQ path (Rory Bolt).
- * 2002-01-14	Minor cleanup; version synch.
- * 2002-01-08	Fix roothub handoff of FS/LS to companion controllers.
- * 2002-01-04	Control/Bulk queuing behaves.
- *
- * 2001-12-12	Initial patch version for Linux 2.5.1 kernel.
- * 2001-June	Works with usb-storage and NEC EHCI on 2.4
- */
-
-#define DRIVER_VERSION "2003-Jan-22"
-#define DRIVER_AUTHOR "David Brownell"
-#define DRIVER_DESC "USB 2.0 'Enhanced' Host Controller (EHCI) Driver"
-
-static const char	hcd_name [] = "ehci-hcd";
-
-
-// #define EHCI_VERBOSE_DEBUG
-// #define have_split_iso
-
-#ifdef DEBUG
-#define EHCI_STATS
-#endif
-
-#define INTR_AUTOMAGIC		/* urb lifecycle mode, gone in 2.5 */
-
-/* magic numbers that can affect system performance */
-#define	EHCI_TUNE_CERR		3	/* 0-3 qtd retries; 0 == don't stop */
-#define	EHCI_TUNE_RL_HS		4	/* nak throttle; see 4.9 */
-#define	EHCI_TUNE_RL_TT		0
-#define	EHCI_TUNE_MULT_HS	1	/* 1-3 transactions/uframe; 4.10.3 */
-#define	EHCI_TUNE_MULT_TT	1
-#define	EHCI_TUNE_FLS		2	/* (small) 256 frame schedule */
-
-#define EHCI_WATCHDOG_JIFFIES	(HZ/100)	/* arbitrary; ~10 msec */
-#define EHCI_ASYNC_JIFFIES	(HZ/20)		/* async idle timeout */
-
-/* Initial IRQ latency:  lower than default */
-static int log2_irq_thresh = 0;		// 0 to 6
-MODULE_PARM (log2_irq_thresh, "i");
-MODULE_PARM_DESC (log2_irq_thresh, "log2 IRQ latency, 1-64 microframes");
-
-#define	INTR_MASK (STS_IAA | STS_FATAL | STS_ERR | STS_INT)
-
-/*-------------------------------------------------------------------------*/
-
-#include "ehci.h"
-#include "ehci-dbg.c"
-
-/*-------------------------------------------------------------------------*/
-
-/*
- * handshake - spin reading hc until handshake completes or fails
- * @ptr: address of hc register to be read
- * @mask: bits to look at in result of read
- * @done: value of those bits when handshake succeeds
- * @usec: timeout in microseconds
- *
- * Returns negative errno, or zero on success
- *
- * Success happens when the "mask" bits have the specified value (hardware
- * handshake done).  There are two failure modes:  "usec" have passed (major
- * hardware flakeout), or the register reads as all-ones (hardware removed).
- *
- * That last failure should_only happen in cases like physical cardbus eject
- * before driver shutdown. But it also seems to be caused by bugs in cardbus
- * bridge shutdown:  shutting down the bridge before the devices using it.
- */
-static int handshake (u32 *ptr, u32 mask, u32 done, int usec)
-{
-	u32	result;
-
-	do {
-		result = readl (ptr);
-		if (result == ~(u32)0)		/* card removed */
-			return -ENODEV;
-		result &= mask;
-		if (result == done)
-			return 0;
-		udelay (1);
-		usec--;
-	} while (usec > 0);
-	return -ETIMEDOUT;
-}
-
-/*
- * hc states include: unknown, halted, ready, running
- * transitional states are messy just now
- * trying to avoid "running" unless urbs are active
- * a "ready" hc can be finishing prefetched work
- */
-
-/* force HC to halt state from unknown (EHCI spec section 2.3) */
-static int ehci_halt (struct ehci_hcd *ehci)
-{
-	u32	temp = readl (&ehci->regs->status);
-
-	if ((temp & STS_HALT) != 0)
-		return 0;
-
-	temp = readl (&ehci->regs->command);
-	temp &= ~CMD_RUN;
-	writel (temp, &ehci->regs->command);
-	return handshake (&ehci->regs->status, STS_HALT, STS_HALT, 16 * 125);
-}
-
-/* reset a non-running (STS_HALT == 1) controller */
-static int ehci_reset (struct ehci_hcd *ehci)
-{
-	u32	command = readl (&ehci->regs->command);
-
-	command |= CMD_RESET;
-	dbg_cmd (ehci, "reset", command);
-	writel (command, &ehci->regs->command);
-	ehci->hcd.state = USB_STATE_HALT;
-	return handshake (&ehci->regs->command, CMD_RESET, 0, 250 * 1000);
-}
-
-/* idle the controller (from running) */
-static void ehci_ready (struct ehci_hcd *ehci)
-{
-	u32	temp;
-
-#ifdef DEBUG
-	if (!HCD_IS_RUNNING (ehci->hcd.state))
-		BUG ();
-#endif
-
-	/* wait for any schedule enables/disables to take effect */
-	temp = 0;
-	if (ehci->async->qh_next.qh)
-		temp = STS_ASS;
-	if (ehci->next_uframe != -1)
-		temp |= STS_PSS;
-	if (handshake (&ehci->regs->status, STS_ASS | STS_PSS,
-				temp, 16 * 125) != 0) {
-		ehci->hcd.state = USB_STATE_HALT;
-		return;
-	}
-
-	/* then disable anything that's still active */
-	temp = readl (&ehci->regs->command);
-	temp &= ~(CMD_ASE | CMD_IAAD | CMD_PSE);
-	writel (temp, &ehci->regs->command);
-
-	/* hardware can take 16 microframes to turn off ... */
-	if (handshake (&ehci->regs->status, STS_ASS | STS_PSS,
-				0, 16 * 125) != 0) {
-		ehci->hcd.state = USB_STATE_HALT;
-		return;
-	}
-	ehci->hcd.state = USB_STATE_READY;
-}
-
-/*-------------------------------------------------------------------------*/
-
-#include "ehci-hub.c"
-#include "ehci-mem.c"
-#include "ehci-q.c"
-#include "ehci-sched.c"
-
-/*-------------------------------------------------------------------------*/
-
-static void ehci_work(struct ehci_hcd *ehci, struct pt_regs *regs);
-
-static void ehci_watchdog (unsigned long param)
-{
-	struct ehci_hcd		*ehci = (struct ehci_hcd *) param;
-	unsigned long		flags;
-
-	spin_lock_irqsave (&ehci->lock, flags);
-
-	/* lost IAA irqs wedge things badly; seen with a vt8235 */
-	if (ehci->reclaim) {
-		u32		status = readl (&ehci->regs->status);
-
-		if (status & STS_IAA) {
-			ehci_vdbg (ehci, "lost IAA\n");
-			writel (STS_IAA, &ehci->regs->status);
-			ehci->reclaim_ready = 1;
-		}
-	}
-
-	ehci_work (ehci, NULL);
-	if (ehci->reclaim && !timer_pending (&ehci->watchdog))
-		mod_timer (&ehci->watchdog,
-				jiffies + EHCI_WATCHDOG_JIFFIES);
-
- 	/* stop async processing after it's idled a while */
-	else if (ehci->async_idle) {
- 		start_unlink_async (ehci, ehci->async);
- 		ehci->async_idle = 0;
-	}
-	spin_unlock_irqrestore (&ehci->lock, flags);
-}
-
-/* EHCI 0.96 (and later) section 5.1 says how to kick BIOS/SMM/...
- * off the controller (maybe it can boot from highspeed USB disks).
- */
-static int bios_handoff (struct ehci_hcd *ehci, int where, u32 cap)
-{
-	if (cap & (1 << 16)) {
-		int msec = 500;
-
-		/* request handoff to OS */
-		cap &= 1 << 24;
-		pci_write_config_dword (ehci->hcd.pdev, where, cap);
-
-		/* and wait a while for it to happen */
-		do {
-			wait_ms (10);
-			msec -= 10;
-			pci_read_config_dword (ehci->hcd.pdev, where, &cap);
-		} while ((cap & (1 << 16)) && msec);
-		if (cap & (1 << 16)) {
-			ehci_err (ehci, "BIOS handoff failed (%d, %04x)\n",
-				where, cap);
-			return 1;
-		} 
-		ehci_dbg (ehci, "BIOS handoff succeeded\n");
-	}
-	return 0;
-}
-
-/* called by khubd or root hub init threads */
-
-static int ehci_start (struct usb_hcd *hcd)
-{
-	struct ehci_hcd		*ehci = hcd_to_ehci (hcd);
-	u32			temp;
-	struct usb_device	*udev;
-	struct usb_bus		*bus;
-	int			retval;
-	u32			hcc_params;
-	u8                      tempbyte;
-
-	spin_lock_init (&ehci->lock);
-
-	ehci->caps = (struct ehci_caps *) hcd->regs;
-	ehci->regs = (struct ehci_regs *) (hcd->regs + ehci->caps->length);
-	dbg_hcs_params (ehci, "ehci_start");
-	dbg_hcc_params (ehci, "ehci_start");
-
-	hcc_params = readl (&ehci->caps->hcc_params);
-
-	/* EHCI 0.96 and later may have "extended capabilities" */
-	temp = HCC_EXT_CAPS (hcc_params);
-	while (temp) {
-		u32		cap;
-
-		pci_read_config_dword (ehci->hcd.pdev, temp, &cap);
-		ehci_dbg (ehci, "capability %04x at %02x\n", cap, temp);
-		switch (cap & 0xff) {
-		case 1:			/* BIOS/SMM/... handoff */
-			if (bios_handoff (ehci, temp, cap) != 0)
-				return -EOPNOTSUPP;
-			break;
-		case 0:			/* illegal reserved capability */
-			ehci_warn (ehci, "illegal capability!\n");
-			cap = 0;
-			/* FALLTHROUGH */
-		default:		/* unknown */
-			break;
-		}
-		temp = (cap >> 8) & 0xff;
-	}
-
-	/* cache this readonly data; minimize PCI reads */
-	ehci->hcs_params = readl (&ehci->caps->hcs_params);
-
-	/* force HC to halt state */
-	if ((retval = ehci_halt (ehci)) != 0)
-		return retval;
-
-	/*
-	 * hw default: 1K periodic list heads, one per frame.
-	 * periodic_size can shrink by USBCMD update if hcc_params allows.
-	 */
-	ehci->periodic_size = DEFAULT_I_TDPS;
-	if ((retval = ehci_mem_init (ehci, SLAB_KERNEL)) < 0)
-		return retval;
-
-	/* controllers may cache some of the periodic schedule ... */
-	if (HCC_ISOC_CACHE (hcc_params)) 	// full frame cache
-		ehci->i_thresh = 8;
-	else					// N microframes cached
-		ehci->i_thresh = 2 + HCC_ISOC_THRES (hcc_params);
-
-	ehci->reclaim = 0;
-	ehci->next_uframe = -1;
-
-	/* controller state:  unknown --> reset */
-
-	/* EHCI spec section 4.1 */
-	if ((retval = ehci_reset (ehci)) != 0) {
-		ehci_mem_cleanup (ehci);
-		return retval;
-	}
-	writel (INTR_MASK, &ehci->regs->intr_enable);
-	writel (ehci->periodic_dma, &ehci->regs->frame_list);
-
-	/*
-	 * dedicate a qh for the async ring head, since we couldn't unlink
-	 * a 'real' qh without stopping the async schedule [4.8].  use it
-	 * as the 'reclamation list head' too.
-	 * its dummy is used in hw_alt_next of many tds, to prevent the qh
-	 * from automatically advancing to the next td after short reads.
-	 */
-	ehci->async->qh_next.qh = 0;
-	ehci->async->hw_next = QH_NEXT (ehci->async->qh_dma);
-	ehci->async->hw_info1 = cpu_to_le32 (QH_HEAD);
-	ehci->async->hw_token = cpu_to_le32 (QTD_STS_HALT);
-	ehci->async->hw_qtd_next = EHCI_LIST_END;
-	ehci->async->qh_state = QH_STATE_LINKED;
-	ehci->async->hw_alt_next = QTD_NEXT (ehci->async->dummy->qtd_dma);
-	writel ((u32)ehci->async->qh_dma, &ehci->regs->async_next);
-
-	/*
-	 * hcc_params controls whether ehci->regs->segment must (!!!)
-	 * be used; it constrains QH/ITD/SITD and QTD locations.
-	 * pci_pool consistent memory always uses segment zero.
-	 * streaming mappings for I/O buffers, like pci_map_single(),
-	 * can return segments above 4GB, if the device allows.
-	 *
-	 * NOTE:  the dma mask is visible through dma_supported(), so
-	 * drivers can pass this info along ... like NETIF_F_HIGHDMA,
-	 * Scsi_Host.highmem_io, and so forth.  It's readonly to all
-	 * host side drivers though.
-	 */
-	if (HCC_64BIT_ADDR (hcc_params)) {
-		writel (0, &ehci->regs->segment);
-		if (!pci_set_dma_mask (ehci->hcd.pdev, 0xffffffffffffffffULL))
-			ehci_info (ehci, "enabled 64bit PCI DMA\n");
-	}
-
-	/* help hc dma work well with cachelines */
-	pci_set_mwi (ehci->hcd.pdev);
-
-	/* clear interrupt enables, set irq latency */
-	temp = readl (&ehci->regs->command) & 0xff;
-	if (log2_irq_thresh < 0 || log2_irq_thresh > 6)
-		log2_irq_thresh = 0;
-	temp |= 1 << (16 + log2_irq_thresh);
-	// if hc can park (ehci >= 0.96), default is 3 packets per async QH 
-	if (HCC_PGM_FRAMELISTLEN (hcc_params)) {
-		/* periodic schedule size can be smaller than default */
-		temp &= ~(3 << 2);
-		temp |= (EHCI_TUNE_FLS << 2);
-		switch (EHCI_TUNE_FLS) {
-		case 0: ehci->periodic_size = 1024; break;
-		case 1: ehci->periodic_size = 512; break;
-		case 2: ehci->periodic_size = 256; break;
-		default:	BUG ();
-		}
-	}
-	temp &= ~(CMD_IAAD | CMD_ASE | CMD_PSE),
-	// Philips, Intel, and maybe others need CMD_RUN before the
-	// root hub will detect new devices (why?); NEC doesn't
-	temp |= CMD_RUN;
-	writel (temp, &ehci->regs->command);
-	dbg_cmd (ehci, "init", temp);
-
-	/* set async sleep time = 10 us ... ? */
-
-	init_timer (&ehci->watchdog);
-	ehci->watchdog.function = ehci_watchdog;
-	ehci->watchdog.data = (unsigned long) ehci;
-
-	/* wire up the root hub */
-	bus = hcd_to_bus (hcd);
-	bus->root_hub = udev = usb_alloc_dev (NULL, bus);
-	if (!udev) {
-done2:
-		ehci_mem_cleanup (ehci);
-		return -ENOMEM;
-	}
-
-	/*
-	 * Start, enabling full USB 2.0 functionality ... usb 1.1 devices
-	 * are explicitly handed to companion controller(s), so no TT is
-	 * involved with the root hub.
-	 */
-	ehci->hcd.state = USB_STATE_READY;
-	writel (FLAG_CF, &ehci->regs->configured_flag);
-	readl (&ehci->regs->command);	/* unblock posted write */
-
-        /* PCI Serial Bus Release Number is at 0x60 offset */
-	pci_read_config_byte (hcd->pdev, 0x60, &tempbyte);
-	temp = readw (&ehci->caps->hci_version);
-	ehci_info (ehci,
-		"USB %x.%x enabled, EHCI %x.%02x, driver %s\n",
-		((tempbyte & 0xf0)>>4), (tempbyte & 0x0f),
-		temp >> 8, temp & 0xff, DRIVER_VERSION);
-
-	/*
-	 * From here on, khubd concurrently accesses the root
-	 * hub; drivers will be talking to enumerated devices.
-	 *
-	 * Before this point the HC was idle/ready.  After, khubd
-	 * and device drivers may start it running.
-	 */
-	usb_connect (udev);
-	udev->speed = USB_SPEED_HIGH;
-	if (hcd_register_root (hcd) != 0) {
-		if (hcd->state == USB_STATE_RUNNING)
-			ehci_ready (ehci);
-		ehci_reset (ehci);
-		bus->root_hub = 0;
-		retval = -ENODEV;
-		goto done2;
-	}
-
-	create_debug_files (ehci);
-
-	return 0;
-}
-
-/* always called by thread; normally rmmod */
-
-static void ehci_stop (struct usb_hcd *hcd)
-{
-	struct ehci_hcd		*ehci = hcd_to_ehci (hcd);
-
-	ehci_dbg (ehci, "stop\n");
-
-	/* no more interrupts ... */
-	if (hcd->state == USB_STATE_RUNNING)
-		ehci_ready (ehci);
-	if (in_interrupt ()) {		/* must not happen!! */
-		ehci_err (ehci, "stopped in_interrupt!\n");
-		return;
-	}
-	del_timer_sync (&ehci->watchdog);
-	ehci_reset (ehci);
-
-	/* let companion controllers work when we aren't */
-	writel (0, &ehci->regs->configured_flag);
-
-	remove_debug_files (ehci);
-
-	/* root hub is shut down separately (first, when possible) */
-	spin_lock_irq (&ehci->lock);
-	ehci_work (ehci, NULL);
-	spin_unlock_irq (&ehci->lock);
-	ehci_mem_cleanup (ehci);
-
-#ifdef	EHCI_STATS
-	ehci_dbg (ehci, "irq normal %ld err %ld reclaim %ld\n",
-		ehci->stats.normal, ehci->stats.error, ehci->stats.reclaim);
-	ehci_dbg (ehci, "complete %ld unlink %ld\n",
-		ehci->stats.complete, ehci->stats.unlink);
-#endif
-
-	dbg_status (ehci, "ehci_stop completed", readl (&ehci->regs->status));
-}
-
-static int ehci_get_frame (struct usb_hcd *hcd)
-{
-	struct ehci_hcd		*ehci = hcd_to_ehci (hcd);
-	return (readl (&ehci->regs->frame_index) >> 3) % ehci->periodic_size;
-}
-
-/*-------------------------------------------------------------------------*/
-
-#ifdef	CONFIG_PM
-
-/* suspend/resume, section 4.3 */
-
-static int ehci_suspend (struct usb_hcd *hcd, u32 state)
-{
-	struct ehci_hcd		*ehci = hcd_to_ehci (hcd);
-	int			ports;
-	int			i;
-
-	dbg ("%s: suspend to %d", hcd_to_bus (hcd)->bus_name, state);
-
-	ports = HCS_N_PORTS (ehci->hcs_params);
-
-	// FIXME:  This assumes what's probably a D3 level suspend...
-
-	// FIXME:  usb wakeup events on this bus should resume the machine.
-	// pci config register PORTWAKECAP controls which ports can do it;
-	// bios may have initted the register...
-
-	/* suspend each port, then stop the hc */
-	for (i = 0; i < ports; i++) {
-		int	temp = readl (&ehci->regs->port_status [i]);
-
-		if ((temp & PORT_PE) == 0
-				|| (temp & PORT_OWNER) != 0)
-			continue;
-dbg ("%s: suspend port %d", hcd_to_bus (hcd)->bus_name, i);
-		temp |= PORT_SUSPEND;
-		writel (temp, &ehci->regs->port_status [i]);
-	}
-
-	if (hcd->state == USB_STATE_RUNNING)
-		ehci_ready (ehci);
-	writel (readl (&ehci->regs->command) & ~CMD_RUN, &ehci->regs->command);
-
-// save pci FLADJ value
-
-	/* who tells PCI to reduce power consumption? */
-
-	return 0;
-}
-
-static int ehci_resume (struct usb_hcd *hcd)
-{
-	struct ehci_hcd		*ehci = hcd_to_ehci (hcd);
-	int			ports;
-	int			i;
-
-	dbg ("%s: resume", hcd_to_bus (hcd)->bus_name);
-
-	ports = HCS_N_PORTS (ehci->hcs_params);
-
-	// FIXME:  if controller didn't retain state,
-	// return and let generic code clean it up
-	// test configured_flag ?
-
-	/* resume HC and each port */
-// restore pci FLADJ value
-	// khubd and drivers will set HC running, if needed;
-	hcd->state = USB_STATE_READY;
-	// FIXME Philips/Intel/... etc don't really have a "READY"
-	// state ... turn on CMD_RUN too
-	for (i = 0; i < ports; i++) {
-		int	temp = readl (&ehci->regs->port_status [i]);
-
-		if ((temp & PORT_PE) == 0
-				|| (temp & PORT_SUSPEND) != 0)
-			continue;
-dbg ("%s: resume port %d", hcd_to_bus (hcd)->bus_name, i);
-		temp |= PORT_RESUME;
-		writel (temp, &ehci->regs->port_status [i]);
-		readl (&ehci->regs->command);	/* unblock posted writes */
-
-		wait_ms (20);
-		temp &= ~PORT_RESUME;
-		writel (temp, &ehci->regs->port_status [i]);
-	}
-	readl (&ehci->regs->command);	/* unblock posted writes */
-	return 0;
-}
-
-#endif
-
-/*-------------------------------------------------------------------------*/
-
-/*
- * ehci_work is called from some interrupts, timers, and so on.
- * it calls driver completion functions, after dropping ehci->lock.
- */
-static void ehci_work (struct ehci_hcd *ehci, struct pt_regs *regs)
-{
-	if (ehci->reclaim_ready)
-		end_unlink_async (ehci, regs);
-	scan_async (ehci, regs);
-	if (ehci->next_uframe != -1)
-		scan_periodic (ehci, regs);
-}
-
-/*-------------------------------------------------------------------------*/
-
-static void ehci_irq (struct usb_hcd *hcd, struct pt_regs *regs)
-{
-	struct ehci_hcd		*ehci = hcd_to_ehci (hcd);
-	u32			status;
-	int			bh;
-
-	spin_lock (&ehci->lock);
-
-	status = readl (&ehci->regs->status);
-
-	/* e.g. cardbus physical eject */
-	if (status == ~(u32) 0) {
-		ehci_dbg (ehci, "device removed\n");
-		goto dead;
-	}
-
-	status &= INTR_MASK;
-	if (!status)			/* irq sharing? */
-		goto done;
-
-	/* clear (just) interrupts */
-	writel (status, &ehci->regs->status);
-	readl (&ehci->regs->command);	/* unblock posted write */
-	bh = 0;
-
-#ifdef	EHCI_VERBOSE_DEBUG
-	/* unrequested/ignored: Port Change Detect, Frame List Rollover */
-	dbg_status (ehci, "irq", status);
-#endif
-
-	/* INT, ERR, and IAA interrupt rates can be throttled */
-
-	/* normal [4.15.1.2] or error [4.15.1.1] completion */
-	if (likely ((status & (STS_INT|STS_ERR)) != 0)) {
-		if (likely ((status & STS_ERR) == 0))
-			COUNT (ehci->stats.normal);
-		else
-			COUNT (ehci->stats.error);
-		bh = 1;
-	}
-
-	/* complete the unlinking of some qh [4.15.2.3] */
-	if (status & STS_IAA) {
-		COUNT (ehci->stats.reclaim);
-		ehci->reclaim_ready = 1;
-		bh = 1;
-	}
-
-	/* PCI errors [4.15.2.4] */
-	if (unlikely ((status & STS_FATAL) != 0)) {
-		ehci_err (ehci, "fatal error\n");
-dead:
-		ehci_reset (ehci);
-		/* generic layer kills/unlinks all urbs, then
-		 * uses ehci_stop to clean up the rest
-		 */
-		bh = 1;
-	}
-
-	if (bh)
-		ehci_work (ehci, regs);
-done:
-	spin_unlock (&ehci->lock);
-}
-
-/*-------------------------------------------------------------------------*/
-
-/*
- * non-error returns are a promise to giveback() the urb later
- * we drop ownership so next owner (or urb unlink) can get it
- *
- * urb + dev is in hcd_dev.urb_list
- * we're queueing TDs onto software and hardware lists
- *
- * hcd-specific init for hcpriv hasn't been done yet
- *
- * NOTE:  control, bulk, and interrupt share the same code to append TDs
- * to a (possibly active) QH, and the same QH scanning code.
- */
-static int ehci_urb_enqueue (
-	struct usb_hcd	*hcd,
-	struct urb	*urb,
-	int		mem_flags
-) {
-	struct ehci_hcd		*ehci = hcd_to_ehci (hcd);
-	struct list_head	qtd_list;
-
-	urb->transfer_flags &= ~EHCI_STATE_UNLINK;
-	INIT_LIST_HEAD (&qtd_list);
-
-	switch (usb_pipetype (urb->pipe)) {
-	// case PIPE_CONTROL:
-	// case PIPE_BULK:
-	default:
-		if (!qh_urb_transaction (ehci, urb, &qtd_list, mem_flags))
-			return -ENOMEM;
-		return submit_async (ehci, urb, &qtd_list, mem_flags);
-
-	case PIPE_INTERRUPT:
-		if (!qh_urb_transaction (ehci, urb, &qtd_list, mem_flags))
-			return -ENOMEM;
-		return intr_submit (ehci, urb, &qtd_list, mem_flags);
-
-	case PIPE_ISOCHRONOUS:
-		if (urb->dev->speed == USB_SPEED_HIGH)
-			return itd_submit (ehci, urb, mem_flags);
-#ifdef have_split_iso
-		else
-			return sitd_submit (ehci, urb, mem_flags);
-#else
-		dbg ("no split iso support yet");
-		return -ENOSYS;
-#endif /* have_split_iso */
-	}
-}
-
-/* remove from hardware lists
- * completions normally happen asynchronously
- */
-
-static int ehci_urb_dequeue (struct usb_hcd *hcd, struct urb *urb)
-{
-	struct ehci_hcd		*ehci = hcd_to_ehci (hcd);
-	struct ehci_qh		*qh;
-	unsigned long		flags;
-
-	spin_lock_irqsave (&ehci->lock, flags);
-	switch (usb_pipetype (urb->pipe)) {
-	// case PIPE_CONTROL:
-	// case PIPE_BULK:
-	default:
-		qh = (struct ehci_qh *) urb->hcpriv;
-		if (!qh)
-			break;
-
-		/* if we need to use IAA and it's busy, defer */
-		if (qh->qh_state == QH_STATE_LINKED
-				&& ehci->reclaim
-				&& HCD_IS_RUNNING (ehci->hcd.state)
-				) {
-			struct ehci_qh		*last;
-
-			for (last = ehci->reclaim;
-					last->reclaim;
-					last = last->reclaim)
-				continue;
-			qh->qh_state = QH_STATE_UNLINK_WAIT;
-			last->reclaim = qh;
-
-		/* bypass IAA if the hc can't care */
-		} else if (!HCD_IS_RUNNING (ehci->hcd.state) && ehci->reclaim)
-			end_unlink_async (ehci, NULL);
-
-		/* something else might have unlinked the qh by now */
-		if (qh->qh_state == QH_STATE_LINKED)
-			start_unlink_async (ehci, qh);
-		break;
-
-	case PIPE_INTERRUPT:
-		qh = (struct ehci_qh *) urb->hcpriv;
-		if (!qh)
-			break;
-		if (qh->qh_state == QH_STATE_LINKED) {
-			/* messy, can spin or block a microframe ... */
-			intr_deschedule (ehci, qh, 1);
-			/* qh_state == IDLE */
-		}
-		qh_completions (ehci, qh, NULL);
-
-		/* reschedule QH iff another request is queued */
-		if (!list_empty (&qh->qtd_list)
-				&& HCD_IS_RUNNING (ehci->hcd.state)) {
-			int status;
-
-			status = qh_schedule (ehci, qh);
-			spin_unlock_irqrestore (&ehci->lock, flags);
-
-			if (status != 0) {
-				// shouldn't happen often, but ...
-				// FIXME kill those tds' urbs
-				err ("can't reschedule qh %p, err %d",
-					qh, status);
-			}
-			return status;
-		}
-		break;
-
-	case PIPE_ISOCHRONOUS:
-		// itd or sitd ...
-
-		// wait till next completion, do it then.
-		// completion irqs can wait up to 1024 msec,
-		urb->transfer_flags |= EHCI_STATE_UNLINK;
-		break;
-	}
-	spin_unlock_irqrestore (&ehci->lock, flags);
-	return 0;
-}
-
-/*-------------------------------------------------------------------------*/
-
-// bulk qh holds the data toggle
-
-static void ehci_free_config (struct usb_hcd *hcd, struct usb_device *udev)
-{
-	struct hcd_dev		*dev = (struct hcd_dev *)udev->hcpriv;
-	struct ehci_hcd		*ehci = hcd_to_ehci (hcd);
-	int			i;
-	unsigned long		flags;
-
-	/* ASSERT:  no requests/urbs are still linked (so no TDs) */
-	/* ASSERT:  nobody can be submitting urbs for this any more */
-
-	dbg ("%s: free_config devnum %d",
-		hcd_to_bus (hcd)->bus_name, udev->devnum);
-
-	spin_lock_irqsave (&ehci->lock, flags);
-	for (i = 0; i < 32; i++) {
-		if (dev->ep [i]) {
-			struct ehci_qh		*qh;
-			char			*why;
-
-			/* dev->ep never has ITDs or SITDs */
-			qh = (struct ehci_qh *) dev->ep [i];
-
-			/* detect/report non-recoverable errors */
-			if (in_interrupt ()) 
-				why = "disconnect() didn't";
-			else if ((qh->hw_info2 & cpu_to_le32 (0xffff)) != 0
-					&& qh->qh_state != QH_STATE_IDLE)
-				why = "(active periodic)";
-			else
-				why = 0;
-			if (why) {
-				err ("dev %s-%s ep %d-%s error: %s",
-					hcd_to_bus (hcd)->bus_name,
-					udev->devpath,
-					i & 0xf, (i & 0x10) ? "IN" : "OUT",
-					why);
-				BUG ();
-			}
-
-			dev->ep [i] = 0;
-			if (qh->qh_state == QH_STATE_IDLE)
-				goto idle;
-			dbg ("free_config, async ep 0x%02x qh %p", i, qh);
-
-			/* scan_async() empties the ring as it does its work,
-			 * using IAA, but doesn't (yet?) turn it off.  if it
-			 * doesn't empty this qh, likely it's the last entry.
-			 */
-			while (qh->qh_state == QH_STATE_LINKED
-					&& ehci->reclaim
-					&& HCD_IS_RUNNING (ehci->hcd.state)
-					) {
-				spin_unlock_irqrestore (&ehci->lock, flags);
-				/* wait_ms() won't spin, we're a thread;
-				 * and we know IRQ/timer/... can progress
-				 */
-				wait_ms (1);
-				spin_lock_irqsave (&ehci->lock, flags);
-			}
-			if (qh->qh_state == QH_STATE_LINKED)
-				start_unlink_async (ehci, qh);
-			while (qh->qh_state != QH_STATE_IDLE
-					&& ehci->hcd.state != USB_STATE_HALT) {
-				spin_unlock_irqrestore (&ehci->lock, flags);
-				wait_ms (1);
-				spin_lock_irqsave (&ehci->lock, flags);
-			}
-idle:
-			qh_put (ehci, qh);
-		}
-	}
-
-	spin_unlock_irqrestore (&ehci->lock, flags);
-}
-
-/*-------------------------------------------------------------------------*/
-
-static const struct hc_driver ehci_driver = {
-	.description =		hcd_name,
-
-	/*
-	 * generic hardware linkage
-	 */
-	.irq =			ehci_irq,
-	.flags =		HCD_MEMORY | HCD_USB2,
-
-	/*
-	 * basic lifecycle operations
-	 */
-	.start =		ehci_start,
-#ifdef	CONFIG_PM
-	.suspend =		ehci_suspend,
-	.resume =		ehci_resume,
-#endif
-	.stop =			ehci_stop,
-
-	/*
-	 * memory lifecycle (except per-request)
-	 */
-	.hcd_alloc =		ehci_hcd_alloc,
-	.hcd_free =		ehci_hcd_free,
-
-	/*
-	 * managing i/o requests and associated device resources
-	 */
-	.urb_enqueue =		ehci_urb_enqueue,
-	.urb_dequeue =		ehci_urb_dequeue,
-	.free_config =		ehci_free_config,
-
-	/*
-	 * scheduling support
-	 */
-	.get_frame_number =	ehci_get_frame,
-
-	/*
-	 * root hub support
-	 */
-	.hub_status_data =	ehci_hub_status_data,
-	.hub_control =		ehci_hub_control,
-};
-
-/*-------------------------------------------------------------------------*/
-
-/* EHCI spec says PCI is required. */
-
-/* PCI driver selection metadata; PCI hotplugging uses this */
-static const struct pci_device_id __devinitdata pci_ids [] = { {
-
-	/* handle any USB 2.0 EHCI controller */
-
-	.class = 		((PCI_CLASS_SERIAL_USB << 8) | 0x20),
-	.class_mask = 	~0,
-	.driver_data =	(unsigned long) &ehci_driver,
-
-	/* no matter who makes it */
-	.vendor =	PCI_ANY_ID,
-	.device =	PCI_ANY_ID,
-	.subvendor =	PCI_ANY_ID,
-	.subdevice =	PCI_ANY_ID,
-
-}, { /* end: all zeroes */ }
-};
-MODULE_DEVICE_TABLE (pci, pci_ids);
-
-/* pci driver glue; this is a "new style" PCI driver module */
-static struct pci_driver ehci_pci_driver = {
-	.name =		(char *) hcd_name,
-	.id_table =	pci_ids,
-
-	.probe =	usb_hcd_pci_probe,
-	.remove =	usb_hcd_pci_remove,
-
-#ifdef	CONFIG_PM
-	.suspend =	usb_hcd_pci_suspend,
-	.resume =	usb_hcd_pci_resume,
-#endif
-};
-
-#define DRIVER_INFO DRIVER_VERSION " " DRIVER_DESC
-
-MODULE_DESCRIPTION (DRIVER_INFO);
-MODULE_AUTHOR (DRIVER_AUTHOR);
-MODULE_LICENSE ("GPL");
-
-static int __init init (void) 
-{
-	dbg (DRIVER_INFO);
-	dbg ("block sizes: qh %Zd qtd %Zd itd %Zd sitd %Zd",
-		sizeof (struct ehci_qh), sizeof (struct ehci_qtd),
-		sizeof (struct ehci_itd), sizeof (struct ehci_sitd));
-
-	return pci_module_init (&ehci_pci_driver);
-}
-module_init (init);
-
-static void __exit cleanup (void) 
-{	
-	pci_unregister_driver (&ehci_pci_driver);
-}
-module_exit (cleanup);
diff -urN linux-2.4.21-pre5/drivers/usb/hcd/ehci-hub.c linux-2.4.21-pre6/drivers/usb/hcd/ehci-hub.c
--- linux-2.4.21-pre5/drivers/usb/hcd/ehci-hub.c	Wed Mar 26 16:11:29 2003
+++ linux-2.4.21-pre6/drivers/usb/hcd/ehci-hub.c	Wed Dec 31 16:00:00 1969
@@ -1,344 +0,0 @@
-/*
- * Copyright (c) 2001-2002 by David Brownell
- * 
- * 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.
- *
- * 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.
- */
-
-/* this file is part of ehci-hcd.c */
-
-/*-------------------------------------------------------------------------*/
-
-/*
- * EHCI Root Hub ... the nonsharable stuff
- *
- * Registers don't need cpu_to_le32, that happens transparently
- */
-
-/*-------------------------------------------------------------------------*/
-
-static int check_reset_complete (
-	struct ehci_hcd	*ehci,
-	int		index,
-	int		port_status
-) {
-	if (!(port_status & PORT_CONNECT)) {
-		ehci->reset_done [index] = 0;
-		return port_status;
-	}
-
-	/* if reset finished and it's still not enabled -- handoff */
-	if (!(port_status & PORT_PE)) {
-		ehci_dbg (ehci, "port %d full speed --> companion\n",
-			index + 1);
-
-		// what happens if HCS_N_CC(params) == 0 ?
-		port_status |= PORT_OWNER;
-		writel (port_status, &ehci->regs->port_status [index]);
-
-	} else
-		ehci_dbg (ehci, "port %d high speed\n", index + 1);
-
-	return port_status;
-}
-
-/*-------------------------------------------------------------------------*/
-
-
-/* build "status change" packet (one or two bytes) from HC registers */
-
-static int
-ehci_hub_status_data (struct usb_hcd *hcd, char *buf)
-{
-	struct ehci_hcd	*ehci = hcd_to_ehci (hcd);
-	u32		temp, status = 0;
-	int		ports, i, retval = 1;
-	unsigned long	flags;
-
-	/* init status to no-changes */
-	buf [0] = 0;
-	ports = HCS_N_PORTS (ehci->hcs_params);
-	if (ports > 7) {
-		buf [1] = 0;
-		retval++;
-	}
-	
-	/* no hub change reports (bit 0) for now (power, ...) */
-
-	/* port N changes (bit N)? */
-	spin_lock_irqsave (&ehci->lock, flags);
-	for (i = 0; i < ports; i++) {
-		temp = readl (&ehci->regs->port_status [i]);
-		if (temp & PORT_OWNER) {
-			/* don't report this in GetPortStatus */
-			if (temp & PORT_CSC) {
-				temp &= ~PORT_CSC;
-				writel (temp, &ehci->regs->port_status [i]);
-			}
-			continue;
-		}
-		if (!(temp & PORT_CONNECT))
-			ehci->reset_done [i] = 0;
-		if ((temp & (PORT_CSC | PORT_PEC | PORT_OCC)) != 0) {
-			if (i < 7)
-			    buf [0] |= 1 << (i + 1);
-			else
-			    buf [1] |= 1 << (i - 7);
-			status = STS_PCD;
-		}
-	}
-	spin_unlock_irqrestore (&ehci->lock, flags);
-	return status ? retval : 0;
-}
-
-/*-------------------------------------------------------------------------*/
-
-static void
-ehci_hub_descriptor (
-	struct ehci_hcd			*ehci,
-	struct usb_hub_descriptor	*desc
-) {
-	int		ports = HCS_N_PORTS (ehci->hcs_params);
-	u16		temp;
-
-	desc->bDescriptorType = 0x29;
-	desc->bPwrOn2PwrGood = 0;	/* FIXME: f(system power) */
-	desc->bHubContrCurrent = 0;
-
-	desc->bNbrPorts = ports;
-	temp = 1 + (ports / 8);
-	desc->bDescLength = 7 + 2 * temp;
-
-	/* two bitmaps:  ports removable, and usb 1.0 legacy PortPwrCtrlMask */
-	memset (&desc->bitmap [0], 0, temp);
-	memset (&desc->bitmap [temp], 0xff, temp);
-
-	temp = 0x0008;			/* per-port overcurrent reporting */
-	if (HCS_PPC (ehci->hcs_params))
-		temp |= 0x0001;		/* per-port power control */
-	if (HCS_INDICATOR (ehci->hcs_params))
-		temp |= 0x0080;		/* per-port indicators (LEDs) */
-	desc->wHubCharacteristics = cpu_to_le16 (temp);
-}
-
-/*-------------------------------------------------------------------------*/
-
-static int ehci_hub_control (
-	struct usb_hcd	*hcd,
-	u16		typeReq,
-	u16		wValue,
-	u16		wIndex,
-	char		*buf,
-	u16		wLength
-) {
-	struct ehci_hcd	*ehci = hcd_to_ehci (hcd);
-	int		ports = HCS_N_PORTS (ehci->hcs_params);
-	u32		temp, status;
-	unsigned long	flags;
-	int		retval = 0;
-
-	/*
-	 * FIXME:  support SetPortFeatures USB_PORT_FEAT_INDICATOR.
-	 * HCS_INDICATOR may say we can change LEDs to off/amber/green.
-	 * (track current state ourselves) ... blink for diagnostics,
-	 * power, "this is the one", etc.  EHCI spec supports this.
-	 */
-
-	spin_lock_irqsave (&ehci->lock, flags);
-	switch (typeReq) {
-	case ClearHubFeature:
-		switch (wValue) {
-		case C_HUB_LOCAL_POWER:
-		case C_HUB_OVER_CURRENT:
-			/* no hub-wide feature/status flags */
-			break;
-		default:
-			goto error;
-		}
-		break;
-	case ClearPortFeature:
-		if (!wIndex || wIndex > ports)
-			goto error;
-		wIndex--;
-		temp = readl (&ehci->regs->port_status [wIndex]);
-		if (temp & PORT_OWNER)
-			break;
-
-		switch (wValue) {
-		case USB_PORT_FEAT_ENABLE:
-			writel (temp & ~PORT_PE,
-				&ehci->regs->port_status [wIndex]);
-			break;
-		case USB_PORT_FEAT_C_ENABLE:
-			writel (temp | PORT_PEC,
-				&ehci->regs->port_status [wIndex]);
-			break;
-		case USB_PORT_FEAT_SUSPEND:
-		case USB_PORT_FEAT_C_SUSPEND:
-			/* ? */
-			break;
-		case USB_PORT_FEAT_POWER:
-			if (HCS_PPC (ehci->hcs_params))
-				writel (temp & ~PORT_POWER,
-					&ehci->regs->port_status [wIndex]);
-			break;
-		case USB_PORT_FEAT_C_CONNECTION:
-			writel (temp | PORT_CSC,
-				&ehci->regs->port_status [wIndex]);
-			break;
-		case USB_PORT_FEAT_C_OVER_CURRENT:
-			writel (temp | PORT_OCC,
-				&ehci->regs->port_status [wIndex]);
-			break;
-		case USB_PORT_FEAT_C_RESET:
-			/* GetPortStatus clears reset */
-			break;
-		default:
-			goto error;
-		}
-		readl (&ehci->regs->command);	/* unblock posted write */
-		break;
-	case GetHubDescriptor:
-		ehci_hub_descriptor (ehci, (struct usb_hub_descriptor *)
-			buf);
-		break;
-	case GetHubStatus:
-		/* no hub-wide feature/status flags */
-		memset (buf, 0, 4);
-		//cpu_to_le32s ((u32 *) buf);
-		break;
-	case GetPortStatus:
-		if (!wIndex || wIndex > ports)
-			goto error;
-		wIndex--;
-		status = 0;
-		temp = readl (&ehci->regs->port_status [wIndex]);
-
-		// wPortChange bits
-		if (temp & PORT_CSC)
-			status |= 1 << USB_PORT_FEAT_C_CONNECTION;
-		if (temp & PORT_PEC)
-			status |= 1 << USB_PORT_FEAT_C_ENABLE;
-		// USB_PORT_FEAT_C_SUSPEND
-		if (temp & PORT_OCC)
-			status |= 1 << USB_PORT_FEAT_C_OVER_CURRENT;
-
-		/* whoever resets must GetPortStatus to complete it!! */
-		if ((temp & PORT_RESET)
-				&& time_after (jiffies,
-					ehci->reset_done [wIndex])) {
-			status |= 1 << USB_PORT_FEAT_C_RESET;
-
-			/* force reset to complete */
-			writel (temp & ~PORT_RESET,
-					&ehci->regs->port_status [wIndex]);
-			do {
-				temp = readl (
-					&ehci->regs->port_status [wIndex]);
-				udelay (10);
-			} while (temp & PORT_RESET);
-
-			/* see what we found out */
-			temp = check_reset_complete (ehci, wIndex, temp);
-		}
-
-		// don't show wPortStatus if it's owned by a companion hc
-		if (!(temp & PORT_OWNER)) {
-			if (temp & PORT_CONNECT) {
-				status |= 1 << USB_PORT_FEAT_CONNECTION;
-				status |= 1 << USB_PORT_FEAT_HIGHSPEED;
-			}
-			if (temp & PORT_PE)
-				status |= 1 << USB_PORT_FEAT_ENABLE;
-			if (temp & PORT_SUSPEND)
-				status |= 1 << USB_PORT_FEAT_SUSPEND;
-			if (temp & PORT_OC)
-				status |= 1 << USB_PORT_FEAT_OVER_CURRENT;
-			if (temp & PORT_RESET)
-				status |= 1 << USB_PORT_FEAT_RESET;
-			if (temp & PORT_POWER)
-				status |= 1 << USB_PORT_FEAT_POWER;
-		}
-
-#ifndef	EHCI_VERBOSE_DEBUG
-	if (status & ~0xffff)	/* only if wPortChange is interesting */
-#endif
-		dbg_port (ehci, "GetStatus", wIndex + 1, temp);
-		// we "know" this alignment is good, caller used kmalloc()...
-		*((u32 *) buf) = cpu_to_le32 (status);
-		break;
-	case SetHubFeature:
-		switch (wValue) {
-		case C_HUB_LOCAL_POWER:
-		case C_HUB_OVER_CURRENT:
-			/* no hub-wide feature/status flags */
-			break;
-		default:
-			goto error;
-		}
-		break;
-	case SetPortFeature:
-		if (!wIndex || wIndex > ports)
-			goto error;
-		wIndex--;
-		temp = readl (&ehci->regs->port_status [wIndex]);
-		if (temp & PORT_OWNER)
-			break;
-
-		switch (wValue) {
-		case USB_PORT_FEAT_SUSPEND:
-			writel (temp | PORT_SUSPEND,
-				&ehci->regs->port_status [wIndex]);
-			break;
-		case USB_PORT_FEAT_POWER:
-			if (HCS_PPC (ehci->hcs_params))
-				writel (temp | PORT_POWER,
-					&ehci->regs->port_status [wIndex]);
-			break;
-		case USB_PORT_FEAT_RESET:
-			/* line status bits may report this as low speed */
-			if ((temp & (PORT_PE|PORT_CONNECT)) == PORT_CONNECT
-					&& PORT_USB11 (temp)) {
-				ehci_dbg (ehci,
-					"port %d low speed --> companion\n",
-					wIndex + 1);
-				temp |= PORT_OWNER;
-			} else {
-				ehci_vdbg (ehci, "port %d reset\n", wIndex + 1);
-				temp |= PORT_RESET;
-				temp &= ~PORT_PE;
-
-				/*
-				 * caller must wait, then call GetPortStatus
-				 * usb 2.0 spec says 50 ms resets on root
-				 */
-				ehci->reset_done [wIndex] = jiffies
-				    	+ ((50 /* msec */ * HZ) / 1000);
-			}
-			writel (temp, &ehci->regs->port_status [wIndex]);
-			break;
-		default:
-			goto error;
-		}
-		readl (&ehci->regs->command);	/* unblock posted writes */
-		break;
-
-	default:
-error:
-		/* "stall" on error */
-		retval = -EPIPE;
-	}
-	spin_unlock_irqrestore (&ehci->lock, flags);
-	return retval;
-}
diff -urN linux-2.4.21-pre5/drivers/usb/hcd/ehci-mem.c linux-2.4.21-pre6/drivers/usb/hcd/ehci-mem.c
--- linux-2.4.21-pre5/drivers/usb/hcd/ehci-mem.c	Wed Mar 26 16:11:29 2003
+++ linux-2.4.21-pre6/drivers/usb/hcd/ehci-mem.c	Wed Dec 31 16:00:00 1969
@@ -1,251 +0,0 @@
-/*
- * Copyright (c) 2001 by David Brownell
- * 
- * 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.
- *
- * 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.
- */
-
-/* this file is part of ehci-hcd.c */
-
-/*-------------------------------------------------------------------------*/
-
-/*
- * There's basically three types of memory:
- *	- data used only by the HCD ... kmalloc is fine
- *	- async and periodic schedules, shared by HC and HCD ... these
- *	  need to use pci_pool or pci_alloc_consistent
- *	- driver buffers, read/written by HC ... single shot DMA mapped 
- *
- * There's also PCI "register" data, which is memory mapped.
- * No memory seen by this driver is pagable.
- */
-
-/*-------------------------------------------------------------------------*/
-/* 
- * Allocator / cleanup for the per device structure
- * Called by hcd init / removal code
- */
-static struct usb_hcd *ehci_hcd_alloc (void)
-{
-	struct ehci_hcd *ehci;
-
-	ehci = (struct ehci_hcd *)
-		kmalloc (sizeof (struct ehci_hcd), GFP_KERNEL);
-	if (ehci != 0) {
-		memset (ehci, 0, sizeof (struct ehci_hcd));
-		return &ehci->hcd;
-	}
-	return 0;
-}
-
-static void ehci_hcd_free (struct usb_hcd *hcd)
-{
-	kfree (hcd_to_ehci (hcd));
-}
-
-/*-------------------------------------------------------------------------*/
-
-/* Allocate the key transfer structures from the previously allocated pool */
-
-static inline void ehci_qtd_init (struct ehci_qtd *qtd, dma_addr_t dma)
-{
-	memset (qtd, 0, sizeof *qtd);
-	qtd->qtd_dma = dma;
-	qtd->hw_next = EHCI_LIST_END;
-	qtd->hw_alt_next = EHCI_LIST_END;
-	INIT_LIST_HEAD (&qtd->qtd_list);
-}
-
-static struct ehci_qtd *ehci_qtd_alloc (struct ehci_hcd *ehci, int flags)
-{
-	struct ehci_qtd		*qtd;
-	dma_addr_t		dma;
-
-	qtd = pci_pool_alloc (ehci->qtd_pool, flags, &dma);
-	if (qtd != 0) {
-		ehci_qtd_init (qtd, dma);
-	}
-	return qtd;
-}
-
-static inline void ehci_qtd_free (struct ehci_hcd *ehci, struct ehci_qtd *qtd)
-{
-	pci_pool_free (ehci->qtd_pool, qtd, qtd->qtd_dma);
-}
-
-
-static struct ehci_qh *ehci_qh_alloc (struct ehci_hcd *ehci, int flags)
-{
-	struct ehci_qh		*qh;
-	dma_addr_t		dma;
-
-	qh = (struct ehci_qh *)
-		pci_pool_alloc (ehci->qh_pool, flags, &dma);
-	if (!qh)
-		return qh;
-
-	memset (qh, 0, sizeof *qh);
-	atomic_set (&qh->refcount, 1);
-	qh->qh_dma = dma;
-	// INIT_LIST_HEAD (&qh->qh_list);
-	INIT_LIST_HEAD (&qh->qtd_list);
-
-	/* dummy td enables safe urb queuing */
-	qh->dummy = ehci_qtd_alloc (ehci, flags);
-	if (qh->dummy == 0) {
-		ehci_dbg (ehci, "no dummy td\n");
-		pci_pool_free (ehci->qh_pool, qh, qh->qh_dma);
-		qh = 0;
-	}
-	return qh;
-}
-
-/* to share a qh (cpu threads, or hc) */
-static inline struct ehci_qh *qh_get (/* ehci, */ struct ehci_qh *qh)
-{
-	atomic_inc (&qh->refcount);
-	return qh;
-}
-
-static void qh_put (struct ehci_hcd *ehci, struct ehci_qh *qh)
-{
-	if (!atomic_dec_and_test (&qh->refcount))
-		return;
-	/* clean qtds first, and know this is not linked */
-	if (!list_empty (&qh->qtd_list) || qh->qh_next.ptr) {
-		ehci_dbg (ehci, "unused qh not empty!\n");
-		BUG ();
-	}
-	if (qh->dummy)
-		ehci_qtd_free (ehci, qh->dummy);
-	pci_pool_free (ehci->qh_pool, qh, qh->qh_dma);
-}
-
-/*-------------------------------------------------------------------------*/
-
-/* The queue heads and transfer descriptors are managed from pools tied 
- * to each of the "per device" structures.
- * This is the initialisation and cleanup code.
- */
-
-static void ehci_mem_cleanup (struct ehci_hcd *ehci)
-{
-	if (ehci->async)
-		qh_put (ehci, ehci->async);
-	ehci->async = 0;
-
-	/* PCI consistent memory and pools */
-	if (ehci->qtd_pool)
-		pci_pool_destroy (ehci->qtd_pool);
-	ehci->qtd_pool = 0;
-
-	if (ehci->qh_pool) {
-		pci_pool_destroy (ehci->qh_pool);
-		ehci->qh_pool = 0;
-	}
-
-	if (ehci->itd_pool)
-		pci_pool_destroy (ehci->itd_pool);
-	ehci->itd_pool = 0;
-
-	if (ehci->sitd_pool)
-		pci_pool_destroy (ehci->sitd_pool);
-	ehci->sitd_pool = 0;
-
-	if (ehci->periodic)
-		pci_free_consistent (ehci->hcd.pdev,
-			ehci->periodic_size * sizeof (u32),
-			ehci->periodic, ehci->periodic_dma);
-	ehci->periodic = 0;
-
-	/* shadow periodic table */
-	if (ehci->pshadow)
-		kfree (ehci->pshadow);
-	ehci->pshadow = 0;
-}
-
-/* remember to add cleanup code (above) if you add anything here */
-static int ehci_mem_init (struct ehci_hcd *ehci, int flags)
-{
-	int i;
-
-	/* QTDs for control/bulk/intr transfers */
-	ehci->qtd_pool = pci_pool_create ("ehci_qtd", ehci->hcd.pdev,
-			sizeof (struct ehci_qtd),
-			32 /* byte alignment (for hw parts) */,
-			4096 /* can't cross 4K */,
-			flags);
-	if (!ehci->qtd_pool) {
-		goto fail;
-	}
-
-	/* QHs for control/bulk/intr transfers */
-	ehci->qh_pool = pci_pool_create ("ehci_qh", ehci->hcd.pdev,
-			sizeof (struct ehci_qh),
-			32 /* byte alignment (for hw parts) */,
-			4096 /* can't cross 4K */,
-			flags);
-	if (!ehci->qh_pool) {
-		goto fail;
-	}
-	ehci->async = ehci_qh_alloc (ehci, flags);
-	if (!ehci->async) {
-		goto fail;
-	}
-
-	/* ITD for high speed ISO transfers */
-	ehci->itd_pool = pci_pool_create ("ehci_itd", ehci->hcd.pdev,
-			sizeof (struct ehci_itd),
-			32 /* byte alignment (for hw parts) */,
-			4096 /* can't cross 4K */,
-			flags);
-	if (!ehci->itd_pool) {
-		goto fail;
-	}
-
-	/* SITD for full/low speed split ISO transfers */
-	ehci->sitd_pool = pci_pool_create ("ehci_sitd", ehci->hcd.pdev,
-			sizeof (struct ehci_sitd),
-			32 /* byte alignment (for hw parts) */,
-			4096 /* can't cross 4K */,
-			flags);
-	if (!ehci->sitd_pool) {
-		goto fail;
-	}
-
-	/* Hardware periodic table */
-	ehci->periodic = (u32 *)
-		pci_alloc_consistent (ehci->hcd.pdev,
-			ehci->periodic_size * sizeof (u32),
-			&ehci->periodic_dma);
-	if (ehci->periodic == 0) {
-		goto fail;
-	}
-	for (i = 0; i < ehci->periodic_size; i++)
-		ehci->periodic [i] = EHCI_LIST_END;
-
-	/* software shadow of hardware table */
-	ehci->pshadow = kmalloc (ehci->periodic_size * sizeof (void *), flags);
-	if (ehci->pshadow == 0) {
-		goto fail;
-	}
-	memset (ehci->pshadow, 0, ehci->periodic_size * sizeof (void *));
-
-	return 0;
-
-fail:
-	ehci_dbg (ehci, "couldn't init memory\n");
-	ehci_mem_cleanup (ehci);
-	return -ENOMEM;
-}
diff -urN linux-2.4.21-pre5/drivers/usb/hcd/ehci-q.c linux-2.4.21-pre6/drivers/usb/hcd/ehci-q.c
--- linux-2.4.21-pre5/drivers/usb/hcd/ehci-q.c	Wed Mar 26 16:11:29 2003
+++ linux-2.4.21-pre6/drivers/usb/hcd/ehci-q.c	Wed Dec 31 16:00:00 1969
@@ -1,1090 +0,0 @@
-/*
- * Copyright (c) 2001-2002 by David Brownell
- * 
- * 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.
- *
- * 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.
- */
-
-/* this file is part of ehci-hcd.c */
-
-/*-------------------------------------------------------------------------*/
-
-/*
- * EHCI hardware queue manipulation ... the core.  QH/QTD manipulation.
- *
- * Control, bulk, and interrupt traffic all use "qh" lists.  They list "qtd"
- * entries describing USB transactions, max 16-20kB/entry (with 4kB-aligned
- * buffers needed for the larger number).  We use one QH per endpoint, queue
- * multiple urbs (all three types) per endpoint.  URBs may need several qtds.
- *
- * ISO traffic uses "ISO TD" (itd, and sitd) records, and (along with
- * interrupts) needs careful scheduling.  Performance improvements can be
- * an ongoing challenge.  That's in "ehci-sched.c".
- * 
- * USB 1.1 devices are handled (a) by "companion" OHCI or UHCI root hubs,
- * or otherwise through transaction translators (TTs) in USB 2.0 hubs using
- * (b) special fields in qh entries or (c) split iso entries.  TTs will
- * buffer low/full speed data so the host collects it at high speed.
- */
-
-/*-------------------------------------------------------------------------*/
-
-/* fill a qtd, returning how much of the buffer we were able to queue up */
-
-static int
-qtd_fill (struct ehci_qtd *qtd, dma_addr_t buf, size_t len,
-		int token, int maxpacket)
-{
-	int	i, count;
-	u64	addr = buf;
-
-	/* one buffer entry per 4K ... first might be short or unaligned */
-	qtd->hw_buf [0] = cpu_to_le32 ((u32)addr);
-	qtd->hw_buf_hi [0] = cpu_to_le32 ((u32)(addr >> 32));
-	count = 0x1000 - (buf & 0x0fff);	/* rest of that page */
-	if (likely (len < count))		/* ... iff needed */
-		count = len;
-	else {
-		buf +=  0x1000;
-		buf &= ~0x0fff;
-
-		/* per-qtd limit: from 16K to 20K (best alignment) */
-		for (i = 1; count < len && i < 5; i++) {
-			addr = buf;
-			qtd->hw_buf [i] = cpu_to_le32 ((u32)addr);
-			qtd->hw_buf_hi [i] = cpu_to_le32 ((u32)(addr >> 32));
-			buf += 0x1000;
-			if ((count + 0x1000) < len)
-				count += 0x1000;
-			else
-				count = len;
-		}
-
-		/* short packets may only terminate transfers */
-		if (count != len)
-			count -= (count % maxpacket);
-	}
-	qtd->hw_token = cpu_to_le32 ((count << 16) | token);
-	qtd->length = count;
-
-	return count;
-}
-
-/*-------------------------------------------------------------------------*/
-
-/* update halted (but potentially linked) qh */
-
-static inline void
-qh_update (struct ehci_hcd *ehci, struct ehci_qh *qh, struct ehci_qtd *qtd)
-{
-	qh->hw_current = 0;
-	qh->hw_qtd_next = QTD_NEXT (qtd->qtd_dma);
-	qh->hw_alt_next = EHCI_LIST_END;
-
-	/* HC must see latest qtd and qh data before we clear ACTIVE+HALT */
-	wmb ();
-	qh->hw_token &= __constant_cpu_to_le32 (QTD_TOGGLE | QTD_STS_PING);
-}
-
-/*-------------------------------------------------------------------------*/
-
-#define IS_SHORT_READ(token) (QTD_LENGTH (token) != 0 && QTD_PID (token) == 1)
-
-static void qtd_copy_status (
-	struct ehci_hcd *ehci,
-	struct urb *urb,
-	size_t length,
-	u32 token
-)
-{
-	/* count IN/OUT bytes, not SETUP (even short packets) */
-	if (likely (QTD_PID (token) != 2))
-		urb->actual_length += length - QTD_LENGTH (token);
-
-	/* don't modify error codes */
-	if (unlikely (urb->status != -EINPROGRESS))
-		return;
-
-	/* force cleanup after short read; not always an error */
-	if (unlikely (IS_SHORT_READ (token)))
-		urb->status = -EREMOTEIO;
-
-	/* serious "can't proceed" faults reported by the hardware */
-	if (token & QTD_STS_HALT) {
-		if (token & QTD_STS_BABBLE) {
-			/* FIXME "must" disable babbling device's port too */
-			urb->status = -EOVERFLOW;
-		} else if (token & QTD_STS_MMF) {
-			/* fs/ls interrupt xfer missed the complete-split */
-			urb->status = -EPROTO;
-		} else if (token & QTD_STS_DBE) {
-			urb->status = (QTD_PID (token) == 1) /* IN ? */
-				? -ENOSR  /* hc couldn't read data */
-				: -ECOMM; /* hc couldn't write data */
-		} else if (token & QTD_STS_XACT) {
-			/* timeout, bad crc, wrong PID, etc; retried */
-			if (QTD_CERR (token))
-				urb->status = -EPIPE;
-			else {
-				dbg ("3strikes");
-				urb->status = -EPROTO;
-			}
-		/* CERR nonzero + no errors + halt --> stall */
-		} else if (QTD_CERR (token))
-			urb->status = -EPIPE;
-		else	/* unknown */
-			urb->status = -EPROTO;
-
-		ehci_vdbg (ehci,
-			"dev%d ep%d%s qtd token %08x --> status %d\n",
-			usb_pipedevice (urb->pipe),
-			usb_pipeendpoint (urb->pipe),
-			usb_pipein (urb->pipe) ? "in" : "out",
-			token, urb->status);
-
-		/* stall indicates some recovery action is needed */
-		if (urb->status == -EPIPE) {
-			int	pipe = urb->pipe;
-
-			if (!usb_pipecontrol (pipe))
-				usb_endpoint_halt (urb->dev,
-					usb_pipeendpoint (pipe),
-					usb_pipeout (pipe));
-			if (urb->dev->tt && !usb_pipeint (pipe)) {
-#ifdef DEBUG
-				struct usb_device *tt = urb->dev->tt->hub;
-				dbg ("clear tt %s-%s p%d buffer, a%d ep%d",
-					tt->bus->bus_name, tt->devpath,
-    					urb->dev->ttport, urb->dev->devnum,
-    					usb_pipeendpoint (pipe));
-#endif /* DEBUG */
-				usb_hub_tt_clear_buffer (urb->dev, pipe);
-			}
-		}
-	}
-}
-
-static void
-ehci_urb_done (struct ehci_hcd *ehci, struct urb *urb, struct pt_regs *regs)
-{
-#ifdef	INTR_AUTOMAGIC
-	struct urb		*resubmit = 0;
-	struct usb_device	*dev = 0;
-
-	static int ehci_urb_enqueue (struct usb_hcd *, struct urb *, int);
-#endif
-
-	if (likely (urb->hcpriv != 0)) {
-		struct ehci_qh	*qh = (struct ehci_qh *) urb->hcpriv;
-
-		/* S-mask in a QH means it's an interrupt urb */
-		if ((qh->hw_info2 & cpu_to_le32 (0x00ff)) != 0) {
-
-			/* ... update hc-wide periodic stats (for usbfs) */
-			hcd_to_bus (&ehci->hcd)->bandwidth_int_reqs--;
-
-#ifdef	INTR_AUTOMAGIC
-			if (!((urb->status == -ENOENT)
-					|| (urb->status == -ECONNRESET))) {
-				resubmit = usb_get_urb (urb);
-				dev = urb->dev;
-			}
-#endif
-		}
-		qh_put (ehci, qh);
-	}
-
-	spin_lock (&urb->lock);
-	urb->hcpriv = 0;
-	switch (urb->status) {
-	case -EINPROGRESS:		/* success */
-		urb->status = 0;
-	default:			/* fault */
-		COUNT (ehci->stats.complete);
-		break;
-	case -EREMOTEIO:		/* fault or normal */
-		if (!(urb->transfer_flags & URB_SHORT_NOT_OK))
-			urb->status = 0;
-		COUNT (ehci->stats.complete);
-		break;
-	case -ECONNRESET:		/* canceled */
-	case -ENOENT:
-		COUNT (ehci->stats.unlink);
-		break;
-	}
-	spin_unlock (&urb->lock);
-
-	/* complete() can reenter this HCD */
-	spin_unlock (&ehci->lock);
-	usb_hcd_giveback_urb (&ehci->hcd, urb, regs);
-
-#ifdef	INTR_AUTOMAGIC
-	if (resubmit && ((urb->status == -ENOENT)
-				|| (urb->status == -ECONNRESET))) {
-		usb_put_urb (resubmit);
-		resubmit = 0;
-	}
-	// device drivers will soon be doing something like this
-	if (resubmit) {
-		int	status;
-
-		resubmit->dev = dev;
-		status = SUBMIT_URB (resubmit, SLAB_ATOMIC);
-		if (status != 0)
-			err ("can't resubmit interrupt urb %p: status %d",
-					resubmit, status);
-		usb_put_urb (resubmit);
-	}
-#endif
-
-	spin_lock (&ehci->lock);
-}
-
-
-/*
- * Process and free completed qtds for a qh, returning URBs to drivers.
- * Chases up to qh->hw_current.  Returns number of completions called,
- * indicating how much "real" work we did.
- */
-#define HALT_BIT cpu_to_le32(QTD_STS_HALT)
-static unsigned
-qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh, struct pt_regs *regs)
-{
-	struct ehci_qtd		*last = 0, *end = qh->dummy;
-	struct list_head	*entry, *tmp;
-	int			stopped;
-	unsigned		count = 0;
-	int			do_status = 0;
-	u8			state;
-
-	if (unlikely (list_empty (&qh->qtd_list)))
-		return count;
-
-	/* completions (or tasks on other cpus) must never clobber HALT
-	 * till we've gone through and cleaned everything up, even when
-	 * they add urbs to this qh's queue or mark them for unlinking.
-	 *
-	 * NOTE:  unlinking expects to be done in queue order.
-	 */
-	state = qh->qh_state;
-	qh->qh_state = QH_STATE_COMPLETING;
-	stopped = (state == QH_STATE_IDLE);
-
-	/* remove de-activated QTDs from front of queue.
-	 * after faults (including short reads), cleanup this urb
-	 * then let the queue advance.
-	 * if queue is stopped, handles unlinks.
-	 */
-	list_for_each_safe (entry, tmp, &qh->qtd_list) {
-		struct ehci_qtd	*qtd;
-		struct urb	*urb;
-		u32		token = 0;
-
-		qtd = list_entry (entry, struct ehci_qtd, qtd_list);
-		urb = qtd->urb;
-
-		/* clean up any state from previous QTD ...*/
-		if (last) {
-			if (likely (last->urb != urb)) {
-				ehci_urb_done (ehci, last->urb, regs);
-				count++;
-			}
-			ehci_qtd_free (ehci, last);
-			last = 0;
-		}
-
-		/* ignore urbs submitted during completions we reported */
-		if (qtd == end)
-			break;
-
-		/* hardware copies qtd out of qh overlay */
-		rmb ();
-		token = le32_to_cpu (qtd->hw_token);
-		stopped = stopped
-			|| (HALT_BIT & qh->hw_token) != 0
-			|| (ehci->hcd.state == USB_STATE_HALT);
-
-		/* always clean up qtds the hc de-activated */
-		if ((token & QTD_STS_ACTIVE) == 0) {
-
-			/* magic dummy for short reads; won't advance */
-			if (IS_SHORT_READ (token)
-					&& !(token & QTD_STS_HALT)
-					&& (qh->hw_alt_next & QTD_MASK)
-						== ehci->async->hw_alt_next) {
-				stopped = 1;
-				goto halt;
-			}
-
-		/* stop scanning when we reach qtds the hc is using */
-		} else if (likely (!stopped)) {
-			break;
-
-		} else {
-			/* ignore active urbs unless some previous qtd
-			 * for the urb faulted (including short read) or
-			 * its urb was canceled.  we may patch qh or qtds.
-			 */
-			if (likely (urb->status == -EINPROGRESS))
-				continue;
-			
-			/* issue status after short control reads */
-			if (unlikely (do_status != 0)
-					&& QTD_PID (token) == 0 /* OUT */) {
-				do_status = 0;
-				continue;
-			}
-
-			/* token in overlay may be most current */
-			if (state == QH_STATE_IDLE
-					&& cpu_to_le32 (qtd->qtd_dma)
-						== qh->hw_current)
-				token = le32_to_cpu (qh->hw_token);
-
-			/* force halt for unlinked or blocked qh, so we'll
-			 * patch the qh later and so that completions can't
-			 * activate it while we "know" it's stopped.
-			 */
-			if ((HALT_BIT & qh->hw_token) == 0) {
-halt:
-				qh->hw_token |= HALT_BIT;
-				wmb ();
-			}
-		}
- 
-		/* remove it from the queue */
-		spin_lock (&urb->lock);
-		qtd_copy_status (ehci, urb, qtd->length, token);
-		do_status = (urb->status == -EREMOTEIO)
-				&& usb_pipecontrol (urb->pipe);
-		spin_unlock (&urb->lock);
-
-		if (stopped && qtd->qtd_list.prev != &qh->qtd_list) {
-			last = list_entry (qtd->qtd_list.prev,
-					struct ehci_qtd, qtd_list);
-			last->hw_next = qtd->hw_next;
-		}
-		list_del (&qtd->qtd_list);
-		last = qtd;
-	}
-
-	/* last urb's completion might still need calling */
-	if (likely (last != 0)) {
-		ehci_urb_done (ehci, last->urb, regs);
-		count++;
-		ehci_qtd_free (ehci, last);
-	}
-
-	/* restore original state; caller must unlink or relink */
-	qh->qh_state = state;
-
-	/* update qh after fault cleanup */
-	if (unlikely ((HALT_BIT & qh->hw_token) != 0)) {
-		qh_update (ehci, qh,
-			list_empty (&qh->qtd_list)
-				? qh->dummy
-				: list_entry (qh->qtd_list.next,
-					struct ehci_qtd, qtd_list));
-	}
-
-	return count;
-}
-#undef HALT_BIT
-
-/*-------------------------------------------------------------------------*/
-
-/*
- * reverse of qh_urb_transaction:  free a list of TDs.
- * used for cleanup after errors, before HC sees an URB's TDs.
- */
-static void qtd_list_free (
-	struct ehci_hcd		*ehci,
-	struct urb		*urb,
-	struct list_head	*qtd_list
-) {
-	struct list_head	*entry, *temp;
-
-	list_for_each_safe (entry, temp, qtd_list) {
-		struct ehci_qtd	*qtd;
-
-		qtd = list_entry (entry, struct ehci_qtd, qtd_list);
-		list_del (&qtd->qtd_list);
-		ehci_qtd_free (ehci, qtd);
-	}
-}
-
-/*
- * create a list of filled qtds for this URB; won't link into qh.
- */
-static struct list_head *
-qh_urb_transaction (
-	struct ehci_hcd		*ehci,
-	struct urb		*urb,
-	struct list_head	*head,
-	int			flags
-) {
-	struct ehci_qtd		*qtd, *qtd_prev;
-	dma_addr_t		buf;
-	int			len, maxpacket;
-	int			is_input;
-	u32			token;
-
-	/*
-	 * URBs map to sequences of QTDs:  one logical transaction
-	 */
-	qtd = ehci_qtd_alloc (ehci, flags);
-	if (unlikely (!qtd))
-		return 0;
-	list_add_tail (&qtd->qtd_list, head);
-	qtd->urb = urb;
-
-	token = QTD_STS_ACTIVE;
-	token |= (EHCI_TUNE_CERR << 10);
-	/* for split transactions, SplitXState initialized to zero */
-
-	len = urb->transfer_buffer_length;
-	is_input = usb_pipein (urb->pipe);
-	if (usb_pipecontrol (urb->pipe)) {
-		/* SETUP pid */
-		qtd_fill (qtd, urb->setup_dma, sizeof (struct usb_ctrlrequest),
-			token | (2 /* "setup" */ << 8), 8);
-
-		/* ... and always at least one more pid */
-		token ^= QTD_TOGGLE;
-		qtd_prev = qtd;
-		qtd = ehci_qtd_alloc (ehci, flags);
-		if (unlikely (!qtd))
-			goto cleanup;
-		qtd->urb = urb;
-		qtd_prev->hw_next = QTD_NEXT (qtd->qtd_dma);
-		list_add_tail (&qtd->qtd_list, head);
-	} 
-
-	/*
-	 * data transfer stage:  buffer setup
-	 */
-	if (likely (len > 0))
-		buf = urb->transfer_dma;
-	else
-		buf = 0;
-
-	// FIXME this 'buf' check break some zlps...
-	if (!buf || is_input)
-		token |= (1 /* "in" */ << 8);
-	/* else it's already initted to "out" pid (0 << 8) */
-
-	maxpacket = usb_maxpacket (urb->dev, urb->pipe, !is_input) & 0x03ff;
-
-	/*
-	 * buffer gets wrapped in one or more qtds;
-	 * last one may be "short" (including zero len)
-	 * and may serve as a control status ack
-	 */
-	for (;;) {
-		int this_qtd_len;
-
-		this_qtd_len = qtd_fill (qtd, buf, len, token, maxpacket);
-		len -= this_qtd_len;
-		buf += this_qtd_len;
-		if (is_input)
-			qtd->hw_alt_next = ehci->async->hw_alt_next;
-
-		/* qh makes control packets use qtd toggle; maybe switch it */
-		if ((maxpacket & (this_qtd_len + (maxpacket - 1))) == 0)
-			token ^= QTD_TOGGLE;
-
-		if (likely (len <= 0))
-			break;
-
-		qtd_prev = qtd;
-		qtd = ehci_qtd_alloc (ehci, flags);
-		if (unlikely (!qtd))
-			goto cleanup;
-		qtd->urb = urb;
-		qtd_prev->hw_next = QTD_NEXT (qtd->qtd_dma);
-		list_add_tail (&qtd->qtd_list, head);
-	}
-
-	/* unless the bulk/interrupt caller wants a chance to clean
-	 * up after short reads, hc should advance qh past this urb
-	 */
-	if (likely ((urb->transfer_flags & URB_SHORT_NOT_OK) == 0
-				|| usb_pipecontrol (urb->pipe)))
-		qtd->hw_alt_next = EHCI_LIST_END;
-
-	/*
-	 * control requests may need a terminating data "status" ack;
-	 * bulk ones may need a terminating short packet (zero length).
-	 */
-	if (likely (buf != 0)) {
-		int	one_more = 0;
-
-		if (usb_pipecontrol (urb->pipe)) {
-			one_more = 1;
-			token ^= 0x0100;	/* "in" <--> "out"  */
-			token |= QTD_TOGGLE;	/* force DATA1 */
-		} else if (usb_pipebulk (urb->pipe)
-				&& (urb->transfer_flags & URB_ZERO_PACKET)
-				&& !(urb->transfer_buffer_length % maxpacket)) {
-			one_more = 1;
-		}
-		if (one_more) {
-			qtd_prev = qtd;
-			qtd = ehci_qtd_alloc (ehci, flags);
-			if (unlikely (!qtd))
-				goto cleanup;
-			qtd->urb = urb;
-			qtd_prev->hw_next = QTD_NEXT (qtd->qtd_dma);
-			list_add_tail (&qtd->qtd_list, head);
-
-			/* never any data in such packets */
-			qtd_fill (qtd, 0, 0, token, 0);
-		}
-	}
-
-	/* by default, enable interrupt on urb completion */
-	if (likely (!(urb->transfer_flags & URB_NO_INTERRUPT)))
-		qtd->hw_token |= __constant_cpu_to_le32 (QTD_IOC);
-	return head;
-
-cleanup:
-	qtd_list_free (ehci, urb, head);
-	return 0;
-}
-
-/*-------------------------------------------------------------------------*/
-
-/*
- * Hardware maintains data toggle (like OHCI) ... here we (re)initialize
- * the hardware data toggle in the QH, and set the pseudo-toggle in udev
- * so we can see if usb_clear_halt() was called.  NOP for control, since
- * we set up qh->hw_info1 to always use the QTD toggle bits. 
- */
-static inline void
-clear_toggle (struct usb_device *udev, int ep, int is_out, struct ehci_qh *qh)
-{
-	vdbg ("clear toggle, dev %d ep 0x%x-%s",
-		udev->devnum, ep, is_out ? "out" : "in");
-	qh->hw_token &= ~__constant_cpu_to_le32 (QTD_TOGGLE);
-	usb_settoggle (udev, ep, is_out, 1);
-}
-
-// Would be best to create all qh's from config descriptors,
-// when each interface/altsetting is established.  Unlink
-// any previous qh and cancel its urbs first; endpoints are
-// implicitly reset then (data toggle too).
-// That'd mean updating how usbcore talks to HCDs. (2.5?)
-
-
-// high bandwidth multiplier, as encoded in highspeed endpoint descriptors
-#define hb_mult(wMaxPacketSize) (1 + (((wMaxPacketSize) >> 11) & 0x03))
-// ... and packet size, for any kind of endpoint descriptor
-#define max_packet(wMaxPacketSize) ((wMaxPacketSize) & 0x03ff)
-
-/*
- * Each QH holds a qtd list; a QH is used for everything except iso.
- *
- * For interrupt urbs, the scheduler must set the microframe scheduling
- * mask(s) each time the QH gets scheduled.  For highspeed, that's
- * just one microframe in the s-mask.  For split interrupt transactions
- * there are additional complications: c-mask, maybe FSTNs.
- */
-static struct ehci_qh *
-qh_make (
-	struct ehci_hcd		*ehci,
-	struct urb		*urb,
-	int			flags
-) {
-	struct ehci_qh		*qh = ehci_qh_alloc (ehci, flags);
-	u32			info1 = 0, info2 = 0;
-	int			is_input, type;
-	int			maxp = 0;
-
-	if (!qh)
-		return qh;
-
-	/*
-	 * init endpoint/device data for this QH
-	 */
-	info1 |= usb_pipeendpoint (urb->pipe) << 8;
-	info1 |= usb_pipedevice (urb->pipe) << 0;
-
-	is_input = usb_pipein (urb->pipe);
-	type = usb_pipetype (urb->pipe);
-	maxp = usb_maxpacket (urb->dev, urb->pipe, !is_input);
-
-	/* Compute interrupt scheduling parameters just once, and save.
-	 * - allowing for high bandwidth, how many nsec/uframe are used?
-	 * - split transactions need a second CSPLIT uframe; same question
-	 * - splits also need a schedule gap (for full/low speed I/O)
-	 * - qh has a polling interval
-	 *
-	 * For control/bulk requests, the HC or TT handles these.
-	 */
-	if (type == PIPE_INTERRUPT) {
-		qh->usecs = usb_calc_bus_time (USB_SPEED_HIGH, is_input, 0,
-				hb_mult (maxp) * max_packet (maxp));
-		qh->start = NO_FRAME;
-
-		if (urb->dev->speed == USB_SPEED_HIGH) {
-			qh->c_usecs = 0;
-			qh->gap_uf = 0;
-
-			/* FIXME handle HS periods of less than 1 frame. */
-			qh->period = urb->interval >> 3;
-			if (qh->period < 1) {
-				dbg ("intr period %d uframes, NYET!",
-						urb->interval);
-				goto done;
-			}
-		} else {
-			/* gap is f(FS/LS transfer times) */
-			qh->gap_uf = 1 + usb_calc_bus_time (urb->dev->speed,
-					is_input, 0, maxp) / (125 * 1000);
-
-			/* FIXME this just approximates SPLIT/CSPLIT times */
-			if (is_input) {		// SPLIT, gap, CSPLIT+DATA
-				qh->c_usecs = qh->usecs + HS_USECS (0);
-				qh->usecs = HS_USECS (1);
-			} else {		// SPLIT+DATA, gap, CSPLIT
-				qh->usecs += HS_USECS (1);
-				qh->c_usecs = HS_USECS (0);
-			}
-
-			qh->period = urb->interval;
-		}
-	}
-
-	/* using TT? */
-	switch (urb->dev->speed) {
-	case USB_SPEED_LOW:
-		info1 |= (1 << 12);	/* EPS "low" */
-		/* FALL THROUGH */
-
-	case USB_SPEED_FULL:
-		/* EPS 0 means "full" */
-		if (type != PIPE_INTERRUPT)
-			info1 |= (EHCI_TUNE_RL_TT << 28);
-		if (type == PIPE_CONTROL) {
-			info1 |= (1 << 27);	/* for TT */
-			info1 |= 1 << 14;	/* toggle from qtd */
-		}
-		info1 |= maxp << 16;
-
-		info2 |= (EHCI_TUNE_MULT_TT << 30);
-		info2 |= urb->dev->ttport << 23;
-		info2 |= urb->dev->tt->hub->devnum << 16;
-
-		/* NOTE:  if (PIPE_INTERRUPT) { scheduler sets c-mask } */
-
-		break;
-
-	case USB_SPEED_HIGH:		/* no TT involved */
-		info1 |= (2 << 12);	/* EPS "high" */
-		if (type == PIPE_CONTROL) {
-			info1 |= (EHCI_TUNE_RL_HS << 28);
-			info1 |= 64 << 16;	/* usb2 fixed maxpacket */
-			info1 |= 1 << 14;	/* toggle from qtd */
-			info2 |= (EHCI_TUNE_MULT_HS << 30);
-		} else if (type == PIPE_BULK) {
-			info1 |= (EHCI_TUNE_RL_HS << 28);
-			info1 |= 512 << 16;	/* usb2 fixed maxpacket */
-			info2 |= (EHCI_TUNE_MULT_HS << 30);
-		} else {		/* PIPE_INTERRUPT */
-			info1 |= max_packet (maxp) << 16;
-			info2 |= hb_mult (maxp) << 30;
-		}
-		break;
-	default:
- 		dbg ("bogus dev %p speed %d", urb->dev, urb->dev->speed);
-done:
-		qh_put (ehci, qh);
-		return 0;
-	}
-
-	/* NOTE:  if (PIPE_INTERRUPT) { scheduler sets s-mask } */
-
-	/* init as halted, toggle clear, advance to dummy */
-	qh->qh_state = QH_STATE_IDLE;
-	qh->hw_info1 = cpu_to_le32 (info1);
-	qh->hw_info2 = cpu_to_le32 (info2);
-	qh_update (ehci, qh, qh->dummy);
-	qh->hw_token = cpu_to_le32 (QTD_STS_HALT);
-	usb_settoggle (urb->dev, usb_pipeendpoint (urb->pipe), !is_input, 1);
-	return qh;
-}
-#undef hb_mult
-#undef hb_packet
-
-/*-------------------------------------------------------------------------*/
-
-/* move qh (and its qtds) onto async queue; maybe enable queue.  */
-
-static void qh_link_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
-{
-	u32		dma = QH_NEXT (qh->qh_dma);
-	struct ehci_qh	*head;
-
-	/* (re)start the async schedule? */
-	head = ehci->async;
-	if (ehci->async_idle)
-		del_timer (&ehci->watchdog);
-	if (!head->qh_next.qh) {
-		u32	cmd = readl (&ehci->regs->command);
-
-		if (!(cmd & CMD_ASE)) {
-			/* in case a clear of CMD_ASE didn't take yet */
-			(void) handshake (&ehci->regs->status, STS_ASS, 0, 150);
-			cmd |= CMD_ASE | CMD_RUN;
-			writel (cmd, &ehci->regs->command);
-			ehci->hcd.state = USB_STATE_RUNNING;
-			/* posted write need not be known to HC yet ... */
-		}
-	}
-
-	qh->hw_token &= ~__constant_cpu_to_le32 (QTD_STS_HALT);
-
-	/* splice right after start */
-	qh->qh_next = head->qh_next;
-	qh->hw_next = head->hw_next;
-	wmb ();
-
-	head->qh_next.qh = qh;
-	head->hw_next = dma;
-
-	qh->qh_state = QH_STATE_LINKED;
-	/* qtd completions reported later by interrupt */
-
-	ehci->async_idle = 0;
-}
-
-/*-------------------------------------------------------------------------*/
-
-/*
- * For control/bulk/interrupt, return QH with these TDs appended.
- * Allocates and initializes the QH if necessary.
- * Returns null if it can't allocate a QH it needs to.
- * If the QH has TDs (urbs) already, that's great.
- */
-static struct ehci_qh *qh_append_tds (
-	struct ehci_hcd		*ehci,
-	struct urb		*urb,
-	struct list_head	*qtd_list,
-	int			epnum,
-	void			**ptr
-)
-{
-	struct ehci_qh		*qh = 0;
-
-	qh = (struct ehci_qh *) *ptr;
-	if (unlikely (qh == 0)) {
-		/* can't sleep here, we have ehci->lock... */
-		qh = qh_make (ehci, urb, SLAB_ATOMIC);
-		*ptr = qh;
-	}
-	if (likely (qh != 0)) {
-		struct ehci_qtd	*qtd;
-
-		if (unlikely (list_empty (qtd_list)))
-			qtd = 0;
-		else
-			qtd = list_entry (qtd_list->next, struct ehci_qtd,
-					qtd_list);
-
-		/* control qh may need patching after enumeration */
-		if (unlikely (epnum == 0)) {
-			/* set_address changes the address */
-			if (le32_to_cpu (qh->hw_info1 & 0x7f) == 0)
-				qh->hw_info1 |= cpu_to_le32 (
-						usb_pipedevice (urb->pipe));
-
-			/* for full speed, ep0 maxpacket can grow */
-			else if (!(qh->hw_info1 & cpu_to_le32 (0x3 << 12))) {
-				u32	info, max;
-
-				info = le32_to_cpu (qh->hw_info1);
-				max = urb->dev->descriptor.bMaxPacketSize0;
-				if (max > (0x07ff & (info >> 16))) {
-					info &= ~(0x07ff << 16);
-					info |= max << 16;
-					qh->hw_info1 = cpu_to_le32 (info);
-				}
-			}
-		}
-
-		/* FIXME:  changing config or interface setting is not
-		 * supported yet.  preferred fix is for usbcore to tell
-		 * us to clear out each endpoint's state, but...
-		 */
-
-		/* usb_clear_halt() means qh data toggle gets reset */
-		if (unlikely (!usb_gettoggle (urb->dev,
-					(epnum & 0x0f), !(epnum & 0x10)))
-				&& !usb_pipecontrol (urb->pipe)) {
-			/* "never happens": drivers do stall cleanup right */
-			if (qh->qh_state != QH_STATE_IDLE
-					&& qh->qh_state != QH_STATE_COMPLETING)
-				ehci_warn (ehci, "clear toggle dev%d "
-						"ep%d%s: not idle\n",
-						usb_pipedevice (urb->pipe),
-						epnum & 0x0f,
-						usb_pipein (urb->pipe)
-							? "in" : "out");
-			/* else we know this overlay write is safe */
-			clear_toggle (urb->dev,
-				epnum & 0x0f, !(epnum & 0x10), qh);
-		}
-
-		/* just one way to queue requests: swap with the dummy qtd.
-		 * only hc or qh_completions() usually modify the overlay.
-		 */
-		if (likely (qtd != 0)) {
-			struct ehci_qtd		*dummy;
-			dma_addr_t		dma;
-			u32			token;
-
-			/* to avoid racing the HC, use the dummy td instead of
-			 * the first td of our list (becomes new dummy).  both
-			 * tds stay deactivated until we're done, when the
-			 * HC is allowed to fetch the old dummy (4.10.2).
-			 */
-			token = qtd->hw_token;
-			qtd->hw_token = 0;
-			wmb ();
-			dummy = qh->dummy;
-
-			dma = dummy->qtd_dma;
-			*dummy = *qtd;
-			dummy->qtd_dma = dma;
-
-			list_del (&qtd->qtd_list);
-			list_add (&dummy->qtd_list, qtd_list);
-			__list_splice (qtd_list, qh->qtd_list.prev);
-
-			ehci_qtd_init (qtd, qtd->qtd_dma);
-			qh->dummy = qtd;
-
-			/* hc must see the new dummy at list end */
-			dma = qtd->qtd_dma;
-			qtd = list_entry (qh->qtd_list.prev,
-					struct ehci_qtd, qtd_list);
-			qtd->hw_next = QTD_NEXT (dma);
-
-			/* let the hc process these next qtds */
-			wmb ();
-			dummy->hw_token = token;
-
-			urb->hcpriv = qh_get (qh);
-		}
-	}
-	return qh;
-}
-
-/*-------------------------------------------------------------------------*/
-
-static int
-submit_async (
-	struct ehci_hcd		*ehci,
-	struct urb		*urb,
-	struct list_head	*qtd_list,
-	int			mem_flags
-) {
-	struct ehci_qtd		*qtd;
-	struct hcd_dev		*dev;
-	int			epnum;
-	unsigned long		flags;
-	struct ehci_qh		*qh = 0;
-
-	qtd = list_entry (qtd_list->next, struct ehci_qtd, qtd_list);
-	dev = (struct hcd_dev *)urb->dev->hcpriv;
-	epnum = usb_pipeendpoint (urb->pipe);
-	if (usb_pipein (urb->pipe) && !usb_pipecontrol (urb->pipe))
-		epnum |= 0x10;
-
-	vdbg ("%s: submit_async urb %p len %d ep %d-%s qtd %p [qh %p]",
-		hcd_to_bus (&ehci->hcd)->bus_name,
-		urb, urb->transfer_buffer_length,
-		epnum & 0x0f, (epnum & 0x10) ? "in" : "out",
-		qtd, dev ? dev->ep [epnum] : (void *)~0);
-
-	spin_lock_irqsave (&ehci->lock, flags);
-	qh = qh_append_tds (ehci, urb, qtd_list, epnum, &dev->ep [epnum]);
-
-	/* Control/bulk operations through TTs don't need scheduling,
-	 * the HC and TT handle it when the TT has a buffer ready.
-	 */
-	if (likely (qh != 0)) {
-		if (likely (qh->qh_state == QH_STATE_IDLE))
-			qh_link_async (ehci, qh_get (qh));
-	}
-	spin_unlock_irqrestore (&ehci->lock, flags);
-	if (unlikely (qh == 0)) {
-		qtd_list_free (ehci, urb, qtd_list);
-		return -ENOMEM;
-	}
-	return 0;
-}
-
-/*-------------------------------------------------------------------------*/
-
-/* the async qh for the qtds being reclaimed are now unlinked from the HC */
-
-static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh);
-
-static void end_unlink_async (struct ehci_hcd *ehci, struct pt_regs *regs)
-{
-	struct ehci_qh		*qh = ehci->reclaim;
-	struct ehci_qh		*next;
-
-	del_timer (&ehci->watchdog);
-
-	qh->hw_next = cpu_to_le32 (qh->qh_dma);
-	qh->qh_state = QH_STATE_IDLE;
-	qh->qh_next.qh = 0;
-	qh_put (ehci, qh);			// refcount from reclaim 
-	ehci->reclaim = 0;
-	ehci->reclaim_ready = 0;
-
-	/* other unlink(s) may be pending (in QH_STATE_UNLINK_WAIT) */
-	next = qh->reclaim;
-	qh->reclaim = 0;
-
-	qh_completions (ehci, qh, regs);
-
-	if (!list_empty (&qh->qtd_list)
-			&& HCD_IS_RUNNING (ehci->hcd.state))
-		qh_link_async (ehci, qh);
-	else {
-		qh_put (ehci, qh);		// refcount from async list
-
-		/* it's not free to turn the async schedule on/off; leave it
-		 * active but idle for a while once it empties.
-		 */
-		if (HCD_IS_RUNNING (ehci->hcd.state)
-				&& ehci->async->qh_next.qh == 0
-				&& !timer_pending (&ehci->watchdog)) {
-			ehci->async_idle = 1;
-			mod_timer (&ehci->watchdog,
-					jiffies + EHCI_ASYNC_JIFFIES);
-		}
-	}
-
-	if (next)
-		start_unlink_async (ehci, next);
-}
-
-/* makes sure the async qh will become idle */
-/* caller must own ehci->lock */
-
-static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
-{
-	int		cmd = readl (&ehci->regs->command);
-	struct ehci_qh	*prev;
-
-#ifdef DEBUG
-	if (ehci->reclaim
-			|| (qh->qh_state != QH_STATE_LINKED
-				&& qh->qh_state != QH_STATE_UNLINK_WAIT)
-#ifdef CONFIG_SMP
-// this macro lies except on SMP compiles
-			|| !spin_is_locked (&ehci->lock)
-#endif
-			)
-		BUG ();
-#endif
-
-	/* stop async schedule right now? */
-	if (unlikely (qh == ehci->async)) {
-		/* can't get here without STS_ASS set */
-		if (ehci->hcd.state != USB_STATE_HALT) {
-			writel (cmd & ~CMD_ASE, &ehci->regs->command);
-			wmb ();
-			// handshake later, if we need to
-		}
-		return;
-	} 
-
-	qh->qh_state = QH_STATE_UNLINK;
-	ehci->reclaim = qh = qh_get (qh);
-
-	prev = ehci->async;
-	while (prev->qh_next.qh != qh)
-		prev = prev->qh_next.qh;
-
-	prev->hw_next = qh->hw_next;
-	prev->qh_next = qh->qh_next;
-	wmb ();
-
-	if (unlikely (ehci->hcd.state == USB_STATE_HALT)) {
-		/* if (unlikely (qh->reclaim != 0))
-		 * 	this will recurse, probably not much
-		 */
-		end_unlink_async (ehci, NULL);
-		return;
-	}
-
-	ehci->reclaim_ready = 0;
-	cmd |= CMD_IAAD;
-	writel (cmd, &ehci->regs->command);
-	/* posted write need not be known to HC yet ... */
-
-	mod_timer (&ehci->watchdog, jiffies + EHCI_WATCHDOG_JIFFIES);
-}
-
-/*-------------------------------------------------------------------------*/
-
-static void
-scan_async (struct ehci_hcd *ehci, struct pt_regs *regs)
-{
-	struct ehci_qh		*qh;
-
-	if (!++(ehci->stamp))
-		ehci->stamp++;
-rescan:
-	qh = ehci->async->qh_next.qh;
-	if (likely (qh != 0)) {
-		do {
-			/* clean any finished work for this qh */
-			if (!list_empty (&qh->qtd_list)
-					&& qh->stamp != ehci->stamp) {
-				int temp;
-
-				/* unlinks could happen here; completion
-				 * reporting drops the lock.  rescan using
-				 * the latest schedule, but don't rescan
-				 * qhs we already finished (no looping).
-				 */
-				qh = qh_get (qh);
-				qh->stamp = ehci->stamp;
-				temp = qh_completions (ehci, qh, regs);
-				qh_put (ehci, qh);
-				if (temp != 0) {
-					goto rescan;
-				}
-			}
-
-			/* unlink idle entries, reducing HC PCI usage as
-			 * well as HCD schedule-scanning costs.
-			 *
-			 * FIXME don't unlink idle entries so quickly; it
-			 * can penalize (common) half duplex protocols.
-			 */
-			if (list_empty (&qh->qtd_list) && !ehci->reclaim) {
-				start_unlink_async (ehci, qh);
-			}
-
-			qh = qh->qh_next.qh;
-		} while (qh);
-	}
-}
diff -urN linux-2.4.21-pre5/drivers/usb/hcd/ehci-sched.c linux-2.4.21-pre6/drivers/usb/hcd/ehci-sched.c
--- linux-2.4.21-pre5/drivers/usb/hcd/ehci-sched.c	Wed Mar 26 16:11:29 2003
+++ linux-2.4.21-pre6/drivers/usb/hcd/ehci-sched.c	Wed Dec 31 16:00:00 1969
@@ -1,1123 +0,0 @@
-/*
- * Copyright (c) 2001-2002 by David Brownell
- * 
- * 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.
- *
- * 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.
- */
-
-/* this file is part of ehci-hcd.c */
-
-/*-------------------------------------------------------------------------*/
-
-/*
- * EHCI scheduled transaction support:  interrupt, iso, split iso
- * These are called "periodic" transactions in the EHCI spec.
- *
- * Note that for interrupt transfers, the QH/QTD manipulation is shared
- * with the "asynchronous" transaction support (control/bulk transfers).
- * The only real difference is in how interrupt transfers are scheduled.
- * We get some funky API restrictions from the current URB model, which
- * works notably better for reading transfers than for writing.  (And
- * which accordingly needs to change before it'll work inside devices,
- * or with "USB On The Go" additions to USB 2.0 ...)
- */
-
-static int ehci_get_frame (struct usb_hcd *hcd);
-
-/*-------------------------------------------------------------------------*/
-
-/*
- * periodic_next_shadow - return "next" pointer on shadow list
- * @periodic: host pointer to qh/itd/sitd
- * @tag: hardware tag for type of this record
- */
-static union ehci_shadow *
-periodic_next_shadow (union ehci_shadow *periodic, int tag)
-{
-	switch (tag) {
-	case Q_TYPE_QH:
-		return &periodic->qh->qh_next;
-	case Q_TYPE_FSTN:
-		return &periodic->fstn->fstn_next;
-	case Q_TYPE_ITD:
-		return &periodic->itd->itd_next;
-#ifdef have_split_iso
-	case Q_TYPE_SITD:
-		return &periodic->sitd->sitd_next;
-#endif /* have_split_iso */
-	}
-	dbg ("BAD shadow %p tag %d", periodic->ptr, tag);
-	// BUG ();
-	return 0;
-}
-
-/* returns true after successful unlink */
-/* caller must hold ehci->lock */
-static int periodic_unlink (struct ehci_hcd *ehci, unsigned frame, void *ptr)
-{
-	union ehci_shadow	*prev_p = &ehci->pshadow [frame];
-	u32			*hw_p = &ehci->periodic [frame];
-	union ehci_shadow	here = *prev_p;
-	union ehci_shadow	*next_p;
-
-	/* find predecessor of "ptr"; hw and shadow lists are in sync */
-	while (here.ptr && here.ptr != ptr) {
-		prev_p = periodic_next_shadow (prev_p, Q_NEXT_TYPE (*hw_p));
-		hw_p = &here.qh->hw_next;
-		here = *prev_p;
-	}
-	/* an interrupt entry (at list end) could have been shared */
-	if (!here.ptr) {
-		dbg ("entry %p no longer on frame [%d]", ptr, frame);
-		return 0;
-	}
-	// vdbg ("periodic unlink %p from frame %d", ptr, frame);
-
-	/* update hardware list ... HC may still know the old structure, so
-	 * don't change hw_next until it'll have purged its cache
-	 */
-	next_p = periodic_next_shadow (&here, Q_NEXT_TYPE (*hw_p));
-	*hw_p = here.qh->hw_next;
-
-	/* unlink from shadow list; HCD won't see old structure again */
-	*prev_p = *next_p;
-	next_p->ptr = 0;
-
-	return 1;
-}
-
-/* how many of the uframe's 125 usecs are allocated? */
-static unsigned short
-periodic_usecs (struct ehci_hcd *ehci, unsigned frame, unsigned uframe)
-{
-	u32			*hw_p = &ehci->periodic [frame];
-	union ehci_shadow	*q = &ehci->pshadow [frame];
-	unsigned		usecs = 0;
-
-	while (q->ptr) {
-		switch (Q_NEXT_TYPE (*hw_p)) {
-		case Q_TYPE_QH:
-			/* is it in the S-mask? */
-			if (q->qh->hw_info2 & cpu_to_le32 (1 << uframe))
-				usecs += q->qh->usecs;
-			/* ... or C-mask? */
-			if (q->qh->hw_info2 & cpu_to_le32 (1 << (8 + uframe)))
-				usecs += q->qh->c_usecs;
-			q = &q->qh->qh_next;
-			break;
-		case Q_TYPE_FSTN:
-			/* for "save place" FSTNs, count the relevant INTR
-			 * bandwidth from the previous frame
-			 */
-			if (q->fstn->hw_prev != EHCI_LIST_END) {
-				dbg ("not counting FSTN bandwidth yet ...");
-			}
-			q = &q->fstn->fstn_next;
-			break;
-		case Q_TYPE_ITD:
-			/* NOTE the "one uframe per itd" policy */
-			if (q->itd->hw_transaction [uframe] != 0)
-				usecs += q->itd->usecs;
-			q = &q->itd->itd_next;
-			break;
-#ifdef have_split_iso
-		case Q_TYPE_SITD:
-			temp = q->sitd->hw_fullspeed_ep &
-				__constant_cpu_to_le32 (1 << 31);
-
-			// FIXME:  this doesn't count data bytes right...
-
-			/* is it in the S-mask?  (count SPLIT, DATA) */
-			if (q->sitd->hw_uframe & cpu_to_le32 (1 << uframe)) {
-				if (temp)
-					usecs += HS_USECS (188);
-				else
-					usecs += HS_USECS (1);
-			}
-
-			/* ... C-mask?  (count CSPLIT, DATA) */
-			if (q->sitd->hw_uframe &
-					cpu_to_le32 (1 << (8 + uframe))) {
-				if (temp)
-					usecs += HS_USECS (0);
-				else
-					usecs += HS_USECS (188);
-			}
-			q = &q->sitd->sitd_next;
-			break;
-#endif /* have_split_iso */
-		default:
-			BUG ();
-		}
-	}
-#ifdef	DEBUG
-	if (usecs > 100)
-		err ("overallocated uframe %d, periodic is %d usecs",
-			frame * 8 + uframe, usecs);
-#endif
-	return usecs;
-}
-
-/*-------------------------------------------------------------------------*/
-
-static int enable_periodic (struct ehci_hcd *ehci)
-{
-	u32	cmd;
-	int	status;
-
-	/* did clearing PSE did take effect yet?
-	 * takes effect only at frame boundaries...
-	 */
-	status = handshake (&ehci->regs->status, STS_PSS, 0, 9 * 125);
-	if (status != 0) {
-		ehci->hcd.state = USB_STATE_HALT;
-		return status;
-	}
-
-	cmd = readl (&ehci->regs->command) | CMD_PSE;
-	writel (cmd, &ehci->regs->command);
-	/* posted write ... PSS happens later */
-	ehci->hcd.state = USB_STATE_RUNNING;
-
-	/* make sure ehci_work scans these */
-	ehci->next_uframe = readl (&ehci->regs->frame_index)
-				% (ehci->periodic_size << 3);
-	return 0;
-}
-
-static int disable_periodic (struct ehci_hcd *ehci)
-{
-	u32	cmd;
-	int	status;
-
-	/* did setting PSE not take effect yet?
-	 * takes effect only at frame boundaries...
-	 */
-	status = handshake (&ehci->regs->status, STS_PSS, STS_PSS, 9 * 125);
-	if (status != 0) {
-		ehci->hcd.state = USB_STATE_HALT;
-		return status;
-	}
-
-	cmd = readl (&ehci->regs->command) & ~CMD_PSE;
-	writel (cmd, &ehci->regs->command);
-	/* posted write ... */
-
-	ehci->next_uframe = -1;
-	return 0;
-}
-
-/*-------------------------------------------------------------------------*/
-
-// FIXME microframe periods not yet handled
-
-static void intr_deschedule (
-	struct ehci_hcd	*ehci,
-	struct ehci_qh	*qh,
-	int		wait
-) {
-	int		status;
-	unsigned	frame = qh->start;
-
-	do {
-		periodic_unlink (ehci, frame, qh);
-		qh_put (ehci, qh);
-		frame += qh->period;
-	} while (frame < ehci->periodic_size);
-
-	qh->qh_state = QH_STATE_UNLINK;
-	qh->qh_next.ptr = 0;
-	ehci->periodic_sched--;
-
-	/* maybe turn off periodic schedule */
-	if (!ehci->periodic_sched)
-		status = disable_periodic (ehci);
-	else {
-		status = 0;
-		vdbg ("periodic schedule still enabled");
-	}
-
-	/*
-	 * If the hc may be looking at this qh, then delay a uframe
-	 * (yeech!) to be sure it's done.
-	 * No other threads may be mucking with this qh.
-	 */
-	if (((ehci_get_frame (&ehci->hcd) - frame) % qh->period) == 0) {
-		if (wait) {
-			udelay (125);
-			qh->hw_next = EHCI_LIST_END;
-		} else {
-			/* we may not be IDLE yet, but if the qh is empty
-			 * the race is very short.  then if qh also isn't
-			 * rescheduled soon, it won't matter.  otherwise...
-			 */
-			vdbg ("intr_deschedule...");
-		}
-	} else
-		qh->hw_next = EHCI_LIST_END;
-
-	qh->qh_state = QH_STATE_IDLE;
-
-	/* update per-qh bandwidth utilization (for usbfs) */
-	hcd_to_bus (&ehci->hcd)->bandwidth_allocated -= 
-		(qh->usecs + qh->c_usecs) / qh->period;
-
-	dbg ("descheduled qh %p, period = %d frame = %d count = %d, urbs = %d",
-		qh, qh->period, frame,
-		atomic_read (&qh->refcount), ehci->periodic_sched);
-}
-
-static int check_period (
-	struct ehci_hcd *ehci, 
-	unsigned	frame,
-	unsigned	uframe,
-	unsigned	period,
-	unsigned	usecs
-) {
-	/* complete split running into next frame?
-	 * given FSTN support, we could sometimes check...
-	 */
-	if (uframe >= 8)
-		return 0;
-
-	/*
-	 * 80% periodic == 100 usec/uframe available
-	 * convert "usecs we need" to "max already claimed" 
-	 */
-	usecs = 100 - usecs;
-
-	do {
-		int	claimed;
-
-// FIXME delete when intr_submit handles non-empty queues
-// this gives us a one intr/frame limit (vs N/uframe)
-// ... and also lets us avoid tracking split transactions
-// that might collide at a given TT/hub.
-		if (ehci->pshadow [frame].ptr)
-			return 0;
-
-		claimed = periodic_usecs (ehci, frame, uframe);
-		if (claimed > usecs)
-			return 0;
-
-// FIXME update to handle sub-frame periods
-	} while ((frame += period) < ehci->periodic_size);
-
-	// success!
-	return 1;
-}
-
-static int check_intr_schedule (
-	struct ehci_hcd		*ehci, 
-	unsigned		frame,
-	unsigned		uframe,
-	const struct ehci_qh	*qh,
-	u32			*c_maskp
-)
-{
-    	int		retval = -ENOSPC;
-
-	if (!check_period (ehci, frame, uframe, qh->period, qh->usecs))
-		goto done;
-	if (!qh->c_usecs) {
-		retval = 0;
-		*c_maskp = cpu_to_le32 (0);
-		goto done;
-	}
-
-	/* This is a split transaction; check the bandwidth available for
-	 * the completion too.  Check both worst and best case gaps: worst
-	 * case is SPLIT near uframe end, and CSPLIT near start ... best is
-	 * vice versa.  Difference can be almost two uframe times, but we
-	 * reserve unnecessary bandwidth (waste it) this way.  (Actually
-	 * even better cases exist, like immediate device NAK.)
-	 *
-	 * FIXME don't even bother unless we know this TT is idle in that
-	 * range of uframes ... for now, check_period() allows only one
-	 * interrupt transfer per frame, so needn't check "TT busy" status
-	 * when scheduling a split (QH, SITD, or FSTN).
-	 *
-	 * FIXME ehci 0.96 and above can use FSTNs
-	 */
-	if (!check_period (ehci, frame, uframe + qh->gap_uf + 1,
-				qh->period, qh->c_usecs))
-		goto done;
-	if (!check_period (ehci, frame, uframe + qh->gap_uf,
-				qh->period, qh->c_usecs))
-		goto done;
-
-	*c_maskp = cpu_to_le32 (0x03 << (8 + uframe + qh->gap_uf));
-	retval = 0;
-done:
-	return retval;
-}
-
-static int qh_schedule (struct ehci_hcd *ehci, struct ehci_qh *qh)
-{
-	int 		status;
-	unsigned	uframe;
-	u32		c_mask;
-	unsigned	frame;		/* 0..(qh->period - 1), or NO_FRAME */
-
-	qh->hw_next = EHCI_LIST_END;
-	frame = qh->start;
-
-	/* reuse the previous schedule slots, if we can */
-	if (frame < qh->period) {
-		uframe = ffs (le32_to_cpup (&qh->hw_info2) & 0x00ff);
-		status = check_intr_schedule (ehci, frame, --uframe,
-				qh, &c_mask);
-	} else {
-		uframe = 0;
-		c_mask = 0;
-		status = -ENOSPC;
-	}
-
-	/* else scan the schedule to find a group of slots such that all
-	 * uframes have enough periodic bandwidth available.
-	 */
-	if (status) {
-		frame = qh->period - 1;
-		do {
-			for (uframe = 0; uframe < 8; uframe++) {
-				status = check_intr_schedule (ehci,
-						frame, uframe, qh,
-						&c_mask);
-				if (status == 0)
-					break;
-			}
-		} while (status && --frame);
-		if (status)
-			goto done;
-		qh->start = frame;
-
-		/* reset S-frame and (maybe) C-frame masks */
-		qh->hw_info2 &= ~0xffff;
-		qh->hw_info2 |= cpu_to_le32 (1 << uframe) | c_mask;
-	} else
-		dbg ("reused previous qh %p schedule", qh);
-
-	/* stuff into the periodic schedule */
-	qh->qh_state = QH_STATE_LINKED;
-	dbg ("scheduled qh %p usecs %d/%d period %d.0 starting %d.%d (gap %d)",
-		qh, qh->usecs, qh->c_usecs,
-		qh->period, frame, uframe, qh->gap_uf);
-	do {
-		if (unlikely (ehci->pshadow [frame].ptr != 0)) {
-
-// FIXME -- just link toward the end, before any qh with a shorter period,
-// AND accomodate it already having been linked here (after some other qh)
-// AS WELL AS updating the schedule checking logic
-
-			BUG ();
-		} else {
-			ehci->pshadow [frame].qh = qh_get (qh);
-			ehci->periodic [frame] =
-				QH_NEXT (qh->qh_dma);
-		}
-		wmb ();
-		frame += qh->period;
-	} while (frame < ehci->periodic_size);
-
-	/* update per-qh bandwidth for usbfs */
-	hcd_to_bus (&ehci->hcd)->bandwidth_allocated += 
-		(qh->usecs + qh->c_usecs) / qh->period;
-
-	/* maybe enable periodic schedule processing */
-	if (!ehci->periodic_sched++)
-		status = enable_periodic (ehci);
-done:
-	return status;
-}
-
-static int intr_submit (
-	struct ehci_hcd		*ehci,
-	struct urb		*urb,
-	struct list_head	*qtd_list,
-	int			mem_flags
-) {
-	unsigned		epnum;
-	unsigned long		flags;
-	struct ehci_qh		*qh;
-	struct hcd_dev		*dev;
-	int			is_input;
-	int			status = 0;
-	struct list_head	empty;
-
-	/* get endpoint and transfer/schedule data */
-	epnum = usb_pipeendpoint (urb->pipe);
-	is_input = usb_pipein (urb->pipe);
-	if (is_input)
-		epnum |= 0x10;
-
-	spin_lock_irqsave (&ehci->lock, flags);
-	dev = (struct hcd_dev *)urb->dev->hcpriv;
-
-	/* get qh and force any scheduling errors */
-	INIT_LIST_HEAD (&empty);
-	qh = qh_append_tds (ehci, urb, &empty, epnum, &dev->ep [epnum]);
-	if (qh == 0) {
-		status = -ENOMEM;
-		goto done;
-	}
-	if (qh->qh_state == QH_STATE_IDLE) {
-		if ((status = qh_schedule (ehci, qh)) != 0)
-			goto done;
-	}
-
-	/* then queue the urb's tds to the qh */
-	qh = qh_append_tds (ehci, urb, qtd_list, epnum, &dev->ep [epnum]);
-	BUG_ON (qh == 0);
-
-	/* ... update usbfs periodic stats */
-	hcd_to_bus (&ehci->hcd)->bandwidth_int_reqs++;
-
-done:
-	spin_unlock_irqrestore (&ehci->lock, flags);
-	if (status)
-		qtd_list_free (ehci, urb, qtd_list);
-
-	return status;
-}
-
-static unsigned
-intr_complete (
-	struct ehci_hcd	*ehci,
-	unsigned	frame,
-	struct ehci_qh	*qh,
-	struct pt_regs	*regs
-) {
-	unsigned	count;
-
-	/* nothing to report? */
-	if (likely ((qh->hw_token & __constant_cpu_to_le32 (QTD_STS_ACTIVE))
-			!= 0))
-		return 0;
-	if (unlikely (list_empty (&qh->qtd_list))) {
-		dbg ("intr qh %p no TDs?", qh);
-		return 0;
-	}
-	
-	/* handle any completions */
-	count = qh_completions (ehci, qh, regs);
-
-	if (unlikely (list_empty (&qh->qtd_list)))
-		intr_deschedule (ehci, qh, 0);
-
-	return count;
-}
-
-/*-------------------------------------------------------------------------*/
-
-static void
-itd_free_list (struct ehci_hcd *ehci, struct urb *urb)
-{
-	struct ehci_itd *first_itd = urb->hcpriv;
-
-	while (!list_empty (&first_itd->itd_list)) {
-		struct ehci_itd	*itd;
-
-		itd = list_entry (
-			first_itd->itd_list.next,
-			struct ehci_itd, itd_list);
-		list_del (&itd->itd_list);
-		pci_pool_free (ehci->itd_pool, itd, itd->itd_dma);
-	}
-	pci_pool_free (ehci->itd_pool, first_itd, first_itd->itd_dma);
-	urb->hcpriv = 0;
-}
-
-static int
-itd_fill (
-	struct ehci_hcd	*ehci,
-	struct ehci_itd	*itd,
-	struct urb	*urb,
-	unsigned	index,		// urb->iso_frame_desc [index]
-	dma_addr_t	dma		// mapped transfer buffer
-) {
-	u64		temp;
-	u32		buf1;
-	unsigned	i, epnum, maxp, multi;
-	unsigned	length;
-	int		is_input;
-
-	itd->hw_next = EHCI_LIST_END;
-	itd->urb = urb;
-	itd->index = index;
-
-	/* tell itd about its transfer buffer, max 2 pages */
-	length = urb->iso_frame_desc [index].length;
-	dma += urb->iso_frame_desc [index].offset;
-	temp = dma & ~0x0fff;
-	for (i = 0; i < 2; i++) {
-		itd->hw_bufp [i] = cpu_to_le32 ((u32) temp);
-		itd->hw_bufp_hi [i] = cpu_to_le32 ((u32)(temp >> 32));
-		temp += 0x1000;
-	}
-	itd->buf_dma = dma;
-
-	/*
-	 * this might be a "high bandwidth" highspeed endpoint,
-	 * as encoded in the ep descriptor's maxpacket field
-	 */
-	epnum = usb_pipeendpoint (urb->pipe);
-	is_input = usb_pipein (urb->pipe);
-	if (is_input) {
-		maxp = urb->dev->epmaxpacketin [epnum];
-		buf1 = (1 << 11);
-	} else {
-		maxp = urb->dev->epmaxpacketout [epnum];
-		buf1 = 0;
-	}
-	buf1 |= (maxp & 0x03ff);
-	multi = 1;
-	multi += (maxp >> 11) & 0x03;
-	maxp &= 0x03ff;
-	maxp *= multi;
-
-	/* transfer can't fit in any uframe? */ 
-	if (length < 0 || maxp < length) {
-		dbg ("BAD iso packet: %d bytes, max %d, urb %p [%d] (of %d)",
-			length, maxp, urb, index,
-			urb->iso_frame_desc [index].length);
-		return -ENOSPC;
-	}
-	itd->usecs = usb_calc_bus_time (USB_SPEED_HIGH, is_input, 1, length);
-
-	/* "plus" info in low order bits of buffer pointers */
-	itd->hw_bufp [0] |= cpu_to_le32 ((epnum << 8) | urb->dev->devnum);
-	itd->hw_bufp [1] |= cpu_to_le32 (buf1);
-	itd->hw_bufp [2] |= cpu_to_le32 (multi);
-
-	/* figure hw_transaction[] value (it's scheduled later) */
-	itd->transaction = EHCI_ISOC_ACTIVE;
-	itd->transaction |= dma & 0x0fff;		/* offset; buffer=0 */
-	if ((index + 1) == urb->number_of_packets)
-		itd->transaction |= EHCI_ITD_IOC; 	/* end-of-urb irq */
-	itd->transaction |= length << 16;
-	cpu_to_le32s (&itd->transaction);
-
-	return 0;
-}
-
-static int
-itd_urb_transaction (
-	struct ehci_hcd		*ehci,
-	struct urb		*urb,
-	int			mem_flags
-) {
-	int			frame_index;
-	struct ehci_itd		*first_itd, *itd;
-	int			status;
-	dma_addr_t		itd_dma;
-
-	/* allocate/init ITDs */
-	for (frame_index = 0, first_itd = 0;
-			frame_index < urb->number_of_packets;
-			frame_index++) {
-		itd = pci_pool_alloc (ehci->itd_pool, mem_flags, &itd_dma);
-		if (!itd) {
-			status = -ENOMEM;
-			goto fail;
-		}
-		memset (itd, 0, sizeof *itd);
-		itd->itd_dma = itd_dma;
-
-		status = itd_fill (ehci, itd, urb, frame_index,
-				urb->transfer_dma);
-		if (status != 0)
-			goto fail;
-
-		if (first_itd)
-			list_add_tail (&itd->itd_list,
-					&first_itd->itd_list);
-		else {
-			INIT_LIST_HEAD (&itd->itd_list);
-			urb->hcpriv = first_itd = itd;
-		}
-	}
-	urb->error_count = 0;
-	return 0;
-
-fail:
-	if (urb->hcpriv)
-		itd_free_list (ehci, urb);
-	return status;
-}
-
-/*-------------------------------------------------------------------------*/
-
-static inline void
-itd_link (struct ehci_hcd *ehci, unsigned frame, struct ehci_itd *itd)
-{
-	/* always prepend ITD/SITD ... only QH tree is order-sensitive */
-	itd->itd_next = ehci->pshadow [frame];
-	itd->hw_next = ehci->periodic [frame];
-	ehci->pshadow [frame].itd = itd;
-	ehci->periodic [frame] = cpu_to_le32 (itd->itd_dma) | Q_TYPE_ITD;
-}
-
-/*
- * return zero on success, else -errno
- * - start holds first uframe to start scheduling into
- * - max is the first uframe it's NOT (!) OK to start scheduling into
- * math to be done modulo "mod" (ehci->periodic_size << 3)
- */
-static int get_iso_range (
-	struct ehci_hcd		*ehci,
-	struct urb		*urb,
-	unsigned		*start,
-	unsigned		*max,
-	unsigned		mod
-) {
-	struct list_head	*lh;
-	struct hcd_dev		*dev = urb->dev->hcpriv;
-	int			last = -1;
-	unsigned		now, span, end;
-
-	span = urb->interval * urb->number_of_packets;
-
-	/* first see if we know when the next transfer SHOULD happen */
-	list_for_each (lh, &dev->urb_list) {
-		struct urb	*u;
-		struct ehci_itd	*itd;
-		unsigned	s;
-
-		u = list_entry (lh, struct urb, urb_list);
-		if (u == urb || u->pipe != urb->pipe)
-			continue;
-		if (u->interval != urb->interval) {	/* must not change! */ 
-			dbg ("urb %p interval %d ... != %p interval %d",
-				u, u->interval, urb, urb->interval);
-			return -EINVAL;
-		}
-		
-		/* URB for this endpoint... covers through when?  */
-		itd = urb->hcpriv;
-		s = itd->uframe + u->interval * u->number_of_packets;
-		if (last < 0)
-			last = s;
-		else {
-			/*
-			 * So far we can only queue two ISO URBs...
-			 *
-			 * FIXME do interval math, figure out whether
-			 * this URB is "before" or not ... also, handle
-			 * the case where the URB might have completed,
-			 * but hasn't yet been processed.
-			 */
-			dbg ("NYET: queue >2 URBs per ISO endpoint");
-			return -EDOM;
-		}
-	}
-
-	/* calculate the legal range [start,max) */
-	now = readl (&ehci->regs->frame_index) + 1;	/* next uframe */
-	if (!ehci->periodic_sched)
-		now += 8;				/* startup delay */
-	now %= mod;
-	end = now + mod;
-	if (last < 0) {
-		*start = now + ehci->i_thresh + /* paranoia */ 1;
-		*max = end - span;
-		if (*max < *start + 1)
-			*max = *start + 1;
-	} else {
-		*start = last % mod;
-		*max = (last + 1) % mod;
-	}
-
-	/* explicit start frame? */
-	if (!(urb->transfer_flags & URB_ISO_ASAP)) {
-		unsigned	temp;
-
-		/* sanity check: must be in range */
-		urb->start_frame %= ehci->periodic_size;
-		temp = urb->start_frame << 3;
-		if (temp < *start)
-			temp += mod;
-		if (temp > *max)
-			return -EDOM;
-
-		/* use that explicit start frame */
-		*start = urb->start_frame << 3;
-		temp += 8;
-		if (temp < *max)
-			*max = temp;
-	}
-
-	// FIXME minimize wraparound to "now" ... insist max+span
-	// (and start+span) remains a few frames short of "end"
-
-	*max %= ehci->periodic_size;
-	if ((*start + span) < end)
-		return 0;
-	return -EFBIG;
-}
-
-static int
-itd_schedule (struct ehci_hcd *ehci, struct urb *urb)
-{
-	unsigned	start, max, i;
-	int		status;
-	unsigned	mod = ehci->periodic_size << 3;
-
-	for (i = 0; i < urb->number_of_packets; i++) {
-		urb->iso_frame_desc [i].status = -EINPROGRESS;
-		urb->iso_frame_desc [i].actual_length = 0;
-	}
-
-	if ((status = get_iso_range (ehci, urb, &start, &max, mod)) != 0)
-		return status;
-
-	do {
-		unsigned	uframe;
-		unsigned	usecs;
-		struct ehci_itd	*itd;
-
-		/* check schedule: enough space? */
-		itd = urb->hcpriv;
-		uframe = start;
-		for (i = 0, uframe = start;
-				i < urb->number_of_packets;
-				i++, uframe += urb->interval) {
-			uframe %= mod;
-
-			/* can't commit more than 80% periodic == 100 usec */
-			if (periodic_usecs (ehci, uframe >> 3, uframe & 0x7)
-					> (100 - itd->usecs)) {
-				itd = 0;
-				break;
-			}
-			itd = list_entry (itd->itd_list.next,
-				struct ehci_itd, itd_list);
-		}
-		if (!itd)
-			continue;
-		
-		/* that's where we'll schedule this! */
-		itd = urb->hcpriv;
-		urb->start_frame = start >> 3;
-		vdbg ("ISO urb %p (%d packets period %d) starting %d.%d",
-			urb, urb->number_of_packets, urb->interval,
-			urb->start_frame, start & 0x7);
-		for (i = 0, uframe = start, usecs = 0;
-				i < urb->number_of_packets;
-				i++, uframe += urb->interval) {
-			uframe %= mod;
-
-			itd->uframe = uframe;
-			itd->hw_transaction [uframe & 0x07] = itd->transaction;
-			itd_link (ehci, (uframe >> 3) % ehci->periodic_size,
-				itd);
-			wmb ();
-			usecs += itd->usecs;
-
-			itd = list_entry (itd->itd_list.next,
-				struct ehci_itd, itd_list);
-		}
-
-		/* update bandwidth utilization records (for usbfs)
-		 *
-		 * FIXME This claims each URB queued to an endpoint, as if
-		 * transfers were concurrent, not sequential.  So bandwidth
-		 * typically gets double-billed ... comes from tying it to
-		 * URBs rather than endpoints in the schedule.  Luckily we
-		 * don't use this usbfs data for serious decision making.
-		 */
-		usecs /= urb->number_of_packets;
-		usecs /= urb->interval;
-		usecs >>= 3;
-		if (usecs < 1)
-			usecs = 1;
-		usb_claim_bandwidth (urb->dev, urb, usecs, 1);
-
-		/* maybe enable periodic schedule processing */
-		if (!ehci->periodic_sched++) {
-			if ((status =  enable_periodic (ehci)) != 0) {
-				// FIXME deschedule right away
-				err ("itd_schedule, enable = %d", status);
-			}
-		}
-
-		return 0;
-
-	} while ((start = ++start % mod) != max);
-
-	/* no room in the schedule */
-	dbg ("urb %p, CAN'T SCHEDULE", urb);
-	return -ENOSPC;
-}
-
-/*-------------------------------------------------------------------------*/
-
-#define	ISO_ERRS (EHCI_ISOC_BUF_ERR | EHCI_ISOC_BABBLE | EHCI_ISOC_XACTERR)
-
-static unsigned
-itd_complete (
-	struct ehci_hcd	*ehci,
-	struct ehci_itd	*itd,
-	unsigned	uframe,
-	struct pt_regs	*regs
-) {
-	struct urb				*urb = itd->urb;
-	struct usb_iso_packet_descriptor	*desc;
-	u32					t;
-
-	/* update status for this uframe's transfers */
-	desc = &urb->iso_frame_desc [itd->index];
-
-	t = itd->hw_transaction [uframe];
-	itd->hw_transaction [uframe] = 0;
-	if (t & EHCI_ISOC_ACTIVE)
-		desc->status = -EXDEV;
-	else if (t & ISO_ERRS) {
-		urb->error_count++;
-		if (t & EHCI_ISOC_BUF_ERR)
-			desc->status = usb_pipein (urb->pipe)
-				? -ENOSR  /* couldn't read */
-				: -ECOMM; /* couldn't write */
-		else if (t & EHCI_ISOC_BABBLE)
-			desc->status = -EOVERFLOW;
-		else /* (t & EHCI_ISOC_XACTERR) */
-			desc->status = -EPROTO;
-
-		/* HC need not update length with this error */
-		if (!(t & EHCI_ISOC_BABBLE))
-			desc->actual_length += EHCI_ITD_LENGTH (t);
-	} else {
-		desc->status = 0;
-		desc->actual_length += EHCI_ITD_LENGTH (t);
-	}
-
-	vdbg ("itd %p urb %p packet %d/%d trans %x status %d len %d",
-		itd, urb, itd->index + 1, urb->number_of_packets,
-		t, desc->status, desc->actual_length);
-
-	/* handle completion now? */
-	if ((itd->index + 1) != urb->number_of_packets)
-		return 0;
-
-	/*
-	 * Always give the urb back to the driver ... expect it to submit
-	 * a new urb (or resubmit this), and to have another already queued
-	 * when un-interrupted transfers are needed.
-	 *
-	 * NOTE that for now we don't accelerate ISO unlinks; they just
-	 * happen according to the current schedule.  Means a delay of
-	 * up to about a second (max).
-	 */
-	itd_free_list (ehci, urb);
-	if (urb->status == -EINPROGRESS)
-		urb->status = 0;
-
-	/* complete() can reenter this HCD */
-	spin_unlock (&ehci->lock);
-	usb_hcd_giveback_urb (&ehci->hcd, urb, regs);
-	spin_lock (&ehci->lock);
-
-	/* defer stopping schedule; completion can submit */
-	ehci->periodic_sched--;
-	if (!ehci->periodic_sched)
-		(void) disable_periodic (ehci);
-
-	return 1;
-}
-
-/*-------------------------------------------------------------------------*/
-
-static int itd_submit (struct ehci_hcd *ehci, struct urb *urb, int mem_flags)
-{
-	int		status;
-	unsigned long	flags;
-
-	dbg ("itd_submit urb %p", urb);
-
-	/* allocate ITDs w/o locking anything */
-	status = itd_urb_transaction (ehci, urb, mem_flags);
-	if (status < 0)
-		return status;
-
-	/* schedule ... need to lock */
-	spin_lock_irqsave (&ehci->lock, flags);
-	status = itd_schedule (ehci, urb);
-	spin_unlock_irqrestore (&ehci->lock, flags);
-	if (status < 0)
-		itd_free_list (ehci, urb);
-
-	return status;
-}
-
-#ifdef have_split_iso
-
-/*-------------------------------------------------------------------------*/
-
-/*
- * "Split ISO TDs" ... used for USB 1.1 devices going through
- * the TTs in USB 2.0 hubs.
- *
- * FIXME not yet implemented
- */
-
-#endif /* have_split_iso */
-
-/*-------------------------------------------------------------------------*/
-
-static void
-scan_periodic (struct ehci_hcd *ehci, struct pt_regs *regs)
-{
-	unsigned	frame, clock, now_uframe, mod;
-	unsigned	count = 0;
-
-	mod = ehci->periodic_size << 3;
-
-	/*
-	 * When running, scan from last scan point up to "now"
-	 * else clean up by scanning everything that's left.
-	 * Touches as few pages as possible:  cache-friendly.
-	 * Don't scan ISO entries more than once, though.
-	 */
-	frame = ehci->next_uframe >> 3;
-	if (HCD_IS_RUNNING (ehci->hcd.state))
-		now_uframe = readl (&ehci->regs->frame_index);
-	else
-		now_uframe = (frame << 3) - 1;
-	now_uframe %= mod;
-	clock = now_uframe >> 3;
-
-	for (;;) {
-		union ehci_shadow	q, *q_p;
-		u32			type, *hw_p;
-		unsigned		uframes;
-
-restart:
-		/* scan schedule to _before_ current frame index */
-		if (frame == clock)
-			uframes = now_uframe & 0x07;
-		else
-			uframes = 8;
-
-		q_p = &ehci->pshadow [frame];
-		hw_p = &ehci->periodic [frame];
-		q.ptr = q_p->ptr;
-		type = Q_NEXT_TYPE (*hw_p);
-
-		/* scan each element in frame's queue for completions */
-		while (q.ptr != 0) {
-			int			last;
-			unsigned		uf;
-			union ehci_shadow	temp;
-
-			switch (type) {
-			case Q_TYPE_QH:
-				last = (q.qh->hw_next == EHCI_LIST_END);
-				temp = q.qh->qh_next;
-				type = Q_NEXT_TYPE (q.qh->hw_next);
-				count += intr_complete (ehci, frame,
-						qh_get (q.qh), regs);
-				qh_put (ehci, q.qh);
-				q = temp;
-				break;
-			case Q_TYPE_FSTN:
-				last = (q.fstn->hw_next == EHCI_LIST_END);
-				/* for "save place" FSTNs, look at QH entries
-				 * in the previous frame for completions.
-				 */
-				if (q.fstn->hw_prev != EHCI_LIST_END) {
-					dbg ("ignoring completions from FSTNs");
-				}
-				type = Q_NEXT_TYPE (q.fstn->hw_next);
-				q = q.fstn->fstn_next;
-				break;
-			case Q_TYPE_ITD:
-				last = (q.itd->hw_next == EHCI_LIST_END);
-
-				/* Unlink each (S)ITD we see, since the ISO
-				 * URB model forces constant rescheduling.
-				 * That complicates sharing uframes in ITDs,
-				 * and means we need to skip uframes the HC
-				 * hasn't yet processed.
-				 */
-				for (uf = 0; uf < uframes; uf++) {
-					if (q.itd->hw_transaction [uf] != 0) {
-						temp = q;
-						*q_p = q.itd->itd_next;
-						*hw_p = q.itd->hw_next;
-						type = Q_NEXT_TYPE (*hw_p);
-
-						/* might free q.itd ... */
-						count += itd_complete (ehci,
-							temp.itd, uf, regs);
-						break;
-					}
-				}
-				/* we might skip this ITD's uframe ... */
-				if (uf == uframes) {
-					q_p = &q.itd->itd_next;
-					hw_p = &q.itd->hw_next;
-					type = Q_NEXT_TYPE (q.itd->hw_next);
-				}
-
-				q = *q_p;
-				break;
-#ifdef have_split_iso
-			case Q_TYPE_SITD:
-				last = (q.sitd->hw_next == EHCI_LIST_END);
-				sitd_complete (ehci, q.sitd);
-				type = Q_NEXT_TYPE (q.sitd->hw_next);
-
-				// FIXME unlink SITD after split completes
-				q = q.sitd->sitd_next;
-				break;
-#endif /* have_split_iso */
-			default:
-				dbg ("corrupt type %d frame %d shadow %p",
-					type, frame, q.ptr);
-				// BUG ();
-				last = 1;
-				q.ptr = 0;
-			}
-
-			/* did completion remove an interior q entry? */
-			if (unlikely (q.ptr == 0 && !last))
-				goto restart;
-		}
-
-		/* stop when we catch up to the HC */
-
-		// FIXME:  this assumes we won't get lapped when
-		// latencies climb; that should be rare, but...
-		// detect it, and just go all the way around.
-		// FLR might help detect this case, so long as latencies
-		// don't exceed periodic_size msec (default 1.024 sec).
-
-		// FIXME:  likewise assumes HC doesn't halt mid-scan
-
-		if (frame == clock) {
-			unsigned	now;
-
-			if (!HCD_IS_RUNNING (ehci->hcd.state))
-				break;
-			ehci->next_uframe = now_uframe;
-			now = readl (&ehci->regs->frame_index) % mod;
-			if (now_uframe == now)
-				break;
-
-			/* rescan the rest of this frame, then ... */
-			now_uframe = now;
-			clock = now_uframe >> 3;
-		} else
-			frame = (frame + 1) % ehci->periodic_size;
-	} 
-}
diff -urN linux-2.4.21-pre5/drivers/usb/hcd/ehci.h linux-2.4.21-pre6/drivers/usb/hcd/ehci.h
--- linux-2.4.21-pre5/drivers/usb/hcd/ehci.h	Wed Mar 26 16:11:29 2003
+++ linux-2.4.21-pre6/drivers/usb/hcd/ehci.h	Wed Dec 31 16:00:00 1969
@@ -1,452 +0,0 @@
-/*
- * Copyright (c) 2001-2002 by David Brownell
- * 
- * 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.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#ifndef __LINUX_EHCI_HCD_H
-#define __LINUX_EHCI_HCD_H
-
-/* definitions used for the EHCI driver */
-
-/* statistics can be kept for for tuning/monitoring */
-struct ehci_stats {
-	/* irq usage */
-	unsigned long		normal;
-	unsigned long		error;
-	unsigned long		reclaim;
-
-	/* termination of urbs from core */
-	unsigned long		complete;
-	unsigned long		unlink;
-};
-
-/* ehci_hcd->lock guards shared data against other CPUs:
- *   ehci_hcd:	async, reclaim, periodic (and shadow), ...
- *   hcd_dev:	ep[]
- *   ehci_qh:	qh_next, qtd_list
- *   ehci_qtd:	qtd_list
- *
- * Also, hold this lock when talking to HC registers or
- * when updating hw_* fields in shared qh/qtd/... structures.
- */
-
-#define	EHCI_MAX_ROOT_PORTS	15		/* see HCS_N_PORTS */
-
-struct ehci_hcd {			/* one per controller */
-	spinlock_t		lock;
-
-	/* async schedule support */
-	struct ehci_qh		*async;
-	struct ehci_qh		*reclaim;
-	int			reclaim_ready : 1,
-				async_idle : 1;
-
-	/* periodic schedule support */
-#define	DEFAULT_I_TDPS		1024		/* some HCs can do less */
-	unsigned		periodic_size;
-	u32			*periodic;	/* hw periodic table */
-	dma_addr_t		periodic_dma;
-	unsigned		i_thresh;	/* uframes HC might cache */
-
-	union ehci_shadow	*pshadow;	/* mirror hw periodic table */
-	int			next_uframe;	/* scan periodic, start here */
-	unsigned		periodic_sched;	/* periodic activity count */
-
-	/* per root hub port */
-	unsigned long		reset_done [EHCI_MAX_ROOT_PORTS];
-
-	/* glue to PCI and HCD framework */
-	struct usb_hcd		hcd;
-	struct ehci_caps	*caps;
-	struct ehci_regs	*regs;
-	u32			hcs_params;	/* cached register copy */
-
-	/* per-HC memory pools (could be per-PCI-bus, but ...) */
-	struct pci_pool		*qh_pool;	/* qh per active urb */
-	struct pci_pool		*qtd_pool;	/* one or more per qh */
-	struct pci_pool		*itd_pool;	/* itd per iso urb */
-	struct pci_pool		*sitd_pool;	/* sitd per split iso urb */
-
-	struct timer_list	watchdog;
-	unsigned		stamp;
-
-#ifdef EHCI_STATS
-	struct ehci_stats	stats;
-#	define COUNT(x) do { (x)++; } while (0)
-#else
-#	define COUNT(x) do {} while (0)
-#endif
-};
-
-/* unwrap an HCD pointer to get an EHCI_HCD pointer */ 
-#define hcd_to_ehci(hcd_ptr) container_of(hcd_ptr, struct ehci_hcd, hcd)
-
-/* NOTE:  urb->transfer_flags expected to not use this bit !!! */
-#define EHCI_STATE_UNLINK	0x8000		/* urb being unlinked */
-
-/*-------------------------------------------------------------------------*/
-
-/* EHCI register interface, corresponds to EHCI Revision 0.95 specification */
-
-/* Section 2.2 Host Controller Capability Registers */
-struct ehci_caps {
-	u8		length;		/* CAPLENGTH - size of this struct */
-	u8		reserved;       /* offset 0x1 */
-	u16		hci_version;    /* HCIVERSION - offset 0x2 */
-	u32		hcs_params;     /* HCSPARAMS - offset 0x4 */
-#define HCS_DEBUG_PORT(p)	(((p)>>20)&0xf)	/* bits 23:20, debug port? */
-#define HCS_INDICATOR(p)	((p)&(1 << 16))	/* true: has port indicators */
-#define HCS_N_CC(p)		(((p)>>12)&0xf)	/* bits 15:12, #companion HCs */
-#define HCS_N_PCC(p)		(((p)>>8)&0xf)	/* bits 11:8, ports per CC */
-#define HCS_PORTROUTED(p)	((p)&(1 << 7))	/* true: port routing */ 
-#define HCS_PPC(p)		((p)&(1 << 4))	/* true: port power control */ 
-#define HCS_N_PORTS(p)		(((p)>>0)&0xf)	/* bits 3:0, ports on HC */
-
-	u32		hcc_params;      /* HCCPARAMS - offset 0x8 */
-#define HCC_EXT_CAPS(p)		(((p)>>8)&0xff)	/* for pci extended caps */
-#define HCC_ISOC_CACHE(p)       ((p)&(1 << 7))  /* true: can cache isoc frame */
-#define HCC_ISOC_THRES(p)       (((p)>>4)&0x7)  /* bits 6:4, uframes cached */
-#define HCC_CANPARK(p)		((p)&(1 << 2))  /* true: can park on async qh */
-#define HCC_PGM_FRAMELISTLEN(p) ((p)&(1 << 1))  /* true: periodic_size changes*/
-#define HCC_64BIT_ADDR(p)       ((p)&(1))       /* true: can use 64-bit addr */
-	u8		portroute [8];	 /* nibbles for routing - offset 0xC */
-} __attribute__ ((packed));
-
-
-/* Section 2.3 Host Controller Operational Registers */
-struct ehci_regs {
-
-	/* USBCMD: offset 0x00 */
-	u32		command;
-/* 23:16 is r/w intr rate, in microframes; default "8" == 1/msec */
-#define CMD_PARK	(1<<11)		/* enable "park" on async qh */
-#define CMD_PARK_CNT(c)	(((c)>>8)&3)	/* how many transfers to park for */
-#define CMD_LRESET	(1<<7)		/* partial reset (no ports, etc) */
-#define CMD_IAAD	(1<<6)		/* "doorbell" interrupt async advance */
-#define CMD_ASE		(1<<5)		/* async schedule enable */
-#define CMD_PSE  	(1<<4)		/* periodic schedule enable */
-/* 3:2 is periodic frame list size */
-#define CMD_RESET	(1<<1)		/* reset HC not bus */
-#define CMD_RUN		(1<<0)		/* start/stop HC */
-
-	/* USBSTS: offset 0x04 */
-	u32		status;
-#define STS_ASS		(1<<15)		/* Async Schedule Status */
-#define STS_PSS		(1<<14)		/* Periodic Schedule Status */
-#define STS_RECL	(1<<13)		/* Reclamation */
-#define STS_HALT	(1<<12)		/* Not running (any reason) */
-/* some bits reserved */
-	/* these STS_* flags are also intr_enable bits (USBINTR) */
-#define STS_IAA		(1<<5)		/* Interrupted on async advance */
-#define STS_FATAL	(1<<4)		/* such as some PCI access errors */
-#define STS_FLR		(1<<3)		/* frame list rolled over */
-#define STS_PCD		(1<<2)		/* port change detect */
-#define STS_ERR		(1<<1)		/* "error" completion (overflow, ...) */
-#define STS_INT		(1<<0)		/* "normal" completion (short, ...) */
-
-	/* USBINTR: offset 0x08 */
-	u32		intr_enable;
-
-	/* FRINDEX: offset 0x0C */
-	u32		frame_index;	/* current microframe number */
-	/* CTRLDSSEGMENT: offset 0x10 */
-	u32		segment; 	/* address bits 63:32 if needed */
-	/* PERIODICLISTBASE: offset 0x14 */
-	u32		frame_list; 	/* points to periodic list */
-	/* ASYNCICLISTADDR: offset 0x18 */
-	u32		async_next;	/* address of next async queue head */
-
-	u32		reserved [9];
-
-	/* CONFIGFLAG: offset 0x40 */
-	u32		configured_flag;
-#define FLAG_CF		(1<<0)		/* true: we'll support "high speed" */
-
-	/* PORTSC: offset 0x44 */
-	u32		port_status [0];	/* up to N_PORTS */
-/* 31:23 reserved */
-#define PORT_WKOC_E	(1<<22)		/* wake on overcurrent (enable) */
-#define PORT_WKDISC_E	(1<<21)		/* wake on disconnect (enable) */
-#define PORT_WKCONN_E	(1<<20)		/* wake on connect (enable) */
-/* 19:16 for port testing */
-/* 15:14 for using port indicator leds (if HCS_INDICATOR allows) */
-#define PORT_OWNER	(1<<13)		/* true: companion hc owns this port */
-#define PORT_POWER	(1<<12)		/* true: has power (see PPC) */
-#define PORT_USB11(x) (((x)&(3<<10))==(1<<10))	/* USB 1.1 device */
-/* 11:10 for detecting lowspeed devices (reset vs release ownership) */
-/* 9 reserved */
-#define PORT_RESET	(1<<8)		/* reset port */
-#define PORT_SUSPEND	(1<<7)		/* suspend port */
-#define PORT_RESUME	(1<<6)		/* resume it */
-#define PORT_OCC	(1<<5)		/* over current change */
-#define PORT_OC		(1<<4)		/* over current active */
-#define PORT_PEC	(1<<3)		/* port enable change */
-#define PORT_PE		(1<<2)		/* port enable */
-#define PORT_CSC	(1<<1)		/* connect status change */
-#define PORT_CONNECT	(1<<0)		/* device connected */
-} __attribute__ ((packed));
-
-
-/*-------------------------------------------------------------------------*/
-
-#define	QTD_NEXT(dma)	cpu_to_le32((u32)dma)
-
-/*
- * EHCI Specification 0.95 Section 3.5
- * QTD: describe data transfer components (buffer, direction, ...) 
- * See Fig 3-6 "Queue Element Transfer Descriptor Block Diagram".
- *
- * These are associated only with "QH" (Queue Head) structures,
- * used with control, bulk, and interrupt transfers.
- */
-struct ehci_qtd {
-	/* first part defined by EHCI spec */
-	u32			hw_next;	  /* see EHCI 3.5.1 */
-	u32			hw_alt_next;      /* see EHCI 3.5.2 */
-	u32			hw_token;         /* see EHCI 3.5.3 */       
-#define	QTD_TOGGLE	(1 << 31)	/* data toggle */
-#define	QTD_LENGTH(tok)	(((tok)>>16) & 0x7fff)
-#define	QTD_IOC		(1 << 15)	/* interrupt on complete */
-#define	QTD_CERR(tok)	(((tok)>>10) & 0x3)
-#define	QTD_PID(tok)	(((tok)>>8) & 0x3)
-#define	QTD_STS_ACTIVE	(1 << 7)	/* HC may execute this */
-#define	QTD_STS_HALT	(1 << 6)	/* halted on error */
-#define	QTD_STS_DBE	(1 << 5)	/* data buffer error (in HC) */
-#define	QTD_STS_BABBLE	(1 << 4)	/* device was babbling (qtd halted) */
-#define	QTD_STS_XACT	(1 << 3)	/* device gave illegal response */
-#define	QTD_STS_MMF	(1 << 2)	/* incomplete split transaction */
-#define	QTD_STS_STS	(1 << 1)	/* split transaction state */
-#define	QTD_STS_PING	(1 << 0)	/* issue PING? */
-	u32			hw_buf [5];        /* see EHCI 3.5.4 */
-	u32			hw_buf_hi [5];        /* Appendix B */
-
-	/* the rest is HCD-private */
-	dma_addr_t		qtd_dma;		/* qtd address */
-	struct list_head	qtd_list;		/* sw qtd list */
-	struct urb		*urb;			/* qtd's urb */
-	size_t			length;			/* length of buffer */
-} __attribute__ ((aligned (32)));
-
-#define QTD_MASK cpu_to_le32 (~0x1f)	/* mask NakCnt+T in qh->hw_alt_next */
-
-/*-------------------------------------------------------------------------*/
-
-/* type tag from {qh,itd,sitd,fstn}->hw_next */
-#define Q_NEXT_TYPE(dma) ((dma) & __constant_cpu_to_le32 (3 << 1))
-
-/* values for that type tag */
-#define Q_TYPE_ITD	__constant_cpu_to_le32 (0 << 1)
-#define Q_TYPE_QH	__constant_cpu_to_le32 (1 << 1)
-#define Q_TYPE_SITD 	__constant_cpu_to_le32 (2 << 1)
-#define Q_TYPE_FSTN 	__constant_cpu_to_le32 (3 << 1)
-
-/* next async queue entry, or pointer to interrupt/periodic QH */
-#define	QH_NEXT(dma)	(cpu_to_le32(((u32)dma)&~0x01f)|Q_TYPE_QH)
-
-/* for periodic/async schedules and qtd lists, mark end of list */
-#define	EHCI_LIST_END	__constant_cpu_to_le32(1) /* "null pointer" to hw */
-
-/*
- * Entries in periodic shadow table are pointers to one of four kinds
- * of data structure.  That's dictated by the hardware; a type tag is
- * encoded in the low bits of the hardware's periodic schedule.  Use
- * Q_NEXT_TYPE to get the tag.
- *
- * For entries in the async schedule, the type tag always says "qh".
- */
-union ehci_shadow {
-	struct ehci_qh 		*qh;		/* Q_TYPE_QH */
-	struct ehci_itd		*itd;		/* Q_TYPE_ITD */
-	struct ehci_sitd	*sitd;		/* Q_TYPE_SITD */
-	struct ehci_fstn	*fstn;		/* Q_TYPE_FSTN */
-	void			*ptr;
-};
-
-/*-------------------------------------------------------------------------*/
-
-/*
- * EHCI Specification 0.95 Section 3.6
- * QH: describes control/bulk/interrupt endpoints
- * See Fig 3-7 "Queue Head Structure Layout".
- *
- * These appear in both the async and (for interrupt) periodic schedules.
- */
-
-struct ehci_qh {
-	/* first part defined by EHCI spec */
-	u32			hw_next;	 /* see EHCI 3.6.1 */
-	u32			hw_info1;        /* see EHCI 3.6.2 */
-#define	QH_HEAD		0x00008000
-	u32			hw_info2;        /* see EHCI 3.6.2 */
-	u32			hw_current;	 /* qtd list - see EHCI 3.6.4 */
-	
-	/* qtd overlay (hardware parts of a struct ehci_qtd) */
-	u32			hw_qtd_next;
-	u32			hw_alt_next;
-	u32			hw_token;
-	u32			hw_buf [5];
-	u32			hw_buf_hi [5];
-
-	/* the rest is HCD-private */
-	dma_addr_t		qh_dma;		/* address of qh */
-	union ehci_shadow	qh_next;	/* ptr to qh; or periodic */
-	struct list_head	qtd_list;	/* sw qtd list */
-	struct ehci_qtd		*dummy;
-	struct ehci_qh		*reclaim;	/* next to reclaim */
-
-	atomic_t		refcount;
-	unsigned		stamp;
-
-	u8			qh_state;
-#define	QH_STATE_LINKED		1		/* HC sees this */
-#define	QH_STATE_UNLINK		2		/* HC may still see this */
-#define	QH_STATE_IDLE		3		/* HC doesn't see this */
-#define	QH_STATE_UNLINK_WAIT	4		/* LINKED and on reclaim q */
-#define	QH_STATE_COMPLETING	5		/* don't touch token.HALT */
-
-	/* periodic schedule info */
-	u8			usecs;		/* intr bandwidth */
-	u8			gap_uf;		/* uframes split/csplit gap */
-	u8			c_usecs;	/* ... split completion bw */
-	unsigned short		period;		/* polling interval */
-	unsigned short		start;		/* where polling starts */
-#define NO_FRAME ((unsigned short)~0)			/* pick new start */
-
-} __attribute__ ((aligned (32)));
-
-/*-------------------------------------------------------------------------*/
-
-/*
- * EHCI Specification 0.95 Section 3.3
- * Fig 3-4 "Isochronous Transaction Descriptor (iTD)"
- *
- * Schedule records for high speed iso xfers
- */
-struct ehci_itd {
-	/* first part defined by EHCI spec */
-	u32			hw_next;           /* see EHCI 3.3.1 */
-	u32			hw_transaction [8]; /* see EHCI 3.3.2 */
-#define EHCI_ISOC_ACTIVE        (1<<31)        /* activate transfer this slot */
-#define EHCI_ISOC_BUF_ERR       (1<<30)        /* Data buffer error */
-#define EHCI_ISOC_BABBLE        (1<<29)        /* babble detected */
-#define EHCI_ISOC_XACTERR       (1<<28)        /* XactErr - transaction error */
-#define	EHCI_ITD_LENGTH(tok)	(((tok)>>16) & 0x7fff)
-#define	EHCI_ITD_IOC		(1 << 15)	/* interrupt on complete */
-
-	u32			hw_bufp [7];	/* see EHCI 3.3.3 */ 
-	u32			hw_bufp_hi [7];	/* Appendix B */
-
-	/* the rest is HCD-private */
-	dma_addr_t		itd_dma;	/* for this itd */
-	union ehci_shadow	itd_next;	/* ptr to periodic q entry */
-
-	struct urb		*urb;
-	struct list_head	itd_list;	/* list of urb frames' itds */
-	dma_addr_t		buf_dma;	/* frame's buffer address */
-
-	/* for now, only one hw_transaction per itd */
-	u32			transaction;
-	u16			index;		/* in urb->iso_frame_desc */
-	u16			uframe;		/* in periodic schedule */
-	u16			usecs;
-} __attribute__ ((aligned (32)));
-
-/*-------------------------------------------------------------------------*/
-
-/*
- * EHCI Specification 0.95 Section 3.4 
- * siTD, aka split-transaction isochronous Transfer Descriptor
- *       ... describe low/full speed iso xfers through TT in hubs
- * see Figure 3-5 "Split-transaction Isochronous Transaction Descriptor (siTD)
- */
-struct ehci_sitd {
-	/* first part defined by EHCI spec */
-	u32			hw_next;
-/* uses bit field macros above - see EHCI 0.95 Table 3-8 */
-	u32			hw_fullspeed_ep;  /* see EHCI table 3-9 */
-	u32                     hw_uframe;        /* see EHCI table 3-10 */
-        u32                     hw_tx_results1;   /* see EHCI table 3-11 */
-	u32                     hw_tx_results2;   /* see EHCI table 3-12 */
-	u32                     hw_tx_results3;   /* see EHCI table 3-12 */
-        u32                     hw_backpointer;   /* see EHCI table 3-13 */
-	u32			hw_buf_hi [2];	  /* Appendix B */
-
-	/* the rest is HCD-private */
-	dma_addr_t		sitd_dma;
-	union ehci_shadow	sitd_next;	/* ptr to periodic q entry */
-	struct urb		*urb;
-	dma_addr_t		buf_dma;	/* buffer address */
-
-	unsigned short		usecs;		/* start bandwidth */
-	unsigned short		c_usecs;	/* completion bandwidth */
-} __attribute__ ((aligned (32)));
-
-/*-------------------------------------------------------------------------*/
-
-/*
- * EHCI Specification 0.96 Section 3.7
- * Periodic Frame Span Traversal Node (FSTN)
- *
- * Manages split interrupt transactions (using TT) that span frame boundaries
- * into uframes 0/1; see 4.12.2.2.  In those uframes, a "save place" FSTN
- * makes the HC jump (back) to a QH to scan for fs/ls QH completions until
- * it hits a "restore" FSTN; then it returns to finish other uframe 0/1 work.
- */
-struct ehci_fstn {
-	u32			hw_next;	/* any periodic q entry */
-	u32			hw_prev;	/* qh or EHCI_LIST_END */
-
-	/* the rest is HCD-private */
-	dma_addr_t		fstn_dma;
-	union ehci_shadow	fstn_next;	/* ptr to periodic q entry */
-} __attribute__ ((aligned (32)));
-
-/*-------------------------------------------------------------------------*/
-
-#include <linux/version.h>
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,32)
-
-#define SUBMIT_URB(urb,mem_flags) usb_submit_urb(urb)
-#define STUB_DEBUG_FILES
-
-static inline int hcd_register_root (struct usb_hcd *hcd)
-{
-	return usb_new_device (hcd_to_bus (hcd)->root_hub);
-}
-
-#else	/* LINUX_VERSION_CODE */
-
-// hcd_to_bus() eventually moves to hcd.h on 2.5 too
-static inline struct usb_bus *hcd_to_bus (struct usb_hcd *hcd)
-	{ return &hcd->self; }
-// ... as does hcd_register_root()
-static inline int hcd_register_root (struct usb_hcd *hcd)
-{
-	return usb_register_root_hub (
-		hcd_to_bus (hcd)->root_hub, &hcd->pdev->dev);
-}
-
-#define SUBMIT_URB(urb,mem_flags) usb_submit_urb(urb,mem_flags)
-
-#ifndef DEBUG
-#define STUB_DEBUG_FILES
-#endif	/* DEBUG */
-
-#endif	/* LINUX_VERSION_CODE */
-
-/*-------------------------------------------------------------------------*/
-
-#endif /* __LINUX_EHCI_HCD_H */
diff -urN linux-2.4.21-pre5/drivers/usb/hcd.c linux-2.4.21-pre6/drivers/usb/hcd.c
--- linux-2.4.21-pre5/drivers/usb/hcd.c	Wed Mar 26 16:11:29 2003
+++ linux-2.4.21-pre6/drivers/usb/hcd.c	Wed Mar 26 16:11:39 2003
@@ -935,6 +935,12 @@
 
 /*-------------------------------------------------------------------------*/
 
+static void hcd_panic (void *_hcd)
+{
+	struct usb_hcd *hcd = _hcd;
+	hcd->driver->stop (hcd);
+}
+
 static void hc_died (struct usb_hcd *hcd)
 {
 	struct list_head	*devlist, *urblist;
@@ -962,7 +968,10 @@
 
 	if (urb)
 		rh_status_dequeue (hcd, urb);
-	hcd->driver->stop (hcd);
+
+	/* hcd->stop() needs a task context */
+	INIT_TQUEUE (&hcd->work, hcd_panic, hcd);
+	(void) schedule_task (&hcd->work);
 }
 
 /*-------------------------------------------------------------------------*/
diff -urN linux-2.4.21-pre5/drivers/usb/hcd.h linux-2.4.21-pre6/drivers/usb/hcd.h
--- linux-2.4.21-pre5/drivers/usb/hcd.h	Wed Mar 26 16:11:29 2003
+++ linux-2.4.21-pre6/drivers/usb/hcd.h	Wed Mar 26 16:11:39 2003
@@ -41,6 +41,7 @@
 
 	struct timer_list	rh_timer;	/* drives root hub */
 	struct list_head	dev_list;	/* devices on this bus */
+	struct tq_struct	work;
 
 	/*
 	 * hardware info/state
diff -urN linux-2.4.21-pre5/drivers/usb/hid-core.c linux-2.4.21-pre6/drivers/usb/hid-core.c
--- linux-2.4.21-pre5/drivers/usb/hid-core.c	Wed Mar 26 16:11:29 2003
+++ linux-2.4.21-pre6/drivers/usb/hid-core.c	Wed Mar 26 16:11:39 2003
@@ -1080,6 +1080,9 @@
 #define USB_DEVICE_ID_WACOM_PL		0x0030
 #define USB_DEVICE_ID_WACOM_INTUOS2	0x0041
 
+#define USB_VENDOR_ID_KBGEAR		0x084e
+#define USB_DEVICE_ID_KBGEAR_JAMSTUDIO	0x1001
+
 #define USB_VENDOR_ID_ATEN		0x0557
 #define USB_DEVICE_ID_ATEN_UC100KM	0x2004
 #define USB_DEVICE_ID_ATEN_CS124U	0x2202
@@ -1096,6 +1099,9 @@
 #define USB_VENDOR_ID_TANGTOP		0x0d3d
 #define USB_DEVICE_ID_TANGTOP_USBPS2	0x0001
 
+#define USB_VENDOR_ID_OKI		0x070a
+#define USB_VENDOR_ID_OKI_MULITI	0x0007
+
 struct hid_blacklist {
 	__u16 idVendor;
 	__u16 idProduct;
@@ -1121,6 +1127,7 @@
 	{ USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_INTUOS2 + 2, HID_QUIRK_IGNORE },
 	{ USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_INTUOS2 + 3, HID_QUIRK_IGNORE },
 	{ USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_INTUOS2 + 4, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_KBGEAR, USB_DEVICE_ID_KBGEAR_JAMSTUDIO, HID_QUIRK_IGNORE },
 	{ USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_UC100KM, HID_QUIRK_NOGET },
 	{ USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_CS124U, HID_QUIRK_NOGET },
 	{ USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_2PORTKVM, HID_QUIRK_NOGET },
@@ -1134,6 +1141,7 @@
  	{ USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 400, HID_QUIRK_IGNORE },
  	{ USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 500, HID_QUIRK_IGNORE },
  	{ USB_VENDOR_ID_TANGTOP, USB_DEVICE_ID_TANGTOP_USBPS2, HID_QUIRK_NOGET },
+	{ USB_VENDOR_ID_OKI, USB_VENDOR_ID_OKI_MULITI, HID_QUIRK_NOGET },
 	{ 0, 0 }
 };
 
diff -urN linux-2.4.21-pre5/drivers/usb/host/Config.in linux-2.4.21-pre6/drivers/usb/host/Config.in
--- linux-2.4.21-pre5/drivers/usb/host/Config.in	Wed Dec 31 16:00:00 1969
+++ linux-2.4.21-pre6/drivers/usb/host/Config.in	Wed Mar 26 16:11:39 2003
@@ -0,0 +1,15 @@
+#
+# USB Host Controller Drivers
+#
+comment 'USB Host Controller Drivers'
+dep_tristate '  EHCI HCD (USB 2.0) support (EXPERIMENTAL)' CONFIG_USB_EHCI_HCD $CONFIG_USB $CONFIG_EXPERIMENTAL
+if [ "$CONFIG_USB_UHCI_ALT" != "y" ]; then
+   dep_tristate '  UHCI (Intel PIIX4, VIA, ...) support' CONFIG_USB_UHCI $CONFIG_USB
+fi
+if [ "$CONFIG_USB_UHCI" != "y" ]; then
+   dep_tristate '  UHCI Alternate Driver (JE) support' CONFIG_USB_UHCI_ALT $CONFIG_USB
+else
+   define_bool CONFIG_USB_UHCI_ALT n
+fi
+dep_tristate '  OHCI (Compaq, iMacs, OPTi, SiS, ALi, ...) support' CONFIG_USB_OHCI $CONFIG_USB
+
diff -urN linux-2.4.21-pre5/drivers/usb/host/Makefile linux-2.4.21-pre6/drivers/usb/host/Makefile
--- linux-2.4.21-pre5/drivers/usb/host/Makefile	Wed Dec 31 16:00:00 1969
+++ linux-2.4.21-pre6/drivers/usb/host/Makefile	Wed Mar 26 16:11:39 2003
@@ -0,0 +1,28 @@
+#
+# Makefile for USB Host Controller Driver
+# framework and drivers
+#
+
+O_TARGET	:=
+
+obj-$(CONFIG_USB_EHCI_HCD)			+= ehci-hcd.o
+obj-$(CONFIG_USB_UHCI_ALT)			+= uhci.o
+obj-$(CONFIG_USB_UHCI)				+= usb-uhci.o
+obj-$(CONFIG_USB_OHCI)				+= usb-ohci.o
+
+# Extract lists of the multi-part drivers.
+# The 'int-*' lists are the intermediate files used to build the multi's.
+multi-y		:= $(filter $(list-multi), $(obj-y))
+multi-m		:= $(filter $(list-multi), $(obj-m))
+int-y		:= $(sort $(foreach m, $(multi-y), $($(basename $(m))-objs)))
+int-m		:= $(sort $(foreach m, $(multi-m), $($(basename $(m))-objs)))
+
+# Take multi-part drivers out of obj-y and put components in.
+obj-y		:= $(filter-out $(list-multi), $(obj-y)) $(int-y)
+
+# Translate to Rules.make lists.
+OX_OBJS		:= $(obj-y)
+MX_OBJS		:= $(obj-m)
+MIX_OBJS	:= $(int-m)
+
+include $(TOPDIR)/Rules.make
diff -urN linux-2.4.21-pre5/drivers/usb/host/ehci-dbg.c linux-2.4.21-pre6/drivers/usb/host/ehci-dbg.c
--- linux-2.4.21-pre5/drivers/usb/host/ehci-dbg.c	Wed Dec 31 16:00:00 1969
+++ linux-2.4.21-pre6/drivers/usb/host/ehci-dbg.c	Wed Mar 26 16:11:39 2003
@@ -0,0 +1,652 @@
+/*
+ * Copyright (c) 2001-2002 by David Brownell
+ * 
+ * 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.
+ *
+ * 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.
+ */
+
+/* this file is part of ehci-hcd.c */
+
+#ifdef DEBUG
+#define ehci_dbg(ehci, fmt, args...) \
+	printk(KERN_DEBUG "%s %s: " fmt , hcd_name , \
+		(ehci)->hcd.pdev->slot_name , ## args )
+#else
+#define ehci_dbg(ehci, fmt, args...) do { } while (0)
+#endif
+
+#define ehci_err(ehci, fmt, args...) \
+	printk(KERN_ERR "%s %s: " fmt , hcd_name , \
+		(ehci)->hcd.pdev->slot_name , ## args )
+#define ehci_info(ehci, fmt, args...) \
+	printk(KERN_INFO "%s %s: " fmt , hcd_name , \
+		(ehci)->hcd.pdev->slot_name , ## args )
+#define ehci_warn(ehci, fmt, args...) \
+	printk(KERN_WARNING "%s %s: " fmt , hcd_name , \
+		(ehci)->hcd.pdev->slot_name , ## args )
+
+
+#ifdef EHCI_VERBOSE_DEBUG
+#	define vdbg dbg
+#	define ehci_vdbg ehci_dbg
+#else
+#	define vdbg(fmt,args...) do { } while (0)
+#	define ehci_vdbg(ehci, fmt, args...) do { } while (0)
+#endif
+
+#ifdef	DEBUG
+
+/* check the values in the HCSPARAMS register
+ * (host controller _Structural_ parameters)
+ * see EHCI spec, Table 2-4 for each value
+ */
+static void dbg_hcs_params (struct ehci_hcd *ehci, char *label)
+{
+	u32	params = readl (&ehci->caps->hcs_params);
+
+	ehci_dbg (ehci,
+		"%s hcs_params 0x%x dbg=%d%s cc=%d pcc=%d%s%s ports=%d\n",
+		label, params,
+		HCS_DEBUG_PORT (params),
+		HCS_INDICATOR (params) ? " ind" : "",
+		HCS_N_CC (params),
+		HCS_N_PCC (params),
+	        HCS_PORTROUTED (params) ? "" : " ordered",
+		HCS_PPC (params) ? "" : " !ppc",
+		HCS_N_PORTS (params)
+		);
+	/* Port routing, per EHCI 0.95 Spec, Section 2.2.5 */
+	if (HCS_PORTROUTED (params)) {
+		int i;
+		char buf [46], tmp [7], byte;
+
+		buf[0] = 0;
+		for (i = 0; i < HCS_N_PORTS (params); i++) {
+			byte = readb (&ehci->caps->portroute[(i>>1)]);
+			sprintf(tmp, "%d ", 
+				((i & 0x1) ? ((byte)&0xf) : ((byte>>4)&0xf)));
+			strcat(buf, tmp);
+		}
+		ehci_dbg (ehci, "%s portroute %s\n",
+				label, buf);
+	}
+}
+#else
+
+static inline void dbg_hcs_params (struct ehci_hcd *ehci, char *label) {}
+
+#endif
+
+#ifdef	DEBUG
+
+/* check the values in the HCCPARAMS register
+ * (host controller _Capability_ parameters)
+ * see EHCI Spec, Table 2-5 for each value
+ * */
+static void dbg_hcc_params (struct ehci_hcd *ehci, char *label)
+{
+	u32	params = readl (&ehci->caps->hcc_params);
+
+	if (HCC_ISOC_CACHE (params)) {
+		ehci_dbg (ehci,
+		     "%s hcc_params %04x caching frame %s%s%s\n",
+		     label, params,
+		     HCC_PGM_FRAMELISTLEN (params) ? "256/512/1024" : "1024",
+		     HCC_CANPARK (params) ? " park" : "",
+		     HCC_64BIT_ADDR (params) ? " 64 bit addr" : "");
+	} else {
+		ehci_dbg (ehci,
+		     "%s hcc_params %04x thresh %d uframes %s%s%s\n",
+		     label,
+		     params,
+		     HCC_ISOC_THRES (params),
+		     HCC_PGM_FRAMELISTLEN (params) ? "256/512/1024" : "1024",
+		     HCC_CANPARK (params) ? " park" : "",
+		     HCC_64BIT_ADDR (params) ? " 64 bit addr" : "");
+	}
+}
+#else
+
+static inline void dbg_hcc_params (struct ehci_hcd *ehci, char *label) {}
+
+#endif
+
+#ifdef	DEBUG
+
+static void __attribute__((__unused__))
+dbg_qh (char *label, struct ehci_hcd *ehci, struct ehci_qh *qh)
+{
+	dbg ("%s %p info1 %x info2 %x hw_curr %x qtd_next %x", label,
+		qh, qh->hw_info1, qh->hw_info2,
+		qh->hw_current, qh->hw_qtd_next);
+	dbg ("  alt+errs= %x, token= %x, page0= %x, page1= %x",
+		qh->hw_alt_next, qh->hw_token,
+		qh->hw_buf [0], qh->hw_buf [1]);
+	if (qh->hw_buf [2]) {
+		dbg ("  page2= %x, page3= %x, page4= %x",
+			qh->hw_buf [2], qh->hw_buf [3],
+			qh->hw_buf [4]);
+	}
+}
+
+static int __attribute__((__unused__))
+dbg_status_buf (char *buf, unsigned len, char *label, u32 status)
+{
+	return snprintf (buf, len,
+		"%s%sstatus %04x%s%s%s%s%s%s%s%s%s%s",
+		label, label [0] ? " " : "", status,
+		(status & STS_ASS) ? " Async" : "",
+		(status & STS_PSS) ? " Periodic" : "",
+		(status & STS_RECL) ? " Recl" : "",
+		(status & STS_HALT) ? " Halt" : "",
+		(status & STS_IAA) ? " IAA" : "",
+		(status & STS_FATAL) ? " FATAL" : "",
+		(status & STS_FLR) ? " FLR" : "",
+		(status & STS_PCD) ? " PCD" : "",
+		(status & STS_ERR) ? " ERR" : "",
+		(status & STS_INT) ? " INT" : ""
+		);
+}
+
+static int __attribute__((__unused__))
+dbg_intr_buf (char *buf, unsigned len, char *label, u32 enable)
+{
+	return snprintf (buf, len,
+		"%s%sintrenable %02x%s%s%s%s%s%s",
+		label, label [0] ? " " : "", enable,
+		(enable & STS_IAA) ? " IAA" : "",
+		(enable & STS_FATAL) ? " FATAL" : "",
+		(enable & STS_FLR) ? " FLR" : "",
+		(enable & STS_PCD) ? " PCD" : "",
+		(enable & STS_ERR) ? " ERR" : "",
+		(enable & STS_INT) ? " INT" : ""
+		);
+}
+
+static const char *const fls_strings [] =
+    { "1024", "512", "256", "??" };
+
+static int dbg_command_buf (char *buf, unsigned len, char *label, u32 command)
+{
+	return snprintf (buf, len,
+		"%s%scommand %06x %s=%d ithresh=%d%s%s%s%s period=%s%s %s",
+		label, label [0] ? " " : "", command,
+		(command & CMD_PARK) ? "park" : "(park)",
+		CMD_PARK_CNT (command),
+		(command >> 16) & 0x3f,
+		(command & CMD_LRESET) ? " LReset" : "",
+		(command & CMD_IAAD) ? " IAAD" : "",
+		(command & CMD_ASE) ? " Async" : "",
+		(command & CMD_PSE) ? " Periodic" : "",
+		fls_strings [(command >> 2) & 0x3],
+		(command & CMD_RESET) ? " Reset" : "",
+		(command & CMD_RUN) ? "RUN" : "HALT"
+		);
+}
+
+static int
+dbg_port_buf (char *buf, unsigned len, char *label, int port, u32 status)
+{
+	char	*sig;
+
+	/* signaling state */
+	switch (status & (3 << 10)) {
+	case 0 << 10: sig = "se0"; break;
+	case 1 << 10: sig = "k"; break;		/* low speed */
+	case 2 << 10: sig = "j"; break;
+	default: sig = "?"; break;
+	}
+
+	return snprintf (buf, len,
+		"%s%sport %d status %06x%s%s sig=%s %s%s%s%s%s%s%s%s%s",
+		label, label [0] ? " " : "", port, status,
+		(status & PORT_POWER) ? " POWER" : "",
+		(status & PORT_OWNER) ? " OWNER" : "",
+		sig,
+		(status & PORT_RESET) ? " RESET" : "",
+		(status & PORT_SUSPEND) ? " SUSPEND" : "",
+		(status & PORT_RESUME) ? " RESUME" : "",
+		(status & PORT_OCC) ? " OCC" : "",
+		(status & PORT_OC) ? " OC" : "",
+		(status & PORT_PEC) ? " PEC" : "",
+		(status & PORT_PE) ? " PE" : "",
+		(status & PORT_CSC) ? " CSC" : "",
+		(status & PORT_CONNECT) ? " CONNECT" : ""
+	    );
+}
+
+#else
+static inline void __attribute__((__unused__))
+dbg_qh (char *label, struct ehci_hcd *ehci, struct ehci_qh *qh)
+{}
+
+static inline int __attribute__((__unused__))
+dbg_status_buf (char *buf, unsigned len, char *label, u32 status)
+{ return 0; }
+
+static inline int __attribute__((__unused__))
+dbg_command_buf (char *buf, unsigned len, char *label, u32 command)
+{ return 0; }
+
+static inline int __attribute__((__unused__))
+dbg_intr_buf (char *buf, unsigned len, char *label, u32 enable)
+{ return 0; }
+
+static inline int __attribute__((__unused__))
+dbg_port_buf (char *buf, unsigned len, char *label, int port, u32 status)
+{ return 0; }
+
+#endif	/* DEBUG */
+
+/* functions have the "wrong" filename when they're output... */
+#define dbg_status(ehci, label, status) { \
+	char _buf [80]; \
+	dbg_status_buf (_buf, sizeof _buf, label, status); \
+	ehci_dbg (ehci, "%s\n", _buf); \
+}
+
+#define dbg_cmd(ehci, label, command) { \
+	char _buf [80]; \
+	dbg_command_buf (_buf, sizeof _buf, label, command); \
+	ehci_dbg (ehci, "%s\n", _buf); \
+}
+
+#define dbg_port(ehci, label, port, status) { \
+	char _buf [80]; \
+	dbg_port_buf (_buf, sizeof _buf, label, port, status); \
+	ehci_dbg (ehci, "%s\n", _buf); \
+}
+
+/*-------------------------------------------------------------------------*/
+
+#ifdef STUB_DEBUG_FILES
+
+static inline void create_debug_files (struct ehci_hcd *bus) { }
+static inline void remove_debug_files (struct ehci_hcd *bus) { }
+
+#else
+
+/* troubleshooting help: expose state in driverfs */
+
+#define speed_char(info1) ({ char tmp; \
+		switch (info1 & (3 << 12)) { \
+		case 0 << 12: tmp = 'f'; break; \
+		case 1 << 12: tmp = 'l'; break; \
+		case 2 << 12: tmp = 'h'; break; \
+		default: tmp = '?'; break; \
+		}; tmp; })
+
+static inline char token_mark (u32 token)
+{
+	token = le32_to_cpu (token);
+	if (token & QTD_STS_ACTIVE)
+		return '*';
+	if (token & QTD_STS_HALT)
+		return '-';
+	if (QTD_PID (token) != 1 /* not IN: OUT or SETUP */
+			|| QTD_LENGTH (token) == 0)
+		return ' ';
+	/* tries to advance through hw_alt_next */
+	return '/';
+}
+
+static void qh_lines (
+	struct ehci_hcd *ehci,
+	struct ehci_qh *qh,
+	char **nextp,
+	unsigned *sizep
+)
+{
+	u32			scratch;
+	u32			hw_curr;
+	struct list_head	*entry;
+	struct ehci_qtd		*td;
+	unsigned		temp;
+	unsigned		size = *sizep;
+	char			*next = *nextp;
+	char			mark;
+
+	mark = token_mark (qh->hw_token);
+	if (mark == '/') {	/* qh_alt_next controls qh advance? */
+		if ((qh->hw_alt_next & QTD_MASK) == ehci->async->hw_alt_next)
+			mark = '#';	/* blocked */
+		else if (qh->hw_alt_next & cpu_to_le32 (0x01))
+			mark = '.';	/* use hw_qtd_next */
+		/* else alt_next points to some other qtd */
+	}
+	scratch = cpu_to_le32p (&qh->hw_info1);
+	hw_curr = (mark == '*') ? cpu_to_le32p (&qh->hw_current) : 0;
+	temp = snprintf (next, size,
+			"qh/%p dev%d %cs ep%d %08x %08x (%08x%c %s nak%d)",
+			qh, scratch & 0x007f,
+			speed_char (scratch),
+			(scratch >> 8) & 0x000f,
+			scratch, cpu_to_le32p (&qh->hw_info2),
+			cpu_to_le32p (&qh->hw_token), mark,
+			(cpu_to_le32 (0x8000000) & qh->hw_token)
+				? "data0" : "data1",
+			(cpu_to_le32p (&qh->hw_alt_next) >> 1) & 0x0f);
+	size -= temp;
+	next += temp;
+
+	/* hc may be modifying the list as we read it ... */
+	list_for_each (entry, &qh->qtd_list) {
+		td = list_entry (entry, struct ehci_qtd, qtd_list);
+		scratch = cpu_to_le32p (&td->hw_token);
+		mark = ' ';
+		if (hw_curr == td->qtd_dma)
+			mark = '*';
+		else if (qh->hw_qtd_next == td->qtd_dma)
+			mark = '+';
+		else if (QTD_LENGTH (scratch)) {
+			if (td->hw_alt_next == ehci->async->hw_alt_next)
+				mark = '#';
+			else if (td->hw_alt_next != EHCI_LIST_END)
+				mark = '/';
+		}
+		temp = snprintf (next, size,
+				"\n\t%p%c%s len=%d %08x urb %p",
+				td, mark, ({ char *tmp;
+				 switch ((scratch>>8)&0x03) {
+				 case 0: tmp = "out"; break;
+				 case 1: tmp = "in"; break;
+				 case 2: tmp = "setup"; break;
+				 default: tmp = "?"; break;
+				 } tmp;}),
+				(scratch >> 16) & 0x7fff,
+				scratch,
+				td->urb);
+		if (temp < 0)
+			temp = 0;
+		else if (size < temp)
+			temp = size;
+		size -= temp;
+		next += temp;
+		if (temp == size)
+			goto done;
+	}
+
+	temp = snprintf (next, size, "\n");
+	if (temp < 0)
+		temp = 0;
+	else if (size < temp)
+		temp = size;
+	size -= temp;
+	next += temp;
+
+done:
+	*sizep = size;
+	*nextp = next;
+}
+
+static ssize_t
+show_async (struct device *dev, char *buf)
+{
+	struct pci_dev		*pdev;
+	struct ehci_hcd		*ehci;
+	unsigned long		flags;
+	unsigned		temp, size;
+	char			*next;
+	struct ehci_qh		*qh;
+
+	pdev = container_of (dev, struct pci_dev, dev);
+	ehci = container_of (pci_get_drvdata (pdev), struct ehci_hcd, hcd);
+	next = buf;
+	size = PAGE_SIZE;
+
+	/* dumps a snapshot of the async schedule.
+	 * usually empty except for long-term bulk reads, or head.
+	 * one QH per line, and TDs we know about
+	 */
+	spin_lock_irqsave (&ehci->lock, flags);
+	for (qh = ehci->async->qh_next.qh; size > 0 && qh; qh = qh->qh_next.qh)
+		qh_lines (ehci, qh, &next, &size);
+	if (ehci->reclaim && size > 0) {
+		temp = snprintf (next, size, "\nreclaim =\n");
+		size -= temp;
+		next += temp;
+
+		for (qh = ehci->reclaim; size > 0 && qh; qh = qh->reclaim)
+			qh_lines (ehci, qh, &next, &size);
+	}
+	spin_unlock_irqrestore (&ehci->lock, flags);
+
+	return PAGE_SIZE - size;
+}
+static DEVICE_ATTR (async, S_IRUGO, show_async, NULL);
+
+#define DBG_SCHED_LIMIT 64
+
+static ssize_t
+show_periodic (struct device *dev, char *buf)
+{
+	struct pci_dev		*pdev;
+	struct ehci_hcd		*ehci;
+	unsigned long		flags;
+	union ehci_shadow	p, *seen;
+	unsigned		temp, size, seen_count;
+	char			*next;
+	unsigned		i, tag;
+
+	if (!(seen = kmalloc (DBG_SCHED_LIMIT * sizeof *seen, SLAB_ATOMIC)))
+		return 0;
+	seen_count = 0;
+
+	pdev = container_of (dev, struct pci_dev, dev);
+	ehci = container_of (pci_get_drvdata (pdev), struct ehci_hcd, hcd);
+	next = buf;
+	size = PAGE_SIZE;
+
+	temp = snprintf (next, size, "size = %d\n", ehci->periodic_size);
+	size -= temp;
+	next += temp;
+
+	/* dump a snapshot of the periodic schedule.
+	 * iso changes, interrupt usually doesn't.
+	 */
+	spin_lock_irqsave (&ehci->lock, flags);
+	for (i = 0; i < ehci->periodic_size; i++) {
+		p = ehci->pshadow [i];
+		if (!p.ptr)
+			continue;
+		tag = Q_NEXT_TYPE (ehci->periodic [i]);
+
+		temp = snprintf (next, size, "%4d: ", i);
+		size -= temp;
+		next += temp;
+
+		do {
+			switch (tag) {
+			case Q_TYPE_QH:
+				temp = snprintf (next, size, " qh%d/%p",
+						p.qh->period, p.qh);
+				size -= temp;
+				next += temp;
+				for (temp = 0; temp < seen_count; temp++) {
+					if (seen [temp].ptr == p.ptr)
+						break;
+				}
+				/* show more info the first time around */
+				if (temp == seen_count) {
+					u32	scratch = cpu_to_le32p (
+							&p.qh->hw_info1);
+
+					temp = snprintf (next, size,
+						" (%cs dev%d ep%d [%d/%d] %d)",
+						speed_char (scratch),
+						scratch & 0x007f,
+						(scratch >> 8) & 0x000f,
+						p.qh->usecs, p.qh->c_usecs,
+						0x7ff & (scratch >> 16));
+
+					/* FIXME TD info too */
+
+					if (seen_count < DBG_SCHED_LIMIT)
+						seen [seen_count++].qh = p.qh;
+				} else
+					temp = 0;
+				tag = Q_NEXT_TYPE (p.qh->hw_next);
+				p = p.qh->qh_next;
+				break;
+			case Q_TYPE_FSTN:
+				temp = snprintf (next, size,
+					" fstn-%8x/%p", p.fstn->hw_prev,
+					p.fstn);
+				tag = Q_NEXT_TYPE (p.fstn->hw_next);
+				p = p.fstn->fstn_next;
+				break;
+			case Q_TYPE_ITD:
+				temp = snprintf (next, size,
+					" itd/%p", p.itd);
+				tag = Q_NEXT_TYPE (p.itd->hw_next);
+				p = p.itd->itd_next;
+				break;
+			case Q_TYPE_SITD:
+				temp = snprintf (next, size,
+					" sitd/%p", p.sitd);
+				tag = Q_NEXT_TYPE (p.sitd->hw_next);
+				p = p.sitd->sitd_next;
+				break;
+			}
+			size -= temp;
+			next += temp;
+		} while (p.ptr);
+
+		temp = snprintf (next, size, "\n");
+		size -= temp;
+		next += temp;
+	}
+	spin_unlock_irqrestore (&ehci->lock, flags);
+	kfree (seen);
+
+	return PAGE_SIZE - size;
+}
+static DEVICE_ATTR (periodic, S_IRUGO, show_periodic, NULL);
+
+#undef DBG_SCHED_LIMIT
+
+static ssize_t
+show_registers (struct device *dev, char *buf)
+{
+	struct pci_dev		*pdev;
+	struct ehci_hcd		*ehci;
+	unsigned long		flags;
+	unsigned		temp, size, i;
+	char			*next, scratch [80];
+	static char		fmt [] = "%*s\n";
+	static char		label [] = "";
+
+	pdev = container_of (dev, struct pci_dev, dev);
+	ehci = container_of (pci_get_drvdata (pdev), struct ehci_hcd, hcd);
+
+	next = buf;
+	size = PAGE_SIZE;
+
+	spin_lock_irqsave (&ehci->lock, flags);
+
+	/* Capability Registers */
+	i = readw (&ehci->caps->hci_version);
+	temp = snprintf (next, size,
+		"EHCI %x.%02x, hcd state %d (version " DRIVER_VERSION ")\n",
+		i >> 8, i & 0x0ff, ehci->hcd.state);
+	size -= temp;
+	next += temp;
+
+	// FIXME interpret both types of params
+	i = readl (&ehci->caps->hcs_params);
+	temp = snprintf (next, size, "structural params 0x%08x\n", i);
+	size -= temp;
+	next += temp;
+
+	i = readl (&ehci->caps->hcc_params);
+	temp = snprintf (next, size, "capability params 0x%08x\n", i);
+	size -= temp;
+	next += temp;
+
+	/* Operational Registers */
+	temp = dbg_status_buf (scratch, sizeof scratch, label,
+			readl (&ehci->regs->status));
+	temp = snprintf (next, size, fmt, temp, scratch);
+	size -= temp;
+	next += temp;
+
+	temp = dbg_command_buf (scratch, sizeof scratch, label,
+			readl (&ehci->regs->command));
+	temp = snprintf (next, size, fmt, temp, scratch);
+	size -= temp;
+	next += temp;
+
+	temp = dbg_intr_buf (scratch, sizeof scratch, label,
+			readl (&ehci->regs->intr_enable));
+	temp = snprintf (next, size, fmt, temp, scratch);
+	size -= temp;
+	next += temp;
+
+	temp = snprintf (next, size, "uframe %04x\n",
+			readl (&ehci->regs->frame_index));
+	size -= temp;
+	next += temp;
+
+	for (i = 0; i < HCS_N_PORTS (ehci->hcs_params); i++) {
+		temp = dbg_port_buf (scratch, sizeof scratch, label, i,
+				readl (&ehci->regs->port_status [i]));
+		temp = snprintf (next, size, fmt, temp, scratch);
+		size -= temp;
+		next += temp;
+	}
+
+	if (ehci->reclaim) {
+		temp = snprintf (next, size, "reclaim qh %p%s\n",
+				ehci->reclaim,
+				ehci->reclaim_ready ? " ready" : "");
+		size -= temp;
+		next += temp;
+	}
+
+#ifdef EHCI_STATS
+	temp = snprintf (next, size,
+		"irq normal %ld err %ld reclaim %ld (lost %ld)\n",
+		ehci->stats.normal, ehci->stats.error, ehci->stats.reclaim,
+		ehci->stats.lost_iaa);
+	size -= temp;
+	next += temp;
+
+	temp = snprintf (next, size, "complete %ld unlink %ld\n",
+		ehci->stats.complete, ehci->stats.unlink);
+	size -= temp;
+	next += temp;
+#endif
+
+	spin_unlock_irqrestore (&ehci->lock, flags);
+
+	return PAGE_SIZE - size;
+}
+static DEVICE_ATTR (registers, S_IRUGO, show_registers, NULL);
+
+static inline void create_debug_files (struct ehci_hcd *bus)
+{
+	device_create_file (&bus->hcd.pdev->dev, &dev_attr_async);
+	device_create_file (&bus->hcd.pdev->dev, &dev_attr_periodic);
+	device_create_file (&bus->hcd.pdev->dev, &dev_attr_registers);
+}
+
+static inline void remove_debug_files (struct ehci_hcd *bus)
+{
+	device_remove_file (&bus->hcd.pdev->dev, &dev_attr_async);
+	device_remove_file (&bus->hcd.pdev->dev, &dev_attr_periodic);
+	device_remove_file (&bus->hcd.pdev->dev, &dev_attr_registers);
+}
+
+#endif /* STUB_DEBUG_FILES */
+
diff -urN linux-2.4.21-pre5/drivers/usb/host/ehci-hcd.c linux-2.4.21-pre6/drivers/usb/host/ehci-hcd.c
--- linux-2.4.21-pre5/drivers/usb/host/ehci-hcd.c	Wed Dec 31 16:00:00 1969
+++ linux-2.4.21-pre6/drivers/usb/host/ehci-hcd.c	Wed Mar 26 16:11:39 2003
@@ -0,0 +1,1054 @@
+/*
+ * Copyright (c) 2000-2002 by David Brownell
+ * 
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/timer.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/reboot.h>
+
+#ifdef CONFIG_USB_DEBUG
+	#define DEBUG
+#else
+	#undef DEBUG
+#endif
+
+#include <linux/usb.h>
+
+#include <linux/version.h>
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,32)
+#include "../hcd.h"
+#else
+#include "../core/hcd.h"
+#endif
+
+#include <asm/byteorder.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+#include <asm/unaligned.h>
+
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * EHCI hc_driver implementation ... experimental, incomplete.
+ * Based on the final 1.0 register interface specification.
+ *
+ * USB 2.0 shows up in upcoming www.pcmcia.org technology.
+ * First was PCMCIA, like ISA; then CardBus, which is PCI.
+ * Next comes "CardBay", using USB 2.0 signals.
+ *
+ * Contains additional contributions by Brad Hards, Rory Bolt, and others.
+ * Special thanks to Intel and VIA for providing host controllers to
+ * test this driver on, and Cypress (including In-System Design) for
+ * providing early devices for those host controllers to talk to!
+ *
+ * HISTORY:
+ *
+ * 2002-11-29	Correct handling for hw async_next register.
+ * 2002-08-06	Handling for bulk and interrupt transfers is mostly shared;
+ *	only scheduling is different, no arbitrary limitations.
+ * 2002-07-25	Sanity check PCI reads, mostly for better cardbus support,
+ * 	clean up HC run state handshaking.
+ * 2002-05-24	Preliminary FS/LS interrupts, using scheduling shortcuts
+ * 2002-05-11	Clear TT errors for FS/LS ctrl/bulk.  Fill in some other
+ *	missing pieces:  enabling 64bit dma, handoff from BIOS/SMM.
+ * 2002-05-07	Some error path cleanups to report better errors; wmb();
+ *	use non-CVS version id; better iso bandwidth claim.
+ * 2002-04-19	Control/bulk/interrupt submit no longer uses giveback() on
+ *	errors in submit path.  Bugfixes to interrupt scheduling/processing.
+ * 2002-03-05	Initial high-speed ISO support; reduce ITD memory; shift
+ *	more checking to generic hcd framework (db).  Make it work with
+ *	Philips EHCI; reduce PCI traffic; shorten IRQ path (Rory Bolt).
+ * 2002-01-14	Minor cleanup; version synch.
+ * 2002-01-08	Fix roothub handoff of FS/LS to companion controllers.
+ * 2002-01-04	Control/Bulk queuing behaves.
+ *
+ * 2001-12-12	Initial patch version for Linux 2.5.1 kernel.
+ * 2001-June	Works with usb-storage and NEC EHCI on 2.4
+ */
+
+#define DRIVER_VERSION "2003-Jan-22"
+#define DRIVER_AUTHOR "David Brownell"
+#define DRIVER_DESC "USB 2.0 'Enhanced' Host Controller (EHCI) Driver"
+
+static const char	hcd_name [] = "ehci-hcd";
+
+
+// #define EHCI_VERBOSE_DEBUG
+// #define have_split_iso
+
+#ifdef DEBUG
+#define EHCI_STATS
+#endif
+
+#define INTR_AUTOMAGIC		/* urb lifecycle mode, gone in 2.5 */
+
+/* magic numbers that can affect system performance */
+#define	EHCI_TUNE_CERR		3	/* 0-3 qtd retries; 0 == don't stop */
+#define	EHCI_TUNE_RL_HS		4	/* nak throttle; see 4.9 */
+#define	EHCI_TUNE_RL_TT		0
+#define	EHCI_TUNE_MULT_HS	1	/* 1-3 transactions/uframe; 4.10.3 */
+#define	EHCI_TUNE_MULT_TT	1
+#define	EHCI_TUNE_FLS		2	/* (small) 256 frame schedule */
+
+#define EHCI_WATCHDOG_JIFFIES	(HZ/100)	/* arbitrary; ~10 msec */
+#define EHCI_ASYNC_JIFFIES	(HZ/20)		/* async idle timeout */
+
+/* Initial IRQ latency:  lower than default */
+static int log2_irq_thresh = 0;		// 0 to 6
+MODULE_PARM (log2_irq_thresh, "i");
+MODULE_PARM_DESC (log2_irq_thresh, "log2 IRQ latency, 1-64 microframes");
+
+#define	INTR_MASK (STS_IAA | STS_FATAL | STS_ERR | STS_INT)
+
+/*-------------------------------------------------------------------------*/
+
+#include "ehci.h"
+#include "ehci-dbg.c"
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * handshake - spin reading hc until handshake completes or fails
+ * @ptr: address of hc register to be read
+ * @mask: bits to look at in result of read
+ * @done: value of those bits when handshake succeeds
+ * @usec: timeout in microseconds
+ *
+ * Returns negative errno, or zero on success
+ *
+ * Success happens when the "mask" bits have the specified value (hardware
+ * handshake done).  There are two failure modes:  "usec" have passed (major
+ * hardware flakeout), or the register reads as all-ones (hardware removed).
+ *
+ * That last failure should_only happen in cases like physical cardbus eject
+ * before driver shutdown. But it also seems to be caused by bugs in cardbus
+ * bridge shutdown:  shutting down the bridge before the devices using it.
+ */
+static int handshake (u32 *ptr, u32 mask, u32 done, int usec)
+{
+	u32	result;
+
+	do {
+		result = readl (ptr);
+		if (result == ~(u32)0)		/* card removed */
+			return -ENODEV;
+		result &= mask;
+		if (result == done)
+			return 0;
+		udelay (1);
+		usec--;
+	} while (usec > 0);
+	return -ETIMEDOUT;
+}
+
+/*
+ * hc states include: unknown, halted, ready, running
+ * transitional states are messy just now
+ * trying to avoid "running" unless urbs are active
+ * a "ready" hc can be finishing prefetched work
+ */
+
+/* force HC to halt state from unknown (EHCI spec section 2.3) */
+static int ehci_halt (struct ehci_hcd *ehci)
+{
+	u32	temp = readl (&ehci->regs->status);
+
+	if ((temp & STS_HALT) != 0)
+		return 0;
+
+	temp = readl (&ehci->regs->command);
+	temp &= ~CMD_RUN;
+	writel (temp, &ehci->regs->command);
+	return handshake (&ehci->regs->status, STS_HALT, STS_HALT, 16 * 125);
+}
+
+/* reset a non-running (STS_HALT == 1) controller */
+static int ehci_reset (struct ehci_hcd *ehci)
+{
+	u32	command = readl (&ehci->regs->command);
+
+	command |= CMD_RESET;
+	dbg_cmd (ehci, "reset", command);
+	writel (command, &ehci->regs->command);
+	ehci->hcd.state = USB_STATE_HALT;
+	return handshake (&ehci->regs->command, CMD_RESET, 0, 250 * 1000);
+}
+
+/* idle the controller (from running) */
+static void ehci_ready (struct ehci_hcd *ehci)
+{
+	u32	temp;
+
+#ifdef DEBUG
+	if (!HCD_IS_RUNNING (ehci->hcd.state))
+		BUG ();
+#endif
+
+	/* wait for any schedule enables/disables to take effect */
+	temp = 0;
+	if (ehci->async->qh_next.qh)
+		temp = STS_ASS;
+	if (ehci->next_uframe != -1)
+		temp |= STS_PSS;
+	if (handshake (&ehci->regs->status, STS_ASS | STS_PSS,
+				temp, 16 * 125) != 0) {
+		ehci->hcd.state = USB_STATE_HALT;
+		return;
+	}
+
+	/* then disable anything that's still active */
+	temp = readl (&ehci->regs->command);
+	temp &= ~(CMD_ASE | CMD_IAAD | CMD_PSE);
+	writel (temp, &ehci->regs->command);
+
+	/* hardware can take 16 microframes to turn off ... */
+	if (handshake (&ehci->regs->status, STS_ASS | STS_PSS,
+				0, 16 * 125) != 0) {
+		ehci->hcd.state = USB_STATE_HALT;
+		return;
+	}
+	ehci->hcd.state = USB_STATE_READY;
+}
+
+/*-------------------------------------------------------------------------*/
+
+#include "ehci-hub.c"
+#include "ehci-mem.c"
+#include "ehci-q.c"
+#include "ehci-sched.c"
+
+/*-------------------------------------------------------------------------*/
+
+static void ehci_work(struct ehci_hcd *ehci, struct pt_regs *regs);
+
+static void ehci_watchdog (unsigned long param)
+{
+	struct ehci_hcd		*ehci = (struct ehci_hcd *) param;
+	unsigned long		flags;
+
+	spin_lock_irqsave (&ehci->lock, flags);
+
+	/* lost IAA irqs wedge things badly; seen with a vt8235 */
+	if (ehci->reclaim) {
+		u32		status = readl (&ehci->regs->status);
+
+		if (status & STS_IAA) {
+			ehci_vdbg (ehci, "lost IAA\n");
+			COUNT (ehci->stats.lost_iaa);
+			writel (STS_IAA, &ehci->regs->status);
+			ehci->reclaim_ready = 1;
+		}
+	}
+
+	ehci_work (ehci, NULL);
+	if (ehci->reclaim && !timer_pending (&ehci->watchdog))
+		mod_timer (&ehci->watchdog,
+				jiffies + EHCI_WATCHDOG_JIFFIES);
+
+ 	/* stop async processing after it's idled a while */
+	else if (ehci->async_idle) {
+ 		start_unlink_async (ehci, ehci->async);
+ 		ehci->async_idle = 0;
+	}
+	spin_unlock_irqrestore (&ehci->lock, flags);
+}
+
+/* EHCI 0.96 (and later) section 5.1 says how to kick BIOS/SMM/...
+ * off the controller (maybe it can boot from highspeed USB disks).
+ */
+static int bios_handoff (struct ehci_hcd *ehci, int where, u32 cap)
+{
+	if (cap & (1 << 16)) {
+		int msec = 500;
+
+		/* request handoff to OS */
+		cap &= 1 << 24;
+		pci_write_config_dword (ehci->hcd.pdev, where, cap);
+
+		/* and wait a while for it to happen */
+		do {
+			wait_ms (10);
+			msec -= 10;
+			pci_read_config_dword (ehci->hcd.pdev, where, &cap);
+		} while ((cap & (1 << 16)) && msec);
+		if (cap & (1 << 16)) {
+			ehci_err (ehci, "BIOS handoff failed (%d, %04x)\n",
+				where, cap);
+			return 1;
+		} 
+		ehci_dbg (ehci, "BIOS handoff succeeded\n");
+	}
+	return 0;
+}
+
+static int
+ehci_reboot (struct notifier_block *self, unsigned long code, void *null)
+{
+	struct ehci_hcd		*ehci;
+
+	ehci = container_of (self, struct ehci_hcd, reboot_notifier);
+
+	/* make BIOS/etc use companion controller during reboot */
+	writel (0, &ehci->regs->configured_flag);
+	return 0;
+}
+
+
+/* called by khubd or root hub init threads */
+
+static int ehci_start (struct usb_hcd *hcd)
+{
+	struct ehci_hcd		*ehci = hcd_to_ehci (hcd);
+	u32			temp;
+	struct usb_device	*udev;
+	struct usb_bus		*bus;
+	int			retval;
+	u32			hcc_params;
+	u8                      tempbyte;
+
+	spin_lock_init (&ehci->lock);
+
+	ehci->caps = (struct ehci_caps *) hcd->regs;
+	ehci->regs = (struct ehci_regs *) (hcd->regs + ehci->caps->length);
+	dbg_hcs_params (ehci, "ehci_start");
+	dbg_hcc_params (ehci, "ehci_start");
+
+	hcc_params = readl (&ehci->caps->hcc_params);
+
+	/* EHCI 0.96 and later may have "extended capabilities" */
+	temp = HCC_EXT_CAPS (hcc_params);
+	while (temp) {
+		u32		cap;
+
+		pci_read_config_dword (ehci->hcd.pdev, temp, &cap);
+		ehci_dbg (ehci, "capability %04x at %02x\n", cap, temp);
+		switch (cap & 0xff) {
+		case 1:			/* BIOS/SMM/... handoff */
+			if (bios_handoff (ehci, temp, cap) != 0)
+				return -EOPNOTSUPP;
+			break;
+		case 0:			/* illegal reserved capability */
+			ehci_warn (ehci, "illegal capability!\n");
+			cap = 0;
+			/* FALLTHROUGH */
+		default:		/* unknown */
+			break;
+		}
+		temp = (cap >> 8) & 0xff;
+	}
+
+	/* cache this readonly data; minimize PCI reads */
+	ehci->hcs_params = readl (&ehci->caps->hcs_params);
+
+	/* force HC to halt state */
+	if ((retval = ehci_halt (ehci)) != 0)
+		return retval;
+
+	/*
+	 * hw default: 1K periodic list heads, one per frame.
+	 * periodic_size can shrink by USBCMD update if hcc_params allows.
+	 */
+	ehci->periodic_size = DEFAULT_I_TDPS;
+	if ((retval = ehci_mem_init (ehci, SLAB_KERNEL)) < 0)
+		return retval;
+
+	/* controllers may cache some of the periodic schedule ... */
+	if (HCC_ISOC_CACHE (hcc_params)) 	// full frame cache
+		ehci->i_thresh = 8;
+	else					// N microframes cached
+		ehci->i_thresh = 2 + HCC_ISOC_THRES (hcc_params);
+
+	ehci->reclaim = 0;
+	ehci->next_uframe = -1;
+
+	/* controller state:  unknown --> reset */
+
+	/* EHCI spec section 4.1 */
+	if ((retval = ehci_reset (ehci)) != 0) {
+		ehci_mem_cleanup (ehci);
+		return retval;
+	}
+	writel (INTR_MASK, &ehci->regs->intr_enable);
+	writel (ehci->periodic_dma, &ehci->regs->frame_list);
+
+	/*
+	 * dedicate a qh for the async ring head, since we couldn't unlink
+	 * a 'real' qh without stopping the async schedule [4.8].  use it
+	 * as the 'reclamation list head' too.
+	 * its dummy is used in hw_alt_next of many tds, to prevent the qh
+	 * from automatically advancing to the next td after short reads.
+	 */
+	ehci->async->qh_next.qh = 0;
+	ehci->async->hw_next = QH_NEXT (ehci->async->qh_dma);
+	ehci->async->hw_info1 = cpu_to_le32 (QH_HEAD);
+	ehci->async->hw_token = cpu_to_le32 (QTD_STS_HALT);
+	ehci->async->hw_qtd_next = EHCI_LIST_END;
+	ehci->async->qh_state = QH_STATE_LINKED;
+	ehci->async->hw_alt_next = QTD_NEXT (ehci->async->dummy->qtd_dma);
+	writel ((u32)ehci->async->qh_dma, &ehci->regs->async_next);
+
+	/*
+	 * hcc_params controls whether ehci->regs->segment must (!!!)
+	 * be used; it constrains QH/ITD/SITD and QTD locations.
+	 * pci_pool consistent memory always uses segment zero.
+	 * streaming mappings for I/O buffers, like pci_map_single(),
+	 * can return segments above 4GB, if the device allows.
+	 *
+	 * NOTE:  the dma mask is visible through dma_supported(), so
+	 * drivers can pass this info along ... like NETIF_F_HIGHDMA,
+	 * Scsi_Host.highmem_io, and so forth.  It's readonly to all
+	 * host side drivers though.
+	 */
+	if (HCC_64BIT_ADDR (hcc_params)) {
+		writel (0, &ehci->regs->segment);
+		if (!pci_set_dma_mask (ehci->hcd.pdev, 0xffffffffffffffffULL))
+			ehci_info (ehci, "enabled 64bit PCI DMA\n");
+	}
+
+	/* help hc dma work well with cachelines */
+	pci_set_mwi (ehci->hcd.pdev);
+
+	/* clear interrupt enables, set irq latency */
+	temp = readl (&ehci->regs->command) & 0xff;
+	if (log2_irq_thresh < 0 || log2_irq_thresh > 6)
+		log2_irq_thresh = 0;
+	temp |= 1 << (16 + log2_irq_thresh);
+	// if hc can park (ehci >= 0.96), default is 3 packets per async QH 
+	if (HCC_PGM_FRAMELISTLEN (hcc_params)) {
+		/* periodic schedule size can be smaller than default */
+		temp &= ~(3 << 2);
+		temp |= (EHCI_TUNE_FLS << 2);
+		switch (EHCI_TUNE_FLS) {
+		case 0: ehci->periodic_size = 1024; break;
+		case 1: ehci->periodic_size = 512; break;
+		case 2: ehci->periodic_size = 256; break;
+		default:	BUG ();
+		}
+	}
+	temp &= ~(CMD_IAAD | CMD_ASE | CMD_PSE),
+	// Philips, Intel, and maybe others need CMD_RUN before the
+	// root hub will detect new devices (why?); NEC doesn't
+	temp |= CMD_RUN;
+	writel (temp, &ehci->regs->command);
+	dbg_cmd (ehci, "init", temp);
+
+	/* set async sleep time = 10 us ... ? */
+
+	init_timer (&ehci->watchdog);
+	ehci->watchdog.function = ehci_watchdog;
+	ehci->watchdog.data = (unsigned long) ehci;
+
+	/* wire up the root hub */
+	bus = hcd_to_bus (hcd);
+	bus->root_hub = udev = usb_alloc_dev (NULL, bus);
+	if (!udev) {
+done2:
+		ehci_mem_cleanup (ehci);
+		return -ENOMEM;
+	}
+
+	/*
+	 * Start, enabling full USB 2.0 functionality ... usb 1.1 devices
+	 * are explicitly handed to companion controller(s), so no TT is
+	 * involved with the root hub.
+	 */
+	ehci->reboot_notifier.notifier_call = ehci_reboot;
+	register_reboot_notifier (&ehci->reboot_notifier);
+
+	ehci->hcd.state = USB_STATE_READY;
+	writel (FLAG_CF, &ehci->regs->configured_flag);
+	readl (&ehci->regs->command);	/* unblock posted write */
+
+        /* PCI Serial Bus Release Number is at 0x60 offset */
+	pci_read_config_byte (hcd->pdev, 0x60, &tempbyte);
+	temp = readw (&ehci->caps->hci_version);
+	ehci_info (ehci,
+		"USB %x.%x enabled, EHCI %x.%02x, driver %s\n",
+		((tempbyte & 0xf0)>>4), (tempbyte & 0x0f),
+		temp >> 8, temp & 0xff, DRIVER_VERSION);
+
+	/*
+	 * From here on, khubd concurrently accesses the root
+	 * hub; drivers will be talking to enumerated devices.
+	 *
+	 * Before this point the HC was idle/ready.  After, khubd
+	 * and device drivers may start it running.
+	 */
+	usb_connect (udev);
+	udev->speed = USB_SPEED_HIGH;
+	if (hcd_register_root (hcd) != 0) {
+		if (hcd->state == USB_STATE_RUNNING)
+			ehci_ready (ehci);
+		ehci_reset (ehci);
+		bus->root_hub = 0;
+		usb_free_dev (udev); 
+		retval = -ENODEV;
+		goto done2;
+	}
+
+	create_debug_files (ehci);
+
+	return 0;
+}
+
+/* always called by thread; normally rmmod */
+
+static void ehci_stop (struct usb_hcd *hcd)
+{
+	struct ehci_hcd		*ehci = hcd_to_ehci (hcd);
+
+	ehci_dbg (ehci, "stop\n");
+
+	/* no more interrupts ... */
+	if (hcd->state == USB_STATE_RUNNING)
+		ehci_ready (ehci);
+	if (in_interrupt ()) {		/* must not happen!! */
+		ehci_err (ehci, "stopped in_interrupt!\n");
+		return;
+	}
+	del_timer_sync (&ehci->watchdog);
+	ehci_reset (ehci);
+
+	/* let companion controllers work when we aren't */
+	writel (0, &ehci->regs->configured_flag);
+	unregister_reboot_notifier (&ehci->reboot_notifier);
+
+	remove_debug_files (ehci);
+
+	/* root hub is shut down separately (first, when possible) */
+	spin_lock_irq (&ehci->lock);
+	ehci_work (ehci, NULL);
+	spin_unlock_irq (&ehci->lock);
+	ehci_mem_cleanup (ehci);
+
+#ifdef	EHCI_STATS
+	ehci_dbg (ehci, "irq normal %ld err %ld reclaim %ld (lost %ld)\n",
+		ehci->stats.normal, ehci->stats.error, ehci->stats.reclaim,
+		ehci->stats.lost_iaa);
+	ehci_dbg (ehci, "complete %ld unlink %ld\n",
+		ehci->stats.complete, ehci->stats.unlink);
+#endif
+
+	dbg_status (ehci, "ehci_stop completed", readl (&ehci->regs->status));
+}
+
+static int ehci_get_frame (struct usb_hcd *hcd)
+{
+	struct ehci_hcd		*ehci = hcd_to_ehci (hcd);
+	return (readl (&ehci->regs->frame_index) >> 3) % ehci->periodic_size;
+}
+
+/*-------------------------------------------------------------------------*/
+
+#ifdef	CONFIG_PM
+
+/* suspend/resume, section 4.3 */
+
+static int ehci_suspend (struct usb_hcd *hcd, u32 state)
+{
+	struct ehci_hcd		*ehci = hcd_to_ehci (hcd);
+	int			ports;
+	int			i;
+
+	dbg ("%s: suspend to %d", hcd_to_bus (hcd)->bus_name, state);
+
+	ports = HCS_N_PORTS (ehci->hcs_params);
+
+	// FIXME:  This assumes what's probably a D3 level suspend...
+
+	// FIXME:  usb wakeup events on this bus should resume the machine.
+	// pci config register PORTWAKECAP controls which ports can do it;
+	// bios may have initted the register...
+
+	/* suspend each port, then stop the hc */
+	for (i = 0; i < ports; i++) {
+		int	temp = readl (&ehci->regs->port_status [i]);
+
+		if ((temp & PORT_PE) == 0
+				|| (temp & PORT_OWNER) != 0)
+			continue;
+dbg ("%s: suspend port %d", hcd_to_bus (hcd)->bus_name, i);
+		temp |= PORT_SUSPEND;
+		writel (temp, &ehci->regs->port_status [i]);
+	}
+
+	if (hcd->state == USB_STATE_RUNNING)
+		ehci_ready (ehci);
+	writel (readl (&ehci->regs->command) & ~CMD_RUN, &ehci->regs->command);
+
+// save pci FLADJ value
+
+	/* who tells PCI to reduce power consumption? */
+
+	return 0;
+}
+
+static int ehci_resume (struct usb_hcd *hcd)
+{
+	struct ehci_hcd		*ehci = hcd_to_ehci (hcd);
+	int			ports;
+	int			i;
+
+	dbg ("%s: resume", hcd_to_bus (hcd)->bus_name);
+
+	ports = HCS_N_PORTS (ehci->hcs_params);
+
+	// FIXME:  if controller didn't retain state,
+	// return and let generic code clean it up
+	// test configured_flag ?
+
+	/* resume HC and each port */
+// restore pci FLADJ value
+	// khubd and drivers will set HC running, if needed;
+	hcd->state = USB_STATE_READY;
+	// FIXME Philips/Intel/... etc don't really have a "READY"
+	// state ... turn on CMD_RUN too
+	for (i = 0; i < ports; i++) {
+		int	temp = readl (&ehci->regs->port_status [i]);
+
+		if ((temp & PORT_PE) == 0
+				|| (temp & PORT_SUSPEND) != 0)
+			continue;
+dbg ("%s: resume port %d", hcd_to_bus (hcd)->bus_name, i);
+		temp |= PORT_RESUME;
+		writel (temp, &ehci->regs->port_status [i]);
+		readl (&ehci->regs->command);	/* unblock posted writes */
+
+		wait_ms (20);
+		temp &= ~PORT_RESUME;
+		writel (temp, &ehci->regs->port_status [i]);
+	}
+	readl (&ehci->regs->command);	/* unblock posted writes */
+	return 0;
+}
+
+#endif
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * ehci_work is called from some interrupts, timers, and so on.
+ * it calls driver completion functions, after dropping ehci->lock.
+ */
+static void ehci_work (struct ehci_hcd *ehci, struct pt_regs *regs)
+{
+	if (ehci->reclaim_ready)
+		end_unlink_async (ehci, regs);
+	scan_async (ehci, regs);
+	if (ehci->next_uframe != -1)
+		scan_periodic (ehci, regs);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void ehci_irq (struct usb_hcd *hcd, struct pt_regs *regs)
+{
+	struct ehci_hcd		*ehci = hcd_to_ehci (hcd);
+	u32			status;
+	int			bh;
+
+	spin_lock (&ehci->lock);
+
+	status = readl (&ehci->regs->status);
+
+	/* e.g. cardbus physical eject */
+	if (status == ~(u32) 0) {
+		ehci_dbg (ehci, "device removed\n");
+		goto dead;
+	}
+
+	status &= INTR_MASK;
+	if (!status)			/* irq sharing? */
+		goto done;
+
+	/* clear (just) interrupts */
+	writel (status, &ehci->regs->status);
+	readl (&ehci->regs->command);	/* unblock posted write */
+	bh = 0;
+
+#ifdef	EHCI_VERBOSE_DEBUG
+	/* unrequested/ignored: Port Change Detect, Frame List Rollover */
+	dbg_status (ehci, "irq", status);
+#endif
+
+	/* INT, ERR, and IAA interrupt rates can be throttled */
+
+	/* normal [4.15.1.2] or error [4.15.1.1] completion */
+	if (likely ((status & (STS_INT|STS_ERR)) != 0)) {
+		if (likely ((status & STS_ERR) == 0))
+			COUNT (ehci->stats.normal);
+		else
+			COUNT (ehci->stats.error);
+		bh = 1;
+	}
+
+	/* complete the unlinking of some qh [4.15.2.3] */
+	if (status & STS_IAA) {
+		COUNT (ehci->stats.reclaim);
+		ehci->reclaim_ready = 1;
+		bh = 1;
+	}
+
+	/* PCI errors [4.15.2.4] */
+	if (unlikely ((status & STS_FATAL) != 0)) {
+		ehci_err (ehci, "fatal error\n");
+dead:
+		ehci_reset (ehci);
+		/* generic layer kills/unlinks all urbs, then
+		 * uses ehci_stop to clean up the rest
+		 */
+		bh = 1;
+	}
+
+	if (bh)
+		ehci_work (ehci, regs);
+done:
+	spin_unlock (&ehci->lock);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * non-error returns are a promise to giveback() the urb later
+ * we drop ownership so next owner (or urb unlink) can get it
+ *
+ * urb + dev is in hcd_dev.urb_list
+ * we're queueing TDs onto software and hardware lists
+ *
+ * hcd-specific init for hcpriv hasn't been done yet
+ *
+ * NOTE:  control, bulk, and interrupt share the same code to append TDs
+ * to a (possibly active) QH, and the same QH scanning code.
+ */
+static int ehci_urb_enqueue (
+	struct usb_hcd	*hcd,
+	struct urb	*urb,
+	int		mem_flags
+) {
+	struct ehci_hcd		*ehci = hcd_to_ehci (hcd);
+	struct list_head	qtd_list;
+
+	urb->transfer_flags &= ~EHCI_STATE_UNLINK;
+	INIT_LIST_HEAD (&qtd_list);
+
+	switch (usb_pipetype (urb->pipe)) {
+	// case PIPE_CONTROL:
+	// case PIPE_BULK:
+	default:
+		if (!qh_urb_transaction (ehci, urb, &qtd_list, mem_flags))
+			return -ENOMEM;
+		return submit_async (ehci, urb, &qtd_list, mem_flags);
+
+	case PIPE_INTERRUPT:
+		if (!qh_urb_transaction (ehci, urb, &qtd_list, mem_flags))
+			return -ENOMEM;
+		return intr_submit (ehci, urb, &qtd_list, mem_flags);
+
+	case PIPE_ISOCHRONOUS:
+		if (urb->dev->speed == USB_SPEED_HIGH)
+			return itd_submit (ehci, urb, mem_flags);
+#ifdef have_split_iso
+		else
+			return sitd_submit (ehci, urb, mem_flags);
+#else
+		dbg ("no split iso support yet");
+		return -ENOSYS;
+#endif /* have_split_iso */
+	}
+}
+
+/* remove from hardware lists
+ * completions normally happen asynchronously
+ */
+
+static int ehci_urb_dequeue (struct usb_hcd *hcd, struct urb *urb)
+{
+	struct ehci_hcd		*ehci = hcd_to_ehci (hcd);
+	struct ehci_qh		*qh;
+	unsigned long		flags;
+
+	spin_lock_irqsave (&ehci->lock, flags);
+	switch (usb_pipetype (urb->pipe)) {
+	// case PIPE_CONTROL:
+	// case PIPE_BULK:
+	default:
+		qh = (struct ehci_qh *) urb->hcpriv;
+		if (!qh)
+			break;
+
+		/* if we need to use IAA and it's busy, defer */
+		if (qh->qh_state == QH_STATE_LINKED
+				&& ehci->reclaim
+				&& HCD_IS_RUNNING (ehci->hcd.state)
+				) {
+			struct ehci_qh		*last;
+
+			for (last = ehci->reclaim;
+					last->reclaim;
+					last = last->reclaim)
+				continue;
+			qh->qh_state = QH_STATE_UNLINK_WAIT;
+			last->reclaim = qh;
+
+		/* bypass IAA if the hc can't care */
+		} else if (!HCD_IS_RUNNING (ehci->hcd.state) && ehci->reclaim)
+			end_unlink_async (ehci, NULL);
+
+		/* something else might have unlinked the qh by now */
+		if (qh->qh_state == QH_STATE_LINKED)
+			start_unlink_async (ehci, qh);
+		break;
+
+	case PIPE_INTERRUPT:
+		qh = (struct ehci_qh *) urb->hcpriv;
+		if (!qh)
+			break;
+		if (qh->qh_state == QH_STATE_LINKED) {
+			/* messy, can spin or block a microframe ... */
+			intr_deschedule (ehci, qh, 1);
+			/* qh_state == IDLE */
+		}
+		qh_completions (ehci, qh, NULL);
+
+		/* reschedule QH iff another request is queued */
+		if (!list_empty (&qh->qtd_list)
+				&& HCD_IS_RUNNING (ehci->hcd.state)) {
+			int status;
+
+			status = qh_schedule (ehci, qh);
+			spin_unlock_irqrestore (&ehci->lock, flags);
+
+			if (status != 0) {
+				// shouldn't happen often, but ...
+				// FIXME kill those tds' urbs
+				err ("can't reschedule qh %p, err %d",
+					qh, status);
+			}
+			return status;
+		}
+		break;
+
+	case PIPE_ISOCHRONOUS:
+		// itd or sitd ...
+
+		// wait till next completion, do it then.
+		// completion irqs can wait up to 1024 msec,
+		urb->transfer_flags |= EHCI_STATE_UNLINK;
+		break;
+	}
+	spin_unlock_irqrestore (&ehci->lock, flags);
+	return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+// bulk qh holds the data toggle
+
+static void ehci_free_config (struct usb_hcd *hcd, struct usb_device *udev)
+{
+	struct hcd_dev		*dev = (struct hcd_dev *)udev->hcpriv;
+	struct ehci_hcd		*ehci = hcd_to_ehci (hcd);
+	int			i;
+	unsigned long		flags;
+
+	/* ASSERT:  no requests/urbs are still linked (so no TDs) */
+	/* ASSERT:  nobody can be submitting urbs for this any more */
+
+	dbg ("%s: free_config devnum %d",
+		hcd_to_bus (hcd)->bus_name, udev->devnum);
+
+	spin_lock_irqsave (&ehci->lock, flags);
+	for (i = 0; i < 32; i++) {
+		if (dev->ep [i]) {
+			struct ehci_qh		*qh;
+			char			*why;
+
+			/* dev->ep never has ITDs or SITDs */
+			qh = (struct ehci_qh *) dev->ep [i];
+
+			/* detect/report non-recoverable errors */
+			if (in_interrupt ()) 
+				why = "disconnect() didn't";
+			else if ((qh->hw_info2 & cpu_to_le32 (0xffff)) != 0
+					&& qh->qh_state != QH_STATE_IDLE)
+				why = "(active periodic)";
+			else
+				why = 0;
+			if (why) {
+				err ("dev %s-%s ep %d-%s error: %s",
+					hcd_to_bus (hcd)->bus_name,
+					udev->devpath,
+					i & 0xf, (i & 0x10) ? "IN" : "OUT",
+					why);
+				BUG ();
+			}
+
+			dev->ep [i] = 0;
+			if (qh->qh_state == QH_STATE_IDLE)
+				goto idle;
+			dbg ("free_config, async ep 0x%02x qh %p", i, qh);
+
+			/* scan_async() empties the ring as it does its work,
+			 * using IAA, but doesn't (yet?) turn it off.  if it
+			 * doesn't empty this qh, likely it's the last entry.
+			 */
+			while (qh->qh_state == QH_STATE_LINKED
+					&& ehci->reclaim
+					&& HCD_IS_RUNNING (ehci->hcd.state)
+					) {
+				spin_unlock_irqrestore (&ehci->lock, flags);
+				/* wait_ms() won't spin, we're a thread;
+				 * and we know IRQ/timer/... can progress
+				 */
+				wait_ms (1);
+				spin_lock_irqsave (&ehci->lock, flags);
+			}
+			if (qh->qh_state == QH_STATE_LINKED)
+				start_unlink_async (ehci, qh);
+			while (qh->qh_state != QH_STATE_IDLE
+					&& ehci->hcd.state != USB_STATE_HALT) {
+				spin_unlock_irqrestore (&ehci->lock, flags);
+				wait_ms (1);
+				spin_lock_irqsave (&ehci->lock, flags);
+			}
+idle:
+			qh_put (ehci, qh);
+		}
+	}
+
+	spin_unlock_irqrestore (&ehci->lock, flags);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static const struct hc_driver ehci_driver = {
+	.description =		hcd_name,
+
+	/*
+	 * generic hardware linkage
+	 */
+	.irq =			ehci_irq,
+	.flags =		HCD_MEMORY | HCD_USB2,
+
+	/*
+	 * basic lifecycle operations
+	 */
+	.start =		ehci_start,
+#ifdef	CONFIG_PM
+	.suspend =		ehci_suspend,
+	.resume =		ehci_resume,
+#endif
+	.stop =			ehci_stop,
+
+	/*
+	 * memory lifecycle (except per-request)
+	 */
+	.hcd_alloc =		ehci_hcd_alloc,
+	.hcd_free =		ehci_hcd_free,
+
+	/*
+	 * managing i/o requests and associated device resources
+	 */
+	.urb_enqueue =		ehci_urb_enqueue,
+	.urb_dequeue =		ehci_urb_dequeue,
+	.free_config =		ehci_free_config,
+
+	/*
+	 * scheduling support
+	 */
+	.get_frame_number =	ehci_get_frame,
+
+	/*
+	 * root hub support
+	 */
+	.hub_status_data =	ehci_hub_status_data,
+	.hub_control =		ehci_hub_control,
+};
+
+/*-------------------------------------------------------------------------*/
+
+/* EHCI spec says PCI is required. */
+
+/* PCI driver selection metadata; PCI hotplugging uses this */
+static const struct pci_device_id __devinitdata pci_ids [] = { {
+
+	/* handle any USB 2.0 EHCI controller */
+
+	.class = 		((PCI_CLASS_SERIAL_USB << 8) | 0x20),
+	.class_mask = 	~0,
+	.driver_data =	(unsigned long) &ehci_driver,
+
+	/* no matter who makes it */
+	.vendor =	PCI_ANY_ID,
+	.device =	PCI_ANY_ID,
+	.subvendor =	PCI_ANY_ID,
+	.subdevice =	PCI_ANY_ID,
+
+}, { /* end: all zeroes */ }
+};
+MODULE_DEVICE_TABLE (pci, pci_ids);
+
+/* pci driver glue; this is a "new style" PCI driver module */
+static struct pci_driver ehci_pci_driver = {
+	.name =		(char *) hcd_name,
+	.id_table =	pci_ids,
+
+	.probe =	usb_hcd_pci_probe,
+	.remove =	usb_hcd_pci_remove,
+
+#ifdef	CONFIG_PM
+	.suspend =	usb_hcd_pci_suspend,
+	.resume =	usb_hcd_pci_resume,
+#endif
+};
+
+#define DRIVER_INFO DRIVER_VERSION " " DRIVER_DESC
+
+MODULE_DESCRIPTION (DRIVER_INFO);
+MODULE_AUTHOR (DRIVER_AUTHOR);
+MODULE_LICENSE ("GPL");
+
+static int __init init (void) 
+{
+	dbg (DRIVER_INFO);
+	dbg ("block sizes: qh %Zd qtd %Zd itd %Zd sitd %Zd",
+		sizeof (struct ehci_qh), sizeof (struct ehci_qtd),
+		sizeof (struct ehci_itd), sizeof (struct ehci_sitd));
+
+	return pci_module_init (&ehci_pci_driver);
+}
+module_init (init);
+
+static void __exit cleanup (void) 
+{	
+	pci_unregister_driver (&ehci_pci_driver);
+}
+module_exit (cleanup);
diff -urN linux-2.4.21-pre5/drivers/usb/host/ehci-hub.c linux-2.4.21-pre6/drivers/usb/host/ehci-hub.c
--- linux-2.4.21-pre5/drivers/usb/host/ehci-hub.c	Wed Dec 31 16:00:00 1969
+++ linux-2.4.21-pre6/drivers/usb/host/ehci-hub.c	Wed Mar 26 16:11:39 2003
@@ -0,0 +1,344 @@
+/*
+ * Copyright (c) 2001-2002 by David Brownell
+ * 
+ * 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.
+ *
+ * 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.
+ */
+
+/* this file is part of ehci-hcd.c */
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * EHCI Root Hub ... the nonsharable stuff
+ *
+ * Registers don't need cpu_to_le32, that happens transparently
+ */
+
+/*-------------------------------------------------------------------------*/
+
+static int check_reset_complete (
+	struct ehci_hcd	*ehci,
+	int		index,
+	int		port_status
+) {
+	if (!(port_status & PORT_CONNECT)) {
+		ehci->reset_done [index] = 0;
+		return port_status;
+	}
+
+	/* if reset finished and it's still not enabled -- handoff */
+	if (!(port_status & PORT_PE)) {
+		ehci_dbg (ehci, "port %d full speed --> companion\n",
+			index + 1);
+
+		// what happens if HCS_N_CC(params) == 0 ?
+		port_status |= PORT_OWNER;
+		writel (port_status, &ehci->regs->port_status [index]);
+
+	} else
+		ehci_dbg (ehci, "port %d high speed\n", index + 1);
+
+	return port_status;
+}
+
+/*-------------------------------------------------------------------------*/
+
+
+/* build "status change" packet (one or two bytes) from HC registers */
+
+static int
+ehci_hub_status_data (struct usb_hcd *hcd, char *buf)
+{
+	struct ehci_hcd	*ehci = hcd_to_ehci (hcd);
+	u32		temp, status = 0;
+	int		ports, i, retval = 1;
+	unsigned long	flags;
+
+	/* init status to no-changes */
+	buf [0] = 0;
+	ports = HCS_N_PORTS (ehci->hcs_params);
+	if (ports > 7) {
+		buf [1] = 0;
+		retval++;
+	}
+	
+	/* no hub change reports (bit 0) for now (power, ...) */
+
+	/* port N changes (bit N)? */
+	spin_lock_irqsave (&ehci->lock, flags);
+	for (i = 0; i < ports; i++) {
+		temp = readl (&ehci->regs->port_status [i]);
+		if (temp & PORT_OWNER) {
+			/* don't report this in GetPortStatus */
+			if (temp & PORT_CSC) {
+				temp &= ~PORT_CSC;
+				writel (temp, &ehci->regs->port_status [i]);
+			}
+			continue;
+		}
+		if (!(temp & PORT_CONNECT))
+			ehci->reset_done [i] = 0;
+		if ((temp & (PORT_CSC | PORT_PEC | PORT_OCC)) != 0) {
+			if (i < 7)
+			    buf [0] |= 1 << (i + 1);
+			else
+			    buf [1] |= 1 << (i - 7);
+			status = STS_PCD;
+		}
+	}
+	spin_unlock_irqrestore (&ehci->lock, flags);
+	return status ? retval : 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void
+ehci_hub_descriptor (
+	struct ehci_hcd			*ehci,
+	struct usb_hub_descriptor	*desc
+) {
+	int		ports = HCS_N_PORTS (ehci->hcs_params);
+	u16		temp;
+
+	desc->bDescriptorType = 0x29;
+	desc->bPwrOn2PwrGood = 0;	/* FIXME: f(system power) */
+	desc->bHubContrCurrent = 0;
+
+	desc->bNbrPorts = ports;
+	temp = 1 + (ports / 8);
+	desc->bDescLength = 7 + 2 * temp;
+
+	/* two bitmaps:  ports removable, and usb 1.0 legacy PortPwrCtrlMask */
+	memset (&desc->bitmap [0], 0, temp);
+	memset (&desc->bitmap [temp], 0xff, temp);
+
+	temp = 0x0008;			/* per-port overcurrent reporting */
+	if (HCS_PPC (ehci->hcs_params))
+		temp |= 0x0001;		/* per-port power control */
+	if (HCS_INDICATOR (ehci->hcs_params))
+		temp |= 0x0080;		/* per-port indicators (LEDs) */
+	desc->wHubCharacteristics = cpu_to_le16 (temp);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int ehci_hub_control (
+	struct usb_hcd	*hcd,
+	u16		typeReq,
+	u16		wValue,
+	u16		wIndex,
+	char		*buf,
+	u16		wLength
+) {
+	struct ehci_hcd	*ehci = hcd_to_ehci (hcd);
+	int		ports = HCS_N_PORTS (ehci->hcs_params);
+	u32		temp, status;
+	unsigned long	flags;
+	int		retval = 0;
+
+	/*
+	 * FIXME:  support SetPortFeatures USB_PORT_FEAT_INDICATOR.
+	 * HCS_INDICATOR may say we can change LEDs to off/amber/green.
+	 * (track current state ourselves) ... blink for diagnostics,
+	 * power, "this is the one", etc.  EHCI spec supports this.
+	 */
+
+	spin_lock_irqsave (&ehci->lock, flags);
+	switch (typeReq) {
+	case ClearHubFeature:
+		switch (wValue) {
+		case C_HUB_LOCAL_POWER:
+		case C_HUB_OVER_CURRENT:
+			/* no hub-wide feature/status flags */
+			break;
+		default:
+			goto error;
+		}
+		break;
+	case ClearPortFeature:
+		if (!wIndex || wIndex > ports)
+			goto error;
+		wIndex--;
+		temp = readl (&ehci->regs->port_status [wIndex]);
+		if (temp & PORT_OWNER)
+			break;
+
+		switch (wValue) {
+		case USB_PORT_FEAT_ENABLE:
+			writel (temp & ~PORT_PE,
+				&ehci->regs->port_status [wIndex]);
+			break;
+		case USB_PORT_FEAT_C_ENABLE:
+			writel (temp | PORT_PEC,
+				&ehci->regs->port_status [wIndex]);
+			break;
+		case USB_PORT_FEAT_SUSPEND:
+		case USB_PORT_FEAT_C_SUSPEND:
+			/* ? */
+			break;
+		case USB_PORT_FEAT_POWER:
+			if (HCS_PPC (ehci->hcs_params))
+				writel (temp & ~PORT_POWER,
+					&ehci->regs->port_status [wIndex]);
+			break;
+		case USB_PORT_FEAT_C_CONNECTION:
+			writel (temp | PORT_CSC,
+				&ehci->regs->port_status [wIndex]);
+			break;
+		case USB_PORT_FEAT_C_OVER_CURRENT:
+			writel (temp | PORT_OCC,
+				&ehci->regs->port_status [wIndex]);
+			break;
+		case USB_PORT_FEAT_C_RESET:
+			/* GetPortStatus clears reset */
+			break;
+		default:
+			goto error;
+		}
+		readl (&ehci->regs->command);	/* unblock posted write */
+		break;
+	case GetHubDescriptor:
+		ehci_hub_descriptor (ehci, (struct usb_hub_descriptor *)
+			buf);
+		break;
+	case GetHubStatus:
+		/* no hub-wide feature/status flags */
+		memset (buf, 0, 4);
+		//cpu_to_le32s ((u32 *) buf);
+		break;
+	case GetPortStatus:
+		if (!wIndex || wIndex > ports)
+			goto error;
+		wIndex--;
+		status = 0;
+		temp = readl (&ehci->regs->port_status [wIndex]);
+
+		// wPortChange bits
+		if (temp & PORT_CSC)
+			status |= 1 << USB_PORT_FEAT_C_CONNECTION;
+		if (temp & PORT_PEC)
+			status |= 1 << USB_PORT_FEAT_C_ENABLE;
+		// USB_PORT_FEAT_C_SUSPEND
+		if (temp & PORT_OCC)
+			status |= 1 << USB_PORT_FEAT_C_OVER_CURRENT;
+
+		/* whoever resets must GetPortStatus to complete it!! */
+		if ((temp & PORT_RESET)
+				&& time_after (jiffies,
+					ehci->reset_done [wIndex])) {
+			status |= 1 << USB_PORT_FEAT_C_RESET;
+
+			/* force reset to complete */
+			writel (temp & ~PORT_RESET,
+					&ehci->regs->port_status [wIndex]);
+			do {
+				temp = readl (
+					&ehci->regs->port_status [wIndex]);
+				udelay (10);
+			} while (temp & PORT_RESET);
+
+			/* see what we found out */
+			temp = check_reset_complete (ehci, wIndex, temp);
+		}
+
+		// don't show wPortStatus if it's owned by a companion hc
+		if (!(temp & PORT_OWNER)) {
+			if (temp & PORT_CONNECT) {
+				status |= 1 << USB_PORT_FEAT_CONNECTION;
+				status |= 1 << USB_PORT_FEAT_HIGHSPEED;
+			}
+			if (temp & PORT_PE)
+				status |= 1 << USB_PORT_FEAT_ENABLE;
+			if (temp & PORT_SUSPEND)
+				status |= 1 << USB_PORT_FEAT_SUSPEND;
+			if (temp & PORT_OC)
+				status |= 1 << USB_PORT_FEAT_OVER_CURRENT;
+			if (temp & PORT_RESET)
+				status |= 1 << USB_PORT_FEAT_RESET;
+			if (temp & PORT_POWER)
+				status |= 1 << USB_PORT_FEAT_POWER;
+		}
+
+#ifndef	EHCI_VERBOSE_DEBUG
+	if (status & ~0xffff)	/* only if wPortChange is interesting */
+#endif
+		dbg_port (ehci, "GetStatus", wIndex + 1, temp);
+		// we "know" this alignment is good, caller used kmalloc()...
+		*((u32 *) buf) = cpu_to_le32 (status);
+		break;
+	case SetHubFeature:
+		switch (wValue) {
+		case C_HUB_LOCAL_POWER:
+		case C_HUB_OVER_CURRENT:
+			/* no hub-wide feature/status flags */
+			break;
+		default:
+			goto error;
+		}
+		break;
+	case SetPortFeature:
+		if (!wIndex || wIndex > ports)
+			goto error;
+		wIndex--;
+		temp = readl (&ehci->regs->port_status [wIndex]);
+		if (temp & PORT_OWNER)
+			break;
+
+		switch (wValue) {
+		case USB_PORT_FEAT_SUSPEND:
+			writel (temp | PORT_SUSPEND,
+				&ehci->regs->port_status [wIndex]);
+			break;
+		case USB_PORT_FEAT_POWER:
+			if (HCS_PPC (ehci->hcs_params))
+				writel (temp | PORT_POWER,
+					&ehci->regs->port_status [wIndex]);
+			break;
+		case USB_PORT_FEAT_RESET:
+			/* line status bits may report this as low speed */
+			if ((temp & (PORT_PE|PORT_CONNECT)) == PORT_CONNECT
+					&& PORT_USB11 (temp)) {
+				ehci_dbg (ehci,
+					"port %d low speed --> companion\n",
+					wIndex + 1);
+				temp |= PORT_OWNER;
+			} else {
+				ehci_vdbg (ehci, "port %d reset\n", wIndex + 1);
+				temp |= PORT_RESET;
+				temp &= ~PORT_PE;
+
+				/*
+				 * caller must wait, then call GetPortStatus
+				 * usb 2.0 spec says 50 ms resets on root
+				 */
+				ehci->reset_done [wIndex] = jiffies
+				    	+ ((50 /* msec */ * HZ) / 1000);
+			}
+			writel (temp, &ehci->regs->port_status [wIndex]);
+			break;
+		default:
+			goto error;
+		}
+		readl (&ehci->regs->command);	/* unblock posted writes */
+		break;
+
+	default:
+error:
+		/* "stall" on error */
+		retval = -EPIPE;
+	}
+	spin_unlock_irqrestore (&ehci->lock, flags);
+	return retval;
+}
diff -urN linux-2.4.21-pre5/drivers/usb/host/ehci-mem.c linux-2.4.21-pre6/drivers/usb/host/ehci-mem.c
--- linux-2.4.21-pre5/drivers/usb/host/ehci-mem.c	Wed Dec 31 16:00:00 1969
+++ linux-2.4.21-pre6/drivers/usb/host/ehci-mem.c	Wed Mar 26 16:11:39 2003
@@ -0,0 +1,251 @@
+/*
+ * Copyright (c) 2001 by David Brownell
+ * 
+ * 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.
+ *
+ * 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.
+ */
+
+/* this file is part of ehci-hcd.c */
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * There's basically three types of memory:
+ *	- data used only by the HCD ... kmalloc is fine
+ *	- async and periodic schedules, shared by HC and HCD ... these
+ *	  need to use pci_pool or pci_alloc_consistent
+ *	- driver buffers, read/written by HC ... single shot DMA mapped 
+ *
+ * There's also PCI "register" data, which is memory mapped.
+ * No memory seen by this driver is pagable.
+ */
+
+/*-------------------------------------------------------------------------*/
+/* 
+ * Allocator / cleanup for the per device structure
+ * Called by hcd init / removal code
+ */
+static struct usb_hcd *ehci_hcd_alloc (void)
+{
+	struct ehci_hcd *ehci;
+
+	ehci = (struct ehci_hcd *)
+		kmalloc (sizeof (struct ehci_hcd), GFP_KERNEL);
+	if (ehci != 0) {
+		memset (ehci, 0, sizeof (struct ehci_hcd));
+		return &ehci->hcd;
+	}
+	return 0;
+}
+
+static void ehci_hcd_free (struct usb_hcd *hcd)
+{
+	kfree (hcd_to_ehci (hcd));
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* Allocate the key transfer structures from the previously allocated pool */
+
+static inline void ehci_qtd_init (struct ehci_qtd *qtd, dma_addr_t dma)
+{
+	memset (qtd, 0, sizeof *qtd);
+	qtd->qtd_dma = dma;
+	qtd->hw_next = EHCI_LIST_END;
+	qtd->hw_alt_next = EHCI_LIST_END;
+	INIT_LIST_HEAD (&qtd->qtd_list);
+}
+
+static struct ehci_qtd *ehci_qtd_alloc (struct ehci_hcd *ehci, int flags)
+{
+	struct ehci_qtd		*qtd;
+	dma_addr_t		dma;
+
+	qtd = pci_pool_alloc (ehci->qtd_pool, flags, &dma);
+	if (qtd != 0) {
+		ehci_qtd_init (qtd, dma);
+	}
+	return qtd;
+}
+
+static inline void ehci_qtd_free (struct ehci_hcd *ehci, struct ehci_qtd *qtd)
+{
+	pci_pool_free (ehci->qtd_pool, qtd, qtd->qtd_dma);
+}
+
+
+static struct ehci_qh *ehci_qh_alloc (struct ehci_hcd *ehci, int flags)
+{
+	struct ehci_qh		*qh;
+	dma_addr_t		dma;
+
+	qh = (struct ehci_qh *)
+		pci_pool_alloc (ehci->qh_pool, flags, &dma);
+	if (!qh)
+		return qh;
+
+	memset (qh, 0, sizeof *qh);
+	atomic_set (&qh->refcount, 1);
+	qh->qh_dma = dma;
+	// INIT_LIST_HEAD (&qh->qh_list);
+	INIT_LIST_HEAD (&qh->qtd_list);
+
+	/* dummy td enables safe urb queuing */
+	qh->dummy = ehci_qtd_alloc (ehci, flags);
+	if (qh->dummy == 0) {
+		ehci_dbg (ehci, "no dummy td\n");
+		pci_pool_free (ehci->qh_pool, qh, qh->qh_dma);
+		qh = 0;
+	}
+	return qh;
+}
+
+/* to share a qh (cpu threads, or hc) */
+static inline struct ehci_qh *qh_get (/* ehci, */ struct ehci_qh *qh)
+{
+	atomic_inc (&qh->refcount);
+	return qh;
+}
+
+static void qh_put (struct ehci_hcd *ehci, struct ehci_qh *qh)
+{
+	if (!atomic_dec_and_test (&qh->refcount))
+		return;
+	/* clean qtds first, and know this is not linked */
+	if (!list_empty (&qh->qtd_list) || qh->qh_next.ptr) {
+		ehci_dbg (ehci, "unused qh not empty!\n");
+		BUG ();
+	}
+	if (qh->dummy)
+		ehci_qtd_free (ehci, qh->dummy);
+	pci_pool_free (ehci->qh_pool, qh, qh->qh_dma);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* The queue heads and transfer descriptors are managed from pools tied 
+ * to each of the "per device" structures.
+ * This is the initialisation and cleanup code.
+ */
+
+static void ehci_mem_cleanup (struct ehci_hcd *ehci)
+{
+	if (ehci->async)
+		qh_put (ehci, ehci->async);
+	ehci->async = 0;
+
+	/* PCI consistent memory and pools */
+	if (ehci->qtd_pool)
+		pci_pool_destroy (ehci->qtd_pool);
+	ehci->qtd_pool = 0;
+
+	if (ehci->qh_pool) {
+		pci_pool_destroy (ehci->qh_pool);
+		ehci->qh_pool = 0;
+	}
+
+	if (ehci->itd_pool)
+		pci_pool_destroy (ehci->itd_pool);
+	ehci->itd_pool = 0;
+
+	if (ehci->sitd_pool)
+		pci_pool_destroy (ehci->sitd_pool);
+	ehci->sitd_pool = 0;
+
+	if (ehci->periodic)
+		pci_free_consistent (ehci->hcd.pdev,
+			ehci->periodic_size * sizeof (u32),
+			ehci->periodic, ehci->periodic_dma);
+	ehci->periodic = 0;
+
+	/* shadow periodic table */
+	if (ehci->pshadow)
+		kfree (ehci->pshadow);
+	ehci->pshadow = 0;
+}
+
+/* remember to add cleanup code (above) if you add anything here */
+static int ehci_mem_init (struct ehci_hcd *ehci, int flags)
+{
+	int i;
+
+	/* QTDs for control/bulk/intr transfers */
+	ehci->qtd_pool = pci_pool_create ("ehci_qtd", ehci->hcd.pdev,
+			sizeof (struct ehci_qtd),
+			32 /* byte alignment (for hw parts) */,
+			4096 /* can't cross 4K */,
+			flags);
+	if (!ehci->qtd_pool) {
+		goto fail;
+	}
+
+	/* QHs for control/bulk/intr transfers */
+	ehci->qh_pool = pci_pool_create ("ehci_qh", ehci->hcd.pdev,
+			sizeof (struct ehci_qh),
+			32 /* byte alignment (for hw parts) */,
+			4096 /* can't cross 4K */,
+			flags);
+	if (!ehci->qh_pool) {
+		goto fail;
+	}
+	ehci->async = ehci_qh_alloc (ehci, flags);
+	if (!ehci->async) {
+		goto fail;
+	}
+
+	/* ITD for high speed ISO transfers */
+	ehci->itd_pool = pci_pool_create ("ehci_itd", ehci->hcd.pdev,
+			sizeof (struct ehci_itd),
+			32 /* byte alignment (for hw parts) */,
+			4096 /* can't cross 4K */,
+			flags);
+	if (!ehci->itd_pool) {
+		goto fail;
+	}
+
+	/* SITD for full/low speed split ISO transfers */
+	ehci->sitd_pool = pci_pool_create ("ehci_sitd", ehci->hcd.pdev,
+			sizeof (struct ehci_sitd),
+			32 /* byte alignment (for hw parts) */,
+			4096 /* can't cross 4K */,
+			flags);
+	if (!ehci->sitd_pool) {
+		goto fail;
+	}
+
+	/* Hardware periodic table */
+	ehci->periodic = (u32 *)
+		pci_alloc_consistent (ehci->hcd.pdev,
+			ehci->periodic_size * sizeof (u32),
+			&ehci->periodic_dma);
+	if (ehci->periodic == 0) {
+		goto fail;
+	}
+	for (i = 0; i < ehci->periodic_size; i++)
+		ehci->periodic [i] = EHCI_LIST_END;
+
+	/* software shadow of hardware table */
+	ehci->pshadow = kmalloc (ehci->periodic_size * sizeof (void *), flags);
+	if (ehci->pshadow == 0) {
+		goto fail;
+	}
+	memset (ehci->pshadow, 0, ehci->periodic_size * sizeof (void *));
+
+	return 0;
+
+fail:
+	ehci_dbg (ehci, "couldn't init memory\n");
+	ehci_mem_cleanup (ehci);
+	return -ENOMEM;
+}
diff -urN linux-2.4.21-pre5/drivers/usb/host/ehci-q.c linux-2.4.21-pre6/drivers/usb/host/ehci-q.c
--- linux-2.4.21-pre5/drivers/usb/host/ehci-q.c	Wed Dec 31 16:00:00 1969
+++ linux-2.4.21-pre6/drivers/usb/host/ehci-q.c	Wed Mar 26 16:11:39 2003
@@ -0,0 +1,1105 @@
+/*
+ * Copyright (c) 2001-2002 by David Brownell
+ * 
+ * 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.
+ *
+ * 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.
+ */
+
+/* this file is part of ehci-hcd.c */
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * EHCI hardware queue manipulation ... the core.  QH/QTD manipulation.
+ *
+ * Control, bulk, and interrupt traffic all use "qh" lists.  They list "qtd"
+ * entries describing USB transactions, max 16-20kB/entry (with 4kB-aligned
+ * buffers needed for the larger number).  We use one QH per endpoint, queue
+ * multiple urbs (all three types) per endpoint.  URBs may need several qtds.
+ *
+ * ISO traffic uses "ISO TD" (itd, and sitd) records, and (along with
+ * interrupts) needs careful scheduling.  Performance improvements can be
+ * an ongoing challenge.  That's in "ehci-sched.c".
+ * 
+ * USB 1.1 devices are handled (a) by "companion" OHCI or UHCI root hubs,
+ * or otherwise through transaction translators (TTs) in USB 2.0 hubs using
+ * (b) special fields in qh entries or (c) split iso entries.  TTs will
+ * buffer low/full speed data so the host collects it at high speed.
+ */
+
+/*-------------------------------------------------------------------------*/
+
+/* fill a qtd, returning how much of the buffer we were able to queue up */
+
+static int
+qtd_fill (struct ehci_qtd *qtd, dma_addr_t buf, size_t len,
+		int token, int maxpacket)
+{
+	int	i, count;
+	u64	addr = buf;
+
+	/* one buffer entry per 4K ... first might be short or unaligned */
+	qtd->hw_buf [0] = cpu_to_le32 ((u32)addr);
+	qtd->hw_buf_hi [0] = cpu_to_le32 ((u32)(addr >> 32));
+	count = 0x1000 - (buf & 0x0fff);	/* rest of that page */
+	if (likely (len < count))		/* ... iff needed */
+		count = len;
+	else {
+		buf +=  0x1000;
+		buf &= ~0x0fff;
+
+		/* per-qtd limit: from 16K to 20K (best alignment) */
+		for (i = 1; count < len && i < 5; i++) {
+			addr = buf;
+			qtd->hw_buf [i] = cpu_to_le32 ((u32)addr);
+			qtd->hw_buf_hi [i] = cpu_to_le32 ((u32)(addr >> 32));
+			buf += 0x1000;
+			if ((count + 0x1000) < len)
+				count += 0x1000;
+			else
+				count = len;
+		}
+
+		/* short packets may only terminate transfers */
+		if (count != len)
+			count -= (count % maxpacket);
+	}
+	qtd->hw_token = cpu_to_le32 ((count << 16) | token);
+	qtd->length = count;
+
+	return count;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* update halted (but potentially linked) qh */
+
+static inline void
+qh_update (struct ehci_hcd *ehci, struct ehci_qh *qh, struct ehci_qtd *qtd)
+{
+	qh->hw_current = 0;
+	qh->hw_qtd_next = QTD_NEXT (qtd->qtd_dma);
+	qh->hw_alt_next = EHCI_LIST_END;
+
+	/* HC must see latest qtd and qh data before we clear ACTIVE+HALT */
+	wmb ();
+	qh->hw_token &= __constant_cpu_to_le32 (QTD_TOGGLE | QTD_STS_PING);
+}
+
+/*-------------------------------------------------------------------------*/
+
+#define IS_SHORT_READ(token) (QTD_LENGTH (token) != 0 && QTD_PID (token) == 1)
+
+static void qtd_copy_status (
+	struct ehci_hcd *ehci,
+	struct urb *urb,
+	size_t length,
+	u32 token
+)
+{
+	/* count IN/OUT bytes, not SETUP (even short packets) */
+	if (likely (QTD_PID (token) != 2))
+		urb->actual_length += length - QTD_LENGTH (token);
+
+	/* don't modify error codes */
+	if (unlikely (urb->status != -EINPROGRESS))
+		return;
+
+	/* force cleanup after short read; not always an error */
+	if (unlikely (IS_SHORT_READ (token)))
+		urb->status = -EREMOTEIO;
+
+	/* serious "can't proceed" faults reported by the hardware */
+	if (token & QTD_STS_HALT) {
+		if (token & QTD_STS_BABBLE) {
+			/* FIXME "must" disable babbling device's port too */
+			urb->status = -EOVERFLOW;
+		} else if (token & QTD_STS_MMF) {
+			/* fs/ls interrupt xfer missed the complete-split */
+			urb->status = -EPROTO;
+		} else if (token & QTD_STS_DBE) {
+			urb->status = (QTD_PID (token) == 1) /* IN ? */
+				? -ENOSR  /* hc couldn't read data */
+				: -ECOMM; /* hc couldn't write data */
+		} else if (token & QTD_STS_XACT) {
+			/* timeout, bad crc, wrong PID, etc; retried */
+			if (QTD_CERR (token))
+				urb->status = -EPIPE;
+			else {
+				dbg ("3strikes");
+				urb->status = -EPROTO;
+			}
+		/* CERR nonzero + no errors + halt --> stall */
+		} else if (QTD_CERR (token))
+			urb->status = -EPIPE;
+		else	/* unknown */
+			urb->status = -EPROTO;
+
+		ehci_vdbg (ehci,
+			"dev%d ep%d%s qtd token %08x --> status %d\n",
+			usb_pipedevice (urb->pipe),
+			usb_pipeendpoint (urb->pipe),
+			usb_pipein (urb->pipe) ? "in" : "out",
+			token, urb->status);
+
+		/* stall indicates some recovery action is needed */
+		if (urb->status == -EPIPE) {
+			int	pipe = urb->pipe;
+
+			if (!usb_pipecontrol (pipe))
+				usb_endpoint_halt (urb->dev,
+					usb_pipeendpoint (pipe),
+					usb_pipeout (pipe));
+			if (urb->dev->tt && !usb_pipeint (pipe)) {
+#ifdef DEBUG
+				struct usb_device *tt = urb->dev->tt->hub;
+				dbg ("clear tt %s-%s p%d buffer, a%d ep%d",
+					tt->bus->bus_name, tt->devpath,
+    					urb->dev->ttport, urb->dev->devnum,
+    					usb_pipeendpoint (pipe));
+#endif /* DEBUG */
+				usb_hub_tt_clear_buffer (urb->dev, pipe);
+			}
+		}
+	}
+}
+
+static void
+ehci_urb_done (struct ehci_hcd *ehci, struct urb *urb, struct pt_regs *regs)
+{
+#ifdef	INTR_AUTOMAGIC
+	struct urb		*resubmit = 0;
+	struct usb_device	*dev = 0;
+
+	static int ehci_urb_enqueue (struct usb_hcd *, struct urb *, int);
+#endif
+
+	if (likely (urb->hcpriv != 0)) {
+		struct ehci_qh	*qh = (struct ehci_qh *) urb->hcpriv;
+
+		/* S-mask in a QH means it's an interrupt urb */
+		if ((qh->hw_info2 & cpu_to_le32 (0x00ff)) != 0) {
+
+			/* ... update hc-wide periodic stats (for usbfs) */
+			hcd_to_bus (&ehci->hcd)->bandwidth_int_reqs--;
+
+#ifdef	INTR_AUTOMAGIC
+			if (!((urb->status == -ENOENT)
+					|| (urb->status == -ECONNRESET))) {
+				resubmit = usb_get_urb (urb);
+				dev = urb->dev;
+			}
+#endif
+		}
+		qh_put (ehci, qh);
+	}
+
+	spin_lock (&urb->lock);
+	urb->hcpriv = 0;
+	switch (urb->status) {
+	case -EINPROGRESS:		/* success */
+		urb->status = 0;
+	default:			/* fault */
+		COUNT (ehci->stats.complete);
+		break;
+	case -EREMOTEIO:		/* fault or normal */
+		if (!(urb->transfer_flags & URB_SHORT_NOT_OK))
+			urb->status = 0;
+		COUNT (ehci->stats.complete);
+		break;
+	case -ECONNRESET:		/* canceled */
+	case -ENOENT:
+		COUNT (ehci->stats.unlink);
+		break;
+	}
+	spin_unlock (&urb->lock);
+
+	/* complete() can reenter this HCD */
+	spin_unlock (&ehci->lock);
+	usb_hcd_giveback_urb (&ehci->hcd, urb, regs);
+
+#ifdef	INTR_AUTOMAGIC
+	if (resubmit && ((urb->status == -ENOENT)
+				|| (urb->status == -ECONNRESET))) {
+		usb_put_urb (resubmit);
+		resubmit = 0;
+	}
+	// device drivers will soon be doing something like this
+	if (resubmit) {
+		int	status;
+
+		resubmit->dev = dev;
+		status = SUBMIT_URB (resubmit, SLAB_ATOMIC);
+		if (status != 0)
+			err ("can't resubmit interrupt urb %p: status %d",
+					resubmit, status);
+		usb_put_urb (resubmit);
+	}
+#endif
+
+	spin_lock (&ehci->lock);
+}
+
+
+/*
+ * Process and free completed qtds for a qh, returning URBs to drivers.
+ * Chases up to qh->hw_current.  Returns number of completions called,
+ * indicating how much "real" work we did.
+ */
+#define HALT_BIT cpu_to_le32(QTD_STS_HALT)
+static unsigned
+qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh, struct pt_regs *regs)
+{
+	struct ehci_qtd		*last = 0, *end = qh->dummy;
+	struct list_head	*entry, *tmp;
+	int			stopped;
+	unsigned		count = 0;
+	int			do_status = 0;
+	u8			state;
+
+	if (unlikely (list_empty (&qh->qtd_list)))
+		return count;
+
+	/* completions (or tasks on other cpus) must never clobber HALT
+	 * till we've gone through and cleaned everything up, even when
+	 * they add urbs to this qh's queue or mark them for unlinking.
+	 *
+	 * NOTE:  unlinking expects to be done in queue order.
+	 */
+	state = qh->qh_state;
+	qh->qh_state = QH_STATE_COMPLETING;
+	stopped = (state == QH_STATE_IDLE);
+
+	/* remove de-activated QTDs from front of queue.
+	 * after faults (including short reads), cleanup this urb
+	 * then let the queue advance.
+	 * if queue is stopped, handles unlinks.
+	 */
+	list_for_each_safe (entry, tmp, &qh->qtd_list) {
+		struct ehci_qtd	*qtd;
+		struct urb	*urb;
+		u32		token = 0;
+
+		qtd = list_entry (entry, struct ehci_qtd, qtd_list);
+		urb = qtd->urb;
+
+		/* clean up any state from previous QTD ...*/
+		if (last) {
+			if (likely (last->urb != urb)) {
+				ehci_urb_done (ehci, last->urb, regs);
+				count++;
+			}
+			ehci_qtd_free (ehci, last);
+			last = 0;
+		}
+
+		/* ignore urbs submitted during completions we reported */
+		if (qtd == end)
+			break;
+
+		/* hardware copies qtd out of qh overlay */
+		rmb ();
+		token = le32_to_cpu (qtd->hw_token);
+		stopped = stopped
+			|| (HALT_BIT & qh->hw_token) != 0
+			|| (ehci->hcd.state == USB_STATE_HALT);
+
+		/* always clean up qtds the hc de-activated */
+		if ((token & QTD_STS_ACTIVE) == 0) {
+
+			/* magic dummy for short reads; won't advance */
+			if (IS_SHORT_READ (token)
+					&& !(token & QTD_STS_HALT)
+					&& (qh->hw_alt_next & QTD_MASK)
+						== ehci->async->hw_alt_next) {
+				stopped = 1;
+				goto halt;
+			}
+
+		/* stop scanning when we reach qtds the hc is using */
+		} else if (likely (!stopped)) {
+			break;
+
+		} else {
+			/* ignore active urbs unless some previous qtd
+			 * for the urb faulted (including short read) or
+			 * its urb was canceled.  we may patch qh or qtds.
+			 */
+			if (likely (urb->status == -EINPROGRESS))
+				continue;
+			
+			/* issue status after short control reads */
+			if (unlikely (do_status != 0)
+					&& QTD_PID (token) == 0 /* OUT */) {
+				do_status = 0;
+				continue;
+			}
+
+			/* token in overlay may be most current */
+			if (state == QH_STATE_IDLE
+					&& cpu_to_le32 (qtd->qtd_dma)
+						== qh->hw_current)
+				token = le32_to_cpu (qh->hw_token);
+
+			/* force halt for unlinked or blocked qh, so we'll
+			 * patch the qh later and so that completions can't
+			 * activate it while we "know" it's stopped.
+			 */
+			if ((HALT_BIT & qh->hw_token) == 0) {
+halt:
+				qh->hw_token |= HALT_BIT;
+				wmb ();
+			}
+		}
+ 
+		/* remove it from the queue */
+		spin_lock (&urb->lock);
+		qtd_copy_status (ehci, urb, qtd->length, token);
+		do_status = (urb->status == -EREMOTEIO)
+				&& usb_pipecontrol (urb->pipe);
+		spin_unlock (&urb->lock);
+
+		if (stopped && qtd->qtd_list.prev != &qh->qtd_list) {
+			last = list_entry (qtd->qtd_list.prev,
+					struct ehci_qtd, qtd_list);
+			last->hw_next = qtd->hw_next;
+		}
+		list_del (&qtd->qtd_list);
+		last = qtd;
+	}
+
+	/* last urb's completion might still need calling */
+	if (likely (last != 0)) {
+		ehci_urb_done (ehci, last->urb, regs);
+		count++;
+		ehci_qtd_free (ehci, last);
+	}
+
+	/* restore original state; caller must unlink or relink */
+	qh->qh_state = state;
+
+	/* update qh after fault cleanup */
+	if (unlikely ((HALT_BIT & qh->hw_token) != 0)) {
+		qh_update (ehci, qh,
+			list_empty (&qh->qtd_list)
+				? qh->dummy
+				: list_entry (qh->qtd_list.next,
+					struct ehci_qtd, qtd_list));
+	}
+
+	return count;
+}
+#undef HALT_BIT
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * reverse of qh_urb_transaction:  free a list of TDs.
+ * used for cleanup after errors, before HC sees an URB's TDs.
+ */
+static void qtd_list_free (
+	struct ehci_hcd		*ehci,
+	struct urb		*urb,
+	struct list_head	*qtd_list
+) {
+	struct list_head	*entry, *temp;
+
+	list_for_each_safe (entry, temp, qtd_list) {
+		struct ehci_qtd	*qtd;
+
+		qtd = list_entry (entry, struct ehci_qtd, qtd_list);
+		list_del (&qtd->qtd_list);
+		ehci_qtd_free (ehci, qtd);
+	}
+}
+
+/*
+ * create a list of filled qtds for this URB; won't link into qh.
+ */
+static struct list_head *
+qh_urb_transaction (
+	struct ehci_hcd		*ehci,
+	struct urb		*urb,
+	struct list_head	*head,
+	int			flags
+) {
+	struct ehci_qtd		*qtd, *qtd_prev;
+	dma_addr_t		buf;
+	int			len, maxpacket;
+	int			is_input;
+	u32			token;
+
+	/*
+	 * URBs map to sequences of QTDs:  one logical transaction
+	 */
+	qtd = ehci_qtd_alloc (ehci, flags);
+	if (unlikely (!qtd))
+		return 0;
+	list_add_tail (&qtd->qtd_list, head);
+	qtd->urb = urb;
+
+	token = QTD_STS_ACTIVE;
+	token |= (EHCI_TUNE_CERR << 10);
+	/* for split transactions, SplitXState initialized to zero */
+
+	len = urb->transfer_buffer_length;
+	is_input = usb_pipein (urb->pipe);
+	if (usb_pipecontrol (urb->pipe)) {
+		/* SETUP pid */
+		qtd_fill (qtd, urb->setup_dma, sizeof (struct usb_ctrlrequest),
+			token | (2 /* "setup" */ << 8), 8);
+
+		/* ... and always at least one more pid */
+		token ^= QTD_TOGGLE;
+		qtd_prev = qtd;
+		qtd = ehci_qtd_alloc (ehci, flags);
+		if (unlikely (!qtd))
+			goto cleanup;
+		qtd->urb = urb;
+		qtd_prev->hw_next = QTD_NEXT (qtd->qtd_dma);
+		list_add_tail (&qtd->qtd_list, head);
+	} 
+
+	/*
+	 * data transfer stage:  buffer setup
+	 */
+	if (likely (len > 0))
+		buf = urb->transfer_dma;
+	else
+		buf = 0;
+
+	// FIXME this 'buf' check break some zlps...
+	if (!buf || is_input)
+		token |= (1 /* "in" */ << 8);
+	/* else it's already initted to "out" pid (0 << 8) */
+
+	maxpacket = usb_maxpacket (urb->dev, urb->pipe, !is_input) & 0x03ff;
+
+	/*
+	 * buffer gets wrapped in one or more qtds;
+	 * last one may be "short" (including zero len)
+	 * and may serve as a control status ack
+	 */
+	for (;;) {
+		int this_qtd_len;
+
+		this_qtd_len = qtd_fill (qtd, buf, len, token, maxpacket);
+		len -= this_qtd_len;
+		buf += this_qtd_len;
+		if (is_input)
+			qtd->hw_alt_next = ehci->async->hw_alt_next;
+
+		/* qh makes control packets use qtd toggle; maybe switch it */
+		if ((maxpacket & (this_qtd_len + (maxpacket - 1))) == 0)
+			token ^= QTD_TOGGLE;
+
+		if (likely (len <= 0))
+			break;
+
+		qtd_prev = qtd;
+		qtd = ehci_qtd_alloc (ehci, flags);
+		if (unlikely (!qtd))
+			goto cleanup;
+		qtd->urb = urb;
+		qtd_prev->hw_next = QTD_NEXT (qtd->qtd_dma);
+		list_add_tail (&qtd->qtd_list, head);
+	}
+
+	/* unless the bulk/interrupt caller wants a chance to clean
+	 * up after short reads, hc should advance qh past this urb
+	 */
+	if (likely ((urb->transfer_flags & URB_SHORT_NOT_OK) == 0
+				|| usb_pipecontrol (urb->pipe)))
+		qtd->hw_alt_next = EHCI_LIST_END;
+
+	/*
+	 * control requests may need a terminating data "status" ack;
+	 * bulk ones may need a terminating short packet (zero length).
+	 */
+	if (likely (buf != 0)) {
+		int	one_more = 0;
+
+		if (usb_pipecontrol (urb->pipe)) {
+			one_more = 1;
+			token ^= 0x0100;	/* "in" <--> "out"  */
+			token |= QTD_TOGGLE;	/* force DATA1 */
+		} else if (usb_pipebulk (urb->pipe)
+				&& (urb->transfer_flags & URB_ZERO_PACKET)
+				&& !(urb->transfer_buffer_length % maxpacket)) {
+			one_more = 1;
+		}
+		if (one_more) {
+			qtd_prev = qtd;
+			qtd = ehci_qtd_alloc (ehci, flags);
+			if (unlikely (!qtd))
+				goto cleanup;
+			qtd->urb = urb;
+			qtd_prev->hw_next = QTD_NEXT (qtd->qtd_dma);
+			list_add_tail (&qtd->qtd_list, head);
+
+			/* never any data in such packets */
+			qtd_fill (qtd, 0, 0, token, 0);
+		}
+	}
+
+	/* by default, enable interrupt on urb completion */
+	if (likely (!(urb->transfer_flags & URB_NO_INTERRUPT)))
+		qtd->hw_token |= __constant_cpu_to_le32 (QTD_IOC);
+	return head;
+
+cleanup:
+	qtd_list_free (ehci, urb, head);
+	return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * Hardware maintains data toggle (like OHCI) ... here we (re)initialize
+ * the hardware data toggle in the QH, and set the pseudo-toggle in udev
+ * so we can see if usb_clear_halt() was called.  NOP for control, since
+ * we set up qh->hw_info1 to always use the QTD toggle bits. 
+ */
+static inline void
+clear_toggle (struct usb_device *udev, int ep, int is_out, struct ehci_qh *qh)
+{
+	vdbg ("clear toggle, dev %d ep 0x%x-%s",
+		udev->devnum, ep, is_out ? "out" : "in");
+	qh->hw_token &= ~__constant_cpu_to_le32 (QTD_TOGGLE);
+	usb_settoggle (udev, ep, is_out, 1);
+}
+
+// Would be best to create all qh's from config descriptors,
+// when each interface/altsetting is established.  Unlink
+// any previous qh and cancel its urbs first; endpoints are
+// implicitly reset then (data toggle too).
+// That'd mean updating how usbcore talks to HCDs. (2.5?)
+
+
+// high bandwidth multiplier, as encoded in highspeed endpoint descriptors
+#define hb_mult(wMaxPacketSize) (1 + (((wMaxPacketSize) >> 11) & 0x03))
+// ... and packet size, for any kind of endpoint descriptor
+#define max_packet(wMaxPacketSize) ((wMaxPacketSize) & 0x03ff)
+
+/*
+ * Each QH holds a qtd list; a QH is used for everything except iso.
+ *
+ * For interrupt urbs, the scheduler must set the microframe scheduling
+ * mask(s) each time the QH gets scheduled.  For highspeed, that's
+ * just one microframe in the s-mask.  For split interrupt transactions
+ * there are additional complications: c-mask, maybe FSTNs.
+ */
+static struct ehci_qh *
+qh_make (
+	struct ehci_hcd		*ehci,
+	struct urb		*urb,
+	int			flags
+) {
+	struct ehci_qh		*qh = ehci_qh_alloc (ehci, flags);
+	u32			info1 = 0, info2 = 0;
+	int			is_input, type;
+	int			maxp = 0;
+
+	if (!qh)
+		return qh;
+
+	/*
+	 * init endpoint/device data for this QH
+	 */
+	info1 |= usb_pipeendpoint (urb->pipe) << 8;
+	info1 |= usb_pipedevice (urb->pipe) << 0;
+
+	is_input = usb_pipein (urb->pipe);
+	type = usb_pipetype (urb->pipe);
+	maxp = usb_maxpacket (urb->dev, urb->pipe, !is_input);
+
+	/* Compute interrupt scheduling parameters just once, and save.
+	 * - allowing for high bandwidth, how many nsec/uframe are used?
+	 * - split transactions need a second CSPLIT uframe; same question
+	 * - splits also need a schedule gap (for full/low speed I/O)
+	 * - qh has a polling interval
+	 *
+	 * For control/bulk requests, the HC or TT handles these.
+	 */
+	if (type == PIPE_INTERRUPT) {
+		qh->usecs = usb_calc_bus_time (USB_SPEED_HIGH, is_input, 0,
+				hb_mult (maxp) * max_packet (maxp));
+		qh->start = NO_FRAME;
+
+		if (urb->dev->speed == USB_SPEED_HIGH) {
+			qh->c_usecs = 0;
+			qh->gap_uf = 0;
+
+			/* FIXME handle HS periods of less than 1 frame. */
+			qh->period = urb->interval >> 3;
+			if (qh->period < 1) {
+				dbg ("intr period %d uframes, NYET!",
+						urb->interval);
+				goto done;
+			}
+		} else {
+			/* gap is f(FS/LS transfer times) */
+			qh->gap_uf = 1 + usb_calc_bus_time (urb->dev->speed,
+					is_input, 0, maxp) / (125 * 1000);
+
+			/* FIXME this just approximates SPLIT/CSPLIT times */
+			if (is_input) {		// SPLIT, gap, CSPLIT+DATA
+				qh->c_usecs = qh->usecs + HS_USECS (0);
+				qh->usecs = HS_USECS (1);
+			} else {		// SPLIT+DATA, gap, CSPLIT
+				qh->usecs += HS_USECS (1);
+				qh->c_usecs = HS_USECS (0);
+			}
+
+			qh->period = urb->interval;
+		}
+	}
+
+	/* using TT? */
+	switch (urb->dev->speed) {
+	case USB_SPEED_LOW:
+		info1 |= (1 << 12);	/* EPS "low" */
+		/* FALL THROUGH */
+
+	case USB_SPEED_FULL:
+		/* EPS 0 means "full" */
+		if (type != PIPE_INTERRUPT)
+			info1 |= (EHCI_TUNE_RL_TT << 28);
+		if (type == PIPE_CONTROL) {
+			info1 |= (1 << 27);	/* for TT */
+			info1 |= 1 << 14;	/* toggle from qtd */
+		}
+		info1 |= maxp << 16;
+
+		info2 |= (EHCI_TUNE_MULT_TT << 30);
+		info2 |= urb->dev->ttport << 23;
+		info2 |= urb->dev->tt->hub->devnum << 16;
+
+		/* NOTE:  if (PIPE_INTERRUPT) { scheduler sets c-mask } */
+
+		break;
+
+	case USB_SPEED_HIGH:		/* no TT involved */
+		info1 |= (2 << 12);	/* EPS "high" */
+		if (type == PIPE_CONTROL) {
+			info1 |= (EHCI_TUNE_RL_HS << 28);
+			info1 |= 64 << 16;	/* usb2 fixed maxpacket */
+			info1 |= 1 << 14;	/* toggle from qtd */
+			info2 |= (EHCI_TUNE_MULT_HS << 30);
+		} else if (type == PIPE_BULK) {
+			info1 |= (EHCI_TUNE_RL_HS << 28);
+			info1 |= 512 << 16;	/* usb2 fixed maxpacket */
+			info2 |= (EHCI_TUNE_MULT_HS << 30);
+		} else {		/* PIPE_INTERRUPT */
+			info1 |= max_packet (maxp) << 16;
+			info2 |= hb_mult (maxp) << 30;
+		}
+		break;
+	default:
+ 		dbg ("bogus dev %p speed %d", urb->dev, urb->dev->speed);
+done:
+		qh_put (ehci, qh);
+		return 0;
+	}
+
+	/* NOTE:  if (PIPE_INTERRUPT) { scheduler sets s-mask } */
+
+	/* init as halted, toggle clear, advance to dummy */
+	qh->qh_state = QH_STATE_IDLE;
+	qh->hw_info1 = cpu_to_le32 (info1);
+	qh->hw_info2 = cpu_to_le32 (info2);
+	qh_update (ehci, qh, qh->dummy);
+	qh->hw_token = cpu_to_le32 (QTD_STS_HALT);
+	usb_settoggle (urb->dev, usb_pipeendpoint (urb->pipe), !is_input, 1);
+	return qh;
+}
+#undef hb_mult
+#undef hb_packet
+
+/*-------------------------------------------------------------------------*/
+
+/* move qh (and its qtds) onto async queue; maybe enable queue.  */
+
+static void qh_link_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
+{
+	u32		dma = QH_NEXT (qh->qh_dma);
+	struct ehci_qh	*head;
+
+	/* (re)start the async schedule? */
+	head = ehci->async;
+	if (ehci->async_idle)
+		del_timer (&ehci->watchdog);
+	if (!head->qh_next.qh) {
+		u32	cmd = readl (&ehci->regs->command);
+
+		if (!(cmd & CMD_ASE)) {
+			/* in case a clear of CMD_ASE didn't take yet */
+			(void) handshake (&ehci->regs->status, STS_ASS, 0, 150);
+
+			/* force async head to be valid */
+			writel ((u32)ehci->async->qh_dma,
+					&ehci->regs->async_next);
+
+			cmd |= CMD_ASE | CMD_RUN;
+			writel (cmd, &ehci->regs->command);
+			ehci->hcd.state = USB_STATE_RUNNING;
+			/* posted write need not be known to HC yet ... */
+		}
+	}
+
+	qh->hw_token &= ~__constant_cpu_to_le32 (QTD_STS_HALT);
+
+	/* splice right after start */
+	qh->qh_next = head->qh_next;
+	qh->hw_next = head->hw_next;
+	wmb ();
+
+	head->qh_next.qh = qh;
+	head->hw_next = dma;
+
+	qh->qh_state = QH_STATE_LINKED;
+	/* qtd completions reported later by interrupt */
+
+	ehci->async_idle = 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * For control/bulk/interrupt, return QH with these TDs appended.
+ * Allocates and initializes the QH if necessary.
+ * Returns null if it can't allocate a QH it needs to.
+ * If the QH has TDs (urbs) already, that's great.
+ */
+static struct ehci_qh *qh_append_tds (
+	struct ehci_hcd		*ehci,
+	struct urb		*urb,
+	struct list_head	*qtd_list,
+	int			epnum,
+	void			**ptr
+)
+{
+	struct ehci_qh		*qh = 0;
+
+	qh = (struct ehci_qh *) *ptr;
+	if (unlikely (qh == 0)) {
+		/* can't sleep here, we have ehci->lock... */
+		qh = qh_make (ehci, urb, SLAB_ATOMIC);
+		*ptr = qh;
+	}
+	if (likely (qh != 0)) {
+		struct ehci_qtd	*qtd;
+
+		if (unlikely (list_empty (qtd_list)))
+			qtd = 0;
+		else
+			qtd = list_entry (qtd_list->next, struct ehci_qtd,
+					qtd_list);
+
+		/* control qh may need patching after enumeration */
+		if (unlikely (epnum == 0)) {
+			/* set_address changes the address */
+			if (le32_to_cpu (qh->hw_info1 & 0x7f) == 0)
+				qh->hw_info1 |= cpu_to_le32 (
+						usb_pipedevice (urb->pipe));
+
+			/* for full speed, ep0 maxpacket can grow */
+			else if (!(qh->hw_info1 & cpu_to_le32 (0x3 << 12))) {
+				u32	info, max;
+
+				info = le32_to_cpu (qh->hw_info1);
+				max = urb->dev->descriptor.bMaxPacketSize0;
+				if (max > (0x07ff & (info >> 16))) {
+					info &= ~(0x07ff << 16);
+					info |= max << 16;
+					qh->hw_info1 = cpu_to_le32 (info);
+				}
+			}
+		}
+
+		/* FIXME:  changing config or interface setting is not
+		 * supported yet.  preferred fix is for usbcore to tell
+		 * us to clear out each endpoint's state, but...
+		 */
+
+		/* usb_clear_halt() means qh data toggle gets reset */
+		if (unlikely (!usb_gettoggle (urb->dev,
+					(epnum & 0x0f), !(epnum & 0x10)))
+				&& !usb_pipecontrol (urb->pipe)) {
+			/* "never happens": drivers do stall cleanup right */
+			if (qh->qh_state != QH_STATE_IDLE
+					&& !list_empty (&qh->qtd_list)
+					&& qh->qh_state != QH_STATE_COMPLETING)
+				ehci_warn (ehci, "clear toggle dev%d "
+						"ep%d%s: not idle\n",
+						usb_pipedevice (urb->pipe),
+						epnum & 0x0f,
+						usb_pipein (urb->pipe)
+							? "in" : "out");
+			/* else we know this overlay write is safe */
+			clear_toggle (urb->dev,
+				epnum & 0x0f, !(epnum & 0x10), qh);
+		}
+
+		/* just one way to queue requests: swap with the dummy qtd.
+		 * only hc or qh_completions() usually modify the overlay.
+		 */
+		if (likely (qtd != 0)) {
+			struct ehci_qtd		*dummy;
+			dma_addr_t		dma;
+			u32			token;
+
+			/* to avoid racing the HC, use the dummy td instead of
+			 * the first td of our list (becomes new dummy).  both
+			 * tds stay deactivated until we're done, when the
+			 * HC is allowed to fetch the old dummy (4.10.2).
+			 */
+			token = qtd->hw_token;
+			qtd->hw_token = 0;
+			wmb ();
+			dummy = qh->dummy;
+
+			dma = dummy->qtd_dma;
+			*dummy = *qtd;
+			dummy->qtd_dma = dma;
+
+			list_del (&qtd->qtd_list);
+			list_add (&dummy->qtd_list, qtd_list);
+			__list_splice (qtd_list, qh->qtd_list.prev);
+
+			ehci_qtd_init (qtd, qtd->qtd_dma);
+			qh->dummy = qtd;
+
+			/* hc must see the new dummy at list end */
+			dma = qtd->qtd_dma;
+			qtd = list_entry (qh->qtd_list.prev,
+					struct ehci_qtd, qtd_list);
+			qtd->hw_next = QTD_NEXT (dma);
+
+			/* let the hc process these next qtds */
+			wmb ();
+			dummy->hw_token = token;
+
+			urb->hcpriv = qh_get (qh);
+		}
+	}
+	return qh;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int
+submit_async (
+	struct ehci_hcd		*ehci,
+	struct urb		*urb,
+	struct list_head	*qtd_list,
+	int			mem_flags
+) {
+	struct ehci_qtd		*qtd;
+	struct hcd_dev		*dev;
+	int			epnum;
+	unsigned long		flags;
+	struct ehci_qh		*qh = 0;
+
+	qtd = list_entry (qtd_list->next, struct ehci_qtd, qtd_list);
+	dev = (struct hcd_dev *)urb->dev->hcpriv;
+	epnum = usb_pipeendpoint (urb->pipe);
+	if (usb_pipein (urb->pipe) && !usb_pipecontrol (urb->pipe))
+		epnum |= 0x10;
+
+	vdbg ("%s: submit_async urb %p len %d ep %d-%s qtd %p [qh %p]",
+		hcd_to_bus (&ehci->hcd)->bus_name,
+		urb, urb->transfer_buffer_length,
+		epnum & 0x0f, (epnum & 0x10) ? "in" : "out",
+		qtd, dev ? dev->ep [epnum] : (void *)~0);
+
+	spin_lock_irqsave (&ehci->lock, flags);
+	qh = qh_append_tds (ehci, urb, qtd_list, epnum, &dev->ep [epnum]);
+
+	/* Control/bulk operations through TTs don't need scheduling,
+	 * the HC and TT handle it when the TT has a buffer ready.
+	 */
+	if (likely (qh != 0)) {
+		if (likely (qh->qh_state == QH_STATE_IDLE))
+			qh_link_async (ehci, qh_get (qh));
+	}
+	spin_unlock_irqrestore (&ehci->lock, flags);
+	if (unlikely (qh == 0)) {
+		qtd_list_free (ehci, urb, qtd_list);
+		return -ENOMEM;
+	}
+	return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* the async qh for the qtds being reclaimed are now unlinked from the HC */
+
+static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh);
+
+static void end_unlink_async (struct ehci_hcd *ehci, struct pt_regs *regs)
+{
+	struct ehci_qh		*qh = ehci->reclaim;
+	struct ehci_qh		*next;
+
+	del_timer (&ehci->watchdog);
+
+	// qh->hw_next = cpu_to_le32 (qh->qh_dma);
+	qh->qh_state = QH_STATE_IDLE;
+	qh->qh_next.qh = 0;
+	qh_put (ehci, qh);			// refcount from reclaim 
+	ehci->reclaim = 0;
+	ehci->reclaim_ready = 0;
+
+	/* other unlink(s) may be pending (in QH_STATE_UNLINK_WAIT) */
+	next = qh->reclaim;
+	qh->reclaim = 0;
+
+	qh_completions (ehci, qh, regs);
+
+	if (!list_empty (&qh->qtd_list)
+			&& HCD_IS_RUNNING (ehci->hcd.state))
+		qh_link_async (ehci, qh);
+	else {
+		qh_put (ehci, qh);		// refcount from async list
+
+		/* it's not free to turn the async schedule on/off; leave it
+		 * active but idle for a while once it empties.
+		 */
+		if (HCD_IS_RUNNING (ehci->hcd.state)
+				&& ehci->async->qh_next.qh == 0
+				&& !timer_pending (&ehci->watchdog)) {
+			ehci->async_idle = 1;
+			mod_timer (&ehci->watchdog,
+					jiffies + EHCI_ASYNC_JIFFIES);
+		}
+	}
+
+	if (next)
+		start_unlink_async (ehci, next);
+}
+
+/* makes sure the async qh will become idle */
+/* caller must own ehci->lock */
+
+static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
+{
+	int		cmd = readl (&ehci->regs->command);
+	struct ehci_qh	*prev;
+
+#ifdef DEBUG
+	if (ehci->reclaim
+			|| (qh->qh_state != QH_STATE_LINKED
+				&& qh->qh_state != QH_STATE_UNLINK_WAIT)
+#ifdef CONFIG_SMP
+// this macro lies except on SMP compiles
+			|| !spin_is_locked (&ehci->lock)
+#endif
+			)
+		BUG ();
+#endif
+
+	/* stop async schedule right now? */
+	if (unlikely (qh == ehci->async)) {
+		/* can't get here without STS_ASS set */
+		if (ehci->hcd.state != USB_STATE_HALT) {
+			writel (cmd & ~CMD_ASE, &ehci->regs->command);
+			wmb ();
+			// handshake later, if we need to
+		}
+		return;
+	} 
+
+	qh->qh_state = QH_STATE_UNLINK;
+	ehci->reclaim = qh = qh_get (qh);
+
+	prev = ehci->async;
+	while (prev->qh_next.qh != qh)
+		prev = prev->qh_next.qh;
+
+	prev->hw_next = qh->hw_next;
+	prev->qh_next = qh->qh_next;
+	wmb ();
+
+	if (unlikely (ehci->hcd.state == USB_STATE_HALT)) {
+		/* if (unlikely (qh->reclaim != 0))
+		 * 	this will recurse, probably not much
+		 */
+		end_unlink_async (ehci, NULL);
+		return;
+	}
+
+	ehci->reclaim_ready = 0;
+	cmd |= CMD_IAAD;
+	writel (cmd, &ehci->regs->command);
+	/* posted write need not be known to HC yet ... */
+
+	mod_timer (&ehci->watchdog, jiffies + EHCI_WATCHDOG_JIFFIES);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void
+scan_async (struct ehci_hcd *ehci, struct pt_regs *regs)
+{
+	struct ehci_qh		*qh;
+	int			unlink_delay = 0;
+
+	if (!++(ehci->stamp))
+		ehci->stamp++;
+rescan:
+	qh = ehci->async->qh_next.qh;
+	if (likely (qh != 0)) {
+		do {
+			/* clean any finished work for this qh */
+			if (!list_empty (&qh->qtd_list)
+					&& qh->stamp != ehci->stamp) {
+				int temp;
+
+				/* unlinks could happen here; completion
+				 * reporting drops the lock.  rescan using
+				 * the latest schedule, but don't rescan
+				 * qhs we already finished (no looping).
+				 */
+				qh = qh_get (qh);
+				qh->stamp = ehci->stamp;
+				temp = qh_completions (ehci, qh, regs);
+				qh_put (ehci, qh);
+				if (temp != 0) {
+					goto rescan;
+				}
+			}
+
+			/* unlink idle entries, reducing HC PCI usage as well
+			 * as HCD schedule-scanning costs.  delay for any qh
+			 * we just scanned, there's a not-unusual case that it
+			 * doesn't stay idle for long.
+			 * (plus, avoids some kind of re-activation race.)
+			 */
+			if (list_empty (&qh->qtd_list)) {
+				if (qh->stamp == ehci->stamp)
+					unlink_delay = 1;
+				else if (!ehci->reclaim) {
+					start_unlink_async (ehci, qh);
+					unlink_delay = 0;
+				}
+			}
+
+			qh = qh->qh_next.qh;
+		} while (qh);
+	}
+
+	if (unlink_delay && !timer_pending (&ehci->watchdog))
+		mod_timer (&ehci->watchdog, jiffies + EHCI_WATCHDOG_JIFFIES/2);
+}
diff -urN linux-2.4.21-pre5/drivers/usb/host/ehci-sched.c linux-2.4.21-pre6/drivers/usb/host/ehci-sched.c
--- linux-2.4.21-pre5/drivers/usb/host/ehci-sched.c	Wed Dec 31 16:00:00 1969
+++ linux-2.4.21-pre6/drivers/usb/host/ehci-sched.c	Wed Mar 26 16:11:39 2003
@@ -0,0 +1,1123 @@
+/*
+ * Copyright (c) 2001-2002 by David Brownell
+ * 
+ * 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.
+ *
+ * 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.
+ */
+
+/* this file is part of ehci-hcd.c */
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * EHCI scheduled transaction support:  interrupt, iso, split iso
+ * These are called "periodic" transactions in the EHCI spec.
+ *
+ * Note that for interrupt transfers, the QH/QTD manipulation is shared
+ * with the "asynchronous" transaction support (control/bulk transfers).
+ * The only real difference is in how interrupt transfers are scheduled.
+ * We get some funky API restrictions from the current URB model, which
+ * works notably better for reading transfers than for writing.  (And
+ * which accordingly needs to change before it'll work inside devices,
+ * or with "USB On The Go" additions to USB 2.0 ...)
+ */
+
+static int ehci_get_frame (struct usb_hcd *hcd);
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * periodic_next_shadow - return "next" pointer on shadow list
+ * @periodic: host pointer to qh/itd/sitd
+ * @tag: hardware tag for type of this record
+ */
+static union ehci_shadow *
+periodic_next_shadow (union ehci_shadow *periodic, int tag)
+{
+	switch (tag) {
+	case Q_TYPE_QH:
+		return &periodic->qh->qh_next;
+	case Q_TYPE_FSTN:
+		return &periodic->fstn->fstn_next;
+	case Q_TYPE_ITD:
+		return &periodic->itd->itd_next;
+#ifdef have_split_iso
+	case Q_TYPE_SITD:
+		return &periodic->sitd->sitd_next;
+#endif /* have_split_iso */
+	}
+	dbg ("BAD shadow %p tag %d", periodic->ptr, tag);
+	// BUG ();
+	return 0;
+}
+
+/* returns true after successful unlink */
+/* caller must hold ehci->lock */
+static int periodic_unlink (struct ehci_hcd *ehci, unsigned frame, void *ptr)
+{
+	union ehci_shadow	*prev_p = &ehci->pshadow [frame];
+	u32			*hw_p = &ehci->periodic [frame];
+	union ehci_shadow	here = *prev_p;
+	union ehci_shadow	*next_p;
+
+	/* find predecessor of "ptr"; hw and shadow lists are in sync */
+	while (here.ptr && here.ptr != ptr) {
+		prev_p = periodic_next_shadow (prev_p, Q_NEXT_TYPE (*hw_p));
+		hw_p = &here.qh->hw_next;
+		here = *prev_p;
+	}
+	/* an interrupt entry (at list end) could have been shared */
+	if (!here.ptr) {
+		dbg ("entry %p no longer on frame [%d]", ptr, frame);
+		return 0;
+	}
+	// vdbg ("periodic unlink %p from frame %d", ptr, frame);
+
+	/* update hardware list ... HC may still know the old structure, so
+	 * don't change hw_next until it'll have purged its cache
+	 */
+	next_p = periodic_next_shadow (&here, Q_NEXT_TYPE (*hw_p));
+	*hw_p = here.qh->hw_next;
+
+	/* unlink from shadow list; HCD won't see old structure again */
+	*prev_p = *next_p;
+	next_p->ptr = 0;
+
+	return 1;
+}
+
+/* how many of the uframe's 125 usecs are allocated? */
+static unsigned short
+periodic_usecs (struct ehci_hcd *ehci, unsigned frame, unsigned uframe)
+{
+	u32			*hw_p = &ehci->periodic [frame];
+	union ehci_shadow	*q = &ehci->pshadow [frame];
+	unsigned		usecs = 0;
+
+	while (q->ptr) {
+		switch (Q_NEXT_TYPE (*hw_p)) {
+		case Q_TYPE_QH:
+			/* is it in the S-mask? */
+			if (q->qh->hw_info2 & cpu_to_le32 (1 << uframe))
+				usecs += q->qh->usecs;
+			/* ... or C-mask? */
+			if (q->qh->hw_info2 & cpu_to_le32 (1 << (8 + uframe)))
+				usecs += q->qh->c_usecs;
+			q = &q->qh->qh_next;
+			break;
+		case Q_TYPE_FSTN:
+			/* for "save place" FSTNs, count the relevant INTR
+			 * bandwidth from the previous frame
+			 */
+			if (q->fstn->hw_prev != EHCI_LIST_END) {
+				dbg ("not counting FSTN bandwidth yet ...");
+			}
+			q = &q->fstn->fstn_next;
+			break;
+		case Q_TYPE_ITD:
+			/* NOTE the "one uframe per itd" policy */
+			if (q->itd->hw_transaction [uframe] != 0)
+				usecs += q->itd->usecs;
+			q = &q->itd->itd_next;
+			break;
+#ifdef have_split_iso
+		case Q_TYPE_SITD:
+			temp = q->sitd->hw_fullspeed_ep &
+				__constant_cpu_to_le32 (1 << 31);
+
+			// FIXME:  this doesn't count data bytes right...
+
+			/* is it in the S-mask?  (count SPLIT, DATA) */
+			if (q->sitd->hw_uframe & cpu_to_le32 (1 << uframe)) {
+				if (temp)
+					usecs += HS_USECS (188);
+				else
+					usecs += HS_USECS (1);
+			}
+
+			/* ... C-mask?  (count CSPLIT, DATA) */
+			if (q->sitd->hw_uframe &
+					cpu_to_le32 (1 << (8 + uframe))) {
+				if (temp)
+					usecs += HS_USECS (0);
+				else
+					usecs += HS_USECS (188);
+			}
+			q = &q->sitd->sitd_next;
+			break;
+#endif /* have_split_iso */
+		default:
+			BUG ();
+		}
+	}
+#ifdef	DEBUG
+	if (usecs > 100)
+		err ("overallocated uframe %d, periodic is %d usecs",
+			frame * 8 + uframe, usecs);
+#endif
+	return usecs;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int enable_periodic (struct ehci_hcd *ehci)
+{
+	u32	cmd;
+	int	status;
+
+	/* did clearing PSE did take effect yet?
+	 * takes effect only at frame boundaries...
+	 */
+	status = handshake (&ehci->regs->status, STS_PSS, 0, 9 * 125);
+	if (status != 0) {
+		ehci->hcd.state = USB_STATE_HALT;
+		return status;
+	}
+
+	cmd = readl (&ehci->regs->command) | CMD_PSE;
+	writel (cmd, &ehci->regs->command);
+	/* posted write ... PSS happens later */
+	ehci->hcd.state = USB_STATE_RUNNING;
+
+	/* make sure ehci_work scans these */
+	ehci->next_uframe = readl (&ehci->regs->frame_index)
+				% (ehci->periodic_size << 3);
+	return 0;
+}
+
+static int disable_periodic (struct ehci_hcd *ehci)
+{
+	u32	cmd;
+	int	status;
+
+	/* did setting PSE not take effect yet?
+	 * takes effect only at frame boundaries...
+	 */
+	status = handshake (&ehci->regs->status, STS_PSS, STS_PSS, 9 * 125);
+	if (status != 0) {
+		ehci->hcd.state = USB_STATE_HALT;
+		return status;
+	}
+
+	cmd = readl (&ehci->regs->command) & ~CMD_PSE;
+	writel (cmd, &ehci->regs->command);
+	/* posted write ... */
+
+	ehci->next_uframe = -1;
+	return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+// FIXME microframe periods not yet handled
+
+static void intr_deschedule (
+	struct ehci_hcd	*ehci,
+	struct ehci_qh	*qh,
+	int		wait
+) {
+	int		status;
+	unsigned	frame = qh->start;
+
+	do {
+		periodic_unlink (ehci, frame, qh);
+		qh_put (ehci, qh);
+		frame += qh->period;
+	} while (frame < ehci->periodic_size);
+
+	qh->qh_state = QH_STATE_UNLINK;
+	qh->qh_next.ptr = 0;
+	ehci->periodic_sched--;
+
+	/* maybe turn off periodic schedule */
+	if (!ehci->periodic_sched)
+		status = disable_periodic (ehci);
+	else {
+		status = 0;
+		vdbg ("periodic schedule still enabled");
+	}
+
+	/*
+	 * If the hc may be looking at this qh, then delay a uframe
+	 * (yeech!) to be sure it's done.
+	 * No other threads may be mucking with this qh.
+	 */
+	if (((ehci_get_frame (&ehci->hcd) - frame) % qh->period) == 0) {
+		if (wait) {
+			udelay (125);
+			qh->hw_next = EHCI_LIST_END;
+		} else {
+			/* we may not be IDLE yet, but if the qh is empty
+			 * the race is very short.  then if qh also isn't
+			 * rescheduled soon, it won't matter.  otherwise...
+			 */
+			vdbg ("intr_deschedule...");
+		}
+	} else
+		qh->hw_next = EHCI_LIST_END;
+
+	qh->qh_state = QH_STATE_IDLE;
+
+	/* update per-qh bandwidth utilization (for usbfs) */
+	hcd_to_bus (&ehci->hcd)->bandwidth_allocated -= 
+		(qh->usecs + qh->c_usecs) / qh->period;
+
+	dbg ("descheduled qh %p, period = %d frame = %d count = %d, urbs = %d",
+		qh, qh->period, frame,
+		atomic_read (&qh->refcount), ehci->periodic_sched);
+}
+
+static int check_period (
+	struct ehci_hcd *ehci, 
+	unsigned	frame,
+	unsigned	uframe,
+	unsigned	period,
+	unsigned	usecs
+) {
+	/* complete split running into next frame?
+	 * given FSTN support, we could sometimes check...
+	 */
+	if (uframe >= 8)
+		return 0;
+
+	/*
+	 * 80% periodic == 100 usec/uframe available
+	 * convert "usecs we need" to "max already claimed" 
+	 */
+	usecs = 100 - usecs;
+
+	do {
+		int	claimed;
+
+// FIXME delete when intr_submit handles non-empty queues
+// this gives us a one intr/frame limit (vs N/uframe)
+// ... and also lets us avoid tracking split transactions
+// that might collide at a given TT/hub.
+		if (ehci->pshadow [frame].ptr)
+			return 0;
+
+		claimed = periodic_usecs (ehci, frame, uframe);
+		if (claimed > usecs)
+			return 0;
+
+// FIXME update to handle sub-frame periods
+	} while ((frame += period) < ehci->periodic_size);
+
+	// success!
+	return 1;
+}
+
+static int check_intr_schedule (
+	struct ehci_hcd		*ehci, 
+	unsigned		frame,
+	unsigned		uframe,
+	const struct ehci_qh	*qh,
+	u32			*c_maskp
+)
+{
+    	int		retval = -ENOSPC;
+
+	if (!check_period (ehci, frame, uframe, qh->period, qh->usecs))
+		goto done;
+	if (!qh->c_usecs) {
+		retval = 0;
+		*c_maskp = cpu_to_le32 (0);
+		goto done;
+	}
+
+	/* This is a split transaction; check the bandwidth available for
+	 * the completion too.  Check both worst and best case gaps: worst
+	 * case is SPLIT near uframe end, and CSPLIT near start ... best is
+	 * vice versa.  Difference can be almost two uframe times, but we
+	 * reserve unnecessary bandwidth (waste it) this way.  (Actually
+	 * even better cases exist, like immediate device NAK.)
+	 *
+	 * FIXME don't even bother unless we know this TT is idle in that
+	 * range of uframes ... for now, check_period() allows only one
+	 * interrupt transfer per frame, so needn't check "TT busy" status
+	 * when scheduling a split (QH, SITD, or FSTN).
+	 *
+	 * FIXME ehci 0.96 and above can use FSTNs
+	 */
+	if (!check_period (ehci, frame, uframe + qh->gap_uf + 1,
+				qh->period, qh->c_usecs))
+		goto done;
+	if (!check_period (ehci, frame, uframe + qh->gap_uf,
+				qh->period, qh->c_usecs))
+		goto done;
+
+	*c_maskp = cpu_to_le32 (0x03 << (8 + uframe + qh->gap_uf));
+	retval = 0;
+done:
+	return retval;
+}
+
+static int qh_schedule (struct ehci_hcd *ehci, struct ehci_qh *qh)
+{
+	int 		status;
+	unsigned	uframe;
+	u32		c_mask;
+	unsigned	frame;		/* 0..(qh->period - 1), or NO_FRAME */
+
+	qh->hw_next = EHCI_LIST_END;
+	frame = qh->start;
+
+	/* reuse the previous schedule slots, if we can */
+	if (frame < qh->period) {
+		uframe = ffs (le32_to_cpup (&qh->hw_info2) & 0x00ff);
+		status = check_intr_schedule (ehci, frame, --uframe,
+				qh, &c_mask);
+	} else {
+		uframe = 0;
+		c_mask = 0;
+		status = -ENOSPC;
+	}
+
+	/* else scan the schedule to find a group of slots such that all
+	 * uframes have enough periodic bandwidth available.
+	 */
+	if (status) {
+		frame = qh->period - 1;
+		do {
+			for (uframe = 0; uframe < 8; uframe++) {
+				status = check_intr_schedule (ehci,
+						frame, uframe, qh,
+						&c_mask);
+				if (status == 0)
+					break;
+			}
+		} while (status && --frame);
+		if (status)
+			goto done;
+		qh->start = frame;
+
+		/* reset S-frame and (maybe) C-frame masks */
+		qh->hw_info2 &= ~0xffff;
+		qh->hw_info2 |= cpu_to_le32 (1 << uframe) | c_mask;
+	} else
+		dbg ("reused previous qh %p schedule", qh);
+
+	/* stuff into the periodic schedule */
+	qh->qh_state = QH_STATE_LINKED;
+	dbg ("scheduled qh %p usecs %d/%d period %d.0 starting %d.%d (gap %d)",
+		qh, qh->usecs, qh->c_usecs,
+		qh->period, frame, uframe, qh->gap_uf);
+	do {
+		if (unlikely (ehci->pshadow [frame].ptr != 0)) {
+
+// FIXME -- just link toward the end, before any qh with a shorter period,
+// AND accomodate it already having been linked here (after some other qh)
+// AS WELL AS updating the schedule checking logic
+
+			BUG ();
+		} else {
+			ehci->pshadow [frame].qh = qh_get (qh);
+			ehci->periodic [frame] =
+				QH_NEXT (qh->qh_dma);
+		}
+		wmb ();
+		frame += qh->period;
+	} while (frame < ehci->periodic_size);
+
+	/* update per-qh bandwidth for usbfs */
+	hcd_to_bus (&ehci->hcd)->bandwidth_allocated += 
+		(qh->usecs + qh->c_usecs) / qh->period;
+
+	/* maybe enable periodic schedule processing */
+	if (!ehci->periodic_sched++)
+		status = enable_periodic (ehci);
+done:
+	return status;
+}
+
+static int intr_submit (
+	struct ehci_hcd		*ehci,
+	struct urb		*urb,
+	struct list_head	*qtd_list,
+	int			mem_flags
+) {
+	unsigned		epnum;
+	unsigned long		flags;
+	struct ehci_qh		*qh;
+	struct hcd_dev		*dev;
+	int			is_input;
+	int			status = 0;
+	struct list_head	empty;
+
+	/* get endpoint and transfer/schedule data */
+	epnum = usb_pipeendpoint (urb->pipe);
+	is_input = usb_pipein (urb->pipe);
+	if (is_input)
+		epnum |= 0x10;
+
+	spin_lock_irqsave (&ehci->lock, flags);
+	dev = (struct hcd_dev *)urb->dev->hcpriv;
+
+	/* get qh and force any scheduling errors */
+	INIT_LIST_HEAD (&empty);
+	qh = qh_append_tds (ehci, urb, &empty, epnum, &dev->ep [epnum]);
+	if (qh == 0) {
+		status = -ENOMEM;
+		goto done;
+	}
+	if (qh->qh_state == QH_STATE_IDLE) {
+		if ((status = qh_schedule (ehci, qh)) != 0)
+			goto done;
+	}
+
+	/* then queue the urb's tds to the qh */
+	qh = qh_append_tds (ehci, urb, qtd_list, epnum, &dev->ep [epnum]);
+	BUG_ON (qh == 0);
+
+	/* ... update usbfs periodic stats */
+	hcd_to_bus (&ehci->hcd)->bandwidth_int_reqs++;
+
+done:
+	spin_unlock_irqrestore (&ehci->lock, flags);
+	if (status)
+		qtd_list_free (ehci, urb, qtd_list);
+
+	return status;
+}
+
+static unsigned
+intr_complete (
+	struct ehci_hcd	*ehci,
+	unsigned	frame,
+	struct ehci_qh	*qh,
+	struct pt_regs	*regs
+) {
+	unsigned	count;
+
+	/* nothing to report? */
+	if (likely ((qh->hw_token & __constant_cpu_to_le32 (QTD_STS_ACTIVE))
+			!= 0))
+		return 0;
+	if (unlikely (list_empty (&qh->qtd_list))) {
+		dbg ("intr qh %p no TDs?", qh);
+		return 0;
+	}
+	
+	/* handle any completions */
+	count = qh_completions (ehci, qh, regs);
+
+	if (unlikely (list_empty (&qh->qtd_list)))
+		intr_deschedule (ehci, qh, 0);
+
+	return count;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void
+itd_free_list (struct ehci_hcd *ehci, struct urb *urb)
+{
+	struct ehci_itd *first_itd = urb->hcpriv;
+
+	while (!list_empty (&first_itd->itd_list)) {
+		struct ehci_itd	*itd;
+
+		itd = list_entry (
+			first_itd->itd_list.next,
+			struct ehci_itd, itd_list);
+		list_del (&itd->itd_list);
+		pci_pool_free (ehci->itd_pool, itd, itd->itd_dma);
+	}
+	pci_pool_free (ehci->itd_pool, first_itd, first_itd->itd_dma);
+	urb->hcpriv = 0;
+}
+
+static int
+itd_fill (
+	struct ehci_hcd	*ehci,
+	struct ehci_itd	*itd,
+	struct urb	*urb,
+	unsigned	index,		// urb->iso_frame_desc [index]
+	dma_addr_t	dma		// mapped transfer buffer
+) {
+	u64		temp;
+	u32		buf1;
+	unsigned	i, epnum, maxp, multi;
+	unsigned	length;
+	int		is_input;
+
+	itd->hw_next = EHCI_LIST_END;
+	itd->urb = urb;
+	itd->index = index;
+
+	/* tell itd about its transfer buffer, max 2 pages */
+	length = urb->iso_frame_desc [index].length;
+	dma += urb->iso_frame_desc [index].offset;
+	temp = dma & ~0x0fff;
+	for (i = 0; i < 2; i++) {
+		itd->hw_bufp [i] = cpu_to_le32 ((u32) temp);
+		itd->hw_bufp_hi [i] = cpu_to_le32 ((u32)(temp >> 32));
+		temp += 0x1000;
+	}
+	itd->buf_dma = dma;
+
+	/*
+	 * this might be a "high bandwidth" highspeed endpoint,
+	 * as encoded in the ep descriptor's maxpacket field
+	 */
+	epnum = usb_pipeendpoint (urb->pipe);
+	is_input = usb_pipein (urb->pipe);
+	if (is_input) {
+		maxp = urb->dev->epmaxpacketin [epnum];
+		buf1 = (1 << 11);
+	} else {
+		maxp = urb->dev->epmaxpacketout [epnum];
+		buf1 = 0;
+	}
+	buf1 |= (maxp & 0x03ff);
+	multi = 1;
+	multi += (maxp >> 11) & 0x03;
+	maxp &= 0x03ff;
+	maxp *= multi;
+
+	/* transfer can't fit in any uframe? */ 
+	if (length < 0 || maxp < length) {
+		dbg ("BAD iso packet: %d bytes, max %d, urb %p [%d] (of %d)",
+			length, maxp, urb, index,
+			urb->iso_frame_desc [index].length);
+		return -ENOSPC;
+	}
+	itd->usecs = usb_calc_bus_time (USB_SPEED_HIGH, is_input, 1, length);
+
+	/* "plus" info in low order bits of buffer pointers */
+	itd->hw_bufp [0] |= cpu_to_le32 ((epnum << 8) | urb->dev->devnum);
+	itd->hw_bufp [1] |= cpu_to_le32 (buf1);
+	itd->hw_bufp [2] |= cpu_to_le32 (multi);
+
+	/* figure hw_transaction[] value (it's scheduled later) */
+	itd->transaction = EHCI_ISOC_ACTIVE;
+	itd->transaction |= dma & 0x0fff;		/* offset; buffer=0 */
+	if ((index + 1) == urb->number_of_packets)
+		itd->transaction |= EHCI_ITD_IOC; 	/* end-of-urb irq */
+	itd->transaction |= length << 16;
+	cpu_to_le32s (&itd->transaction);
+
+	return 0;
+}
+
+static int
+itd_urb_transaction (
+	struct ehci_hcd		*ehci,
+	struct urb		*urb,
+	int			mem_flags
+) {
+	int			frame_index;
+	struct ehci_itd		*first_itd, *itd;
+	int			status;
+	dma_addr_t		itd_dma;
+
+	/* allocate/init ITDs */
+	for (frame_index = 0, first_itd = 0;
+			frame_index < urb->number_of_packets;
+			frame_index++) {
+		itd = pci_pool_alloc (ehci->itd_pool, mem_flags, &itd_dma);
+		if (!itd) {
+			status = -ENOMEM;
+			goto fail;
+		}
+		memset (itd, 0, sizeof *itd);
+		itd->itd_dma = itd_dma;
+
+		status = itd_fill (ehci, itd, urb, frame_index,
+				urb->transfer_dma);
+		if (status != 0)
+			goto fail;
+
+		if (first_itd)
+			list_add_tail (&itd->itd_list,
+					&first_itd->itd_list);
+		else {
+			INIT_LIST_HEAD (&itd->itd_list);
+			urb->hcpriv = first_itd = itd;
+		}
+	}
+	urb->error_count = 0;
+	return 0;
+
+fail:
+	if (urb->hcpriv)
+		itd_free_list (ehci, urb);
+	return status;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static inline void
+itd_link (struct ehci_hcd *ehci, unsigned frame, struct ehci_itd *itd)
+{
+	/* always prepend ITD/SITD ... only QH tree is order-sensitive */
+	itd->itd_next = ehci->pshadow [frame];
+	itd->hw_next = ehci->periodic [frame];
+	ehci->pshadow [frame].itd = itd;
+	ehci->periodic [frame] = cpu_to_le32 (itd->itd_dma) | Q_TYPE_ITD;
+}
+
+/*
+ * return zero on success, else -errno
+ * - start holds first uframe to start scheduling into
+ * - max is the first uframe it's NOT (!) OK to start scheduling into
+ * math to be done modulo "mod" (ehci->periodic_size << 3)
+ */
+static int get_iso_range (
+	struct ehci_hcd		*ehci,
+	struct urb		*urb,
+	unsigned		*start,
+	unsigned		*max,
+	unsigned		mod
+) {
+	struct list_head	*lh;
+	struct hcd_dev		*dev = urb->dev->hcpriv;
+	int			last = -1;
+	unsigned		now, span, end;
+
+	span = urb->interval * urb->number_of_packets;
+
+	/* first see if we know when the next transfer SHOULD happen */
+	list_for_each (lh, &dev->urb_list) {
+		struct urb	*u;
+		struct ehci_itd	*itd;
+		unsigned	s;
+
+		u = list_entry (lh, struct urb, urb_list);
+		if (u == urb || u->pipe != urb->pipe)
+			continue;
+		if (u->interval != urb->interval) {	/* must not change! */ 
+			dbg ("urb %p interval %d ... != %p interval %d",
+				u, u->interval, urb, urb->interval);
+			return -EINVAL;
+		}
+		
+		/* URB for this endpoint... covers through when?  */
+		itd = urb->hcpriv;
+		s = itd->uframe + u->interval * u->number_of_packets;
+		if (last < 0)
+			last = s;
+		else {
+			/*
+			 * So far we can only queue two ISO URBs...
+			 *
+			 * FIXME do interval math, figure out whether
+			 * this URB is "before" or not ... also, handle
+			 * the case where the URB might have completed,
+			 * but hasn't yet been processed.
+			 */
+			dbg ("NYET: queue >2 URBs per ISO endpoint");
+			return -EDOM;
+		}
+	}
+
+	/* calculate the legal range [start,max) */
+	now = readl (&ehci->regs->frame_index) + 1;	/* next uframe */
+	if (!ehci->periodic_sched)
+		now += 8;				/* startup delay */
+	now %= mod;
+	end = now + mod;
+	if (last < 0) {
+		*start = now + ehci->i_thresh + /* paranoia */ 1;
+		*max = end - span;
+		if (*max < *start + 1)
+			*max = *start + 1;
+	} else {
+		*start = last % mod;
+		*max = (last + 1) % mod;
+	}
+
+	/* explicit start frame? */
+	if (!(urb->transfer_flags & URB_ISO_ASAP)) {
+		unsigned	temp;
+
+		/* sanity check: must be in range */
+		urb->start_frame %= ehci->periodic_size;
+		temp = urb->start_frame << 3;
+		if (temp < *start)
+			temp += mod;
+		if (temp > *max)
+			return -EDOM;
+
+		/* use that explicit start frame */
+		*start = urb->start_frame << 3;
+		temp += 8;
+		if (temp < *max)
+			*max = temp;
+	}
+
+	// FIXME minimize wraparound to "now" ... insist max+span
+	// (and start+span) remains a few frames short of "end"
+
+	*max %= ehci->periodic_size;
+	if ((*start + span) < end)
+		return 0;
+	return -EFBIG;
+}
+
+static int
+itd_schedule (struct ehci_hcd *ehci, struct urb *urb)
+{
+	unsigned	start, max, i;
+	int		status;
+	unsigned	mod = ehci->periodic_size << 3;
+
+	for (i = 0; i < urb->number_of_packets; i++) {
+		urb->iso_frame_desc [i].status = -EINPROGRESS;
+		urb->iso_frame_desc [i].actual_length = 0;
+	}
+
+	if ((status = get_iso_range (ehci, urb, &start, &max, mod)) != 0)
+		return status;
+
+	do {
+		unsigned	uframe;
+		unsigned	usecs;
+		struct ehci_itd	*itd;
+
+		/* check schedule: enough space? */
+		itd = urb->hcpriv;
+		uframe = start;
+		for (i = 0, uframe = start;
+				i < urb->number_of_packets;
+				i++, uframe += urb->interval) {
+			uframe %= mod;
+
+			/* can't commit more than 80% periodic == 100 usec */
+			if (periodic_usecs (ehci, uframe >> 3, uframe & 0x7)
+					> (100 - itd->usecs)) {
+				itd = 0;
+				break;
+			}
+			itd = list_entry (itd->itd_list.next,
+				struct ehci_itd, itd_list);
+		}
+		if (!itd)
+			continue;
+		
+		/* that's where we'll schedule this! */
+		itd = urb->hcpriv;
+		urb->start_frame = start >> 3;
+		vdbg ("ISO urb %p (%d packets period %d) starting %d.%d",
+			urb, urb->number_of_packets, urb->interval,
+			urb->start_frame, start & 0x7);
+		for (i = 0, uframe = start, usecs = 0;
+				i < urb->number_of_packets;
+				i++, uframe += urb->interval) {
+			uframe %= mod;
+
+			itd->uframe = uframe;
+			itd->hw_transaction [uframe & 0x07] = itd->transaction;
+			itd_link (ehci, (uframe >> 3) % ehci->periodic_size,
+				itd);
+			wmb ();
+			usecs += itd->usecs;
+
+			itd = list_entry (itd->itd_list.next,
+				struct ehci_itd, itd_list);
+		}
+
+		/* update bandwidth utilization records (for usbfs)
+		 *
+		 * FIXME This claims each URB queued to an endpoint, as if
+		 * transfers were concurrent, not sequential.  So bandwidth
+		 * typically gets double-billed ... comes from tying it to
+		 * URBs rather than endpoints in the schedule.  Luckily we
+		 * don't use this usbfs data for serious decision making.
+		 */
+		usecs /= urb->number_of_packets;
+		usecs /= urb->interval;
+		usecs >>= 3;
+		if (usecs < 1)
+			usecs = 1;
+		usb_claim_bandwidth (urb->dev, urb, usecs, 1);
+
+		/* maybe enable periodic schedule processing */
+		if (!ehci->periodic_sched++) {
+			if ((status =  enable_periodic (ehci)) != 0) {
+				// FIXME deschedule right away
+				err ("itd_schedule, enable = %d", status);
+			}
+		}
+
+		return 0;
+
+	} while ((start = ++start % mod) != max);
+
+	/* no room in the schedule */
+	dbg ("urb %p, CAN'T SCHEDULE", urb);
+	return -ENOSPC;
+}
+
+/*-------------------------------------------------------------------------*/
+
+#define	ISO_ERRS (EHCI_ISOC_BUF_ERR | EHCI_ISOC_BABBLE | EHCI_ISOC_XACTERR)
+
+static unsigned
+itd_complete (
+	struct ehci_hcd	*ehci,
+	struct ehci_itd	*itd,
+	unsigned	uframe,
+	struct pt_regs	*regs
+) {
+	struct urb				*urb = itd->urb;
+	struct usb_iso_packet_descriptor	*desc;
+	u32					t;
+
+	/* update status for this uframe's transfers */
+	desc = &urb->iso_frame_desc [itd->index];
+
+	t = itd->hw_transaction [uframe];
+	itd->hw_transaction [uframe] = 0;
+	if (t & EHCI_ISOC_ACTIVE)
+		desc->status = -EXDEV;
+	else if (t & ISO_ERRS) {
+		urb->error_count++;
+		if (t & EHCI_ISOC_BUF_ERR)
+			desc->status = usb_pipein (urb->pipe)
+				? -ENOSR  /* couldn't read */
+				: -ECOMM; /* couldn't write */
+		else if (t & EHCI_ISOC_BABBLE)
+			desc->status = -EOVERFLOW;
+		else /* (t & EHCI_ISOC_XACTERR) */
+			desc->status = -EPROTO;
+
+		/* HC need not update length with this error */
+		if (!(t & EHCI_ISOC_BABBLE))
+			desc->actual_length += EHCI_ITD_LENGTH (t);
+	} else {
+		desc->status = 0;
+		desc->actual_length += EHCI_ITD_LENGTH (t);
+	}
+
+	vdbg ("itd %p urb %p packet %d/%d trans %x status %d len %d",
+		itd, urb, itd->index + 1, urb->number_of_packets,
+		t, desc->status, desc->actual_length);
+
+	/* handle completion now? */
+	if ((itd->index + 1) != urb->number_of_packets)
+		return 0;
+
+	/*
+	 * Always give the urb back to the driver ... expect it to submit
+	 * a new urb (or resubmit this), and to have another already queued
+	 * when un-interrupted transfers are needed.
+	 *
+	 * NOTE that for now we don't accelerate ISO unlinks; they just
+	 * happen according to the current schedule.  Means a delay of
+	 * up to about a second (max).
+	 */
+	itd_free_list (ehci, urb);
+	if (urb->status == -EINPROGRESS)
+		urb->status = 0;
+
+	/* complete() can reenter this HCD */
+	spin_unlock (&ehci->lock);
+	usb_hcd_giveback_urb (&ehci->hcd, urb, regs);
+	spin_lock (&ehci->lock);
+
+	/* defer stopping schedule; completion can submit */
+	ehci->periodic_sched--;
+	if (!ehci->periodic_sched)
+		(void) disable_periodic (ehci);
+
+	return 1;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int itd_submit (struct ehci_hcd *ehci, struct urb *urb, int mem_flags)
+{
+	int		status;
+	unsigned long	flags;
+
+	dbg ("itd_submit urb %p", urb);
+
+	/* allocate ITDs w/o locking anything */
+	status = itd_urb_transaction (ehci, urb, mem_flags);
+	if (status < 0)
+		return status;
+
+	/* schedule ... need to lock */
+	spin_lock_irqsave (&ehci->lock, flags);
+	status = itd_schedule (ehci, urb);
+	spin_unlock_irqrestore (&ehci->lock, flags);
+	if (status < 0)
+		itd_free_list (ehci, urb);
+
+	return status;
+}
+
+#ifdef have_split_iso
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * "Split ISO TDs" ... used for USB 1.1 devices going through
+ * the TTs in USB 2.0 hubs.
+ *
+ * FIXME not yet implemented
+ */
+
+#endif /* have_split_iso */
+
+/*-------------------------------------------------------------------------*/
+
+static void
+scan_periodic (struct ehci_hcd *ehci, struct pt_regs *regs)
+{
+	unsigned	frame, clock, now_uframe, mod;
+	unsigned	count = 0;
+
+	mod = ehci->periodic_size << 3;
+
+	/*
+	 * When running, scan from last scan point up to "now"
+	 * else clean up by scanning everything that's left.
+	 * Touches as few pages as possible:  cache-friendly.
+	 * Don't scan ISO entries more than once, though.
+	 */
+	frame = ehci->next_uframe >> 3;
+	if (HCD_IS_RUNNING (ehci->hcd.state))
+		now_uframe = readl (&ehci->regs->frame_index);
+	else
+		now_uframe = (frame << 3) - 1;
+	now_uframe %= mod;
+	clock = now_uframe >> 3;
+
+	for (;;) {
+		union ehci_shadow	q, *q_p;
+		u32			type, *hw_p;
+		unsigned		uframes;
+
+restart:
+		/* scan schedule to _before_ current frame index */
+		if (frame == clock)
+			uframes = now_uframe & 0x07;
+		else
+			uframes = 8;
+
+		q_p = &ehci->pshadow [frame];
+		hw_p = &ehci->periodic [frame];
+		q.ptr = q_p->ptr;
+		type = Q_NEXT_TYPE (*hw_p);
+
+		/* scan each element in frame's queue for completions */
+		while (q.ptr != 0) {
+			int			last;
+			unsigned		uf;
+			union ehci_shadow	temp;
+
+			switch (type) {
+			case Q_TYPE_QH:
+				last = (q.qh->hw_next == EHCI_LIST_END);
+				temp = q.qh->qh_next;
+				type = Q_NEXT_TYPE (q.qh->hw_next);
+				count += intr_complete (ehci, frame,
+						qh_get (q.qh), regs);
+				qh_put (ehci, q.qh);
+				q = temp;
+				break;
+			case Q_TYPE_FSTN:
+				last = (q.fstn->hw_next == EHCI_LIST_END);
+				/* for "save place" FSTNs, look at QH entries
+				 * in the previous frame for completions.
+				 */
+				if (q.fstn->hw_prev != EHCI_LIST_END) {
+					dbg ("ignoring completions from FSTNs");
+				}
+				type = Q_NEXT_TYPE (q.fstn->hw_next);
+				q = q.fstn->fstn_next;
+				break;
+			case Q_TYPE_ITD:
+				last = (q.itd->hw_next == EHCI_LIST_END);
+
+				/* Unlink each (S)ITD we see, since the ISO
+				 * URB model forces constant rescheduling.
+				 * That complicates sharing uframes in ITDs,
+				 * and means we need to skip uframes the HC
+				 * hasn't yet processed.
+				 */
+				for (uf = 0; uf < uframes; uf++) {
+					if (q.itd->hw_transaction [uf] != 0) {
+						temp = q;
+						*q_p = q.itd->itd_next;
+						*hw_p = q.itd->hw_next;
+						type = Q_NEXT_TYPE (*hw_p);
+
+						/* might free q.itd ... */
+						count += itd_complete (ehci,
+							temp.itd, uf, regs);
+						break;
+					}
+				}
+				/* we might skip this ITD's uframe ... */
+				if (uf == uframes) {
+					q_p = &q.itd->itd_next;
+					hw_p = &q.itd->hw_next;
+					type = Q_NEXT_TYPE (q.itd->hw_next);
+				}
+
+				q = *q_p;
+				break;
+#ifdef have_split_iso
+			case Q_TYPE_SITD:
+				last = (q.sitd->hw_next == EHCI_LIST_END);
+				sitd_complete (ehci, q.sitd);
+				type = Q_NEXT_TYPE (q.sitd->hw_next);
+
+				// FIXME unlink SITD after split completes
+				q = q.sitd->sitd_next;
+				break;
+#endif /* have_split_iso */
+			default:
+				dbg ("corrupt type %d frame %d shadow %p",
+					type, frame, q.ptr);
+				// BUG ();
+				last = 1;
+				q.ptr = 0;
+			}
+
+			/* did completion remove an interior q entry? */
+			if (unlikely (q.ptr == 0 && !last))
+				goto restart;
+		}
+
+		/* stop when we catch up to the HC */
+
+		// FIXME:  this assumes we won't get lapped when
+		// latencies climb; that should be rare, but...
+		// detect it, and just go all the way around.
+		// FLR might help detect this case, so long as latencies
+		// don't exceed periodic_size msec (default 1.024 sec).
+
+		// FIXME:  likewise assumes HC doesn't halt mid-scan
+
+		if (frame == clock) {
+			unsigned	now;
+
+			if (!HCD_IS_RUNNING (ehci->hcd.state))
+				break;
+			ehci->next_uframe = now_uframe;
+			now = readl (&ehci->regs->frame_index) % mod;
+			if (now_uframe == now)
+				break;
+
+			/* rescan the rest of this frame, then ... */
+			now_uframe = now;
+			clock = now_uframe >> 3;
+		} else
+			frame = (frame + 1) % ehci->periodic_size;
+	} 
+}
diff -urN linux-2.4.21-pre5/drivers/usb/host/ehci.h linux-2.4.21-pre6/drivers/usb/host/ehci.h
--- linux-2.4.21-pre5/drivers/usb/host/ehci.h	Wed Dec 31 16:00:00 1969
+++ linux-2.4.21-pre6/drivers/usb/host/ehci.h	Wed Mar 26 16:11:39 2003
@@ -0,0 +1,455 @@
+/*
+ * Copyright (c) 2001-2002 by David Brownell
+ * 
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __LINUX_EHCI_HCD_H
+#define __LINUX_EHCI_HCD_H
+
+/* definitions used for the EHCI driver */
+
+/* statistics can be kept for for tuning/monitoring */
+struct ehci_stats {
+	/* irq usage */
+	unsigned long		normal;
+	unsigned long		error;
+	unsigned long		reclaim;
+	unsigned long		lost_iaa;
+
+	/* termination of urbs from core */
+	unsigned long		complete;
+	unsigned long		unlink;
+};
+
+/* ehci_hcd->lock guards shared data against other CPUs:
+ *   ehci_hcd:	async, reclaim, periodic (and shadow), ...
+ *   hcd_dev:	ep[]
+ *   ehci_qh:	qh_next, qtd_list
+ *   ehci_qtd:	qtd_list
+ *
+ * Also, hold this lock when talking to HC registers or
+ * when updating hw_* fields in shared qh/qtd/... structures.
+ */
+
+#define	EHCI_MAX_ROOT_PORTS	15		/* see HCS_N_PORTS */
+
+struct ehci_hcd {			/* one per controller */
+	spinlock_t		lock;
+
+	/* async schedule support */
+	struct ehci_qh		*async;
+	struct ehci_qh		*reclaim;
+	int			reclaim_ready : 1,
+				async_idle : 1;
+
+	/* periodic schedule support */
+#define	DEFAULT_I_TDPS		1024		/* some HCs can do less */
+	unsigned		periodic_size;
+	u32			*periodic;	/* hw periodic table */
+	dma_addr_t		periodic_dma;
+	unsigned		i_thresh;	/* uframes HC might cache */
+
+	union ehci_shadow	*pshadow;	/* mirror hw periodic table */
+	int			next_uframe;	/* scan periodic, start here */
+	unsigned		periodic_sched;	/* periodic activity count */
+
+	/* per root hub port */
+	unsigned long		reset_done [EHCI_MAX_ROOT_PORTS];
+
+	/* glue to PCI and HCD framework */
+	struct usb_hcd		hcd;
+	struct ehci_caps	*caps;
+	struct ehci_regs	*regs;
+	u32			hcs_params;	/* cached register copy */
+
+	/* per-HC memory pools (could be per-PCI-bus, but ...) */
+	struct pci_pool		*qh_pool;	/* qh per active urb */
+	struct pci_pool		*qtd_pool;	/* one or more per qh */
+	struct pci_pool		*itd_pool;	/* itd per iso urb */
+	struct pci_pool		*sitd_pool;	/* sitd per split iso urb */
+
+	struct timer_list	watchdog;
+	struct notifier_block	reboot_notifier;
+	unsigned		stamp;
+
+	/* irq statistics */
+#ifdef EHCI_STATS
+	struct ehci_stats	stats;
+#	define COUNT(x) do { (x)++; } while (0)
+#else
+#	define COUNT(x) do {} while (0)
+#endif
+};
+
+/* unwrap an HCD pointer to get an EHCI_HCD pointer */ 
+#define hcd_to_ehci(hcd_ptr) container_of(hcd_ptr, struct ehci_hcd, hcd)
+
+/* NOTE:  urb->transfer_flags expected to not use this bit !!! */
+#define EHCI_STATE_UNLINK	0x8000		/* urb being unlinked */
+
+/*-------------------------------------------------------------------------*/
+
+/* EHCI register interface, corresponds to EHCI Revision 0.95 specification */
+
+/* Section 2.2 Host Controller Capability Registers */
+struct ehci_caps {
+	u8		length;		/* CAPLENGTH - size of this struct */
+	u8		reserved;       /* offset 0x1 */
+	u16		hci_version;    /* HCIVERSION - offset 0x2 */
+	u32		hcs_params;     /* HCSPARAMS - offset 0x4 */
+#define HCS_DEBUG_PORT(p)	(((p)>>20)&0xf)	/* bits 23:20, debug port? */
+#define HCS_INDICATOR(p)	((p)&(1 << 16))	/* true: has port indicators */
+#define HCS_N_CC(p)		(((p)>>12)&0xf)	/* bits 15:12, #companion HCs */
+#define HCS_N_PCC(p)		(((p)>>8)&0xf)	/* bits 11:8, ports per CC */
+#define HCS_PORTROUTED(p)	((p)&(1 << 7))	/* true: port routing */ 
+#define HCS_PPC(p)		((p)&(1 << 4))	/* true: port power control */ 
+#define HCS_N_PORTS(p)		(((p)>>0)&0xf)	/* bits 3:0, ports on HC */
+
+	u32		hcc_params;      /* HCCPARAMS - offset 0x8 */
+#define HCC_EXT_CAPS(p)		(((p)>>8)&0xff)	/* for pci extended caps */
+#define HCC_ISOC_CACHE(p)       ((p)&(1 << 7))  /* true: can cache isoc frame */
+#define HCC_ISOC_THRES(p)       (((p)>>4)&0x7)  /* bits 6:4, uframes cached */
+#define HCC_CANPARK(p)		((p)&(1 << 2))  /* true: can park on async qh */
+#define HCC_PGM_FRAMELISTLEN(p) ((p)&(1 << 1))  /* true: periodic_size changes*/
+#define HCC_64BIT_ADDR(p)       ((p)&(1))       /* true: can use 64-bit addr */
+	u8		portroute [8];	 /* nibbles for routing - offset 0xC */
+} __attribute__ ((packed));
+
+
+/* Section 2.3 Host Controller Operational Registers */
+struct ehci_regs {
+
+	/* USBCMD: offset 0x00 */
+	u32		command;
+/* 23:16 is r/w intr rate, in microframes; default "8" == 1/msec */
+#define CMD_PARK	(1<<11)		/* enable "park" on async qh */
+#define CMD_PARK_CNT(c)	(((c)>>8)&3)	/* how many transfers to park for */
+#define CMD_LRESET	(1<<7)		/* partial reset (no ports, etc) */
+#define CMD_IAAD	(1<<6)		/* "doorbell" interrupt async advance */
+#define CMD_ASE		(1<<5)		/* async schedule enable */
+#define CMD_PSE  	(1<<4)		/* periodic schedule enable */
+/* 3:2 is periodic frame list size */
+#define CMD_RESET	(1<<1)		/* reset HC not bus */
+#define CMD_RUN		(1<<0)		/* start/stop HC */
+
+	/* USBSTS: offset 0x04 */
+	u32		status;
+#define STS_ASS		(1<<15)		/* Async Schedule Status */
+#define STS_PSS		(1<<14)		/* Periodic Schedule Status */
+#define STS_RECL	(1<<13)		/* Reclamation */
+#define STS_HALT	(1<<12)		/* Not running (any reason) */
+/* some bits reserved */
+	/* these STS_* flags are also intr_enable bits (USBINTR) */
+#define STS_IAA		(1<<5)		/* Interrupted on async advance */
+#define STS_FATAL	(1<<4)		/* such as some PCI access errors */
+#define STS_FLR		(1<<3)		/* frame list rolled over */
+#define STS_PCD		(1<<2)		/* port change detect */
+#define STS_ERR		(1<<1)		/* "error" completion (overflow, ...) */
+#define STS_INT		(1<<0)		/* "normal" completion (short, ...) */
+
+	/* USBINTR: offset 0x08 */
+	u32		intr_enable;
+
+	/* FRINDEX: offset 0x0C */
+	u32		frame_index;	/* current microframe number */
+	/* CTRLDSSEGMENT: offset 0x10 */
+	u32		segment; 	/* address bits 63:32 if needed */
+	/* PERIODICLISTBASE: offset 0x14 */
+	u32		frame_list; 	/* points to periodic list */
+	/* ASYNCICLISTADDR: offset 0x18 */
+	u32		async_next;	/* address of next async queue head */
+
+	u32		reserved [9];
+
+	/* CONFIGFLAG: offset 0x40 */
+	u32		configured_flag;
+#define FLAG_CF		(1<<0)		/* true: we'll support "high speed" */
+
+	/* PORTSC: offset 0x44 */
+	u32		port_status [0];	/* up to N_PORTS */
+/* 31:23 reserved */
+#define PORT_WKOC_E	(1<<22)		/* wake on overcurrent (enable) */
+#define PORT_WKDISC_E	(1<<21)		/* wake on disconnect (enable) */
+#define PORT_WKCONN_E	(1<<20)		/* wake on connect (enable) */
+/* 19:16 for port testing */
+/* 15:14 for using port indicator leds (if HCS_INDICATOR allows) */
+#define PORT_OWNER	(1<<13)		/* true: companion hc owns this port */
+#define PORT_POWER	(1<<12)		/* true: has power (see PPC) */
+#define PORT_USB11(x) (((x)&(3<<10))==(1<<10))	/* USB 1.1 device */
+/* 11:10 for detecting lowspeed devices (reset vs release ownership) */
+/* 9 reserved */
+#define PORT_RESET	(1<<8)		/* reset port */
+#define PORT_SUSPEND	(1<<7)		/* suspend port */
+#define PORT_RESUME	(1<<6)		/* resume it */
+#define PORT_OCC	(1<<5)		/* over current change */
+#define PORT_OC		(1<<4)		/* over current active */
+#define PORT_PEC	(1<<3)		/* port enable change */
+#define PORT_PE		(1<<2)		/* port enable */
+#define PORT_CSC	(1<<1)		/* connect status change */
+#define PORT_CONNECT	(1<<0)		/* device connected */
+} __attribute__ ((packed));
+
+
+/*-------------------------------------------------------------------------*/
+
+#define	QTD_NEXT(dma)	cpu_to_le32((u32)dma)
+
+/*
+ * EHCI Specification 0.95 Section 3.5
+ * QTD: describe data transfer components (buffer, direction, ...) 
+ * See Fig 3-6 "Queue Element Transfer Descriptor Block Diagram".
+ *
+ * These are associated only with "QH" (Queue Head) structures,
+ * used with control, bulk, and interrupt transfers.
+ */
+struct ehci_qtd {
+	/* first part defined by EHCI spec */
+	u32			hw_next;	  /* see EHCI 3.5.1 */
+	u32			hw_alt_next;      /* see EHCI 3.5.2 */
+	u32			hw_token;         /* see EHCI 3.5.3 */       
+#define	QTD_TOGGLE	(1 << 31)	/* data toggle */
+#define	QTD_LENGTH(tok)	(((tok)>>16) & 0x7fff)
+#define	QTD_IOC		(1 << 15)	/* interrupt on complete */
+#define	QTD_CERR(tok)	(((tok)>>10) & 0x3)
+#define	QTD_PID(tok)	(((tok)>>8) & 0x3)
+#define	QTD_STS_ACTIVE	(1 << 7)	/* HC may execute this */
+#define	QTD_STS_HALT	(1 << 6)	/* halted on error */
+#define	QTD_STS_DBE	(1 << 5)	/* data buffer error (in HC) */
+#define	QTD_STS_BABBLE	(1 << 4)	/* device was babbling (qtd halted) */
+#define	QTD_STS_XACT	(1 << 3)	/* device gave illegal response */
+#define	QTD_STS_MMF	(1 << 2)	/* incomplete split transaction */
+#define	QTD_STS_STS	(1 << 1)	/* split transaction state */
+#define	QTD_STS_PING	(1 << 0)	/* issue PING? */
+	u32			hw_buf [5];        /* see EHCI 3.5.4 */
+	u32			hw_buf_hi [5];        /* Appendix B */
+
+	/* the rest is HCD-private */
+	dma_addr_t		qtd_dma;		/* qtd address */
+	struct list_head	qtd_list;		/* sw qtd list */
+	struct urb		*urb;			/* qtd's urb */
+	size_t			length;			/* length of buffer */
+} __attribute__ ((aligned (32)));
+
+#define QTD_MASK cpu_to_le32 (~0x1f)	/* mask NakCnt+T in qh->hw_alt_next */
+
+/*-------------------------------------------------------------------------*/
+
+/* type tag from {qh,itd,sitd,fstn}->hw_next */
+#define Q_NEXT_TYPE(dma) ((dma) & __constant_cpu_to_le32 (3 << 1))
+
+/* values for that type tag */
+#define Q_TYPE_ITD	__constant_cpu_to_le32 (0 << 1)
+#define Q_TYPE_QH	__constant_cpu_to_le32 (1 << 1)
+#define Q_TYPE_SITD 	__constant_cpu_to_le32 (2 << 1)
+#define Q_TYPE_FSTN 	__constant_cpu_to_le32 (3 << 1)
+
+/* next async queue entry, or pointer to interrupt/periodic QH */
+#define	QH_NEXT(dma)	(cpu_to_le32(((u32)dma)&~0x01f)|Q_TYPE_QH)
+
+/* for periodic/async schedules and qtd lists, mark end of list */
+#define	EHCI_LIST_END	__constant_cpu_to_le32(1) /* "null pointer" to hw */
+
+/*
+ * Entries in periodic shadow table are pointers to one of four kinds
+ * of data structure.  That's dictated by the hardware; a type tag is
+ * encoded in the low bits of the hardware's periodic schedule.  Use
+ * Q_NEXT_TYPE to get the tag.
+ *
+ * For entries in the async schedule, the type tag always says "qh".
+ */
+union ehci_shadow {
+	struct ehci_qh 		*qh;		/* Q_TYPE_QH */
+	struct ehci_itd		*itd;		/* Q_TYPE_ITD */
+	struct ehci_sitd	*sitd;		/* Q_TYPE_SITD */
+	struct ehci_fstn	*fstn;		/* Q_TYPE_FSTN */
+	void			*ptr;
+};
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * EHCI Specification 0.95 Section 3.6
+ * QH: describes control/bulk/interrupt endpoints
+ * See Fig 3-7 "Queue Head Structure Layout".
+ *
+ * These appear in both the async and (for interrupt) periodic schedules.
+ */
+
+struct ehci_qh {
+	/* first part defined by EHCI spec */
+	u32			hw_next;	 /* see EHCI 3.6.1 */
+	u32			hw_info1;        /* see EHCI 3.6.2 */
+#define	QH_HEAD		0x00008000
+	u32			hw_info2;        /* see EHCI 3.6.2 */
+	u32			hw_current;	 /* qtd list - see EHCI 3.6.4 */
+	
+	/* qtd overlay (hardware parts of a struct ehci_qtd) */
+	u32			hw_qtd_next;
+	u32			hw_alt_next;
+	u32			hw_token;
+	u32			hw_buf [5];
+	u32			hw_buf_hi [5];
+
+	/* the rest is HCD-private */
+	dma_addr_t		qh_dma;		/* address of qh */
+	union ehci_shadow	qh_next;	/* ptr to qh; or periodic */
+	struct list_head	qtd_list;	/* sw qtd list */
+	struct ehci_qtd		*dummy;
+	struct ehci_qh		*reclaim;	/* next to reclaim */
+
+	atomic_t		refcount;
+	unsigned		stamp;
+
+	u8			qh_state;
+#define	QH_STATE_LINKED		1		/* HC sees this */
+#define	QH_STATE_UNLINK		2		/* HC may still see this */
+#define	QH_STATE_IDLE		3		/* HC doesn't see this */
+#define	QH_STATE_UNLINK_WAIT	4		/* LINKED and on reclaim q */
+#define	QH_STATE_COMPLETING	5		/* don't touch token.HALT */
+
+	/* periodic schedule info */
+	u8			usecs;		/* intr bandwidth */
+	u8			gap_uf;		/* uframes split/csplit gap */
+	u8			c_usecs;	/* ... split completion bw */
+	unsigned short		period;		/* polling interval */
+	unsigned short		start;		/* where polling starts */
+#define NO_FRAME ((unsigned short)~0)			/* pick new start */
+
+} __attribute__ ((aligned (32)));
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * EHCI Specification 0.95 Section 3.3
+ * Fig 3-4 "Isochronous Transaction Descriptor (iTD)"
+ *
+ * Schedule records for high speed iso xfers
+ */
+struct ehci_itd {
+	/* first part defined by EHCI spec */
+	u32			hw_next;           /* see EHCI 3.3.1 */
+	u32			hw_transaction [8]; /* see EHCI 3.3.2 */
+#define EHCI_ISOC_ACTIVE        (1<<31)        /* activate transfer this slot */
+#define EHCI_ISOC_BUF_ERR       (1<<30)        /* Data buffer error */
+#define EHCI_ISOC_BABBLE        (1<<29)        /* babble detected */
+#define EHCI_ISOC_XACTERR       (1<<28)        /* XactErr - transaction error */
+#define	EHCI_ITD_LENGTH(tok)	(((tok)>>16) & 0x7fff)
+#define	EHCI_ITD_IOC		(1 << 15)	/* interrupt on complete */
+
+	u32			hw_bufp [7];	/* see EHCI 3.3.3 */ 
+	u32			hw_bufp_hi [7];	/* Appendix B */
+
+	/* the rest is HCD-private */
+	dma_addr_t		itd_dma;	/* for this itd */
+	union ehci_shadow	itd_next;	/* ptr to periodic q entry */
+
+	struct urb		*urb;
+	struct list_head	itd_list;	/* list of urb frames' itds */
+	dma_addr_t		buf_dma;	/* frame's buffer address */
+
+	/* for now, only one hw_transaction per itd */
+	u32			transaction;
+	u16			index;		/* in urb->iso_frame_desc */
+	u16			uframe;		/* in periodic schedule */
+	u16			usecs;
+} __attribute__ ((aligned (32)));
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * EHCI Specification 0.95 Section 3.4 
+ * siTD, aka split-transaction isochronous Transfer Descriptor
+ *       ... describe low/full speed iso xfers through TT in hubs
+ * see Figure 3-5 "Split-transaction Isochronous Transaction Descriptor (siTD)
+ */
+struct ehci_sitd {
+	/* first part defined by EHCI spec */
+	u32			hw_next;
+/* uses bit field macros above - see EHCI 0.95 Table 3-8 */
+	u32			hw_fullspeed_ep;  /* see EHCI table 3-9 */
+	u32                     hw_uframe;        /* see EHCI table 3-10 */
+        u32                     hw_tx_results1;   /* see EHCI table 3-11 */
+	u32                     hw_tx_results2;   /* see EHCI table 3-12 */
+	u32                     hw_tx_results3;   /* see EHCI table 3-12 */
+        u32                     hw_backpointer;   /* see EHCI table 3-13 */
+	u32			hw_buf_hi [2];	  /* Appendix B */
+
+	/* the rest is HCD-private */
+	dma_addr_t		sitd_dma;
+	union ehci_shadow	sitd_next;	/* ptr to periodic q entry */
+	struct urb		*urb;
+	dma_addr_t		buf_dma;	/* buffer address */
+
+	unsigned short		usecs;		/* start bandwidth */
+	unsigned short		c_usecs;	/* completion bandwidth */
+} __attribute__ ((aligned (32)));
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * EHCI Specification 0.96 Section 3.7
+ * Periodic Frame Span Traversal Node (FSTN)
+ *
+ * Manages split interrupt transactions (using TT) that span frame boundaries
+ * into uframes 0/1; see 4.12.2.2.  In those uframes, a "save place" FSTN
+ * makes the HC jump (back) to a QH to scan for fs/ls QH completions until
+ * it hits a "restore" FSTN; then it returns to finish other uframe 0/1 work.
+ */
+struct ehci_fstn {
+	u32			hw_next;	/* any periodic q entry */
+	u32			hw_prev;	/* qh or EHCI_LIST_END */
+
+	/* the rest is HCD-private */
+	dma_addr_t		fstn_dma;
+	union ehci_shadow	fstn_next;	/* ptr to periodic q entry */
+} __attribute__ ((aligned (32)));
+
+/*-------------------------------------------------------------------------*/
+
+#include <linux/version.h>
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,32)
+
+#define SUBMIT_URB(urb,mem_flags) usb_submit_urb(urb)
+#define STUB_DEBUG_FILES
+
+static inline int hcd_register_root (struct usb_hcd *hcd)
+{
+	return usb_new_device (hcd_to_bus (hcd)->root_hub);
+}
+
+#else	/* LINUX_VERSION_CODE */
+
+// hcd_to_bus() eventually moves to hcd.h on 2.5 too
+static inline struct usb_bus *hcd_to_bus (struct usb_hcd *hcd)
+	{ return &hcd->self; }
+// ... as does hcd_register_root()
+static inline int hcd_register_root (struct usb_hcd *hcd)
+{
+	return usb_register_root_hub (
+		hcd_to_bus (hcd)->root_hub, &hcd->pdev->dev);
+}
+
+#define SUBMIT_URB(urb,mem_flags) usb_submit_urb(urb,mem_flags)
+
+#ifndef DEBUG
+#define STUB_DEBUG_FILES
+#endif	/* DEBUG */
+
+#endif	/* LINUX_VERSION_CODE */
+
+/*-------------------------------------------------------------------------*/
+
+#endif /* __LINUX_EHCI_HCD_H */
diff -urN linux-2.4.21-pre5/drivers/usb/host/uhci-debug.h linux-2.4.21-pre6/drivers/usb/host/uhci-debug.h
--- linux-2.4.21-pre5/drivers/usb/host/uhci-debug.h	Wed Dec 31 16:00:00 1969
+++ linux-2.4.21-pre6/drivers/usb/host/uhci-debug.h	Wed Mar 26 16:11:39 2003
@@ -0,0 +1,573 @@
+/*
+ * UHCI-specific debugging code. Invaluable when something
+ * goes wrong, but don't get in my face.
+ *
+ * Kernel visible pointers are surrounded in []'s and bus
+ * visible pointers are surrounded in ()'s
+ *
+ * (C) Copyright 1999 Linus Torvalds
+ * (C) Copyright 1999-2001 Johannes Erdfelt
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/proc_fs.h>
+#include <asm/io.h>
+
+#include "uhci.h"
+
+/* Handle REALLY large printk's so we don't overflow buffers */
+static void inline lprintk(char *buf)
+{
+	char *p;
+
+	/* Just write one line at a time */
+	while (buf) {
+		p = strchr(buf, '\n');
+		if (p)
+			*p = 0;
+		printk("%s\n", buf);
+		buf = p;
+		if (buf)
+			buf++;
+	}
+}
+
+static int inline uhci_is_skeleton_td(struct uhci *uhci, struct uhci_td *td)
+{
+	int i;
+
+	for (i = 0; i < UHCI_NUM_SKELTD; i++)
+		if (td == uhci->skeltd[i])
+			return 1;
+
+	return 0;
+}
+
+static int inline uhci_is_skeleton_qh(struct uhci *uhci, struct uhci_qh *qh)
+{
+	int i;
+
+	for (i = 0; i < UHCI_NUM_SKELQH; i++)
+		if (qh == uhci->skelqh[i])
+			return 1;
+
+	return 0;
+}
+
+static int uhci_show_td(struct uhci_td *td, char *buf, int len, int space)
+{
+	char *out = buf;
+	char *spid;
+
+	/* Try to make sure there's enough memory */
+	if (len < 160)
+		return 0;
+
+	out += sprintf(out, "%*s[%p] link (%08x) ", space, "", td, td->link);
+	out += sprintf(out, "e%d %s%s%s%s%s%s%s%s%s%sLength=%x ",
+		((td->status >> 27) & 3),
+		(td->status & TD_CTRL_SPD) ?      "SPD " : "",
+		(td->status & TD_CTRL_LS) ?       "LS " : "",
+		(td->status & TD_CTRL_IOC) ?      "IOC " : "",
+		(td->status & TD_CTRL_ACTIVE) ?   "Active " : "",
+		(td->status & TD_CTRL_STALLED) ?  "Stalled " : "",
+		(td->status & TD_CTRL_DBUFERR) ?  "DataBufErr " : "",
+		(td->status & TD_CTRL_BABBLE) ?   "Babble " : "",
+		(td->status & TD_CTRL_NAK) ?      "NAK " : "",
+		(td->status & TD_CTRL_CRCTIMEO) ? "CRC/Timeo " : "",
+		(td->status & TD_CTRL_BITSTUFF) ? "BitStuff " : "",
+		td->status & 0x7ff);
+
+	switch (td->info & 0xff) {
+		case USB_PID_SETUP:
+			spid = "SETUP";
+			break;
+		case USB_PID_OUT:
+			spid = "OUT";
+			break;
+		case USB_PID_IN:
+			spid = "IN";
+			break;
+		default:
+			spid = "?";
+			break;
+	}
+
+	out += sprintf(out, "MaxLen=%x DT%d EndPt=%x Dev=%x, PID=%x(%s) ",
+		td->info >> 21,
+		((td->info >> 19) & 1),
+		(td->info >> 15) & 15,
+		(td->info >> 8) & 127,
+		(td->info & 0xff),
+		spid);
+	out += sprintf(out, "(buf=%08x)\n", td->buffer);
+
+	return out - buf;
+}
+
+static int uhci_show_sc(int port, unsigned short status, char *buf, int len)
+{
+	char *out = buf;
+
+	/* Try to make sure there's enough memory */
+	if (len < 80)
+		return 0;
+
+	out += sprintf(out, "  stat%d     =     %04x   %s%s%s%s%s%s%s%s\n",
+		port,
+		status,
+		(status & USBPORTSC_SUSP) ? "PortSuspend " : "",
+		(status & USBPORTSC_PR) ?   "PortReset " : "",
+		(status & USBPORTSC_LSDA) ? "LowSpeed " : "",
+		(status & USBPORTSC_RD) ?   "ResumeDetect " : "",
+		(status & USBPORTSC_PEC) ?  "EnableChange " : "",
+		(status & USBPORTSC_PE) ?   "PortEnabled " : "",
+		(status & USBPORTSC_CSC) ?  "ConnectChange " : "",
+		(status & USBPORTSC_CCS) ?  "PortConnected " : "");
+
+	return out - buf;
+}
+
+static int uhci_show_status(struct uhci *uhci, char *buf, int len)
+{
+	char *out = buf;
+	unsigned int io_addr = uhci->io_addr;
+	unsigned short usbcmd, usbstat, usbint, usbfrnum;
+	unsigned int flbaseadd;
+	unsigned char sof;
+	unsigned short portsc1, portsc2;
+
+	/* Try to make sure there's enough memory */
+	if (len < 80 * 6)
+		return 0;
+
+	usbcmd    = inw(io_addr + 0);
+	usbstat   = inw(io_addr + 2);
+	usbint    = inw(io_addr + 4);
+	usbfrnum  = inw(io_addr + 6);
+	flbaseadd = inl(io_addr + 8);
+	sof       = inb(io_addr + 12);
+	portsc1   = inw(io_addr + 16);
+	portsc2   = inw(io_addr + 18);
+
+	out += sprintf(out, "  usbcmd    =     %04x   %s%s%s%s%s%s%s%s\n",
+		usbcmd,
+		(usbcmd & USBCMD_MAXP) ?    "Maxp64 " : "Maxp32 ",
+		(usbcmd & USBCMD_CF) ?      "CF " : "",
+		(usbcmd & USBCMD_SWDBG) ?   "SWDBG " : "",
+		(usbcmd & USBCMD_FGR) ?     "FGR " : "",
+		(usbcmd & USBCMD_EGSM) ?    "EGSM " : "",
+		(usbcmd & USBCMD_GRESET) ?  "GRESET " : "",
+		(usbcmd & USBCMD_HCRESET) ? "HCRESET " : "",
+		(usbcmd & USBCMD_RS) ?      "RS " : "");
+
+	out += sprintf(out, "  usbstat   =     %04x   %s%s%s%s%s%s\n",
+		usbstat,
+		(usbstat & USBSTS_HCH) ?    "HCHalted " : "",
+		(usbstat & USBSTS_HCPE) ?   "HostControllerProcessError " : "",
+		(usbstat & USBSTS_HSE) ?    "HostSystemError " : "",
+		(usbstat & USBSTS_RD) ?     "ResumeDetect " : "",
+		(usbstat & USBSTS_ERROR) ?  "USBError " : "",
+		(usbstat & USBSTS_USBINT) ? "USBINT " : "");
+
+	out += sprintf(out, "  usbint    =     %04x\n", usbint);
+	out += sprintf(out, "  usbfrnum  =   (%d)%03x\n", (usbfrnum >> 10) & 1,
+		0xfff & (4*(unsigned int)usbfrnum));
+	out += sprintf(out, "  flbaseadd = %08x\n", flbaseadd);
+	out += sprintf(out, "  sof       =       %02x\n", sof);
+	out += uhci_show_sc(1, portsc1, out, len - (out - buf));
+	out += uhci_show_sc(2, portsc2, out, len - (out - buf));
+
+	return out - buf;
+}
+
+static int uhci_show_qh(struct uhci_qh *qh, char *buf, int len, int space)
+{
+	char *out = buf;
+	struct urb_priv *urbp;
+	struct list_head *head, *tmp;
+	struct uhci_td *td;
+	int i = 0, checked = 0, prevactive = 0;
+
+	/* Try to make sure there's enough memory */
+	if (len < 80 * 6)
+		return 0;
+
+	out += sprintf(out, "%*s[%p] link (%08x) element (%08x)\n", space, "",
+			qh, qh->link, qh->element);
+
+	if (qh->element & UHCI_PTR_QH)
+		out += sprintf(out, "%*s  Element points to QH (bug?)\n", space, "");
+
+	if (qh->element & UHCI_PTR_DEPTH)
+		out += sprintf(out, "%*s  Depth traverse\n", space, "");
+
+	if (qh->element & 8)
+		out += sprintf(out, "%*s  Bit 3 set (bug?)\n", space, "");
+
+	if (!(qh->element & ~(UHCI_PTR_QH | UHCI_PTR_DEPTH)))
+		out += sprintf(out, "%*s  Element is NULL (bug?)\n", space, "");
+
+	if (!qh->urbp) {
+		out += sprintf(out, "%*s  urbp == NULL\n", space, "");
+		goto out;
+	}
+
+	urbp = qh->urbp;
+
+	head = &urbp->td_list;
+	tmp = head->next;
+
+	td = list_entry(tmp, struct uhci_td, list);
+
+	if (td->dma_handle != (qh->element & ~UHCI_PTR_BITS))
+		out += sprintf(out, "%*s Element != First TD\n", space, "");
+
+	while (tmp != head) {
+		struct uhci_td *td = list_entry(tmp, struct uhci_td, list);
+
+		tmp = tmp->next;
+
+		out += sprintf(out, "%*s%d: ", space + 2, "", i++);
+		out += uhci_show_td(td, out, len - (out - buf), 0);
+
+		if (i > 10 && !checked && prevactive && tmp != head &&
+		    debug <= 2) {
+			struct list_head *ntmp = tmp;
+			struct uhci_td *ntd = td;
+			int active = 1, ni = i;
+
+			checked = 1;
+
+			while (ntmp != head && ntmp->next != head && active) {
+				ntd = list_entry(ntmp, struct uhci_td, list);
+
+				ntmp = ntmp->next;
+
+				active = ntd->status & TD_CTRL_ACTIVE;
+
+				ni++;
+			}
+
+			if (active && ni > i) {
+				out += sprintf(out, "%*s[skipped %d active TD's]\n", space, "", ni - i);
+				tmp = ntmp;
+				td = ntd;
+				i = ni;
+			}
+		}
+
+		prevactive = td->status & TD_CTRL_ACTIVE;
+	}
+
+	if (list_empty(&urbp->queue_list) || urbp->queued)
+		goto out;
+
+	out += sprintf(out, "%*sQueued QH's:\n", -space, "--");
+
+	head = &urbp->queue_list;
+	tmp = head->next;
+
+	while (tmp != head) {
+		struct urb_priv *nurbp = list_entry(tmp, struct urb_priv,
+						queue_list);
+		tmp = tmp->next;
+
+		out += uhci_show_qh(nurbp->qh, out, len - (out - buf), space);
+	}
+
+out:
+	return out - buf;
+}
+
+static const char *td_names[] = {"skel_int1_td", "skel_int2_td",
+				 "skel_int4_td", "skel_int8_td",
+				 "skel_int16_td", "skel_int32_td",
+				 "skel_int64_td", "skel_int128_td",
+				 "skel_int256_td", "skel_term_td" };
+static const char *qh_names[] = { "skel_ls_control_qh", "skel_hs_control_qh",
+				  "skel_bulk_qh", "skel_term_qh" };
+
+#define show_frame_num()	\
+	if (!shown) {		\
+	  shown = 1;		\
+	  out += sprintf(out, "- Frame %d\n", i); \
+	}
+
+#define show_td_name()		\
+	if (!shown) {		\
+	  shown = 1;		\
+	  out += sprintf(out, "- %s\n", td_names[i]); \
+	}
+
+#define show_qh_name()		\
+	if (!shown) {		\
+	  shown = 1;		\
+	  out += sprintf(out, "- %s\n", qh_names[i]); \
+	}
+
+static int uhci_sprint_schedule(struct uhci *uhci, char *buf, int len)
+{
+	char *out = buf;
+	int i;
+	struct uhci_qh *qh;
+	struct uhci_td *td;
+	struct list_head *tmp, *head;
+
+	out += sprintf(out, "HC status\n");
+	out += uhci_show_status(uhci, out, len - (out - buf));
+
+	out += sprintf(out, "Frame List\n");
+	for (i = 0; i < UHCI_NUMFRAMES; ++i) {
+		int shown = 0;
+		td = uhci->fl->frame_cpu[i];
+		if (!td)
+			continue;
+
+		if (td->dma_handle != (dma_addr_t)uhci->fl->frame[i]) {
+			show_frame_num();
+			out += sprintf(out, "    frame list does not match td->dma_handle!\n");
+		}
+		if (uhci_is_skeleton_td(uhci, td))
+			continue;
+		show_frame_num();
+
+		head = &td->fl_list;
+		tmp = head;
+		do {
+			td = list_entry(tmp, struct uhci_td, fl_list);
+			tmp = tmp->next;
+			out += uhci_show_td(td, out, len - (out - buf), 4);
+		} while (tmp != head);
+	}
+
+	out += sprintf(out, "Skeleton TD's\n");
+	for (i = UHCI_NUM_SKELTD - 1; i >= 0; i--) {
+		int shown = 0;
+
+		td = uhci->skeltd[i];
+
+		if (debug > 1) {
+			show_td_name();
+			out += uhci_show_td(td, out, len - (out - buf), 4);
+		}
+
+		if (list_empty(&td->fl_list)) {
+			/* TD 0 is the int1 TD and links to control_ls_qh */
+			if (!i) {
+				if (td->link !=
+				    (uhci->skel_ls_control_qh->dma_handle | UHCI_PTR_QH)) {
+					show_td_name();
+					out += sprintf(out, "    skeleton TD not linked to ls_control QH!\n");
+				}
+			} else if (i < 9) {
+				if (td->link != uhci->skeltd[i - 1]->dma_handle) {
+					show_td_name();
+					out += sprintf(out, "    skeleton TD not linked to next skeleton TD!\n");
+				}
+			} else {
+				show_td_name();
+
+				if (td->link != td->dma_handle)
+					out += sprintf(out, "    skel_term_td does not link to self\n");
+
+				/* Don't show it twice */
+				if (debug <= 1)
+					out += uhci_show_td(td, out, len - (out - buf), 4);
+			}
+
+			continue;
+		}
+
+		show_td_name();
+
+		head = &td->fl_list;
+		tmp = head->next;
+
+		while (tmp != head) {
+			td = list_entry(tmp, struct uhci_td, fl_list);
+
+			tmp = tmp->next;
+
+			out += uhci_show_td(td, out, len - (out - buf), 4);
+		}
+
+		if (!i) {
+			if (td->link !=
+			    (uhci->skel_ls_control_qh->dma_handle | UHCI_PTR_QH))
+				out += sprintf(out, "    last TD not linked to ls_control QH!\n");
+		} else if (i < 9) {
+			if (td->link != uhci->skeltd[i - 1]->dma_handle)
+				out += sprintf(out, "    last TD not linked to next skeleton!\n");
+		}
+	}
+
+	out += sprintf(out, "Skeleton QH's\n");
+
+	for (i = 0; i < UHCI_NUM_SKELQH; ++i) {
+		int shown = 0;
+
+		qh = uhci->skelqh[i];
+
+		if (debug > 1) {
+			show_qh_name();
+			out += uhci_show_qh(qh, out, len - (out - buf), 4);
+		}
+
+		/* QH 3 is the Terminating QH, it's different */
+		if (i == 3) {
+			if (qh->link != UHCI_PTR_TERM) {
+				show_qh_name();
+				out += sprintf(out, "    bandwidth reclamation on!\n");
+			}
+
+			if (qh->element != uhci->skel_term_td->dma_handle) {
+				show_qh_name();
+				out += sprintf(out, "    skel_term_qh element is not set to skel_term_td\n");
+			}
+		}
+
+		if (list_empty(&qh->list)) {
+			if (i < 3) {
+				if (qh->link !=
+				    (uhci->skelqh[i + 1]->dma_handle | UHCI_PTR_QH)) {
+					show_qh_name();
+					out += sprintf(out, "    skeleton QH not linked to next skeleton QH!\n");
+				}
+			}
+
+			continue;
+		}
+
+		show_qh_name();
+
+		head = &qh->list;
+		tmp = head->next;
+
+		while (tmp != head) {
+			qh = list_entry(tmp, struct uhci_qh, list);
+
+			tmp = tmp->next;
+
+			out += uhci_show_qh(qh, out, len - (out - buf), 4);
+		}
+
+		if (i < 3) {
+			if (qh->link !=
+			    (uhci->skelqh[i + 1]->dma_handle | UHCI_PTR_QH))
+				out += sprintf(out, "    last QH not linked to next skeleton!\n");
+		}
+	}
+
+	return out - buf;
+}
+
+#ifdef CONFIG_PROC_FS
+#define MAX_OUTPUT	(PAGE_SIZE * 8)
+
+static struct proc_dir_entry *uhci_proc_root = NULL;
+
+struct uhci_proc {
+	int size;
+	char *data;
+	struct uhci *uhci;
+};
+
+static int uhci_proc_open(struct inode *inode, struct file *file)
+{
+	const struct proc_dir_entry *dp = inode->u.generic_ip;
+	struct uhci *uhci = dp->data;
+	struct uhci_proc *up;
+	unsigned long flags;
+	int ret = -ENOMEM;
+
+	lock_kernel();
+	up = kmalloc(sizeof(*up), GFP_KERNEL);
+	if (!up)
+		goto out;
+
+	up->data = kmalloc(MAX_OUTPUT, GFP_KERNEL);
+	if (!up->data) {
+		kfree(up);
+		goto out;
+	}
+
+	spin_lock_irqsave(&uhci->frame_list_lock, flags);
+	up->size = uhci_sprint_schedule(uhci, up->data, MAX_OUTPUT);
+	spin_unlock_irqrestore(&uhci->frame_list_lock, flags);
+
+	file->private_data = up;
+
+	ret = 0;
+out:
+	unlock_kernel();
+	return ret;
+}
+
+static loff_t uhci_proc_lseek(struct file *file, loff_t off, int whence)
+{
+	struct uhci_proc *up = file->private_data;
+	loff_t new;
+
+	switch (whence) {
+	case 0:
+		new = off;
+		break;
+	case 1:
+		new = file->f_pos + off;
+		break;
+	case 2:
+	default:
+		return -EINVAL;
+	}
+	if (new < 0 || new > up->size)
+		return -EINVAL;
+	return (file->f_pos = new);
+}
+
+static ssize_t uhci_proc_read(struct file *file, char *buf, size_t nbytes,
+			loff_t *ppos)
+{
+	struct uhci_proc *up = file->private_data;
+	unsigned int pos;
+	unsigned int size;
+
+	pos = *ppos;
+	size = up->size;
+	if (pos >= size)
+		return 0;
+	if (nbytes >= size)
+		nbytes = size;
+	if (pos + nbytes > size)
+		nbytes = size - pos;
+
+	if (!access_ok(VERIFY_WRITE, buf, nbytes))
+		return -EINVAL;
+
+	copy_to_user(buf, up->data + pos, nbytes);
+
+	*ppos += nbytes;
+
+	return nbytes;
+}
+
+static int uhci_proc_release(struct inode *inode, struct file *file)
+{
+	struct uhci_proc *up = file->private_data;
+
+	kfree(up->data);
+	kfree(up);
+
+	return 0;
+}
+
+static struct file_operations uhci_proc_operations = {
+	open:		uhci_proc_open,
+	llseek:		uhci_proc_lseek,
+	read:		uhci_proc_read,
+//	write:		uhci_proc_write,
+	release:	uhci_proc_release,
+};
+#endif
+
diff -urN linux-2.4.21-pre5/drivers/usb/host/uhci.c linux-2.4.21-pre6/drivers/usb/host/uhci.c
--- linux-2.4.21-pre5/drivers/usb/host/uhci.c	Wed Dec 31 16:00:00 1969
+++ linux-2.4.21-pre6/drivers/usb/host/uhci.c	Wed Mar 26 16:11:39 2003
@@ -0,0 +1,3172 @@
+/*
+ * Universal Host Controller Interface driver for USB.
+ *
+ * Maintainer: Johannes Erdfelt <johannes@erdfelt.com>
+ *
+ * (C) Copyright 1999 Linus Torvalds
+ * (C) Copyright 1999-2002 Johannes Erdfelt, johannes@erdfelt.com
+ * (C) Copyright 1999 Randy Dunlap
+ * (C) Copyright 1999 Georg Acher, acher@in.tum.de
+ * (C) Copyright 1999 Deti Fliegl, deti@fliegl.de
+ * (C) Copyright 1999 Thomas Sailer, sailer@ife.ee.ethz.ch
+ * (C) Copyright 1999 Roman Weissgaerber, weissg@vienna.at
+ * (C) Copyright 2000 Yggdrasil Computing, Inc. (port of new PCI interface
+ *               support from usb-ohci.c by Adam Richter, adam@yggdrasil.com).
+ * (C) Copyright 1999 Gregory P. Smith (from usb-ohci.c)
+ *
+ * Intel documents this fairly well, and as far as I know there
+ * are no royalties or anything like that, but even so there are
+ * people who decided that they want to do the same thing in a
+ * completely different way.
+ *
+ * WARNING! The USB documentation is downright evil. Most of it
+ * is just crap, written by a committee. You're better off ignoring
+ * most of it, the important stuff is:
+ *  - the low-level protocol (fairly simple but lots of small details)
+ *  - working around the horridness of the rest
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+#include <linux/errno.h>
+#include <linux/unistd.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/proc_fs.h>
+#ifdef CONFIG_USB_DEBUG
+#define DEBUG
+#else
+#undef DEBUG
+#endif
+#include <linux/usb.h>
+
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+
+#include "uhci.h"
+
+#include <linux/pm.h>
+
+#include "../hcd.h"
+
+/*
+ * Version Information
+ */
+#define DRIVER_VERSION "v1.1"
+#define DRIVER_AUTHOR "Linus 'Frodo Rabbit' Torvalds, Johannes Erdfelt, Randy Dunlap, Georg Acher, Deti Fliegl, Thomas Sailer, Roman Weissgaerber"
+#define DRIVER_DESC "USB Universal Host Controller Interface driver"
+
+/*
+ * debug = 0, no debugging messages
+ * debug = 1, dump failed URB's except for stalls
+ * debug = 2, dump all failed URB's (including stalls)
+ *            show all queues in /proc/uhci/hc*
+ * debug = 3, show all TD's in URB's when dumping
+ */
+#ifdef DEBUG
+static int debug = 1;
+#else
+static int debug = 0;
+#endif
+MODULE_PARM(debug, "i");
+MODULE_PARM_DESC(debug, "Debug level");
+static char *errbuf;
+#define ERRBUF_LEN    (PAGE_SIZE * 8)
+
+#include "uhci-debug.h"
+
+static kmem_cache_t *uhci_up_cachep;	/* urb_priv */
+
+static int rh_submit_urb(struct urb *urb);
+static int rh_unlink_urb(struct urb *urb);
+static int uhci_get_current_frame_number(struct usb_device *dev);
+static int uhci_unlink_urb(struct urb *urb);
+static void uhci_unlink_generic(struct uhci *uhci, struct urb *urb);
+static void uhci_call_completion(struct urb *urb);
+
+static int  ports_active(struct uhci *uhci);
+static void suspend_hc(struct uhci *uhci);
+static void wakeup_hc(struct uhci *uhci);
+
+/* If a transfer is still active after this much time, turn off FSBR */
+#define IDLE_TIMEOUT	(HZ / 20)	/* 50 ms */
+#define FSBR_DELAY	(HZ / 20)	/* 50 ms */
+
+/* When we timeout an idle transfer for FSBR, we'll switch it over to */
+/* depth first traversal. We'll do it in groups of this number of TD's */
+/* to make sure it doesn't hog all of the bandwidth */
+#define DEPTH_INTERVAL	5
+
+#define MAX_URB_LOOP	2048		/* Maximum number of linked URB's */
+
+/*
+ * Only the USB core should call uhci_alloc_dev and uhci_free_dev
+ */
+static int uhci_alloc_dev(struct usb_device *dev)
+{
+	return 0;
+}
+
+static int uhci_free_dev(struct usb_device *dev)
+{
+	return 0;
+}
+
+/*
+ * Technically, updating td->status here is a race, but it's not really a
+ * problem. The worst that can happen is that we set the IOC bit again
+ * generating a spurios interrupt. We could fix this by creating another
+ * QH and leaving the IOC bit always set, but then we would have to play
+ * games with the FSBR code to make sure we get the correct order in all
+ * the cases. I don't think it's worth the effort
+ */
+static inline void uhci_set_next_interrupt(struct uhci *uhci)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&uhci->frame_list_lock, flags);
+	uhci->skel_term_td->status |= TD_CTRL_IOC;
+	spin_unlock_irqrestore(&uhci->frame_list_lock, flags);
+}
+
+static inline void uhci_clear_next_interrupt(struct uhci *uhci)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&uhci->frame_list_lock, flags);
+	uhci->skel_term_td->status &= ~TD_CTRL_IOC;
+	spin_unlock_irqrestore(&uhci->frame_list_lock, flags);
+}
+
+static inline void uhci_add_complete(struct urb *urb)
+{
+	struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv;
+	struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
+	unsigned long flags;
+
+	spin_lock_irqsave(&uhci->complete_list_lock, flags);
+	list_add(&urbp->complete_list, &uhci->complete_list);
+	spin_unlock_irqrestore(&uhci->complete_list_lock, flags);
+}
+
+static struct uhci_td *uhci_alloc_td(struct uhci *uhci, struct usb_device *dev)
+{
+	dma_addr_t dma_handle;
+	struct uhci_td *td;
+
+	td = pci_pool_alloc(uhci->td_pool, GFP_DMA | GFP_ATOMIC, &dma_handle);
+	if (!td)
+		return NULL;
+
+	td->dma_handle = dma_handle;
+
+	td->link = UHCI_PTR_TERM;
+	td->buffer = 0;
+
+	td->frame = -1;
+	td->dev = dev;
+
+	INIT_LIST_HEAD(&td->list);
+	INIT_LIST_HEAD(&td->fl_list);
+
+	usb_inc_dev_use(dev);
+
+	return td;
+}
+
+static void inline uhci_fill_td(struct uhci_td *td, __u32 status,
+		__u32 info, __u32 buffer)
+{
+	td->status = status;
+	td->info = info;
+	td->buffer = buffer;
+}
+
+static void uhci_insert_td(struct uhci *uhci, struct uhci_td *skeltd, struct uhci_td *td)
+{
+	unsigned long flags;
+	struct uhci_td *ltd;
+
+	spin_lock_irqsave(&uhci->frame_list_lock, flags);
+
+	ltd = list_entry(skeltd->fl_list.prev, struct uhci_td, fl_list);
+
+	td->link = ltd->link;
+	mb();
+	ltd->link = td->dma_handle;
+
+	list_add_tail(&td->fl_list, &skeltd->fl_list);
+
+	spin_unlock_irqrestore(&uhci->frame_list_lock, flags);
+}
+
+/*
+ * We insert Isochronous transfers directly into the frame list at the
+ * beginning
+ * The layout looks as follows:
+ * frame list pointer -> iso td's (if any) ->
+ * periodic interrupt td (if frame 0) -> irq td's -> control qh -> bulk qh
+ */
+static void uhci_insert_td_frame_list(struct uhci *uhci, struct uhci_td *td, unsigned framenum)
+{
+	unsigned long flags;
+
+	framenum %= UHCI_NUMFRAMES;
+
+	spin_lock_irqsave(&uhci->frame_list_lock, flags);
+
+	td->frame = framenum;
+
+	/* Is there a TD already mapped there? */
+	if (uhci->fl->frame_cpu[framenum]) {
+		struct uhci_td *ftd, *ltd;
+
+		ftd = uhci->fl->frame_cpu[framenum];
+		ltd = list_entry(ftd->fl_list.prev, struct uhci_td, fl_list);
+
+		list_add_tail(&td->fl_list, &ftd->fl_list);
+
+		td->link = ltd->link;
+		mb();
+		ltd->link = td->dma_handle;
+	} else {
+		td->link = uhci->fl->frame[framenum];
+		mb();
+		uhci->fl->frame[framenum] = td->dma_handle;
+		uhci->fl->frame_cpu[framenum] = td;
+	}
+
+	spin_unlock_irqrestore(&uhci->frame_list_lock, flags);
+}
+
+static void uhci_remove_td(struct uhci *uhci, struct uhci_td *td)
+{
+	unsigned long flags;
+
+	/* If it's not inserted, don't remove it */
+	spin_lock_irqsave(&uhci->frame_list_lock, flags);
+	if (td->frame == -1 && list_empty(&td->fl_list))
+		goto out;
+
+	if (td->frame != -1 && uhci->fl->frame_cpu[td->frame] == td) {
+		if (list_empty(&td->fl_list)) {
+			uhci->fl->frame[td->frame] = td->link;
+			uhci->fl->frame_cpu[td->frame] = NULL;
+		} else {
+			struct uhci_td *ntd;
+
+			ntd = list_entry(td->fl_list.next, struct uhci_td, fl_list);
+			uhci->fl->frame[td->frame] = ntd->dma_handle;
+			uhci->fl->frame_cpu[td->frame] = ntd;
+		}
+	} else {
+		struct uhci_td *ptd;
+
+		ptd = list_entry(td->fl_list.prev, struct uhci_td, fl_list);
+		ptd->link = td->link;
+	}
+
+	mb();
+	td->link = UHCI_PTR_TERM;
+
+	list_del_init(&td->fl_list);
+	td->frame = -1;
+
+out:
+	spin_unlock_irqrestore(&uhci->frame_list_lock, flags);
+}
+
+/*
+ * Inserts a td into qh list at the top.
+ */
+static void uhci_insert_tds_in_qh(struct uhci_qh *qh, struct urb *urb, int breadth)
+{
+	struct list_head *tmp, *head;
+	struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
+	struct uhci_td *td, *ptd;
+
+	if (list_empty(&urbp->td_list))
+		return;
+
+	head = &urbp->td_list;
+	tmp = head->next;
+
+	/* Ordering isn't important here yet since the QH hasn't been */
+	/*  inserted into the schedule yet */
+	td = list_entry(tmp, struct uhci_td, list);
+
+	/* Add the first TD to the QH element pointer */
+	qh->element = td->dma_handle | (breadth ? 0 : UHCI_PTR_DEPTH);
+
+	ptd = td;
+
+	/* Then link the rest of the TD's */
+	tmp = tmp->next;
+	while (tmp != head) {
+		td = list_entry(tmp, struct uhci_td, list);
+
+		tmp = tmp->next;
+
+		ptd->link = td->dma_handle | (breadth ? 0 : UHCI_PTR_DEPTH);
+
+		ptd = td;
+	}
+
+	ptd->link = UHCI_PTR_TERM;
+}
+
+static void uhci_free_td(struct uhci *uhci, struct uhci_td *td)
+{
+	if (!list_empty(&td->list) || !list_empty(&td->fl_list))
+		dbg("td is still in URB list!");
+
+	if (td->dev)
+		usb_dec_dev_use(td->dev);
+
+	pci_pool_free(uhci->td_pool, td, td->dma_handle);
+}
+
+static struct uhci_qh *uhci_alloc_qh(struct uhci *uhci, struct usb_device *dev)
+{
+	dma_addr_t dma_handle;
+	struct uhci_qh *qh;
+
+	qh = pci_pool_alloc(uhci->qh_pool, GFP_DMA | GFP_ATOMIC, &dma_handle);
+	if (!qh)
+		return NULL;
+
+	qh->dma_handle = dma_handle;
+
+	qh->element = UHCI_PTR_TERM;
+	qh->link = UHCI_PTR_TERM;
+
+	qh->dev = dev;
+	qh->urbp = NULL;
+
+	INIT_LIST_HEAD(&qh->list);
+	INIT_LIST_HEAD(&qh->remove_list);
+
+	usb_inc_dev_use(dev);
+
+	return qh;
+}
+
+static void uhci_free_qh(struct uhci *uhci, struct uhci_qh *qh)
+{
+	if (!list_empty(&qh->list))
+		dbg("qh list not empty!");
+	if (!list_empty(&qh->remove_list))
+		dbg("qh still in remove_list!");
+
+	if (qh->dev)
+		usb_dec_dev_use(qh->dev);
+
+	pci_pool_free(uhci->qh_pool, qh, qh->dma_handle);
+}
+
+/*
+ * MUST be called with uhci->frame_list_lock acquired
+ */
+static void _uhci_insert_qh(struct uhci *uhci, struct uhci_qh *skelqh, struct urb *urb)
+{
+	struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
+	struct list_head *head, *tmp;
+	struct uhci_qh *lqh;
+
+	/* Grab the last QH */
+	lqh = list_entry(skelqh->list.prev, struct uhci_qh, list);
+
+	if (lqh->urbp) {
+		head = &lqh->urbp->queue_list;
+		tmp = head->next;
+		while (head != tmp) {
+			struct urb_priv *turbp =
+				list_entry(tmp, struct urb_priv, queue_list);
+
+			tmp = tmp->next;
+
+			turbp->qh->link = urbp->qh->dma_handle | UHCI_PTR_QH;
+		}
+	}
+
+	head = &urbp->queue_list;
+	tmp = head->next;
+	while (head != tmp) {
+		struct urb_priv *turbp =
+			list_entry(tmp, struct urb_priv, queue_list);
+
+		tmp = tmp->next;
+
+		turbp->qh->link = lqh->link;
+	}
+
+	urbp->qh->link = lqh->link;
+	mb();				/* Ordering is important */
+	lqh->link = urbp->qh->dma_handle | UHCI_PTR_QH;
+
+	list_add_tail(&urbp->qh->list, &skelqh->list);
+}
+
+static void uhci_insert_qh(struct uhci *uhci, struct uhci_qh *skelqh, struct urb *urb)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&uhci->frame_list_lock, flags);
+	_uhci_insert_qh(uhci, skelqh, urb);
+	spin_unlock_irqrestore(&uhci->frame_list_lock, flags);
+}
+
+static void uhci_remove_qh(struct uhci *uhci, struct uhci_qh *qh)
+{
+	unsigned long flags;
+	struct uhci_qh *pqh;
+
+	if (!qh)
+		return;
+
+	qh->urbp = NULL;
+
+	/* Only go through the hoops if it's actually linked in */
+	spin_lock_irqsave(&uhci->frame_list_lock, flags);
+	if (!list_empty(&qh->list)) {
+		pqh = list_entry(qh->list.prev, struct uhci_qh, list);
+
+		if (pqh->urbp) {
+			struct list_head *head, *tmp;
+
+			head = &pqh->urbp->queue_list;
+			tmp = head->next;
+			while (head != tmp) {
+				struct urb_priv *turbp =
+					list_entry(tmp, struct urb_priv, queue_list);
+
+				tmp = tmp->next;
+
+				turbp->qh->link = qh->link;
+			}
+		}
+
+		pqh->link = qh->link;
+		mb();
+		qh->element = qh->link = UHCI_PTR_TERM;
+
+		list_del_init(&qh->list);
+	}
+	spin_unlock_irqrestore(&uhci->frame_list_lock, flags);
+
+	spin_lock_irqsave(&uhci->qh_remove_list_lock, flags);
+
+	/* Check to see if the remove list is empty. Set the IOC bit */
+	/* to force an interrupt so we can remove the QH */
+	if (list_empty(&uhci->qh_remove_list))
+		uhci_set_next_interrupt(uhci);
+
+	list_add(&qh->remove_list, &uhci->qh_remove_list);
+
+	spin_unlock_irqrestore(&uhci->qh_remove_list_lock, flags);
+}
+
+static int uhci_fixup_toggle(struct urb *urb, unsigned int toggle)
+{
+	struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
+	struct list_head *head, *tmp;
+
+	head = &urbp->td_list;
+	tmp = head->next;
+	while (head != tmp) {
+		struct uhci_td *td = list_entry(tmp, struct uhci_td, list);
+
+		tmp = tmp->next;
+
+		if (toggle)
+			td->info |= TD_TOKEN_TOGGLE;
+		else
+			td->info &= ~TD_TOKEN_TOGGLE;
+
+		toggle ^= 1;
+	}
+
+	return toggle;
+}
+
+/* This function will append one URB's QH to another URB's QH. This is for */
+/*  USB_QUEUE_BULK support for bulk transfers and soon implicitily for */
+/*  control transfers */
+static void uhci_append_queued_urb(struct uhci *uhci, struct urb *eurb, struct urb *urb)
+{
+	struct urb_priv *eurbp, *urbp, *furbp, *lurbp;
+	struct list_head *tmp;
+	struct uhci_td *lltd;
+	unsigned long flags;
+
+	eurbp = eurb->hcpriv;
+	urbp = urb->hcpriv;
+
+	spin_lock_irqsave(&uhci->frame_list_lock, flags);
+
+	/* Find the first URB in the queue */
+	if (eurbp->queued) {
+		struct list_head *head = &eurbp->queue_list;
+
+		tmp = head->next;
+		while (tmp != head) {
+			struct urb_priv *turbp =
+				list_entry(tmp, struct urb_priv, queue_list);
+
+			if (!turbp->queued)
+				break;
+
+			tmp = tmp->next;
+		}
+	} else
+		tmp = &eurbp->queue_list;
+
+	furbp = list_entry(tmp, struct urb_priv, queue_list);
+	lurbp = list_entry(furbp->queue_list.prev, struct urb_priv, queue_list);
+
+	lltd = list_entry(lurbp->td_list.prev, struct uhci_td, list);
+
+	usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe),
+		uhci_fixup_toggle(urb, uhci_toggle(lltd->info) ^ 1));
+
+	/* All qh's in the queue need to link to the next queue */
+	urbp->qh->link = eurbp->qh->link;
+
+	mb();			/* Make sure we flush everything */
+	/* Only support bulk right now, so no depth */
+	lltd->link = urbp->qh->dma_handle | UHCI_PTR_QH;
+
+	list_add_tail(&urbp->queue_list, &furbp->queue_list);
+
+	urbp->queued = 1;
+
+	spin_unlock_irqrestore(&uhci->frame_list_lock, flags);
+}
+
+static void uhci_delete_queued_urb(struct uhci *uhci, struct urb *urb)
+{
+	struct urb_priv *urbp, *nurbp;
+	struct list_head *head, *tmp;
+	struct urb_priv *purbp;
+	struct uhci_td *pltd;
+	unsigned int toggle;
+	unsigned long flags;
+
+	urbp = urb->hcpriv;
+
+	spin_lock_irqsave(&uhci->frame_list_lock, flags);
+
+	if (list_empty(&urbp->queue_list))
+		goto out;
+
+	nurbp = list_entry(urbp->queue_list.next, struct urb_priv, queue_list);
+
+	/* Fix up the toggle for the next URB's */
+	if (!urbp->queued)
+		/* We set the toggle when we unlink */
+		toggle = usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe));
+	else {
+		/* If we're in the middle of the queue, grab the toggle */
+		/*  from the TD previous to us */
+		purbp = list_entry(urbp->queue_list.prev, struct urb_priv,
+				queue_list);
+
+		pltd = list_entry(purbp->td_list.prev, struct uhci_td, list);
+
+		toggle = uhci_toggle(pltd->info) ^ 1;
+	}
+
+	head = &urbp->queue_list;
+	tmp = head->next;
+	while (head != tmp) {
+		struct urb_priv *turbp;
+
+		turbp = list_entry(tmp, struct urb_priv, queue_list);
+
+		tmp = tmp->next;
+
+		if (!turbp->queued)
+			break;
+
+		toggle = uhci_fixup_toggle(turbp->urb, toggle);
+	}
+
+	usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe),
+		usb_pipeout(urb->pipe), toggle);
+
+	if (!urbp->queued) {
+		nurbp->queued = 0;
+
+		_uhci_insert_qh(uhci, uhci->skel_bulk_qh, nurbp->urb);
+	} else {
+		/* We're somewhere in the middle (or end). A bit trickier */
+		/*  than the head scenario */
+		purbp = list_entry(urbp->queue_list.prev, struct urb_priv,
+				queue_list);
+
+		pltd = list_entry(purbp->td_list.prev, struct uhci_td, list);
+		if (nurbp->queued)
+			pltd->link = nurbp->qh->dma_handle | UHCI_PTR_QH;
+		else
+			/* The next URB happens to be the beginning, so */
+			/*  we're the last, end the chain */
+			pltd->link = UHCI_PTR_TERM;
+	}
+
+	list_del_init(&urbp->queue_list);
+
+out:
+	spin_unlock_irqrestore(&uhci->frame_list_lock, flags);
+}
+
+static struct urb_priv *uhci_alloc_urb_priv(struct uhci *uhci, struct urb *urb)
+{
+	struct urb_priv *urbp;
+
+	urbp = kmem_cache_alloc(uhci_up_cachep, SLAB_ATOMIC);
+	if (!urbp) {
+		err("uhci_alloc_urb_priv: couldn't allocate memory for urb_priv\n");
+		return NULL;
+	}
+
+	memset((void *)urbp, 0, sizeof(*urbp));
+
+	urbp->inserttime = jiffies;
+	urbp->fsbrtime = jiffies;
+	urbp->urb = urb;
+	urbp->dev = urb->dev;
+	
+	INIT_LIST_HEAD(&urbp->td_list);
+	INIT_LIST_HEAD(&urbp->queue_list);
+	INIT_LIST_HEAD(&urbp->complete_list);
+
+	urb->hcpriv = urbp;
+
+	if (urb->dev != uhci->rh.dev) {
+		if (urb->transfer_buffer_length) {
+			urbp->transfer_buffer_dma_handle = pci_map_single(uhci->dev,
+				urb->transfer_buffer, urb->transfer_buffer_length,
+				usb_pipein(urb->pipe) ? PCI_DMA_FROMDEVICE :
+				PCI_DMA_TODEVICE);
+			if (!urbp->transfer_buffer_dma_handle)
+				return NULL;
+		}
+
+		if (usb_pipetype(urb->pipe) == PIPE_CONTROL && urb->setup_packet) {
+			urbp->setup_packet_dma_handle = pci_map_single(uhci->dev,
+				urb->setup_packet, sizeof(struct usb_ctrlrequest),
+				PCI_DMA_TODEVICE);
+			if (!urbp->setup_packet_dma_handle)
+				return NULL;
+		}
+	}
+
+	return urbp;
+}
+
+/*
+ * MUST be called with urb->lock acquired
+ */
+static void uhci_add_td_to_urb(struct urb *urb, struct uhci_td *td)
+{
+	struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
+
+	td->urb = urb;
+
+	list_add_tail(&td->list, &urbp->td_list);
+}
+
+/*
+ * MUST be called with urb->lock acquired
+ */
+static void uhci_remove_td_from_urb(struct uhci_td *td)
+{
+	if (list_empty(&td->list))
+		return;
+
+	list_del_init(&td->list);
+
+	td->urb = NULL;
+}
+
+/*
+ * MUST be called with urb->lock acquired
+ */
+static void uhci_destroy_urb_priv(struct urb *urb)
+{
+	struct list_head *head, *tmp;
+	struct urb_priv *urbp;
+	struct uhci *uhci;
+
+	urbp = (struct urb_priv *)urb->hcpriv;
+	if (!urbp)
+		return;
+
+	if (!urbp->dev || !urbp->dev->bus || !urbp->dev->bus->hcpriv) {
+		warn("uhci_destroy_urb_priv: urb %p belongs to disconnected device or bus?", urb);
+		return;
+	}
+
+	if (!list_empty(&urb->urb_list))
+		warn("uhci_destroy_urb_priv: urb %p still on uhci->urb_list or uhci->remove_list", urb);
+
+	if (!list_empty(&urbp->complete_list))
+		warn("uhci_destroy_urb_priv: urb %p still on uhci->complete_list", urb);
+
+	uhci = urbp->dev->bus->hcpriv;
+
+	head = &urbp->td_list;
+	tmp = head->next;
+	while (tmp != head) {
+		struct uhci_td *td = list_entry(tmp, struct uhci_td, list);
+
+		tmp = tmp->next;
+
+		uhci_remove_td_from_urb(td);
+		uhci_remove_td(uhci, td);
+		uhci_free_td(uhci, td);
+	}
+
+	if (urbp->setup_packet_dma_handle) {
+		pci_unmap_single(uhci->dev, urbp->setup_packet_dma_handle,
+			sizeof(struct usb_ctrlrequest), PCI_DMA_TODEVICE);
+		urbp->setup_packet_dma_handle = 0;
+	}
+
+	if (urbp->transfer_buffer_dma_handle) {
+		pci_unmap_single(uhci->dev, urbp->transfer_buffer_dma_handle,
+			urb->transfer_buffer_length, usb_pipein(urb->pipe) ?
+			PCI_DMA_FROMDEVICE : PCI_DMA_TODEVICE);
+		urbp->transfer_buffer_dma_handle = 0;
+	}
+
+	urb->hcpriv = NULL;
+	kmem_cache_free(uhci_up_cachep, urbp);
+}
+
+static void uhci_inc_fsbr(struct uhci *uhci, struct urb *urb)
+{
+	unsigned long flags;
+	struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
+
+	spin_lock_irqsave(&uhci->frame_list_lock, flags);
+
+	if ((!(urb->transfer_flags & USB_NO_FSBR)) && !urbp->fsbr) {
+		urbp->fsbr = 1;
+		if (!uhci->fsbr++ && !uhci->fsbrtimeout)
+			uhci->skel_term_qh->link = uhci->skel_hs_control_qh->dma_handle | UHCI_PTR_QH;
+	}
+
+	spin_unlock_irqrestore(&uhci->frame_list_lock, flags);
+}
+
+static void uhci_dec_fsbr(struct uhci *uhci, struct urb *urb)
+{
+	unsigned long flags;
+	struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
+
+	spin_lock_irqsave(&uhci->frame_list_lock, flags);
+
+	if ((!(urb->transfer_flags & USB_NO_FSBR)) && urbp->fsbr) {
+		urbp->fsbr = 0;
+		if (!--uhci->fsbr)
+			uhci->fsbrtimeout = jiffies + FSBR_DELAY;
+	}
+
+	spin_unlock_irqrestore(&uhci->frame_list_lock, flags);
+}
+
+/*
+ * Map status to standard result codes
+ *
+ * <status> is (td->status & 0xFE0000) [a.k.a. uhci_status_bits(td->status)]
+ * <dir_out> is True for output TDs and False for input TDs.
+ */
+static int uhci_map_status(int status, int dir_out)
+{
+	if (!status)
+		return 0;
+	if (status & TD_CTRL_BITSTUFF)			/* Bitstuff error */
+		return -EPROTO;
+	if (status & TD_CTRL_CRCTIMEO) {		/* CRC/Timeout */
+		if (dir_out)
+			return -ETIMEDOUT;
+		else
+			return -EILSEQ;
+	}
+	if (status & TD_CTRL_NAK)			/* NAK */
+		return -ETIMEDOUT;
+	if (status & TD_CTRL_BABBLE)			/* Babble */
+		return -EOVERFLOW;
+	if (status & TD_CTRL_DBUFERR)			/* Buffer error */
+		return -ENOSR;
+	if (status & TD_CTRL_STALLED)			/* Stalled */
+		return -EPIPE;
+	if (status & TD_CTRL_ACTIVE)			/* Active */
+		return 0;
+
+	return -EINVAL;
+}
+
+/*
+ * Control transfers
+ */
+static int uhci_submit_control(struct urb *urb)
+{
+	struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
+	struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv;
+	struct uhci_td *td;
+	struct uhci_qh *qh;
+	unsigned long destination, status;
+	int maxsze = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe));
+	int len = urb->transfer_buffer_length;
+	dma_addr_t data = urbp->transfer_buffer_dma_handle;
+
+	/* The "pipe" thing contains the destination in bits 8--18 */
+	destination = (urb->pipe & PIPE_DEVEP_MASK) | USB_PID_SETUP;
+
+	/* 3 errors */
+	status = (urb->pipe & TD_CTRL_LS) | TD_CTRL_ACTIVE | (3 << 27);
+
+	/*
+	 * Build the TD for the control request
+	 */
+	td = uhci_alloc_td(uhci, urb->dev);
+	if (!td)
+		return -ENOMEM;
+
+	uhci_add_td_to_urb(urb, td);
+	uhci_fill_td(td, status, destination | (7 << 21),
+		urbp->setup_packet_dma_handle);
+
+	/*
+	 * If direction is "send", change the frame from SETUP (0x2D)
+	 * to OUT (0xE1). Else change it from SETUP to IN (0x69).
+	 */
+	destination ^= (USB_PID_SETUP ^ usb_packetid(urb->pipe));
+
+	if (!(urb->transfer_flags & USB_DISABLE_SPD))
+		status |= TD_CTRL_SPD;
+
+	/*
+	 * Build the DATA TD's
+	 */
+	while (len > 0) {
+		int pktsze = len;
+
+		if (pktsze > maxsze)
+			pktsze = maxsze;
+
+		td = uhci_alloc_td(uhci, urb->dev);
+		if (!td)
+			return -ENOMEM;
+
+		/* Alternate Data0/1 (start with Data1) */
+		destination ^= TD_TOKEN_TOGGLE;
+	
+		uhci_add_td_to_urb(urb, td);
+		uhci_fill_td(td, status, destination | ((pktsze - 1) << 21),
+			data);
+
+		data += pktsze;
+		len -= pktsze;
+	}
+
+	/*
+	 * Build the final TD for control status 
+	 */
+	td = uhci_alloc_td(uhci, urb->dev);
+	if (!td)
+		return -ENOMEM;
+
+	/*
+	 * It's IN if the pipe is an output pipe or we're not expecting
+	 * data back.
+	 */
+	destination &= ~TD_TOKEN_PID_MASK;
+	if (usb_pipeout(urb->pipe) || !urb->transfer_buffer_length)
+		destination |= USB_PID_IN;
+	else
+		destination |= USB_PID_OUT;
+
+	destination |= TD_TOKEN_TOGGLE;		/* End in Data1 */
+
+	status &= ~TD_CTRL_SPD;
+
+	uhci_add_td_to_urb(urb, td);
+	uhci_fill_td(td, status | TD_CTRL_IOC,
+		destination | (UHCI_NULL_DATA_SIZE << 21), 0);
+
+	qh = uhci_alloc_qh(uhci, urb->dev);
+	if (!qh)
+		return -ENOMEM;
+
+	urbp->qh = qh;
+	qh->urbp = urbp;
+
+	/* Low speed or small transfers gets a different queue and treatment */
+	if (urb->pipe & TD_CTRL_LS) {
+		uhci_insert_tds_in_qh(qh, urb, 0);
+		uhci_insert_qh(uhci, uhci->skel_ls_control_qh, urb);
+	} else {
+		uhci_insert_tds_in_qh(qh, urb, 1);
+		uhci_insert_qh(uhci, uhci->skel_hs_control_qh, urb);
+		uhci_inc_fsbr(uhci, urb);
+	}
+
+	return -EINPROGRESS;
+}
+
+static int usb_control_retrigger_status(struct urb *urb);
+
+static int uhci_result_control(struct urb *urb)
+{
+	struct list_head *tmp, *head;
+	struct urb_priv *urbp = urb->hcpriv;
+	struct uhci_td *td;
+	unsigned int status;
+	int ret = 0;
+
+	if (list_empty(&urbp->td_list))
+		return -EINVAL;
+
+	head = &urbp->td_list;
+
+	if (urbp->short_control_packet) {
+		tmp = head->prev;
+		goto status_phase;
+	}
+
+	tmp = head->next;
+	td = list_entry(tmp, struct uhci_td, list);
+
+	/* The first TD is the SETUP phase, check the status, but skip */
+	/*  the count */
+	status = uhci_status_bits(td->status);
+	if (status & TD_CTRL_ACTIVE)
+		return -EINPROGRESS;
+
+	if (status)
+		goto td_error;
+
+	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 uhci_td, list);
+
+		tmp = tmp->next;
+
+		status = uhci_status_bits(td->status);
+		if (status & TD_CTRL_ACTIVE)
+			return -EINPROGRESS;
+
+		urb->actual_length += uhci_actual_length(td->status);
+
+		if (status)
+			goto td_error;
+
+		/* Check to see if we received a short packet */
+		if (uhci_actual_length(td->status) < uhci_expected_length(td->info)) {
+			if (urb->transfer_flags & USB_DISABLE_SPD) {
+				ret = -EREMOTEIO;
+				goto err;
+			}
+
+			if (uhci_packetid(td->info) == USB_PID_IN)
+				return usb_control_retrigger_status(urb);
+			else
+				return 0;
+		}
+	}
+
+status_phase:
+	td = list_entry(tmp, struct uhci_td, list);
+
+	/* Control status phase */
+	status = uhci_status_bits(td->status);
+
+#ifdef I_HAVE_BUGGY_APC_BACKUPS
+	/* APC BackUPS Pro kludge */
+	/* It tries to send all of the descriptor instead of the amount */
+	/*  we requested */
+	if (td->status & TD_CTRL_IOC &&	/* IOC is masked out by uhci_status_bits */
+	    status & TD_CTRL_ACTIVE &&
+	    status & TD_CTRL_NAK)
+		return 0;
+#endif
+
+	if (status & TD_CTRL_ACTIVE)
+		return -EINPROGRESS;
+
+	if (status)
+		goto td_error;
+
+	return 0;
+
+td_error:
+	ret = uhci_map_status(status, uhci_packetout(td->info));
+	if (ret == -EPIPE)
+		/* endpoint has stalled - mark it halted */
+		usb_endpoint_halt(urb->dev, uhci_endpoint(td->info),
+	    			uhci_packetout(td->info));
+
+err:
+	if ((debug == 1 && ret != -EPIPE) || debug > 1) {
+		/* Some debugging code */
+		dbg("uhci_result_control() failed with status %x", status);
+
+		if (errbuf) {
+			/* Print the chain for debugging purposes */
+			uhci_show_qh(urbp->qh, errbuf, ERRBUF_LEN, 0);
+
+			lprintk(errbuf);
+		}
+	}
+
+	return ret;
+}
+
+static int usb_control_retrigger_status(struct urb *urb)
+{
+	struct list_head *tmp, *head;
+	struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
+	struct uhci *uhci = urb->dev->bus->hcpriv;
+
+	urbp->short_control_packet = 1;
+
+	/* Create a new QH to avoid pointer overwriting problems */
+	uhci_remove_qh(uhci, urbp->qh);
+
+	/* Delete all of the TD's except for the status TD at the end */
+	head = &urbp->td_list;
+	tmp = head->next;
+	while (tmp != head && tmp->next != head) {
+		struct uhci_td *td = list_entry(tmp, struct uhci_td, list);
+
+		tmp = tmp->next;
+
+		uhci_remove_td_from_urb(td);
+		uhci_remove_td(uhci, td);
+		uhci_free_td(uhci, td);
+	}
+
+	urbp->qh = uhci_alloc_qh(uhci, urb->dev);
+	if (!urbp->qh) {
+		err("unable to allocate new QH for control retrigger");
+		return -ENOMEM;
+	}
+
+	urbp->qh->urbp = urbp;
+
+	/* One TD, who cares about Breadth first? */
+	uhci_insert_tds_in_qh(urbp->qh, urb, 0);
+
+	/* Low speed or small transfers gets a different queue and treatment */
+	if (urb->pipe & TD_CTRL_LS)
+		uhci_insert_qh(uhci, uhci->skel_ls_control_qh, urb);
+	else
+		uhci_insert_qh(uhci, uhci->skel_hs_control_qh, urb);
+
+	return -EINPROGRESS;
+}
+
+/*
+ * Interrupt transfers
+ */
+static int uhci_submit_interrupt(struct urb *urb)
+{
+	struct uhci_td *td;
+	unsigned long destination, status;
+	struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv;
+	struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
+
+	if (urb->transfer_buffer_length > usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe)))
+		return -EINVAL;
+
+	/* The "pipe" thing contains the destination in bits 8--18 */
+	destination = (urb->pipe & PIPE_DEVEP_MASK) | usb_packetid(urb->pipe);
+
+	status = (urb->pipe & TD_CTRL_LS) | TD_CTRL_ACTIVE | TD_CTRL_IOC;
+
+	td = uhci_alloc_td(uhci, urb->dev);
+	if (!td)
+		return -ENOMEM;
+
+	destination |= (usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe)) << TD_TOKEN_TOGGLE_SHIFT);
+	destination |= ((urb->transfer_buffer_length - 1) << 21);
+
+	usb_dotoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe));
+
+	uhci_add_td_to_urb(urb, td);
+	uhci_fill_td(td, status, destination, urbp->transfer_buffer_dma_handle);
+
+	uhci_insert_td(uhci, uhci->skeltd[__interval_to_skel(urb->interval)], td);
+
+	return -EINPROGRESS;
+}
+
+static int uhci_result_interrupt(struct urb *urb)
+{
+	struct list_head *tmp, *head;
+	struct urb_priv *urbp = urb->hcpriv;
+	struct uhci_td *td;
+	unsigned int status;
+	int ret = 0;
+
+	urb->actual_length = 0;
+
+	head = &urbp->td_list;
+	tmp = head->next;
+	while (tmp != head) {
+		td = list_entry(tmp, struct uhci_td, list);
+
+		tmp = tmp->next;
+
+		status = uhci_status_bits(td->status);
+		if (status & TD_CTRL_ACTIVE)
+			return -EINPROGRESS;
+
+		urb->actual_length += uhci_actual_length(td->status);
+
+		if (status)
+			goto td_error;
+
+		if (uhci_actual_length(td->status) < uhci_expected_length(td->info)) {
+			if (urb->transfer_flags & USB_DISABLE_SPD) {
+				ret = -EREMOTEIO;
+				goto err;
+			} else
+				return 0;
+		}
+	}
+
+	return 0;
+
+td_error:
+	ret = uhci_map_status(status, uhci_packetout(td->info));
+	if (ret == -EPIPE)
+		/* endpoint has stalled - mark it halted */
+		usb_endpoint_halt(urb->dev, uhci_endpoint(td->info),
+	    			uhci_packetout(td->info));
+
+err:
+	if ((debug == 1 && ret != -EPIPE) || debug > 1) {
+		/* Some debugging code */
+		dbg("uhci_result_interrupt/bulk() failed with status %x",
+			status);
+
+		if (errbuf) {
+			/* Print the chain for debugging purposes */
+			if (urbp->qh)
+				uhci_show_qh(urbp->qh, errbuf, ERRBUF_LEN, 0);
+			else
+				uhci_show_td(td, errbuf, ERRBUF_LEN, 0);
+
+			lprintk(errbuf);
+		}
+	}
+
+	return ret;
+}
+
+static void uhci_reset_interrupt(struct urb *urb)
+{
+	struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv;
+	struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
+	struct uhci_td *td;
+	unsigned long flags;
+
+	spin_lock_irqsave(&urb->lock, flags);
+
+	/* Root hub is special */
+	if (urb->dev == uhci->rh.dev)
+		goto out;
+
+	td = list_entry(urbp->td_list.next, struct uhci_td, list);
+
+	td->status = (td->status & 0x2F000000) | TD_CTRL_ACTIVE | TD_CTRL_IOC;
+	td->info &= ~TD_TOKEN_TOGGLE;
+	td->info |= (usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe)) << TD_TOKEN_TOGGLE_SHIFT);
+	usb_dotoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe));
+
+out:
+	urb->status = -EINPROGRESS;
+
+	spin_unlock_irqrestore(&urb->lock, flags);
+}
+
+/*
+ * Bulk transfers
+ */
+static int uhci_submit_bulk(struct urb *urb, struct urb *eurb)
+{
+	struct uhci_td *td;
+	struct uhci_qh *qh;
+	unsigned long destination, status;
+	struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv;
+	int maxsze = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe));
+	int len = urb->transfer_buffer_length;
+	struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
+	dma_addr_t data = urbp->transfer_buffer_dma_handle;
+
+	if (len < 0)
+		return -EINVAL;
+
+	/* Can't have low speed bulk transfers */
+	if (urb->pipe & TD_CTRL_LS)
+		return -EINVAL;
+
+	/* The "pipe" thing contains the destination in bits 8--18 */
+	destination = (urb->pipe & PIPE_DEVEP_MASK) | usb_packetid(urb->pipe);
+
+	/* 3 errors */
+	status = TD_CTRL_ACTIVE | (3 << TD_CTRL_C_ERR_SHIFT);
+
+	if (!(urb->transfer_flags & USB_DISABLE_SPD))
+		status |= TD_CTRL_SPD;
+
+	/*
+	 * Build the DATA TD's
+	 */
+	do {	/* Allow zero length packets */
+		int pktsze = len;
+
+		if (pktsze > maxsze)
+			pktsze = maxsze;
+
+		td = uhci_alloc_td(uhci, urb->dev);
+		if (!td)
+			return -ENOMEM;
+
+		uhci_add_td_to_urb(urb, td);
+		uhci_fill_td(td, status, destination |
+			(((pktsze - 1) & UHCI_NULL_DATA_SIZE) << 21) |
+			(usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe),
+			 usb_pipeout(urb->pipe)) << TD_TOKEN_TOGGLE_SHIFT),
+			data);
+
+		data += pktsze;
+		len -= maxsze;
+
+		usb_dotoggle(urb->dev, usb_pipeendpoint(urb->pipe),
+			usb_pipeout(urb->pipe));
+	} 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 = uhci_alloc_td(uhci, urb->dev);
+		if (!td)
+			return -ENOMEM;
+
+		uhci_add_td_to_urb(urb, td);
+		uhci_fill_td(td, status, destination |
+			(UHCI_NULL_DATA_SIZE << 21) |
+			(usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe),
+			 usb_pipeout(urb->pipe)) << TD_TOKEN_TOGGLE_SHIFT),
+			data);
+
+		usb_dotoggle(urb->dev, usb_pipeendpoint(urb->pipe),
+			usb_pipeout(urb->pipe));
+	}
+
+	/* Set the flag on the last packet */
+	td->status |= TD_CTRL_IOC;
+
+	qh = uhci_alloc_qh(uhci, urb->dev);
+	if (!qh)
+		return -ENOMEM;
+
+	urbp->qh = qh;
+	qh->urbp = urbp;
+
+	/* Always assume breadth first */
+	uhci_insert_tds_in_qh(qh, urb, 1);
+
+	if (urb->transfer_flags & USB_QUEUE_BULK && eurb)
+		uhci_append_queued_urb(uhci, eurb, urb);
+	else
+		uhci_insert_qh(uhci, uhci->skel_bulk_qh, urb);
+
+	uhci_inc_fsbr(uhci, urb);
+
+	return -EINPROGRESS;
+}
+
+/* We can use the result interrupt since they're identical */
+#define uhci_result_bulk uhci_result_interrupt
+
+/*
+ * Isochronous transfers
+ */
+static int isochronous_find_limits(struct urb *urb, unsigned int *start, unsigned int *end)
+{
+	struct urb *last_urb = NULL;
+	struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv;
+	struct list_head *tmp, *head;
+	int ret = 0;
+
+	head = &uhci->urb_list;
+	tmp = head->next;
+	while (tmp != head) {
+		struct urb *u = list_entry(tmp, struct urb, urb_list);
+
+		tmp = tmp->next;
+
+		/* look for pending URB's with identical pipe handle */
+		if ((urb->pipe == u->pipe) && (urb->dev == u->dev) &&
+		    (u->status == -EINPROGRESS) && (u != urb)) {
+			if (!last_urb)
+				*start = u->start_frame;
+			last_urb = u;
+		}
+	}
+
+	if (last_urb) {
+		*end = (last_urb->start_frame + last_urb->number_of_packets) & 1023;
+		ret = 0;
+	} else
+		ret = -1;	/* no previous urb found */
+
+	return ret;
+}
+
+static int isochronous_find_start(struct urb *urb)
+{
+	int limits;
+	unsigned int start = 0, end = 0;
+
+	if (urb->number_of_packets > 900)	/* 900? Why? */
+		return -EFBIG;
+
+	limits = isochronous_find_limits(urb, &start, &end);
+
+	if (urb->transfer_flags & USB_ISO_ASAP) {
+		if (limits) {
+			int curframe;
+
+			curframe = uhci_get_current_frame_number(urb->dev) % UHCI_NUMFRAMES;
+			urb->start_frame = (curframe + 10) % UHCI_NUMFRAMES;
+		} else
+			urb->start_frame = end;
+	} else {
+		urb->start_frame %= UHCI_NUMFRAMES;
+		/* FIXME: Sanity check */
+	}
+
+	return 0;
+}
+
+/*
+ * Isochronous transfers
+ */
+static int uhci_submit_isochronous(struct urb *urb)
+{
+	struct uhci_td *td;
+	struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv;
+	int i, ret, framenum;
+	int status, destination;
+	struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
+
+	status = TD_CTRL_ACTIVE | TD_CTRL_IOS;
+	destination = (urb->pipe & PIPE_DEVEP_MASK) | usb_packetid(urb->pipe);
+
+	ret = isochronous_find_start(urb);
+	if (ret)
+		return ret;
+
+	framenum = urb->start_frame;
+	for (i = 0; i < urb->number_of_packets; i++, framenum++) {
+		if (!urb->iso_frame_desc[i].length)
+			continue;
+
+		td = uhci_alloc_td(uhci, urb->dev);
+		if (!td)
+			return -ENOMEM;
+
+		uhci_add_td_to_urb(urb, td);
+		uhci_fill_td(td, status, destination | ((urb->iso_frame_desc[i].length - 1) << 21),
+			urbp->transfer_buffer_dma_handle + urb->iso_frame_desc[i].offset);
+
+		if (i + 1 >= urb->number_of_packets)
+			td->status |= TD_CTRL_IOC;
+
+		uhci_insert_td_frame_list(uhci, td, framenum);
+	}
+
+	return -EINPROGRESS;
+}
+
+static int uhci_result_isochronous(struct urb *urb)
+{
+	struct list_head *tmp, *head;
+	struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
+	int status;
+	int i, ret = 0;
+
+	urb->actual_length = 0;
+
+	i = 0;
+	head = &urbp->td_list;
+	tmp = head->next;
+	while (tmp != head) {
+		struct uhci_td *td = list_entry(tmp, struct uhci_td, list);
+		int actlength;
+
+		tmp = tmp->next;
+
+		if (td->status & TD_CTRL_ACTIVE)
+			return -EINPROGRESS;
+
+		actlength = uhci_actual_length(td->status);
+		urb->iso_frame_desc[i].actual_length = actlength;
+		urb->actual_length += actlength;
+
+		status = uhci_map_status(uhci_status_bits(td->status), usb_pipeout(urb->pipe));
+		urb->iso_frame_desc[i].status = status;
+		if (status) {
+			urb->error_count++;
+			ret = status;
+		}
+
+		i++;
+	}
+
+	return ret;
+}
+
+/*
+ * MUST be called with uhci->urb_list_lock acquired
+ */
+static struct urb *uhci_find_urb_ep(struct uhci *uhci, struct urb *urb)
+{
+	struct list_head *tmp, *head;
+
+	/* We don't match Isoc transfers since they are special */
+	if (usb_pipeisoc(urb->pipe))
+		return NULL;
+
+	head = &uhci->urb_list;
+	tmp = head->next;
+	while (tmp != head) {
+		struct urb *u = list_entry(tmp, struct urb, urb_list);
+
+		tmp = tmp->next;
+
+		if (u->dev == urb->dev && u->pipe == urb->pipe &&
+		    u->status == -EINPROGRESS)
+			return u;
+	}
+
+	return NULL;
+}
+
+static int uhci_submit_urb(struct urb *urb)
+{
+	int ret = -EINVAL;
+	struct uhci *uhci;
+	unsigned long flags;
+	struct urb *eurb;
+	int bustime;
+
+	if (!urb)
+		return -EINVAL;
+
+	if (!urb->dev || !urb->dev->bus || !urb->dev->bus->hcpriv) {
+		warn("uhci_submit_urb: urb %p belongs to disconnected device or bus?", urb);
+		return -ENODEV;
+	}
+
+	uhci = (struct uhci *)urb->dev->bus->hcpriv;
+
+	usb_inc_dev_use(urb->dev);
+
+	spin_lock_irqsave(&uhci->urb_list_lock, flags);
+	spin_lock(&urb->lock);
+
+	if (urb->status == -EINPROGRESS || urb->status == -ECONNRESET ||
+	    urb->status == -ECONNABORTED) {
+		dbg("uhci_submit_urb: urb not available to submit (status = %d)", urb->status);
+		/* Since we can have problems on the out path */
+		spin_unlock(&urb->lock);
+		spin_unlock_irqrestore(&uhci->urb_list_lock, flags);
+		usb_dec_dev_use(urb->dev);
+
+		return ret;
+	}
+
+	INIT_LIST_HEAD(&urb->urb_list);
+	if (!uhci_alloc_urb_priv(uhci, urb)) {
+		ret = -ENOMEM;
+
+		goto out;
+	}
+
+	eurb = uhci_find_urb_ep(uhci, urb);
+	if (eurb && !(urb->transfer_flags & USB_QUEUE_BULK)) {
+		ret = -ENXIO;
+
+		goto out;
+	}
+
+	/* Short circuit the virtual root hub */
+	if (urb->dev == uhci->rh.dev) {
+		ret = rh_submit_urb(urb);
+
+		goto out;
+	}
+
+	switch (usb_pipetype(urb->pipe)) {
+	case PIPE_CONTROL:
+		ret = uhci_submit_control(urb);
+		break;
+	case PIPE_INTERRUPT:
+		if (urb->bandwidth == 0) {	/* not yet checked/allocated */
+			bustime = usb_check_bandwidth(urb->dev, urb);
+			if (bustime < 0)
+				ret = bustime;
+			else {
+				ret = uhci_submit_interrupt(urb);
+				if (ret == -EINPROGRESS)
+					usb_claim_bandwidth(urb->dev, urb, bustime, 0);
+			}
+		} else		/* bandwidth is already set */
+			ret = uhci_submit_interrupt(urb);
+		break;
+	case PIPE_BULK:
+		ret = uhci_submit_bulk(urb, eurb);
+		break;
+	case PIPE_ISOCHRONOUS:
+		if (urb->bandwidth == 0) {	/* not yet checked/allocated */
+			if (urb->number_of_packets <= 0) {
+				ret = -EINVAL;
+				break;
+			}
+			bustime = usb_check_bandwidth(urb->dev, urb);
+			if (bustime < 0) {
+				ret = bustime;
+				break;
+			}
+
+			ret = uhci_submit_isochronous(urb);
+			if (ret == -EINPROGRESS)
+				usb_claim_bandwidth(urb->dev, urb, bustime, 1);
+		} else		/* bandwidth is already set */
+			ret = uhci_submit_isochronous(urb);
+		break;
+	}
+
+out:
+	urb->status = ret;
+
+	if (ret == -EINPROGRESS) {
+		/* We use _tail to make find_urb_ep more efficient */
+		list_add_tail(&urb->urb_list, &uhci->urb_list);
+
+		spin_unlock(&urb->lock);
+		spin_unlock_irqrestore(&uhci->urb_list_lock, flags);
+
+		return 0;
+	}
+
+	uhci_unlink_generic(uhci, urb);
+
+	spin_unlock(&urb->lock);
+	spin_unlock_irqrestore(&uhci->urb_list_lock, flags);
+
+	/* Only call completion if it was successful */
+	if (!ret)
+		uhci_call_completion(urb);
+
+	return ret;
+}
+
+/*
+ * Return the result of a transfer
+ *
+ * MUST be called with urb_list_lock acquired
+ */
+static void uhci_transfer_result(struct uhci *uhci, struct urb *urb)
+{
+	int ret = -EINVAL;
+	unsigned long flags;
+	struct urb_priv *urbp;
+
+	/* The root hub is special */
+	if (urb->dev == uhci->rh.dev)
+		return;
+
+	spin_lock_irqsave(&urb->lock, flags);
+
+	urbp = (struct urb_priv *)urb->hcpriv;
+
+	if (urb->status != -EINPROGRESS) {
+		info("uhci_transfer_result: called for URB %p not in flight?", urb);
+		goto out;
+	}
+
+	switch (usb_pipetype(urb->pipe)) {
+	case PIPE_CONTROL:
+		ret = uhci_result_control(urb);
+		break;
+	case PIPE_INTERRUPT:
+		ret = uhci_result_interrupt(urb);
+		break;
+	case PIPE_BULK:
+		ret = uhci_result_bulk(urb);
+		break;
+	case PIPE_ISOCHRONOUS:
+		ret = uhci_result_isochronous(urb);
+		break;
+	}
+
+	urbp->status = ret;
+
+	if (ret == -EINPROGRESS)
+		goto out;
+
+	switch (usb_pipetype(urb->pipe)) {
+	case PIPE_CONTROL:
+	case PIPE_BULK:
+	case PIPE_ISOCHRONOUS:
+		/* Release bandwidth for Interrupt or Isoc. transfers */
+		/* Spinlock needed ? */
+		if (urb->bandwidth)
+			usb_release_bandwidth(urb->dev, urb, 1);
+		uhci_unlink_generic(uhci, urb);
+		break;
+	case PIPE_INTERRUPT:
+		/* Interrupts are an exception */
+		if (urb->interval)
+			goto out_complete;
+
+		/* Release bandwidth for Interrupt or Isoc. transfers */
+		/* Spinlock needed ? */
+		if (urb->bandwidth)
+			usb_release_bandwidth(urb->dev, urb, 0);
+		uhci_unlink_generic(uhci, urb);
+		break;
+	default:
+		info("uhci_transfer_result: unknown pipe type %d for urb %p\n",
+			usb_pipetype(urb->pipe), urb);
+	}
+
+	/* Remove it from uhci->urb_list */
+	list_del_init(&urb->urb_list);
+
+out_complete:
+	uhci_add_complete(urb);
+
+out:
+	spin_unlock_irqrestore(&urb->lock, flags);
+}
+
+/*
+ * MUST be called with urb->lock acquired
+ */
+static void uhci_unlink_generic(struct uhci *uhci, struct urb *urb)
+{
+	struct list_head *head, *tmp;
+	struct urb_priv *urbp = urb->hcpriv;
+	int prevactive = 1;
+
+	/* We can get called when urbp allocation fails, so check */
+	if (!urbp)
+		return;
+
+	uhci_dec_fsbr(uhci, urb);	/* Safe since it checks */
+
+	/*
+	 * Now we need to find out what the last successful toggle was
+	 * so we can update the local data toggle for the next transfer
+	 *
+	 * There's 3 way's the last successful completed TD is found:
+	 *
+	 * 1) The TD is NOT active and the actual length < expected length
+	 * 2) The TD is NOT active and it's the last TD in the chain
+	 * 3) The TD is active and the previous TD is NOT active
+	 *
+	 * Control and Isochronous ignore the toggle, so this is safe
+	 * for all types
+	 */
+	head = &urbp->td_list;
+	tmp = head->next;
+	while (tmp != head) {
+		struct uhci_td *td = list_entry(tmp, struct uhci_td, list);
+
+		tmp = tmp->next;
+
+		if (!(td->status & TD_CTRL_ACTIVE) &&
+		    (uhci_actual_length(td->status) < uhci_expected_length(td->info) ||
+		    tmp == head))
+			usb_settoggle(urb->dev, uhci_endpoint(td->info),
+				uhci_packetout(td->info),
+				uhci_toggle(td->info) ^ 1);
+		else if ((td->status & TD_CTRL_ACTIVE) && !prevactive)
+			usb_settoggle(urb->dev, uhci_endpoint(td->info),
+				uhci_packetout(td->info),
+				uhci_toggle(td->info));
+
+		prevactive = td->status & TD_CTRL_ACTIVE;
+	}
+
+	uhci_delete_queued_urb(uhci, urb);
+
+	/* The interrupt loop will reclaim the QH's */
+	uhci_remove_qh(uhci, urbp->qh);
+	urbp->qh = NULL;
+}
+
+static int uhci_unlink_urb(struct urb *urb)
+{
+	struct uhci *uhci;
+	unsigned long flags;
+	struct urb_priv *urbp = urb->hcpriv;
+
+	if (!urb)
+		return -EINVAL;
+
+	if (!urb->dev || !urb->dev->bus || !urb->dev->bus->hcpriv)
+		return -ENODEV;
+
+	uhci = (struct uhci *)urb->dev->bus->hcpriv;
+
+	spin_lock_irqsave(&uhci->urb_list_lock, flags);
+	spin_lock(&urb->lock);
+
+	/* Release bandwidth for Interrupt or Isoc. transfers */
+	/* Spinlock needed ? */
+	if (urb->bandwidth) {
+		switch (usb_pipetype(urb->pipe)) {
+		case PIPE_INTERRUPT:
+			usb_release_bandwidth(urb->dev, urb, 0);
+			break;
+		case PIPE_ISOCHRONOUS:
+			usb_release_bandwidth(urb->dev, urb, 1);
+			break;
+		default:
+			break;
+		}
+	}
+
+	if (urb->status != -EINPROGRESS) {
+		spin_unlock(&urb->lock);
+		spin_unlock_irqrestore(&uhci->urb_list_lock, flags);
+		return 0;
+	}
+
+	list_del_init(&urb->urb_list);
+
+	uhci_unlink_generic(uhci, urb);
+
+	/* Short circuit the virtual root hub */
+	if (urb->dev == uhci->rh.dev) {
+		rh_unlink_urb(urb);
+
+		spin_unlock(&urb->lock);
+		spin_unlock_irqrestore(&uhci->urb_list_lock, flags);
+
+		uhci_call_completion(urb);
+	} else {
+		if (urb->transfer_flags & USB_ASYNC_UNLINK) {
+			urbp->status = urb->status = -ECONNABORTED;
+
+			spin_lock(&uhci->urb_remove_list_lock);
+
+			/* If we're the first, set the next interrupt bit */
+			if (list_empty(&uhci->urb_remove_list))
+				uhci_set_next_interrupt(uhci);
+			
+			list_add(&urb->urb_list, &uhci->urb_remove_list);
+
+			spin_unlock(&uhci->urb_remove_list_lock);
+
+			spin_unlock(&urb->lock);
+			spin_unlock_irqrestore(&uhci->urb_list_lock, flags);
+
+		} else {
+			urb->status = -ENOENT;
+
+			spin_unlock(&urb->lock);
+			spin_unlock_irqrestore(&uhci->urb_list_lock, flags);
+
+			if (in_interrupt()) {	/* wait at least 1 frame */
+				static int errorcount = 10;
+
+				if (errorcount--)
+					dbg("uhci_unlink_urb called from interrupt for urb %p", urb);
+				udelay(1000);
+			} else
+				schedule_timeout(1+1*HZ/1000); 
+
+			uhci_call_completion(urb);
+		}
+	}
+
+	return 0;
+}
+
+static int uhci_fsbr_timeout(struct uhci *uhci, struct urb *urb)
+{
+	struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
+	struct list_head *head, *tmp;
+	int count = 0;
+
+	uhci_dec_fsbr(uhci, urb);
+
+	urbp->fsbr_timeout = 1;
+
+	/*
+	 * Ideally we would want to fix qh->element as well, but it's
+	 * read/write by the HC, so that can introduce a race. It's not
+	 * really worth the hassle
+	 */
+
+	head = &urbp->td_list;
+	tmp = head->next;
+	while (tmp != head) {
+		struct uhci_td *td = list_entry(tmp, struct uhci_td, list);
+
+		tmp = tmp->next;
+
+		/*
+		 * Make sure we don't do the last one (since it'll have the
+		 * TERM bit set) as well as we skip every so many TD's to
+		 * make sure it doesn't hog the bandwidth
+		 */
+		if (tmp != head && (count % DEPTH_INTERVAL) == (DEPTH_INTERVAL - 1))
+			td->link |= UHCI_PTR_DEPTH;
+
+		count++;
+	}
+
+	return 0;
+}
+
+/*
+ * uhci_get_current_frame_number()
+ *
+ * returns the current frame number for a USB bus/controller.
+ */
+static int uhci_get_current_frame_number(struct usb_device *dev)
+{
+	struct uhci *uhci = (struct uhci *)dev->bus->hcpriv;
+
+	return inw(uhci->io_addr + USBFRNUM);
+}
+
+struct usb_operations uhci_device_operations = {
+	uhci_alloc_dev,
+	uhci_free_dev,
+	uhci_get_current_frame_number,
+	uhci_submit_urb,
+	uhci_unlink_urb
+};
+
+/* Virtual Root Hub */
+
+static __u8 root_hub_dev_des[] =
+{
+ 	0x12,			/*  __u8  bLength; */
+	0x01,			/*  __u8  bDescriptorType; Device */
+	0x00,			/*  __u16 bcdUSB; v1.0 */
+	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 root_hub_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,
+					Bit 5 Remote-wakeup, 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; 8 Bytes */
+	0x00,
+	0xff			/*  __u8  ep_bInterval; 255 ms */
+};
+
+static __u8 root_hub_hub_des[] =
+{
+	0x09,			/*  __u8  bLength; */
+	0x29,			/*  __u8  bDescriptorType; Hub-descriptor */
+	0x02,			/*  __u8  bNbrPorts; */
+	0x00,			/* __u16  wHubCharacteristics; */
+	0x00,
+	0x01,			/*  __u8  bPwrOn2pwrGood; 2ms */
+	0x00,			/*  __u8  bHubContrCurrent; 0 mA */
+	0x00,			/*  __u8  DeviceRemovable; *** 7 Ports max *** */
+	0xff			/*  __u8  PortPwrCtrlMask; *** 7 ports max *** */
+};
+
+/* prepare Interrupt pipe transaction data; HUB INTERRUPT ENDPOINT */
+static int rh_send_irq(struct urb *urb)
+{
+	struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv;
+	struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
+	unsigned int io_addr = uhci->io_addr;
+	unsigned long flags;
+	int i, len = 1;
+	__u16 data = 0;
+
+	spin_lock_irqsave(&urb->lock, flags);
+	for (i = 0; i < uhci->rh.numports; i++) {
+		data |= ((inw(io_addr + USBPORTSC1 + i * 2) & 0xa) > 0 ? (1 << (i + 1)) : 0);
+		len = (i + 1) / 8 + 1;
+	}
+
+	*(__u16 *) urb->transfer_buffer = cpu_to_le16(data);
+	urb->actual_length = len;
+	urbp->status = 0;
+
+	spin_unlock_irqrestore(&urb->lock, flags);
+
+	if ((data > 0) && (uhci->rh.send != 0)) {
+		dbg("root-hub INT complete: port1: %x port2: %x data: %x",
+			inw(io_addr + USBPORTSC1), inw(io_addr + USBPORTSC2), data);
+		uhci_call_completion(urb);
+	}
+
+	return 0;
+}
+
+/* Virtual Root Hub INTs are polled by this timer every "interval" ms */
+static int rh_init_int_timer(struct urb *urb);
+
+static void rh_int_timer_do(unsigned long ptr)
+{
+	struct urb *urb = (struct urb *)ptr;
+	struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv;
+	struct list_head list, *tmp, *head;
+	unsigned long flags;
+
+	if (uhci->rh.send)
+		rh_send_irq(urb);
+
+	INIT_LIST_HEAD(&list);
+
+	spin_lock_irqsave(&uhci->urb_list_lock, flags);
+	head = &uhci->urb_list;
+	tmp = head->next;
+	while (tmp != head) {
+		struct urb *u = list_entry(tmp, struct urb, urb_list);
+		struct urb_priv *up = (struct urb_priv *)u->hcpriv;
+
+		tmp = tmp->next;
+
+		spin_lock(&u->lock);
+
+		/* Check if the FSBR timed out */
+		if (up->fsbr && !up->fsbr_timeout && time_after_eq(jiffies, up->fsbrtime + IDLE_TIMEOUT))
+			uhci_fsbr_timeout(uhci, u);
+
+		/* Check if the URB timed out */
+		if (u->timeout && time_after_eq(jiffies, up->inserttime + u->timeout)) {
+			list_del(&u->urb_list);
+			list_add_tail(&u->urb_list, &list);
+		}
+
+		spin_unlock(&u->lock);
+	}
+	spin_unlock_irqrestore(&uhci->urb_list_lock, flags);
+
+	head = &list;
+	tmp = head->next;
+	while (tmp != head) {
+		struct urb *u = list_entry(tmp, struct urb, urb_list);
+
+		tmp = tmp->next;
+
+		u->transfer_flags |= USB_ASYNC_UNLINK | USB_TIMEOUT_KILLED;
+		uhci_unlink_urb(u);
+	}
+
+	/* Really disable FSBR */
+	if (!uhci->fsbr && uhci->fsbrtimeout && time_after_eq(jiffies, uhci->fsbrtimeout)) {
+		uhci->fsbrtimeout = 0;
+		uhci->skel_term_qh->link = UHCI_PTR_TERM;
+	}
+
+	/* enter global suspend if nothing connected */
+	if (!uhci->is_suspended && !ports_active(uhci))
+		suspend_hc(uhci);
+
+	rh_init_int_timer(urb);
+}
+
+/* Root Hub INTs are polled by this timer */
+static int rh_init_int_timer(struct urb *urb)
+{
+	struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv;
+
+	uhci->rh.interval = urb->interval;
+	init_timer(&uhci->rh.rh_int_timer);
+	uhci->rh.rh_int_timer.function = rh_int_timer_do;
+	uhci->rh.rh_int_timer.data = (unsigned long)urb;
+	uhci->rh.rh_int_timer.expires = jiffies + (HZ * (urb->interval < 30 ? 30 : urb->interval)) / 1000;
+	add_timer(&uhci->rh.rh_int_timer);
+
+	return 0;
+}
+
+#define OK(x)			len = (x); break
+
+#define CLR_RH_PORTSTAT(x) \
+	status = inw(io_addr + USBPORTSC1 + 2 * (wIndex-1)); \
+	status = (status & 0xfff5) & ~(x); \
+	outw(status, io_addr + USBPORTSC1 + 2 * (wIndex-1))
+
+#define SET_RH_PORTSTAT(x) \
+	status = inw(io_addr + USBPORTSC1 + 2 * (wIndex-1)); \
+	status = (status & 0xfff5) | (x); \
+	outw(status, io_addr + USBPORTSC1 + 2 * (wIndex-1))
+
+
+/* Root Hub Control Pipe */
+static int rh_submit_urb(struct urb *urb)
+{
+	struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv;
+	unsigned int pipe = urb->pipe;
+	struct usb_ctrlrequest *cmd = (struct usb_ctrlrequest *)urb->setup_packet;
+	void *data = urb->transfer_buffer;
+	int leni = urb->transfer_buffer_length;
+	int len = 0;
+	int status = 0;
+	int stat = 0;
+	int i;
+	unsigned int io_addr = uhci->io_addr;
+	__u16 cstatus;
+	__u16 bmRType_bReq;
+	__u16 wValue;
+	__u16 wIndex;
+	__u16 wLength;
+
+	if (usb_pipetype(pipe) == PIPE_INTERRUPT) {
+		uhci->rh.urb = urb;
+		uhci->rh.send = 1;
+		uhci->rh.interval = urb->interval;
+		rh_init_int_timer(urb);
+
+		return -EINPROGRESS;
+	}
+
+	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);
+
+	for (i = 0; i < 8; i++)
+		uhci->rh.c_p_r[i] = 0;
+
+	switch (bmRType_bReq) {
+		/* Request Destination:
+		   without flags: Device,
+		   RH_INTERFACE: interface,
+		   RH_ENDPOINT: endpoint,
+		   RH_CLASS means HUB here,
+		   RH_OTHER | RH_CLASS  almost ever means HUB_PORT here
+		*/
+
+	case RH_GET_STATUS:
+		*(__u16 *)data = cpu_to_le16(1);
+		OK(2);
+	case RH_GET_STATUS | RH_INTERFACE:
+		*(__u16 *)data = cpu_to_le16(0);
+		OK(2);
+	case RH_GET_STATUS | RH_ENDPOINT:
+		*(__u16 *)data = cpu_to_le16(0);
+		OK(2);
+	case RH_GET_STATUS | RH_CLASS:
+		*(__u32 *)data = cpu_to_le32(0);
+		OK(4);		/* hub power */
+	case RH_GET_STATUS | RH_OTHER | RH_CLASS:
+		status = inw(io_addr + USBPORTSC1 + 2 * (wIndex - 1));
+		cstatus = ((status & USBPORTSC_CSC) >> (1 - 0)) |
+			((status & USBPORTSC_PEC) >> (3 - 1)) |
+			(uhci->rh.c_p_r[wIndex - 1] << (0 + 4));
+			status = (status & USBPORTSC_CCS) |
+			((status & USBPORTSC_PE) >> (2 - 1)) |
+			((status & USBPORTSC_SUSP) >> (12 - 2)) |
+			((status & USBPORTSC_PR) >> (9 - 4)) |
+			(1 << 8) |      /* power on */
+			((status & USBPORTSC_LSDA) << (-8 + 9));
+
+		*(__u16 *)data = cpu_to_le16(status);
+		*(__u16 *)(data + 2) = cpu_to_le16(cstatus);
+		OK(4);
+	case RH_CLEAR_FEATURE | RH_ENDPOINT:
+		switch (wValue) {
+		case RH_ENDPOINT_STALL:
+			OK(0);
+		}
+		break;
+	case RH_CLEAR_FEATURE | RH_CLASS:
+		switch (wValue) {
+		case RH_C_HUB_OVER_CURRENT:
+			OK(0);	/* hub power over current */
+		}
+		break;
+	case RH_CLEAR_FEATURE | RH_OTHER | RH_CLASS:
+		switch (wValue) {
+		case RH_PORT_ENABLE:
+			CLR_RH_PORTSTAT(USBPORTSC_PE);
+			OK(0);
+		case RH_PORT_SUSPEND:
+			CLR_RH_PORTSTAT(USBPORTSC_SUSP);
+			OK(0);
+		case RH_PORT_POWER:
+			OK(0);	/* port power */
+		case RH_C_PORT_CONNECTION:
+			SET_RH_PORTSTAT(USBPORTSC_CSC);
+			OK(0);
+		case RH_C_PORT_ENABLE:
+			SET_RH_PORTSTAT(USBPORTSC_PEC);
+			OK(0);
+		case RH_C_PORT_SUSPEND:
+			/*** WR_RH_PORTSTAT(RH_PS_PSSC); */
+			OK(0);
+		case RH_C_PORT_OVER_CURRENT:
+			OK(0);	/* port power over current */
+		case RH_C_PORT_RESET:
+			uhci->rh.c_p_r[wIndex - 1] = 0;
+			OK(0);
+		}
+		break;
+	case RH_SET_FEATURE | RH_OTHER | RH_CLASS:
+		switch (wValue) {
+		case RH_PORT_SUSPEND:
+			SET_RH_PORTSTAT(USBPORTSC_SUSP);
+			OK(0);
+		case RH_PORT_RESET:
+			SET_RH_PORTSTAT(USBPORTSC_PR);
+			mdelay(50);	/* USB v1.1 7.1.7.3 */
+			uhci->rh.c_p_r[wIndex - 1] = 1;
+			CLR_RH_PORTSTAT(USBPORTSC_PR);
+			udelay(10);
+			SET_RH_PORTSTAT(USBPORTSC_PE);
+			mdelay(10);
+			SET_RH_PORTSTAT(0xa);
+			OK(0);
+		case RH_PORT_POWER:
+			OK(0); /* port power ** */
+		case RH_PORT_ENABLE:
+			SET_RH_PORTSTAT(USBPORTSC_PE);
+			OK(0);
+		}
+		break;
+	case RH_SET_ADDRESS:
+		uhci->rh.devnum = wValue;
+		OK(0);
+	case RH_GET_DESCRIPTOR:
+		switch ((wValue & 0xff00) >> 8) {
+		case 0x01:	/* device descriptor */
+			len = min_t(unsigned int, leni,
+				  min_t(unsigned int,
+				      sizeof(root_hub_dev_des), wLength));
+			memcpy(data, root_hub_dev_des, len);
+			OK(len);
+		case 0x02:	/* configuration descriptor */
+			len = min_t(unsigned int, leni,
+				  min_t(unsigned int,
+				      sizeof(root_hub_config_des), wLength));
+			memcpy (data, root_hub_config_des, len);
+			OK(len);
+		case 0x03:	/* string descriptors */
+			len = usb_root_hub_string (wValue & 0xff,
+				uhci->io_addr, "UHCI-alt",
+				data, wLength);
+			if (len > 0) {
+				OK(min_t(int, leni, len));
+			} else 
+				stat = -EPIPE;
+		}
+		break;
+	case RH_GET_DESCRIPTOR | RH_CLASS:
+		root_hub_hub_des[2] = uhci->rh.numports;
+		len = min_t(unsigned int, leni,
+			  min_t(unsigned int, sizeof(root_hub_hub_des), wLength));
+		memcpy(data, root_hub_hub_des, len);
+		OK(len);
+	case RH_GET_CONFIGURATION:
+		*(__u8 *)data = 0x01;
+		OK(1);
+	case RH_SET_CONFIGURATION:
+		OK(0);
+	case RH_GET_INTERFACE | RH_INTERFACE:
+		*(__u8 *)data = 0x00;
+		OK(1);
+	case RH_SET_INTERFACE | RH_INTERFACE:
+		OK(0);
+	default:
+		stat = -EPIPE;
+	}
+
+	urb->actual_length = len;
+
+	return stat;
+}
+
+/*
+ * MUST be called with urb->lock acquired
+ */
+static int rh_unlink_urb(struct urb *urb)
+{
+	struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv;
+
+	if (uhci->rh.urb == urb) {
+		urb->status = -ENOENT;
+		uhci->rh.send = 0;
+		uhci->rh.urb = NULL;
+		del_timer(&uhci->rh.rh_int_timer);
+	}
+	return 0;
+}
+
+static void uhci_free_pending_qhs(struct uhci *uhci)
+{
+	struct list_head *tmp, *head;
+	unsigned long flags;
+
+	spin_lock_irqsave(&uhci->qh_remove_list_lock, flags);
+	head = &uhci->qh_remove_list;
+	tmp = head->next;
+	while (tmp != head) {
+		struct uhci_qh *qh = list_entry(tmp, struct uhci_qh, remove_list);
+
+		tmp = tmp->next;
+
+		list_del_init(&qh->remove_list);
+
+		uhci_free_qh(uhci, qh);
+	}
+	spin_unlock_irqrestore(&uhci->qh_remove_list_lock, flags);
+}
+
+static void uhci_call_completion(struct urb *urb)
+{
+	struct urb_priv *urbp;
+	struct usb_device *dev = urb->dev;
+	struct uhci *uhci = (struct uhci *)dev->bus->hcpriv;
+	int is_ring = 0, killed, resubmit_interrupt, status;
+	struct urb *nurb;
+	unsigned long flags;
+
+	spin_lock_irqsave(&urb->lock, flags);
+
+	urbp = (struct urb_priv *)urb->hcpriv;
+	if (!urbp || !urb->dev) {
+		spin_unlock_irqrestore(&urb->lock, flags);
+		return;
+	}
+
+	killed = (urb->status == -ENOENT || urb->status == -ECONNABORTED ||
+			urb->status == -ECONNRESET);
+	resubmit_interrupt = (usb_pipetype(urb->pipe) == PIPE_INTERRUPT &&
+			urb->interval);
+
+	nurb = urb->next;
+	if (nurb && !killed) {
+		int count = 0;
+
+		while (nurb && nurb != urb && count < MAX_URB_LOOP) {
+			if (nurb->status == -ENOENT ||
+			    nurb->status == -ECONNABORTED ||
+			    nurb->status == -ECONNRESET) {
+				killed = 1;
+				break;
+			}
+
+			nurb = nurb->next;
+			count++;
+		}
+
+		if (count == MAX_URB_LOOP)
+			err("uhci_call_completion: too many linked URB's, loop? (first loop)");
+
+		/* Check to see if chain is a ring */
+		is_ring = (nurb == urb);
+	}
+
+	if (urbp->transfer_buffer_dma_handle)
+		pci_dma_sync_single(uhci->dev, urbp->transfer_buffer_dma_handle,
+			urb->transfer_buffer_length, usb_pipein(urb->pipe) ?
+			PCI_DMA_FROMDEVICE : PCI_DMA_TODEVICE);
+
+	if (urbp->setup_packet_dma_handle)
+		pci_dma_sync_single(uhci->dev, urbp->setup_packet_dma_handle,
+			sizeof(struct usb_ctrlrequest), PCI_DMA_TODEVICE);
+
+	status = urbp->status;
+	if (!resubmit_interrupt || killed)
+		/* We don't need urb_priv anymore */
+		uhci_destroy_urb_priv(urb);
+
+	if (!killed)
+		urb->status = status;
+
+	urb->dev = NULL;
+	spin_unlock_irqrestore(&urb->lock, flags);
+
+	if (urb->complete)
+		urb->complete(urb);
+
+	if (resubmit_interrupt)
+		/* Recheck the status. The completion handler may have */
+		/*  unlinked the resubmitting interrupt URB */
+		killed = (urb->status == -ENOENT ||
+			  urb->status == -ECONNABORTED ||
+			  urb->status == -ECONNRESET);
+
+	if (resubmit_interrupt && !killed) {
+		urb->dev = dev;
+		uhci_reset_interrupt(urb);
+	} else {
+		if (is_ring && !killed) {
+			urb->dev = dev;
+			uhci_submit_urb(urb);
+		} else {
+			/* We decrement the usage count after we're done */
+			/*  with everything */
+			usb_dec_dev_use(dev);
+		}
+	}
+}
+
+static void uhci_finish_completion(struct uhci *uhci)
+{
+	struct list_head *tmp, *head;
+	unsigned long flags;
+
+	spin_lock_irqsave(&uhci->complete_list_lock, flags);
+	head = &uhci->complete_list;
+	tmp = head->next;
+	while (tmp != head) {
+		struct urb_priv *urbp = list_entry(tmp, struct urb_priv, complete_list);
+		struct urb *urb = urbp->urb;
+
+		list_del_init(&urbp->complete_list);
+		spin_unlock_irqrestore(&uhci->complete_list_lock, flags);
+
+		uhci_call_completion(urb);
+
+		spin_lock_irqsave(&uhci->complete_list_lock, flags);
+		head = &uhci->complete_list;
+		tmp = head->next;
+	}
+	spin_unlock_irqrestore(&uhci->complete_list_lock, flags);
+}
+
+static void uhci_remove_pending_qhs(struct uhci *uhci)
+{
+	struct list_head *tmp, *head;
+	unsigned long flags;
+
+	spin_lock_irqsave(&uhci->urb_remove_list_lock, flags);
+	head = &uhci->urb_remove_list;
+	tmp = head->next;
+	while (tmp != head) {
+		struct urb *urb = list_entry(tmp, struct urb, urb_list);
+		struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
+
+		tmp = tmp->next;
+
+		list_del_init(&urb->urb_list);
+
+		urbp->status = urb->status = -ECONNRESET;
+
+		uhci_add_complete(urb);
+	}
+	spin_unlock_irqrestore(&uhci->urb_remove_list_lock, flags);
+}
+
+static void uhci_interrupt(int irq, void *__uhci, struct pt_regs *regs)
+{
+	struct uhci *uhci = __uhci;
+	unsigned int io_addr = uhci->io_addr;
+	unsigned short status;
+	struct list_head *tmp, *head;
+
+	/*
+	 * Read the interrupt status, and write it back to clear the
+	 * interrupt cause
+	 */
+	status = inw(io_addr + USBSTS);
+	if (!status)	/* shared interrupt, not mine */
+		return;
+	outw(status, io_addr + USBSTS);		/* Clear it */
+
+	if (status & ~(USBSTS_USBINT | USBSTS_ERROR | USBSTS_RD)) {
+		if (status & USBSTS_HSE)
+			err("%x: host system error, PCI problems?", io_addr);
+		if (status & USBSTS_HCPE)
+			err("%x: host controller process error. something bad happened", io_addr);
+		if ((status & USBSTS_HCH) && !uhci->is_suspended) {
+			err("%x: host controller halted. very bad", io_addr);
+			/* FIXME: Reset the controller, fix the offending TD */
+		}
+	}
+
+	if (status & USBSTS_RD)
+		wakeup_hc(uhci);
+
+	uhci_free_pending_qhs(uhci);
+
+	uhci_remove_pending_qhs(uhci);
+
+	uhci_clear_next_interrupt(uhci);
+
+	/* Walk the list of pending URB's to see which ones completed */
+	spin_lock(&uhci->urb_list_lock);
+	head = &uhci->urb_list;
+	tmp = head->next;
+	while (tmp != head) {
+		struct urb *urb = list_entry(tmp, struct urb, urb_list);
+
+		tmp = tmp->next;
+
+		/* Checks the status and does all of the magic necessary */
+		uhci_transfer_result(uhci, urb);
+	}
+	spin_unlock(&uhci->urb_list_lock);
+
+	uhci_finish_completion(uhci);
+}
+
+static void reset_hc(struct uhci *uhci)
+{
+	unsigned int io_addr = uhci->io_addr;
+
+	/* Global reset for 50ms */
+	outw(USBCMD_GRESET, io_addr + USBCMD);
+	wait_ms(50);
+	outw(0, io_addr + USBCMD);
+	wait_ms(10);
+}
+
+static void suspend_hc(struct uhci *uhci)
+{
+	unsigned int io_addr = uhci->io_addr;
+
+	dbg("%x: suspend_hc", io_addr);
+
+	outw(USBCMD_EGSM, io_addr + USBCMD);
+
+	uhci->is_suspended = 1;
+}
+
+static void wakeup_hc(struct uhci *uhci)
+{
+	unsigned int io_addr = uhci->io_addr;
+	unsigned int status;
+
+	dbg("%x: wakeup_hc", io_addr);
+
+	outw(0, io_addr + USBCMD);
+	
+	/* wait for EOP to be sent */
+	status = inw(io_addr + USBCMD);
+	while (status & USBCMD_FGR)
+		status = inw(io_addr + USBCMD);
+
+	uhci->is_suspended = 0;
+
+	/* Run and mark it configured with a 64-byte max packet */
+	outw(USBCMD_RS | USBCMD_CF | USBCMD_MAXP, io_addr + USBCMD);
+}
+
+static int ports_active(struct uhci *uhci)
+{
+	unsigned int io_addr = uhci->io_addr;
+	int connection = 0;
+	int i;
+
+	for (i = 0; i < uhci->rh.numports; i++)
+		connection |= (inw(io_addr + USBPORTSC1 + i * 2) & 0x1);
+
+	return connection;
+}
+
+static void start_hc(struct uhci *uhci)
+{
+	unsigned int io_addr = uhci->io_addr;
+	int timeout = 1000;
+
+	/*
+	 * Reset the HC - this will force us to get a
+	 * new notification of any already connected
+	 * ports due to the virtual disconnect that it
+	 * implies.
+	 */
+	outw(USBCMD_HCRESET, io_addr + USBCMD);
+	while (inw(io_addr + USBCMD) & USBCMD_HCRESET) {
+		if (!--timeout) {
+			printk(KERN_ERR "uhci: USBCMD_HCRESET timed out!\n");
+			break;
+		}
+	}
+
+	/* Turn on all interrupts */
+	outw(USBINTR_TIMEOUT | USBINTR_RESUME | USBINTR_IOC | USBINTR_SP,
+		io_addr + USBINTR);
+
+	/* Start at frame 0 */
+	outw(0, io_addr + USBFRNUM);
+	outl(uhci->fl->dma_handle, io_addr + USBFLBASEADD);
+
+	/* Run and mark it configured with a 64-byte max packet */
+	outw(USBCMD_RS | USBCMD_CF | USBCMD_MAXP, io_addr + USBCMD);
+}
+
+#ifdef CONFIG_PROC_FS
+static int uhci_num = 0;
+#endif
+
+static void free_uhci(struct uhci *uhci)
+{
+	kfree(uhci);
+}
+
+/*
+ * De-allocate all resources..
+ */
+static void release_uhci(struct uhci *uhci)
+{
+	int i;
+#ifdef CONFIG_PROC_FS
+	char buf[8];
+#endif
+
+	if (uhci->irq >= 0) {
+		free_irq(uhci->irq, uhci);
+		uhci->irq = -1;
+	}
+
+	for (i = 0; i < UHCI_NUM_SKELQH; i++)
+		if (uhci->skelqh[i]) {
+			uhci_free_qh(uhci, uhci->skelqh[i]);
+			uhci->skelqh[i] = NULL;
+		}
+
+	for (i = 0; i < UHCI_NUM_SKELTD; i++)
+		if (uhci->skeltd[i]) {
+			uhci_free_td(uhci, uhci->skeltd[i]);
+			uhci->skeltd[i] = NULL;
+		}
+
+	if (uhci->qh_pool) {
+		pci_pool_destroy(uhci->qh_pool);
+		uhci->qh_pool = NULL;
+	}
+
+	if (uhci->td_pool) {
+		pci_pool_destroy(uhci->td_pool);
+		uhci->td_pool = NULL;
+	}
+
+	if (uhci->fl) {
+		pci_free_consistent(uhci->dev, sizeof(*uhci->fl), uhci->fl, uhci->fl->dma_handle);
+		uhci->fl = NULL;
+	}
+
+	if (uhci->bus) {
+		usb_free_bus(uhci->bus);
+		uhci->bus = NULL;
+	}
+
+#ifdef CONFIG_PROC_FS
+	if (uhci->proc_entry) {
+		sprintf(buf, "hc%d", uhci->num);
+
+		remove_proc_entry(buf, uhci_proc_root);
+		uhci->proc_entry = NULL;
+	}
+#endif
+
+	free_uhci(uhci);
+}
+
+/*
+ * Allocate a frame list, and then setup the skeleton
+ *
+ * The hardware doesn't really know any difference
+ * in the queues, but the order does matter for the
+ * protocols higher up. The order is:
+ *
+ *  - any isochronous events handled before any
+ *    of the queues. We don't do that here, because
+ *    we'll create the actual TD entries on demand.
+ *  - The first queue is the interrupt queue.
+ *  - The second queue is the control queue, split into low and high speed
+ *  - The third queue is bulk queue.
+ *  - The fourth queue is the bandwidth reclamation queue, which loops back
+ *    to the high speed control queue.
+ */
+static int alloc_uhci(struct pci_dev *dev, unsigned int io_addr, unsigned int io_size)
+{
+	struct uhci *uhci;
+	int retval;
+	char buf[8], *bufp = buf;
+	int i, port;
+	struct usb_bus *bus;
+	dma_addr_t dma_handle;
+#ifdef CONFIG_PROC_FS
+	struct proc_dir_entry *ent;
+#endif
+
+	retval = -ENODEV;
+	if (pci_enable_device(dev) < 0) {
+		err("couldn't enable PCI device");
+		goto err_enable_device;
+	}
+
+	if (!dev->irq) {
+		err("found UHCI device with no IRQ assigned. check BIOS settings!");
+		goto err_invalid_irq;
+	}
+
+	if (!pci_dma_supported(dev, 0xFFFFFFFF)) {
+		err("PCI subsystem doesn't support 32 bit addressing?");
+		goto err_pci_dma_supported;
+	}
+
+	retval = -EBUSY;
+	if (!request_region(io_addr, io_size, "usb-uhci")) {
+		err("couldn't allocate I/O range %x - %x", io_addr,
+			io_addr + io_size - 1);
+		goto err_request_region;
+	}
+
+	pci_set_master(dev);
+
+#ifndef __sparc__
+	sprintf(buf, "%d", dev->irq);
+#else
+	bufp = __irq_itoa(dev->irq);
+#endif
+	printk(KERN_INFO __FILE__ ": USB UHCI at I/O 0x%x, IRQ %s\n",
+		io_addr, bufp);
+
+	if (pci_set_dma_mask(dev, 0xFFFFFFFF)) {
+		err("couldn't set PCI dma mask");
+		retval = -ENODEV;
+		goto err_pci_set_dma_mask;
+	}
+
+	uhci = kmalloc(sizeof(*uhci), GFP_KERNEL);
+	if (!uhci) {
+		err("couldn't allocate uhci structure");
+		retval = -ENOMEM;
+		goto err_alloc_uhci;
+	}
+
+	uhci->dev = dev;
+	uhci->irq = dev->irq;
+	uhci->io_addr = io_addr;
+	uhci->io_size = io_size;
+	pci_set_drvdata(dev, uhci);
+
+#ifdef CONFIG_PROC_FS
+	uhci->num = uhci_num++;
+
+	sprintf(buf, "hc%d", uhci->num);
+
+	ent = create_proc_entry(buf, S_IFREG|S_IRUGO|S_IWUSR, uhci_proc_root);
+	if (!ent) {
+		err("couldn't create uhci proc entry");
+		retval = -ENOMEM;
+		goto err_create_proc_entry;
+	}
+
+	ent->data = uhci;
+	ent->proc_fops = &uhci_proc_operations;
+	ent->size = 0;
+	uhci->proc_entry = ent;
+#endif
+
+	/* Reset here so we don't get any interrupts from an old setup */
+	/*  or broken setup */
+	reset_hc(uhci);
+
+	uhci->fsbr = 0;
+	uhci->fsbrtimeout = 0;
+
+	uhci->is_suspended = 0;
+
+	spin_lock_init(&uhci->qh_remove_list_lock);
+	INIT_LIST_HEAD(&uhci->qh_remove_list);
+
+	spin_lock_init(&uhci->urb_remove_list_lock);
+	INIT_LIST_HEAD(&uhci->urb_remove_list);
+
+	spin_lock_init(&uhci->urb_list_lock);
+	INIT_LIST_HEAD(&uhci->urb_list);
+
+	spin_lock_init(&uhci->complete_list_lock);
+	INIT_LIST_HEAD(&uhci->complete_list);
+
+	spin_lock_init(&uhci->frame_list_lock);
+
+	/* We need exactly one page (per UHCI specs), how convenient */
+	/* We assume that one page is atleast 4k (1024 frames * 4 bytes) */
+#if PAGE_SIZE < (4 * 1024)
+#error PAGE_SIZE is not atleast 4k
+#endif
+	uhci->fl = pci_alloc_consistent(uhci->dev, sizeof(*uhci->fl), &dma_handle);
+	if (!uhci->fl) {
+		err("unable to allocate consistent memory for frame list");
+		goto err_alloc_fl;
+	}
+
+	memset((void *)uhci->fl, 0, sizeof(*uhci->fl));
+
+	uhci->fl->dma_handle = dma_handle;
+
+	uhci->td_pool = pci_pool_create("uhci_td", uhci->dev,
+		sizeof(struct uhci_td), 16, 0, GFP_DMA | GFP_ATOMIC);
+	if (!uhci->td_pool) {
+		err("unable to create td pci_pool");
+		goto err_create_td_pool;
+	}
+
+	uhci->qh_pool = pci_pool_create("uhci_qh", uhci->dev,
+		sizeof(struct uhci_qh), 16, 0, GFP_DMA | GFP_ATOMIC);
+	if (!uhci->qh_pool) {
+		err("unable to create qh pci_pool");
+		goto err_create_qh_pool;
+	}
+
+	bus = usb_alloc_bus(&uhci_device_operations);
+	if (!bus) {
+		err("unable to allocate bus");
+		goto err_alloc_bus;
+	}
+
+	uhci->bus = bus;
+	bus->bus_name = dev->slot_name;
+	bus->hcpriv = uhci;
+
+	usb_register_bus(uhci->bus);
+
+	/* Initialize the root hub */
+
+	/* UHCI specs says devices must have 2 ports, but goes on to say */
+	/*  they may have more but give no way to determine how many they */
+	/*  have. However, according to the UHCI spec, Bit 7 is always set */
+	/*  to 1. So we try to use this to our advantage */
+	for (port = 0; port < (uhci->io_size - 0x10) / 2; port++) {
+		unsigned int portstatus;
+
+		portstatus = inw(uhci->io_addr + 0x10 + (port * 2));
+		if (!(portstatus & 0x0080))
+			break;
+	}
+	if (debug)
+		info("detected %d ports", port);
+
+	/* This is experimental so anything less than 2 or greater than 8 is */
+	/*  something weird and we'll ignore it */
+	if (port < 2 || port > 8) {
+		info("port count misdetected? forcing to 2 ports");
+		port = 2;
+	}
+
+	uhci->rh.numports = port;
+
+	uhci->bus->root_hub = uhci->rh.dev = usb_alloc_dev(NULL, uhci->bus);
+	if (!uhci->rh.dev) {
+		err("unable to allocate root hub");
+		goto err_alloc_root_hub;
+	}
+
+	uhci->skeltd[0] = uhci_alloc_td(uhci, uhci->rh.dev);
+	if (!uhci->skeltd[0]) {
+		err("unable to allocate TD 0");
+		goto err_alloc_skeltd;
+	}
+
+	/*
+	 * 9 Interrupt queues; link int2 to int1, int4 to int2, etc
+	 * then link int1 to control and control to bulk
+	 */
+	for (i = 1; i < 9; i++) {
+		struct uhci_td *td;
+
+		td = uhci->skeltd[i] = uhci_alloc_td(uhci, uhci->rh.dev);
+		if (!td) {
+			err("unable to allocate TD %d", i);
+			goto err_alloc_skeltd;
+		}
+
+		uhci_fill_td(td, 0, (UHCI_NULL_DATA_SIZE << 21) | (0x7f << 8) | USB_PID_IN, 0);
+		td->link = uhci->skeltd[i - 1]->dma_handle;
+	}
+
+	uhci->skel_term_td = uhci_alloc_td(uhci, uhci->rh.dev);
+	if (!uhci->skel_term_td) {
+		err("unable to allocate skel TD term");
+		goto err_alloc_skeltd;
+	}
+
+	for (i = 0; i < UHCI_NUM_SKELQH; i++) {
+		uhci->skelqh[i] = uhci_alloc_qh(uhci, uhci->rh.dev);
+		if (!uhci->skelqh[i]) {
+			err("unable to allocate QH %d", i);
+			goto err_alloc_skelqh;
+		}
+	}
+
+	uhci_fill_td(uhci->skel_int1_td, 0, (UHCI_NULL_DATA_SIZE << 21) | (0x7f << 8) | USB_PID_IN, 0);
+	uhci->skel_int1_td->link = uhci->skel_ls_control_qh->dma_handle | UHCI_PTR_QH;
+
+	uhci->skel_ls_control_qh->link = uhci->skel_hs_control_qh->dma_handle | UHCI_PTR_QH;
+	uhci->skel_ls_control_qh->element = UHCI_PTR_TERM;
+
+	uhci->skel_hs_control_qh->link = uhci->skel_bulk_qh->dma_handle | UHCI_PTR_QH;
+	uhci->skel_hs_control_qh->element = UHCI_PTR_TERM;
+
+	uhci->skel_bulk_qh->link = uhci->skel_term_qh->dma_handle | UHCI_PTR_QH;
+	uhci->skel_bulk_qh->element = UHCI_PTR_TERM;
+
+	/* This dummy TD is to work around a bug in Intel PIIX controllers */
+	uhci_fill_td(uhci->skel_term_td, 0, (UHCI_NULL_DATA_SIZE << 21) | (0x7f << 8) | USB_PID_IN, 0);
+	uhci->skel_term_td->link = uhci->skel_term_td->dma_handle;
+
+	uhci->skel_term_qh->link = UHCI_PTR_TERM;
+	uhci->skel_term_qh->element = uhci->skel_term_td->dma_handle;
+
+	/*
+	 * Fill the frame list: make all entries point to
+	 * the proper interrupt queue.
+	 *
+	 * This is probably silly, but it's a simple way to
+	 * scatter the interrupt queues in a way that gives
+	 * us a reasonable dynamic range for irq latencies.
+	 */
+	for (i = 0; i < UHCI_NUMFRAMES; i++) {
+		int irq = 0;
+
+		if (i & 1) {
+			irq++;
+			if (i & 2) {
+				irq++;
+				if (i & 4) { 
+					irq++;
+					if (i & 8) { 
+						irq++;
+						if (i & 16) {
+							irq++;
+							if (i & 32) {
+								irq++;
+								if (i & 64)
+									irq++;
+							}
+						}
+					}
+				}
+			}
+		}
+
+		/* Only place we don't use the frame list routines */
+		uhci->fl->frame[i] =  uhci->skeltd[irq]->dma_handle;
+	}
+
+	start_hc(uhci);
+
+	if (request_irq(dev->irq, uhci_interrupt, SA_SHIRQ, "usb-uhci", uhci))
+		goto err_request_irq;
+
+	/* disable legacy emulation */
+	pci_write_config_word(uhci->dev, USBLEGSUP, USBLEGSUP_DEFAULT);
+
+	usb_connect(uhci->rh.dev);
+
+	if (usb_new_device(uhci->rh.dev) != 0) {
+		err("unable to start root hub");
+		retval = -ENOMEM;
+		goto err_start_root_hub;
+	}
+
+	return 0;
+
+/*
+ * error exits:
+ */
+err_start_root_hub:
+	free_irq(uhci->irq, uhci);
+	uhci->irq = -1;
+
+err_request_irq:
+	for (i = 0; i < UHCI_NUM_SKELQH; i++)
+		if (uhci->skelqh[i]) {
+			uhci_free_qh(uhci, uhci->skelqh[i]);
+			uhci->skelqh[i] = NULL;
+		}
+
+err_alloc_skelqh:
+	for (i = 0; i < UHCI_NUM_SKELTD; i++)
+		if (uhci->skeltd[i]) {
+			uhci_free_td(uhci, uhci->skeltd[i]);
+			uhci->skeltd[i] = NULL;
+		}
+
+err_alloc_skeltd:
+	usb_free_dev(uhci->rh.dev);
+	uhci->rh.dev = NULL;
+
+err_alloc_root_hub:
+	usb_free_bus(uhci->bus);
+	uhci->bus = NULL;
+
+err_alloc_bus:
+	pci_pool_destroy(uhci->qh_pool);
+	uhci->qh_pool = NULL;
+
+err_create_qh_pool:
+	pci_pool_destroy(uhci->td_pool);
+	uhci->td_pool = NULL;
+
+err_create_td_pool:
+	pci_free_consistent(uhci->dev, sizeof(*uhci->fl), uhci->fl, uhci->fl->dma_handle);
+	uhci->fl = NULL;
+
+err_alloc_fl:
+#ifdef CONFIG_PROC_FS
+	remove_proc_entry(buf, uhci_proc_root);
+	uhci->proc_entry = NULL;
+
+err_create_proc_entry:
+	free_uhci(uhci);
+#endif
+
+err_alloc_uhci:
+
+err_pci_set_dma_mask:
+	release_region(io_addr, io_size);
+
+err_request_region:
+
+err_pci_dma_supported:
+
+err_invalid_irq:
+
+err_enable_device:
+
+	return retval;
+}
+
+static int __devinit uhci_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
+{
+	int i;
+
+	/* Search for the IO base address.. */
+	for (i = 0; i < 6; i++) {
+		unsigned int io_addr = pci_resource_start(dev, i);
+		unsigned int io_size = pci_resource_len(dev, i);
+
+		/* IO address? */
+		if (!(pci_resource_flags(dev, i) & IORESOURCE_IO))
+			continue;
+
+		return alloc_uhci(dev, io_addr, io_size);
+	}
+
+	return -ENODEV;
+}
+
+static void __devexit uhci_pci_remove(struct pci_dev *dev)
+{
+	struct uhci *uhci = pci_get_drvdata(dev);
+
+	if (uhci->bus->root_hub)
+		usb_disconnect(&uhci->bus->root_hub);
+
+	usb_deregister_bus(uhci->bus);
+
+	/*
+	 * At this point, we're guaranteed that no new connects can be made
+	 * to this bus since there are no more parents
+	 */
+	uhci_free_pending_qhs(uhci);
+	uhci_remove_pending_qhs(uhci);
+
+	reset_hc(uhci);
+	release_region(uhci->io_addr, uhci->io_size);
+
+	uhci_free_pending_qhs(uhci);
+
+	release_uhci(uhci);
+}
+
+#ifdef CONFIG_PM
+static int uhci_pci_suspend(struct pci_dev *dev, u32 state)
+{
+	suspend_hc((struct uhci *) pci_get_drvdata(dev));
+	return 0;
+}
+
+static int uhci_pci_resume(struct pci_dev *dev)
+{
+	reset_hc((struct uhci *) pci_get_drvdata(dev));
+	start_hc((struct uhci *) pci_get_drvdata(dev));
+	return 0;
+}
+#endif
+
+static const struct pci_device_id __devinitdata uhci_pci_ids[] = { {
+
+	/* handle any USB UHCI controller */
+	class: 		((PCI_CLASS_SERIAL_USB << 8) | 0x00),
+	class_mask: 	~0,
+
+	/* no matter who makes it */
+	vendor:		PCI_ANY_ID,
+	device:		PCI_ANY_ID,
+	subvendor:	PCI_ANY_ID,
+	subdevice:	PCI_ANY_ID,
+
+	}, { /* end: all zeroes */ }
+};
+
+MODULE_DEVICE_TABLE(pci, uhci_pci_ids);
+
+static struct pci_driver uhci_pci_driver = {
+	name:		"usb-uhci",
+	id_table:	uhci_pci_ids,
+
+	probe:		uhci_pci_probe,
+	remove:		__devexit_p(uhci_pci_remove),
+
+#ifdef	CONFIG_PM
+	suspend:	uhci_pci_suspend,
+	resume:		uhci_pci_resume,
+#endif	/* PM */
+};
+
+ 
+static int __init uhci_hcd_init(void)
+{
+	int retval = -ENOMEM;
+
+	info(DRIVER_DESC " " DRIVER_VERSION);
+
+	if (debug) {
+		errbuf = kmalloc(ERRBUF_LEN, GFP_KERNEL);
+		if (!errbuf)
+			goto errbuf_failed;
+	}
+
+#ifdef CONFIG_PROC_FS
+	uhci_proc_root = create_proc_entry("driver/uhci", S_IFDIR, 0);
+	if (!uhci_proc_root)
+		goto proc_failed;
+#endif
+
+	uhci_up_cachep = kmem_cache_create("uhci_urb_priv",
+		sizeof(struct urb_priv), 0, 0, NULL, NULL);
+	if (!uhci_up_cachep)
+		goto up_failed;
+
+	retval = pci_module_init(&uhci_pci_driver);
+	if (retval)
+		goto init_failed;
+
+	return 0;
+
+init_failed:
+	if (kmem_cache_destroy(uhci_up_cachep))
+		printk(KERN_INFO "uhci: not all urb_priv's were freed\n");
+
+up_failed:
+
+#ifdef CONFIG_PROC_FS
+	remove_proc_entry("driver/uhci", 0);
+
+proc_failed:
+#endif
+	if (errbuf)
+		kfree(errbuf);
+
+errbuf_failed:
+
+	return retval;
+}
+
+static void __exit uhci_hcd_cleanup(void) 
+{
+	pci_unregister_driver(&uhci_pci_driver);
+	
+	if (kmem_cache_destroy(uhci_up_cachep))
+		printk(KERN_INFO "uhci: not all urb_priv's were freed\n");
+
+#ifdef CONFIG_PROC_FS
+	remove_proc_entry("driver/uhci", 0);
+#endif
+
+	if (errbuf)
+		kfree(errbuf);
+}
+
+module_init(uhci_hcd_init);
+module_exit(uhci_hcd_cleanup);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
diff -urN linux-2.4.21-pre5/drivers/usb/host/uhci.h linux-2.4.21-pre6/drivers/usb/host/uhci.h
--- linux-2.4.21-pre5/drivers/usb/host/uhci.h	Wed Dec 31 16:00:00 1969
+++ linux-2.4.21-pre6/drivers/usb/host/uhci.h	Wed Mar 26 16:11:39 2003
@@ -0,0 +1,441 @@
+#ifndef __LINUX_UHCI_H
+#define __LINUX_UHCI_H
+
+#include <linux/list.h>
+#include <linux/usb.h>
+
+/*
+ * Universal Host Controller Interface data structures and defines
+ */
+
+/* Command register */
+#define USBCMD		0
+#define   USBCMD_RS		0x0001	/* Run/Stop */
+#define   USBCMD_HCRESET	0x0002	/* Host reset */
+#define   USBCMD_GRESET		0x0004	/* Global reset */
+#define   USBCMD_EGSM		0x0008	/* Global Suspend Mode */
+#define   USBCMD_FGR		0x0010	/* Force Global Resume */
+#define   USBCMD_SWDBG		0x0020	/* SW Debug mode */
+#define   USBCMD_CF		0x0040	/* Config Flag (sw only) */
+#define   USBCMD_MAXP		0x0080	/* Max Packet (0 = 32, 1 = 64) */
+
+/* Status register */
+#define USBSTS		2
+#define   USBSTS_USBINT		0x0001	/* Interrupt due to IOC */
+#define   USBSTS_ERROR		0x0002	/* Interrupt due to error */
+#define   USBSTS_RD		0x0004	/* Resume Detect */
+#define   USBSTS_HSE		0x0008	/* Host System Error - basically PCI problems */
+#define   USBSTS_HCPE		0x0010	/* Host Controller Process Error - the scripts were buggy */
+#define   USBSTS_HCH		0x0020	/* HC Halted */
+
+/* Interrupt enable register */
+#define USBINTR		4
+#define   USBINTR_TIMEOUT	0x0001	/* Timeout/CRC error enable */
+#define   USBINTR_RESUME	0x0002	/* Resume interrupt enable */
+#define   USBINTR_IOC		0x0004	/* Interrupt On Complete enable */
+#define   USBINTR_SP		0x0008	/* Short packet interrupt enable */
+
+#define USBFRNUM	6
+#define USBFLBASEADD	8
+#define USBSOF		12
+
+/* USB port status and control registers */
+#define USBPORTSC1	16
+#define USBPORTSC2	18
+#define   USBPORTSC_CCS		0x0001	/* Current Connect Status ("device present") */
+#define   USBPORTSC_CSC		0x0002	/* Connect Status Change */
+#define   USBPORTSC_PE		0x0004	/* Port Enable */
+#define   USBPORTSC_PEC		0x0008	/* Port Enable Change */
+#define   USBPORTSC_LS		0x0030	/* Line Status */
+#define   USBPORTSC_RD		0x0040	/* Resume Detect */
+#define   USBPORTSC_LSDA	0x0100	/* Low Speed Device Attached */
+#define   USBPORTSC_PR		0x0200	/* Port Reset */
+#define   USBPORTSC_SUSP	0x1000	/* Suspend */
+
+/* Legacy support register */
+#define USBLEGSUP		0xc0
+#define   USBLEGSUP_DEFAULT	0x2000	/* only PIRQ enable set */
+
+#define UHCI_NULL_DATA_SIZE	0x7FF	/* for UHCI controller TD */
+
+#define UHCI_PTR_BITS		0x000F
+#define UHCI_PTR_TERM		0x0001
+#define UHCI_PTR_QH		0x0002
+#define UHCI_PTR_DEPTH		0x0004
+
+#define UHCI_NUMFRAMES		1024	/* in the frame list [array] */
+#define UHCI_MAX_SOF_NUMBER	2047	/* in an SOF packet */
+#define CAN_SCHEDULE_FRAMES	1000	/* how far future frames can be scheduled */
+
+struct uhci_frame_list {
+	__u32 frame[UHCI_NUMFRAMES];
+
+	void *frame_cpu[UHCI_NUMFRAMES];
+
+	dma_addr_t dma_handle;
+};
+
+struct urb_priv;
+
+struct uhci_qh {
+	/* Hardware fields */
+	__u32 link;			/* Next queue */
+	__u32 element;			/* Queue element pointer */
+
+	/* Software fields */
+	dma_addr_t dma_handle;
+
+	struct usb_device *dev;
+	struct urb_priv *urbp;
+
+	struct list_head list;		/* P: uhci->frame_list_lock */
+	struct list_head remove_list;	/* P: uhci->remove_list_lock */
+} __attribute__((aligned(16)));
+
+/*
+ * for TD <status>:
+ */
+#define TD_CTRL_SPD		(1 << 29)	/* Short Packet Detect */
+#define TD_CTRL_C_ERR_MASK	(3 << 27)	/* Error Counter bits */
+#define TD_CTRL_C_ERR_SHIFT	27
+#define TD_CTRL_LS		(1 << 26)	/* Low Speed Device */
+#define TD_CTRL_IOS		(1 << 25)	/* Isochronous Select */
+#define TD_CTRL_IOC		(1 << 24)	/* Interrupt on Complete */
+#define TD_CTRL_ACTIVE		(1 << 23)	/* TD Active */
+#define TD_CTRL_STALLED		(1 << 22)	/* TD Stalled */
+#define TD_CTRL_DBUFERR		(1 << 21)	/* Data Buffer Error */
+#define TD_CTRL_BABBLE		(1 << 20)	/* Babble Detected */
+#define TD_CTRL_NAK		(1 << 19)	/* NAK Received */
+#define TD_CTRL_CRCTIMEO	(1 << 18)	/* CRC/Time Out Error */
+#define TD_CTRL_BITSTUFF	(1 << 17)	/* Bit Stuff Error */
+#define TD_CTRL_ACTLEN_MASK	0x7FF		/* actual length, encoded as n - 1 */
+
+#define TD_CTRL_ANY_ERROR	(TD_CTRL_STALLED | TD_CTRL_DBUFERR | \
+				 TD_CTRL_BABBLE | TD_CTRL_CRCTIME | TD_CTRL_BITSTUFF)
+
+#define uhci_status_bits(ctrl_sts)	(ctrl_sts & 0xFE0000)
+#define uhci_actual_length(ctrl_sts)	((ctrl_sts + 1) & TD_CTRL_ACTLEN_MASK) /* 1-based */
+
+/*
+ * for TD <info>: (a.k.a. Token)
+ */
+#define TD_TOKEN_TOGGLE_SHIFT	19
+#define TD_TOKEN_TOGGLE		(1 << 19)
+#define TD_TOKEN_PID_MASK	0xFF
+#define TD_TOKEN_EXPLEN_MASK	0x7FF		/* expected length, encoded as n - 1 */
+
+#define uhci_maxlen(token)	((token) >> 21)
+#define uhci_expected_length(info) (((info >> 21) + 1) & TD_TOKEN_EXPLEN_MASK) /* 1-based */
+#define uhci_toggle(token)	(((token) >> TD_TOKEN_TOGGLE_SHIFT) & 1)
+#define uhci_endpoint(token)	(((token) >> 15) & 0xf)
+#define uhci_devaddr(token)	(((token) >> 8) & 0x7f)
+#define uhci_devep(token)	(((token) >> 8) & 0x7ff)
+#define uhci_packetid(token)	((token) & TD_TOKEN_PID_MASK)
+#define uhci_packetout(token)	(uhci_packetid(token) != USB_PID_IN)
+#define uhci_packetin(token)	(uhci_packetid(token) == USB_PID_IN)
+
+/*
+ * The documentation says "4 words for hardware, 4 words for software".
+ *
+ * That's silly, the hardware doesn't care. The hardware only cares that
+ * the hardware words are 16-byte aligned, and we can have any amount of
+ * sw space after the TD entry as far as I can tell.
+ *
+ * But let's just go with the documentation, at least for 32-bit machines.
+ * On 64-bit machines we probably want to take advantage of the fact that
+ * hw doesn't really care about the size of the sw-only area.
+ *
+ * Alas, not anymore, we have more than 4 words for software, woops.
+ * Everything still works tho, surprise! -jerdfelt
+ */
+struct uhci_td {
+	/* Hardware fields */
+	__u32 link;
+	__u32 status;
+	__u32 info;
+	__u32 buffer;
+
+	/* Software fields */
+	dma_addr_t dma_handle;
+
+	struct usb_device *dev;
+	struct urb *urb;
+
+	struct list_head list;		/* P: urb->lock */
+
+	int frame;
+	struct list_head fl_list;	/* P: uhci->frame_list_lock */
+} __attribute__((aligned(16)));
+
+/*
+ * There are various standard queues. We set up several different
+ * queues for each of the three basic queue types: interrupt,
+ * control, and bulk.
+ *
+ *  - There are various different interrupt latencies: ranging from
+ *    every other USB frame (2 ms apart) to every 256 USB frames (ie
+ *    256 ms apart). Make your choice according to how obnoxious you
+ *    want to be on the wire, vs how critical latency is for you.
+ *  - The control list is done every frame.
+ *  - There are 4 bulk lists, so that up to four devices can have a
+ *    bulk list of their own and when run concurrently all four lists
+ *    will be be serviced.
+ *
+ * This is a bit misleading, there are various interrupt latencies, but they
+ * vary a bit, interrupt2 isn't exactly 2ms, it can vary up to 4ms since the
+ * other queues can "override" it. interrupt4 can vary up to 8ms, etc. Minor
+ * problem
+ *
+ * In the case of the root hub, these QH's are just head's of qh's. Don't
+ * be scared, it kinda makes sense. Look at this wonderful picture care of
+ * Linus:
+ *
+ *  generic-  ->  dev1-  ->  generic-  ->  dev1-  ->  control-  ->  bulk- -> ...
+ *   iso-QH      iso-QH       irq-QH      irq-QH        QH           QH
+ *      |           |            |           |           |            |
+ *     End     dev1-iso-TD1     End     dev1-irq-TD1    ...          ... 
+ *                  |
+ *             dev1-iso-TD2
+ *                  |
+ *                ....
+ *
+ * This may vary a bit (the UHCI docs don't explicitly say you can put iso
+ * transfers in QH's and all of their pictures don't have that either) but
+ * other than that, that is what we're doing now
+ *
+ * And now we don't put Iso transfers in QH's, so we don't waste one on it
+ * --jerdfelt
+ *
+ * To keep with Linus' nomenclature, this is called the QH skeleton. These
+ * labels (below) are only signficant to the root hub's QH's
+ */
+
+#define UHCI_NUM_SKELTD		10
+#define skel_int1_td		skeltd[0]
+#define skel_int2_td		skeltd[1]
+#define skel_int4_td		skeltd[2]
+#define skel_int8_td		skeltd[3]
+#define skel_int16_td		skeltd[4]
+#define skel_int32_td		skeltd[5]
+#define skel_int64_td		skeltd[6]
+#define skel_int128_td		skeltd[7]
+#define skel_int256_td		skeltd[8]
+#define skel_term_td		skeltd[9]	/* To work around PIIX UHCI bug */
+
+#define UHCI_NUM_SKELQH		4
+#define skel_ls_control_qh	skelqh[0]
+#define skel_hs_control_qh	skelqh[1]
+#define skel_bulk_qh		skelqh[2]
+#define skel_term_qh		skelqh[3]
+
+/*
+ * Search tree for determining where <interval> fits in the
+ * skelqh[] skeleton.
+ *
+ * An interrupt request should be placed into the slowest skelqh[]
+ * which meets the interval/period/frequency requirement.
+ * An interrupt request is allowed to be faster than <interval> but not slower.
+ *
+ * For a given <interval>, this function returns the appropriate/matching
+ * skelqh[] index value.
+ *
+ * NOTE: For UHCI, we don't really need int256_qh since the maximum interval
+ * is 255 ms.  However, we do need an int1_qh since 1 is a valid interval
+ * and we should meet that frequency when requested to do so.
+ * This will require some change(s) to the UHCI skeleton.
+ */
+static inline int __interval_to_skel(int interval)
+{
+	if (interval < 16) {
+		if (interval < 4) {
+			if (interval < 2)
+				return 0;	/* int1 for 0-1 ms */
+			return 1;		/* int2 for 2-3 ms */
+		}
+		if (interval < 8)
+			return 2;		/* int4 for 4-7 ms */
+		return 3;			/* int8 for 8-15 ms */
+	}
+	if (interval < 64) {
+		if (interval < 32)
+			return 4;		/* int16 for 16-31 ms */
+		return 5;			/* int32 for 32-63 ms */
+	}
+	if (interval < 128)
+		return 6;			/* int64 for 64-127 ms */
+	return 7;				/* int128 for 128-255 ms (Max.) */
+}
+
+struct virt_root_hub {
+	struct usb_device *dev;
+	int devnum;		/* Address of Root Hub endpoint */
+	struct urb *urb;
+	void *int_addr;
+	int send;
+	int interval;
+	int numports;
+	int c_p_r[8];
+	struct timer_list rh_int_timer;
+};
+
+/*
+ * This describes the full uhci information.
+ *
+ * Note how the "proper" USB information is just
+ * a subset of what the full implementation needs.
+ */
+struct uhci {
+	struct pci_dev *dev;
+
+#ifdef CONFIG_PROC_FS
+	/* procfs */
+	int num;
+	struct proc_dir_entry *proc_entry;
+#endif
+
+	/* Grabbed from PCI */
+	int irq;
+	unsigned int io_addr;
+	unsigned int io_size;
+
+	struct pci_pool *qh_pool;
+	struct pci_pool *td_pool;
+
+	struct usb_bus *bus;
+
+	struct uhci_td *skeltd[UHCI_NUM_SKELTD];	/* Skeleton TD's */
+	struct uhci_qh *skelqh[UHCI_NUM_SKELQH];	/* Skeleton QH's */
+
+	spinlock_t frame_list_lock;
+	struct uhci_frame_list *fl;		/* P: uhci->frame_list_lock */
+	int fsbr;				/* Full speed bandwidth reclamation */
+	unsigned long fsbrtimeout;		/* FSBR delay */
+	int is_suspended;
+
+	/* Main list of URB's currently controlled by this HC */
+	spinlock_t urb_list_lock;
+	struct list_head urb_list;		/* P: uhci->urb_list_lock */
+
+	/* List of QH's that are done, but waiting to be unlinked (race) */
+	spinlock_t qh_remove_list_lock;
+	struct list_head qh_remove_list;	/* P: uhci->qh_remove_list_lock */
+
+	/* List of asynchronously unlinked URB's */
+	spinlock_t urb_remove_list_lock;
+	struct list_head urb_remove_list;	/* P: uhci->urb_remove_list_lock */
+
+	/* List of URB's awaiting completion callback */
+	spinlock_t complete_list_lock;
+	struct list_head complete_list;		/* P: uhci->complete_list_lock */
+
+	struct virt_root_hub rh;	/* private data of the virtual root hub */
+};
+
+struct urb_priv {
+	struct urb *urb;
+	struct usb_device *dev;
+
+	dma_addr_t setup_packet_dma_handle;
+	dma_addr_t transfer_buffer_dma_handle;
+
+	struct uhci_qh *qh;		/* QH for this URB */
+	struct list_head td_list;	/* P: urb->lock */
+
+	int fsbr : 1;			/* URB turned on FSBR */
+	int fsbr_timeout : 1;		/* URB timed out on FSBR */
+	int queued : 1;			/* QH was queued (not linked in) */
+	int short_control_packet : 1;	/* If we get a short packet during */
+					/*  a control transfer, retrigger */
+					/*  the status phase */
+
+	int status;			/* Final status */
+
+	unsigned long inserttime;	/* In jiffies */
+	unsigned long fsbrtime;		/* In jiffies */
+
+	struct list_head queue_list;	/* P: uhci->frame_list_lock */
+	struct list_head complete_list;	/* P: uhci->complete_list_lock */
+};
+
+/*
+ * Locking in uhci.c
+ *
+ * spinlocks are used extensively to protect the many lists and data
+ * structures we have. It's not that pretty, but it's necessary. We
+ * need to be done with all of the locks (except complete_list_lock) when
+ * we call urb->complete. I've tried to make it simple enough so I don't
+ * have to spend hours racking my brain trying to figure out if the
+ * locking is safe.
+ *
+ * Here's the safe locking order to prevent deadlocks:
+ *
+ * #1 uhci->urb_list_lock
+ * #2 urb->lock
+ * #3 uhci->urb_remove_list_lock, uhci->frame_list_lock, 
+ *   uhci->qh_remove_list_lock
+ * #4 uhci->complete_list_lock
+ *
+ * If you're going to grab 2 or more locks at once, ALWAYS grab the lock
+ * at the lowest level FIRST and NEVER grab locks at the same level at the
+ * same time.
+ * 
+ * So, if you need uhci->urb_list_lock, grab it before you grab urb->lock
+ */
+
+/* -------------------------------------------------------------------------
+   Virtual Root HUB
+   ------------------------------------------------------------------------- */
+/* destination of request */
+#define RH_DEVICE		0x00
+#define RH_INTERFACE		0x01
+#define RH_ENDPOINT		0x02
+#define RH_OTHER		0x03
+
+#define RH_CLASS		0x20
+#define RH_VENDOR		0x40
+
+/* 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
+/* Our Vendor Specific Request */
+#define RH_SET_EP		0x2000
+
+/* Hub port features */
+#define RH_PORT_CONNECTION	0x00
+#define RH_PORT_ENABLE		0x01
+#define RH_PORT_SUSPEND		0x02
+#define RH_PORT_OVER_CURRENT	0x03
+#define RH_PORT_RESET		0x04
+#define RH_PORT_POWER		0x08
+#define RH_PORT_LOW_SPEED	0x09
+#define RH_C_PORT_CONNECTION	0x10
+#define RH_C_PORT_ENABLE	0x11
+#define RH_C_PORT_SUSPEND	0x12
+#define RH_C_PORT_OVER_CURRENT	0x13
+#define RH_C_PORT_RESET		0x14
+
+/* Hub features */
+#define RH_C_HUB_LOCAL_POWER	0x00
+#define RH_C_HUB_OVER_CURRENT	0x01
+#define RH_DEVICE_REMOTE_WAKEUP	0x00
+#define RH_ENDPOINT_STALL	0x01
+
+/* Our Vendor Specific feature */
+#define RH_REMOVE_EP		0x00
+
+#define RH_ACK			0x01
+#define RH_REQ_ERR		-1
+#define RH_NACK			0x00
+
+#endif
+
diff -urN linux-2.4.21-pre5/drivers/usb/host/usb-ohci.c linux-2.4.21-pre6/drivers/usb/host/usb-ohci.c
--- linux-2.4.21-pre5/drivers/usb/host/usb-ohci.c	Wed Dec 31 16:00:00 1969
+++ linux-2.4.21-pre6/drivers/usb/host/usb-ohci.c	Wed Mar 26 16:11:39 2003
@@ -0,0 +1,2938 @@
+/*
+ * URB OHCI HCD (Host Controller Driver) for USB.
+ *
+ * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
+ * (C) Copyright 2000-2001 David Brownell <dbrownell@users.sourceforge.net>
+ * 
+ * [ Initialisation is based on Linus'  ]
+ * [ uhci code and gregs ohci fragments ]
+ * [ (C) Copyright 1999 Linus Torvalds  ]
+ * [ (C) Copyright 1999 Gregory P. Smith]
+ * 
+ * 
+ * History:
+ *
+ * 2002/10/22 OHCI_USB_OPER for ALi lockup in IBM i1200 (ALEX <thchou@ali>)
+ * 2002/03/08 interrupt unlink fix (Matt Hughes), better cleanup on
+ *	load failure (Matthew Frederickson)
+ * 2002/01/20 async unlink fixes:  return -EINPROGRESS (per spec) and
+ *	make interrupt unlink-in-completion work (db)
+ * 2001/09/19 USB_ZERO_PACKET support (Jean Tourrilhes)
+ * 2001/07/17 power management and pmac cleanup (Benjamin Herrenschmidt)
+ * 2001/03/24 td/ed hashing to remove bus_to_virt (Steve Longerbeam);
+ 	pci_map_single (db)
+ * 2001/03/21 td and dev/ed allocation uses new pci_pool API (db)
+ * 2001/03/07 hcca allocation uses pci_alloc_consistent (Steve Longerbeam)
+ *
+ * 2000/09/26 fixed races in removing the private portion of the urb
+ * 2000/09/07 disable bulk and control lists when unlinking the last
+ *	endpoint descriptor in order to avoid unrecoverable errors on
+ *	the Lucent chips. (rwc@sgi)
+ * 2000/08/29 use bandwidth claiming hooks (thanks Randy!), fix some
+ *	urb unlink probs, indentation fixes
+ * 2000/08/11 various oops fixes mostly affecting iso and cleanup from
+ *	device unplugs.
+ * 2000/06/28 use PCI hotplug framework, for better power management
+ *	and for Cardbus support (David Brownell)
+ * 2000/earlier:  fixes for NEC/Lucent chips; suspend/resume handling
+ *	when the controller loses power; handle UE; cleanup; ...
+ *
+ * v5.2 1999/12/07 URB 3rd preview, 
+ * v5.1 1999/11/30 URB 2nd preview, cpia, (usb-scsi)
+ * v5.0 1999/11/22 URB Technical preview, Paul Mackerras powerbook susp/resume 
+ * 	i386: HUB, Keyboard, Mouse, Printer 
+ *
+ * v4.3 1999/10/27 multiple HCs, bulk_request
+ * v4.2 1999/09/05 ISO API alpha, new dev alloc, neg Error-codes
+ * v4.1 1999/08/27 Randy Dunlap's - ISO API first impl.
+ * v4.0 1999/08/18 
+ * v3.0 1999/06/25 
+ * v2.1 1999/05/09  code clean up
+ * v2.0 1999/05/04 
+ * v1.0 1999/04/27 initial release
+ */
+ 
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/timer.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>  /* for in_interrupt() */
+#undef DEBUG
+#include <linux/usb.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+#include <asm/unaligned.h>
+
+#define OHCI_USE_NPS		// force NoPowerSwitching mode
+// #define OHCI_VERBOSE_DEBUG	/* not always helpful */
+
+#include "usb-ohci.h"
+
+#include "../hcd.h"
+
+#ifdef CONFIG_PMAC_PBOOK
+#include <asm/machdep.h>
+#include <asm/pmac_feature.h>
+#include <asm/pci-bridge.h>
+#ifndef CONFIG_PM
+#define CONFIG_PM
+#endif
+#endif
+
+
+/*
+ * Version Information
+ */
+#define DRIVER_VERSION "v5.3"
+#define DRIVER_AUTHOR "Roman Weissgaerber <weissg@vienna.at>, David Brownell"
+#define DRIVER_DESC "USB OHCI Host Controller Driver"
+
+/* For initializing controller (mask in an HCFS mode too) */
+#define	OHCI_CONTROL_INIT \
+	(OHCI_CTRL_CBSR & 0x3) | OHCI_CTRL_IE | OHCI_CTRL_PLE
+
+#define OHCI_UNLINK_TIMEOUT	(HZ / 10)
+
+static LIST_HEAD (ohci_hcd_list);
+static spinlock_t usb_ed_lock = SPIN_LOCK_UNLOCKED;
+
+
+/*-------------------------------------------------------------------------*/
+
+/* AMD-756 (D2 rev) reports corrupt register contents in some cases.
+ * The erratum (#4) description is incorrect.  AMD's workaround waits
+ * till some bits (mostly reserved) are clear; ok for all revs.
+ */
+#define read_roothub(hc, register, mask) ({ \
+	u32 temp = readl (&hc->regs->roothub.register); \
+	if (hc->flags & OHCI_QUIRK_AMD756) \
+		while (temp & mask) \
+			temp = readl (&hc->regs->roothub.register); \
+	temp; })
+
+static u32 roothub_a (struct ohci *hc)
+	{ return read_roothub (hc, a, 0xfc0fe000); }
+static inline u32 roothub_b (struct ohci *hc)
+	{ return readl (&hc->regs->roothub.b); }
+static inline u32 roothub_status (struct ohci *hc)
+	{ return readl (&hc->regs->roothub.status); }
+static u32 roothub_portstatus (struct ohci *hc, int i)
+	{ return read_roothub (hc, portstatus [i], 0xffe0fce0); }
+
+
+/*-------------------------------------------------------------------------*
+ * URB support functions 
+ *-------------------------------------------------------------------------*/ 
+ 
+/* free HCD-private data associated with this URB */
+
+static void urb_free_priv (struct ohci *hc, urb_priv_t * urb_priv)
+{
+	int		i;
+	int		last = urb_priv->length - 1;
+	int		len;
+	int		dir;
+	struct td	*td;
+
+	if (last >= 0) {
+
+		/* ISOC, BULK, INTR data buffer starts at td 0 
+		 * CTRL setup starts at td 0 */
+		td = urb_priv->td [0];
+
+		len = td->urb->transfer_buffer_length,
+		dir = usb_pipeout (td->urb->pipe)
+					? PCI_DMA_TODEVICE
+					: PCI_DMA_FROMDEVICE;
+
+		/* unmap CTRL URB setup */
+		if (usb_pipecontrol (td->urb->pipe)) {
+			pci_unmap_single (hc->ohci_dev, 
+					td->data_dma, 8, PCI_DMA_TODEVICE);
+			
+			/* CTRL data buffer starts at td 1 if len > 0 */
+			if (len && last > 0)
+				td = urb_priv->td [1]; 		
+		} 
+
+		/* unmap data buffer */
+		if (len && td->data_dma)
+			pci_unmap_single (hc->ohci_dev, td->data_dma, len, dir);
+		
+		for (i = 0; i <= last; i++) {
+			td = urb_priv->td [i];
+			if (td)
+				td_free (hc, td);
+		}
+	}
+
+	kfree (urb_priv);
+}
+ 
+static void urb_rm_priv_locked (struct urb * urb) 
+{
+	urb_priv_t * urb_priv = urb->hcpriv;
+	
+	if (urb_priv) {
+		urb->hcpriv = NULL;
+
+#ifdef	DO_TIMEOUTS
+		if (urb->timeout) {
+			list_del (&urb->urb_list);
+			urb->timeout -= jiffies;
+		}
+#endif
+
+		/* Release int/iso bandwidth */
+		if (urb->bandwidth) {
+			switch (usb_pipetype(urb->pipe)) {
+			case PIPE_INTERRUPT:
+				usb_release_bandwidth (urb->dev, urb, 0);
+				break;
+			case PIPE_ISOCHRONOUS:
+				usb_release_bandwidth (urb->dev, urb, 1);
+				break;
+			default:
+				break;
+			}
+		}
+
+		urb_free_priv ((struct ohci *)urb->dev->bus->hcpriv, urb_priv);
+		usb_dec_dev_use (urb->dev);
+		urb->dev = NULL;
+	}
+}
+
+static void urb_rm_priv (struct urb * urb)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave (&usb_ed_lock, flags);
+	urb_rm_priv_locked (urb);
+	spin_unlock_irqrestore (&usb_ed_lock, flags);
+}
+
+/*-------------------------------------------------------------------------*/
+ 
+#ifdef DEBUG
+static int sohci_get_current_frame_number (struct usb_device * dev);
+
+/* debug| print the main components of an URB     
+ * small: 0) header + data packets 1) just header */
+ 
+static void urb_print (struct urb * urb, char * str, int small)
+{
+	unsigned int pipe= urb->pipe;
+	
+	if (!urb->dev || !urb->dev->bus) {
+		dbg("%s URB: no dev", str);
+		return;
+	}
+	
+#ifndef	OHCI_VERBOSE_DEBUG
+	if (urb->status != 0)
+#endif
+	dbg("%s URB:[%4x] dev:%2d,ep:%2d-%c,type:%s,flags:%4x,len:%d/%d,stat:%d(%x)", 
+			str,
+		 	sohci_get_current_frame_number (urb->dev), 
+		 	usb_pipedevice (pipe),
+		 	usb_pipeendpoint (pipe), 
+		 	usb_pipeout (pipe)? 'O': 'I',
+		 	usb_pipetype (pipe) < 2? (usb_pipeint (pipe)? "INTR": "ISOC"):
+		 		(usb_pipecontrol (pipe)? "CTRL": "BULK"),
+		 	urb->transfer_flags, 
+		 	urb->actual_length, 
+		 	urb->transfer_buffer_length,
+		 	urb->status, urb->status);
+#ifdef	OHCI_VERBOSE_DEBUG
+	if (!small) {
+		int i, len;
+
+		if (usb_pipecontrol (pipe)) {
+			printk (KERN_DEBUG __FILE__ ": cmd(8):");
+			for (i = 0; i < 8 ; i++) 
+				printk (" %02x", ((__u8 *) urb->setup_packet) [i]);
+			printk ("\n");
+		}
+		if (urb->transfer_buffer_length > 0 && urb->transfer_buffer) {
+			printk (KERN_DEBUG __FILE__ ": data(%d/%d):", 
+				urb->actual_length, 
+				urb->transfer_buffer_length);
+			len = usb_pipeout (pipe)? 
+						urb->transfer_buffer_length: urb->actual_length;
+			for (i = 0; i < 16 && i < len; i++) 
+				printk (" %02x", ((__u8 *) urb->transfer_buffer) [i]);
+			printk ("%s stat:%d\n", i < len? "...": "", urb->status);
+		}
+	} 
+#endif
+}
+
+/* just for debugging; prints non-empty branches of the int ed tree inclusive iso eds*/
+void ep_print_int_eds (ohci_t * ohci, char * str) {
+	int i, j;
+	 __u32 * ed_p;
+	for (i= 0; i < 32; i++) {
+		j = 5;
+		ed_p = &(ohci->hcca->int_table [i]);
+		if (*ed_p == 0)
+		    continue;
+		printk (KERN_DEBUG __FILE__ ": %s branch int %2d(%2x):", str, i, i);
+		while (*ed_p != 0 && j--) {
+			ed_t *ed = dma_to_ed (ohci, le32_to_cpup(ed_p));
+			printk (" ed: %4x;", ed->hwINFO);
+			ed_p = &ed->hwNextED;
+		}
+		printk ("\n");
+	}
+}
+
+
+static void ohci_dump_intr_mask (char *label, __u32 mask)
+{
+	dbg ("%s: 0x%08x%s%s%s%s%s%s%s%s%s",
+		label,
+		mask,
+		(mask & OHCI_INTR_MIE) ? " MIE" : "",
+		(mask & OHCI_INTR_OC) ? " OC" : "",
+		(mask & OHCI_INTR_RHSC) ? " RHSC" : "",
+		(mask & OHCI_INTR_FNO) ? " FNO" : "",
+		(mask & OHCI_INTR_UE) ? " UE" : "",
+		(mask & OHCI_INTR_RD) ? " RD" : "",
+		(mask & OHCI_INTR_SF) ? " SF" : "",
+		(mask & OHCI_INTR_WDH) ? " WDH" : "",
+		(mask & OHCI_INTR_SO) ? " SO" : ""
+		);
+}
+
+static void maybe_print_eds (char *label, __u32 value)
+{
+	if (value)
+		dbg ("%s %08x", label, value);
+}
+
+static char *hcfs2string (int state)
+{
+	switch (state) {
+		case OHCI_USB_RESET:	return "reset";
+		case OHCI_USB_RESUME:	return "resume";
+		case OHCI_USB_OPER:	return "operational";
+		case OHCI_USB_SUSPEND:	return "suspend";
+	}
+	return "?";
+}
+
+// dump control and status registers
+static void ohci_dump_status (ohci_t *controller)
+{
+	struct ohci_regs	*regs = controller->regs;
+	__u32			temp;
+
+	temp = readl (&regs->revision) & 0xff;
+	if (temp != 0x10)
+		dbg ("spec %d.%d", (temp >> 4), (temp & 0x0f));
+
+	temp = readl (&regs->control);
+	dbg ("control: 0x%08x%s%s%s HCFS=%s%s%s%s%s CBSR=%d", temp,
+		(temp & OHCI_CTRL_RWE) ? " RWE" : "",
+		(temp & OHCI_CTRL_RWC) ? " RWC" : "",
+		(temp & OHCI_CTRL_IR) ? " IR" : "",
+		hcfs2string (temp & OHCI_CTRL_HCFS),
+		(temp & OHCI_CTRL_BLE) ? " BLE" : "",
+		(temp & OHCI_CTRL_CLE) ? " CLE" : "",
+		(temp & OHCI_CTRL_IE) ? " IE" : "",
+		(temp & OHCI_CTRL_PLE) ? " PLE" : "",
+		temp & OHCI_CTRL_CBSR
+		);
+
+	temp = readl (&regs->cmdstatus);
+	dbg ("cmdstatus: 0x%08x SOC=%d%s%s%s%s", temp,
+		(temp & OHCI_SOC) >> 16,
+		(temp & OHCI_OCR) ? " OCR" : "",
+		(temp & OHCI_BLF) ? " BLF" : "",
+		(temp & OHCI_CLF) ? " CLF" : "",
+		(temp & OHCI_HCR) ? " HCR" : ""
+		);
+
+	ohci_dump_intr_mask ("intrstatus", readl (&regs->intrstatus));
+	ohci_dump_intr_mask ("intrenable", readl (&regs->intrenable));
+	// intrdisable always same as intrenable
+	// ohci_dump_intr_mask ("intrdisable", readl (&regs->intrdisable));
+
+	maybe_print_eds ("ed_periodcurrent", readl (&regs->ed_periodcurrent));
+
+	maybe_print_eds ("ed_controlhead", readl (&regs->ed_controlhead));
+	maybe_print_eds ("ed_controlcurrent", readl (&regs->ed_controlcurrent));
+
+	maybe_print_eds ("ed_bulkhead", readl (&regs->ed_bulkhead));
+	maybe_print_eds ("ed_bulkcurrent", readl (&regs->ed_bulkcurrent));
+
+	maybe_print_eds ("donehead", readl (&regs->donehead));
+}
+
+static void ohci_dump_roothub (ohci_t *controller, int verbose)
+{
+	__u32			temp, ndp, i;
+
+	temp = roothub_a (controller);
+	if (temp == ~(u32)0)
+		return;
+	ndp = (temp & RH_A_NDP);
+
+	if (verbose) {
+		dbg ("roothub.a: %08x POTPGT=%d%s%s%s%s%s NDP=%d", temp,
+			((temp & RH_A_POTPGT) >> 24) & 0xff,
+			(temp & RH_A_NOCP) ? " NOCP" : "",
+			(temp & RH_A_OCPM) ? " OCPM" : "",
+			(temp & RH_A_DT) ? " DT" : "",
+			(temp & RH_A_NPS) ? " NPS" : "",
+			(temp & RH_A_PSM) ? " PSM" : "",
+			ndp
+			);
+		temp = roothub_b (controller);
+		dbg ("roothub.b: %08x PPCM=%04x DR=%04x",
+			temp,
+			(temp & RH_B_PPCM) >> 16,
+			(temp & RH_B_DR)
+			);
+		temp = roothub_status (controller);
+		dbg ("roothub.status: %08x%s%s%s%s%s%s",
+			temp,
+			(temp & RH_HS_CRWE) ? " CRWE" : "",
+			(temp & RH_HS_OCIC) ? " OCIC" : "",
+			(temp & RH_HS_LPSC) ? " LPSC" : "",
+			(temp & RH_HS_DRWE) ? " DRWE" : "",
+			(temp & RH_HS_OCI) ? " OCI" : "",
+			(temp & RH_HS_LPS) ? " LPS" : ""
+			);
+	}
+	
+	for (i = 0; i < ndp; i++) {
+		temp = roothub_portstatus (controller, i);
+		dbg ("roothub.portstatus [%d] = 0x%08x%s%s%s%s%s%s%s%s%s%s%s%s",
+			i,
+			temp,
+			(temp & RH_PS_PRSC) ? " PRSC" : "",
+			(temp & RH_PS_OCIC) ? " OCIC" : "",
+			(temp & RH_PS_PSSC) ? " PSSC" : "",
+			(temp & RH_PS_PESC) ? " PESC" : "",
+			(temp & RH_PS_CSC) ? " CSC" : "",
+
+			(temp & RH_PS_LSDA) ? " LSDA" : "",
+			(temp & RH_PS_PPS) ? " PPS" : "",
+			(temp & RH_PS_PRS) ? " PRS" : "",
+			(temp & RH_PS_POCI) ? " POCI" : "",
+			(temp & RH_PS_PSS) ? " PSS" : "",
+
+			(temp & RH_PS_PES) ? " PES" : "",
+			(temp & RH_PS_CCS) ? " CCS" : ""
+			);
+	}
+}
+
+static void ohci_dump (ohci_t *controller, int verbose)
+{
+	dbg ("OHCI controller usb-%s state", controller->ohci_dev->slot_name);
+
+	// dumps some of the state we know about
+	ohci_dump_status (controller);
+	if (verbose)
+		ep_print_int_eds (controller, "hcca");
+	dbg ("hcca frame #%04x", controller->hcca->frame_no);
+	ohci_dump_roothub (controller, 1);
+}
+
+
+#endif
+
+/*-------------------------------------------------------------------------*
+ * Interface functions (URB)
+ *-------------------------------------------------------------------------*/
+
+/* return a request to the completion handler */
+ 
+static int sohci_return_urb (struct ohci *hc, struct urb * urb)
+{
+	urb_priv_t * urb_priv = urb->hcpriv;
+	struct urb * urbt;
+	unsigned long flags;
+	int i;
+	
+	if (!urb_priv)
+		return -1; /* urb already unlinked */
+
+	/* just to be sure */
+	if (!urb->complete) {
+		urb_rm_priv (urb);
+		return -1;
+	}
+	
+#ifdef DEBUG
+	urb_print (urb, "RET", usb_pipeout (urb->pipe));
+#endif
+
+	switch (usb_pipetype (urb->pipe)) {
+  		case PIPE_INTERRUPT:
+			pci_unmap_single (hc->ohci_dev,
+				urb_priv->td [0]->data_dma,
+				urb->transfer_buffer_length,
+				usb_pipeout (urb->pipe)
+					? PCI_DMA_TODEVICE
+					: PCI_DMA_FROMDEVICE);
+			urb->complete (urb);
+
+			/* implicitly requeued */
+  			urb->actual_length = 0;
+			urb->status = -EINPROGRESS;
+			td_submit_urb (urb);
+  			break;
+  			
+		case PIPE_ISOCHRONOUS:
+			for (urbt = urb->next; urbt && (urbt != urb); urbt = urbt->next);
+			if (urbt) { /* send the reply and requeue URB */	
+				pci_unmap_single (hc->ohci_dev,
+					urb_priv->td [0]->data_dma,
+					urb->transfer_buffer_length,
+					usb_pipeout (urb->pipe)
+						? PCI_DMA_TODEVICE
+						: PCI_DMA_FROMDEVICE);
+				urb->complete (urb);
+				spin_lock_irqsave (&usb_ed_lock, flags);
+				urb->actual_length = 0;
+  				urb->status = USB_ST_URB_PENDING;
+  				urb->start_frame = urb_priv->ed->last_iso + 1;
+  				if (urb_priv->state != URB_DEL) {
+  					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_submit_urb (urb);
+  				}
+  				spin_unlock_irqrestore (&usb_ed_lock, flags);
+  				
+  			} else { /* unlink URB, call complete */
+				urb_rm_priv (urb);
+				urb->complete (urb); 	
+			}		
+			break;
+  				
+		case PIPE_BULK:
+		case PIPE_CONTROL: /* unlink URB, call complete */
+			urb_rm_priv (urb);
+			urb->complete (urb);	
+			break;
+	}
+	return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* get a transfer request */
+ 
+static int sohci_submit_urb (struct urb * urb)
+{
+	ohci_t * ohci;
+	ed_t * ed;
+	urb_priv_t * urb_priv;
+	unsigned int pipe = urb->pipe;
+	int maxps = usb_maxpacket (urb->dev, pipe, usb_pipeout (pipe));
+	int i, size = 0;
+	unsigned long flags;
+	int bustime = 0;
+	int mem_flags = ALLOC_FLAGS;
+	
+	if (!urb->dev || !urb->dev->bus)
+		return -ENODEV;
+	
+	if (urb->hcpriv)			/* urb already in use */
+		return -EINVAL;
+
+//	if(usb_endpoint_halted (urb->dev, usb_pipeendpoint (pipe), usb_pipeout (pipe))) 
+//		return -EPIPE;
+	
+	usb_inc_dev_use (urb->dev);
+	ohci = (ohci_t *) urb->dev->bus->hcpriv;
+	
+#ifdef DEBUG
+	urb_print (urb, "SUB", usb_pipein (pipe));
+#endif
+	
+	/* handle a request to the virtual root hub */
+	if (usb_pipedevice (pipe) == ohci->rh.devnum) 
+		return rh_submit_urb (urb);
+	
+	/* when controller's hung, permit only roothub cleanup attempts
+	 * such as powering down ports */
+	if (ohci->disabled) {
+		usb_dec_dev_use (urb->dev);	
+		return -ESHUTDOWN;
+	}
+
+	/* every endpoint has a ed, locate and fill it */
+	if (!(ed = ep_add_ed (urb->dev, pipe, urb->interval, 1, mem_flags))) {
+		usb_dec_dev_use (urb->dev);	
+		return -ENOMEM;
+	}
+
+	/* for the private part of the URB we need the number of TDs (size) */
+	switch (usb_pipetype (pipe)) {
+		case PIPE_BULK:	/* one TD for every 4096 Byte */
+			size = (urb->transfer_buffer_length - 1) / 4096 + 1;
+
+			/* If the transfer size is multiple of the pipe mtu,
+			 * we may need an extra TD to create a empty frame
+			 * Jean II */
+			if ((urb->transfer_flags & USB_ZERO_PACKET) &&
+			    usb_pipeout (pipe) &&
+			    (urb->transfer_buffer_length != 0) && 
+			    ((urb->transfer_buffer_length % maxps) == 0))
+				size++;
+			break;
+		case PIPE_ISOCHRONOUS: /* number of packets from URB */
+			size = urb->number_of_packets;
+			if (size <= 0) {
+				usb_dec_dev_use (urb->dev);	
+				return -EINVAL;
+			}
+			for (i = 0; i < urb->number_of_packets; i++) {
+  				urb->iso_frame_desc[i].actual_length = 0;
+  				urb->iso_frame_desc[i].status = -EXDEV;
+  			}
+			break;
+		case PIPE_CONTROL: /* 1 TD for setup, 1 for ACK and 1 for every 4096 B */
+			size = (urb->transfer_buffer_length == 0)? 2: 
+						(urb->transfer_buffer_length - 1) / 4096 + 3;
+			break;
+		case PIPE_INTERRUPT: /* one TD */
+			size = 1;
+			break;
+	}
+
+	/* allocate the private part of the URB */
+	urb_priv = kmalloc (sizeof (urb_priv_t) + size * sizeof (td_t *), 
+							in_interrupt() ? GFP_ATOMIC : GFP_KERNEL);
+	if (!urb_priv) {
+		usb_dec_dev_use (urb->dev);	
+		return -ENOMEM;
+	}
+	memset (urb_priv, 0, sizeof (urb_priv_t) + size * sizeof (td_t *));
+	
+	/* fill the private part of the URB */
+	urb_priv->length = size;
+	urb_priv->ed = ed;	
+
+	/* allocate the TDs (updating hash chains) */
+	spin_lock_irqsave (&usb_ed_lock, flags);
+	for (i = 0; i < size; i++) { 
+		urb_priv->td[i] = td_alloc (ohci, SLAB_ATOMIC);
+		if (!urb_priv->td[i]) {
+			urb_priv->length = i;
+			urb_free_priv (ohci, urb_priv);
+			spin_unlock_irqrestore (&usb_ed_lock, flags);
+			usb_dec_dev_use (urb->dev);	
+			return -ENOMEM;
+		}
+	}	
+
+	if (ed->state == ED_NEW || (ed->state & ED_DEL)) {
+		urb_free_priv (ohci, urb_priv);
+		spin_unlock_irqrestore (&usb_ed_lock, flags);
+		usb_dec_dev_use (urb->dev);	
+		return -EINVAL;
+	}
+	
+	/* allocate and claim bandwidth if needed; ISO
+	 * needs start frame index if it was't provided.
+	 */
+	switch (usb_pipetype (pipe)) {
+		case PIPE_ISOCHRONOUS:
+			if (urb->transfer_flags & USB_ISO_ASAP) { 
+				urb->start_frame = ((ed->state == ED_OPER)
+					? (ed->last_iso + 1)
+					: (le16_to_cpu (ohci->hcca->frame_no) + 10)) & 0xffff;
+			}	
+			/* FALLTHROUGH */
+		case PIPE_INTERRUPT:
+			if (urb->bandwidth == 0) {
+				bustime = usb_check_bandwidth (urb->dev, urb);
+			}
+			if (bustime < 0) {
+				urb_free_priv (ohci, urb_priv);
+				spin_unlock_irqrestore (&usb_ed_lock, flags);
+				usb_dec_dev_use (urb->dev);	
+				return bustime;
+			}
+			usb_claim_bandwidth (urb->dev, urb, bustime, usb_pipeisoc (urb->pipe));
+#ifdef	DO_TIMEOUTS
+			urb->timeout = 0;
+#endif
+	}
+
+	urb->actual_length = 0;
+	urb->hcpriv = urb_priv;
+	urb->status = USB_ST_URB_PENDING;
+
+	/* link the ed into a chain if is not already */
+	if (ed->state != ED_OPER)
+		ep_link (ohci, ed);
+
+	/* fill the TDs and link it to the ed */
+	td_submit_urb (urb);
+
+#ifdef	DO_TIMEOUTS
+	/* maybe add to ordered list of timeouts */
+	if (urb->timeout) {
+		struct list_head	*entry;
+
+		urb->timeout += jiffies;
+
+		list_for_each (entry, &ohci->timeout_list) {
+			struct urb	*next_urb;
+
+			next_urb = list_entry (entry, struct urb, urb_list);
+			if (time_after_eq (urb->timeout, next_urb->timeout))
+				break;
+		}
+		list_add (&urb->urb_list, entry);
+
+		/* drive timeouts by SF (messy, but works) */
+		writel (OHCI_INTR_SF, &ohci->regs->intrenable);	
+	}
+#endif
+
+	spin_unlock_irqrestore (&usb_ed_lock, flags);
+
+	return 0;	
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* deactivate all TDs and remove the private part of the URB */
+/* interrupt callers must use async unlink mode */
+
+static int sohci_unlink_urb (struct urb * urb)
+{
+	unsigned long flags;
+	ohci_t * ohci;
+	
+	if (!urb) /* just to be sure */ 
+		return -EINVAL;
+		
+	if (!urb->dev || !urb->dev->bus)
+		return -ENODEV;
+
+	ohci = (ohci_t *) urb->dev->bus->hcpriv; 
+
+#ifdef DEBUG
+	urb_print (urb, "UNLINK", 1);
+#endif		  
+
+	/* handle a request to the virtual root hub */
+	if (usb_pipedevice (urb->pipe) == ohci->rh.devnum)
+		return rh_unlink_urb (urb);
+
+	if (urb->hcpriv && (urb->status == USB_ST_URB_PENDING)) { 
+		if (!ohci->disabled) {
+			urb_priv_t  * urb_priv;
+
+			/* interrupt code may not sleep; it must use
+			 * async status return to unlink pending urbs.
+			 */
+			if (!(urb->transfer_flags & USB_ASYNC_UNLINK)
+					&& in_interrupt ()) {
+				err ("bug in call from %p; use async!",
+					__builtin_return_address(0));
+				return -EWOULDBLOCK;
+			}
+
+			/* flag the urb and its TDs for deletion in some
+			 * upcoming SF interrupt delete list processing
+			 */
+			spin_lock_irqsave (&usb_ed_lock, flags);
+			urb_priv = urb->hcpriv;
+
+			if (!urb_priv || (urb_priv->state == URB_DEL)) {
+				spin_unlock_irqrestore (&usb_ed_lock, flags);
+				return 0;
+			}
+				
+			urb_priv->state = URB_DEL; 
+			ep_rm_ed (urb->dev, urb_priv->ed);
+			urb_priv->ed->state |= ED_URB_DEL;
+
+			if (!(urb->transfer_flags & USB_ASYNC_UNLINK)) {
+				DECLARE_WAIT_QUEUE_HEAD (unlink_wakeup); 
+				DECLARE_WAITQUEUE (wait, current);
+				int timeout = OHCI_UNLINK_TIMEOUT;
+
+				add_wait_queue (&unlink_wakeup, &wait);
+				urb_priv->wait = &unlink_wakeup;
+				spin_unlock_irqrestore (&usb_ed_lock, flags);
+
+				/* wait until all TDs are deleted */
+				set_current_state(TASK_UNINTERRUPTIBLE);
+				while (timeout && (urb->status == USB_ST_URB_PENDING))
+					timeout = schedule_timeout (timeout);
+				set_current_state(TASK_RUNNING);
+				remove_wait_queue (&unlink_wakeup, &wait); 
+				if (urb->status == USB_ST_URB_PENDING) {
+					err ("unlink URB timeout");
+					return -ETIMEDOUT;
+				}
+			} else {
+				/* usb_dec_dev_use done in dl_del_list() */
+				urb->status = -EINPROGRESS;
+				spin_unlock_irqrestore (&usb_ed_lock, flags);
+				return -EINPROGRESS;
+			}
+		} else {
+			urb_rm_priv (urb);
+			if (urb->transfer_flags & USB_ASYNC_UNLINK) {
+				urb->status = -ECONNRESET;
+				if (urb->complete)
+					urb->complete (urb); 
+			} else 
+				urb->status = -ENOENT;
+		}	
+	}	
+	return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* allocate private data space for a usb device */
+
+static int sohci_alloc_dev (struct usb_device *usb_dev)
+{
+	struct ohci_device * dev;
+
+	dev = dev_alloc ((struct ohci *) usb_dev->bus->hcpriv, ALLOC_FLAGS);
+	if (!dev)
+		return -ENOMEM;
+
+	usb_dev->hcpriv = dev;
+	return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* may be called from interrupt context */
+/* frees private data space of usb device */
+  
+static int sohci_free_dev (struct usb_device * usb_dev)
+{
+	unsigned long flags;
+	int i, cnt = 0;
+	ed_t * ed;
+	struct ohci_device * dev = usb_to_ohci (usb_dev);
+	ohci_t * ohci = usb_dev->bus->hcpriv;
+	
+	if (!dev)
+		return 0;
+	
+	if (usb_dev->devnum >= 0) {
+	
+		/* driver disconnects should have unlinked all urbs
+		 * (freeing all the TDs, unlinking EDs) but we need
+		 * to defend against bugs that prevent that.
+		 */
+		spin_lock_irqsave (&usb_ed_lock, flags);	
+		for(i = 0; i < NUM_EDS; i++) {
+  			ed = &(dev->ed[i]);
+  			if (ed->state != ED_NEW) {
+  				if (ed->state == ED_OPER) {
+					/* driver on that interface didn't unlink an urb */
+					dbg ("driver usb-%s dev %d ed 0x%x unfreed URB",
+						ohci->ohci_dev->slot_name, usb_dev->devnum, i);
+					ep_unlink (ohci, ed);
+				}
+  				ep_rm_ed (usb_dev, ed);
+  				ed->state = ED_DEL;
+  				cnt++;
+  			}
+  		}
+  		spin_unlock_irqrestore (&usb_ed_lock, flags);
+  		
+		/* if the controller is running, tds for those unlinked
+		 * urbs get freed by dl_del_list at the next SF interrupt
+		 */
+		if (cnt > 0) {
+
+			if (ohci->disabled) {
+				/* FIXME: Something like this should kick in,
+				 * though it's currently an exotic case ...
+				 * the controller won't ever be touching
+				 * these lists again!!
+				dl_del_list (ohci,
+					le16_to_cpu (ohci->hcca->frame_no) & 1);
+				 */
+				warn ("TD leak, %d", cnt);
+
+			} else if (!in_interrupt ()) {
+				DECLARE_WAIT_QUEUE_HEAD (freedev_wakeup); 
+				DECLARE_WAITQUEUE (wait, current);
+				int timeout = OHCI_UNLINK_TIMEOUT;
+
+				/* SF interrupt handler calls dl_del_list */
+				add_wait_queue (&freedev_wakeup, &wait);
+				dev->wait = &freedev_wakeup;
+				set_current_state(TASK_UNINTERRUPTIBLE);
+				while (timeout && dev->ed_cnt)
+					timeout = schedule_timeout (timeout);
+				set_current_state(TASK_RUNNING);
+				remove_wait_queue (&freedev_wakeup, &wait);
+				if (dev->ed_cnt) {
+					err ("free device %d timeout", usb_dev->devnum);
+					return -ETIMEDOUT;
+				}
+			} else {
+				/* likely some interface's driver has a refcount bug */
+				err ("bus %s devnum %d deletion in interrupt",
+					ohci->ohci_dev->slot_name, usb_dev->devnum);
+				BUG ();
+			}
+		}
+	}
+
+	/* free device, and associated EDs */
+	dev_free (ohci, dev);
+
+	return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* tell us the current USB frame number */
+
+static int sohci_get_current_frame_number (struct usb_device *usb_dev) 
+{
+	ohci_t * ohci = usb_dev->bus->hcpriv;
+	
+	return le16_to_cpu (ohci->hcca->frame_no);
+}
+
+/*-------------------------------------------------------------------------*/
+
+struct usb_operations sohci_device_operations = {
+	sohci_alloc_dev,
+	sohci_free_dev,
+	sohci_get_current_frame_number,
+	sohci_submit_urb,
+	sohci_unlink_urb
+};
+
+/*-------------------------------------------------------------------------*
+ * ED handling functions
+ *-------------------------------------------------------------------------*/  
+		
+/* search for the right branch to insert an interrupt ed into the int tree 
+ * do some load ballancing;
+ * returns the branch and 
+ * sets the interval to interval = 2^integer (ld (interval)) */
+
+static int ep_int_ballance (ohci_t * ohci, int interval, int load)
+{
+	int i, branch = 0;
+   
+	/* search for the least loaded interrupt endpoint branch of all 32 branches */
+	for (i = 0; i < 32; i++) 
+		if (ohci->ohci_int_load [branch] > ohci->ohci_int_load [i]) branch = i; 
+  
+	branch = branch % interval;
+	for (i = branch; i < 32; i += interval) ohci->ohci_int_load [i] += load;
+
+	return branch;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*  2^int( ld (inter)) */
+
+static int ep_2_n_interval (int inter)
+{	
+	int i;
+	for (i = 0; ((inter >> i) > 1 ) && (i < 5); i++); 
+	return 1 << i;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* the int tree is a binary tree 
+ * in order to process it sequentially the indexes of the branches have to be mapped 
+ * the mapping reverses the bits of a word of num_bits length */
+ 
+static int ep_rev (int num_bits, int word)
+{
+	int i, wout = 0;
+
+	for (i = 0; i < num_bits; i++) wout |= (((word >> i) & 1) << (num_bits - i - 1));
+	return wout;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* link an ed into one of the HC chains */
+
+static int ep_link (ohci_t * ohci, ed_t * edi)
+{	 
+	int int_branch;
+	int i;
+	int inter;
+	int interval;
+	int load;
+	__u32 * ed_p;
+	volatile ed_t * ed = edi;
+	
+	ed->state = ED_OPER;
+	
+	switch (ed->type) {
+	case PIPE_CONTROL:
+		ed->hwNextED = 0;
+		if (ohci->ed_controltail == NULL) {
+			writel (ed->dma, &ohci->regs->ed_controlhead);
+		} else {
+			ohci->ed_controltail->hwNextED = cpu_to_le32 (ed->dma);
+		}
+		ed->ed_prev = ohci->ed_controltail;
+		if (!ohci->ed_controltail && !ohci->ed_rm_list[0] &&
+			!ohci->ed_rm_list[1] && !ohci->sleeping) {
+			ohci->hc_control |= OHCI_CTRL_CLE;
+			writel (ohci->hc_control, &ohci->regs->control);
+		}
+		ohci->ed_controltail = edi;	  
+		break;
+		
+	case PIPE_BULK:
+		ed->hwNextED = 0;
+		if (ohci->ed_bulktail == NULL) {
+			writel (ed->dma, &ohci->regs->ed_bulkhead);
+		} else {
+			ohci->ed_bulktail->hwNextED = cpu_to_le32 (ed->dma);
+		}
+		ed->ed_prev = ohci->ed_bulktail;
+		if (!ohci->ed_bulktail && !ohci->ed_rm_list[0] &&
+			!ohci->ed_rm_list[1] && !ohci->sleeping) {
+			ohci->hc_control |= OHCI_CTRL_BLE;
+			writel (ohci->hc_control, &ohci->regs->control);
+		}
+		ohci->ed_bulktail = edi;	  
+		break;
+		
+	case PIPE_INTERRUPT:
+		load = ed->int_load;
+		interval = ep_2_n_interval (ed->int_period);
+		ed->int_interval = interval;
+		int_branch = ep_int_ballance (ohci, interval, load);
+		ed->int_branch = int_branch;
+		
+		for (i = 0; i < ep_rev (6, interval); i += inter) {
+			inter = 1;
+			for (ed_p = &(ohci->hcca->int_table[ep_rev (5, i) + int_branch]); 
+				(*ed_p != 0) && ((dma_to_ed (ohci, le32_to_cpup (ed_p)))->int_interval >= interval); 
+				ed_p = &((dma_to_ed (ohci, le32_to_cpup (ed_p)))->hwNextED)) 
+					inter = ep_rev (6, (dma_to_ed (ohci, le32_to_cpup (ed_p)))->int_interval);
+			ed->hwNextED = *ed_p; 
+			*ed_p = cpu_to_le32 (ed->dma);
+		}
+#ifdef DEBUG
+		ep_print_int_eds (ohci, "LINK_INT");
+#endif
+		break;
+		
+	case PIPE_ISOCHRONOUS:
+		ed->hwNextED = 0;
+		ed->int_interval = 1;
+		if (ohci->ed_isotail != NULL) {
+			ohci->ed_isotail->hwNextED = cpu_to_le32 (ed->dma);
+			ed->ed_prev = ohci->ed_isotail;
+		} else {
+			for ( i = 0; i < 32; i += inter) {
+				inter = 1;
+				for (ed_p = &(ohci->hcca->int_table[ep_rev (5, i)]); 
+					*ed_p != 0; 
+					ed_p = &((dma_to_ed (ohci, le32_to_cpup (ed_p)))->hwNextED)) 
+						inter = ep_rev (6, (dma_to_ed (ohci, le32_to_cpup (ed_p)))->int_interval);
+				*ed_p = cpu_to_le32 (ed->dma);	
+			}	
+			ed->ed_prev = NULL;
+		}	
+		ohci->ed_isotail = edi;  
+#ifdef DEBUG
+		ep_print_int_eds (ohci, "LINK_ISO");
+#endif
+		break;
+	}	 	
+	return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* scan the periodic table to find and unlink this ED */
+static void periodic_unlink (
+	struct ohci	*ohci,
+	struct ed	*ed,
+	unsigned	index,
+	unsigned	period
+) {
+	for (; index < NUM_INTS; index += period) {
+		__u32	*ed_p = &ohci->hcca->int_table [index];
+
+		/* ED might have been unlinked through another path */
+		while (*ed_p != 0) {
+			if ((dma_to_ed (ohci, le32_to_cpup (ed_p))) == ed) {
+				*ed_p = ed->hwNextED;		
+				break;
+			}
+			ed_p = & ((dma_to_ed (ohci,
+				le32_to_cpup (ed_p)))->hwNextED);
+		}
+	}	
+}
+
+/* unlink an ed from one of the HC chains. 
+ * just the link to the ed is unlinked.
+ * the link from the ed still points to another operational ed or 0
+ * so the HC can eventually finish the processing of the unlinked ed */
+
+static int ep_unlink (ohci_t * ohci, ed_t * ed) 
+{
+	int i;
+
+	ed->hwINFO |= cpu_to_le32 (OHCI_ED_SKIP);
+
+	switch (ed->type) {
+	case PIPE_CONTROL:
+		if (ed->ed_prev == NULL) {
+			if (!ed->hwNextED) {
+				ohci->hc_control &= ~OHCI_CTRL_CLE;
+				writel (ohci->hc_control, &ohci->regs->control);
+			}
+			writel (le32_to_cpup (&ed->hwNextED), &ohci->regs->ed_controlhead);
+		} else {
+			ed->ed_prev->hwNextED = ed->hwNextED;
+		}
+		if (ohci->ed_controltail == ed) {
+			ohci->ed_controltail = ed->ed_prev;
+		} else {
+			(dma_to_ed (ohci, le32_to_cpup (&ed->hwNextED)))->ed_prev = ed->ed_prev;
+		}
+		break;
+      
+	case PIPE_BULK:
+		if (ed->ed_prev == NULL) {
+			if (!ed->hwNextED) {
+				ohci->hc_control &= ~OHCI_CTRL_BLE;
+				writel (ohci->hc_control, &ohci->regs->control);
+			}
+			writel (le32_to_cpup (&ed->hwNextED), &ohci->regs->ed_bulkhead);
+		} else {
+			ed->ed_prev->hwNextED = ed->hwNextED;
+		}
+		if (ohci->ed_bulktail == ed) {
+			ohci->ed_bulktail = ed->ed_prev;
+		} else {
+			(dma_to_ed (ohci, le32_to_cpup (&ed->hwNextED)))->ed_prev = ed->ed_prev;
+		}
+		break;
+      
+	case PIPE_INTERRUPT:
+		periodic_unlink (ohci, ed, 0, 1);
+		for (i = ed->int_branch; i < 32; i += ed->int_interval)
+		    ohci->ohci_int_load[i] -= ed->int_load;
+#ifdef DEBUG
+		ep_print_int_eds (ohci, "UNLINK_INT");
+#endif
+		break;
+		
+	case PIPE_ISOCHRONOUS:
+		if (ohci->ed_isotail == ed)
+			ohci->ed_isotail = ed->ed_prev;
+		if (ed->hwNextED != 0) 
+		    (dma_to_ed (ohci, le32_to_cpup (&ed->hwNextED)))
+		    	->ed_prev = ed->ed_prev;
+				    
+		if (ed->ed_prev != NULL)
+			ed->ed_prev->hwNextED = ed->hwNextED;
+		else
+			periodic_unlink (ohci, ed, 0, 1);
+#ifdef DEBUG
+		ep_print_int_eds (ohci, "UNLINK_ISO");
+#endif
+		break;
+	}
+	ed->state = ED_UNLINK;
+	return 0;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+/* add/reinit an endpoint; this should be done once at the usb_set_configuration command,
+ * but the USB stack is a little bit stateless  so we do it at every transaction
+ * if the state of the ed is ED_NEW then a dummy td is added and the state is changed to ED_UNLINK
+ * in all other cases the state is left unchanged
+ * the ed info fields are setted anyway even though most of them should not change */
+ 
+static ed_t * ep_add_ed (
+	struct usb_device * usb_dev,
+	unsigned int pipe,
+	int interval,
+	int load,
+	int mem_flags
+)
+{
+   	ohci_t * ohci = usb_dev->bus->hcpriv;
+	td_t * td;
+	ed_t * ed_ret;
+	volatile ed_t * ed; 
+	unsigned long flags;
+ 	
+ 	
+	spin_lock_irqsave (&usb_ed_lock, flags);
+
+	ed = ed_ret = &(usb_to_ohci (usb_dev)->ed[(usb_pipeendpoint (pipe) << 1) | 
+			(usb_pipecontrol (pipe)? 0: usb_pipeout (pipe))]);
+
+	if ((ed->state & ED_DEL) || (ed->state & ED_URB_DEL)) {
+		/* pending delete request */
+		spin_unlock_irqrestore (&usb_ed_lock, flags);
+		return NULL;
+	}
+	
+	if (ed->state == ED_NEW) {
+		ed->hwINFO = cpu_to_le32 (OHCI_ED_SKIP); /* skip ed */
+  		/* dummy td; end of td list for ed */
+		td = td_alloc (ohci, SLAB_ATOMIC);
+		/* hash the ed for later reverse mapping */
+ 		if (!td || !hash_add_ed (ohci, (ed_t *)ed)) {
+			/* out of memory */
+		        if (td)
+		            td_free(ohci, td);
+			spin_unlock_irqrestore (&usb_ed_lock, flags);
+			return NULL;
+		}
+		ed->hwTailP = cpu_to_le32 (td->td_dma);
+		ed->hwHeadP = ed->hwTailP;	
+		ed->state = ED_UNLINK;
+		ed->type = usb_pipetype (pipe);
+		usb_to_ohci (usb_dev)->ed_cnt++;
+	}
+
+	ohci->dev[usb_pipedevice (pipe)] = usb_dev;
+	
+	ed->hwINFO = cpu_to_le32 (usb_pipedevice (pipe)
+			| usb_pipeendpoint (pipe) << 7
+			| (usb_pipeisoc (pipe)? 0x8000: 0)
+			| (usb_pipecontrol (pipe)? 0: (usb_pipeout (pipe)? 0x800: 0x1000)) 
+			| usb_pipeslow (pipe) << 13
+			| usb_maxpacket (usb_dev, pipe, usb_pipeout (pipe)) << 16);
+  
+  	if (ed->type == PIPE_INTERRUPT && ed->state == ED_UNLINK) {
+  		ed->int_period = interval;
+  		ed->int_load = load;
+  	}
+  	
+	spin_unlock_irqrestore (&usb_ed_lock, flags);
+	return ed_ret; 
+}
+
+/*-------------------------------------------------------------------------*/
+ 
+/* request the removal of an endpoint
+ * put the ep on the rm_list and request a stop of the bulk or ctrl list 
+ * real removal is done at the next start frame (SF) hardware interrupt */
+ 
+static void ep_rm_ed (struct usb_device * usb_dev, ed_t * ed)
+{    
+	unsigned int frame;
+	ohci_t * ohci = usb_dev->bus->hcpriv;
+
+	if ((ed->state & ED_DEL) || (ed->state & ED_URB_DEL))
+		return;
+	
+	ed->hwINFO |= cpu_to_le32 (OHCI_ED_SKIP);
+
+	if (!ohci->disabled) {
+		switch (ed->type) {
+			case PIPE_CONTROL: /* stop control list */
+				ohci->hc_control &= ~OHCI_CTRL_CLE;
+				writel (ohci->hc_control, &ohci->regs->control); 
+  				break;
+			case PIPE_BULK: /* stop bulk list */
+				ohci->hc_control &= ~OHCI_CTRL_BLE;
+				writel (ohci->hc_control, &ohci->regs->control); 
+				break;
+		}
+	}
+
+	frame = le16_to_cpu (ohci->hcca->frame_no) & 0x1;
+	ed->ed_rm_list = ohci->ed_rm_list[frame];
+	ohci->ed_rm_list[frame] = ed;
+
+	if (!ohci->disabled && !ohci->sleeping) {
+		/* enable SOF interrupt */
+		writel (OHCI_INTR_SF, &ohci->regs->intrstatus);
+		writel (OHCI_INTR_SF, &ohci->regs->intrenable);
+	}
+}
+
+/*-------------------------------------------------------------------------*
+ * TD handling functions
+ *-------------------------------------------------------------------------*/
+
+/* enqueue next TD for this URB (OHCI spec 5.2.8.2) */
+
+static void
+td_fill (ohci_t * ohci, unsigned int info,
+	dma_addr_t data, int len,
+	struct urb * urb, int index)
+{
+	volatile td_t  * td, * td_pt;
+	urb_priv_t * urb_priv = urb->hcpriv;
+	
+	if (index >= urb_priv->length) {
+		err("internal OHCI error: TD index > length");
+		return;
+	}
+	
+	/* use this td as the next dummy */
+	td_pt = urb_priv->td [index];
+	td_pt->hwNextTD = 0;
+
+	/* fill the old dummy TD */
+	td = urb_priv->td [index] = dma_to_td (ohci,
+			le32_to_cpup (&urb_priv->ed->hwTailP) & ~0xf);
+
+	td->ed = urb_priv->ed;
+	td->next_dl_td = NULL;
+	td->index = index;
+	td->urb = urb; 
+	td->data_dma = data;
+	if (!len)
+		data = 0;
+
+	td->hwINFO = cpu_to_le32 (info);
+	if ((td->ed->type) == PIPE_ISOCHRONOUS) {
+		td->hwCBP = cpu_to_le32 (data & 0xFFFFF000);
+		td->ed->last_iso = info & 0xffff;
+	} else {
+		td->hwCBP = cpu_to_le32 (data); 
+	}			
+	if (data)
+		td->hwBE = cpu_to_le32 (data + len - 1);
+	else
+		td->hwBE = 0;
+	td->hwNextTD = cpu_to_le32 (td_pt->td_dma);
+	td->hwPSW [0] = cpu_to_le16 ((data & 0x0FFF) | 0xE000);
+
+	/* append to queue */
+	wmb();
+	td->ed->hwTailP = td->hwNextTD;
+}
+
+/*-------------------------------------------------------------------------*/
+ 
+/* prepare all TDs of a transfer */
+
+static void td_submit_urb (struct urb * urb)
+{ 
+	urb_priv_t * urb_priv = urb->hcpriv;
+	ohci_t * ohci = (ohci_t *) urb->dev->bus->hcpriv;
+	dma_addr_t data;
+	int data_len = urb->transfer_buffer_length;
+	int maxps = usb_maxpacket (urb->dev, urb->pipe, usb_pipeout (urb->pipe));
+	int cnt = 0; 
+	__u32 info = 0;
+  	unsigned int toggle = 0;
+
+	/* OHCI handles the DATA-toggles itself, we just use the USB-toggle bits for reseting */
+  	if(usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe))) {
+  		toggle = TD_T_TOGGLE;
+	} else {
+  		toggle = TD_T_DATA0;
+		usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe), 1);
+	}
+	
+	urb_priv->td_cnt = 0;
+
+	if (data_len) {
+		data = pci_map_single (ohci->ohci_dev,
+			urb->transfer_buffer, data_len,
+			usb_pipeout (urb->pipe)
+				? PCI_DMA_TODEVICE
+				: PCI_DMA_FROMDEVICE
+			);
+	} else
+		data = 0;
+	
+	switch (usb_pipetype (urb->pipe)) {
+		case PIPE_BULK:
+			info = usb_pipeout (urb->pipe)? 
+				TD_CC | TD_DP_OUT : TD_CC | TD_DP_IN ;
+			while(data_len > 4096) {		
+				td_fill (ohci, info | (cnt? TD_T_TOGGLE:toggle), data, 4096, urb, cnt);
+				data += 4096; data_len -= 4096; cnt++;
+			}
+			info = usb_pipeout (urb->pipe)?
+				TD_CC | TD_DP_OUT : TD_CC | TD_R | TD_DP_IN ;
+			td_fill (ohci, info | (cnt? TD_T_TOGGLE:toggle), data, data_len, urb, cnt);
+			cnt++;
+
+			/* If the transfer size is multiple of the pipe mtu,
+			 * we may need an extra TD to create a empty frame
+			 * Note : another way to check this condition is
+			 * to test if(urb_priv->length > cnt) - Jean II */
+			if ((urb->transfer_flags & USB_ZERO_PACKET) &&
+			    usb_pipeout (urb->pipe) &&
+			    (urb->transfer_buffer_length != 0) && 
+			    ((urb->transfer_buffer_length % maxps) == 0)) {
+				td_fill (ohci, info | (cnt? TD_T_TOGGLE:toggle), 0, 0, urb, cnt);
+				cnt++;
+			}
+
+			if (!ohci->sleeping) {
+				wmb();
+				writel (OHCI_BLF, &ohci->regs->cmdstatus); /* start bulk list */
+			}
+			break;
+
+		case PIPE_INTERRUPT:
+			info = usb_pipeout (urb->pipe)? 
+				TD_CC | TD_DP_OUT | toggle: TD_CC | TD_R | TD_DP_IN | toggle;
+			td_fill (ohci, info, data, data_len, urb, cnt++);
+			break;
+
+		case PIPE_CONTROL:
+			info = TD_CC | TD_DP_SETUP | TD_T_DATA0;
+			td_fill (ohci, info,
+				pci_map_single (ohci->ohci_dev,
+					urb->setup_packet, 8,
+					PCI_DMA_TODEVICE),
+				8, urb, cnt++); 
+			if (data_len > 0) {  
+				info = usb_pipeout (urb->pipe)? 
+					TD_CC | TD_R | TD_DP_OUT | TD_T_DATA1 : TD_CC | TD_R | TD_DP_IN | TD_T_DATA1;
+				/* NOTE:  mishandles transfers >8K, some >4K */
+				td_fill (ohci, info, data, data_len, urb, cnt++);  
+			} 
+			info = usb_pipeout (urb->pipe)? 
+ 				TD_CC | TD_DP_IN | TD_T_DATA1: TD_CC | TD_DP_OUT | TD_T_DATA1;
+			td_fill (ohci, info, data, 0, urb, cnt++);
+			if (!ohci->sleeping) {
+				wmb();
+				writel (OHCI_CLF, &ohci->regs->cmdstatus); /* start Control list */
+			}
+			break;
+
+		case PIPE_ISOCHRONOUS:
+			for (cnt = 0; cnt < urb->number_of_packets; cnt++) {
+				td_fill (ohci, TD_CC|TD_ISO | ((urb->start_frame + cnt) & 0xffff), 
+					data + urb->iso_frame_desc[cnt].offset, 
+					urb->iso_frame_desc[cnt].length, urb, cnt); 
+			}
+			break;
+	} 
+	if (urb_priv->length != cnt) 
+		dbg("TD LENGTH %d != CNT %d", urb_priv->length, cnt);
+}
+
+/*-------------------------------------------------------------------------*
+ * Done List handling functions
+ *-------------------------------------------------------------------------*/
+
+
+/* calculate the transfer length and update the urb */
+
+static void dl_transfer_length(td_t * td)
+{
+	__u32 tdINFO, tdBE, tdCBP;
+ 	__u16 tdPSW;
+ 	struct urb * urb = td->urb;
+ 	urb_priv_t * urb_priv = urb->hcpriv;
+	int dlen = 0;
+	int cc = 0;
+	
+	tdINFO = le32_to_cpup (&td->hwINFO);
+  	tdBE   = le32_to_cpup (&td->hwBE);
+  	tdCBP  = le32_to_cpup (&td->hwCBP);
+
+
+  	if (tdINFO & TD_ISO) {
+ 		tdPSW = le16_to_cpu (td->hwPSW[0]);
+ 		cc = (tdPSW >> 12) & 0xF;
+		if (cc < 0xE)  {
+			if (usb_pipeout(urb->pipe)) {
+				dlen = urb->iso_frame_desc[td->index].length;
+			} else {
+				dlen = tdPSW & 0x3ff;
+			}
+			urb->actual_length += dlen;
+			urb->iso_frame_desc[td->index].actual_length = dlen;
+			if (!(urb->transfer_flags & USB_DISABLE_SPD) && (cc == TD_DATAUNDERRUN))
+				cc = TD_CC_NOERROR;
+					 
+			urb->iso_frame_desc[td->index].status = cc_to_error[cc];
+		}
+	} else { /* BULK, INT, CONTROL DATA */
+		if (!(usb_pipetype (urb->pipe) == PIPE_CONTROL && 
+				((td->index == 0) || (td->index == urb_priv->length - 1)))) {
+ 			if (tdBE != 0) {
+ 				if (td->hwCBP == 0)
+					urb->actual_length += tdBE - td->data_dma + 1;
+  				else
+					urb->actual_length += tdCBP - td->data_dma;
+			}
+  		}
+  	}
+}
+
+/* handle an urb that is being unlinked */
+
+static void dl_del_urb (struct urb * urb)
+{
+	wait_queue_head_t * wait_head = ((urb_priv_t *)(urb->hcpriv))->wait;
+
+	urb_rm_priv_locked (urb);
+
+	if (urb->transfer_flags & USB_ASYNC_UNLINK) {
+		urb->status = -ECONNRESET;
+		if (urb->complete)
+			urb->complete (urb);
+	} else {
+		urb->status = -ENOENT;
+		if (urb->complete)
+			urb->complete (urb);
+
+		/* unblock sohci_unlink_urb */
+		if (wait_head)
+			wake_up (wait_head);
+	}
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* replies to the request have to be on a FIFO basis so
+ * we reverse the reversed done-list */
+ 
+static td_t * dl_reverse_done_list (ohci_t * ohci)
+{
+	__u32 td_list_hc;
+	td_t * td_rev = NULL;
+	td_t * td_list = NULL;
+  	urb_priv_t * urb_priv = NULL;
+  	unsigned long flags;
+  	
+  	spin_lock_irqsave (&usb_ed_lock, flags);
+  	
+	td_list_hc = le32_to_cpup (&ohci->hcca->done_head) & 0xfffffff0;
+	ohci->hcca->done_head = 0;
+	
+	while (td_list_hc) {		
+		td_list = dma_to_td (ohci, td_list_hc);
+
+		if (TD_CC_GET (le32_to_cpup (&td_list->hwINFO))) {
+			urb_priv = (urb_priv_t *) td_list->urb->hcpriv;
+			dbg(" USB-error/status: %x : %p", 
+					TD_CC_GET (le32_to_cpup (&td_list->hwINFO)), td_list);
+			if (td_list->ed->hwHeadP & cpu_to_le32 (0x1)) {
+				if (urb_priv && ((td_list->index + 1) < urb_priv->length)) {
+					td_list->ed->hwHeadP = 
+						(urb_priv->td[urb_priv->length - 1]->hwNextTD & cpu_to_le32 (0xfffffff0)) |
+									(td_list->ed->hwHeadP & cpu_to_le32 (0x2));
+					urb_priv->td_cnt += urb_priv->length - td_list->index - 1;
+				} else 
+					td_list->ed->hwHeadP &= cpu_to_le32 (0xfffffff2);
+			}
+		}
+
+		td_list->next_dl_td = td_rev;	
+		td_rev = td_list;
+		td_list_hc = le32_to_cpup (&td_list->hwNextTD) & 0xfffffff0;	
+	}	
+	spin_unlock_irqrestore (&usb_ed_lock, flags);
+	return td_list;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* there are some pending requests to remove 
+ * - some of the eds (if ed->state & ED_DEL (set by sohci_free_dev)
+ * - some URBs/TDs if urb_priv->state == URB_DEL */
+ 
+static void dl_del_list (ohci_t  * ohci, unsigned int frame)
+{
+	unsigned long flags;
+	ed_t * ed;
+	__u32 edINFO;
+	__u32 tdINFO;
+	td_t * td = NULL, * td_next = NULL, * tdHeadP = NULL, * tdTailP;
+	__u32 * td_p;
+	int ctrl = 0, bulk = 0;
+
+	spin_lock_irqsave (&usb_ed_lock, flags);
+
+	for (ed = ohci->ed_rm_list[frame]; ed != NULL; ed = ed->ed_rm_list) {
+
+		tdTailP = dma_to_td (ohci, le32_to_cpup (&ed->hwTailP) & 0xfffffff0);
+		tdHeadP = dma_to_td (ohci, le32_to_cpup (&ed->hwHeadP) & 0xfffffff0);
+		edINFO = le32_to_cpup (&ed->hwINFO);
+		td_p = &ed->hwHeadP;
+
+		for (td = tdHeadP; td != tdTailP; td = td_next) { 
+			struct urb * urb = td->urb;
+			urb_priv_t * urb_priv = td->urb->hcpriv;
+			
+			td_next = dma_to_td (ohci, le32_to_cpup (&td->hwNextTD) & 0xfffffff0);
+			if ((urb_priv->state == URB_DEL) || (ed->state & ED_DEL)) {
+				tdINFO = le32_to_cpup (&td->hwINFO);
+				if (TD_CC_GET (tdINFO) < 0xE)
+					dl_transfer_length (td);
+				*td_p = td->hwNextTD | (*td_p & cpu_to_le32 (0x3));
+
+				/* URB is done; clean up */
+				if (++(urb_priv->td_cnt) == urb_priv->length)
+					dl_del_urb (urb);
+			} else {
+				td_p = &td->hwNextTD;
+			}
+		}
+
+		if (ed->state & ED_DEL) { /* set by sohci_free_dev */
+			struct ohci_device * dev = usb_to_ohci (ohci->dev[edINFO & 0x7F]);
+			td_free (ohci, tdTailP); /* free dummy td */
+   	 		ed->hwINFO = cpu_to_le32 (OHCI_ED_SKIP); 
+			ed->state = ED_NEW;
+			hash_free_ed(ohci, ed);
+   	 		/* if all eds are removed wake up sohci_free_dev */
+   	 		if (!--dev->ed_cnt) {
+				wait_queue_head_t *wait_head = dev->wait;
+
+				dev->wait = 0;
+				if (wait_head)
+					wake_up (wait_head);
+			}
+   	 	} else {
+   	 		ed->state &= ~ED_URB_DEL;
+			tdHeadP = dma_to_td (ohci, le32_to_cpup (&ed->hwHeadP) & 0xfffffff0);
+
+			if (tdHeadP == tdTailP) {
+				if (ed->state == ED_OPER)
+					ep_unlink(ohci, ed);
+			} else
+   	 			ed->hwINFO &= ~cpu_to_le32 (OHCI_ED_SKIP);
+   	 	}
+
+		switch (ed->type) {
+			case PIPE_CONTROL:
+				ctrl = 1;
+				break;
+			case PIPE_BULK:
+				bulk = 1;
+				break;
+		}
+   	}
+   	
+	/* maybe reenable control and bulk lists */ 
+	if (!ohci->disabled) {
+		if (ctrl) 	/* reset control list */
+			writel (0, &ohci->regs->ed_controlcurrent);
+		if (bulk)	/* reset bulk list */
+			writel (0, &ohci->regs->ed_bulkcurrent);
+		if (!ohci->ed_rm_list[!frame] && !ohci->sleeping) {
+			if (ohci->ed_controltail)
+				ohci->hc_control |= OHCI_CTRL_CLE;
+			if (ohci->ed_bulktail)
+				ohci->hc_control |= OHCI_CTRL_BLE;
+			writel (ohci->hc_control, &ohci->regs->control);   
+		}
+	}
+
+   	ohci->ed_rm_list[frame] = NULL;
+   	spin_unlock_irqrestore (&usb_ed_lock, flags);
+}
+
+
+  		
+/*-------------------------------------------------------------------------*/
+
+/* td done list */
+
+static void dl_done_list (ohci_t * ohci, td_t * td_list)
+{
+  	td_t * td_list_next = NULL;
+	ed_t * ed;
+	int cc = 0;
+	struct urb * urb;
+	urb_priv_t * urb_priv;
+ 	__u32 tdINFO, edHeadP, edTailP;
+ 	
+ 	unsigned long flags;
+ 	
+  	while (td_list) {
+   		td_list_next = td_list->next_dl_td;
+   		
+  		urb = td_list->urb;
+  		urb_priv = urb->hcpriv;
+  		tdINFO = le32_to_cpup (&td_list->hwINFO);
+  		
+   		ed = td_list->ed;
+   		
+   		dl_transfer_length(td_list);
+ 			
+  		/* error code of transfer */
+  		cc = TD_CC_GET (tdINFO);
+  		if (cc == TD_CC_STALL)
+			usb_endpoint_halt(urb->dev,
+				usb_pipeendpoint(urb->pipe),
+				usb_pipeout(urb->pipe));
+  		
+  		if (!(urb->transfer_flags & USB_DISABLE_SPD)
+				&& (cc == TD_DATAUNDERRUN))
+			cc = TD_CC_NOERROR;
+
+  		if (++(urb_priv->td_cnt) == urb_priv->length) {
+			if ((ed->state & (ED_OPER | ED_UNLINK))
+					&& (urb_priv->state != URB_DEL)) {
+  				urb->status = cc_to_error[cc];
+  				sohci_return_urb (ohci, urb);
+  			} else {
+				spin_lock_irqsave (&usb_ed_lock, flags);
+  				dl_del_urb (urb);
+				spin_unlock_irqrestore (&usb_ed_lock, flags);
+			}
+  		}
+  		
+  		spin_lock_irqsave (&usb_ed_lock, flags);
+  		if (ed->state != ED_NEW) { 
+  			edHeadP = le32_to_cpup (&ed->hwHeadP) & 0xfffffff0;
+  			edTailP = le32_to_cpup (&ed->hwTailP);
+
+			/* unlink eds if they are not busy */
+     			if ((edHeadP == edTailP) && (ed->state == ED_OPER)) 
+     				ep_unlink (ohci, ed);
+     		}	
+     		spin_unlock_irqrestore (&usb_ed_lock, flags);
+     	
+    		td_list = td_list_next;
+  	}  
+}
+
+
+
+
+/*-------------------------------------------------------------------------*
+ * Virtual Root Hub 
+ *-------------------------------------------------------------------------*/
+ 
+/* Device descriptor */
+static __u8 root_hub_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 root_hub_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 */
+ 	0x02,       /*  __u16 ep_wMaxPacketSize; ((MAX_ROOT_PORTS + 1) / 8 */
+ 	0x00,
+	0xff        /*  __u8  ep_bInterval; 255 ms */
+};
+
+/* Hub class-specific descriptor is constructed dynamically */
+
+
+/*-------------------------------------------------------------------------*/
+
+/* prepare Interrupt pipe data; HUB INTERRUPT ENDPOINT */ 
+ 
+static int rh_send_irq (ohci_t * ohci, void * rh_data, int rh_len)
+{
+	int num_ports;
+	int i;
+	int ret;
+	int len;
+
+	__u8 data[8];
+
+	num_ports = roothub_a (ohci) & RH_A_NDP; 
+	if (num_ports > MAX_ROOT_PORTS) {
+		err ("bogus NDP=%d for OHCI usb-%s", num_ports,
+			ohci->ohci_dev->slot_name);
+		err ("rereads as NDP=%d",
+			readl (&ohci->regs->roothub.a) & RH_A_NDP);
+		/* retry later; "should not happen" */
+		return 0;
+	}
+	*(__u8 *) data = (roothub_status (ohci) & (RH_HS_LPSC | RH_HS_OCIC))
+		? 1: 0;
+	ret = *(__u8 *) data;
+
+	for ( i = 0; i < num_ports; i++) {
+		*(__u8 *) (data + (i + 1) / 8) |= 
+			((roothub_portstatus (ohci, i) &
+				(RH_PS_CSC | RH_PS_PESC | RH_PS_PSSC | RH_PS_OCIC | RH_PS_PRSC))
+			    ? 1: 0) << ((i + 1) % 8);
+		ret += *(__u8 *) (data + (i + 1) / 8);
+	}
+	len = i/8 + 1;
+  
+	if (ret > 0) { 
+		memcpy(rh_data, data,
+		       min_t(unsigned int, len,
+			   min_t(unsigned int, rh_len, sizeof(data))));
+		return len;
+	}
+	return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* Virtual Root Hub INTs are polled by this timer every "interval" ms */
+ 
+static void rh_int_timer_do (unsigned long ptr)
+{
+	int len; 
+
+	struct urb * urb = (struct urb *) ptr;
+	ohci_t * ohci = urb->dev->bus->hcpriv;
+
+	if (ohci->disabled)
+		return;
+
+	/* ignore timers firing during PM suspend, etc */
+	if ((ohci->hc_control & OHCI_CTRL_HCFS) != OHCI_USB_OPER)
+		goto out;
+
+	if(ohci->rh.send) { 
+		len = rh_send_irq (ohci, urb->transfer_buffer, urb->transfer_buffer_length);
+		if (len > 0) {
+			urb->actual_length = len;
+#ifdef DEBUG
+			urb_print (urb, "RET-t(rh)", usb_pipeout (urb->pipe));
+#endif
+			if (urb->complete)
+				urb->complete (urb);
+		}
+	}
+ out:
+	rh_init_int_timer (urb);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* Root Hub INTs are polled by this timer */
+
+static int rh_init_int_timer (struct urb * urb) 
+{
+	ohci_t * ohci = urb->dev->bus->hcpriv;
+
+	ohci->rh.interval = urb->interval;
+	init_timer (&ohci->rh.rh_int_timer);
+	ohci->rh.rh_int_timer.function = rh_int_timer_do;
+	ohci->rh.rh_int_timer.data = (unsigned long) urb;
+	ohci->rh.rh_int_timer.expires = 
+			jiffies + (HZ * (urb->interval < 30? 30: urb->interval)) / 1000;
+	add_timer (&ohci->rh.rh_int_timer);
+	
+	return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+#define OK(x) 			len = (x); break
+#define WR_RH_STAT(x) 		writel((x), &ohci->regs->roothub.status)
+#define WR_RH_PORTSTAT(x) 	writel((x), &ohci->regs->roothub.portstatus[wIndex-1])
+#define RD_RH_STAT		roothub_status(ohci)
+#define RD_RH_PORTSTAT		roothub_portstatus(ohci,wIndex-1)
+
+/* request to virtual root hub */
+
+static int rh_submit_urb (struct urb * urb)
+{
+	struct usb_device * usb_dev = urb->dev;
+	ohci_t * ohci = usb_dev->bus->hcpriv;
+	unsigned int pipe = urb->pipe;
+	struct usb_ctrlrequest * cmd = (struct usb_ctrlrequest *) urb->setup_packet;
+	void * data = urb->transfer_buffer;
+	int leni = urb->transfer_buffer_length;
+	int len = 0;
+	int status = TD_CC_NOERROR;
+	
+	__u32 datab[4];
+	__u8  * data_buf = (__u8 *) datab;
+	
+ 	__u16 bmRType_bReq;
+	__u16 wValue; 
+	__u16 wIndex;
+	__u16 wLength;
+
+	if (usb_pipeint(pipe)) {
+		ohci->rh.urb =  urb;
+		ohci->rh.send = 1;
+		ohci->rh.interval = urb->interval;
+		rh_init_int_timer(urb);
+		urb->status = cc_to_error [TD_CC_NOERROR];
+		
+		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);
+
+	switch (bmRType_bReq) {
+	/* Request Destination:
+	   without flags: Device, 
+	   RH_INTERFACE: interface, 
+	   RH_ENDPOINT: endpoint,
+	   RH_CLASS means HUB here, 
+	   RH_OTHER | RH_CLASS  almost ever means HUB_PORT here 
+	*/
+  
+		case RH_GET_STATUS: 				 		
+				*(__u16 *) data_buf = cpu_to_le16 (1); OK (2);
+		case RH_GET_STATUS | RH_INTERFACE: 	 		
+				*(__u16 *) data_buf = cpu_to_le16 (0); OK (2);
+		case RH_GET_STATUS | RH_ENDPOINT:	 		
+				*(__u16 *) data_buf = cpu_to_le16 (0); OK (2);   
+		case RH_GET_STATUS | RH_CLASS: 				
+				*(__u32 *) data_buf = cpu_to_le32 (
+					RD_RH_STAT & ~(RH_HS_CRWE | RH_HS_DRWE));
+				OK (4);
+		case RH_GET_STATUS | RH_OTHER | RH_CLASS: 	
+				*(__u32 *) data_buf = cpu_to_le32 (RD_RH_PORTSTAT); OK (4);
+
+		case RH_CLEAR_FEATURE | RH_ENDPOINT:  
+			switch (wValue) {
+				case (RH_ENDPOINT_STALL): OK (0);
+			}
+			break;
+
+		case RH_CLEAR_FEATURE | RH_CLASS:
+			switch (wValue) {
+				case RH_C_HUB_LOCAL_POWER:
+					OK(0);
+				case (RH_C_HUB_OVER_CURRENT): 
+						WR_RH_STAT(RH_HS_OCIC); OK (0);
+			}
+			break;
+		
+		case RH_CLEAR_FEATURE | RH_OTHER | RH_CLASS:
+			switch (wValue) {
+				case (RH_PORT_ENABLE): 			
+						WR_RH_PORTSTAT (RH_PS_CCS ); OK (0);
+				case (RH_PORT_SUSPEND):			
+						WR_RH_PORTSTAT (RH_PS_POCI); OK (0);
+				case (RH_PORT_POWER):			
+						WR_RH_PORTSTAT (RH_PS_LSDA); OK (0);
+				case (RH_C_PORT_CONNECTION):	
+						WR_RH_PORTSTAT (RH_PS_CSC ); OK (0);
+				case (RH_C_PORT_ENABLE):		
+						WR_RH_PORTSTAT (RH_PS_PESC); OK (0);
+				case (RH_C_PORT_SUSPEND):		
+						WR_RH_PORTSTAT (RH_PS_PSSC); OK (0);
+				case (RH_C_PORT_OVER_CURRENT):	
+						WR_RH_PORTSTAT (RH_PS_OCIC); OK (0);
+				case (RH_C_PORT_RESET):			
+						WR_RH_PORTSTAT (RH_PS_PRSC); OK (0); 
+			}
+			break;
+ 
+		case RH_SET_FEATURE | RH_OTHER | RH_CLASS:
+			switch (wValue) {
+				case (RH_PORT_SUSPEND):			
+						WR_RH_PORTSTAT (RH_PS_PSS ); OK (0); 
+				case (RH_PORT_RESET): /* BUG IN HUP CODE *********/
+						if (RD_RH_PORTSTAT & RH_PS_CCS)
+						    WR_RH_PORTSTAT (RH_PS_PRS);
+						OK (0);
+				case (RH_PORT_POWER):			
+						WR_RH_PORTSTAT (RH_PS_PPS ); OK (0); 
+				case (RH_PORT_ENABLE): /* BUG IN HUP CODE *********/
+						if (RD_RH_PORTSTAT & RH_PS_CCS)
+						    WR_RH_PORTSTAT (RH_PS_PES );
+						OK (0);
+			}
+			break;
+
+		case RH_SET_ADDRESS: ohci->rh.devnum = wValue; OK(0);
+
+		case RH_GET_DESCRIPTOR:
+			switch ((wValue & 0xff00) >> 8) {
+				case (0x01): /* device descriptor */
+					len = min_t(unsigned int,
+						  leni,
+						  min_t(unsigned int,
+						      sizeof (root_hub_dev_des),
+						      wLength));
+					data_buf = root_hub_dev_des; OK(len);
+				case (0x02): /* configuration descriptor */
+					len = min_t(unsigned int,
+						  leni,
+						  min_t(unsigned int,
+						      sizeof (root_hub_config_des),
+						      wLength));
+					data_buf = root_hub_config_des; OK(len);
+				case (0x03): /* string descriptors */
+					len = usb_root_hub_string (wValue & 0xff,
+						(int)(long) ohci->regs, "OHCI",
+						data, wLength);
+					if (len > 0) {
+						data_buf = data;
+						OK(min_t(int, leni, len));
+					}
+					// else fallthrough
+				default: 
+					status = TD_CC_STALL;
+			}
+			break;
+		
+		case RH_GET_DESCRIPTOR | RH_CLASS:
+		    {
+			    __u32 temp = roothub_a (ohci);
+
+			    data_buf [0] = 9;		// min length;
+			    data_buf [1] = 0x29;
+			    data_buf [2] = temp & RH_A_NDP;
+			    data_buf [3] = 0;
+			    if (temp & RH_A_PSM) 	/* per-port power switching? */
+				data_buf [3] |= 0x1;
+			    if (temp & RH_A_NOCP)	/* no overcurrent reporting? */
+				data_buf [3] |= 0x10;
+			    else if (temp & RH_A_OCPM)	/* per-port overcurrent reporting? */
+				data_buf [3] |= 0x8;
+
+			    datab [1] = 0;
+			    data_buf [5] = (temp & RH_A_POTPGT) >> 24;
+			    temp = roothub_b (ohci);
+			    data_buf [7] = temp & RH_B_DR;
+			    if (data_buf [2] < 7) {
+				data_buf [8] = 0xff;
+			    } else {
+				data_buf [0] += 2;
+				data_buf [8] = (temp & RH_B_DR) >> 8;
+				data_buf [10] = data_buf [9] = 0xff;
+			    }
+				
+			    len = min_t(unsigned int, leni,
+				      min_t(unsigned int, data_buf [0], wLength));
+			    OK (len);
+			}
+ 
+		case RH_GET_CONFIGURATION: 	*(__u8 *) data_buf = 0x01; OK (1);
+
+		case RH_SET_CONFIGURATION: 	WR_RH_STAT (0x10000); OK (0);
+
+		default: 
+			dbg ("unsupported root hub command");
+			status = TD_CC_STALL;
+	}
+	
+#ifdef	DEBUG
+	// ohci_dump_roothub (ohci, 0);
+#endif
+
+	len = min_t(int, len, leni);
+	if (data != data_buf)
+	    memcpy (data, data_buf, len);
+  	urb->actual_length = len;
+	urb->status = cc_to_error [status];
+	
+#ifdef DEBUG
+	urb_print (urb, "RET(rh)", usb_pipeout (urb->pipe));
+#endif
+
+	urb->hcpriv = NULL;
+	usb_dec_dev_use (usb_dev);
+	urb->dev = NULL;
+	if (urb->complete)
+	    	urb->complete (urb);
+	return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int rh_unlink_urb (struct urb * urb)
+{
+	ohci_t * ohci = urb->dev->bus->hcpriv;
+ 
+	if (ohci->rh.urb == urb) {
+		ohci->rh.send = 0;
+		del_timer (&ohci->rh.rh_int_timer);
+		ohci->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;
+}
+ 
+/*-------------------------------------------------------------------------*
+ * HC functions
+ *-------------------------------------------------------------------------*/
+
+/* reset the HC and BUS */
+
+static int hc_reset (ohci_t * ohci)
+{
+	int timeout = 30;
+	int smm_timeout = 50; /* 0,5 sec */
+	 	
+#ifndef __hppa__
+	/* PA-RISC doesn't have SMM, but PDC might leave IR set */
+	if (readl (&ohci->regs->control) & OHCI_CTRL_IR) { /* SMM owns the HC */
+		writel (OHCI_OCR, &ohci->regs->cmdstatus); /* request ownership */
+		dbg("USB HC TakeOver from SMM");
+		while (readl (&ohci->regs->control) & OHCI_CTRL_IR) {
+			wait_ms (10);
+			if (--smm_timeout == 0) {
+				err("USB HC TakeOver failed!");
+				return -1;
+			}
+		}
+	}
+#endif	
+		
+	/* Disable HC interrupts */
+	writel (OHCI_INTR_MIE, &ohci->regs->intrdisable);
+
+	dbg("USB HC reset_hc usb-%s: ctrl = 0x%x ;",
+		ohci->ohci_dev->slot_name,
+		readl (&ohci->regs->control));
+
+  	/* Reset USB (needed by some controllers) */
+	writel (0, &ohci->regs->control);
+
+	/* Force a state change from USBRESET to USBOPERATIONAL for ALi */
+	(void) readl (&ohci->regs->control);	/* PCI posting */
+	writel (ohci->hc_control = OHCI_USB_OPER, &ohci->regs->control);
+
+	/* HC Reset requires max 10 ms delay */
+	writel (OHCI_HCR,  &ohci->regs->cmdstatus);
+	while ((readl (&ohci->regs->cmdstatus) & OHCI_HCR) != 0) {
+		if (--timeout == 0) {
+			err("USB HC reset timed out!");
+			return -1;
+		}	
+		udelay (1);
+	}	 
+	return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* Start an OHCI controller, set the BUS operational
+ * enable interrupts 
+ * connect the virtual root hub */
+
+static int hc_start (ohci_t * ohci)
+{
+  	__u32 mask;
+  	unsigned int fminterval;
+  	struct usb_device  * usb_dev;
+	struct ohci_device * dev;
+	
+	ohci->disabled = 1;
+
+	/* Tell the controller where the control and bulk lists are
+	 * The lists are empty now. */
+	 
+	writel (0, &ohci->regs->ed_controlhead);
+	writel (0, &ohci->regs->ed_bulkhead);
+	
+	writel (ohci->hcca_dma, &ohci->regs->hcca); /* a reset clears this */
+   
+  	fminterval = 0x2edf;
+	writel ((fminterval * 9) / 10, &ohci->regs->periodicstart);
+	fminterval |= ((((fminterval - 210) * 6) / 7) << 16); 
+	writel (fminterval, &ohci->regs->fminterval);	
+	writel (0x628, &ohci->regs->lsthresh);
+
+ 	/* start controller operations */
+ 	ohci->hc_control = OHCI_CONTROL_INIT | OHCI_USB_OPER;
+	ohci->disabled = 0;
+ 	writel (ohci->hc_control, &ohci->regs->control);
+ 
+	/* Choose the interrupts we care about now, others later on demand */
+	mask = OHCI_INTR_MIE | OHCI_INTR_UE | OHCI_INTR_WDH | OHCI_INTR_SO;
+	writel (mask, &ohci->regs->intrenable);
+	writel (mask, &ohci->regs->intrstatus);
+
+#ifdef	OHCI_USE_NPS
+	if(ohci->flags & OHCI_QUIRK_SUCKYIO)
+	{
+		/* NSC 87560 at least requires different setup .. */
+		writel ((roothub_a (ohci) | RH_A_NOCP) &
+			~(RH_A_OCPM | RH_A_POTPGT | RH_A_PSM | RH_A_NPS),
+			&ohci->regs->roothub.a);
+	}
+	else
+	{
+		/* required for AMD-756 and some Mac platforms */
+		writel ((roothub_a (ohci) | RH_A_NPS) & ~RH_A_PSM,
+			&ohci->regs->roothub.a);
+	}
+	writel (RH_HS_LPSC, &ohci->regs->roothub.status);
+#endif	/* OHCI_USE_NPS */
+
+	// POTPGT delay is bits 24-31, in 2 ms units.
+	mdelay ((roothub_a (ohci) >> 23) & 0x1fe);
+ 
+	/* connect the virtual root hub */
+	ohci->rh.devnum = 0;
+	usb_dev = usb_alloc_dev (NULL, ohci->bus);
+	if (!usb_dev) {
+	    ohci->disabled = 1;
+	    return -ENOMEM;
+	}
+
+	dev = usb_to_ohci (usb_dev);
+	ohci->bus->root_hub = usb_dev;
+	usb_connect (usb_dev);
+	if (usb_new_device (usb_dev) != 0) {
+		usb_free_dev (usb_dev); 
+		ohci->disabled = 1;
+		return -ENODEV;
+	}
+	
+	return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* called only from interrupt handler */
+
+static void check_timeouts (struct ohci *ohci)
+{
+	spin_lock (&usb_ed_lock);
+	while (!list_empty (&ohci->timeout_list)) {
+		struct urb	*urb;
+
+		urb = list_entry (ohci->timeout_list.next, struct urb, urb_list);
+		if (time_after (jiffies, urb->timeout))
+			break;
+
+		list_del_init (&urb->urb_list);
+		if (urb->status != -EINPROGRESS)
+			continue;
+
+		urb->transfer_flags |= USB_TIMEOUT_KILLED | USB_ASYNC_UNLINK;
+		spin_unlock (&usb_ed_lock);
+
+		// outside the interrupt handler (in a timer...)
+		// this reference would race interrupts
+		sohci_unlink_urb (urb);
+
+		spin_lock (&usb_ed_lock);
+	}
+	spin_unlock (&usb_ed_lock);
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+/* an interrupt happens */
+
+static void hc_interrupt (int irq, void * __ohci, struct pt_regs * r)
+{
+	ohci_t * ohci = __ohci;
+	struct ohci_regs * regs = ohci->regs;
+ 	int ints; 
+
+	/* avoid (slow) readl if only WDH happened */
+	if ((ohci->hcca->done_head != 0)
+			&& !(le32_to_cpup (&ohci->hcca->done_head) & 0x01)) {
+		ints =  OHCI_INTR_WDH;
+
+	/* cardbus/... hardware gone before remove() */
+	} else if ((ints = readl (&regs->intrstatus)) == ~(u32)0) {
+		ohci->disabled++;
+		err ("%s device removed!", ohci->ohci_dev->slot_name);
+		return;
+
+	/* interrupt for some other device? */
+	} else if ((ints &= readl (&regs->intrenable)) == 0) {
+		return;
+	} 
+
+	// dbg("Interrupt: %x frame: %x", ints, le16_to_cpu (ohci->hcca->frame_no));
+
+	if (ints & OHCI_INTR_UE) {
+		ohci->disabled++;
+		err ("OHCI Unrecoverable Error, controller usb-%s disabled",
+			ohci->ohci_dev->slot_name);
+		// e.g. due to PCI Master/Target Abort
+
+#ifdef	DEBUG
+		ohci_dump (ohci, 1);
+#else
+		// FIXME: be optimistic, hope that bug won't repeat often.
+		// Make some non-interrupt context restart the controller.
+		// Count and limit the retries though; either hardware or
+		// software errors can go forever...
+#endif
+		hc_reset (ohci);
+	}
+  
+	if (ints & OHCI_INTR_WDH) {
+		writel (OHCI_INTR_WDH, &regs->intrdisable);	
+		dl_done_list (ohci, dl_reverse_done_list (ohci));
+		writel (OHCI_INTR_WDH, &regs->intrenable); 
+	}
+  
+	if (ints & OHCI_INTR_SO) {
+		dbg("USB Schedule overrun");
+		writel (OHCI_INTR_SO, &regs->intrenable); 	 
+	}
+
+	// FIXME:  this assumes SOF (1/ms) interrupts don't get lost...
+	if (ints & OHCI_INTR_SF) { 
+		unsigned int frame = le16_to_cpu (ohci->hcca->frame_no) & 1;
+		writel (OHCI_INTR_SF, &regs->intrdisable);	
+		if (ohci->ed_rm_list[!frame] != NULL) {
+			dl_del_list (ohci, !frame);
+		}
+		if (ohci->ed_rm_list[frame] != NULL)
+			writel (OHCI_INTR_SF, &regs->intrenable);	
+	}
+
+	if (!list_empty (&ohci->timeout_list)) {
+		check_timeouts (ohci);
+// FIXME:  enable SF as needed in a timer;
+// don't make lots of 1ms interrupts
+// On unloaded USB, think 4k ~= 4-5msec
+		if (!list_empty (&ohci->timeout_list))
+			writel (OHCI_INTR_SF, &regs->intrenable);	
+	}
+
+	writel (ints, &regs->intrstatus);
+	writel (OHCI_INTR_MIE, &regs->intrenable);	
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* allocate OHCI */
+
+static ohci_t * __devinit hc_alloc_ohci (struct pci_dev *dev, void * mem_base)
+{
+	ohci_t * ohci;
+
+	ohci = (ohci_t *) kmalloc (sizeof *ohci, GFP_KERNEL);
+	if (!ohci)
+		return NULL;
+		
+	memset (ohci, 0, sizeof (ohci_t));
+
+	ohci->hcca = pci_alloc_consistent (dev, sizeof *ohci->hcca,
+			&ohci->hcca_dma);
+        if (!ohci->hcca) {
+                kfree (ohci);
+                return NULL;
+        }
+        memset (ohci->hcca, 0, sizeof (struct ohci_hcca));
+
+	ohci->disabled = 1;
+	ohci->sleeping = 0;
+	ohci->irq = -1;
+	ohci->regs = mem_base;   
+
+	ohci->ohci_dev = dev;
+	pci_set_drvdata(dev, ohci);
+ 
+	INIT_LIST_HEAD (&ohci->ohci_hcd_list);
+	list_add (&ohci->ohci_hcd_list, &ohci_hcd_list);
+
+	INIT_LIST_HEAD (&ohci->timeout_list);
+
+	ohci->bus = usb_alloc_bus (&sohci_device_operations);
+	if (!ohci->bus) {
+		pci_set_drvdata (dev, NULL);
+		pci_free_consistent (ohci->ohci_dev, sizeof *ohci->hcca,
+				ohci->hcca, ohci->hcca_dma);
+		kfree (ohci);
+		return NULL;
+	}
+	ohci->bus->bus_name = dev->slot_name;
+	ohci->bus->hcpriv = (void *) ohci;
+
+	return ohci;
+} 
+
+
+/*-------------------------------------------------------------------------*/
+
+/* De-allocate all resources.. */
+
+static void hc_release_ohci (ohci_t * ohci)
+{	
+	dbg ("USB HC release ohci usb-%s", ohci->ohci_dev->slot_name);
+
+	/* disconnect all devices */    
+	if (ohci->bus->root_hub)
+		usb_disconnect (&ohci->bus->root_hub);
+
+	if (!ohci->disabled)
+		hc_reset (ohci);
+	
+	if (ohci->irq >= 0) {
+		free_irq (ohci->irq, ohci);
+		ohci->irq = -1;
+	}
+	pci_set_drvdata(ohci->ohci_dev, NULL);
+	if (ohci->bus) {
+		if (ohci->bus->busnum != -1)
+			usb_deregister_bus (ohci->bus);
+
+		usb_free_bus (ohci->bus);
+	}
+
+	list_del (&ohci->ohci_hcd_list);
+	INIT_LIST_HEAD (&ohci->ohci_hcd_list);
+
+	ohci_mem_cleanup (ohci);
+    
+	/* unmap the IO address space */
+	iounmap (ohci->regs);
+
+	pci_free_consistent (ohci->ohci_dev, sizeof *ohci->hcca,
+		ohci->hcca, ohci->hcca_dma);
+	kfree (ohci);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* Increment the module usage count, start the control thread and
+ * return success. */
+
+static struct pci_driver ohci_pci_driver;
+ 
+static int __devinit
+hc_found_ohci (struct pci_dev *dev, int irq,
+	void *mem_base, const struct pci_device_id *id)
+{
+	ohci_t * ohci;
+	char buf[8], *bufp = buf;
+	int ret;
+
+#ifndef __sparc__
+	sprintf(buf, "%d", irq);
+#else
+	bufp = __irq_itoa(irq);
+#endif
+	printk(KERN_INFO __FILE__ ": USB OHCI at membase 0x%lx, IRQ %s\n",
+		(unsigned long)	mem_base, bufp);
+	printk(KERN_INFO __FILE__ ": usb-%s, %s\n", dev->slot_name, dev->name);
+    
+	ohci = hc_alloc_ohci (dev, mem_base);
+	if (!ohci) {
+		return -ENOMEM;
+	}
+	if ((ret = ohci_mem_init (ohci)) < 0) {
+		hc_release_ohci (ohci);
+		return ret;
+	}
+	ohci->flags = id->driver_data;
+	
+	/* Check for NSC87560. We have to look at the bridge (fn1) to identify
+	   the USB (fn2). This quirk might apply to more or even all NSC stuff
+	   I don't know.. */
+	   
+	if(dev->vendor == PCI_VENDOR_ID_NS)
+	{
+		struct pci_dev *fn1  = pci_find_slot(dev->bus->number, PCI_DEVFN(PCI_SLOT(dev->devfn), 1));
+		if(fn1 && fn1->vendor == PCI_VENDOR_ID_NS && fn1->device == PCI_DEVICE_ID_NS_87560_LIO)
+			ohci->flags |= OHCI_QUIRK_SUCKYIO;
+		
+	}
+	
+	if (ohci->flags & OHCI_QUIRK_SUCKYIO)
+		printk (KERN_INFO __FILE__ ": Using NSC SuperIO setup\n");
+	if (ohci->flags & OHCI_QUIRK_AMD756)
+		printk (KERN_INFO __FILE__ ": AMD756 erratum 4 workaround\n");
+
+	if (hc_reset (ohci) < 0) {
+		hc_release_ohci (ohci);
+		return -ENODEV;
+	}
+
+	/* FIXME this is a second HC reset; why?? */
+	writel (ohci->hc_control = OHCI_USB_RESET, &ohci->regs->control);
+	wait_ms (10);
+
+	usb_register_bus (ohci->bus);
+	
+	if (request_irq (irq, hc_interrupt, SA_SHIRQ,
+			ohci_pci_driver.name, ohci) != 0) {
+		err ("request interrupt %s failed", bufp);
+		hc_release_ohci (ohci);
+		return -EBUSY;
+	}
+	ohci->irq = irq;     
+
+	if (hc_start (ohci) < 0) {
+		err ("can't start usb-%s", dev->slot_name);
+		hc_release_ohci (ohci);
+		return -EBUSY;
+	}
+
+#ifdef	DEBUG
+	ohci_dump (ohci, 1);
+#endif
+	return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+#ifdef	CONFIG_PM
+
+/* controller died; cleanup debris, then restart */
+/* must not be called from interrupt context */
+
+static void hc_restart (ohci_t *ohci)
+{
+	int temp;
+	int i;
+
+	if (ohci->pci_latency)
+		pci_write_config_byte (ohci->ohci_dev, PCI_LATENCY_TIMER, ohci->pci_latency);
+
+	ohci->disabled = 1;
+	ohci->sleeping = 0;
+	if (ohci->bus->root_hub)
+		usb_disconnect (&ohci->bus->root_hub);
+	
+	/* empty the interrupt branches */
+	for (i = 0; i < NUM_INTS; i++) ohci->ohci_int_load[i] = 0;
+	for (i = 0; i < NUM_INTS; i++) ohci->hcca->int_table[i] = 0;
+	
+	/* no EDs to remove */
+	ohci->ed_rm_list [0] = NULL;
+	ohci->ed_rm_list [1] = NULL;
+
+	/* empty control and bulk lists */	 
+	ohci->ed_isotail     = NULL;
+	ohci->ed_controltail = NULL;
+	ohci->ed_bulktail    = NULL;
+
+	if ((temp = hc_reset (ohci)) < 0 || (temp = hc_start (ohci)) < 0) {
+		err ("can't restart usb-%s, %d", ohci->ohci_dev->slot_name, temp);
+	} else
+		dbg ("restart usb-%s completed", ohci->ohci_dev->slot_name);
+}
+
+#endif	/* CONFIG_PM */
+
+/*-------------------------------------------------------------------------*/
+
+/* configured so that an OHCI device is always provided */
+/* always called with process context; sleeping is OK */
+
+static int __devinit
+ohci_pci_probe (struct pci_dev *dev, const struct pci_device_id *id)
+{
+	unsigned long mem_resource, mem_len;
+	void *mem_base;
+	int status;
+
+	if (pci_enable_device(dev) < 0)
+		return -ENODEV;
+
+        if (!dev->irq) {
+        	err("found OHCI device with no IRQ assigned. check BIOS settings!");
+		pci_disable_device (dev);
+   	        return -ENODEV;
+        }
+	
+	/* we read its hardware registers as memory */
+	mem_resource = pci_resource_start(dev, 0);
+	mem_len = pci_resource_len(dev, 0);
+	if (!request_mem_region (mem_resource, mem_len, ohci_pci_driver.name)) {
+		dbg ("controller already in use");
+		pci_disable_device (dev);
+		return -EBUSY;
+	}
+
+	mem_base = ioremap_nocache (mem_resource, mem_len);
+	if (!mem_base) {
+		err("Error mapping OHCI memory");
+		release_mem_region (mem_resource, mem_len);
+		pci_disable_device (dev);
+		return -EFAULT;
+	}
+
+	/* controller writes into our memory */
+	pci_set_master (dev);
+
+	status = hc_found_ohci (dev, dev->irq, mem_base, id);
+	if (status < 0) {
+		iounmap (mem_base);
+		release_mem_region (mem_resource, mem_len);
+		pci_disable_device (dev);
+	}
+	return status;
+} 
+
+/*-------------------------------------------------------------------------*/
+
+/* may be called from interrupt context [interface spec] */
+/* may be called without controller present */
+/* may be called with controller, bus, and devices active */
+
+static void __devexit
+ohci_pci_remove (struct pci_dev *dev)
+{
+	ohci_t		*ohci = pci_get_drvdata(dev);
+
+	dbg ("remove %s controller usb-%s%s%s",
+		hcfs2string (ohci->hc_control & OHCI_CTRL_HCFS),
+		dev->slot_name,
+		ohci->disabled ? " (disabled)" : "",
+		in_interrupt () ? " in interrupt" : ""
+		);
+#ifdef	DEBUG
+	ohci_dump (ohci, 1);
+#endif
+
+	/* don't wake up sleeping controllers, or block in interrupt context */
+	if ((ohci->hc_control & OHCI_CTRL_HCFS) != OHCI_USB_OPER || in_interrupt ()) {
+		dbg ("controller being disabled");
+		ohci->disabled = 1;
+	}
+
+	/* on return, USB will always be reset (if present) */
+	if (ohci->disabled)
+		writel (ohci->hc_control = OHCI_USB_RESET,
+			&ohci->regs->control);
+
+	hc_release_ohci (ohci);
+
+	release_mem_region (pci_resource_start (dev, 0), pci_resource_len (dev, 0));
+	pci_disable_device (dev);
+}
+
+
+#ifdef	CONFIG_PM
+
+/*-------------------------------------------------------------------------*/
+
+static int
+ohci_pci_suspend (struct pci_dev *dev, u32 state)
+{
+	ohci_t			*ohci = pci_get_drvdata(dev);
+	unsigned long		flags;
+	u16 cmd;
+
+	if ((ohci->hc_control & OHCI_CTRL_HCFS) != OHCI_USB_OPER) {
+		dbg ("can't suspend usb-%s (state is %s)", dev->slot_name,
+			hcfs2string (ohci->hc_control & OHCI_CTRL_HCFS));
+		return -EIO;
+	}
+
+	/* act as if usb suspend can always be used */
+	info ("USB suspend: usb-%s", dev->slot_name);
+	ohci->sleeping = 1;
+
+	/* First stop processing */
+  	spin_lock_irqsave (&usb_ed_lock, flags);
+	ohci->hc_control &= ~(OHCI_CTRL_PLE|OHCI_CTRL_CLE|OHCI_CTRL_BLE|OHCI_CTRL_IE);
+	writel (ohci->hc_control, &ohci->regs->control);
+	writel (OHCI_INTR_SF, &ohci->regs->intrstatus);
+	(void) readl (&ohci->regs->intrstatus);
+  	spin_unlock_irqrestore (&usb_ed_lock, flags);
+
+	/* Wait a frame or two */
+	mdelay(1);
+	if (!readl (&ohci->regs->intrstatus) & OHCI_INTR_SF)
+		mdelay (1);
+		
+#ifdef CONFIG_PMAC_PBOOK
+	if (_machine == _MACH_Pmac)
+		disable_irq (ohci->irq);
+	/* else, 2.4 assumes shared irqs -- don't disable */
+#endif
+	/* Enable remote wakeup */
+	writel (readl(&ohci->regs->intrenable) | OHCI_INTR_RD, &ohci->regs->intrenable);
+
+	/* Suspend chip and let things settle down a bit */
+	ohci->hc_control = OHCI_USB_SUSPEND;
+	writel (ohci->hc_control, &ohci->regs->control);
+	(void) readl (&ohci->regs->control);
+	mdelay (500); /* No schedule here ! */
+	switch (readl (&ohci->regs->control) & OHCI_CTRL_HCFS) {
+		case OHCI_USB_RESET:
+			dbg("Bus in reset phase ???");
+			break;
+		case OHCI_USB_RESUME:
+			dbg("Bus in resume phase ???");
+			break;
+		case OHCI_USB_OPER:
+			dbg("Bus in operational phase ???");
+			break;
+		case OHCI_USB_SUSPEND:
+			dbg("Bus suspended");
+			break;
+	}
+	/* In some rare situations, Apple's OHCI have happily trashed
+	 * memory during sleep. We disable it's bus master bit during
+	 * suspend
+	 */
+	pci_read_config_word (dev, PCI_COMMAND, &cmd);
+	cmd &= ~PCI_COMMAND_MASTER;
+	pci_write_config_word (dev, PCI_COMMAND, cmd);
+#ifdef CONFIG_PMAC_PBOOK
+	{
+	   	struct device_node	*of_node;
+
+		/* Disable USB PAD & cell clock */
+		of_node = pci_device_to_OF_node (ohci->ohci_dev);
+		if (of_node)
+			pmac_call_feature(PMAC_FTR_USB_ENABLE, of_node, 0, 0);
+	}
+#endif
+	return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int
+ohci_pci_resume (struct pci_dev *dev)
+{
+	ohci_t		*ohci = pci_get_drvdata(dev);
+	int		temp;
+	unsigned long	flags;
+
+	/* guard against multiple resumes */
+	atomic_inc (&ohci->resume_count);
+	if (atomic_read (&ohci->resume_count) != 1) {
+		err ("concurrent PCI resumes for usb-%s", dev->slot_name);
+		atomic_dec (&ohci->resume_count);
+		return 0;
+	}
+
+#ifdef CONFIG_PMAC_PBOOK
+	{
+		struct device_node *of_node;
+
+		/* Re-enable USB PAD & cell clock */
+		of_node = pci_device_to_OF_node (ohci->ohci_dev);
+		if (of_node)
+			pmac_call_feature(PMAC_FTR_USB_ENABLE, of_node, 0, 1);
+	}
+#endif
+
+	/* did we suspend, or were we powered off? */
+	ohci->hc_control = readl (&ohci->regs->control);
+	temp = ohci->hc_control & OHCI_CTRL_HCFS;
+
+#ifdef DEBUG
+	/* the registers may look crazy here */
+	ohci_dump_status (ohci);
+#endif
+
+	/* Re-enable bus mastering */
+	pci_set_master(ohci->ohci_dev);
+	
+	switch (temp) {
+
+	case OHCI_USB_RESET:	// lost power
+		info ("USB restart: usb-%s", dev->slot_name);
+		hc_restart (ohci);
+		break;
+
+	case OHCI_USB_SUSPEND:	// host wakeup
+	case OHCI_USB_RESUME:	// remote wakeup
+		info ("USB continue: usb-%s from %s wakeup", dev->slot_name,
+			(temp == OHCI_USB_SUSPEND)
+				? "host" : "remote");
+		ohci->hc_control = OHCI_USB_RESUME;
+		writel (ohci->hc_control, &ohci->regs->control);
+		(void) readl (&ohci->regs->control);
+		mdelay (20); /* no schedule here ! */
+		/* Some controllers (lucent) need a longer delay here */
+		mdelay (15);
+		temp = readl (&ohci->regs->control);
+		temp = ohci->hc_control & OHCI_CTRL_HCFS;
+		if (temp != OHCI_USB_RESUME) {
+			err ("controller usb-%s won't resume", dev->slot_name);
+			ohci->disabled = 1;
+			return -EIO;
+		}
+
+		/* Some chips likes being resumed first */
+		writel (OHCI_USB_OPER, &ohci->regs->control);
+		(void) readl (&ohci->regs->control);
+		mdelay (3);
+
+		/* Then re-enable operations */
+		spin_lock_irqsave (&usb_ed_lock, flags);
+		ohci->disabled = 0;
+		ohci->sleeping = 0;
+		ohci->hc_control = OHCI_CONTROL_INIT | OHCI_USB_OPER;
+		if (!ohci->ed_rm_list[0] && !ohci->ed_rm_list[1]) {
+			if (ohci->ed_controltail)
+				ohci->hc_control |= OHCI_CTRL_CLE;
+			if (ohci->ed_bulktail)
+				ohci->hc_control |= OHCI_CTRL_BLE;
+		}
+		writel (ohci->hc_control, &ohci->regs->control);
+		writel (OHCI_INTR_SF, &ohci->regs->intrstatus);
+		writel (OHCI_INTR_SF, &ohci->regs->intrenable);
+		/* Check for a pending done list */
+		writel (OHCI_INTR_WDH, &ohci->regs->intrdisable);	
+		(void) readl (&ohci->regs->intrdisable);
+		spin_unlock_irqrestore (&usb_ed_lock, flags);
+#ifdef CONFIG_PMAC_PBOOK
+		if (_machine == _MACH_Pmac)
+			enable_irq (ohci->irq);
+#endif
+		if (ohci->hcca->done_head)
+			dl_done_list (ohci, dl_reverse_done_list (ohci));
+		writel (OHCI_INTR_WDH, &ohci->regs->intrenable); 
+		writel (OHCI_BLF, &ohci->regs->cmdstatus); /* start bulk list */
+		writel (OHCI_CLF, &ohci->regs->cmdstatus); /* start Control list */
+		break;
+
+	default:
+		warn ("odd PCI resume for usb-%s", dev->slot_name);
+	}
+
+	/* controller is operational, extra resumes are harmless */
+	atomic_dec (&ohci->resume_count);
+
+	return 0;
+}
+
+#endif	/* CONFIG_PM */
+
+
+/*-------------------------------------------------------------------------*/
+
+static const struct pci_device_id __devinitdata ohci_pci_ids [] = { {
+
+	/*
+	 * AMD-756 [Viper] USB has a serious erratum when used with
+	 * lowspeed devices like mice.
+	 */
+	vendor:		0x1022,
+	device:		0x740c,
+	subvendor:	PCI_ANY_ID,
+	subdevice:	PCI_ANY_ID,
+
+	driver_data:	OHCI_QUIRK_AMD756,
+
+} , {
+
+	/* handle any USB OHCI controller */
+	class: 		((PCI_CLASS_SERIAL_USB << 8) | 0x10),
+	class_mask: 	~0,
+
+	/* no matter who makes it */
+	vendor:		PCI_ANY_ID,
+	device:		PCI_ANY_ID,
+	subvendor:	PCI_ANY_ID,
+	subdevice:	PCI_ANY_ID,
+
+	}, { /* end: all zeroes */ }
+};
+
+MODULE_DEVICE_TABLE (pci, ohci_pci_ids);
+
+static struct pci_driver ohci_pci_driver = {
+	name:		"usb-ohci",
+	id_table:	&ohci_pci_ids [0],
+
+	probe:		ohci_pci_probe,
+	remove:		__devexit_p(ohci_pci_remove),
+
+#ifdef	CONFIG_PM
+	suspend:	ohci_pci_suspend,
+	resume:		ohci_pci_resume,
+#endif	/* PM */
+};
+
+ 
+/*-------------------------------------------------------------------------*/
+
+static int __init ohci_hcd_init (void) 
+{
+	return pci_module_init (&ohci_pci_driver);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void __exit ohci_hcd_cleanup (void) 
+{	
+	pci_unregister_driver (&ohci_pci_driver);
+}
+
+module_init (ohci_hcd_init);
+module_exit (ohci_hcd_cleanup);
+
+
+MODULE_AUTHOR( DRIVER_AUTHOR );
+MODULE_DESCRIPTION( DRIVER_DESC );
+MODULE_LICENSE("GPL");
diff -urN linux-2.4.21-pre5/drivers/usb/host/usb-ohci.h linux-2.4.21-pre6/drivers/usb/host/usb-ohci.h
--- linux-2.4.21-pre5/drivers/usb/host/usb-ohci.h	Wed Dec 31 16:00:00 1969
+++ linux-2.4.21-pre6/drivers/usb/host/usb-ohci.h	Wed Mar 26 16:11:39 2003
@@ -0,0 +1,643 @@
+/*
+ * URB OHCI HCD (Host Controller Driver) for USB.
+ * 
+ * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
+ * (C) Copyright 2000-2001 David Brownell <dbrownell@users.sourceforge.net>
+ * 
+ * usb-ohci.h
+ */
+
+ 
+static int cc_to_error[16] = { 
+
+/* mapping of the OHCI CC status to error codes */ 
+	/* No  Error  */               USB_ST_NOERROR,
+	/* CRC Error  */               USB_ST_CRC,
+	/* Bit Stuff  */               USB_ST_BITSTUFF,
+	/* Data Togg  */               USB_ST_CRC,
+	/* Stall      */               USB_ST_STALL,
+	/* DevNotResp */               USB_ST_NORESPONSE,
+	/* PIDCheck   */               USB_ST_BITSTUFF,
+	/* UnExpPID   */               USB_ST_BITSTUFF,
+	/* DataOver   */               USB_ST_DATAOVERRUN,
+	/* DataUnder  */               USB_ST_DATAUNDERRUN,
+	/* reservd    */               USB_ST_NORESPONSE,
+	/* reservd    */               USB_ST_NORESPONSE,
+	/* BufferOver */               USB_ST_BUFFEROVERRUN,
+	/* BuffUnder  */               USB_ST_BUFFERUNDERRUN,
+	/* Not Access */               USB_ST_NORESPONSE,
+	/* Not Access */               USB_ST_NORESPONSE 
+};
+
+#include <linux/config.h>
+
+/* ED States */
+
+#define ED_NEW 		0x00
+#define ED_UNLINK 	0x01
+#define ED_OPER		0x02
+#define ED_DEL		0x04
+#define ED_URB_DEL  	0x08
+
+/* usb_ohci_ed */
+struct ed {
+	__u32 hwINFO;       
+	__u32 hwTailP;
+	__u32 hwHeadP;
+	__u32 hwNextED;
+
+	struct ed * ed_prev;  
+	__u8 int_period;
+	__u8 int_branch;
+	__u8 int_load; 
+	__u8 int_interval;
+	__u8 state;
+	__u8 type; 
+	__u16 last_iso;
+	struct ed * ed_rm_list;
+
+	dma_addr_t dma;
+	__u32 unused[3];
+} __attribute((aligned(16)));
+typedef struct ed ed_t;
+
+ 
+/* TD info field */
+#define TD_CC       0xf0000000
+#define TD_CC_GET(td_p) ((td_p >>28) & 0x0f)
+#define TD_CC_SET(td_p, cc) (td_p) = ((td_p) & 0x0fffffff) | (((cc) & 0x0f) << 28)
+#define TD_EC       0x0C000000
+#define TD_T        0x03000000
+#define TD_T_DATA0  0x02000000
+#define TD_T_DATA1  0x03000000
+#define TD_T_TOGGLE 0x00000000
+#define TD_R        0x00040000
+#define TD_DI       0x00E00000
+#define TD_DI_SET(X) (((X) & 0x07)<< 21)
+#define TD_DP       0x00180000
+#define TD_DP_SETUP 0x00000000
+#define TD_DP_IN    0x00100000
+#define TD_DP_OUT   0x00080000
+
+#define TD_ISO	    0x00010000
+#define TD_DEL      0x00020000
+
+/* CC Codes */
+#define TD_CC_NOERROR      0x00
+#define TD_CC_CRC          0x01
+#define TD_CC_BITSTUFFING  0x02
+#define TD_CC_DATATOGGLEM  0x03
+#define TD_CC_STALL        0x04
+#define TD_DEVNOTRESP      0x05
+#define TD_PIDCHECKFAIL    0x06
+#define TD_UNEXPECTEDPID   0x07
+#define TD_DATAOVERRUN     0x08
+#define TD_DATAUNDERRUN    0x09
+#define TD_BUFFEROVERRUN   0x0C
+#define TD_BUFFERUNDERRUN  0x0D
+#define TD_NOTACCESSED     0x0F
+
+
+#define MAXPSW 1
+
+struct td {
+	__u32 hwINFO;
+  	__u32 hwCBP;		/* Current Buffer Pointer */
+  	__u32 hwNextTD;		/* Next TD Pointer */
+  	__u32 hwBE;		/* Memory Buffer End Pointer */
+
+  	__u16 hwPSW[MAXPSW];
+  	__u8 unused;
+  	__u8 index;
+  	struct ed * ed;
+  	struct td * next_dl_td;
+  	struct urb * urb;
+
+	dma_addr_t td_dma;
+	dma_addr_t data_dma;
+	__u32 unused2[2];
+} __attribute((aligned(32)));	/* normally 16, iso needs 32 */
+typedef struct td td_t;
+
+#define OHCI_ED_SKIP	(1 << 14)
+
+/*
+ * The HCCA (Host Controller Communications Area) is a 256 byte
+ * structure defined in the OHCI spec. that the host controller is
+ * told the base address of.  It must be 256-byte aligned.
+ */
+ 
+#define NUM_INTS 32	/* part of the OHCI standard */
+struct ohci_hcca {
+	__u32	int_table[NUM_INTS];	/* Interrupt ED table */
+	__u16	frame_no;		/* current frame number */
+	__u16	pad1;			/* set to 0 on each frame_no change */
+	__u32	done_head;		/* info returned for an interrupt */
+	u8		reserved_for_hc[116];
+} __attribute((aligned(256)));
+
+  
+/*
+ * Maximum number of root hub ports.  
+ */
+#define MAX_ROOT_PORTS	15	/* maximum OHCI root hub ports */
+
+/*
+ * This is the structure of the OHCI controller's memory mapped I/O
+ * region.  This is Memory Mapped I/O.  You must use the readl() and
+ * writel() macros defined in asm/io.h to access these!!
+ */
+struct ohci_regs {
+	/* control and status registers */
+	__u32	revision;
+	__u32	control;
+	__u32	cmdstatus;
+	__u32	intrstatus;
+	__u32	intrenable;
+	__u32	intrdisable;
+	/* memory pointers */
+	__u32	hcca;
+	__u32	ed_periodcurrent;
+	__u32	ed_controlhead;
+	__u32	ed_controlcurrent;
+	__u32	ed_bulkhead;
+	__u32	ed_bulkcurrent;
+	__u32	donehead;
+	/* frame counters */
+	__u32	fminterval;
+	__u32	fmremaining;
+	__u32	fmnumber;
+	__u32	periodicstart;
+	__u32	lsthresh;
+	/* Root hub ports */
+	struct	ohci_roothub_regs {
+		__u32	a;
+		__u32	b;
+		__u32	status;
+		__u32	portstatus[MAX_ROOT_PORTS];
+	} roothub;
+} __attribute((aligned(32)));
+
+
+/* OHCI CONTROL AND STATUS REGISTER MASKS */
+
+/*
+ * HcControl (control) register masks
+ */
+#define OHCI_CTRL_CBSR	(3 << 0)	/* control/bulk service ratio */
+#define OHCI_CTRL_PLE	(1 << 2)	/* periodic list enable */
+#define OHCI_CTRL_IE	(1 << 3)	/* isochronous enable */
+#define OHCI_CTRL_CLE	(1 << 4)	/* control list enable */
+#define OHCI_CTRL_BLE	(1 << 5)	/* bulk list enable */
+#define OHCI_CTRL_HCFS	(3 << 6)	/* host controller functional state */
+#define OHCI_CTRL_IR	(1 << 8)	/* interrupt routing */
+#define OHCI_CTRL_RWC	(1 << 9)	/* remote wakeup connected */
+#define OHCI_CTRL_RWE	(1 << 10)	/* remote wakeup enable */
+
+/* pre-shifted values for HCFS */
+#	define OHCI_USB_RESET	(0 << 6)
+#	define OHCI_USB_RESUME	(1 << 6)
+#	define OHCI_USB_OPER	(2 << 6)
+#	define OHCI_USB_SUSPEND	(3 << 6)
+
+/*
+ * HcCommandStatus (cmdstatus) register masks
+ */
+#define OHCI_HCR	(1 << 0)	/* host controller reset */
+#define OHCI_CLF  	(1 << 1)	/* control list filled */
+#define OHCI_BLF  	(1 << 2)	/* bulk list filled */
+#define OHCI_OCR  	(1 << 3)	/* ownership change request */
+#define OHCI_SOC  	(3 << 16)	/* scheduling overrun count */
+
+/*
+ * masks used with interrupt registers:
+ * HcInterruptStatus (intrstatus)
+ * HcInterruptEnable (intrenable)
+ * HcInterruptDisable (intrdisable)
+ */
+#define OHCI_INTR_SO	(1 << 0)	/* scheduling overrun */
+#define OHCI_INTR_WDH	(1 << 1)	/* writeback of done_head */
+#define OHCI_INTR_SF	(1 << 2)	/* start frame */
+#define OHCI_INTR_RD	(1 << 3)	/* resume detect */
+#define OHCI_INTR_UE	(1 << 4)	/* unrecoverable error */
+#define OHCI_INTR_FNO	(1 << 5)	/* frame number overflow */
+#define OHCI_INTR_RHSC	(1 << 6)	/* root hub status change */
+#define OHCI_INTR_OC	(1 << 30)	/* ownership change */
+#define OHCI_INTR_MIE	(1 << 31)	/* master interrupt enable */
+
+
+
+/* Virtual Root HUB */
+struct virt_root_hub {
+	int devnum; /* Address of Root Hub endpoint */ 
+	void * urb;
+	void * int_addr;
+	int send;
+	int interval;
+	struct timer_list rh_int_timer;
+};
+
+
+/* USB HUB CONSTANTS (not OHCI-specific; see hub.h) */
+ 
+/* destination of request */
+#define RH_INTERFACE               0x01
+#define RH_ENDPOINT                0x02
+#define RH_OTHER                   0x03
+
+#define RH_CLASS                   0x20
+#define RH_VENDOR                  0x40
+
+/* 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
+/* Our Vendor Specific Request */
+#define RH_SET_EP               0x2000
+
+
+/* Hub port features */
+#define RH_PORT_CONNECTION         0x00
+#define RH_PORT_ENABLE             0x01
+#define RH_PORT_SUSPEND            0x02
+#define RH_PORT_OVER_CURRENT       0x03
+#define RH_PORT_RESET              0x04
+#define RH_PORT_POWER              0x08
+#define RH_PORT_LOW_SPEED          0x09
+
+#define RH_C_PORT_CONNECTION       0x10
+#define RH_C_PORT_ENABLE           0x11
+#define RH_C_PORT_SUSPEND          0x12
+#define RH_C_PORT_OVER_CURRENT     0x13
+#define RH_C_PORT_RESET            0x14  
+
+/* Hub features */
+#define RH_C_HUB_LOCAL_POWER       0x00
+#define RH_C_HUB_OVER_CURRENT      0x01
+
+#define RH_DEVICE_REMOTE_WAKEUP    0x00
+#define RH_ENDPOINT_STALL          0x01
+
+#define RH_ACK                     0x01
+#define RH_REQ_ERR                 -1
+#define RH_NACK                    0x00
+
+
+/* OHCI ROOT HUB REGISTER MASKS */
+ 
+/* roothub.portstatus [i] bits */
+#define RH_PS_CCS            0x00000001   	/* current connect status */
+#define RH_PS_PES            0x00000002   	/* port enable status*/
+#define RH_PS_PSS            0x00000004   	/* port suspend status */
+#define RH_PS_POCI           0x00000008   	/* port over current indicator */
+#define RH_PS_PRS            0x00000010  	/* port reset status */
+#define RH_PS_PPS            0x00000100   	/* port power status */
+#define RH_PS_LSDA           0x00000200    	/* low speed device attached */
+#define RH_PS_CSC            0x00010000 	/* connect status change */
+#define RH_PS_PESC           0x00020000   	/* port enable status change */
+#define RH_PS_PSSC           0x00040000    	/* port suspend status change */
+#define RH_PS_OCIC           0x00080000    	/* over current indicator change */
+#define RH_PS_PRSC           0x00100000   	/* port reset status change */
+
+/* roothub.status bits */
+#define RH_HS_LPS	     0x00000001		/* local power status */
+#define RH_HS_OCI	     0x00000002		/* over current indicator */
+#define RH_HS_DRWE	     0x00008000		/* device remote wakeup enable */
+#define RH_HS_LPSC	     0x00010000		/* local power status change */
+#define RH_HS_OCIC	     0x00020000		/* over current indicator change */
+#define RH_HS_CRWE	     0x80000000		/* clear remote wakeup enable */
+
+/* roothub.b masks */
+#define RH_B_DR		0x0000ffff		/* device removable flags */
+#define RH_B_PPCM	0xffff0000		/* port power control mask */
+
+/* roothub.a masks */
+#define	RH_A_NDP	(0xff << 0)		/* number of downstream ports */
+#define	RH_A_PSM	(1 << 8)		/* power switching mode */
+#define	RH_A_NPS	(1 << 9)		/* no power switching */
+#define	RH_A_DT		(1 << 10)		/* device type (mbz) */
+#define	RH_A_OCPM	(1 << 11)		/* over current protection mode */
+#define	RH_A_NOCP	(1 << 12)		/* no over current protection */
+#define	RH_A_POTPGT	(0xff << 24)		/* power on to power good time */
+
+/* urb */
+typedef struct 
+{
+	ed_t * ed;
+	__u16 length;	// number of tds associated with this request
+	__u16 td_cnt;	// number of tds already serviced
+	int   state;
+	wait_queue_head_t * wait;
+	td_t * td[0];	// list pointer to all corresponding TDs associated with this request
+
+} urb_priv_t;
+#define URB_DEL 1
+
+
+/* Hash struct used for TD/ED hashing */
+struct hash_t {
+	void		*virt;
+	dma_addr_t	dma;
+	struct hash_t	*next; // chaining for collision cases
+};
+
+/* List of TD/ED hash entries */
+struct hash_list_t {
+	struct hash_t	*head;
+	struct hash_t	*tail;
+};
+
+#define TD_HASH_SIZE    64    /* power'o'two */
+#define ED_HASH_SIZE    64    /* power'o'two */
+
+#define TD_HASH_FUNC(td_dma) ((td_dma ^ (td_dma >> 5)) % TD_HASH_SIZE)
+#define ED_HASH_FUNC(ed_dma) ((ed_dma ^ (ed_dma >> 5)) % ED_HASH_SIZE)
+
+
+/*
+ * This is the full ohci controller description
+ *
+ * Note how the "proper" USB information is just
+ * a subset of what the full implementation needs. (Linus)
+ */
+
+
+typedef struct ohci {
+	struct ohci_hcca *hcca;		/* hcca */
+	dma_addr_t hcca_dma;
+
+	int irq;
+	int disabled;			/* e.g. got a UE, we're hung */
+	int sleeping;
+	atomic_t resume_count;		/* defending against multiple resumes */
+	unsigned long flags;		/* for HC bugs */
+#define	OHCI_QUIRK_AMD756	0x01		/* erratum #4 */
+#define OHCI_QUIRK_SUCKYIO	0x02		/* NSC superio */
+
+	struct ohci_regs * regs;	/* OHCI controller's memory */
+	struct list_head ohci_hcd_list;	/* list of all ohci_hcd */
+
+	struct list_head timeout_list;
+	// struct list_head urb_list; 	// list of all pending urbs
+	// spinlock_t urb_list_lock; 	// lock to keep consistency 
+  
+	int ohci_int_load[32];		/* load of the 32 Interrupt Chains (for load balancing)*/
+	ed_t * ed_rm_list[2];     /* lists of all endpoints to be removed */
+	ed_t * ed_bulktail;       /* last endpoint of bulk list */
+	ed_t * ed_controltail;    /* last endpoint of control list */
+ 	ed_t * ed_isotail;        /* last endpoint of iso list */
+	int intrstatus;
+	__u32 hc_control;		/* copy of the hc control reg */
+	struct usb_bus * bus;    
+	struct usb_device * dev[128];
+	struct virt_root_hub rh;
+
+	/* PCI device handle, settings, ... */
+	struct pci_dev	*ohci_dev;
+	u8		pci_latency;
+	struct pci_pool	*td_cache;
+	struct pci_pool	*dev_cache;
+	struct hash_list_t	td_hash[TD_HASH_SIZE];
+	struct hash_list_t	ed_hash[ED_HASH_SIZE];
+
+} ohci_t;
+
+#define NUM_EDS 32		/* num of preallocated endpoint descriptors */
+
+struct ohci_device {
+	ed_t 	ed[NUM_EDS];
+	dma_addr_t dma;
+	int ed_cnt;
+	wait_queue_head_t * wait;
+};
+
+// #define ohci_to_usb(ohci)	((ohci)->usb)
+#define usb_to_ohci(usb)	((struct ohci_device *)(usb)->hcpriv)
+
+/* hcd */
+/* endpoint */
+static int ep_link(ohci_t * ohci, ed_t * ed);
+static int ep_unlink(ohci_t * ohci, ed_t * ed);
+static ed_t * ep_add_ed(struct usb_device * usb_dev, unsigned int pipe, int interval, int load, int mem_flags);
+static void ep_rm_ed(struct usb_device * usb_dev, ed_t * ed);
+/* td */
+static void td_fill(ohci_t * ohci, unsigned int info, dma_addr_t data, int len, struct urb * urb, int index);
+static void td_submit_urb(struct urb * urb);
+/* root hub */
+static int rh_submit_urb(struct urb * urb);
+static int rh_unlink_urb(struct urb * urb);
+static int rh_init_int_timer(struct urb * urb);
+
+/*-------------------------------------------------------------------------*/
+
+#define ALLOC_FLAGS (in_interrupt () || current->state != TASK_RUNNING ? GFP_ATOMIC : GFP_KERNEL)
+
+#ifdef DEBUG
+#	define OHCI_MEM_FLAGS	SLAB_POISON
+#else
+#	define OHCI_MEM_FLAGS	0
+#endif
+ 
+#ifndef CONFIG_PCI
+#	error "usb-ohci currently requires PCI-based controllers"
+	/* to support non-PCI OHCIs, you need custom bus/mem/... glue */
+#endif
+
+
+/* Recover a TD/ED using its collision chain */
+static inline void *
+dma_to_ed_td (struct hash_list_t * entry, dma_addr_t dma)
+{
+	struct hash_t * scan = entry->head;
+	while (scan && scan->dma != dma)
+		scan = scan->next;
+	if (!scan)
+		BUG();
+	return scan->virt;
+}
+
+static inline struct ed *
+dma_to_ed (struct ohci * hc, dma_addr_t ed_dma)
+{
+	return (struct ed *) dma_to_ed_td(&(hc->ed_hash[ED_HASH_FUNC(ed_dma)]),
+				      ed_dma);
+}
+
+static inline struct td *
+dma_to_td (struct ohci * hc, dma_addr_t td_dma)
+{
+	return (struct td *) dma_to_ed_td(&(hc->td_hash[TD_HASH_FUNC(td_dma)]),
+				      td_dma);
+}
+
+/* Add a hash entry for a TD/ED; return true on success */
+static inline int
+hash_add_ed_td(struct hash_list_t * entry, void * virt, dma_addr_t dma)
+{
+	struct hash_t * scan;
+	
+	scan = (struct hash_t *)kmalloc(sizeof(struct hash_t), ALLOC_FLAGS);
+	if (!scan)
+		return 0;
+	
+	if (!entry->tail) {
+		entry->head = entry->tail = scan;
+	} else {
+		entry->tail->next = scan;
+		entry->tail = scan;
+	}
+	
+	scan->virt = virt;
+	scan->dma = dma;
+	scan->next = NULL;
+	return 1;
+}
+
+static inline int
+hash_add_ed (struct ohci * hc, struct ed * ed)
+{
+	return hash_add_ed_td (&(hc->ed_hash[ED_HASH_FUNC(ed->dma)]),
+			ed, ed->dma);
+}
+
+static inline int
+hash_add_td (struct ohci * hc, struct td * td)
+{
+	return hash_add_ed_td (&(hc->td_hash[TD_HASH_FUNC(td->td_dma)]),
+			td, td->td_dma);
+}
+
+
+static inline void
+hash_free_ed_td (struct hash_list_t * entry, void * virt)
+{
+	struct hash_t *scan, *prev;
+	scan = prev = entry->head;
+
+	// Find and unlink hash entry
+	while (scan && scan->virt != virt) {
+		prev = scan;
+		scan = scan->next;
+	}
+	if (scan) {
+		if (scan == entry->head) {
+			if (entry->head == entry->tail)
+				entry->head = entry->tail = NULL;
+			else
+				entry->head = scan->next;
+		} else if (scan == entry->tail) {
+			entry->tail = prev;
+			prev->next = NULL;
+		} else
+			prev->next = scan->next;
+		kfree(scan);
+	}
+}
+
+static inline void
+hash_free_ed (struct ohci * hc, struct ed * ed)
+{
+	hash_free_ed_td (&(hc->ed_hash[ED_HASH_FUNC(ed->dma)]), ed);
+}
+
+static inline void
+hash_free_td (struct ohci * hc, struct td * td)
+{
+	hash_free_ed_td (&(hc->td_hash[TD_HASH_FUNC(td->td_dma)]), td);
+}
+
+
+static int ohci_mem_init (struct ohci *ohci)
+{
+	ohci->td_cache = pci_pool_create ("ohci_td", ohci->ohci_dev,
+		sizeof (struct td),
+		32 /* byte alignment */,
+		0 /* no page-crossing issues */,
+		GFP_KERNEL | OHCI_MEM_FLAGS);
+	if (!ohci->td_cache)
+		return -ENOMEM;
+	ohci->dev_cache = pci_pool_create ("ohci_dev", ohci->ohci_dev,
+		sizeof (struct ohci_device),
+		16 /* byte alignment */,
+		0 /* no page-crossing issues */,
+		GFP_KERNEL | OHCI_MEM_FLAGS);
+	if (!ohci->dev_cache)
+		return -ENOMEM;
+	return 0;
+}
+
+static void ohci_mem_cleanup (struct ohci *ohci)
+{
+	if (ohci->td_cache) {
+		pci_pool_destroy (ohci->td_cache);
+		ohci->td_cache = 0;
+	}
+	if (ohci->dev_cache) {
+		pci_pool_destroy (ohci->dev_cache);
+		ohci->dev_cache = 0;
+	}
+}
+
+/* TDs ... */
+static inline struct td *
+td_alloc (struct ohci *hc, int mem_flags)
+{
+	dma_addr_t	dma;
+	struct td	*td;
+
+	td = pci_pool_alloc (hc->td_cache, mem_flags, &dma);
+	if (td) {
+		td->td_dma = dma;
+
+		/* hash it for later reverse mapping */
+		if (!hash_add_td (hc, td)) {
+			pci_pool_free (hc->td_cache, td, dma);
+			return NULL;
+		}
+	}
+	return td;
+}
+
+static inline void
+td_free (struct ohci *hc, struct td *td)
+{
+	hash_free_td (hc, td);
+	pci_pool_free (hc->td_cache, td, td->td_dma);
+}
+
+
+/* DEV + EDs ... only the EDs need to be consistent */
+static inline struct ohci_device *
+dev_alloc (struct ohci *hc, int mem_flags)
+{
+	dma_addr_t		dma;
+	struct ohci_device	*dev;
+	int			i, offset;
+
+	dev = pci_pool_alloc (hc->dev_cache, mem_flags, &dma);
+	if (dev) {
+		memset (dev, 0, sizeof (*dev));
+		dev->dma = dma;
+		offset = ((char *)&dev->ed) - ((char *)dev);
+		for (i = 0; i < NUM_EDS; i++, offset += sizeof dev->ed [0])
+			dev->ed [i].dma = dma + offset;
+		/* add to hashtable if used */
+	}
+	return dev;
+}
+
+static inline void
+dev_free (struct ohci *hc, struct ohci_device *dev)
+{
+	pci_pool_free (hc->dev_cache, dev, dev->dma);
+}
+
diff -urN linux-2.4.21-pre5/drivers/usb/host/usb-uhci-debug.h linux-2.4.21-pre6/drivers/usb/host/usb-uhci-debug.h
--- linux-2.4.21-pre5/drivers/usb/host/usb-uhci-debug.h	Wed Dec 31 16:00:00 1969
+++ linux-2.4.21-pre6/drivers/usb/host/usb-uhci-debug.h	Wed Mar 26 16:11:39 2003
@@ -0,0 +1,195 @@
+#ifdef DEBUG
+static void __attribute__((__unused__)) uhci_show_qh (puhci_desc_t qh)
+{
+	if (qh->type != QH_TYPE) {
+		dbg("qh has not QH_TYPE");
+		return;
+	}
+	dbg("QH @ %p/%08llX:", qh, (unsigned long long)qh->dma_addr);
+
+	if (qh->hw.qh.head & UHCI_PTR_TERM)
+		dbg("    Head Terminate");
+	else 
+		dbg("    Head: %s @ %08X",
+		    (qh->hw.qh.head & UHCI_PTR_QH?"QH":"TD"),
+		    qh->hw.qh.head & ~UHCI_PTR_BITS);
+
+	if (qh->hw.qh.element & UHCI_PTR_TERM)
+		dbg("    Element Terminate");
+	else 
+		dbg("    Element: %s @ %08X",
+		    (qh->hw.qh.element & UHCI_PTR_QH?"QH":"TD"),
+		    qh->hw.qh.element & ~UHCI_PTR_BITS);
+}
+#endif
+
+#if 0
+static void uhci_show_td (puhci_desc_t td)
+{
+	char *spid;
+	
+	switch (td->hw.td.info & 0xff) {
+	case USB_PID_SETUP:
+		spid = "SETUP";
+		break;
+	case USB_PID_OUT:
+		spid = " OUT ";
+		break;
+	case USB_PID_IN:
+		spid = " IN  ";
+		break;
+	default:
+		spid = "  ?  ";
+		break;
+	}
+
+	warn("  TD @ %p/%08X, MaxLen=%02x DT%d EP=%x Dev=%x PID=(%s) buf=%08x",
+	     td, td->dma_addr,
+	     td->hw.td.info >> 21,
+	     ((td->hw.td.info >> 19) & 1),
+	     (td->hw.td.info >> 15) & 15,
+	     (td->hw.td.info >> 8) & 127,
+	     spid,
+	     td->hw.td.buffer);
+
+	warn("    Len=%02x e%d %s%s%s%s%s%s%s%s%s%s",
+	     td->hw.td.status & 0x7ff,
+	     ((td->hw.td.status >> 27) & 3),
+	     (td->hw.td.status & TD_CTRL_SPD) ? "SPD " : "",
+	     (td->hw.td.status & TD_CTRL_LS) ? "LS " : "",
+	     (td->hw.td.status & TD_CTRL_IOC) ? "IOC " : "",
+	     (td->hw.td.status & TD_CTRL_ACTIVE) ? "Active " : "",
+	     (td->hw.td.status & TD_CTRL_STALLED) ? "Stalled " : "",
+	     (td->hw.td.status & TD_CTRL_DBUFERR) ? "DataBufErr " : "",
+	     (td->hw.td.status & TD_CTRL_BABBLE) ? "Babble " : "",
+	     (td->hw.td.status & TD_CTRL_NAK) ? "NAK " : "",
+	     (td->hw.td.status & TD_CTRL_CRCTIMEO) ? "CRC/Timeo " : "",
+	     (td->hw.td.status & TD_CTRL_BITSTUFF) ? "BitStuff " : ""
+		);
+
+	if (td->hw.td.link & UHCI_PTR_TERM)
+		warn("   TD Link Terminate");
+	else 
+		warn("    Link points to %s @ %08x, %s",
+		     (td->hw.td.link & UHCI_PTR_QH?"QH":"TD"),
+		     td->hw.td.link & ~UHCI_PTR_BITS,
+		     (td->hw.td.link & UHCI_PTR_DEPTH ? "Depth first" : "Breadth first"));
+}
+#endif
+
+#ifdef DEBUG
+static void __attribute__((__unused__)) uhci_show_td_queue (puhci_desc_t td)
+{
+	//dbg("uhci_show_td_queue %p (%08lX):", td, td->dma_addr);
+#if 1
+	return;
+#else
+	while (1) {
+		uhci_show_td (td);
+		if (td->hw.td.link & UHCI_PTR_TERM)
+			break;
+		if (td != bus_to_virt (td->hw.td.link & ~UHCI_PTR_BITS))
+			td = bus_to_virt (td->hw.td.link & ~UHCI_PTR_BITS);
+		else {
+			dbg("td points to itself!");
+			break;
+		}
+	}
+#endif
+}
+
+static void __attribute__((__unused__)) uhci_show_queue (puhci_desc_t qh)
+{
+#if 0
+	uhci_desc_t *start_qh=qh;
+#endif
+
+	dbg("uhci_show_queue %p:", qh);
+#if 1
+	return;
+#else
+	while (1) {
+		uhci_show_qh (qh);
+
+		if (!(qh->hw.qh.element & UHCI_PTR_TERM))
+			uhci_show_td_queue (bus_to_virt (qh->hw.qh.element & ~UHCI_PTR_BITS));
+
+		if (qh->hw.qh.head & UHCI_PTR_TERM)
+			break;
+
+		if (qh != bus_to_virt (qh->hw.qh.head & ~UHCI_PTR_BITS))
+			qh = bus_to_virt (qh->hw.qh.head & ~UHCI_PTR_BITS);
+		else {
+			dbg("qh points to itself!");
+			break;
+		}
+		
+		if (qh==start_qh) { // avoid loop
+			dbg("Loop detect");
+			break;
+		}
+	}		
+#endif
+}
+
+static void __attribute__((__unused__)) uhci_show_sc (int port, unsigned short status)
+{
+	dbg("  stat%d     =     %04x   %s%s%s%s%s%s%s%s",
+	     port,
+	     status,
+	     (status & USBPORTSC_SUSP) ? "PortSuspend " : "",
+	     (status & USBPORTSC_PR) ? "PortReset " : "",
+	     (status & USBPORTSC_LSDA) ? "LowSpeed " : "",
+	     (status & USBPORTSC_RD) ? "ResumeDetect " : "",
+	     (status & USBPORTSC_PEC) ? "EnableChange " : "",
+	     (status & USBPORTSC_PE) ? "PortEnabled " : "",
+	     (status & USBPORTSC_CSC) ? "ConnectChange " : "",
+	     (status & USBPORTSC_CCS) ? "PortConnected " : "");
+}
+
+void uhci_show_status (puhci_t s)
+{
+	unsigned int io_addr = s->io_addr;
+	unsigned short usbcmd, usbstat, usbint, usbfrnum;
+	unsigned int flbaseadd;
+	unsigned char sof;
+	unsigned short portsc1, portsc2;
+
+	usbcmd = inw (io_addr + 0);
+	usbstat = inw (io_addr + 2);
+	usbint = inw (io_addr + 4);
+	usbfrnum = inw (io_addr + 6);
+	flbaseadd = inl (io_addr + 8);
+	sof = inb (io_addr + 12);
+	portsc1 = inw (io_addr + 16);
+	portsc2 = inw (io_addr + 18);
+
+	dbg("  usbcmd    =     %04x   %s%s%s%s%s%s%s%s",
+	     usbcmd,
+	     (usbcmd & USBCMD_MAXP) ? "Maxp64 " : "Maxp32 ",
+	     (usbcmd & USBCMD_CF) ? "CF " : "",
+	     (usbcmd & USBCMD_SWDBG) ? "SWDBG " : "",
+	     (usbcmd & USBCMD_FGR) ? "FGR " : "",
+	     (usbcmd & USBCMD_EGSM) ? "EGSM " : "",
+	     (usbcmd & USBCMD_GRESET) ? "GRESET " : "",
+	     (usbcmd & USBCMD_HCRESET) ? "HCRESET " : "",
+	     (usbcmd & USBCMD_RS) ? "RS " : "");
+
+	dbg("  usbstat   =     %04x   %s%s%s%s%s%s",
+	     usbstat,
+	     (usbstat & USBSTS_HCH) ? "HCHalted " : "",
+	     (usbstat & USBSTS_HCPE) ? "HostControllerProcessError " : "",
+	     (usbstat & USBSTS_HSE) ? "HostSystemError " : "",
+	     (usbstat & USBSTS_RD) ? "ResumeDetect " : "",
+	     (usbstat & USBSTS_ERROR) ? "USBError " : "",
+	     (usbstat & USBSTS_USBINT) ? "USBINT " : "");
+
+	dbg("  usbint    =     %04x", usbint);
+	dbg("  usbfrnum  =   (%d)%03x", (usbfrnum >> 10) & 1,
+	     0xfff & (4 * (unsigned int) usbfrnum));
+	dbg("  flbaseadd = %08x", flbaseadd);
+	dbg("  sof       =       %02x", sof);
+	uhci_show_sc (1, portsc1);
+	uhci_show_sc (2, portsc2);
+}
+#endif
diff -urN linux-2.4.21-pre5/drivers/usb/host/usb-uhci.c linux-2.4.21-pre6/drivers/usb/host/usb-uhci.c
--- linux-2.4.21-pre5/drivers/usb/host/usb-uhci.c	Wed Dec 31 16:00:00 1969
+++ linux-2.4.21-pre6/drivers/usb/host/usb-uhci.c	Wed Mar 26 16:11:39 2003
@@ -0,0 +1,3143 @@
+/* 
+ * Universal Host Controller Interface driver for USB (take II).
+ *
+ * (c) 1999-2001 Georg Acher, acher@in.tum.de (executive slave) (base guitar)
+ *               Deti Fliegl, deti@fliegl.de (executive slave) (lead voice)
+ *               Thomas Sailer, sailer@ife.ee.ethz.ch (chief consultant) (cheer leader)
+ *               Roman Weissgaerber, weissg@vienna.at (virt root hub) (studio porter)
+ * (c) 2000      Yggdrasil Computing, Inc. (port of new PCI interface support
+ *               from usb-ohci.c by Adam Richter, adam@yggdrasil.com).
+ * (C) 2000      David Brownell, david-b@pacbell.net (usb-ohci.c)
+ *          
+ * HW-initalization based on material of
+ *
+ * (C) Copyright 1999 Linus Torvalds
+ * (C) Copyright 1999 Johannes Erdfelt
+ * (C) Copyright 1999 Randy Dunlap
+ * (C) Copyright 1999 Gregory P. Smith
+ *
+ * $Id: usb-uhci.c,v 1.275 2002/01/19 20:57:33 acher Exp $
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+#include <linux/errno.h>
+#include <linux/unistd.h>
+#include <linux/interrupt.h>	/* for in_interrupt() */
+#include <linux/init.h>
+#include <linux/version.h>
+#include <linux/pm.h>
+#include <linux/timer.h>
+
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+
+/* This enables more detailed sanity checks in submit_iso */
+//#define ISO_SANITY_CHECK
+
+/* This enables debug printks */
+#define DEBUG
+
+/* This enables all symbols to be exported, to ease debugging oopses */
+//#define DEBUG_SYMBOLS
+
+/* This enables an extra UHCI slab for memory debugging */
+#define DEBUG_SLAB
+
+#define VERSTR "$Revision: 1.275 $ time " __TIME__ " " __DATE__
+
+#include <linux/usb.h>
+#include "usb-uhci.h"
+#include "usb-uhci-debug.h"
+
+#include "../hcd.h"
+
+/*
+ * Version Information
+ */
+#define DRIVER_VERSION "v1.275"
+#define DRIVER_AUTHOR "Georg Acher, Deti Fliegl, Thomas Sailer, Roman Weissgaerber"
+#define DRIVER_DESC "USB Universal Host Controller Interface driver"
+
+#undef DEBUG
+#undef dbg
+#define dbg(format, arg...) do {} while (0)
+#define DEBUG_SYMBOLS
+#ifdef DEBUG_SYMBOLS
+	#define _static
+	#ifndef EXPORT_SYMTAB
+		#define EXPORT_SYMTAB
+	#endif
+#else
+	#define _static static
+#endif
+
+#define queue_dbg dbg //err
+#define async_dbg dbg //err
+
+#ifdef DEBUG_SLAB
+	static kmem_cache_t *urb_priv_kmem;
+#endif
+
+#define SLAB_FLAG     (in_interrupt () || current->state != TASK_RUNNING ? SLAB_ATOMIC : SLAB_KERNEL)
+#define KMALLOC_FLAG  (in_interrupt () || current->state != TASK_RUNNING ? GFP_ATOMIC : GFP_KERNEL)
+
+/* CONFIG_USB_UHCI_HIGH_BANDWITH turns on Full Speed Bandwidth
+ * Reclamation: feature that puts loop on descriptor loop when
+ * there's some transfer going on. With FSBR, USB performance
+ * is optimal, but PCI can be slowed down up-to 5 times, slowing down
+ * system performance (eg. framebuffer devices).
+ */
+#define CONFIG_USB_UHCI_HIGH_BANDWIDTH
+
+/* *_DEPTH_FIRST puts descriptor in depth-first mode. This has
+ * somehow similar effect to FSBR (higher speed), but does not
+ * slow PCI down. OTOH USB performace is slightly slower than
+ * in FSBR case and single device could hog whole USB, starving
+ * other devices.
+ */
+#define USE_CTRL_DEPTH_FIRST 0  // 0: Breadth first, 1: Depth first
+#define USE_BULK_DEPTH_FIRST 0  // 0: Breadth first, 1: Depth first
+
+/* Turning off both CONFIG_USB_UHCI_HIGH_BANDWITH and *_DEPTH_FIRST
+ * will lead to <64KB/sec performance over USB for bulk transfers targeting
+ * one device's endpoint. You probably do not want to do that.
+ */
+
+// stop bandwidth reclamation after (roughly) 50ms
+#define IDLE_TIMEOUT  (HZ/20)
+
+// Suppress HC interrupt error messages for 5s
+#define ERROR_SUPPRESSION_TIME (HZ*5)
+
+_static int rh_submit_urb (struct urb *urb);
+_static int rh_unlink_urb (struct urb *urb);
+_static int delete_qh (uhci_t *s, uhci_desc_t *qh);
+_static int process_transfer (uhci_t *s, struct urb *urb, int mode);
+_static int process_interrupt (uhci_t *s, struct urb *urb);
+_static int process_iso (uhci_t *s, struct urb *urb, int force);
+
+// How much URBs with ->next are walked
+#define MAX_NEXT_COUNT 2048
+
+static uhci_t *devs = NULL;
+
+/* used by userspace UHCI data structure dumper */
+uhci_t **uhci_devices = &devs;
+
+/*-------------------------------------------------------------------*/
+// Cleans up collected QHs, but not more than 100 in one go
+void clean_descs(uhci_t *s, int force)
+{
+	struct list_head *q;
+	uhci_desc_t *qh;
+	int now=UHCI_GET_CURRENT_FRAME(s), n=0;
+
+	q=s->free_desc.prev;
+
+	while (q != &s->free_desc && (force || n<100)) {
+		qh = list_entry (q, uhci_desc_t, horizontal);
+		q=qh->horizontal.prev;
+
+		if ((qh->last_used!=now) || force)
+			delete_qh(s,qh);
+		n++;
+	}
+}
+/*-------------------------------------------------------------------*/
+_static void uhci_switch_timer_int(uhci_t *s)
+{
+
+	if (!list_empty(&s->urb_unlinked))
+		set_td_ioc(s->td1ms);
+	else
+		clr_td_ioc(s->td1ms);
+
+	if (s->timeout_urbs)
+		set_td_ioc(s->td32ms);
+	else
+		clr_td_ioc(s->td32ms);
+	wmb();
+}
+/*-------------------------------------------------------------------*/
+#ifdef CONFIG_USB_UHCI_HIGH_BANDWIDTH
+_static void enable_desc_loop(uhci_t *s, struct urb *urb)
+{
+	unsigned long flags;
+
+	if (urb->transfer_flags & USB_NO_FSBR)
+		return;
+
+	spin_lock_irqsave (&s->qh_lock, flags);
+	s->chain_end->hw.qh.head&=cpu_to_le32(~UHCI_PTR_TERM);
+	mb();
+	s->loop_usage++;
+	((urb_priv_t*)urb->hcpriv)->use_loop=1;
+	spin_unlock_irqrestore (&s->qh_lock, flags);
+}
+/*-------------------------------------------------------------------*/
+_static void disable_desc_loop(uhci_t *s, struct urb *urb)
+{
+	unsigned long flags;
+
+	if (urb->transfer_flags & USB_NO_FSBR)
+		return;
+
+	spin_lock_irqsave (&s->qh_lock, flags);
+	if (((urb_priv_t*)urb->hcpriv)->use_loop) {
+		s->loop_usage--;
+
+		if (!s->loop_usage) {
+			s->chain_end->hw.qh.head|=cpu_to_le32(UHCI_PTR_TERM);
+			mb();
+		}
+		((urb_priv_t*)urb->hcpriv)->use_loop=0;
+	}
+	spin_unlock_irqrestore (&s->qh_lock, flags);
+}
+#endif
+/*-------------------------------------------------------------------*/
+_static void queue_urb_unlocked (uhci_t *s, struct urb *urb)
+{
+	struct list_head *p=&urb->urb_list;
+#ifdef CONFIG_USB_UHCI_HIGH_BANDWIDTH
+	{
+		int type;
+		type=usb_pipetype (urb->pipe);
+
+		if ((type == PIPE_BULK) || (type == PIPE_CONTROL))
+			enable_desc_loop(s, urb);
+	}
+#endif
+	urb->status = -EINPROGRESS;
+	((urb_priv_t*)urb->hcpriv)->started=jiffies;
+	list_add (p, &s->urb_list);
+	if (urb->timeout)
+		s->timeout_urbs++;
+	uhci_switch_timer_int(s);
+}
+/*-------------------------------------------------------------------*/
+_static void queue_urb (uhci_t *s, struct urb *urb)
+{
+	unsigned long flags=0;
+
+	spin_lock_irqsave (&s->urb_list_lock, flags);
+	queue_urb_unlocked(s,urb);
+	spin_unlock_irqrestore (&s->urb_list_lock, flags);
+}
+/*-------------------------------------------------------------------*/
+_static void dequeue_urb (uhci_t *s, struct urb *urb)
+{
+#ifdef CONFIG_USB_UHCI_HIGH_BANDWIDTH
+	int type;
+
+	type=usb_pipetype (urb->pipe);
+
+	if ((type == PIPE_BULK) || (type == PIPE_CONTROL))
+		disable_desc_loop(s, urb);
+#endif
+
+	list_del (&urb->urb_list);
+	if (urb->timeout && s->timeout_urbs)
+		s->timeout_urbs--;
+
+}
+/*-------------------------------------------------------------------*/
+_static int alloc_td (uhci_t *s, uhci_desc_t ** new, int flags)
+{
+	dma_addr_t dma_handle;
+
+	*new = pci_pool_alloc(s->desc_pool, GFP_DMA | GFP_ATOMIC, &dma_handle);
+	if (!*new)
+		return -ENOMEM;
+	memset (*new, 0, sizeof (uhci_desc_t));
+	(*new)->dma_addr = dma_handle;
+	set_td_link((*new), UHCI_PTR_TERM | (flags & UHCI_PTR_BITS));	// last by default
+	(*new)->type = TD_TYPE;
+	mb();
+	INIT_LIST_HEAD (&(*new)->vertical);
+	INIT_LIST_HEAD (&(*new)->horizontal);
+	
+	return 0;
+}
+/*-------------------------------------------------------------------*/
+// append a qh to td.link physically, the SW linkage is not affected
+_static void append_qh(uhci_t *s, uhci_desc_t *td, uhci_desc_t* qh, int  flags)
+{
+	unsigned long xxx;
+	
+	spin_lock_irqsave (&s->td_lock, xxx);
+
+	set_td_link(td, qh->dma_addr | (flags & UHCI_PTR_DEPTH) | UHCI_PTR_QH);
+       
+	mb();
+	spin_unlock_irqrestore (&s->td_lock, xxx);
+}
+/*-------------------------------------------------------------------*/
+/* insert td at last position in td-list of qh (vertical) */
+_static int insert_td (uhci_t *s, uhci_desc_t *qh, uhci_desc_t* new, int flags)
+{
+	uhci_desc_t *prev;
+	unsigned long xxx;
+	
+	spin_lock_irqsave (&s->td_lock, xxx);
+
+	list_add_tail (&new->vertical, &qh->vertical);
+
+	prev = list_entry (new->vertical.prev, uhci_desc_t, vertical);
+
+	if (qh == prev ) {
+		// virgin qh without any tds
+		set_qh_element(qh, new->dma_addr | UHCI_PTR_TERM);
+	}
+	else {
+		// already tds inserted, implicitely remove TERM bit of prev
+		set_td_link(prev, new->dma_addr | (flags & UHCI_PTR_DEPTH));
+	}
+	mb();
+	spin_unlock_irqrestore (&s->td_lock, xxx);
+	
+	return 0;
+}
+/*-------------------------------------------------------------------*/
+/* insert new_td after td (horizontal) */
+_static int insert_td_horizontal (uhci_t *s, uhci_desc_t *td, uhci_desc_t* new)
+{
+	uhci_desc_t *next;
+	unsigned long flags;
+	
+	spin_lock_irqsave (&s->td_lock, flags);
+
+	next = list_entry (td->horizontal.next, uhci_desc_t, horizontal);
+	list_add (&new->horizontal, &td->horizontal);
+	new->hw.td.link = td->hw.td.link;
+	set_td_link(td, new->dma_addr);
+	mb();
+	spin_unlock_irqrestore (&s->td_lock, flags);	
+	
+	return 0;
+}
+/*-------------------------------------------------------------------*/
+_static int unlink_td (uhci_t *s, uhci_desc_t *element, int phys_unlink)
+{
+	uhci_desc_t *next, *prev;
+	int dir = 0;
+	unsigned long flags;
+	
+	spin_lock_irqsave (&s->td_lock, flags);
+	
+	next = list_entry (element->vertical.next, uhci_desc_t, vertical);
+	
+	if (next == element) {
+		dir = 1;
+		prev = list_entry (element->horizontal.prev, uhci_desc_t, horizontal);
+	}
+	else 
+		prev = list_entry (element->vertical.prev, uhci_desc_t, vertical);
+	
+	if (phys_unlink) {
+		// really remove HW linking
+		if (prev->type == TD_TYPE)
+			prev->hw.td.link = element->hw.td.link;
+		else
+			prev->hw.qh.element = element->hw.td.link;
+	}
+
+	mb ();
+
+	if (dir == 0)
+		list_del (&element->vertical);
+	else
+		list_del (&element->horizontal);
+	
+	spin_unlock_irqrestore (&s->td_lock, flags);	
+	
+	return 0;
+}
+
+/*-------------------------------------------------------------------*/
+_static int delete_desc (uhci_t *s, uhci_desc_t *element)
+{
+	pci_pool_free(s->desc_pool, element, element->dma_addr);
+	return 0;
+}
+/*-------------------------------------------------------------------*/
+// Allocates qh element
+_static int alloc_qh (uhci_t *s, uhci_desc_t ** new)
+{
+	dma_addr_t dma_handle;
+
+	*new = pci_pool_alloc(s->desc_pool, GFP_DMA | GFP_ATOMIC, &dma_handle);
+	if (!*new)
+		return -ENOMEM;
+	memset (*new, 0, sizeof (uhci_desc_t));
+	(*new)->dma_addr = dma_handle;
+	set_qh_head(*new, UHCI_PTR_TERM);
+	set_qh_element(*new, UHCI_PTR_TERM);
+	(*new)->type = QH_TYPE;
+	
+	mb();
+	INIT_LIST_HEAD (&(*new)->horizontal);
+	INIT_LIST_HEAD (&(*new)->vertical);
+	
+	dbg("Allocated qh @ %p", *new);
+	
+	return 0;
+}
+/*-------------------------------------------------------------------*/
+// inserts new qh before/after the qh at pos
+// flags: 0: insert before pos, 1: insert after pos (for low speed transfers)
+_static int insert_qh (uhci_t *s, uhci_desc_t *pos, uhci_desc_t *new, int order)
+{
+	uhci_desc_t *old;
+	unsigned long flags;
+
+	spin_lock_irqsave (&s->qh_lock, flags);
+
+	if (!order) {
+		// (OLD) (POS) -> (OLD) (NEW) (POS)
+		old = list_entry (pos->horizontal.prev, uhci_desc_t, horizontal);
+		list_add_tail (&new->horizontal, &pos->horizontal);
+		set_qh_head(new, MAKE_QH_ADDR (pos)) ;
+		if (!(old->hw.qh.head & cpu_to_le32(UHCI_PTR_TERM)))
+			set_qh_head(old, MAKE_QH_ADDR (new)) ;
+	}
+	else {
+		// (POS) (OLD) -> (POS) (NEW) (OLD)
+		old = list_entry (pos->horizontal.next, uhci_desc_t, horizontal);
+		list_add (&new->horizontal, &pos->horizontal);
+		set_qh_head(new, MAKE_QH_ADDR (old));
+		set_qh_head(pos, MAKE_QH_ADDR (new)) ;
+	}
+
+	mb ();
+	
+	spin_unlock_irqrestore (&s->qh_lock, flags);
+
+	return 0;
+}
+
+/*-------------------------------------------------------------------*/
+_static int unlink_qh (uhci_t *s, uhci_desc_t *element)
+{
+	uhci_desc_t  *prev;
+	unsigned long flags;
+
+	spin_lock_irqsave (&s->qh_lock, flags);
+	
+	prev = list_entry (element->horizontal.prev, uhci_desc_t, horizontal);
+	prev->hw.qh.head = element->hw.qh.head;
+
+	dbg("unlink qh %p, pqh %p, nxqh %p, to %08x", element, prev, 
+	    list_entry (element->horizontal.next, uhci_desc_t, horizontal),le32_to_cpu(element->hw.qh.head) &~15);
+	
+	list_del(&element->horizontal);
+
+	mb ();
+	spin_unlock_irqrestore (&s->qh_lock, flags);
+	
+	return 0;
+}
+/*-------------------------------------------------------------------*/
+_static int delete_qh (uhci_t *s, uhci_desc_t *qh)
+{
+	uhci_desc_t *td;
+	struct list_head *p;
+	
+	list_del (&qh->horizontal);
+
+	while ((p = qh->vertical.next) != &qh->vertical) {
+		td = list_entry (p, uhci_desc_t, vertical);
+		dbg("unlink td @ %p",td);
+		unlink_td (s, td, 0); // no physical unlink
+		delete_desc (s, td);
+	}
+
+	delete_desc (s, qh);
+	
+	return 0;
+}
+/*-------------------------------------------------------------------*/
+_static void clean_td_chain (uhci_t *s, uhci_desc_t *td)
+{
+	struct list_head *p;
+	uhci_desc_t *td1;
+
+	if (!td)
+		return;
+	
+	while ((p = td->horizontal.next) != &td->horizontal) {
+		td1 = list_entry (p, uhci_desc_t, horizontal);
+		delete_desc (s, td1);
+	}
+	
+	delete_desc (s, td);
+}
+
+/*-------------------------------------------------------------------*/
+_static void fill_td (uhci_desc_t *td, int status, int info, __u32 buffer)
+{
+	td->hw.td.status = cpu_to_le32(status);
+	td->hw.td.info = cpu_to_le32(info);
+	td->hw.td.buffer = cpu_to_le32(buffer);
+}
+/*-------------------------------------------------------------------*/
+// Removes ALL qhs in chain (paranoia!)
+_static void cleanup_skel (uhci_t *s)
+{
+	unsigned int n;
+	uhci_desc_t *td;
+
+	dbg("cleanup_skel");
+
+	clean_descs(s,1);
+
+	
+	if (s->td32ms) {
+	
+		unlink_td(s,s->td32ms,1);
+		delete_desc(s, s->td32ms);
+	}
+
+	for (n = 0; n < 8; n++) {
+		td = s->int_chain[n];
+		clean_td_chain (s, td);
+	}
+
+	if (s->iso_td) {
+		for (n = 0; n < 1024; n++) {
+			td = s->iso_td[n];
+			clean_td_chain (s, td);
+		}
+		kfree (s->iso_td);
+	}
+
+	if (s->framelist)
+		pci_free_consistent(s->uhci_pci, PAGE_SIZE,
+				    s->framelist, s->framelist_dma);
+
+	if (s->control_chain) {
+		// completed init_skel?
+		struct list_head *p;
+		uhci_desc_t *qh, *qh1;
+
+		qh = s->control_chain;
+		while ((p = qh->horizontal.next) != &qh->horizontal) {
+			qh1 = list_entry (p, uhci_desc_t, horizontal);
+			delete_qh (s, qh1);
+		}
+
+		delete_qh (s, qh);
+	}
+	else {
+		if (s->ls_control_chain)
+			delete_desc (s, s->ls_control_chain);
+		if (s->control_chain)
+			delete_desc (s, s->control_chain);
+		if (s->bulk_chain)
+			delete_desc (s, s->bulk_chain);
+		if (s->chain_end)
+			delete_desc (s, s->chain_end);
+	}
+
+	if (s->desc_pool) {
+		pci_pool_destroy(s->desc_pool);
+		s->desc_pool = NULL;
+	}
+
+	dbg("cleanup_skel finished");	
+}
+/*-------------------------------------------------------------------*/
+// allocates framelist and qh-skeletons
+// only HW-links provide continous linking, SW-links stay in their domain (ISO/INT)
+_static int init_skel (uhci_t *s)
+{
+	int n, ret;
+	uhci_desc_t *qh, *td;
+	
+	dbg("init_skel");
+	
+	s->framelist = pci_alloc_consistent(s->uhci_pci, PAGE_SIZE,
+					    &s->framelist_dma);
+
+	if (!s->framelist)
+		return -ENOMEM;
+
+	memset (s->framelist, 0, 4096);
+
+	dbg("creating descriptor pci_pool");
+
+	s->desc_pool = pci_pool_create("uhci_desc", s->uhci_pci,
+				       sizeof(uhci_desc_t), 16, 0,
+				       GFP_DMA | GFP_ATOMIC);	
+	if (!s->desc_pool)
+		goto init_skel_cleanup;
+
+	dbg("allocating iso desc pointer list");
+	s->iso_td = (uhci_desc_t **) kmalloc (1024 * sizeof (uhci_desc_t*), GFP_KERNEL);
+	
+	if (!s->iso_td)
+		goto init_skel_cleanup;
+
+	s->ls_control_chain = NULL;
+	s->control_chain = NULL;
+	s->bulk_chain = NULL;
+	s->chain_end = NULL;
+
+	dbg("allocating iso descs");
+	for (n = 0; n < 1024; n++) {
+	 	// allocate skeleton iso/irq-tds
+		if (alloc_td (s, &td, 0))
+			goto init_skel_cleanup;
+
+		s->iso_td[n] = td;
+		s->framelist[n] = cpu_to_le32((__u32) td->dma_addr);
+	}
+
+	dbg("allocating qh: chain_end");
+	if (alloc_qh (s, &qh))	
+		goto init_skel_cleanup;
+				
+	s->chain_end = qh;
+
+	if (alloc_td (s, &td, 0))
+		goto init_skel_cleanup;
+	
+	fill_td (td, 0 * TD_CTRL_IOC, 0, 0); // generate 1ms interrupt (enabled on demand)
+	insert_td (s, qh, td, 0);
+	qh->hw.qh.element &= cpu_to_le32(~UHCI_PTR_TERM); // remove TERM bit
+	s->td1ms=td;
+
+	dbg("allocating qh: bulk_chain");
+	if (alloc_qh (s, &qh))
+		goto init_skel_cleanup;
+	
+	insert_qh (s, s->chain_end, qh, 0);
+	s->bulk_chain = qh;
+
+	dbg("allocating qh: control_chain");
+	ret = alloc_qh (s, &qh);
+	if (ret)
+		goto init_skel_cleanup;
+	
+	insert_qh (s, s->bulk_chain, qh, 0);
+	s->control_chain = qh;
+
+#ifdef	CONFIG_USB_UHCI_HIGH_BANDWIDTH
+	// disabled reclamation loop
+	set_qh_head(s->chain_end, s->control_chain->dma_addr | UHCI_PTR_QH | UHCI_PTR_TERM);
+#endif
+
+	dbg("allocating qh: ls_control_chain");
+	if (alloc_qh (s, &qh))
+		goto init_skel_cleanup;
+	
+	insert_qh (s, s->control_chain, qh, 0);
+	s->ls_control_chain = qh;
+
+	for (n = 0; n < 8; n++)
+		s->int_chain[n] = 0;
+
+	dbg("allocating skeleton INT-TDs");
+	
+	for (n = 0; n < 8; n++) {
+		uhci_desc_t *td;
+
+		if (alloc_td (s, &td, 0))
+			goto init_skel_cleanup;
+
+		s->int_chain[n] = td;
+		if (n == 0) {
+			set_td_link(s->int_chain[0], s->ls_control_chain->dma_addr | UHCI_PTR_QH);
+		}
+		else {
+			set_td_link(s->int_chain[n], s->int_chain[0]->dma_addr);
+		}
+	}
+
+	dbg("Linking skeleton INT-TDs");
+	
+	for (n = 0; n < 1024; n++) {
+		// link all iso-tds to the interrupt chains
+		int m, o;
+		dbg("framelist[%i]=%x",n,le32_to_cpu(s->framelist[n]));
+		if ((n&127)==127) 
+			((uhci_desc_t*) s->iso_td[n])->hw.td.link = cpu_to_le32(s->int_chain[0]->dma_addr);
+		else 
+			for (o = 1, m = 2; m <= 128; o++, m += m)
+				if ((n & (m - 1)) == ((m - 1) / 2))
+					set_td_link(((uhci_desc_t*) s->iso_td[n]), s->int_chain[o]->dma_addr);
+	}
+
+	if (alloc_td (s, &td, 0))
+		goto init_skel_cleanup;
+	
+	fill_td (td, 0 * TD_CTRL_IOC, 0, 0); // generate 32ms interrupt (activated later)
+	s->td32ms=td;
+
+	insert_td_horizontal (s, s->int_chain[5], td);
+
+	mb();
+	//uhci_show_queue(s->control_chain);   
+	dbg("init_skel exit");
+	return 0;
+
+      init_skel_cleanup:
+	cleanup_skel (s);
+	return -ENOMEM;
+}
+
+/*-------------------------------------------------------------------*/
+//                         LOW LEVEL STUFF
+//          assembles QHs und TDs for control, bulk and iso
+/*-------------------------------------------------------------------*/
+_static int uhci_submit_control_urb (struct urb *urb)
+{
+	uhci_desc_t *qh, *td;
+	uhci_t *s = (uhci_t*) urb->dev->bus->hcpriv;
+	urb_priv_t *urb_priv = urb->hcpriv;
+	unsigned long destination, status;
+	int maxsze = usb_maxpacket (urb->dev, urb->pipe, usb_pipeout (urb->pipe));
+	unsigned long len;
+	char *data;
+	int depth_first=USE_CTRL_DEPTH_FIRST;  // UHCI descriptor chasing method
+
+	dbg("uhci_submit_control start");
+	if (alloc_qh (s, &qh))		// alloc qh for this request
+		return -ENOMEM;
+
+	if (alloc_td (s, &td, UHCI_PTR_DEPTH * depth_first))		// get td for setup stage
+	{
+		delete_qh (s, qh);
+		return -ENOMEM;
+	}
+
+	/* The "pipe" thing contains the destination in bits 8--18 */
+	destination = (urb->pipe & PIPE_DEVEP_MASK) | USB_PID_SETUP;
+
+	/* 3 errors */
+	status = (urb->pipe & TD_CTRL_LS) | TD_CTRL_ACTIVE |
+		(urb->transfer_flags & USB_DISABLE_SPD ? 0 : TD_CTRL_SPD) | (3 << 27);
+
+	/*  Build the TD for the control request, try forever, 8 bytes of data */
+	fill_td (td, status, destination | (7 << 21), urb_priv->setup_packet_dma);
+
+	insert_td (s, qh, td, 0);	// queue 'setup stage'-td in qh
+#if 0
+	{
+		char *sp=urb->setup_packet;
+		dbg("SETUP to pipe %x: %x %x %x %x %x %x %x %x", urb->pipe,
+		    sp[0],sp[1],sp[2],sp[3],sp[4],sp[5],sp[6],sp[7]);
+	}
+	//uhci_show_td(td);
+#endif
+
+	len = urb->transfer_buffer_length;
+	data = urb->transfer_buffer;
+
+	/* If direction is "send", change the frame from SETUP (0x2D)
+	   to OUT (0xE1). Else change it from SETUP to IN (0x69). */
+
+	destination = (urb->pipe & PIPE_DEVEP_MASK) | (usb_pipeout (urb->pipe)?USB_PID_OUT:USB_PID_IN);
+
+	while (len > 0) {
+		int pktsze = len;
+
+		if (alloc_td (s, &td, UHCI_PTR_DEPTH * depth_first))
+			goto fail_unmap_enomem;
+
+		if (pktsze > maxsze)
+			pktsze = maxsze;
+
+		destination ^= 1 << TD_TOKEN_TOGGLE;	// toggle DATA0/1
+
+		// Status, pktsze bytes of data
+		fill_td (td, status, destination | ((pktsze - 1) << 21),
+			 urb_priv->transfer_buffer_dma + (data - (char *)urb->transfer_buffer));
+
+		insert_td (s, qh, td, UHCI_PTR_DEPTH * depth_first);	// queue 'data stage'-td in qh
+
+		data += pktsze;
+		len -= pktsze;
+	}
+
+	/* Build the final TD for control status */
+	/* It's only IN if the pipe is out AND we aren't expecting data */
+
+	destination &= ~UHCI_PID;
+
+	if (usb_pipeout (urb->pipe) || (urb->transfer_buffer_length == 0))
+		destination |= USB_PID_IN;
+	else
+		destination |= USB_PID_OUT;
+
+	destination |= 1 << TD_TOKEN_TOGGLE;	/* End in Data1 */
+
+	if (alloc_td (s, &td, UHCI_PTR_DEPTH))
+		goto fail_unmap_enomem;
+
+	status &=~TD_CTRL_SPD;
+
+	/* no limit on errors on final packet , 0 bytes of data */
+	fill_td (td, status | TD_CTRL_IOC, destination | (UHCI_NULL_DATA_SIZE << 21),
+		 0);
+
+	insert_td (s, qh, td, UHCI_PTR_DEPTH * depth_first);	// queue status td
+
+	list_add (&qh->desc_list, &urb_priv->desc_list);
+
+	queue_urb (s, urb);	// queue before inserting in desc chain
+
+	qh->hw.qh.element &= cpu_to_le32(~UHCI_PTR_TERM);
+
+	//uhci_show_queue(qh);
+	/* Start it up... put low speed first */
+	if (urb->pipe & TD_CTRL_LS)
+		insert_qh (s, s->control_chain, qh, 0);
+	else
+		insert_qh (s, s->bulk_chain, qh, 0);
+
+	dbg("uhci_submit_control end");
+	return 0;
+
+fail_unmap_enomem:
+	delete_qh(s, qh);
+	return -ENOMEM;
+}
+/*-------------------------------------------------------------------*/
+// For queued bulk transfers, two additional QH helpers are allocated (nqh, bqh)
+// Due to the linking with other bulk urbs, it has to be locked with urb_list_lock!
+
+_static int uhci_submit_bulk_urb (struct urb *urb, struct urb *bulk_urb)
+{
+	uhci_t *s = (uhci_t*) urb->dev->bus->hcpriv;
+	urb_priv_t *urb_priv = urb->hcpriv, *upriv, *bpriv=NULL;
+	uhci_desc_t *qh, *td, *nqh=NULL, *bqh=NULL, *first_td=NULL;
+	unsigned long destination, status;
+	char *data;
+	unsigned int pipe = urb->pipe;
+	int maxsze = usb_maxpacket (urb->dev, pipe, usb_pipeout (pipe));
+	int info, len, last;
+	int depth_first=USE_BULK_DEPTH_FIRST;  // UHCI descriptor chasing method
+
+	if (usb_endpoint_halted (urb->dev, usb_pipeendpoint (pipe), usb_pipeout (pipe)))
+		return -EPIPE;
+
+	queue_dbg("uhci_submit_bulk_urb: urb %p, old %p, pipe %08x, len %i",
+		  urb,bulk_urb,urb->pipe,urb->transfer_buffer_length);
+
+	upriv = (urb_priv_t*)urb->hcpriv;
+
+	if (!bulk_urb) {
+		if (alloc_qh (s, &qh))		// get qh for this request
+			return -ENOMEM;
+
+		if (urb->transfer_flags & USB_QUEUE_BULK) {
+			if (alloc_qh(s, &nqh)) // placeholder for clean unlink
+			{
+				delete_desc (s, qh);
+				return -ENOMEM;
+			}
+			upriv->next_qh = nqh;
+			queue_dbg("new next qh %p",nqh);
+		}
+	}
+	else { 
+		bpriv = (urb_priv_t*)bulk_urb->hcpriv;
+		qh = bpriv->bottom_qh;  // re-use bottom qh and next qh
+		nqh = bpriv->next_qh;
+		upriv->next_qh=nqh;	
+		upriv->prev_queued_urb=bulk_urb;
+	}
+
+	if (urb->transfer_flags & USB_QUEUE_BULK) {
+		if (alloc_qh (s, &bqh))  // "bottom" QH
+		{
+			if (!bulk_urb) { 
+				delete_desc(s, qh);
+				delete_desc(s, nqh);
+			}
+			return -ENOMEM;
+		}
+		set_qh_element(bqh, UHCI_PTR_TERM);
+		set_qh_head(bqh, nqh->dma_addr | UHCI_PTR_QH); // element
+		upriv->bottom_qh = bqh;
+	}
+	queue_dbg("uhci_submit_bulk: qh %p bqh %p nqh %p",qh, bqh, nqh);
+
+	/* The "pipe" thing contains the destination in bits 8--18. */
+	destination = (pipe & PIPE_DEVEP_MASK) | usb_packetid (pipe);
+
+	/* 3 errors */
+	status = (pipe & TD_CTRL_LS) | TD_CTRL_ACTIVE |
+		((urb->transfer_flags & USB_DISABLE_SPD) ? 0 : TD_CTRL_SPD) | (3 << 27);
+
+	/* Build the TDs for the bulk request */
+	len = urb->transfer_buffer_length;
+	data = urb->transfer_buffer;
+	
+	do {					// TBD: Really allow zero-length packets?
+		int pktsze = len;
+
+		if (alloc_td (s, &td, UHCI_PTR_DEPTH * depth_first))
+		{
+			delete_qh (s, qh);
+			return -ENOMEM;
+		}
+
+		if (pktsze > maxsze)
+			pktsze = maxsze;
+
+		// pktsze bytes of data 
+		info = destination | (((pktsze - 1)&UHCI_NULL_DATA_SIZE) << 21) |
+			(usb_gettoggle (urb->dev, usb_pipeendpoint (pipe), usb_pipeout (pipe)) << TD_TOKEN_TOGGLE);
+
+		fill_td (td, status, info,
+			 urb_priv->transfer_buffer_dma + (data - (char *)urb->transfer_buffer));
+
+		data += pktsze;
+		len -= pktsze;
+		// Use USB_ZERO_PACKET to finish bulk OUTs always with a zero length packet
+		last = (len == 0 && (usb_pipein(pipe) || pktsze < maxsze || !(urb->transfer_flags & USB_ZERO_PACKET)));
+
+		if (last)
+			set_td_ioc(td);	// last one generates INT
+
+		insert_td (s, qh, td, UHCI_PTR_DEPTH * depth_first);
+		if (!first_td)
+			first_td=td;
+		usb_dotoggle (urb->dev, usb_pipeendpoint (pipe), usb_pipeout (pipe));
+
+	} while (!last);
+
+	if (bulk_urb && bpriv)   // everything went OK, link with old bulk URB
+		bpriv->next_queued_urb=urb;
+
+	list_add (&qh->desc_list, &urb_priv->desc_list);
+
+	if (urb->transfer_flags & USB_QUEUE_BULK)
+		append_qh(s, td, bqh, UHCI_PTR_DEPTH * depth_first);
+
+	queue_urb_unlocked (s, urb);
+	
+	if (urb->transfer_flags & USB_QUEUE_BULK)
+		set_qh_element(qh, first_td->dma_addr);
+	else
+		qh->hw.qh.element &= cpu_to_le32(~UHCI_PTR_TERM);    // arm QH
+
+	if (!bulk_urb) { 					// new bulk queue	
+		if (urb->transfer_flags & USB_QUEUE_BULK) {
+			spin_lock (&s->td_lock);		// both QHs in one go
+			insert_qh (s, s->chain_end, qh, 0);	// Main QH
+			insert_qh (s, s->chain_end, nqh, 0);	// Helper QH
+			spin_unlock (&s->td_lock);
+		}
+		else
+			insert_qh (s, s->chain_end, qh, 0);
+	}
+	
+	//uhci_show_queue(s->bulk_chain);
+	//dbg("uhci_submit_bulk_urb: exit\n");
+	return 0;
+}
+/*-------------------------------------------------------------------*/
+_static void uhci_clean_iso_step1(uhci_t *s, urb_priv_t *urb_priv)
+{
+	struct list_head *p;
+	uhci_desc_t *td;
+
+	for (p = urb_priv->desc_list.next; p != &urb_priv->desc_list; p = p->next) {
+				td = list_entry (p, uhci_desc_t, desc_list);
+				unlink_td (s, td, 1);
+	}
+}
+/*-------------------------------------------------------------------*/
+_static void uhci_clean_iso_step2(uhci_t *s, urb_priv_t *urb_priv)
+{
+	struct list_head *p;
+	uhci_desc_t *td;
+
+	while ((p = urb_priv->desc_list.next) != &urb_priv->desc_list) {
+				td = list_entry (p, uhci_desc_t, desc_list);
+				list_del (p);
+				delete_desc (s, td);
+	}
+}
+/*-------------------------------------------------------------------*/
+/* mode: CLEAN_TRANSFER_NO_DELETION: unlink but no deletion mark (step 1 of async_unlink)
+         CLEAN_TRANSFER_REGULAR: regular (unlink/delete-mark)
+         CLEAN_TRANSFER_DELETION_MARK: deletion mark for QH (step 2 of async_unlink)
+ looks a bit complicated because of all the bulk queueing goodies
+*/
+
+_static void uhci_clean_transfer (uhci_t *s, struct urb *urb, uhci_desc_t *qh, int mode)
+{
+	uhci_desc_t *bqh, *nqh, *prevqh, *prevtd;
+	int now;
+	urb_priv_t *priv=(urb_priv_t*)urb->hcpriv;
+
+	now=UHCI_GET_CURRENT_FRAME(s);
+
+	bqh=priv->bottom_qh;	
+	
+	if (!priv->next_queued_urb)  { // no more appended bulk queues
+
+		queue_dbg("uhci_clean_transfer: No more bulks for urb %p, qh %p, bqh %p, nqh %p", urb, qh, bqh, priv->next_qh);	
+	
+		if (priv->prev_queued_urb && mode != CLEAN_TRANSFER_DELETION_MARK) {  // qh not top of the queue
+				unsigned long flags; 
+				urb_priv_t* ppriv=(urb_priv_t*)priv->prev_queued_urb->hcpriv;
+
+				spin_lock_irqsave (&s->qh_lock, flags);
+				prevqh = list_entry (ppriv->desc_list.next, uhci_desc_t, desc_list);
+				prevtd = list_entry (prevqh->vertical.prev, uhci_desc_t, vertical);
+				set_td_link(prevtd, priv->bottom_qh->dma_addr | UHCI_PTR_QH); // skip current qh
+				mb();
+				queue_dbg("uhci_clean_transfer: relink pqh %p, ptd %p",prevqh, prevtd);
+				spin_unlock_irqrestore (&s->qh_lock, flags);
+
+				ppriv->bottom_qh = priv->bottom_qh;
+				ppriv->next_queued_urb = NULL;
+			}
+		else {   // queue is dead, qh is top of the queue
+			
+			if (mode != CLEAN_TRANSFER_DELETION_MARK) 				
+				unlink_qh(s, qh); // remove qh from horizontal chain
+
+			if (bqh) {  // remove remainings of bulk queue
+				nqh=priv->next_qh;
+
+				if (mode != CLEAN_TRANSFER_DELETION_MARK) 
+					unlink_qh(s, nqh);  // remove nqh from horizontal chain
+				
+				if (mode != CLEAN_TRANSFER_NO_DELETION) {  // add helper QHs to free desc list
+					nqh->last_used = bqh->last_used = now;
+					list_add_tail (&nqh->horizontal, &s->free_desc);
+					list_add_tail (&bqh->horizontal, &s->free_desc);
+				}			
+			}
+		}
+	}
+	else { // there are queued urbs following
+	
+	  queue_dbg("uhci_clean_transfer: urb %p, prevurb %p, nexturb %p, qh %p, bqh %p, nqh %p",
+		       urb, priv->prev_queued_urb,  priv->next_queued_urb, qh, bqh, priv->next_qh);	
+       	
+		if (mode != CLEAN_TRANSFER_DELETION_MARK) {	// no work for cleanup at unlink-completion
+			struct urb *nurb;
+			unsigned long flags;
+
+			nurb = priv->next_queued_urb;
+			spin_lock_irqsave (&s->qh_lock, flags);		
+
+			if (!priv->prev_queued_urb) { // top QH
+				
+				prevqh = list_entry (qh->horizontal.prev, uhci_desc_t, horizontal);
+				set_qh_head(prevqh, bqh->dma_addr | UHCI_PTR_QH);
+				list_del (&qh->horizontal);  // remove this qh form horizontal chain
+				list_add (&bqh->horizontal, &prevqh->horizontal); // insert next bqh in horizontal chain
+			}
+			else {		// intermediate QH
+				urb_priv_t* ppriv=(urb_priv_t*)priv->prev_queued_urb->hcpriv;
+				urb_priv_t* npriv=(urb_priv_t*)nurb->hcpriv;
+				uhci_desc_t * bnqh;
+				
+				bnqh = list_entry (npriv->desc_list.next, uhci_desc_t, desc_list);
+				ppriv->bottom_qh = bnqh;
+				ppriv->next_queued_urb = nurb;				
+				prevqh = list_entry (ppriv->desc_list.next, uhci_desc_t, desc_list);
+				set_qh_head(prevqh, bqh->dma_addr | UHCI_PTR_QH);
+			}
+
+			mb();
+			((urb_priv_t*)nurb->hcpriv)->prev_queued_urb=priv->prev_queued_urb;
+			spin_unlock_irqrestore (&s->qh_lock, flags);
+		}		
+	}
+
+	if (mode != CLEAN_TRANSFER_NO_DELETION) {
+		qh->last_used = now;	
+		list_add_tail (&qh->horizontal, &s->free_desc); // mark qh for later deletion/kfree
+	}
+}
+/*-------------------------------------------------------------------*/
+// Release bandwidth for Interrupt or Isoc. transfers 
+_static void uhci_release_bandwidth(struct urb *urb)
+{       
+	if (urb->bandwidth) {
+		switch (usb_pipetype(urb->pipe)) {
+		case PIPE_INTERRUPT:
+			usb_release_bandwidth (urb->dev, urb, 0);
+			break;
+		case PIPE_ISOCHRONOUS:
+			usb_release_bandwidth (urb->dev, urb, 1);
+			break;
+		default:
+			break;
+		}
+	}	
+}
+
+_static void uhci_urb_dma_sync(uhci_t *s, struct urb *urb, urb_priv_t *urb_priv)
+{
+	if (urb_priv->setup_packet_dma)
+		pci_dma_sync_single(s->uhci_pci, urb_priv->setup_packet_dma,
+				    sizeof(struct usb_ctrlrequest), PCI_DMA_TODEVICE);
+
+	if (urb_priv->transfer_buffer_dma)
+		pci_dma_sync_single(s->uhci_pci, urb_priv->transfer_buffer_dma,
+				    urb->transfer_buffer_length,
+				    usb_pipein(urb->pipe) ?
+				    PCI_DMA_FROMDEVICE :
+				    PCI_DMA_TODEVICE);
+}
+
+_static void uhci_urb_dma_unmap(uhci_t *s, struct urb *urb, urb_priv_t *urb_priv)
+{
+	if (urb_priv->setup_packet_dma) {
+		pci_unmap_single(s->uhci_pci, urb_priv->setup_packet_dma,
+				 sizeof(struct usb_ctrlrequest), PCI_DMA_TODEVICE);
+		urb_priv->setup_packet_dma = 0;
+	}
+	if (urb_priv->transfer_buffer_dma) {
+		pci_unmap_single(s->uhci_pci, urb_priv->transfer_buffer_dma,
+				 urb->transfer_buffer_length,
+				 usb_pipein(urb->pipe) ?
+				 PCI_DMA_FROMDEVICE :
+				 PCI_DMA_TODEVICE);
+		urb_priv->transfer_buffer_dma = 0;
+	}
+}
+/*-------------------------------------------------------------------*/
+/* needs urb_list_lock!
+   mode: UNLINK_ASYNC_STORE_URB: unlink and move URB into unlinked list
+         UNLINK_ASYNC_DONT_STORE: unlink, don't move URB into unlinked list
+*/
+_static int uhci_unlink_urb_async (uhci_t *s,struct urb *urb, int mode)
+{
+	uhci_desc_t *qh;
+	urb_priv_t *urb_priv;
+	
+	async_dbg("unlink_urb_async called %p",urb);
+
+	if ((urb->status == -EINPROGRESS) ||
+	    ((usb_pipetype (urb->pipe) ==  PIPE_INTERRUPT) && ((urb_priv_t*)urb->hcpriv)->flags))
+	{
+		((urb_priv_t*)urb->hcpriv)->started = ~0;  // mark
+		dequeue_urb (s, urb);
+
+		if (mode==UNLINK_ASYNC_STORE_URB)
+			list_add_tail (&urb->urb_list, &s->urb_unlinked); // store urb
+
+		uhci_switch_timer_int(s);
+       		s->unlink_urb_done = 1;
+		uhci_release_bandwidth(urb);
+
+		urb->status = -ECONNABORTED;	// mark urb as "waiting to be killed"	
+		urb_priv = (urb_priv_t*)urb->hcpriv;
+
+		switch (usb_pipetype (urb->pipe)) {
+		case PIPE_INTERRUPT:
+			usb_dotoggle (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe));
+
+		case PIPE_ISOCHRONOUS:
+			uhci_clean_iso_step1 (s, urb_priv);
+			break;
+
+		case PIPE_BULK:
+		case PIPE_CONTROL:
+			qh = list_entry (urb_priv->desc_list.next, uhci_desc_t, desc_list);
+			uhci_clean_transfer (s, urb, qh, CLEAN_TRANSFER_NO_DELETION);
+			break;
+		}
+		((urb_priv_t*)urb->hcpriv)->started = UHCI_GET_CURRENT_FRAME(s);
+		return -EINPROGRESS;  // completion will follow
+	}		
+
+	return 0;    // URB already dead
+}
+/*-------------------------------------------------------------------*/
+// kills an urb by unlinking descriptors and waiting for at least one frame
+_static int uhci_unlink_urb_sync (uhci_t *s, struct urb *urb)
+{
+	uhci_desc_t *qh;
+	urb_priv_t *urb_priv;
+	unsigned long flags=0;
+	struct usb_device *usb_dev;
+
+	spin_lock_irqsave (&s->urb_list_lock, flags);
+
+	if (urb->status == -EINPROGRESS) {
+
+		// move descriptors out of the running chains, dequeue urb
+		uhci_unlink_urb_async(s, urb, UNLINK_ASYNC_DONT_STORE);
+
+		urb_priv = urb->hcpriv;
+		urb->status = -ENOENT;	// prevent from double deletion after unlock		
+		spin_unlock_irqrestore (&s->urb_list_lock, flags);
+		
+		// cleanup the rest
+		switch (usb_pipetype (urb->pipe)) {
+
+		case PIPE_INTERRUPT:
+		case PIPE_ISOCHRONOUS:
+			uhci_wait_ms(1);
+			uhci_clean_iso_step2(s, urb_priv);
+			break;
+
+		case PIPE_BULK:
+		case PIPE_CONTROL:
+			qh = list_entry (urb_priv->desc_list.next, uhci_desc_t, desc_list);
+			uhci_clean_transfer(s, urb, qh, CLEAN_TRANSFER_DELETION_MARK);
+			uhci_wait_ms(1);
+		}
+		urb->status = -ENOENT;	// mark urb as killed		
+					
+		uhci_urb_dma_unmap(s, urb, urb->hcpriv);
+
+#ifdef DEBUG_SLAB
+		kmem_cache_free (urb_priv_kmem, urb->hcpriv);
+#else
+		kfree (urb->hcpriv);
+#endif
+		usb_dev = urb->dev;
+		if (urb->complete) {
+			dbg("unlink_urb: calling completion");
+			urb->dev = NULL;
+			urb->complete ((struct urb *) urb);
+		}
+		usb_dec_dev_use (usb_dev);
+	}
+	else
+		spin_unlock_irqrestore (&s->urb_list_lock, flags);
+
+	return 0;
+}
+/*-------------------------------------------------------------------*/
+// async unlink_urb completion/cleanup work
+// has to be protected by urb_list_lock!
+// features: if set in transfer_flags, the resulting status of the killed
+// transaction is not overwritten
+
+_static void uhci_cleanup_unlink(uhci_t *s, int force)
+{
+	struct list_head *q;
+	struct urb *urb;
+	struct usb_device *dev;
+	int now, type;
+	urb_priv_t *urb_priv;
+
+	q=s->urb_unlinked.next;
+	now=UHCI_GET_CURRENT_FRAME(s);
+
+	while (q != &s->urb_unlinked) {
+
+		urb = list_entry (q, struct urb, urb_list);
+
+		urb_priv = (urb_priv_t*)urb->hcpriv;
+		q = urb->urb_list.next;
+		
+		if (!urb_priv) // avoid crash when URB is corrupted
+			break;
+			
+		if (force || ((urb_priv->started != ~0) && (urb_priv->started != now))) {
+			async_dbg("async cleanup %p",urb);
+			type=usb_pipetype (urb->pipe);
+
+			switch (type) { // process descriptors
+			case PIPE_CONTROL:
+				process_transfer (s, urb, CLEAN_TRANSFER_DELETION_MARK);  // don't unlink (already done)
+				break;
+			case PIPE_BULK:
+				if (!s->avoid_bulk.counter)
+					process_transfer (s, urb, CLEAN_TRANSFER_DELETION_MARK); // don't unlink (already done)
+				else
+					continue;
+				break;
+			case PIPE_ISOCHRONOUS:
+				process_iso (s, urb, PROCESS_ISO_FORCE); // force, don't unlink
+				break;
+			case PIPE_INTERRUPT:
+				process_interrupt (s, urb);
+				break;
+			}
+
+			if (!(urb->transfer_flags & USB_TIMEOUT_KILLED))
+		  		urb->status = -ECONNRESET; // mark as asynchronously killed
+
+			dev = urb->dev;	// completion may destroy all...
+			urb_priv = urb->hcpriv;
+			list_del (&urb->urb_list);
+			
+			uhci_urb_dma_sync(s, urb, urb_priv);
+			if (urb->complete) {
+				spin_unlock(&s->urb_list_lock);
+				urb->dev = NULL;
+				urb->complete ((struct urb *) urb);
+				spin_lock(&s->urb_list_lock);
+			}
+
+			if (!(urb->transfer_flags & USB_TIMEOUT_KILLED))
+				urb->status = -ENOENT;  // now the urb is really dead
+
+			switch (type) {
+			case PIPE_ISOCHRONOUS:
+			case PIPE_INTERRUPT:
+				uhci_clean_iso_step2(s, urb_priv);
+				break;
+			}
+	
+			uhci_urb_dma_unmap(s, urb, urb_priv);
+
+			usb_dec_dev_use (dev);
+#ifdef DEBUG_SLAB
+			kmem_cache_free (urb_priv_kmem, urb_priv);
+#else
+			kfree (urb_priv);
+#endif
+
+		}
+	}
+}
+ 
+/*-------------------------------------------------------------------*/
+_static int uhci_unlink_urb (struct urb *urb)
+{
+	uhci_t *s;
+	unsigned long flags=0;
+	dbg("uhci_unlink_urb called for %p",urb);
+	if (!urb || !urb->dev)		// you never know...
+		return -EINVAL;
+	
+	s = (uhci_t*) urb->dev->bus->hcpriv;
+
+	if (usb_pipedevice (urb->pipe) == s->rh.devnum)
+		return rh_unlink_urb (urb);
+
+	if (!urb->hcpriv)
+		return -EINVAL;
+
+	if (urb->transfer_flags & USB_ASYNC_UNLINK) {
+		int ret;
+       		spin_lock_irqsave (&s->urb_list_lock, flags);
+       		
+		uhci_release_bandwidth(urb);
+		ret = uhci_unlink_urb_async(s, urb, UNLINK_ASYNC_STORE_URB);
+
+		spin_unlock_irqrestore (&s->urb_list_lock, flags);	
+		return ret;
+	}
+	else
+		return uhci_unlink_urb_sync(s, urb);
+}
+/*-------------------------------------------------------------------*/
+// In case of ASAP iso transfer, search the URB-list for already queued URBs
+// for this EP and calculate the earliest start frame for the new
+// URB (easy seamless URB continuation!)
+_static int find_iso_limits (struct urb *urb, unsigned int *start, unsigned int *end)
+{
+	struct urb *u, *last_urb = NULL;
+	uhci_t *s = (uhci_t*) urb->dev->bus->hcpriv;
+	struct list_head *p;
+	int ret=-1;
+	unsigned long flags;
+	
+	spin_lock_irqsave (&s->urb_list_lock, flags);
+	p=s->urb_list.prev;
+
+	for (; p != &s->urb_list; p = p->prev) {
+		u = list_entry (p, struct urb, urb_list);
+		// look for pending URBs with identical pipe handle
+		// works only because iso doesn't toggle the data bit!
+		if ((urb->pipe == u->pipe) && (urb->dev == u->dev) && (u->status == -EINPROGRESS)) {
+			if (!last_urb)
+				*start = u->start_frame;
+			last_urb = u;
+		}
+	}
+	
+	if (last_urb) {
+		*end = (last_urb->start_frame + last_urb->number_of_packets) & 1023;
+		ret=0;
+	}
+	
+	spin_unlock_irqrestore(&s->urb_list_lock, flags);
+	
+	return ret;
+}
+/*-------------------------------------------------------------------*/
+// adjust start_frame according to scheduling constraints (ASAP etc)
+
+_static int iso_find_start (struct urb *urb)
+{
+	uhci_t *s = (uhci_t*) urb->dev->bus->hcpriv;
+	unsigned int now;
+	unsigned int start_limit = 0, stop_limit = 0, queued_size;
+	int limits;
+
+	now = UHCI_GET_CURRENT_FRAME (s) & 1023;
+
+	if ((unsigned) urb->number_of_packets > 900)
+		return -EFBIG;
+	
+	limits = find_iso_limits (urb, &start_limit, &stop_limit);
+	queued_size = (stop_limit - start_limit) & 1023;
+
+	if (urb->transfer_flags & USB_ISO_ASAP) {
+		// first iso
+		if (limits) {
+			// 10ms setup should be enough //FIXME!
+			urb->start_frame = (now + 10) & 1023;
+		}
+		else {
+			urb->start_frame = stop_limit;		//seamless linkage
+
+			if (((now - urb->start_frame) & 1023) <= (unsigned) urb->number_of_packets) {
+				info("iso_find_start: gap in seamless isochronous scheduling");
+				dbg("iso_find_start: now %u start_frame %u number_of_packets %u pipe 0x%08x",
+					now, urb->start_frame, urb->number_of_packets, urb->pipe);
+				urb->start_frame = (now + 5) & 1023;	// 5ms setup should be enough //FIXME!
+			}
+		}
+	}
+	else {
+		urb->start_frame &= 1023;
+		if (((now - urb->start_frame) & 1023) < (unsigned) urb->number_of_packets) {
+			dbg("iso_find_start: now between start_frame and end");
+			return -EAGAIN;
+		}
+	}
+
+	/* check if either start_frame or start_frame+number_of_packets-1 lies between start_limit and stop_limit */
+	if (limits)
+		return 0;
+
+	if (((urb->start_frame - start_limit) & 1023) < queued_size ||
+	    ((urb->start_frame + urb->number_of_packets - 1 - start_limit) & 1023) < queued_size) {
+		dbg("iso_find_start: start_frame %u number_of_packets %u start_limit %u stop_limit %u",
+			urb->start_frame, urb->number_of_packets, start_limit, stop_limit);
+		return -EAGAIN;
+	}
+
+	return 0;
+}
+/*-------------------------------------------------------------------*/
+// submits USB interrupt (ie. polling ;-) 
+// ASAP-flag set implicitely
+// if period==0, the transfer is only done once
+
+_static int uhci_submit_int_urb (struct urb *urb)
+{
+	uhci_t *s = (uhci_t*) urb->dev->bus->hcpriv;
+	urb_priv_t *urb_priv = urb->hcpriv;
+	int nint, n;
+	uhci_desc_t *td;
+	int status, destination;
+	int info;
+	unsigned int pipe = urb->pipe;
+
+	if (urb->interval < 0 || urb->interval >= 256)
+		return -EINVAL;
+
+	if (urb->interval == 0)
+		nint = 0;
+	else {
+		for (nint = 0, n = 1; nint <= 8; nint++, n += n)	// round interval down to 2^n
+		 {
+			if (urb->interval < n) {
+				urb->interval = n / 2;
+				break;
+			}
+		}
+		nint--;
+	}
+
+	dbg("Rounded interval to %i, chain  %i", urb->interval, nint);
+
+	urb->start_frame = UHCI_GET_CURRENT_FRAME (s) & 1023;	// remember start frame, just in case...
+
+	urb->number_of_packets = 1;
+
+	// INT allows only one packet
+	if (urb->transfer_buffer_length > usb_maxpacket (urb->dev, pipe, usb_pipeout (pipe)))
+		return -EINVAL;
+
+	if (alloc_td (s, &td, UHCI_PTR_DEPTH))
+		return -ENOMEM;
+
+	status = (pipe & TD_CTRL_LS) | TD_CTRL_ACTIVE | TD_CTRL_IOC |
+		(urb->transfer_flags & USB_DISABLE_SPD ? 0 : TD_CTRL_SPD) | (3 << 27);
+
+	destination = (urb->pipe & PIPE_DEVEP_MASK) | usb_packetid (urb->pipe) |
+		(((urb->transfer_buffer_length - 1) & 0x7ff) << 21);
+
+
+	info = destination | (usb_gettoggle (urb->dev, usb_pipeendpoint (pipe), usb_pipeout (pipe)) << TD_TOKEN_TOGGLE);
+
+	fill_td (td, status, info, urb_priv->transfer_buffer_dma);
+	list_add_tail (&td->desc_list, &urb_priv->desc_list);
+
+	queue_urb (s, urb);
+
+	insert_td_horizontal (s, s->int_chain[nint], td);	// store in INT-TDs
+
+	usb_dotoggle (urb->dev, usb_pipeendpoint (pipe), usb_pipeout (pipe));
+
+	return 0;
+}
+/*-------------------------------------------------------------------*/
+_static int uhci_submit_iso_urb (struct urb *urb)
+{
+	uhci_t *s = (uhci_t*) urb->dev->bus->hcpriv;
+	urb_priv_t *urb_priv = urb->hcpriv;
+#ifdef ISO_SANITY_CHECK
+	int pipe=urb->pipe;
+	int maxsze = usb_maxpacket (urb->dev, pipe, usb_pipeout (pipe));
+#endif
+	int n, ret, last=0;
+	uhci_desc_t *td, **tdm;
+	int status, destination;
+	unsigned long flags;
+
+	__save_flags(flags);
+	__cli();		      // Disable IRQs to schedule all ISO-TDs in time
+	ret = iso_find_start (urb);	// adjusts urb->start_frame for later use
+	
+	if (ret)
+		goto err;
+
+	tdm = (uhci_desc_t **) kmalloc (urb->number_of_packets * sizeof (uhci_desc_t*), KMALLOC_FLAG);
+
+	if (!tdm) {
+		ret = -ENOMEM;
+		goto err;
+	}
+
+	memset(tdm, 0, urb->number_of_packets * sizeof (uhci_desc_t*));
+
+	// First try to get all TDs. Cause: Removing already inserted TDs can only be done 
+	// racefree in three steps: unlink TDs, wait one frame, delete TDs. 
+	// So, this solutions seems simpler...
+
+	for (n = 0; n < urb->number_of_packets; n++) {
+		dbg("n:%d urb->iso_frame_desc[n].length:%d", n, urb->iso_frame_desc[n].length);
+		if (!urb->iso_frame_desc[n].length)
+			continue;  // allows ISO striping by setting length to zero in iso_descriptor
+
+
+#ifdef ISO_SANITY_CHECK
+		if(urb->iso_frame_desc[n].length > maxsze) {
+
+			err("submit_iso: urb->iso_frame_desc[%d].length(%d)>%d",n , urb->iso_frame_desc[n].length, maxsze);
+			ret=-EINVAL;		
+		}
+		else
+#endif
+		if (alloc_td (s, &td, UHCI_PTR_DEPTH)) {
+			int i;	// Cleanup allocated TDs
+
+			for (i = 0; i < n; n++)
+				if (tdm[i])
+					 delete_desc(s, tdm[i]);
+			kfree (tdm);
+			goto err;
+		}
+		last=n;
+		tdm[n] = td;
+	}
+
+	status = TD_CTRL_ACTIVE | TD_CTRL_IOS;
+
+	destination = (urb->pipe & PIPE_DEVEP_MASK) | usb_packetid (urb->pipe);
+
+	// Queue all allocated TDs
+	for (n = 0; n < urb->number_of_packets; n++) {
+		td = tdm[n];
+		if (!td)
+			continue;
+			
+		if (n  == last) {
+			status |= TD_CTRL_IOC;
+			queue_urb (s, urb);
+		}
+
+		fill_td (td, status, destination | (((urb->iso_frame_desc[n].length - 1) & 0x7ff) << 21),
+			 urb_priv->transfer_buffer_dma + urb->iso_frame_desc[n].offset);
+		list_add_tail (&td->desc_list, &urb_priv->desc_list);
+	
+		insert_td_horizontal (s, s->iso_td[(urb->start_frame + n) & 1023], td);	// store in iso-tds
+	}
+
+	kfree (tdm);
+	dbg("ISO-INT# %i, start %i, now %i", urb->number_of_packets, urb->start_frame, UHCI_GET_CURRENT_FRAME (s) & 1023);
+	ret = 0;
+
+      err:
+	__restore_flags(flags);
+	return ret;
+}
+/*-------------------------------------------------------------------*/
+// returns: 0 (no transfer queued), urb* (this urb already queued)
+ 
+_static struct urb* search_dev_ep (uhci_t *s, struct urb *urb)
+{
+	struct list_head *p;
+	struct urb *tmp;
+	unsigned int mask = usb_pipecontrol(urb->pipe) ? (~USB_DIR_IN) : (~0);
+
+	dbg("search_dev_ep:");
+
+	p=s->urb_list.next;
+
+	for (; p != &s->urb_list; p = p->next) {
+		tmp = list_entry (p, struct urb, urb_list);
+		dbg("urb: %p", tmp);
+		// we can accept this urb if it is not queued at this time 
+		// or if non-iso transfer requests should be scheduled for the same device and pipe
+		if ((!usb_pipeisoc(urb->pipe) && (tmp->dev == urb->dev) && !((tmp->pipe ^ urb->pipe) & mask)) ||
+		    (urb == tmp)) {
+			return tmp;	// found another urb already queued for processing
+		}
+	}
+
+	return 0;
+}
+/*-------------------------------------------------------------------*/
+_static int uhci_submit_urb (struct urb *urb)
+{
+	uhci_t *s;
+	urb_priv_t *urb_priv;
+	int ret = 0, type;
+	unsigned long flags;
+	struct urb *queued_urb=NULL;
+	int bustime;
+		
+	if (!urb->dev || !urb->dev->bus)
+		return -ENODEV;
+
+	s = (uhci_t*) urb->dev->bus->hcpriv;
+	//dbg("submit_urb: %p type %d",urb,usb_pipetype(urb->pipe));
+	
+	if (!s->running)
+		return -ENODEV;
+	
+	type = usb_pipetype (urb->pipe);
+
+	if (usb_pipedevice (urb->pipe) == s->rh.devnum)
+		return rh_submit_urb (urb);	/* virtual root hub */
+
+	// Sanity checks
+	if (usb_maxpacket (urb->dev, urb->pipe, usb_pipeout (urb->pipe)) <= 0) {		
+		err("uhci_submit_urb: pipesize for pipe %x is zero", urb->pipe);
+		return -EMSGSIZE;
+	}
+
+	if (urb->transfer_buffer_length < 0 && type != PIPE_ISOCHRONOUS) {
+		err("uhci_submit_urb: Negative transfer length for urb %p", urb);
+		return -EINVAL;
+	}
+
+	usb_inc_dev_use (urb->dev);
+
+	spin_lock_irqsave (&s->urb_list_lock, flags);
+
+	queued_urb = search_dev_ep (s, urb); // returns already queued urb for that pipe
+
+	if (queued_urb) {
+
+		queue_dbg("found bulk urb %p\n", queued_urb);
+
+		if (( type != PIPE_BULK) ||
+		    ((type == PIPE_BULK) &&
+		     (!(urb->transfer_flags & USB_QUEUE_BULK) || !(queued_urb->transfer_flags & USB_QUEUE_BULK)))) {
+			spin_unlock_irqrestore (&s->urb_list_lock, flags);
+			usb_dec_dev_use (urb->dev);
+			err("ENXIO %08x, flags %x, urb %p, burb %p",urb->pipe,urb->transfer_flags,urb,queued_urb);
+			return -ENXIO;	// urb already queued
+		}
+	}
+
+#ifdef DEBUG_SLAB
+	urb_priv = kmem_cache_alloc(urb_priv_kmem, SLAB_FLAG);
+#else
+	urb_priv = kmalloc (sizeof (urb_priv_t), KMALLOC_FLAG);
+#endif
+	if (!urb_priv) {
+		usb_dec_dev_use (urb->dev);
+		spin_unlock_irqrestore (&s->urb_list_lock, flags);
+		return -ENOMEM;
+	}
+
+	memset(urb_priv, 0, sizeof(urb_priv_t));
+	urb->hcpriv = urb_priv;
+	INIT_LIST_HEAD (&urb_priv->desc_list);
+
+	dbg("submit_urb: scheduling %p", urb);
+	
+	if (type == PIPE_CONTROL)
+		urb_priv->setup_packet_dma = pci_map_single(s->uhci_pci, urb->setup_packet,
+							    sizeof(struct usb_ctrlrequest), PCI_DMA_TODEVICE);
+
+	if (urb->transfer_buffer_length)
+		urb_priv->transfer_buffer_dma = pci_map_single(s->uhci_pci,
+							       urb->transfer_buffer,
+							       urb->transfer_buffer_length,
+							       usb_pipein(urb->pipe) ?
+							       PCI_DMA_FROMDEVICE :
+							       PCI_DMA_TODEVICE);
+
+	if (type == PIPE_BULK) {
+	
+		if (queued_urb) {
+			while (((urb_priv_t*)queued_urb->hcpriv)->next_queued_urb)  // find last queued bulk
+				queued_urb=((urb_priv_t*)queued_urb->hcpriv)->next_queued_urb;
+			
+			((urb_priv_t*)queued_urb->hcpriv)->next_queued_urb=urb;
+		}
+		atomic_inc (&s->avoid_bulk);
+		ret = uhci_submit_bulk_urb (urb, queued_urb);
+		atomic_dec (&s->avoid_bulk);
+		spin_unlock_irqrestore (&s->urb_list_lock, flags);
+	}
+	else {
+		spin_unlock_irqrestore (&s->urb_list_lock, flags);
+		switch (type) {
+		case PIPE_ISOCHRONOUS:			
+			if (urb->bandwidth == 0) {      /* not yet checked/allocated */
+				if (urb->number_of_packets <= 0) {
+					ret = -EINVAL;
+					break;
+				}
+
+				bustime = usb_check_bandwidth (urb->dev, urb);
+				if (bustime < 0) 
+					ret = bustime;
+				else {
+					ret = uhci_submit_iso_urb(urb);
+					if (ret == 0)
+						usb_claim_bandwidth (urb->dev, urb, bustime, 1);
+				}
+			} else {        /* bandwidth is already set */
+				ret = uhci_submit_iso_urb(urb);
+			}
+			break;
+		case PIPE_INTERRUPT:
+			if (urb->bandwidth == 0) {      /* not yet checked/allocated */
+				bustime = usb_check_bandwidth (urb->dev, urb);
+				if (bustime < 0)
+					ret = bustime;
+				else {
+					ret = uhci_submit_int_urb(urb);
+					if (ret == 0)
+						usb_claim_bandwidth (urb->dev, urb, bustime, 0);
+				}
+			} else {        /* bandwidth is already set */
+				ret = uhci_submit_int_urb(urb);
+			}
+			break;
+		case PIPE_CONTROL:
+			ret = uhci_submit_control_urb (urb);
+			break;
+		default:
+			ret = -EINVAL;
+		}
+	}
+
+	dbg("submit_urb: scheduled with ret: %d", ret);
+	
+	if (ret != 0) {
+		uhci_urb_dma_unmap(s, urb, urb_priv);
+		usb_dec_dev_use (urb->dev);
+#ifdef DEBUG_SLAB
+		kmem_cache_free(urb_priv_kmem, urb_priv);
+#else
+		kfree (urb_priv);
+#endif
+		return ret;
+	}
+
+	return 0;
+}
+
+// Checks for URB timeout and removes bandwidth reclamation if URB idles too long
+_static void uhci_check_timeouts(uhci_t *s)
+{
+	struct list_head *p,*p2;
+	struct urb *urb;
+	int type;	
+
+	p = s->urb_list.prev;	
+
+	while (p != &s->urb_list) {
+		urb_priv_t *hcpriv;
+
+		p2 = p;
+		p = p->prev;
+		urb = list_entry (p2, struct urb, urb_list);
+		type = usb_pipetype (urb->pipe);
+
+		hcpriv = (urb_priv_t*)urb->hcpriv;
+		
+		if ( urb->timeout && time_after(jiffies, hcpriv->started + urb->timeout)) {
+			urb->transfer_flags |= USB_TIMEOUT_KILLED | USB_ASYNC_UNLINK;
+			async_dbg("uhci_check_timeout: timeout for %p",urb);
+			uhci_unlink_urb_async(s, urb, UNLINK_ASYNC_STORE_URB);
+		}
+#ifdef CONFIG_USB_UHCI_HIGH_BANDWIDTH
+		else if (((type == PIPE_BULK) || (type == PIPE_CONTROL)) &&  
+			 (hcpriv->use_loop) && time_after(jiffies, hcpriv->started + IDLE_TIMEOUT))
+			disable_desc_loop(s, urb);
+#endif
+
+	}
+	s->timeout_check=jiffies;
+}
+
+/*-------------------------------------------------------------------
+ Virtual Root Hub
+ -------------------------------------------------------------------*/
+
+_static __u8 root_hub_dev_des[] =
+{
+	0x12,			/*  __u8  bLength; */
+	0x01,			/*  __u8  bDescriptorType; Device */
+	0x00,			/*  __u16 bcdUSB; v1.0 */
+	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 root_hub_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; 8 Bytes */
+	0x00,
+	0xff			/*  __u8  ep_bInterval; 255 ms */
+};
+
+
+_static __u8 root_hub_hub_des[] =
+{
+	0x09,			/*  __u8  bLength; */
+	0x29,			/*  __u8  bDescriptorType; Hub-descriptor */
+	0x02,			/*  __u8  bNbrPorts; */
+	0x00,			/* __u16  wHubCharacteristics; */
+	0x00,
+	0x01,			/*  __u8  bPwrOn2pwrGood; 2ms */
+	0x00,			/*  __u8  bHubContrCurrent; 0 mA */
+	0x00,			/*  __u8  DeviceRemovable; *** 7 Ports max *** */
+	0xff			/*  __u8  PortPwrCtrlMask; *** 7 ports max *** */
+};
+
+/*-------------------------------------------------------------------------*/
+/* prepare Interrupt pipe transaction data; HUB INTERRUPT ENDPOINT */
+_static int rh_send_irq (struct urb *urb)
+{
+	int len = 1;
+	int i;
+	uhci_t *uhci = urb->dev->bus->hcpriv;
+	unsigned int io_addr = uhci->io_addr;
+	__u16 data = 0;
+
+	for (i = 0; i < uhci->rh.numports; i++) {
+		data |= ((inw (io_addr + USBPORTSC1 + i * 2) & 0xa) > 0 ? (1 << (i + 1)) : 0);
+		len = (i + 1) / 8 + 1;
+	}
+
+	*(__u16 *) urb->transfer_buffer = cpu_to_le16 (data);
+	urb->actual_length = len;
+	urb->status = 0;
+	
+	if ((data > 0) && (uhci->rh.send != 0)) {
+		dbg("Root-Hub INT complete: port1: %x port2: %x data: %x",
+		     inw (io_addr + USBPORTSC1), inw (io_addr + USBPORTSC2), data);
+		urb->complete (urb);
+	}
+	return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+/* Virtual Root Hub INTs are polled by this timer every "intervall" ms */
+_static int rh_init_int_timer (struct urb *urb);
+
+_static void rh_int_timer_do (unsigned long ptr)
+{
+	int len;
+	struct urb *urb = (struct urb*) ptr;
+	uhci_t *uhci = urb->dev->bus->hcpriv;
+
+	if (uhci->rh.send) {
+		len = rh_send_irq (urb);
+		if (len > 0) {
+			urb->actual_length = len;
+			if (urb->complete)
+				urb->complete (urb);
+		}
+	}
+	rh_init_int_timer (urb);
+}
+
+/*-------------------------------------------------------------------------*/
+/* Root Hub INTs are polled by this timer, polling interval 20ms */
+
+_static int rh_init_int_timer (struct urb *urb)
+{
+	uhci_t *uhci = urb->dev->bus->hcpriv;
+
+	uhci->rh.interval = urb->interval;
+	init_timer (&uhci->rh.rh_int_timer);
+	uhci->rh.rh_int_timer.function = rh_int_timer_do;
+	uhci->rh.rh_int_timer.data = (unsigned long) urb;
+	uhci->rh.rh_int_timer.expires = jiffies + (HZ * 20) / 1000;
+	add_timer (&uhci->rh.rh_int_timer);
+
+	return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+#define OK(x) 			len = (x); break
+
+#define CLR_RH_PORTSTAT(x) \
+		status = inw(io_addr+USBPORTSC1+2*(wIndex-1)); \
+		status = (status & 0xfff5) & ~(x); \
+		outw(status, io_addr+USBPORTSC1+2*(wIndex-1))
+
+#define SET_RH_PORTSTAT(x) \
+		status = inw(io_addr+USBPORTSC1+2*(wIndex-1)); \
+		status = (status & 0xfff5) | (x); \
+		outw(status, io_addr+USBPORTSC1+2*(wIndex-1))
+
+
+/*-------------------------------------------------------------------------*/
+/****
+ ** Root Hub Control Pipe
+ *************************/
+
+
+_static int rh_submit_urb (struct urb *urb)
+{
+	struct usb_device *usb_dev = urb->dev;
+	uhci_t *uhci = usb_dev->bus->hcpriv;
+	unsigned int pipe = urb->pipe;
+	struct usb_ctrlrequest *cmd = (struct usb_ctrlrequest *) urb->setup_packet;
+	void *data = urb->transfer_buffer;
+	int leni = urb->transfer_buffer_length;
+	int len = 0;
+	int status = 0;
+	int stat = 0;
+	int i;
+	unsigned int io_addr = uhci->io_addr;
+	__u16 cstatus;
+
+	__u16 bmRType_bReq;
+	__u16 wValue;
+	__u16 wIndex;
+	__u16 wLength;
+
+	if (usb_pipetype (pipe) == PIPE_INTERRUPT) {
+		dbg("Root-Hub submit IRQ: every %d ms", urb->interval);
+		uhci->rh.urb = urb;
+		uhci->rh.send = 1;
+		uhci->rh.interval = urb->interval;
+		rh_init_int_timer (urb);
+
+		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);
+
+	for (i = 0; i < 8; i++)
+		uhci->rh.c_p_r[i] = 0;
+
+	dbg("Root-Hub: adr: %2x cmd(%1x): %04x %04x %04x %04x",
+	     uhci->rh.devnum, 8, bmRType_bReq, wValue, wIndex, wLength);
+
+	switch (bmRType_bReq) {
+		/* Request Destination:
+		   without flags: Device, 
+		   RH_INTERFACE: interface, 
+		   RH_ENDPOINT: endpoint,
+		   RH_CLASS means HUB here, 
+		   RH_OTHER | RH_CLASS  almost ever means HUB_PORT here 
+		 */
+
+	case RH_GET_STATUS:
+		*(__u16 *) data = cpu_to_le16 (1);
+		OK (2);
+	case RH_GET_STATUS | RH_INTERFACE:
+		*(__u16 *) data = cpu_to_le16 (0);
+		OK (2);
+	case RH_GET_STATUS | RH_ENDPOINT:
+		*(__u16 *) data = cpu_to_le16 (0);
+		OK (2);
+	case RH_GET_STATUS | RH_CLASS:
+		*(__u32 *) data = cpu_to_le32 (0);
+		OK (4);		/* hub power ** */
+	case RH_GET_STATUS | RH_OTHER | RH_CLASS:
+		status = inw (io_addr + USBPORTSC1 + 2 * (wIndex - 1));
+		cstatus = ((status & USBPORTSC_CSC) >> (1 - 0)) |
+			((status & USBPORTSC_PEC) >> (3 - 1)) |
+			(uhci->rh.c_p_r[wIndex - 1] << (0 + 4));
+		status = (status & USBPORTSC_CCS) |
+			((status & USBPORTSC_PE) >> (2 - 1)) |
+			((status & USBPORTSC_SUSP) >> (12 - 2)) |
+			((status & USBPORTSC_PR) >> (9 - 4)) |
+			(1 << 8) |	/* power on ** */
+			((status & USBPORTSC_LSDA) << (-8 + 9));
+
+		*(__u16 *) data = cpu_to_le16 (status);
+		*(__u16 *) (data + 2) = cpu_to_le16 (cstatus);
+		OK (4);
+
+	case RH_CLEAR_FEATURE | RH_ENDPOINT:
+		switch (wValue) {
+		case (RH_ENDPOINT_STALL):
+			OK (0);
+		}
+		break;
+
+	case RH_CLEAR_FEATURE | RH_CLASS:
+		switch (wValue) {
+		case (RH_C_HUB_OVER_CURRENT):
+			OK (0);	/* hub power over current ** */
+		}
+		break;
+
+	case RH_CLEAR_FEATURE | RH_OTHER | RH_CLASS:
+		switch (wValue) {
+		case (RH_PORT_ENABLE):
+			CLR_RH_PORTSTAT (USBPORTSC_PE);
+			OK (0);
+		case (RH_PORT_SUSPEND):
+			CLR_RH_PORTSTAT (USBPORTSC_SUSP);
+			OK (0);
+		case (RH_PORT_POWER):
+			OK (0);	/* port power ** */
+		case (RH_C_PORT_CONNECTION):
+			SET_RH_PORTSTAT (USBPORTSC_CSC);
+			OK (0);
+		case (RH_C_PORT_ENABLE):
+			SET_RH_PORTSTAT (USBPORTSC_PEC);
+			OK (0);
+		case (RH_C_PORT_SUSPEND):
+/*** WR_RH_PORTSTAT(RH_PS_PSSC); */
+			OK (0);
+		case (RH_C_PORT_OVER_CURRENT):
+			OK (0);	/* port power over current ** */
+		case (RH_C_PORT_RESET):
+			uhci->rh.c_p_r[wIndex - 1] = 0;
+			OK (0);
+		}
+		break;
+
+	case RH_SET_FEATURE | RH_OTHER | RH_CLASS:
+		switch (wValue) {
+		case (RH_PORT_SUSPEND):
+			SET_RH_PORTSTAT (USBPORTSC_SUSP);
+			OK (0);
+		case (RH_PORT_RESET):
+			SET_RH_PORTSTAT (USBPORTSC_PR);
+			uhci_wait_ms (10);
+			uhci->rh.c_p_r[wIndex - 1] = 1;
+			CLR_RH_PORTSTAT (USBPORTSC_PR);
+			udelay (10);
+			SET_RH_PORTSTAT (USBPORTSC_PE);
+			uhci_wait_ms (10);
+			SET_RH_PORTSTAT (0xa);
+			OK (0);
+		case (RH_PORT_POWER):
+			OK (0);	/* port power ** */
+		case (RH_PORT_ENABLE):
+			SET_RH_PORTSTAT (USBPORTSC_PE);
+			OK (0);
+		}
+		break;
+
+	case RH_SET_ADDRESS:
+		uhci->rh.devnum = wValue;
+		OK (0);
+
+	case RH_GET_DESCRIPTOR:
+		switch ((wValue & 0xff00) >> 8) {
+		case (0x01):	/* device descriptor */
+			len = min_t(unsigned int, leni,
+				  min_t(unsigned int,
+				      sizeof (root_hub_dev_des), wLength));
+			memcpy (data, root_hub_dev_des, len);
+			OK (len);
+		case (0x02):	/* configuration descriptor */
+			len = min_t(unsigned int, leni,
+				  min_t(unsigned int,
+				      sizeof (root_hub_config_des), wLength));
+			memcpy (data, root_hub_config_des, len);
+			OK (len);
+		case (0x03):	/* string descriptors */
+			len = usb_root_hub_string (wValue & 0xff,
+			        uhci->io_addr, "UHCI",
+				data, wLength);
+			if (len > 0) {
+				OK(min_t(int, leni, len));
+			} else 
+				stat = -EPIPE;
+		}
+		break;
+
+	case RH_GET_DESCRIPTOR | RH_CLASS:
+		root_hub_hub_des[2] = uhci->rh.numports;
+		len = min_t(unsigned int, leni,
+			  min_t(unsigned int, sizeof (root_hub_hub_des), wLength));
+		memcpy (data, root_hub_hub_des, len);
+		OK (len);
+
+	case RH_GET_CONFIGURATION:
+		*(__u8 *) data = 0x01;
+		OK (1);
+
+	case RH_SET_CONFIGURATION:
+		OK (0);
+	default:
+		stat = -EPIPE;
+	}
+
+	dbg("Root-Hub stat port1: %x port2: %x",
+	     inw (io_addr + USBPORTSC1), inw (io_addr + USBPORTSC2));
+
+	urb->actual_length = len;
+	urb->status = stat;
+	urb->dev=NULL;
+	if (urb->complete)
+		urb->complete (urb);
+	return 0;
+}
+/*-------------------------------------------------------------------------*/
+
+_static int rh_unlink_urb (struct urb *urb)
+{
+	uhci_t *uhci = urb->dev->bus->hcpriv;
+
+	if (uhci->rh.urb==urb) {
+		dbg("Root-Hub unlink IRQ");
+		uhci->rh.send = 0;
+		del_timer (&uhci->rh.rh_int_timer);
+	}
+	return 0;
+}
+/*-------------------------------------------------------------------*/
+
+/*
+ * Map status to standard result codes
+ *
+ * <status> is (td->status & 0xFE0000) [a.k.a. uhci_status_bits(td->status)
+ * <dir_out> is True for output TDs and False for input TDs.
+ */
+_static int uhci_map_status (int status, int dir_out)
+{
+	if (!status)
+		return 0;
+	if (status & TD_CTRL_BITSTUFF)	/* Bitstuff error */
+		return -EPROTO;
+	if (status & TD_CTRL_CRCTIMEO) {	/* CRC/Timeout */
+		if (dir_out)
+			return -ETIMEDOUT;
+		else
+			return -EILSEQ;
+	}
+	if (status & TD_CTRL_NAK)	/* NAK */
+		return -ETIMEDOUT;
+	if (status & TD_CTRL_BABBLE)	/* Babble */
+		return -EOVERFLOW;
+	if (status & TD_CTRL_DBUFERR)	/* Buffer error */
+		return -ENOSR;
+	if (status & TD_CTRL_STALLED)	/* Stalled */
+		return -EPIPE;
+	if (status & TD_CTRL_ACTIVE)	/* Active */
+		return 0;
+
+	return -EPROTO;
+}
+
+/*
+ * Only the USB core should call uhci_alloc_dev and uhci_free_dev
+ */
+_static int uhci_alloc_dev (struct usb_device *usb_dev)
+{
+	return 0;
+}
+
+_static void uhci_unlink_urbs(uhci_t *s, struct usb_device *usb_dev, int remove_all)
+{
+	unsigned long flags;
+	struct list_head *p;
+	struct list_head *p2;
+	struct urb *urb;
+
+	spin_lock_irqsave (&s->urb_list_lock, flags);
+	p = s->urb_list.prev;	
+	while (p != &s->urb_list) {
+		p2 = p;
+		p = p->prev ;
+		urb = list_entry (p2, struct urb, urb_list);
+		dbg("urb: %p, dev %p, %p", urb, usb_dev,urb->dev);
+		
+		//urb->transfer_flags |=USB_ASYNC_UNLINK; 
+			
+		if (remove_all || (usb_dev == urb->dev)) {
+			spin_unlock_irqrestore (&s->urb_list_lock, flags);
+			warn("forced removing of queued URB %p due to disconnect",urb);
+			uhci_unlink_urb(urb);
+			urb->dev = NULL; // avoid further processing of this URB
+			spin_lock_irqsave (&s->urb_list_lock, flags);
+			p = s->urb_list.prev;	
+		}
+	}
+	spin_unlock_irqrestore (&s->urb_list_lock, flags);
+}
+
+_static int uhci_free_dev (struct usb_device *usb_dev)
+{
+	uhci_t *s;
+	
+
+	if(!usb_dev || !usb_dev->bus || !usb_dev->bus->hcpriv)
+		return -EINVAL;
+	
+	s=(uhci_t*) usb_dev->bus->hcpriv;	
+	uhci_unlink_urbs(s, usb_dev, 0);
+
+	return 0;
+}
+
+/*
+ * uhci_get_current_frame_number()
+ *
+ * returns the current frame number for a USB bus/controller.
+ */
+_static int uhci_get_current_frame_number (struct usb_device *usb_dev)
+{
+	return UHCI_GET_CURRENT_FRAME ((uhci_t*) usb_dev->bus->hcpriv);
+}
+
+struct usb_operations uhci_device_operations =
+{
+	uhci_alloc_dev,
+	uhci_free_dev,
+	uhci_get_current_frame_number,
+	uhci_submit_urb,
+	uhci_unlink_urb
+};
+
+_static void correct_data_toggles(struct urb *urb)
+{
+	usb_settoggle (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe), 
+		       !usb_gettoggle (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe)));
+
+	while(urb) {
+		urb_priv_t *priv=urb->hcpriv;		
+		uhci_desc_t *qh = list_entry (priv->desc_list.next, uhci_desc_t, desc_list);
+		struct list_head *p = qh->vertical.next;
+		uhci_desc_t *td;
+		dbg("URB to correct %p\n", urb);
+	
+		for (; p != &qh->vertical; p = p->next) {
+			td = list_entry (p, uhci_desc_t, vertical);
+			td->hw.td.info^=cpu_to_le32(1<<TD_TOKEN_TOGGLE);
+		}
+		urb=priv->next_queued_urb;
+	}
+}
+
+/* 
+ * For IN-control transfers, process_transfer gets a bit more complicated,
+ * since there are devices that return less data (eg. strings) than they
+ * have announced. This leads to a queue abort due to the short packet,
+ * the status stage is not executed. If this happens, the status stage
+ * is manually re-executed.
+ * mode: PROCESS_TRANSFER_REGULAR: regular (unlink QH)
+ *       PROCESS_TRANSFER_DONT_UNLINK: QHs already unlinked (for async unlink_urb)
+ */
+
+_static int process_transfer (uhci_t *s, struct urb *urb, int mode)
+{
+	int ret = 0;
+	urb_priv_t *urb_priv = urb->hcpriv;
+	struct list_head *qhl = urb_priv->desc_list.next;
+	uhci_desc_t *qh = list_entry (qhl, uhci_desc_t, desc_list);
+	struct list_head *p = qh->vertical.next;
+	uhci_desc_t *desc= list_entry (urb_priv->desc_list.prev, uhci_desc_t, desc_list);
+	uhci_desc_t *last_desc = list_entry (desc->vertical.prev, uhci_desc_t, vertical);
+	int data_toggle = usb_gettoggle (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe));	// save initial data_toggle
+	int maxlength; 	// extracted and remapped info from TD
+	int actual_length;
+	int status = 0;
+
+	//dbg("process_transfer: urb %p, urb_priv %p, qh %p last_desc %p\n",urb,urb_priv, qh, last_desc);
+
+	/* if the status phase has been retriggered and the
+	   queue is empty or the last status-TD is inactive, the retriggered
+	   status stage is completed
+	 */
+
+	if (urb_priv->flags && 
+	    ((qh->hw.qh.element == cpu_to_le32(UHCI_PTR_TERM)) || !is_td_active(desc)))
+		goto transfer_finished;
+
+	urb->actual_length=0;
+
+	for (; p != &qh->vertical; p = p->next) {
+		desc = list_entry (p, uhci_desc_t, vertical);
+
+		if (is_td_active(desc)) {	// do not process active TDs
+			if (mode == CLEAN_TRANSFER_DELETION_MARK) // if called from async_unlink
+				uhci_clean_transfer(s, urb, qh, CLEAN_TRANSFER_DELETION_MARK);
+			return ret;
+		}
+	
+		actual_length = uhci_actual_length(le32_to_cpu(desc->hw.td.status));		// extract transfer parameters from TD
+		maxlength = (((le32_to_cpu(desc->hw.td.info) >> 21) & 0x7ff) + 1) & 0x7ff;
+		status = uhci_map_status (uhci_status_bits (le32_to_cpu(desc->hw.td.status)), usb_pipeout (urb->pipe));
+
+		if (status == -EPIPE) { 		// see if EP is stalled
+			// set up stalled condition
+			usb_endpoint_halt (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe));
+		}
+
+		if (status && (status != -EPIPE)) {	// if any error occurred stop processing of further TDs
+			// only set ret if status returned an error
+  is_error:
+			ret = status;
+			urb->error_count++;
+			break;
+		}
+		else if ((le32_to_cpu(desc->hw.td.info) & 0xff) != USB_PID_SETUP)
+			urb->actual_length += actual_length;
+
+		// got less data than requested
+		if ( (actual_length < maxlength)) {
+			if (urb->transfer_flags & USB_DISABLE_SPD) {
+				status = -EREMOTEIO;	// treat as real error
+				dbg("process_transfer: SPD!!");
+				break;	// exit after this TD because SP was detected
+			}
+
+			// short read during control-IN: re-start status stage
+			if ((usb_pipetype (urb->pipe) == PIPE_CONTROL)) {
+				if (uhci_packetid(le32_to_cpu(last_desc->hw.td.info)) == USB_PID_OUT) {
+			
+					set_qh_element(qh, last_desc->dma_addr);  // re-trigger status stage
+					dbg("short packet during control transfer, retrigger status stage @ %p",last_desc);
+					urb_priv->flags = 1; // mark as short control packet
+					return 0;
+				}
+			}
+			// all other cases: short read is OK
+			data_toggle = uhci_toggle (le32_to_cpu(desc->hw.td.info));
+			break;
+		}
+		else if (status)
+			goto is_error;
+
+		data_toggle = uhci_toggle (le32_to_cpu(desc->hw.td.info));
+		queue_dbg("process_transfer: len:%d status:%x mapped:%x toggle:%d", actual_length, le32_to_cpu(desc->hw.td.status),status, data_toggle);      
+
+	}
+
+	if (usb_pipetype (urb->pipe) == PIPE_BULK ) {  /* toggle correction for short bulk transfers (nonqueued/queued) */
+
+		urb_priv_t *priv=(urb_priv_t*)urb->hcpriv;
+		struct urb *next_queued_urb=priv->next_queued_urb;
+
+		if (next_queued_urb) {
+			urb_priv_t *next_priv=(urb_priv_t*)next_queued_urb->hcpriv;
+			uhci_desc_t *qh = list_entry (next_priv->desc_list.next, uhci_desc_t, desc_list);
+			uhci_desc_t *first_td=list_entry (qh->vertical.next, uhci_desc_t, vertical);
+
+			if (data_toggle == uhci_toggle (le32_to_cpu(first_td->hw.td.info))) {
+				err("process_transfer: fixed toggle");
+				correct_data_toggles(next_queued_urb);
+			}						
+		}
+		else
+			usb_settoggle (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe), !data_toggle);		
+	}
+
+ transfer_finished:
+	
+	uhci_clean_transfer(s, urb, qh, mode);
+
+	urb->status = status;
+
+#ifdef CONFIG_USB_UHCI_HIGH_BANDWIDTH	
+	disable_desc_loop(s,urb);
+#endif	
+
+	queue_dbg("process_transfer: (end) urb %p, wanted len %d, len %d status %x err %d",
+		urb,urb->transfer_buffer_length,urb->actual_length, urb->status, urb->error_count);
+	return ret;
+}
+
+_static int process_interrupt (uhci_t *s, struct urb *urb)
+{
+	int i, ret = -EINPROGRESS;
+	urb_priv_t *urb_priv = urb->hcpriv;
+	struct list_head *p = urb_priv->desc_list.next;
+	uhci_desc_t *desc = list_entry (urb_priv->desc_list.prev, uhci_desc_t, desc_list);
+
+	int actual_length;
+	int status = 0;
+
+	//dbg("urb contains interrupt request");
+
+	for (i = 0; p != &urb_priv->desc_list; p = p->next, i++)	// Maybe we allow more than one TD later ;-)
+	{
+		desc = list_entry (p, uhci_desc_t, desc_list);
+
+		if (is_td_active(desc)) {
+			// do not process active TDs
+			//dbg("TD ACT Status @%p %08x",desc,le32_to_cpu(desc->hw.td.status));
+			break;
+		}
+
+		if (!(desc->hw.td.status & cpu_to_le32(TD_CTRL_IOC))) {
+			// do not process one-shot TDs, no recycling
+			break;
+		}
+		// extract transfer parameters from TD
+
+		actual_length = uhci_actual_length(le32_to_cpu(desc->hw.td.status));
+		status = uhci_map_status (uhci_status_bits (le32_to_cpu(desc->hw.td.status)), usb_pipeout (urb->pipe));
+
+		// see if EP is stalled
+		if (status == -EPIPE) {
+			// set up stalled condition
+			usb_endpoint_halt (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe));
+		}
+
+		// if any error occurred: ignore this td, and continue
+		if (status != 0) {
+			//uhci_show_td (desc);
+			urb->error_count++;
+			goto recycle;
+		}
+		else
+			urb->actual_length = actual_length;
+
+	recycle:
+		uhci_urb_dma_sync(s, urb, urb->hcpriv);
+		if (urb->complete) {
+			//dbg("process_interrupt: calling completion, status %i",status);
+			urb->status = status;
+			((urb_priv_t*)urb->hcpriv)->flags=1; // if unlink_urb is called during completion
+
+			spin_unlock(&s->urb_list_lock);
+			
+			urb->complete ((struct urb *) urb);
+			
+			spin_lock(&s->urb_list_lock);
+
+			((urb_priv_t*)urb->hcpriv)->flags=0;		       			
+		}
+		
+		if ((urb->status != -ECONNABORTED) && (urb->status != ECONNRESET) &&
+			    (urb->status != -ENOENT)) {
+
+			urb->status = -EINPROGRESS;
+
+			// Recycle INT-TD if interval!=0, else mark TD as one-shot
+			if (urb->interval) {
+				
+				desc->hw.td.info &= cpu_to_le32(~(1 << TD_TOKEN_TOGGLE));
+				if (status==0) {
+					((urb_priv_t*)urb->hcpriv)->started=jiffies;
+					desc->hw.td.info |= cpu_to_le32((usb_gettoggle (urb->dev, usb_pipeendpoint (urb->pipe),
+									    usb_pipeout (urb->pipe)) << TD_TOKEN_TOGGLE));
+					usb_dotoggle (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe));
+				} else {
+					desc->hw.td.info |= cpu_to_le32((!usb_gettoggle (urb->dev, usb_pipeendpoint (urb->pipe),
+									     usb_pipeout (urb->pipe)) << TD_TOKEN_TOGGLE));
+				}
+				desc->hw.td.status= cpu_to_le32((urb->pipe & TD_CTRL_LS) | TD_CTRL_ACTIVE | TD_CTRL_IOC |
+					(urb->transfer_flags & USB_DISABLE_SPD ? 0 : TD_CTRL_SPD) | (3 << 27));
+				mb();
+			}
+			else {
+				uhci_unlink_urb_async(s, urb, UNLINK_ASYNC_STORE_URB);
+				uhci_clean_iso_step2(s, urb_priv);
+				// correct toggle after unlink
+				usb_dotoggle (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe));
+				clr_td_ioc(desc); // inactivate TD
+			}
+		}
+	}
+
+	return ret;
+}
+
+// mode: PROCESS_ISO_REGULAR: processing only for done TDs, unlink TDs
+// mode: PROCESS_ISO_FORCE: force processing, don't unlink TDs (already unlinked)
+
+_static int process_iso (uhci_t *s, struct urb *urb, int mode)
+{
+	int i;
+	int ret = 0;
+	urb_priv_t *urb_priv = urb->hcpriv;
+	struct list_head *p = urb_priv->desc_list.next, *p_tmp;
+	uhci_desc_t *desc = list_entry (urb_priv->desc_list.prev, uhci_desc_t, desc_list);
+
+	dbg("urb contains iso request");
+	if (is_td_active(desc) && mode==PROCESS_ISO_REGULAR)
+		return -EXDEV;	// last TD not finished
+
+	urb->error_count = 0;
+	urb->actual_length = 0;
+	urb->status = 0;
+	dbg("process iso urb %p, %li, %i, %i, %i %08x",urb,jiffies,UHCI_GET_CURRENT_FRAME(s),
+	    urb->number_of_packets,mode,le32_to_cpu(desc->hw.td.status));
+
+	for (i = 0; p != &urb_priv->desc_list;  i++) {
+		desc = list_entry (p, uhci_desc_t, desc_list);
+		
+		//uhci_show_td(desc);
+		if (is_td_active(desc)) {
+			// means we have completed the last TD, but not the TDs before
+			desc->hw.td.status &= cpu_to_le32(~TD_CTRL_ACTIVE);
+			dbg("TD still active (%x)- grrr. paranoia!", le32_to_cpu(desc->hw.td.status));
+			ret = -EXDEV;
+			urb->iso_frame_desc[i].status = ret;
+			unlink_td (s, desc, 1);
+			// FIXME: immediate deletion may be dangerous
+			goto err;
+		}
+
+		if (mode == PROCESS_ISO_REGULAR)
+			unlink_td (s, desc, 1);
+
+		if (urb->number_of_packets <= i) {
+			dbg("urb->number_of_packets (%d)<=(%d)", urb->number_of_packets, i);
+			ret = -EINVAL;
+			goto err;
+		}
+
+		urb->iso_frame_desc[i].actual_length = uhci_actual_length(le32_to_cpu(desc->hw.td.status));
+		urb->iso_frame_desc[i].status = uhci_map_status (uhci_status_bits (le32_to_cpu(desc->hw.td.status)), usb_pipeout (urb->pipe));
+		urb->actual_length += urb->iso_frame_desc[i].actual_length;
+
+	      err:
+
+		if (urb->iso_frame_desc[i].status != 0) {
+			urb->error_count++;
+			urb->status = urb->iso_frame_desc[i].status;
+		}
+		dbg("process_iso: %i: len:%d %08x status:%x",
+		     i, urb->iso_frame_desc[i].actual_length, le32_to_cpu(desc->hw.td.status),urb->iso_frame_desc[i].status);
+
+		p_tmp = p;
+		p = p->next;
+		list_del (p_tmp);
+		delete_desc (s, desc);
+	}
+	
+	dbg("process_iso: exit %i (%d), actual_len %i", i, ret,urb->actual_length);
+	return ret;
+}
+
+
+_static int process_urb (uhci_t *s, struct list_head *p)
+{
+	int ret = 0;
+	struct urb *urb;
+
+	urb=list_entry (p, struct urb, urb_list);
+	//dbg("process_urb: found queued urb: %p", urb);
+
+	switch (usb_pipetype (urb->pipe)) {
+	case PIPE_CONTROL:
+		ret = process_transfer (s, urb, CLEAN_TRANSFER_REGULAR);
+		break;
+	case PIPE_BULK:
+		if (!s->avoid_bulk.counter)
+			ret = process_transfer (s, urb, CLEAN_TRANSFER_REGULAR);
+		else
+			return 0;
+		break;
+	case PIPE_ISOCHRONOUS:
+		ret = process_iso (s, urb, PROCESS_ISO_REGULAR);
+		break;
+	case PIPE_INTERRUPT:
+		ret = process_interrupt (s, urb);
+		break;
+	}
+
+	if (urb->status != -EINPROGRESS) {
+		urb_priv_t *urb_priv;
+		struct usb_device *usb_dev;
+		
+		usb_dev=urb->dev;
+
+		/* Release bandwidth for Interrupt or Iso transfers */
+		if (urb->bandwidth) {
+			if (usb_pipetype(urb->pipe)==PIPE_ISOCHRONOUS)
+				usb_release_bandwidth (urb->dev, urb, 1);
+			else if (usb_pipetype(urb->pipe)==PIPE_INTERRUPT && urb->interval)
+				usb_release_bandwidth (urb->dev, urb, 0);
+		}
+
+		dbg("dequeued urb: %p", urb);
+		dequeue_urb (s, urb);
+
+		urb_priv = urb->hcpriv;
+
+		uhci_urb_dma_unmap(s, urb, urb_priv);
+
+#ifdef DEBUG_SLAB
+		kmem_cache_free(urb_priv_kmem, urb_priv);
+#else
+		kfree (urb_priv);
+#endif
+
+		if ((usb_pipetype (urb->pipe) != PIPE_INTERRUPT)) {  // process_interrupt does completion on its own		
+			struct urb *next_urb = urb->next;
+			int is_ring = 0;
+			int contains_killed = 0;
+			int loop_count=0;
+			
+			if (next_urb) {
+				// Find out if the URBs are linked to a ring
+				while  (next_urb != NULL && next_urb != urb && loop_count < MAX_NEXT_COUNT) {
+					if (next_urb->status == -ENOENT) {// killed URBs break ring structure & resubmission
+						contains_killed = 1;
+						break;
+					}	
+					next_urb = next_urb->next;
+					loop_count++;
+				}
+				
+				if (loop_count == MAX_NEXT_COUNT)
+					err("process_urb: Too much linked URBs in ring detection!");
+
+				if (next_urb == urb)
+					is_ring=1;
+			}			
+
+			// Submit idle/non-killed URBs linked with urb->next
+			// Stop before the current URB				
+			
+			next_urb = urb->next;	
+			if (next_urb && !contains_killed) {
+				int ret_submit;
+				next_urb = urb->next;	
+				
+				loop_count=0;
+				while (next_urb != NULL && next_urb != urb && loop_count < MAX_NEXT_COUNT) {
+					if (next_urb->status != -EINPROGRESS) {
+					
+						if (next_urb->status == -ENOENT) 
+							break;
+
+						spin_unlock(&s->urb_list_lock);
+
+						ret_submit=uhci_submit_urb(next_urb);
+						spin_lock(&s->urb_list_lock);
+						
+						if (ret_submit)
+							break;						
+					}
+					loop_count++;
+					next_urb = next_urb->next;
+				}
+				if (loop_count == MAX_NEXT_COUNT)
+					err("process_urb: Too much linked URBs in resubmission!");
+			}
+
+			// Completion
+			if (urb->complete) {
+				int was_unlinked = (urb->status == -ENOENT);
+				urb->dev = NULL;
+				spin_unlock(&s->urb_list_lock);
+
+				urb->complete ((struct urb *) urb);
+
+				// Re-submit the URB if ring-linked
+				if (is_ring && !was_unlinked && !contains_killed) {
+					urb->dev=usb_dev;
+					uhci_submit_urb (urb);
+				}
+				spin_lock(&s->urb_list_lock);
+			}
+			
+			usb_dec_dev_use (usb_dev);
+		}
+	}
+
+	return ret;
+}
+
+_static void uhci_interrupt (int irq, void *__uhci, struct pt_regs *regs)
+{
+	uhci_t *s = __uhci;
+	unsigned int io_addr = s->io_addr;
+	unsigned short status;
+	struct list_head *p, *p2;
+	int restarts, work_done;
+	
+	/*
+	 * Read the interrupt status, and write it back to clear the
+	 * interrupt cause
+	 */
+
+	status = inw (io_addr + USBSTS);
+
+	if (!status)		/* shared interrupt, not mine */
+		return;
+
+	dbg("interrupt");
+
+	if (status != 1) {
+		// Avoid too much error messages at a time
+		if (time_after(jiffies, s->last_error_time + ERROR_SUPPRESSION_TIME)) {
+			warn("interrupt, status %x, frame# %i", status, 
+			     UHCI_GET_CURRENT_FRAME(s));
+			s->last_error_time = jiffies;
+		}
+		
+		// remove host controller halted state
+		if ((status&0x20) && (s->running)) {
+			err("Host controller halted, trying to restart.");
+			outw (USBCMD_RS | inw(io_addr + USBCMD), io_addr + USBCMD);
+		}
+		//uhci_show_status (s);
+	}
+	/*
+	 * traverse the list in *reverse* direction, because new entries
+	 * may be added at the end.
+	 * also, because process_urb may unlink the current urb,
+	 * we need to advance the list before
+	 * New: check for max. workload and restart count
+	 */
+
+	spin_lock (&s->urb_list_lock);
+
+	restarts=0;
+	work_done=0;
+
+restart:
+	s->unlink_urb_done=0;
+	p = s->urb_list.prev;	
+
+	while (p != &s->urb_list && (work_done < 1024)) {
+		p2 = p;
+		p = p->prev;
+
+		process_urb (s, p2);
+		
+		work_done++;
+
+		if (s->unlink_urb_done) {
+			s->unlink_urb_done=0;
+			restarts++;
+			
+			if (restarts<16)	// avoid endless restarts
+				goto restart;
+			else 
+				break;
+		}
+	}
+	if (time_after(jiffies, s->timeout_check + (HZ/30)))
+		uhci_check_timeouts(s);
+
+	clean_descs(s, CLEAN_NOT_FORCED);
+	uhci_cleanup_unlink(s, CLEAN_NOT_FORCED);
+	uhci_switch_timer_int(s);
+							
+	spin_unlock (&s->urb_list_lock);
+	
+	outw (status, io_addr + USBSTS);
+
+	//dbg("uhci_interrupt: done");
+}
+
+_static void reset_hc (uhci_t *s)
+{
+	unsigned int io_addr = s->io_addr;
+
+	s->apm_state = 0;
+	/* Global reset for 50ms */
+	outw (USBCMD_GRESET, io_addr + USBCMD);
+	uhci_wait_ms (50);
+	outw (0, io_addr + USBCMD);
+	uhci_wait_ms (10);
+}
+
+_static void start_hc (uhci_t *s)
+{
+	unsigned int io_addr = s->io_addr;
+	int timeout = 10;
+
+	/*
+	 * Reset the HC - this will force us to get a
+	 * new notification of any already connected
+	 * ports due to the virtual disconnect that it
+	 * implies.
+	 */
+	outw (USBCMD_HCRESET, io_addr + USBCMD);
+
+	while (inw (io_addr + USBCMD) & USBCMD_HCRESET) {
+		if (!--timeout) {
+			err("USBCMD_HCRESET timed out!");
+			break;
+		}
+		udelay(1);
+	}
+
+	/* Turn on all interrupts */
+	outw (USBINTR_TIMEOUT | USBINTR_RESUME | USBINTR_IOC | USBINTR_SP, io_addr + USBINTR);
+
+	/* Start at frame 0 */
+	outw (0, io_addr + USBFRNUM);
+	outl (s->framelist_dma, io_addr + USBFLBASEADD);
+
+	/* Run and mark it configured with a 64-byte max packet */
+	outw (USBCMD_RS | USBCMD_CF | USBCMD_MAXP, io_addr + USBCMD);
+	s->apm_state = 1;
+	s->running = 1;
+}
+
+/* No  __devexit, since it maybe called from alloc_uhci() */
+_static void
+uhci_pci_remove (struct pci_dev *dev)
+{
+	uhci_t *s = pci_get_drvdata(dev);
+	struct usb_device *root_hub = s->bus->root_hub;
+
+	s->running = 0;		    // Don't allow submit_urb
+
+	if (root_hub)
+		usb_disconnect (&root_hub);
+
+	reset_hc (s);
+	wait_ms (1);
+
+	uhci_unlink_urbs (s, 0, CLEAN_FORCED);  // Forced unlink of remaining URBs
+	uhci_cleanup_unlink (s, CLEAN_FORCED);  // force cleanup of async killed URBs
+	
+	usb_deregister_bus (s->bus);
+
+	release_region (s->io_addr, s->io_size);
+	free_irq (s->irq, s);
+	usb_free_bus (s->bus);
+	cleanup_skel (s);
+	kfree (s);
+}
+
+_static int __init uhci_start_usb (uhci_t *s)
+{				/* start it up */
+	/* connect the virtual root hub */
+	struct usb_device *usb_dev;
+
+	usb_dev = usb_alloc_dev (NULL, s->bus);
+	if (!usb_dev)
+		return -1;
+
+	s->bus->root_hub = usb_dev;
+	usb_connect (usb_dev);
+
+	if (usb_new_device (usb_dev) != 0) {
+		usb_free_dev (usb_dev);
+		return -1;
+	}
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+_static int
+uhci_pci_suspend (struct pci_dev *dev, u32 state)
+{
+	reset_hc((uhci_t *) pci_get_drvdata(dev));
+	return 0;
+}
+
+_static int
+uhci_pci_resume (struct pci_dev *dev)
+{
+	start_hc((uhci_t *) pci_get_drvdata(dev));
+	return 0;
+}
+#endif
+
+_static int __devinit alloc_uhci (struct pci_dev *dev, int irq, unsigned int io_addr, unsigned int io_size)
+{
+	uhci_t *s;
+	struct usb_bus *bus;
+	char buf[8], *bufp = buf;
+
+#ifndef __sparc__
+	sprintf(buf, "%d", irq);
+#else
+	bufp = __irq_itoa(irq);
+#endif
+	printk(KERN_INFO __FILE__ ": USB UHCI at I/O 0x%x, IRQ %s\n",
+		io_addr, bufp);
+
+	s = kmalloc (sizeof (uhci_t), GFP_KERNEL);
+	if (!s)
+		return -1;
+
+	memset (s, 0, sizeof (uhci_t));
+	INIT_LIST_HEAD (&s->free_desc);
+	INIT_LIST_HEAD (&s->urb_list);
+	INIT_LIST_HEAD (&s->urb_unlinked);
+	spin_lock_init (&s->urb_list_lock);
+	spin_lock_init (&s->qh_lock);
+	spin_lock_init (&s->td_lock);
+	atomic_set(&s->avoid_bulk, 0);
+	s->irq = -1;
+	s->io_addr = io_addr;
+	s->io_size = io_size;
+	s->uhci_pci=dev;
+
+	bus = usb_alloc_bus (&uhci_device_operations);
+	if (!bus) {
+		kfree (s);
+		return -1;
+	}
+
+	s->bus = bus;
+	bus->bus_name = dev->slot_name;
+	bus->hcpriv = s;
+
+	/* UHCI specs says devices must have 2 ports, but goes on to say */
+	/* they may have more but give no way to determine how many they */
+	/* have, so default to 2 */
+	/* According to the UHCI spec, Bit 7 is always set to 1. So we try */
+	/* to use this to our advantage */
+
+	for (s->maxports = 0; s->maxports < (io_size - 0x10) / 2; s->maxports++) {
+		unsigned int portstatus;
+
+		portstatus = inw (io_addr + 0x10 + (s->maxports * 2));
+		dbg("port %i, adr %x status %x", s->maxports,
+			io_addr + 0x10 + (s->maxports * 2), portstatus);
+		if (!(portstatus & 0x0080))
+			break;
+	}
+	warn("Detected %d ports", s->maxports);
+
+	/* This is experimental so anything less than 2 or greater than 8 is */
+	/*  something weird and we'll ignore it */
+	if (s->maxports < 2 || s->maxports > 8) {
+		dbg("Port count misdetected, forcing to 2 ports");
+		s->maxports = 2;
+	}
+
+	s->rh.numports = s->maxports;
+	s->loop_usage=0;
+	if (init_skel (s)) {
+		usb_free_bus (bus);
+		kfree(s);
+		return -1;
+	}
+
+	request_region (s->io_addr, io_size, MODNAME);
+	reset_hc (s);
+	usb_register_bus (s->bus);
+
+	start_hc (s);
+
+	if (request_irq (irq, uhci_interrupt, SA_SHIRQ, MODNAME, s)) {
+		err("request_irq %d failed!",irq);
+		usb_free_bus (bus);
+		reset_hc (s);
+		release_region (s->io_addr, s->io_size);
+		cleanup_skel(s);
+		kfree(s);
+		return -1;
+	}
+
+	/* Enable PIRQ */
+	pci_write_config_word (dev, USBLEGSUP, USBLEGSUP_DEFAULT);
+
+	s->irq = irq;
+
+	if(uhci_start_usb (s) < 0) {
+		uhci_pci_remove(dev);
+		return -1;
+	}
+
+	//chain new uhci device into global list
+	pci_set_drvdata(dev, s);
+	devs=s;
+
+	return 0;
+}
+
+_static int __devinit
+uhci_pci_probe (struct pci_dev *dev, const struct pci_device_id *id)
+{
+	int i;
+
+	if (pci_enable_device(dev) < 0)
+		return -ENODEV;
+		
+	if (!dev->irq) {
+		err("found UHCI device with no IRQ assigned. check BIOS settings!");
+		return -ENODEV;
+	}
+	
+	pci_set_master(dev);
+
+	/* Search for the IO base address.. */
+	for (i = 0; i < 6; i++) {
+
+		unsigned int io_addr = pci_resource_start(dev, i);
+		unsigned int io_size = pci_resource_len(dev, i);
+		if (!(pci_resource_flags(dev,i) & IORESOURCE_IO))
+			continue;
+
+		/* Is it already in use? */
+		if (check_region (io_addr, io_size))
+			break;
+		/* disable legacy emulation */
+		pci_write_config_word (dev, USBLEGSUP, 0);
+	
+		return alloc_uhci(dev, dev->irq, io_addr, io_size);
+	}
+	return -ENODEV;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static const struct pci_device_id __devinitdata uhci_pci_ids [] = { {
+
+	/* handle any USB UHCI controller */
+	class: 		((PCI_CLASS_SERIAL_USB << 8) | 0x00),
+	class_mask: 	~0,
+
+	/* no matter who makes it */
+	vendor:		PCI_ANY_ID,
+	device:		PCI_ANY_ID,
+	subvendor:	PCI_ANY_ID,
+	subdevice:	PCI_ANY_ID,
+
+	}, { /* end: all zeroes */ }
+};
+
+MODULE_DEVICE_TABLE (pci, uhci_pci_ids);
+
+static struct pci_driver uhci_pci_driver = {
+	name:		"usb-uhci",
+	id_table:	&uhci_pci_ids [0],
+
+	probe:		uhci_pci_probe,
+	remove:		uhci_pci_remove,
+
+#ifdef	CONFIG_PM
+	suspend:	uhci_pci_suspend,
+	resume:		uhci_pci_resume,
+#endif	/* PM */
+
+};
+
+/*-------------------------------------------------------------------------*/
+
+static int __init uhci_hcd_init (void) 
+{
+	int retval;
+
+#ifdef DEBUG_SLAB
+	urb_priv_kmem = kmem_cache_create("urb_priv", sizeof(urb_priv_t), 0, SLAB_HWCACHE_ALIGN, NULL, NULL);
+	
+	if(!urb_priv_kmem) {
+		err("kmem_cache_create for urb_priv_t failed (out of memory)");
+		return -ENOMEM;
+	}
+#endif	
+	info(VERSTR);
+
+#ifdef CONFIG_USB_UHCI_HIGH_BANDWIDTH
+	info("High bandwidth mode enabled");	
+#endif
+
+	retval = pci_module_init (&uhci_pci_driver);
+
+#ifdef DEBUG_SLAB
+	if (retval < 0 ) {
+		if (kmem_cache_destroy(urb_priv_kmem))
+			err("urb_priv_kmem remained");
+	}
+#endif
+	
+	info(DRIVER_VERSION ":" DRIVER_DESC);
+
+	return retval;
+}
+
+static void __exit uhci_hcd_cleanup (void) 
+{      
+	pci_unregister_driver (&uhci_pci_driver);
+	
+#ifdef DEBUG_SLAB
+	if(kmem_cache_destroy(urb_priv_kmem))
+		err("urb_priv_kmem remained");
+#endif
+}
+
+module_init (uhci_hcd_init);
+module_exit (uhci_hcd_cleanup);
+
+
+MODULE_AUTHOR( DRIVER_AUTHOR );
+MODULE_DESCRIPTION( DRIVER_DESC );
+MODULE_LICENSE("GPL");
diff -urN linux-2.4.21-pre5/drivers/usb/host/usb-uhci.h linux-2.4.21-pre6/drivers/usb/host/usb-uhci.h
--- linux-2.4.21-pre5/drivers/usb/host/usb-uhci.h	Wed Dec 31 16:00:00 1969
+++ linux-2.4.21-pre6/drivers/usb/host/usb-uhci.h	Wed Mar 26 16:11:39 2003
@@ -0,0 +1,308 @@
+#ifndef __LINUX_UHCI_H
+#define __LINUX_UHCI_H
+
+/*
+   $Id: usb-uhci.h,v 1.58 2001/08/28 16:45:00 acher Exp $
+ */
+#define MODNAME "usb-uhci"
+#define UHCI_LATENCY_TIMER 0
+
+static __inline__ void uhci_wait_ms(unsigned int ms)
+{
+	if(!in_interrupt())
+	{
+		set_current_state(TASK_UNINTERRUPTIBLE);
+		schedule_timeout(1 + ms * HZ / 1000);
+	}
+	else
+		mdelay(ms);
+}
+
+/* Command register */
+#define USBCMD		0
+#define   USBCMD_RS		0x0001	/* Run/Stop */
+#define   USBCMD_HCRESET	0x0002	/* Host reset */
+#define   USBCMD_GRESET		0x0004	/* Global reset */
+#define   USBCMD_EGSM		0x0008	/* Global Suspend Mode */
+#define   USBCMD_FGR		0x0010	/* Force Global Resume */
+#define   USBCMD_SWDBG		0x0020	/* SW Debug mode */
+#define   USBCMD_CF		0x0040	/* Config Flag (sw only) */
+#define   USBCMD_MAXP		0x0080	/* Max Packet (0 = 32, 1 = 64) */
+
+/* Status register */
+#define USBSTS		2
+#define   USBSTS_USBINT		0x0001	/* Interrupt due to IOC */
+#define   USBSTS_ERROR		0x0002	/* Interrupt due to error */
+#define   USBSTS_RD		0x0004	/* Resume Detect */
+#define   USBSTS_HSE		0x0008	/* Host System Error - basically PCI problems */
+#define   USBSTS_HCPE		0x0010	/* Host Controller Process Error - the scripts were buggy */
+#define   USBSTS_HCH		0x0020	/* HC Halted */
+
+/* Interrupt enable register */
+#define USBINTR		4
+#define   USBINTR_TIMEOUT	0x0001	/* Timeout/CRC error enable */
+#define   USBINTR_RESUME	0x0002	/* Resume interrupt enable */
+#define   USBINTR_IOC		0x0004	/* Interrupt On Complete enable */
+#define   USBINTR_SP		0x0008	/* Short packet interrupt enable */
+
+#define USBFRNUM	6
+#define USBFLBASEADD	8
+#define USBSOF		12
+
+/* USB port status and control registers */
+#define USBPORTSC1	16
+#define USBPORTSC2	18
+#define   USBPORTSC_CCS		0x0001	/* Current Connect Status ("device present") */
+#define   USBPORTSC_CSC		0x0002	/* Connect Status Change */
+#define   USBPORTSC_PE		0x0004	/* Port Enable */
+#define   USBPORTSC_PEC		0x0008	/* Port Enable Change */
+#define   USBPORTSC_LS		0x0030	/* Line Status */
+#define   USBPORTSC_RD		0x0040	/* Resume Detect */
+#define   USBPORTSC_LSDA	0x0100	/* Low Speed Device Attached */
+#define   USBPORTSC_PR		0x0200	/* Port Reset */
+#define   USBPORTSC_SUSP	0x1000	/* Suspend */
+
+/* Legacy support register */
+#define USBLEGSUP 0xc0
+#define USBLEGSUP_DEFAULT 0x2000	/* only PIRQ enable set */
+
+#define UHCI_NULL_DATA_SIZE	0x7ff	/* for UHCI controller TD */
+#define UHCI_PID 		0xff	/* PID MASK */
+
+#define UHCI_PTR_BITS		0x000F
+#define UHCI_PTR_TERM		0x0001
+#define UHCI_PTR_QH		0x0002
+#define UHCI_PTR_DEPTH		0x0004
+
+#define UHCI_NUMFRAMES		1024	/* in the frame list [array] */
+#define UHCI_MAX_SOF_NUMBER	2047	/* in an SOF packet */
+#define CAN_SCHEDULE_FRAMES	1000	/* how far future frames can be scheduled */
+
+/*
+ * for TD <status>:
+ */
+#define TD_CTRL_SPD		(1 << 29)	/* Short Packet Detect */
+#define TD_CTRL_C_ERR_MASK	(3 << 27)	/* Error Counter bits */
+#define TD_CTRL_LS		(1 << 26)	/* Low Speed Device */
+#define TD_CTRL_IOS		(1 << 25)	/* Isochronous Select */
+#define TD_CTRL_IOC		(1 << 24)	/* Interrupt on Complete */
+#define TD_CTRL_ACTIVE		(1 << 23)	/* TD Active */
+#define TD_CTRL_STALLED		(1 << 22)	/* TD Stalled */
+#define TD_CTRL_DBUFERR		(1 << 21)	/* Data Buffer Error */
+#define TD_CTRL_BABBLE		(1 << 20)	/* Babble Detected */
+#define TD_CTRL_NAK		(1 << 19)	/* NAK Received */
+#define TD_CTRL_CRCTIMEO	(1 << 18)	/* CRC/Time Out Error */
+#define TD_CTRL_BITSTUFF	(1 << 17)	/* Bit Stuff Error */
+#define TD_CTRL_ACTLEN_MASK	0x7ff	/* actual length, encoded as n - 1 */
+
+#define TD_CTRL_ANY_ERROR	(TD_CTRL_STALLED | TD_CTRL_DBUFERR | \
+				 TD_CTRL_BABBLE | TD_CTRL_CRCTIME | TD_CTRL_BITSTUFF)
+
+#define uhci_status_bits(ctrl_sts)	(ctrl_sts & 0xFE0000)
+#define uhci_actual_length(ctrl_sts)	((ctrl_sts + 1) & TD_CTRL_ACTLEN_MASK)	/* 1-based */
+#define uhci_ptr_to_virt(x)	bus_to_virt(x & ~UHCI_PTR_BITS)
+
+/*
+ * for TD <flags>:
+ */
+#define UHCI_TD_REMOVE		0x0001	/* Remove when done */
+
+/*
+ * for TD <info>: (a.k.a. Token)
+ */
+#define TD_TOKEN_TOGGLE		19
+
+#define uhci_maxlen(token)	((token) >> 21)
+#define uhci_toggle(token)	(((token) >> TD_TOKEN_TOGGLE) & 1)
+#define uhci_endpoint(token)	(((token) >> 15) & 0xf)
+#define uhci_devaddr(token)	(((token) >> 8) & 0x7f)
+#define uhci_devep(token)	(((token) >> 8) & 0x7ff)
+#define uhci_packetid(token)	((token) & 0xff)
+#define uhci_packetout(token)	(uhci_packetid(token) != USB_PID_IN)
+#define uhci_packetin(token)	(uhci_packetid(token) == USB_PID_IN)
+
+/* ------------------------------------------------------------------------------------
+   New TD/QH-structures
+   ------------------------------------------------------------------------------------ */
+typedef enum {
+	TD_TYPE, QH_TYPE
+} uhci_desc_type_t;
+
+typedef struct {
+	__u32 link;
+	__u32 status;
+	__u32 info;
+	__u32 buffer;
+} uhci_td_t, *puhci_td_t;
+
+typedef struct {
+	__u32 head;
+	__u32 element;		/* Queue element pointer */
+} uhci_qh_t, *puhci_qh_t;
+
+typedef struct {
+	union {
+		uhci_td_t td;
+		uhci_qh_t qh;
+	} hw;
+	uhci_desc_type_t type;
+	dma_addr_t dma_addr;
+	struct list_head horizontal;
+	struct list_head vertical;
+	struct list_head desc_list;
+	int last_used;
+} uhci_desc_t, *puhci_desc_t;
+
+typedef struct {
+	struct list_head desc_list;	// list pointer to all corresponding TDs/QHs associated with this request
+	dma_addr_t setup_packet_dma;
+	dma_addr_t transfer_buffer_dma;
+	unsigned long started;
+	struct urb *next_queued_urb;	// next queued urb for this EP
+	struct urb *prev_queued_urb;
+	uhci_desc_t *bottom_qh;
+	uhci_desc_t *next_qh;       	// next helper QH
+	char use_loop;
+	char flags;
+} urb_priv_t, *purb_priv_t;
+
+struct virt_root_hub {
+	int devnum;		/* Address of Root Hub endpoint */
+	void *urb;
+	void *int_addr;
+	int send;
+	int interval;
+	int numports;
+	int c_p_r[8];
+	struct timer_list rh_int_timer;
+};
+
+typedef struct uhci {
+	int irq;
+	unsigned int io_addr;
+	unsigned int io_size;
+	unsigned int maxports;
+	int running;
+
+	int apm_state;
+
+	struct uhci *next;	// chain of uhci device contexts
+
+	struct list_head urb_list;	// list of all pending urbs
+
+	spinlock_t urb_list_lock;	// lock to keep consistency 
+
+	int unlink_urb_done;
+	atomic_t avoid_bulk;
+	
+	struct usb_bus *bus;	// our bus
+
+	__u32 *framelist;
+	dma_addr_t framelist_dma;
+	uhci_desc_t **iso_td;
+	uhci_desc_t *int_chain[8];
+	uhci_desc_t *ls_control_chain;
+	uhci_desc_t *control_chain;
+	uhci_desc_t *bulk_chain;
+	uhci_desc_t *chain_end;
+	uhci_desc_t *td1ms;
+	uhci_desc_t *td32ms;
+	struct list_head free_desc;
+	spinlock_t qh_lock;
+	spinlock_t td_lock;
+	struct virt_root_hub rh;	//private data of the virtual root hub
+	int loop_usage;            // URBs using bandwidth reclamation
+
+	struct list_head urb_unlinked;	// list of all unlinked  urbs
+	long timeout_check;
+	int timeout_urbs;
+	struct pci_dev *uhci_pci;
+	struct pci_pool *desc_pool;
+	long last_error_time;          // last error output in uhci_interrupt()
+} uhci_t, *puhci_t;
+
+
+#define MAKE_TD_ADDR(a) ((a)->dma_addr&~UHCI_PTR_QH)
+#define MAKE_QH_ADDR(a) ((a)->dma_addr|UHCI_PTR_QH)
+#define UHCI_GET_CURRENT_FRAME(uhci) (inw ((uhci)->io_addr + USBFRNUM))
+
+#define CLEAN_TRANSFER_NO_DELETION 0
+#define CLEAN_TRANSFER_REGULAR 1
+#define CLEAN_TRANSFER_DELETION_MARK 2
+
+#define CLEAN_NOT_FORCED 0
+#define CLEAN_FORCED 1
+
+#define PROCESS_ISO_REGULAR 0
+#define PROCESS_ISO_FORCE 1
+
+#define UNLINK_ASYNC_STORE_URB 0
+#define UNLINK_ASYNC_DONT_STORE 1
+
+#define is_td_active(desc) (desc->hw.td.status & cpu_to_le32(TD_CTRL_ACTIVE))
+
+#define set_qh_head(desc,val) (desc)->hw.qh.head=cpu_to_le32(val)
+#define set_qh_element(desc,val) (desc)->hw.qh.element=cpu_to_le32(val)
+#define set_td_link(desc,val) (desc)->hw.td.link=cpu_to_le32(val)
+#define set_td_ioc(desc) (desc)->hw.td.status |= cpu_to_le32(TD_CTRL_IOC)
+#define clr_td_ioc(desc) (desc)->hw.td.status &= cpu_to_le32(~TD_CTRL_IOC)
+
+
+/* ------------------------------------------------------------------------------------ 
+   Virtual Root HUB 
+   ------------------------------------------------------------------------------------ */
+/* destination of request */
+#define RH_INTERFACE               0x01
+#define RH_ENDPOINT                0x02
+#define RH_OTHER                   0x03
+
+#define RH_CLASS                   0x20
+#define RH_VENDOR                  0x40
+
+/* 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
+/* Our Vendor Specific Request */
+#define RH_SET_EP               0x2000
+
+
+/* Hub port features */
+#define RH_PORT_CONNECTION         0x00
+#define RH_PORT_ENABLE             0x01
+#define RH_PORT_SUSPEND            0x02
+#define RH_PORT_OVER_CURRENT       0x03
+#define RH_PORT_RESET              0x04
+#define RH_PORT_POWER              0x08
+#define RH_PORT_LOW_SPEED          0x09
+#define RH_C_PORT_CONNECTION       0x10
+#define RH_C_PORT_ENABLE           0x11
+#define RH_C_PORT_SUSPEND          0x12
+#define RH_C_PORT_OVER_CURRENT     0x13
+#define RH_C_PORT_RESET            0x14
+
+/* Hub features */
+#define RH_C_HUB_LOCAL_POWER       0x00
+#define RH_C_HUB_OVER_CURRENT      0x01
+
+#define RH_DEVICE_REMOTE_WAKEUP    0x00
+#define RH_ENDPOINT_STALL          0x01
+
+/* Our Vendor Specific feature */
+#define RH_REMOVE_EP               0x00
+
+
+#define RH_ACK                     0x01
+#define RH_REQ_ERR                 -1
+#define RH_NACK                    0x00
+
+#endif
diff -urN linux-2.4.21-pre5/drivers/usb/hpusbscsi.c linux-2.4.21-pre6/drivers/usb/hpusbscsi.c
--- linux-2.4.21-pre5/drivers/usb/hpusbscsi.c	Wed Mar 26 16:11:29 2003
+++ linux-2.4.21-pre6/drivers/usb/hpusbscsi.c	Wed Mar 26 16:11:39 2003
@@ -127,6 +127,9 @@
 	init_waitqueue_head (&new->deathrow);
 	init_MUTEX(&new->lock);
 	INIT_LIST_HEAD (&new->lh);
+	
+	if (id->idVendor == 0x0686 && id->idProduct == 0x4004)
+		new->need_short_workaround = 1;
 
 
 
@@ -340,7 +343,7 @@
 {
 	struct hpusbscsi* hpusbscsi = (struct hpusbscsi*)(srb->host->hostdata[0]);
 	usb_urb_callback usb_callback;
-	int res;
+	int res, passed_length;
 
 	spin_unlock_irq(&io_request_lock);
 
@@ -359,6 +362,19 @@
 		callback(srb);
 		goto out;
 	}
+	
+	/* otto fix - the Scan Elite II has a 5 second
+	* delay anytime the srb->cmd_len=6
+	* This causes it to run very slowly unless we
+	* pad the command length to 10 */
+        
+	if (hpusbscsi -> need_short_workaround && srb->cmd_len < 10) {
+		memset(srb->cmnd + srb->cmd_len, 0, 10 - srb->cmd_len);
+		passed_length = 10;
+	} else {
+		passed_length = srb->cmd_len;
+	}
+        
 
 	/* Now we need to decide which callback to give to the urb we send the command with */
 
@@ -404,7 +420,7 @@
 		hpusbscsi->dev,
 		usb_sndbulkpipe(hpusbscsi->dev,hpusbscsi->ep_out),
 		srb->cmnd,
-		srb->cmd_len,
+		passed_length,
 		usb_callback,
 		hpusbscsi
 	);
diff -urN linux-2.4.21-pre5/drivers/usb/hpusbscsi.h linux-2.4.21-pre6/drivers/usb/hpusbscsi.h
--- linux-2.4.21-pre5/drivers/usb/hpusbscsi.h	Wed Mar 26 16:11:29 2003
+++ linux-2.4.21-pre6/drivers/usb/hpusbscsi.h	Wed Mar 26 16:11:39 2003
@@ -20,6 +20,7 @@
         int ep_in;
         int ep_int;
         int interrupt_interval;
+	int need_short_workaround;
 
         struct Scsi_Host *host;
         Scsi_Host_Template ctempl;
diff -urN linux-2.4.21-pre5/drivers/usb/kaweth.c linux-2.4.21-pre6/drivers/usb/kaweth.c
--- linux-2.4.21-pre5/drivers/usb/kaweth.c	Wed Mar 26 16:11:29 2003
+++ linux-2.4.21-pre6/drivers/usb/kaweth.c	Wed Mar 26 16:11:39 2003
@@ -143,6 +143,7 @@
 	{ USB_DEVICE(0x06e1, 0x0008) }, /* ADS USB-10BT */
 	{ USB_DEVICE(0x06e1, 0x0009) }, /* ADS USB-10BT */
 	{ USB_DEVICE(0x0707, 0x0100) }, /* SMC 2202USB */
+	{ USB_DEVICE(0x0785, 0x0002) }, /* NTT-TE MN128 USB-Ethernet Adapter */
 	{ USB_DEVICE(0x07aa, 0x0001) }, /* Correga K.K. */
 	{ USB_DEVICE(0x07b8, 0x4000) }, /* D-Link DU-E10 */
 	{ USB_DEVICE(0x0846, 0x1001) }, /* NetGear EA-101 */
diff -urN linux-2.4.21-pre5/drivers/usb/kbtab.c linux-2.4.21-pre6/drivers/usb/kbtab.c
--- linux-2.4.21-pre5/drivers/usb/kbtab.c	Wed Dec 31 16:00:00 1969
+++ linux-2.4.21-pre6/drivers/usb/kbtab.c	Wed Mar 26 16:11:39 2003
@@ -0,0 +1,179 @@
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/usb.h>
+
+/*
+ * Version Information
+ */
+#define DRIVER_VERSION "v0.0.1"
+#define DRIVER_AUTHOR "Josh Myer <josh@joshisanerd.com>"
+#define DRIVER_DESC "KB Gear Jam Studio Tablet Driver"
+
+MODULE_AUTHOR( DRIVER_AUTHOR );
+MODULE_DESCRIPTION( DRIVER_DESC );
+MODULE_LICENSE("GPL");
+
+#define USB_VENDOR_ID_KBTAB 0x84e
+
+static int       kb_pressure_click = 0x10;
+MODULE_PARM     (kb_pressure_click,"i");
+MODULE_PARM_DESC(kb_pressure_click,
+		 "pressure threshold for clicks");
+
+struct kbtab {
+	signed char data[8];
+	struct input_dev dev;
+	struct usb_device *usbdev;
+	struct urb irq;
+	int open;
+	int x, y;
+	int button;
+	int pressure;
+};
+
+static void kbtab_irq(struct urb *urb)
+{
+
+	struct kbtab *tab = urb->context;
+	unsigned char *data = tab->data;
+	struct input_dev *dev = &tab->dev;
+
+	if(urb->status)
+		return;
+
+	tab->x = (data[2] << 8) + data[1];
+	tab->y = (data[4] << 8) + data[3];
+
+	tab->pressure = (data[5]);
+
+	/* XXX: don't report unless actual change */
+
+	input_report_abs(dev, ABS_X, tab->x);
+	input_report_abs(dev, ABS_Y, tab->y);
+	input_report_abs(dev, ABS_PRESSURE, tab->pressure);
+
+	input_report_key(dev, BTN_STYLUS, (data[0] & 2));
+	input_report_key(dev, BTN_TOUCH, (data[0] & 1));
+	input_report_key(dev, BTN_LEFT, (tab->pressure > kb_pressure_click) ? 1 : 0);
+
+	input_event(dev, EV_MSC, MSC_SERIAL, 0);
+}
+
+struct usb_device_id kbtab_ids[] = {
+	{ USB_DEVICE(USB_VENDOR_ID_KBTAB, 0x1001), driver_info : 0 },
+	{ }
+};
+  
+MODULE_DEVICE_TABLE(usb, kbtab_ids);
+
+static int kbtab_open(struct input_dev *dev)
+{
+	struct kbtab *kbtab = dev->private;
+
+	if(kbtab->open++)
+		return 0;
+
+	kbtab->irq.dev = kbtab->usbdev;
+	if(usb_submit_urb(&kbtab->irq))
+		return -EIO;
+
+	return 0;
+}
+
+static void kbtab_close(struct input_dev *dev)
+{
+	struct kbtab *kbtab = dev->private;
+
+	if(!--kbtab->open)
+		usb_unlink_urb(&kbtab->irq);
+}
+
+static void *kbtab_probe(struct usb_device *dev, unsigned int ifnum, const struct usb_device_id *id)
+{
+	struct usb_endpoint_descriptor *endpoint;
+	struct kbtab *kbtab;
+
+	if(!(kbtab = kmalloc(sizeof(struct kbtab), GFP_KERNEL)))
+		return NULL;
+
+	memset(kbtab, 0, sizeof(struct kbtab));
+
+	kbtab->dev.evbit[0] |= BIT(EV_KEY) | BIT(EV_ABS) | BIT(EV_MSC);
+	kbtab->dev.absbit[0] |= BIT(ABS_X) | BIT(ABS_Y) | BIT(ABS_PRESSURE);
+
+	kbtab->dev.keybit[LONG(BTN_LEFT)] |= BIT(BTN_LEFT) | BIT(BTN_RIGHT) | BIT(BTN_MIDDLE);
+	kbtab->dev.keybit[LONG(BTN_DIGI)] |= BIT(BTN_STYLUS);
+
+	kbtab->dev.mscbit[0] |= BIT(MSC_SERIAL);
+
+	kbtab->dev.absmax[ABS_X] = 0x2000;
+	kbtab->dev.absmax[ABS_Y] = 0x1750;
+
+	kbtab->dev.absmax[ABS_PRESSURE] = 0xff;
+  
+	kbtab->dev.absfuzz[ABS_X] = 4;
+	kbtab->dev.absfuzz[ABS_Y] = 4;
+  
+	kbtab->dev.private = kbtab;
+
+	kbtab->dev.open = kbtab_open;
+	kbtab->dev.close = kbtab_close;
+
+	kbtab->dev.name = "KB Gear Tablet";
+	kbtab->dev.idbus = BUS_USB;
+  
+	kbtab->dev.idvendor = dev->descriptor.idVendor;
+	kbtab->dev.idproduct = dev->descriptor.idProduct;
+	kbtab->dev.idversion = dev->descriptor.bcdDevice;
+	kbtab->usbdev = dev;
+
+
+	endpoint = dev->config[0].interface[ifnum].altsetting[0].endpoint + 0;
+
+	usb_set_idle(dev, dev->config[0].interface[ifnum].altsetting[0].bInterfaceNumber, 0, 0);
+
+	FILL_INT_URB(&kbtab->irq, dev, usb_rcvintpipe(dev, endpoint->bEndpointAddress),
+		     kbtab->data, 8, kbtab_irq, kbtab, endpoint->bInterval);
+
+	input_register_device(&kbtab->dev);
+
+	printk(KERN_INFO "input%d: KB Gear Tablet on usb%d:%d.%d\n",
+	       kbtab->dev.number, dev->bus->busnum, dev->devnum, ifnum);
+
+	return kbtab;
+
+}
+
+static void kbtab_disconnect(struct usb_device *dev, void *ptr)
+{
+	struct kbtab *kbtab = ptr;
+	usb_unlink_urb(&kbtab->irq);
+	input_unregister_device(&kbtab->dev);
+	kfree(kbtab);
+}
+
+static struct usb_driver kbtab_driver = {
+	name:		"kbtab",
+	probe:		kbtab_probe,
+	disconnect:	kbtab_disconnect,
+	id_table:	kbtab_ids,
+};
+
+static int __init kbtab_init(void)
+{
+	usb_register(&kbtab_driver);
+	info(DRIVER_VERSION " " DRIVER_AUTHOR);
+	info(DRIVER_DESC);
+	return 0;
+}
+
+static void __exit kbtab_exit(void)
+{
+	usb_deregister(&kbtab_driver);
+}
+
+module_init(kbtab_init);
+module_exit(kbtab_exit);
diff -urN linux-2.4.21-pre5/drivers/usb/pegasus.h linux-2.4.21-pre6/drivers/usb/pegasus.h
--- linux-2.4.21-pre5/drivers/usb/pegasus.h	Wed Mar 26 16:11:29 2003
+++ linux-2.4.21-pre6/drivers/usb/pegasus.h	Wed Mar 26 16:11:39 2003
@@ -232,6 +232,8 @@
 		DEFAULT_GPIO_RESET )
 PEGASUS_DEV( "LANEED USB Ethernet LD-USB/TX", VENDOR_LANEED, 0x200c,
 		DEFAULT_GPIO_RESET | PEGASUS_II )
+PEGASUS_DEV( "LANEED USB Ethernet LD-USBL/TX", VENDOR_LANEED, 0x4005,
+		DEFAULT_GPIO_RESET | PEGASUS_II )
 PEGASUS_DEV( "Linksys USB10TX", VENDOR_LINKSYS, 0x2202,
 		LINKSYS_GPIO_RESET )
 PEGASUS_DEV( "Linksys USB100TX", VENDOR_LINKSYS, 0x2203,
diff -urN linux-2.4.21-pre5/drivers/usb/scanner.c linux-2.4.21-pre6/drivers/usb/scanner.c
--- linux-2.4.21-pre5/drivers/usb/scanner.c	Wed Mar 26 16:11:29 2003
+++ linux-2.4.21-pre6/drivers/usb/scanner.c	Wed Mar 26 16:11:39 2003
@@ -334,6 +334,12 @@
  *      <oliver@neukum.name>.
  *
  * 0.4.10  01/07/2003
+ *    - Added vendor/product ids for Artec, Canon, Compaq, Epson, HP, Microtek 
+ *      and Visioneer scanners. Thanks to William Lam <wklam@triad.rr.com>,
+ *      Till Kamppeter <till.kamppeter@gmx.net> and others for all the ids.
+ *    - Cleaned up list of vendor/product ids.
+ *    - Print ids and device number when a device was detected.
+ *    - Don't print errors when the device is busy.
  *    - Added vendor/product ids for Visioneer scanners.
  *    - Print information about user-supplied ids only once at startup instead
  *      of everytime any USB device is plugged in.
@@ -346,6 +352,10 @@
  *    - Print ids and device number when a device was detected.
  *    - Don't print errors when the device is busy.
  *      
+ * 0.4.11  2003-02-25
+ *    - Added vendor/product ids for Artec, Avision, Brother, Medion, Primax,
+ *      Prolink, Fujitsu, Plustek, and SYSCAN scanners.
+ *
  * TODO
  *    - Performance
  *    - Select/poll methods
diff -urN linux-2.4.21-pre5/drivers/usb/scanner.h linux-2.4.21-pre6/drivers/usb/scanner.h
--- linux-2.4.21-pre5/drivers/usb/scanner.h	Wed Mar 26 16:11:29 2003
+++ linux-2.4.21-pre6/drivers/usb/scanner.h	Wed Mar 26 16:11:39 2003
@@ -44,7 +44,7 @@
 
 // #define DEBUG
 
-#define DRIVER_VERSION "0.4.10"
+#define DRIVER_VERSION "0.4.11"
 #define DRIVER_DESC "USB Scanner Driver"
 
 #include <linux/usb.h>
@@ -71,79 +71,102 @@
 
 static struct usb_device_id scanner_device_ids [] = {
 	/* Acer (now Benq) */
-	{ USB_DEVICE(0x04a5, 0x2060) },	/* 620U & 640U (!)*/
-	{ USB_DEVICE(0x04a5, 0x2040) },	/* 620U (!) */
-	{ USB_DEVICE(0x04a5, 0x20c0) }, /* 1240UT, 1240U */
-	{ USB_DEVICE(0x04a5, 0x2022) },	/* 340U */
 	{ USB_DEVICE(0x04a5, 0x1a20) },	/* Unknown - Oliver Schwartz */
 	{ USB_DEVICE(0x04a5, 0x1a2a) },	/* Another 620U */
+	{ USB_DEVICE(0x04a5, 0x2022) },	/* 340U */
+	{ USB_DEVICE(0x04a5, 0x2040) },	/* 620U (!) */
+	{ USB_DEVICE(0x04a5, 0x2060) },	/* 620U & 640U (!)*/
 	{ USB_DEVICE(0x04a5, 0x207e) },	/* 640BU */
+	{ USB_DEVICE(0x04a5, 0x20b0) },	/* Benq 4300 */
 	{ USB_DEVICE(0x04a5, 0x20be) },	/* Unknown - Oliver Schwartz */
+	{ USB_DEVICE(0x04a5, 0x20c0) }, /* 1240UT, 1240U */
 	{ USB_DEVICE(0x04a5, 0x20de) },	/* S2W 3300U */
-	{ USB_DEVICE(0x04a5, 0x20b0) },	/* Benq 4300 */
-	{ USB_DEVICE(0x04a5, 0x20fe) },	/* Benq 5300 */
 	{ USB_DEVICE(0x04a5, 0x20fc) }, /* Benq 5000 */
+	{ USB_DEVICE(0x04a5, 0x20fe) },	/* Benq 5300 */
 	/* Agfa */
 	{ USB_DEVICE(0x06bd, 0x0001) },	/* SnapScan 1212U */
 	{ USB_DEVICE(0x06bd, 0x0002) },	/* SnapScan 1236U */
-	{ USB_DEVICE(0x06bd, 0x2061) },	/* Another SnapScan 1212U (?)*/
 	{ USB_DEVICE(0x06bd, 0x0100) },	/* SnapScan Touch */
+	{ USB_DEVICE(0x06bd, 0x2061) },	/* Another SnapScan 1212U (?)*/
+	{ USB_DEVICE(0x06bd, 0x208d) }, /* Snapscan e40 */
+	{ USB_DEVICE(0x06bd, 0x208f) }, /* SnapScan e50*/
 	{ USB_DEVICE(0x06bd, 0x2091) }, /* SnapScan e20 */
+	{ USB_DEVICE(0x06bd, 0x2093) }, /* SnapScan e10*/
 	{ USB_DEVICE(0x06bd, 0x2095) }, /* SnapScan e25 */
 	{ USB_DEVICE(0x06bd, 0x2097) }, /* SnapScan e26 */
-	{ USB_DEVICE(0x06bd, 0x208d) }, /* Snapscan e40 */
-	{ USB_DEVICE(0x06bd, 0x2093) }, /* SnapScan e10*/
-	{ USB_DEVICE(0x06bd, 0x20ff) }, /* SnapScan e42*/
-	{ USB_DEVICE(0x06bd, 0x208f) }, /* SnapScan e50*/
 	{ USB_DEVICE(0x06bd, 0x20fd) }, /* SnapScan e52*/
+	{ USB_DEVICE(0x06bd, 0x20ff) }, /* SnapScan e42*/
+	/* Artec */
+	{ USB_DEVICE(0x05d8, 0x4001) },	/* Ultima 2000 */
+	{ USB_DEVICE(0x05d8, 0x4002) }, /* Ultima 2000 (GT6801 based) */
+	{ USB_DEVICE(0x05d8, 0x4003) }, /* E+ 48U */
+	{ USB_DEVICE(0x05d8, 0x4004) }, /* E+ Pro */
+	/* Avision */
+	{ USB_DEVICE(0x0638, 0x0a10) },	/* iVina FB1600 (=Umax Astra 4500) */
 	/* Benq: see Acer */
+	/* Brother */
+	{ USB_DEVICE(0x04f9, 0x0111) },	/* MFC 6800 */
 	/* Canon */
-	{ USB_DEVICE(0x04a9, 0x2201) }, /* FB320U */
-	{ USB_DEVICE(0x04a9, 0x2205) }, /* FB1210U */
+	{ USB_DEVICE(0x04a9, 0x2201) }, /* CanoScan FB320U */
 	{ USB_DEVICE(0x04a9, 0x2202) }, /* CanoScan FB620U */
 	{ USB_DEVICE(0x04a9, 0x2204) }, /* CanoScan FB630U/FB636U */
+	{ USB_DEVICE(0x04a9, 0x2205) }, /* CanoScan FB1210U */
 	{ USB_DEVICE(0x04a9, 0x2206) }, /* CanoScan N650U/N656U */
 	{ USB_DEVICE(0x04a9, 0x2207) }, /* CanoScan N1220U */
 	{ USB_DEVICE(0x04a9, 0x2208) }, /* CanoScan D660U */ 
-	{ USB_DEVICE(0x04a9, 0x220b) }, /* D646U */
+	{ USB_DEVICE(0x04a9, 0x220b) }, /* CanoScan D646U */
+	{ USB_DEVICE(0x04a9, 0x220c) },	/* CanoScan D1250U2 */
 	{ USB_DEVICE(0x04a9, 0x220d) }, /* CanoScan N670U/N676U/LIDE 20 */
 	{ USB_DEVICE(0x04a9, 0x220e) }, /* CanoScan N1240U/LIDE 30 */
 	{ USB_DEVICE(0x04a9, 0x3042) }, /* FS4000US */
 	/* Colorado -- See Primax/Colorado below */
+	/* Compaq */
+	{ USB_DEVICE(0x049f, 0x0021) },	/* S200 */
 	/* Epson -- See Seiko/Epson below */
+	/* Fujitsu */
+	{ USB_DEVICE(0x04c5, 0x1041) }, /* fi-4220c USB/SCSI info:mza@mu-tec.de */
+	{ USB_DEVICE(0x04c5, 0x1042) }, /* fi-4120c USB/SCSI info:mza@mu-tec.de */
+	{ USB_DEVICE(0x04c5, 0x1029) }, /* fi-4010c USB AVision info:mza@mu-tec.de */
 	/* Genius */
-	{ USB_DEVICE(0x0458, 0x2001) },	/* ColorPage-Vivid Pro */
+	{ USB_DEVICE(0x0458, 0x2001) },	/* ColorPage Vivid Pro */
 	{ USB_DEVICE(0x0458, 0x2007) },	/* ColorPage HR6 V2 */
-	{ USB_DEVICE(0x0458, 0x2008) }, /* ColorPage-HR6 V2 */
-	{ USB_DEVICE(0x0458, 0x2009) }, /* ColorPage-HR6A */
-	{ USB_DEVICE(0x0458, 0x2011) }, /* ColorPage-Vivid3x */
-	{ USB_DEVICE(0x0458, 0x2013) }, /* ColorPage-HR7 */
-        { USB_DEVICE(0x0458, 0x2015) }, /* ColorPage-HR7LE */
-	{ USB_DEVICE(0x0458, 0x2016) }, /* ColorPage-HR6X */
+	{ USB_DEVICE(0x0458, 0x2008) }, /* ColorPage HR6 V2 */
+	{ USB_DEVICE(0x0458, 0x2009) }, /* ColorPage HR6A */
+	{ USB_DEVICE(0x0458, 0x2011) }, /* ColorPage Vivid3x */
+	{ USB_DEVICE(0x0458, 0x2013) }, /* ColorPage HR7 */
+	{ USB_DEVICE(0x0458, 0x2015) }, /* ColorPage HR7LE */
+	{ USB_DEVICE(0x0458, 0x2016) }, /* ColorPage HR6X */
 	/* Hewlett Packard */
-	{ USB_DEVICE(0x03f0, 0x0505) }, /* ScanJet 2100C */
-	{ USB_DEVICE(0x03f0, 0x0605) },	/* 2200C */
-	{ USB_DEVICE(0x03f0, 0x0901) }, /* 2300C */
-	{ USB_DEVICE(0x03f0, 0x0205) },	/* 3300C */
-	{ USB_DEVICE(0x03f0, 0x0405) }, /* 3400C */
-	{ USB_DEVICE(0x03f0, 0x0101) },	/* 4100C */
-	{ USB_DEVICE(0x03f0, 0x0105) },	/* 4200C */
-	{ USB_DEVICE(0x03f0, 0x0305) }, /* 4300C */
+	{ USB_DEVICE(0x03f0, 0x0101) },	/* ScanJet 4100C */
 	{ USB_DEVICE(0x03f0, 0x0102) },	/* PhotoSmart S20 */
-	{ USB_DEVICE(0x03f0, 0x0705) }, /* 4400C */
-	{ USB_DEVICE(0x03f0, 0x0401) },	/* 5200C */
-	//	{ USB_DEVICE(0x03f0, 0x0701) },	/* 5300C - NOT SUPPORTED - see http://www.neatech.nl/oss/HP5300C/ */
-	{ USB_DEVICE(0x03f0, 0x0201) },	/* 6200C */
-	{ USB_DEVICE(0x03f0, 0x0601) },	/* 6300C */
+	{ USB_DEVICE(0x03f0, 0x0105) },	/* ScanJet 4200C */
+	{ USB_DEVICE(0x03f0, 0x0201) },	/* ScanJet 6200C */
+	{ USB_DEVICE(0x03f0, 0x0205) },	/* ScanJet 3300C */
+	{ USB_DEVICE(0x03f0, 0x0305) }, /* ScanJet 4300C */
+	{ USB_DEVICE(0x03f0, 0x0401) },	/* ScanJet 5200C */
+	{ USB_DEVICE(0x03f0, 0x0405) }, /* ScanJet 3400C */
+	{ USB_DEVICE(0x03f0, 0x0505) }, /* ScanJet 2100C */
+	{ USB_DEVICE(0x03f0, 0x0601) },	/* ScanJet 6300C */
+	{ USB_DEVICE(0x03f0, 0x0605) },	/* ScanJet 2200C */
+	//	{ USB_DEVICE(0x03f0, 0x0701) },	/* ScanJet 5300C - NOT SUPPORTED - use hpusbscsi driver */
+	{ USB_DEVICE(0x03f0, 0x0705) }, /* ScanJet 4400C */
+	//	{ USB_DEVICE(0x03f0, 0x0801) },	/* ScanJet 7400C - NOT SUPPORTED - use hpusbscsi driver */
+	{ USB_DEVICE(0x03f0, 0x0901) }, /* ScanJet 2300C */
+	{ USB_DEVICE(0x03f0, 0x1305) },	/* Scanjet 4570c */
+	{ USB_DEVICE(0x03f0, 0x2005) },	/* ScanJet 3570c */
+	{ USB_DEVICE(0x03f0, 0x2205) },	/* ScanJet 3500c */
 	/* iVina */
 	{ USB_DEVICE(0x0638, 0x0268) }, /* 1200U */
 	/* Lexmark */
 	{ USB_DEVICE(0x043d, 0x002d) }, /* X70/X73 */
-	/* Lifetec */
-	{ USB_DEVICE(0x05d8, 0x4002) }, /* Lifetec LT9385 */
+	/* Medion */
+	{ USB_DEVICE(0x0461, 0x0377) },	/* MD 5345 - repackaged Primax? */
 	/* Memorex */
 	{ USB_DEVICE(0x0461, 0x0346) }, /* 6136u - repackaged Primax ? */
-	/* Microtek -- No longer supported - Enable SCSI and USB Microtek in kernel config */
+	/* Microtek */
+	{ USB_DEVICE(0x05da, 0x30ce) },	/* ScanMaker 3800 */
+	/* The following SCSI-over-USB Microtek devices are supported by the
+	   microtek driver: Enable SCSI and USB Microtek in kernel config */
 	//	{ USB_DEVICE(0x05da, 0x0099) },	/* ScanMaker X6 - X6U */
 	//	{ USB_DEVICE(0x05da, 0x0094) },	/* Phantom 336CX - C3 */
 	//	{ USB_DEVICE(0x05da, 0x00a0) },	/* Phantom 336CX - C3 #2 */
@@ -152,58 +175,63 @@
 	//	{ USB_DEVICE(0x05da, 0x80a3) },	/* ScanMaker V6USL #2 */
 	//	{ USB_DEVICE(0x05da, 0x80ac) },	/* ScanMaker V6UL - SpicyU */
 	/* Minolta */
-	//	{ USB_DEVICE(0x0638,0x026a) }, /* Minolta Dimage Scan Dual II */
+	{ USB_DEVICE(0x0686, 0x400d) }, /* Scan Dual III */
+	/* The following SCSI-over-USB Minolta devices are supported by the
+	   hpusbscsi driver: Enable SCSI and USB hpusbscsi in kernel config */
+	//	{ USB_DEVICE(0x0638, 0x026a) }, /* Minolta Dimage Scan Dual II */
 	//	{ USB_DEVICE(0x0686, 0x4004) }, /* Scan Elite II (need interrupt ep) */
-	{ USB_DEVICE(0x0686, 0x400d) }, /* Scan Dual III  */
 	/* Mustek */
+	{ USB_DEVICE(0x0400, 0x1000) },	/* BearPaw 1200 (National Semiconductor LM9831) */
+	{ USB_DEVICE(0x0400, 0x1001) }, /* BearPaw 2400 (National Semiconductor LM9832) */
 	{ USB_DEVICE(0x055f, 0x0001) },	/* ScanExpress 1200 CU */
-	{ USB_DEVICE(0x0400, 0x1000) },	/* BearPaw 1200 */
 	{ USB_DEVICE(0x055f, 0x0002) },	/* ScanExpress 600 CU */
-	{ USB_DEVICE(0x055f, 0x0873) }, /* ScanExpress 600 USB */
 	{ USB_DEVICE(0x055f, 0x0003) },	/* ScanExpress 1200 USB */
 	{ USB_DEVICE(0x055f, 0x0006) },	/* ScanExpress 1200 UB */
 	{ USB_DEVICE(0x055f, 0x0007) },	/* ScanExpress 1200 USB Plus */
-	{ USB_DEVICE(0x055f, 0x0210) },	/* ScanExpress A3 USB */
-	{ USB_DEVICE(0x0400, 0x1001) }, /* BearPaw 2400 */
 	{ USB_DEVICE(0x055f, 0x0008) }, /* ScanExpress 1200 CU Plus */
 	{ USB_DEVICE(0x055f, 0x0010) }, /* BearPaw 1200F */
+	{ USB_DEVICE(0x055f, 0x0210) },	/* ScanExpress A3 USB */
 	{ USB_DEVICE(0x055f, 0x0218) }, /* BearPaw 2400 TA */
-	{ USB_DEVICE(0x05d8, 0x4002) }, /* BearPaw 1200 CU and ScanExpress 1200 UB Plus */
 	{ USB_DEVICE(0x055f, 0x0219) }, /* BearPaw 2400 TA Plus */
 	{ USB_DEVICE(0x055f, 0x021c) }, /* BearPaw 1200 CU Plus */
 	{ USB_DEVICE(0x055f, 0x021d) }, /* Bearpaw 2400 CU Plus */
 	{ USB_DEVICE(0x055f, 0x021e) }, /* BearPaw 1200 TA/CS */
 	{ USB_DEVICE(0x055f, 0x0400) }, /* BearPaw 2400 TA PRO */
+	{ USB_DEVICE(0x055f, 0x0873) }, /* ScanExpress 600 USB */
 	{ USB_DEVICE(0x055f, 0x1000) }, /* BearPaw 4800 TA PRO */
+	//	{ USB_DEVICE(0x05d8, 0x4002) }, /* BearPaw 1200 CU and ScanExpress 1200 UB Plus (see Artec) */
 	/* Nikon */
-	{ USB_DEVICE(0x04b0, 0x4000) },  /* Coolscan LS 40 ED */
+	{ USB_DEVICE(0x04b0, 0x4000) }, /* Coolscan LS 40 ED */
 	/* Plustek */
-	{ USB_DEVICE(0x07b3, 0x0017) }, /* OpticPro UT12/UT16/UT24 */
-	{ USB_DEVICE(0x07b3, 0x0011) }, /* OpticPro U24 */
-	{ USB_DEVICE(0x07b3, 0x0010) }, /* OpticPro U12 */
-	{ USB_DEVICE(0x07b3, 0x0015) }, /* OpticPro U24 */
 	{ USB_DEVICE(0x07b3, 0x0005) }, /* Unknown */
 	{ USB_DEVICE(0x07b3, 0x0007) }, /* Unknown */
 	{ USB_DEVICE(0x07b3, 0x000F) }, /* Unknown */
+	{ USB_DEVICE(0x07b3, 0x0010) }, /* OpticPro U12 */
+	{ USB_DEVICE(0x07b3, 0x0011) }, /* OpticPro U24 */
 	{ USB_DEVICE(0x07b3, 0x0012) }, /* Unknown */
 	{ USB_DEVICE(0x07b3, 0x0013) }, /* Unknown */
 	{ USB_DEVICE(0x07b3, 0x0014) }, /* Unknown */
+	{ USB_DEVICE(0x07b3, 0x0015) }, /* OpticPro U24 */
 	{ USB_DEVICE(0x07b3, 0x0016) }, /* Unknown */
-	{ USB_DEVICE(0x07b3, 0x0012) }, /* Unknown */
-	{ USB_DEVICE(0x07b3, 0x0401) }, /* OpticPro 1248U */
+	{ USB_DEVICE(0x07b3, 0x0017) }, /* OpticPro UT12/UT16/UT24 */
+	{ USB_DEVICE(0x07b3, 0x0400) }, /* OpticPro 1248U */
+	{ USB_DEVICE(0x07b3, 0x0401) }, /* OpticPro 1248U (another one) */
 	/* Primax/Colorado */
 	{ USB_DEVICE(0x0461, 0x0300) },	/* G2-300 #1 */
-	{ USB_DEVICE(0x0461, 0x0380) },	/* G2-600 #1 */
 	{ USB_DEVICE(0x0461, 0x0301) },	/* G2E-300 #1 */
-	{ USB_DEVICE(0x0461, 0x0381) },	/* ReadyScan 636i */
 	{ USB_DEVICE(0x0461, 0x0302) },	/* G2-300 #2 */
-	{ USB_DEVICE(0x0461, 0x0382) },	/* G2-600 #2 */
 	{ USB_DEVICE(0x0461, 0x0303) },	/* G2E-300 #2 */
-	{ USB_DEVICE(0x0461, 0x0383) },	/* G2E-600 */
 	{ USB_DEVICE(0x0461, 0x0340) },	/* Colorado USB 9600 */
-	// { USB_DEVICE(0x0461, 0x0360) },	/* Colorado USB 19200 - undetected endpoint */
 	{ USB_DEVICE(0x0461, 0x0341) },	/* Colorado 600u */
+	{ USB_DEVICE(0x0461, 0x0347) },	/* Primascan Colorado 2600u */
+	{ USB_DEVICE(0x0461, 0x0360) },	/* Colorado USB 19200 */
 	{ USB_DEVICE(0x0461, 0x0361) },	/* Colorado 1200u */
+	{ USB_DEVICE(0x0461, 0x0380) },	/* G2-600 #1 */
+	{ USB_DEVICE(0x0461, 0x0381) },	/* ReadyScan 636i */
+	{ USB_DEVICE(0x0461, 0x0382) },	/* G2-600 #2 */
+	{ USB_DEVICE(0x0461, 0x0383) },	/* G2E-600 */
+	/* Prolink */
+	{ USB_DEVICE(0x06dc, 0x0014) }, /* Winscan Pro 2448U */
 	/* Relisis */
 	// { USB_DEVICE(0x0475, 0x0103) },	/* Episode - undetected endpoint */
 	/* Seiko/Epson Corp. */
@@ -227,7 +255,10 @@
 	{ USB_DEVICE(0x04b8, 0x011c) }, /* Perfection 3200 */
 	{ USB_DEVICE(0x04b8, 0x011d) }, /* Perfection 1260 */
 	{ USB_DEVICE(0x04b8, 0x011e) }, /* Perfection 1660 Photo */
+	{ USB_DEVICE(0x04b8, 0x0801) }, /* Stylus CX5200 */
 	{ USB_DEVICE(0x04b8, 0x0802) }, /* Stylus CX3200 */
+	/* SYSCAN */
+	{ USB_DEVICE(0x0a82, 0x4600) }, /* TravelScan 460/464 */
 	/* Umax */
 	{ USB_DEVICE(0x1606, 0x0010) },	/* Astra 1220U */
 	{ USB_DEVICE(0x1606, 0x0030) },	/* Astra 2000U */
@@ -236,8 +267,8 @@
 	{ USB_DEVICE(0x1606, 0x0160) }, /* Astra 5400U */  
 	{ USB_DEVICE(0x1606, 0x0230) },	/* Astra 2200U */
 	/* Visioneer */
-	{ USB_DEVICE(0x04a7, 0x0221) },	/* OneTouch 5300 USB */
 	{ USB_DEVICE(0x04a7, 0x0211) },	/* OneTouch 7600 USB */
+	{ USB_DEVICE(0x04a7, 0x0221) },	/* OneTouch 5300 USB */
 	{ USB_DEVICE(0x04a7, 0x0231) },	/* 6100 USB */
 	{ USB_DEVICE(0x04a7, 0x0311) },	/* 6200 EPP/USB */
 	{ USB_DEVICE(0x04a7, 0x0321) },	/* OneTouch 8100 EPP/USB */
diff -urN linux-2.4.21-pre5/drivers/usb/serial/Config.in linux-2.4.21-pre6/drivers/usb/serial/Config.in
--- linux-2.4.21-pre5/drivers/usb/serial/Config.in	Wed Mar 26 16:11:29 2003
+++ linux-2.4.21-pre6/drivers/usb/serial/Config.in	Wed Mar 26 16:11:39 2003
@@ -30,7 +30,9 @@
       bool '    USB Keyspan USA-19W Firmware' CONFIG_USB_SERIAL_KEYSPAN_USA19W
       bool '    USB Keyspan USA-19QW Firmware' CONFIG_USB_SERIAL_KEYSPAN_USA19QW
       bool '    USB Keyspan USA-19QI Firmware' CONFIG_USB_SERIAL_KEYSPAN_USA19QI
+      bool '    USB Keyspan MPR Firmware' CONFIG_USB_SERIAL_KEYSPAN_MPR
       bool '    USB Keyspan USA-49W Firmware' CONFIG_USB_SERIAL_KEYSPAN_USA49W
+      bool '    USB Keyspan USA-49WLC Firmware' CONFIG_USB_SERIAL_KEYSPAN_USA49WLC
    fi
    dep_tristate '  USB MCT Single Port Serial Driver' CONFIG_USB_SERIAL_MCT_U232 $CONFIG_USB_SERIAL
    dep_tristate '  USB KL5KUSB105 (Palmconnect) Driver' CONFIG_USB_SERIAL_KLSI $CONFIG_USB_SERIAL
diff -urN linux-2.4.21-pre5/drivers/usb/serial/io_edgeport.c linux-2.4.21-pre6/drivers/usb/serial/io_edgeport.c
--- linux-2.4.21-pre5/drivers/usb/serial/io_edgeport.c	Wed Mar 26 16:11:29 2003
+++ linux-2.4.21-pre6/drivers/usb/serial/io_edgeport.c	Wed Mar 26 16:11:39 2003
@@ -515,7 +515,7 @@
 		case EDGE_DOWNLOAD_FILE_I930:
 			BootMajorVersion	= BootCodeImageVersion_GEN1.MajorVersion;
 			BootMinorVersion	= BootCodeImageVersion_GEN1.MinorVersion;
-			BootBuildNumber		= BootCodeImageVersion_GEN1.BuildNumber;
+			BootBuildNumber		= cpu_to_le16(BootCodeImageVersion_GEN1.BuildNumber);
 			BootImage		= &BootCodeImage_GEN1[0];
 			BootSize		= sizeof( BootCodeImage_GEN1 );
 			break;
@@ -523,7 +523,7 @@
 		case EDGE_DOWNLOAD_FILE_80251:
 			BootMajorVersion	= BootCodeImageVersion_GEN2.MajorVersion;
 			BootMinorVersion	= BootCodeImageVersion_GEN2.MinorVersion;
-			BootBuildNumber		= BootCodeImageVersion_GEN2.BuildNumber;
+			BootBuildNumber		= cpu_to_le16(BootCodeImageVersion_GEN2.BuildNumber);
 			BootImage		= &BootCodeImage_GEN2[0];
 			BootSize		= sizeof( BootCodeImage_GEN2 );
 			break;
@@ -535,26 +535,26 @@
 	// Check Boot Image Version
 	BootCurVer = (edge_serial->boot_descriptor.MajorVersion << 24) +
 		     (edge_serial->boot_descriptor.MinorVersion << 16) +
-		      edge_serial->boot_descriptor.BuildNumber;
+		      le16_to_cpu(edge_serial->boot_descriptor.BuildNumber);
 
 	BootNewVer = (BootMajorVersion << 24) +
 		     (BootMinorVersion << 16) +
-		      BootBuildNumber;
+		      le16_to_cpu(BootBuildNumber);
 
 	dbg("Current Boot Image version %d.%d.%d",
 	    edge_serial->boot_descriptor.MajorVersion,
 	    edge_serial->boot_descriptor.MinorVersion,
-	    edge_serial->boot_descriptor.BuildNumber);
+	    le16_to_cpu(edge_serial->boot_descriptor.BuildNumber));
 
 
 	if (BootNewVer > BootCurVer) {
 		dbg("**Update Boot Image from %d.%d.%d to %d.%d.%d",
 		    edge_serial->boot_descriptor.MajorVersion,
 		    edge_serial->boot_descriptor.MinorVersion,
-		    edge_serial->boot_descriptor.BuildNumber,
+		    le16_to_cpu(edge_serial->boot_descriptor.BuildNumber),
 		    BootMajorVersion,
 		    BootMinorVersion,
-		    BootBuildNumber);
+		    le16_to_cpu(BootBuildNumber));
 
 
 		dbg("Downloading new Boot Image");
@@ -563,12 +563,12 @@
 
 		for (;;) {
 			record = (struct edge_firmware_image_record *)firmware;
-			response = rom_write (edge_serial->serial, record->ExtAddr, record->Addr, record->Len, &record->Data[0]);
+			response = rom_write (edge_serial->serial, le16_to_cpu(record->ExtAddr), le16_to_cpu(record->Addr), le16_to_cpu(record->Len), &record->Data[0]);
 			if (response < 0) {
-				err("sram_write failed (%x, %x, %d)", record->ExtAddr, record->Addr, record->Len);
+				err("rom_write failed (%x, %x, %d)", le16_to_cpu(record->ExtAddr), le16_to_cpu(record->Addr), le16_to_cpu(record->Len));
 				break;
 			}
-			firmware += sizeof (struct edge_firmware_image_record) + record->Len;
+			firmware += sizeof (struct edge_firmware_image_record) + le16_to_cpu(record->Len);
 			if (firmware >= &BootImage[BootSize]) {
 				break;
 			}
@@ -671,12 +671,12 @@
 	if (edge_serial->serial->dev->descriptor.idProduct & ION_DEVICE_ID_GENERATION_2) {
 		product_info->FirmwareMajorVersion	= OperationalCodeImageVersion_GEN2.MajorVersion;
 		product_info->FirmwareMinorVersion	= OperationalCodeImageVersion_GEN2.MinorVersion;
-		product_info->FirmwareBuildNumber	= OperationalCodeImageVersion_GEN2.BuildNumber;
+		product_info->FirmwareBuildNumber	= cpu_to_le16(OperationalCodeImageVersion_GEN2.BuildNumber);
 		product_info->iDownloadFile		= EDGE_DOWNLOAD_FILE_80251;
 	} else {
 		product_info->FirmwareMajorVersion	= OperationalCodeImageVersion_GEN1.MajorVersion;
 		product_info->FirmwareMinorVersion	= OperationalCodeImageVersion_GEN1.MinorVersion;
-		product_info->FirmwareBuildNumber	= OperationalCodeImageVersion_GEN1.BuildNumber;
+		product_info->FirmwareBuildNumber	= cpu_to_le16(OperationalCodeImageVersion_GEN1.BuildNumber);
 		product_info->iDownloadFile		= EDGE_DOWNLOAD_FILE_I930;
 	}
 
@@ -722,10 +722,10 @@
 	dbg("  BoardRev              %x", product_info->BoardRev);
 	dbg("  BootMajorVersion      %d.%d.%d", product_info->BootMajorVersion,
 	    product_info->BootMinorVersion,
-	    product_info->BootBuildNumber);
+	    le16_to_cpu(product_info->BootBuildNumber));
 	dbg("  FirmwareMajorVersion  %d.%d.%d", product_info->FirmwareMajorVersion,
 	    product_info->FirmwareMinorVersion,
-	    product_info->FirmwareBuildNumber);
+	    le16_to_cpu(product_info->FirmwareBuildNumber));
 	dbg("  ManufactureDescDate   %d/%d/%d", product_info->ManufactureDescDate[0],
 	    product_info->ManufactureDescDate[1],
 	    product_info->ManufactureDescDate[2]+1900);
@@ -2305,7 +2305,7 @@
 	__u16 current_length;
 	unsigned char *transfer_buffer;
 
-//	dbg("%s - %x, %x, %d", __FUNCTION__, extAddr, addr, length);
+	dbg("%s - %x, %x, %d", __FUNCTION__, extAddr, addr, length);
 
 	transfer_buffer =  kmalloc (64, GFP_KERNEL);
 	if (!transfer_buffer) {
@@ -2793,12 +2793,13 @@
  *	Turns a string from Unicode into ASCII.
  *	Doesn't do a good job with any characters that are outside the normal
  *	ASCII range, but it's only for debugging...
+ *	NOTE: expects the unicode in LE format
  ****************************************************************************/
 static void unicode_to_ascii (char *string, short *unicode, int unicode_size)
 {
 	int i;
 	for (i = 0; i < unicode_size; ++i) {
-		string[i] = (char)(unicode[i]);
+		string[i] = (char)(le16_to_cpu(unicode[i]));
 	}
 	string[unicode_size] = 0x00;
 }
@@ -2862,11 +2863,11 @@
 		err("error in getting boot descriptor");
 	} else {
 		dbg("**Boot Descriptor:");
-		dbg("  BootCodeLength: %d", edge_serial->boot_descriptor.BootCodeLength);
+		dbg("  BootCodeLength: %d", le16_to_cpu(edge_serial->boot_descriptor.BootCodeLength));
 		dbg("  MajorVersion:   %d", edge_serial->boot_descriptor.MajorVersion);
 		dbg("  MinorVersion:   %d", edge_serial->boot_descriptor.MinorVersion);
-		dbg("  BuildNumber:    %d", edge_serial->boot_descriptor.BuildNumber);
-		dbg("  Capabilities:   0x%x", edge_serial->boot_descriptor.Capabilities);
+		dbg("  BuildNumber:    %d", le16_to_cpu(edge_serial->boot_descriptor.BuildNumber));
+		dbg("  Capabilities:   0x%x", le16_to_cpu(edge_serial->boot_descriptor.Capabilities));
 		dbg("  UConfig0:       %d", edge_serial->boot_descriptor.UConfig0);
 		dbg("  UConfig1:       %d", edge_serial->boot_descriptor.UConfig1);
 	}
@@ -2918,12 +2919,12 @@
 
 	for (;;) {
 		record = (struct edge_firmware_image_record *)firmware;
-		response = sram_write (edge_serial->serial, record->ExtAddr, record->Addr, record->Len, &record->Data[0]);
+		response = sram_write (edge_serial->serial, le16_to_cpu(record->ExtAddr), le16_to_cpu(record->Addr), le16_to_cpu(record->Len), &record->Data[0]);
 		if (response < 0) {
-			err("sram_write failed (%x, %x, %d)", record->ExtAddr, record->Addr, record->Len);
+			err("sram_write failed (%x, %x, %d)", le16_to_cpu(record->ExtAddr), le16_to_cpu(record->Addr), record->Len);
 			break;
 		}
-		firmware += sizeof (struct edge_firmware_image_record) + record->Len;
+		firmware += sizeof (struct edge_firmware_image_record) + le16_to_cpu(record->Len);
 		if (firmware >= &FirmwareImage[ImageSize]) {
 			break;
 		}
diff -urN linux-2.4.21-pre5/drivers/usb/serial/ipaq.c linux-2.4.21-pre6/drivers/usb/serial/ipaq.c
--- linux-2.4.21-pre5/drivers/usb/serial/ipaq.c	Wed Mar 26 16:11:29 2003
+++ linux-2.4.21-pre6/drivers/usb/serial/ipaq.c	Wed Mar 26 16:11:39 2003
@@ -107,6 +107,7 @@
 	{ USB_DEVICE(COMPAQ_VENDOR_ID, COMPAQ_IPAQ_ID) },
 	{ USB_DEVICE(COMPAQ_VENDOR_ID, COMPAQ_0032_ID) },
 	{ USB_DEVICE(DELL_VENDOR_ID, DELL_AXIM_ID) },
+	{ USB_DEVICE(FSC_VENDOR_ID, FSC_LOOX_ID) },
 	{ USB_DEVICE(HP_VENDOR_ID, HP_JORNADA_548_ID) },
 	{ USB_DEVICE(HP_VENDOR_ID, HP_JORNADA_568_ID) },
 	{ USB_DEVICE(HP_VENDOR_ID, HP_2016_ID) },
diff -urN linux-2.4.21-pre5/drivers/usb/serial/ipaq.h linux-2.4.21-pre6/drivers/usb/serial/ipaq.h
--- linux-2.4.21-pre5/drivers/usb/serial/ipaq.h	Wed Mar 26 16:11:29 2003
+++ linux-2.4.21-pre6/drivers/usb/serial/ipaq.h	Wed Mar 26 16:11:39 2003
@@ -33,6 +33,9 @@
 #define DELL_VENDOR_ID		0x413c
 #define DELL_AXIM_ID		0x4001
 
+#define FSC_VENDOR_ID		0x0bf8
+#define FSC_LOOX_ID		0x1001
+
 #define HP_VENDOR_ID		0x03f0
 #define HP_JORNADA_548_ID	0x1016
 #define HP_JORNADA_568_ID	0x1116
diff -urN linux-2.4.21-pre5/drivers/usb/serial/keyspan.c linux-2.4.21-pre6/drivers/usb/serial/keyspan.c
--- linux-2.4.21-pre5/drivers/usb/serial/keyspan.c	Wed Mar 26 16:11:29 2003
+++ linux-2.4.21-pre6/drivers/usb/serial/keyspan.c	Wed Mar 26 16:11:39 2003
@@ -28,6 +28,24 @@
 
   Change History
 
+    Wed Feb 19 22:00:00 PST 2003 (Jeffrey S. Laing <keyspan@jsl.com>)
+      Merged the current (1/31/03) Keyspan code with the current (2.4.21-pre4)
+      Linux source tree.  The Linux tree lacked support for the 49WLC and
+      others.  The Keyspan patches didn't work with the current kernel.
+
+    2003jan30	LPM	add support for the 49WLC and MPR
+
+    Wed Apr 25 12:00:00 PST 2002 (Keyspan)
+      Started with Hugh Blemings' code dated Jan 17, 2002.  All adapters
+      now supported (including QI and QW).  Modified port open, port
+      close, and send setup() logic to fix various data and endpoint
+      synchronization bugs and device LED status bugs.  Changed keyspan_
+      write_room() to accurately return transmit buffer availability.
+      Changed forwardingLength from 1 to 16 for all adapters.
+
+    Fri Oct 12 16:45:00 EST 2001
+      Preliminary USA-19QI and USA-28 support (both test OK for me, YMMV)
+
     Wed Apr 25 12:00:00 PST 2002 (Keyspan)
       Started with Hugh Blemings' code dated Jan 17, 2002.  All adapters
       now supported (including QI and QW).  Modified port open, port
@@ -102,7 +120,7 @@
 /*
  * Version Information
  */
-#define DRIVER_VERSION "v1.1.3"
+#define DRIVER_VERSION "v1.1.4"
 #define DRIVER_AUTHOR "Hugh Blemings <hugh@misc.nu"
 #define DRIVER_DESC "Keyspan USB to Serial Converter Driver"
 
@@ -904,6 +922,9 @@
 		/* usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe), 0); */
 	}
 
+	// if the device is a USA49x, determine whether it is an W or WLC model
+	// and set the baud clock accordingly
+
 	keyspan_send_setup(port, 1);
 	//mdelay(100);
 	keyspan_set_termios(port, NULL);
@@ -1012,6 +1033,11 @@
 		fw_name = "USA19QI";
 		break;
 			     
+	case keyspan_mpr_pre_product_id:
+		record = &keyspan_mpr_firmware[0];
+		fw_name = "MPR";
+		break;
+
 	case keyspan_usa19qw_pre_product_id:
 		record = &keyspan_usa19qw_firmware[0];
 		fw_name = "USA19QI";
@@ -1032,6 +1058,11 @@
 		fw_name = "USA49W";
 		break;
 
+	case keyspan_usa49wlc_pre_product_id:
+		record = &keyspan_usa49wlc_firmware[0];
+		fw_name = "USA49WLC";
+		break;
+
 	default:
 		record = NULL;
 		fw_name = "Unknown";
diff -urN linux-2.4.21-pre5/drivers/usb/serial/keyspan.h linux-2.4.21-pre6/drivers/usb/serial/keyspan.h
--- linux-2.4.21-pre5/drivers/usb/serial/keyspan.h	Thu Nov 28 15:53:14 2002
+++ linux-2.4.21-pre6/drivers/usb/serial/keyspan.h	Wed Mar 26 16:11:39 2003
@@ -136,6 +136,12 @@
 	static const struct ezusb_hex_record *keyspan_usa19qi_firmware = NULL;
 #endif
 
+#ifdef CONFIG_USB_SERIAL_KEYSPAN_MPR
+        #include "keyspan_mpr_fw.h"
+#else
+	static const struct ezusb_hex_record *keyspan_mpr_firmware = NULL;
+#endif
+
 #ifdef CONFIG_USB_SERIAL_KEYSPAN_USA19QW
 	#include "keyspan_usa19qw_fw.h"
 #else
@@ -160,6 +166,12 @@
 	static const struct ezusb_hex_record *keyspan_usa49w_firmware = NULL;
 #endif
 
+#ifdef CONFIG_USB_SERIAL_KEYSPAN_USA49WLC
+        #include "keyspan_usa49wlc_fw.h"
+#else
+	static const struct ezusb_hex_record *keyspan_usa49wlc_firmware = NULL;
+#endif
+
 /* Values used for baud rate calculation - device specific */
 #define	KEYSPAN_INVALID_BAUD_RATE		(-1)
 #define	KEYSPAN_BAUD_RATE_OK			(0)
@@ -182,6 +194,7 @@
 #define	keyspan_usa18x_pre_product_id		0x0105
 #define	keyspan_usa19_pre_product_id		0x0103
 #define	keyspan_usa19qi_pre_product_id		0x010b
+#define	keyspan_mpr_pre_product_id		0x011b
 #define	keyspan_usa19qw_pre_product_id		0x0118
 #define	keyspan_usa19w_pre_product_id		0x0106
 #define	keyspan_usa28_pre_product_id		0x0101
@@ -189,6 +202,7 @@
 #define	keyspan_usa28xa_pre_product_id		0x0114
 #define	keyspan_usa28xb_pre_product_id		0x0113
 #define	keyspan_usa49w_pre_product_id		0x0109
+#define	keyspan_usa49wlc_pre_product_id		0x011a
 
 /* Product IDs post-renumeration.  Note that the 28x and 28xb
    have the same id's post-renumeration but behave identically
@@ -196,6 +210,7 @@
 #define	keyspan_usa18x_product_id		0x0112
 #define	keyspan_usa19_product_id		0x0107
 #define	keyspan_usa19qi_product_id		0x010c
+#define	keyspan_mpr_product_id			0x011c
 #define	keyspan_usa19qw_product_id		0x0119
 #define	keyspan_usa19w_product_id		0x0108
 #define	keyspan_usa28_product_id		0x010f
@@ -203,6 +218,7 @@
 #define	keyspan_usa28xa_product_id		0x0115
 #define	keyspan_usa28xb_product_id		0x0110
 #define	keyspan_usa49w_product_id		0x010a
+#define	keyspan_usa49wlc_product_id		0x012a
 
 
 struct keyspan_device_details {
@@ -394,6 +410,22 @@
 	baudclk:		KEYSPAN_USA49W_BAUDCLK,
 };
 
+static const struct keyspan_device_details usa49wlc_device_details = {
+	product_id:		keyspan_usa49wlc_product_id,
+	msg_format:		msg_usa49,
+	num_ports:		4,
+	indat_endp_flip:	0,
+	outdat_endp_flip:	0,
+	indat_endpoints:	{0x81, 0x82, 0x83, 0x84},
+	outdat_endpoints:	{0x01, 0x02, 0x03, 0x04},
+	inack_endpoints:	{-1, -1, -1, -1},
+	outcont_endpoints:	{-1, -1, -1, -1},
+	instat_endpoint:	0x87,
+	glocont_endpoint:	0x07,
+	calculate_baud_rate:	keyspan_usa19w_calc_baud,
+	baudclk:		KEYSPAN_USA19W_BAUDCLK,
+};
+
 static const struct keyspan_device_details *keyspan_devices[] = {
 	&usa18x_device_details,
 	&usa19_device_details,
@@ -405,6 +437,7 @@
 	&usa28xa_device_details,
 	/* 28xb not required as it renumerates as a 28x */
 	&usa49w_device_details,
+	&usa49wlc_device_details,
 	NULL,
 };
 
@@ -414,21 +447,25 @@
 	{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa19w_pre_product_id) },
 	{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa19qi_pre_product_id) },
 	{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa19qw_pre_product_id) },
+	{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_mpr_pre_product_id) },
 	{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa28_pre_product_id) },
 	{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa28x_pre_product_id) },
 	{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa28xa_pre_product_id) },
 	{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa28xb_pre_product_id) },
 	{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa49w_pre_product_id) },
+	{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa49wlc_pre_product_id) },
 	{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa18x_product_id) },
 	{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa19_product_id) },
 	{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa19w_product_id) },
 	{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa19qi_product_id) },
 	{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa19qw_product_id) },
+	{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_mpr_product_id) },
 	{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa28_product_id) },
 	{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa28x_product_id) },
 	{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa28xa_product_id) },
 	{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa28xb_product_id) },
 	{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa49w_product_id)},
+	{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa49wlc_product_id)},
 	{ } /* Terminating entry */
 };
 
@@ -441,11 +478,13 @@
 	{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa19qi_pre_product_id) },
 	{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa19qw_pre_product_id) },
 	{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa19w_pre_product_id) },
+	{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_mpr_pre_product_id) },
 	{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa28_pre_product_id) },
 	{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa28x_pre_product_id) },
 	{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa28xa_pre_product_id) },
 	{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa28xb_pre_product_id) },
 	{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa49w_pre_product_id) },
+	{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa49wlc_pre_product_id) },
 	{ } /* Terminating entry */
 };
 
@@ -455,6 +494,7 @@
 	{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa19qi_product_id) },
 	{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa19qw_product_id) },
 	{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa19w_product_id) },
+	{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_mpr_pre_product_id) },
 	{ } /* Terminating entry */
 };
 
@@ -462,11 +502,13 @@
 	{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa28_product_id) },
 	{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa28x_product_id) },
 	{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa28xa_product_id) },
+	{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa28xb_product_id) },
 	{ } /* Terminating entry */
 };
 
 static struct usb_device_id keyspan_4port_ids[] = {
 	{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa49w_product_id) },
+	{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa49wlc_product_id)},
 	{ } /* Terminating entry */
 };
 
diff -urN linux-2.4.21-pre5/drivers/usb/serial/keyspan_mpr_fw.h linux-2.4.21-pre6/drivers/usb/serial/keyspan_mpr_fw.h
--- linux-2.4.21-pre5/drivers/usb/serial/keyspan_mpr_fw.h	Wed Dec 31 16:00:00 1969
+++ linux-2.4.21-pre6/drivers/usb/serial/keyspan_mpr_fw.h	Wed Mar 26 16:11:39 2003
@@ -0,0 +1,286 @@
+/* keyspan_mpr_fw.h
+ 
+	The firmware contained herein as keyspan_mpr_fw.h is
+
+		Copyright (C) 1999-2001
+		Keyspan, A division of InnoSys Incorporated ("Keyspan")
+		
+	as an unpublished work. This notice does not imply unrestricted or
+	public access to the source code from which this firmware image is
+	derived.  Except as noted below this firmware image may not be 
+	reproduced, used, sold or transferred to any third party without 
+	Keyspan's prior written consent.  All Rights Reserved.
+
+	Permission is hereby granted for the distribution of this firmware 
+	image as part of a Linux or other Open Source operating system kernel 
+	in text or binary form as required. 
+
+	This firmware may not be modified and may only be used with  
+	Keyspan hardware.  Distribution and/or Modification of the 
+	keyspan.c driver which includes this firmware, in whole or in 
+	part, requires the inclusion of this statement."
+
+static char theFirmwareDate53[] =
+	"04/26/2002  02:47p              11,570 USA53";
+*/
+
+static const struct ezusb_hex_record keyspan_mpr_firmware[] = {
+ {0x0033,  3, { 0x02, 0x00, 0x1a}},
+ {0x001a,  4, { 0x53, 0xd8, 0xef, 0x32}},
+ {0x0003, 16, { 0x8e, 0x56, 0x8f, 0x57, 0xe5, 0x57, 0x15, 0x57, 0xae, 0x56, 0x70, 0x02, 0x15, 0x56, 0x4e, 0x60}},
+ {0x0013,  7, { 0x05, 0x12, 0x0f, 0xa2, 0x80, 0xee, 0x22}},
+ {0x0023,  3, { 0x02, 0x00, 0x46}},
+ {0x0046, 16, { 0xc0, 0xe0, 0xc0, 0x83, 0xc0, 0x82, 0xc0, 0x86, 0x75, 0x86, 0x00, 0xc0, 0xd0, 0x75, 0xd0, 0x08}},
+ {0x0056, 16, { 0x30, 0x99, 0x0e, 0x30, 0x0b, 0x07, 0xa2, 0x0e, 0x92, 0x9b, 0x85, 0x36, 0x99, 0xc2, 0x99, 0xd2}},
+ {0x0066, 16, { 0x12, 0x20, 0x12, 0x03, 0x02, 0x02, 0xf9, 0xc2, 0x12, 0x30, 0x03, 0x19, 0x7e, 0x7e, 0x7f, 0x40}},
+ {0x0076, 16, { 0x75, 0x1a, 0x7e, 0x75, 0x1b, 0x40, 0x75, 0x17, 0x00, 0x7e, 0x7d, 0x7f, 0xc0, 0x75, 0x18, 0x7d}},
+ {0x0086, 16, { 0x75, 0x19, 0xc0, 0x80, 0x17, 0x7e, 0x7d, 0x7f, 0xc0, 0x75, 0x1a, 0x7d, 0x75, 0x1b, 0xc0, 0x75}},
+ {0x0096, 16, { 0x17, 0x01, 0x7e, 0x7e, 0x7f, 0x40, 0x75, 0x18, 0x7e, 0x75, 0x19, 0x40, 0x20, 0x0b, 0x03, 0x02}},
+ {0x00a6, 16, { 0x01, 0x84, 0xe5, 0x3a, 0xc3, 0x95, 0x53, 0x50, 0x3c, 0x20, 0x0c, 0x34, 0x20, 0x09, 0x31, 0x90}},
+ {0x00b6, 16, { 0x7f, 0x9b, 0xe0, 0x55, 0x38, 0x70, 0x29, 0x30, 0x10, 0x12, 0xaf, 0x3a, 0x05, 0x3a, 0xe5, 0x1b}},
+ {0x00c6, 16, { 0x2f, 0xf5, 0x82, 0xe4, 0x35, 0x1a, 0xf5, 0x83, 0xe0, 0x13, 0x92, 0x0e, 0xaf, 0x3a, 0x05, 0x3a}},
+ {0x00d6, 16, { 0xe5, 0x1b, 0x2f, 0xf5, 0x82, 0xe4, 0x35, 0x1a, 0xf5, 0x83, 0xe0, 0xf5, 0x36, 0x02, 0x02, 0xf7}},
+ {0x00e6, 16, { 0xc2, 0x0b, 0x02, 0x02, 0xf7, 0x30, 0x03, 0x11, 0x90, 0x7f, 0xc7, 0xe4, 0xf0, 0xa3, 0xe0, 0x54}},
+ {0x00f6, 16, { 0x02, 0xf5, 0x1d, 0xa3, 0xe0, 0xf5, 0x1c, 0x80, 0x11, 0x90, 0x7f, 0xc9, 0xe4, 0xf0, 0x90, 0x7f}},
+ {0x0043,  3, { 0x02, 0x0f, 0x00}},
+ {0x0000,  3, { 0x02, 0x00, 0x26}},
+ {0x0026, 12, { 0x78, 0x7f, 0xe4, 0xf6, 0xd8, 0xfd, 0x75, 0x81, 0x5a, 0x02, 0x0a, 0x33}},
+ {0x0106, 64, { 0xc6, 0xe0, 0x54, 0x02, 0xf5, 0x1d, 0xa3, 0xe0, 0xf5, 0x1c, 0xe5, 0x17, 0x24, 0xff, 0x92, 0x03, 0x30,
+  0x0d, 0x0d, 0xc2, 0x0d, 0x90, 0x7f, 0xbb, 0x74, 0x01, 0xf0, 0xc2, 0x0b, 0x02, 0x02, 0xf7, 0xe5,
+  0x1d, 0x60, 0x05, 0xc2, 0x0b, 0x02, 0x02, 0xf7, 0x85, 0x1c, 0x53, 0x85, 0x19, 0x82, 0x85, 0x18,
+  0x83, 0xe0, 0x13, 0x92, 0x0d, 0x75, 0x12, 0xff, 0x20, 0x0c, 0x3a, 0x20, 0x09, 0x37, 0x90}},
+ {0x0146, 64, { 0x7f, 0x9b, 0xe0, 0x55, 0x38, 0x70, 0x2f, 0x30, 0x10, 0x1c, 0x85, 0x19, 0x82, 0x85, 0x18, 0x83, 0xa3,
+  0xe0, 0x13, 0x92, 0x0e, 0x85, 0x19, 0x82, 0x85, 0x18, 0x83, 0xa3, 0xa3, 0xe0, 0xf5, 0x36, 0x75,
+  0x3a, 0x03, 0x02, 0x02, 0xf7, 0x75, 0x3a, 0x02, 0x85, 0x19, 0x82, 0x85, 0x18, 0x83, 0xa3, 0xe0,
+  0xf5, 0x36, 0x02, 0x02, 0xf7, 0x75, 0x3a, 0x01, 0xc2, 0x0b, 0x02, 0x02, 0xf7, 0x30, 0x03}},
+ {0x0186, 64, { 0x0e, 0x90, 0x7f, 0xc6, 0xe0, 0x54, 0x02, 0xf5, 0x1d, 0xa3, 0xe0, 0xf5, 0x1c, 0x80, 0x0c, 0x90, 0x7f,
+  0xc8, 0xe0, 0x54, 0x02, 0xf5, 0x1d, 0xa3, 0xe0, 0xf5, 0x1c, 0xe5, 0x3a, 0xc3, 0x95, 0x53, 0x50,
+  0x03, 0x02, 0x02, 0x68, 0xe5, 0x1d, 0x60, 0x07, 0xc2, 0x14, 0xc2, 0x05, 0x02, 0x02, 0xf7, 0x85,
+  0x1c, 0x53, 0x85, 0x1b, 0x82, 0x85, 0x1a, 0x83, 0xe0, 0x13, 0x92, 0x0d, 0x75, 0x12, 0xff}},
+ {0x01c6, 64, { 0x30, 0x0c, 0x03, 0x02, 0x02, 0x60, 0x30, 0x09, 0x03, 0x02, 0x02, 0x60, 0x90, 0x7f, 0x9b, 0xe0, 0x55,
+  0x38, 0x60, 0x03, 0x02, 0x02, 0x60, 0x30, 0x10, 0x1b, 0x85, 0x1b, 0x82, 0x85, 0x1a, 0x83, 0xa3,
+  0xe0, 0x13, 0x92, 0x9b, 0x85, 0x1b, 0x82, 0x85, 0x1a, 0x83, 0xa3, 0xa3, 0xe0, 0xf5, 0x99, 0x75,
+  0x3a, 0x03, 0x80, 0x0d, 0x85, 0x1b, 0x82, 0x85, 0x1a, 0x83, 0xa3, 0xe0, 0xf5, 0x99, 0x75}},
+ {0x0206, 64, { 0x3a, 0x02, 0xe5, 0x3a, 0xc3, 0x95, 0x53, 0x40, 0x26, 0x30, 0x03, 0x07, 0x90, 0x7f, 0xc7, 0xe4, 0xf0,
+  0x80, 0x05, 0x90, 0x7f, 0xc9, 0xe4, 0xf0, 0xe5, 0x17, 0x24, 0xff, 0x92, 0x03, 0x20, 0x0d, 0x03,
+  0x02, 0x02, 0xf7, 0xc2, 0x0d, 0x90, 0x7f, 0xbb, 0x74, 0x01, 0xf0, 0x02, 0x02, 0xf7, 0x30, 0x10,
+  0x12, 0xaf, 0x3a, 0x05, 0x3a, 0xe5, 0x1b, 0x2f, 0xf5, 0x82, 0xe4, 0x35, 0x1a, 0xf5, 0x83}},
+ {0x0246, 64, { 0xe0, 0x13, 0x92, 0x0e, 0xaf, 0x3a, 0x05, 0x3a, 0xe5, 0x1b, 0x2f, 0xf5, 0x82, 0xe4, 0x35, 0x1a, 0xf5,
+  0x83, 0xe0, 0xf5, 0x36, 0xd2, 0x0b, 0x02, 0x02, 0xf7, 0x75, 0x3a, 0x01, 0xc2, 0x14, 0x02, 0x02,
+  0xf7, 0x30, 0x0c, 0x03, 0x02, 0x02, 0xf5, 0x30, 0x09, 0x03, 0x02, 0x02, 0xf5, 0x90, 0x7f, 0x9b,
+  0xe0, 0x55, 0x38, 0x70, 0x79, 0x30, 0x10, 0x12, 0xaf, 0x3a, 0x05, 0x3a, 0xe5, 0x1b, 0x2f}},
+ {0x0286, 64, { 0xf5, 0x82, 0xe4, 0x35, 0x1a, 0xf5, 0x83, 0xe0, 0x13, 0x92, 0x9b, 0xaf, 0x3a, 0x05, 0x3a, 0xe5, 0x1b,
+  0x2f, 0xf5, 0x82, 0xe4, 0x35, 0x1a, 0xf5, 0x83, 0xe0, 0xf5, 0x99, 0xe5, 0x3a, 0xc3, 0x95, 0x53,
+  0x40, 0x22, 0x30, 0x03, 0x07, 0x90, 0x7f, 0xc7, 0xe4, 0xf0, 0x80, 0x05, 0x90, 0x7f, 0xc9, 0xe4,
+  0xf0, 0xe5, 0x17, 0x24, 0xff, 0x92, 0x03, 0x30, 0x0d, 0x36, 0xc2, 0x0d, 0x90, 0x7f, 0xbb}},
+ {0x02c6, 64, { 0x74, 0x01, 0xf0, 0x80, 0x2c, 0x30, 0x10, 0x12, 0xaf, 0x3a, 0x05, 0x3a, 0xe5, 0x1b, 0x2f, 0xf5, 0x82,
+  0xe4, 0x35, 0x1a, 0xf5, 0x83, 0xe0, 0x13, 0x92, 0x0e, 0xaf, 0x3a, 0x05, 0x3a, 0xe5, 0x1b, 0x2f,
+  0xf5, 0x82, 0xe4, 0x35, 0x1a, 0xf5, 0x83, 0xe0, 0xf5, 0x36, 0xd2, 0x0b, 0x80, 0x02, 0xc2, 0x14,
+  0xd2, 0x01, 0x20, 0x98, 0x03, 0x02, 0x04, 0x35, 0xc2, 0x98, 0x20, 0x02, 0x03, 0x02, 0x03}},
+ {0x0306, 64, { 0xa2, 0x20, 0x15, 0x27, 0xaf, 0x39, 0x05, 0x39, 0x74, 0x80, 0x2f, 0xf5, 0x82, 0xe4, 0x34, 0x7e, 0xf5,
+  0x83, 0xe5, 0x99, 0xf0, 0x30, 0x10, 0x4d, 0xaf, 0x39, 0x05, 0x39, 0x74, 0x80, 0x2f, 0xf5, 0x82,
+  0xe4, 0x34, 0x7e, 0xf5, 0x83, 0xe5, 0x98, 0xf0, 0x80, 0x3a, 0x85, 0x99, 0x55, 0xe5, 0x55, 0xb5,
+  0x47, 0x04, 0xd2, 0x09, 0x80, 0x2e, 0xe5, 0x55, 0xb5, 0x46, 0x04, 0xc2, 0x09, 0x80, 0x25}},
+ {0x0346, 64, { 0xaf, 0x39, 0x05, 0x39, 0x74, 0x80, 0x2f, 0xf5, 0x82, 0xe4, 0x34, 0x7e, 0xf5, 0x83, 0xe5, 0x55, 0xf0,
+  0x30, 0x10, 0x11, 0xaf, 0x39, 0x05, 0x39, 0x74, 0x80, 0x2f, 0xf5, 0x82, 0xe4, 0x34, 0x7e, 0xf5,
+  0x83, 0xe5, 0x98, 0xf0, 0xd2, 0x0f, 0xe5, 0x39, 0xc3, 0x95, 0x43, 0x50, 0x03, 0x02, 0x04, 0x33,
+  0x90, 0x7f, 0xb8, 0xe0, 0x30, 0xe1, 0x16, 0xe5, 0x39, 0xc3, 0x94, 0x40, 0x50, 0x03, 0x02}},
+ {0x0386, 64, { 0x04, 0x33, 0x15, 0x39, 0x15, 0x39, 0x05, 0x2b, 0x43, 0x34, 0x01, 0x02, 0x04, 0x33, 0x90, 0x7f, 0xb7,
+  0xe5, 0x39, 0xf0, 0x75, 0x39, 0x00, 0xc2, 0x02, 0x02, 0x04, 0x33, 0x20, 0x15, 0x27, 0xaf, 0x39,
+  0x05, 0x39, 0x74, 0x00, 0x2f, 0xf5, 0x82, 0xe4, 0x34, 0x7e, 0xf5, 0x83, 0xe5, 0x99, 0xf0, 0x30,
+  0x10, 0x4d, 0xaf, 0x39, 0x05, 0x39, 0x74, 0x00, 0x2f, 0xf5, 0x82, 0xe4, 0x34, 0x7e, 0xf5}},
+ {0x03c6, 64, { 0x83, 0xe5, 0x98, 0xf0, 0x80, 0x3a, 0x85, 0x99, 0x55, 0xe5, 0x55, 0xb5, 0x47, 0x04, 0xd2, 0x09, 0x80,
+  0x2e, 0xe5, 0x55, 0xb5, 0x46, 0x04, 0xc2, 0x09, 0x80, 0x25, 0xaf, 0x39, 0x05, 0x39, 0x74, 0x00,
+  0x2f, 0xf5, 0x82, 0xe4, 0x34, 0x7e, 0xf5, 0x83, 0xe5, 0x55, 0xf0, 0x30, 0x10, 0x11, 0xaf, 0x39,
+  0x05, 0x39, 0x74, 0x00, 0x2f, 0xf5, 0x82, 0xe4, 0x34, 0x7e, 0xf5, 0x83, 0xe5, 0x98, 0xf0}},
+ {0x0406, 64, { 0xd2, 0x0f, 0xe5, 0x39, 0xc3, 0x95, 0x43, 0x40, 0x24, 0x90, 0x7f, 0xb6, 0xe0, 0x30, 0xe1, 0x12, 0xe5,
+  0x39, 0xc3, 0x94, 0x40, 0x40, 0x16, 0x15, 0x39, 0x15, 0x39, 0x05, 0x2b, 0x43, 0x34, 0x01, 0x80,
+  0x0b, 0x90, 0x7f, 0xb9, 0xe5, 0x39, 0xf0, 0x75, 0x39, 0x00, 0xd2, 0x02, 0xd2, 0x01, 0x30, 0x01,
+  0x05, 0xc2, 0x01, 0x02, 0x00, 0x56, 0xd0, 0xd0, 0xd0, 0x86, 0xd0, 0x82, 0xd0, 0x83, 0xd0}},
+ {0x0446, 64, { 0xe0, 0x32, 0x90, 0x7f, 0xbc, 0xe0, 0x20, 0xe1, 0x54, 0xe5, 0x34, 0x60, 0x50, 0xe5, 0x31, 0x70, 0x4c,
+  0xe5, 0x34, 0x30, 0xe1, 0x0b, 0xe4, 0xf5, 0x2f, 0x75, 0x34, 0x01, 0x75, 0x31, 0x02, 0x80, 0x0e,
+  0xa2, 0x08, 0xe4, 0x33, 0xf5, 0x2f, 0xc2, 0x08, 0xe4, 0xf5, 0x34, 0x75, 0x31, 0x10, 0xe4, 0xf5,
+  0x56, 0x7e, 0x00, 0x7b, 0x00, 0x74, 0x24, 0x25, 0x56, 0xf9, 0xee, 0x34, 0x00, 0xfa, 0x12}},
+ {0x0486, 64, { 0x0c, 0x79, 0xff, 0x74, 0x00, 0x25, 0x56, 0xf5, 0x82, 0xe4, 0x34, 0x7d, 0xf5, 0x83, 0xef, 0xf0, 0x05,
+  0x56, 0xe5, 0x56, 0xb4, 0x0c, 0xdb, 0x90, 0x7f, 0xbd, 0x74, 0x0c, 0xf0, 0x90, 0x7f, 0xca, 0xe0,
+  0x30, 0xe1, 0x03, 0x02, 0x05, 0xd1, 0xe4, 0xf5, 0x56, 0x74, 0x40, 0x25, 0x56, 0xf5, 0x82, 0xe4,
+  0x34, 0x7d, 0xf5, 0x83, 0xe0, 0xff, 0xe5, 0x56, 0x7c, 0x00, 0x7b, 0x00, 0x24, 0x3b, 0xf9}},
+ {0x04c6, 64, { 0xec, 0x34, 0x00, 0xfa, 0xef, 0x12, 0x0c, 0x92, 0x05, 0x56, 0xe5, 0x56, 0xb4, 0x18, 0xdb, 0xe5, 0x3b,
+  0x60, 0x11, 0x75, 0xc9, 0x20, 0x75, 0xc8, 0x36, 0x85, 0x3c, 0xca, 0x85, 0x3d, 0xcb, 0xe4, 0x90,
+  0x7f, 0x9f, 0xf0, 0xe5, 0x3e, 0x13, 0x92, 0x10, 0x92, 0x9f, 0x85, 0x3f, 0x38, 0xe5, 0x40, 0x13,
+  0x92, 0x15, 0xe5, 0x41, 0x60, 0x09, 0x90, 0x7f, 0x98, 0xe0, 0x54, 0xfb, 0xf0, 0x80, 0x07}},
+ {0x0506, 64, { 0x90, 0x7f, 0x98, 0xe0, 0x44, 0x04, 0xf0, 0xe5, 0x42, 0x60, 0x09, 0x90, 0x7f, 0x98, 0xe0, 0x54, 0x7f,
+  0xf0, 0x80, 0x07, 0x90, 0x7f, 0x98, 0xe0, 0x44, 0x80, 0xf0, 0xe5, 0x48, 0x60, 0x0b, 0xc2, 0x0c,
+  0xc2, 0x09, 0x90, 0x7f, 0x95, 0xe0, 0x44, 0x02, 0xf0, 0xe5, 0x49, 0x60, 0x0c, 0xd2, 0x09, 0x43,
+  0x34, 0x01, 0x90, 0x7f, 0x95, 0xe0, 0x44, 0x02, 0xf0, 0xe5, 0x4a, 0x60, 0x0d, 0xc2, 0xaf}},
+ {0x0546, 64, { 0xc2, 0x0b, 0xd2, 0x00, 0xe4, 0xf5, 0x53, 0xf5, 0x3a, 0xd2, 0xaf, 0xe5, 0x4b, 0x60, 0x05, 0x30, 0x15,
+  0x02, 0xd2, 0x09, 0xe5, 0x4c, 0x60, 0x15, 0x90, 0x7f, 0x95, 0xe0, 0x54, 0xfd, 0xf0, 0x90, 0x7f,
+  0x9e, 0xe0, 0x44, 0x02, 0xf0, 0x90, 0x7f, 0x98, 0xe0, 0x54, 0xfd, 0xf0, 0xe5, 0x4d, 0x60, 0x0a,
+  0xd2, 0x9c, 0xc2, 0x98, 0x75, 0x2c, 0x01, 0x75, 0x31, 0x1e, 0xe5, 0x4e, 0x60, 0x07, 0xc2}},
+ {0x0586, 64, { 0x9c, 0xe4, 0xf5, 0x39, 0xf5, 0x2c, 0xe5, 0x4f, 0x60, 0x03, 0xe4, 0xf5, 0x39, 0xe5, 0x50, 0x60, 0x02,
+  0xd2, 0x07, 0xe5, 0x51, 0x60, 0x0a, 0xe5, 0x4d, 0x70, 0x02, 0xf5, 0x31, 0xe5, 0x51, 0x42, 0x34,
+  0xe5, 0x52, 0x60, 0x1f, 0x90, 0x7f, 0xd7, 0x74, 0x11, 0xf0, 0x74, 0x31, 0xf0, 0x74, 0x12, 0xf0,
+  0x74, 0x32, 0xf0, 0x74, 0x13, 0xf0, 0x74, 0x33, 0xf0, 0x74, 0x14, 0xf0, 0x74, 0x34, 0xf0}},
+ {0x05c6, 64, { 0xd2, 0x03, 0xd2, 0x02, 0xd2, 0x08, 0xe4, 0x90, 0x7f, 0xcb, 0xf0, 0xa2, 0x0c, 0xe4, 0x33, 0xff, 0x65,
+  0x29, 0x60, 0x05, 0x8f, 0x29, 0x43, 0x34, 0x01, 0xa2, 0x09, 0xe4, 0x33, 0xff, 0x65, 0x2a, 0x60,
+  0x05, 0x8f, 0x2a, 0x43, 0x34, 0x01, 0x90, 0x7f, 0x9b, 0xe0, 0xff, 0x54, 0x08, 0x64, 0x08, 0xf5,
+  0x57, 0x65, 0x25, 0x60, 0x06, 0x85, 0x57, 0x25, 0x43, 0x34, 0x01, 0xef, 0x54, 0x10, 0x64}},
+ {0x0606, 64, { 0x10, 0xf5, 0x57, 0x65, 0x26, 0x60, 0x06, 0x85, 0x57, 0x26, 0x43, 0x34, 0x01, 0xef, 0x54, 0x40, 0x64,
+  0x40, 0xf5, 0x57, 0x65, 0x27, 0x60, 0x06, 0x85, 0x57, 0x27, 0x43, 0x34, 0x01, 0xef, 0x54, 0x20,
+  0x64, 0x20, 0xf5, 0x57, 0x65, 0x28, 0x60, 0x06, 0x85, 0x57, 0x28, 0x43, 0x34, 0x01, 0x90, 0x7f,
+  0x9a, 0xe0, 0x54, 0x40, 0x64, 0x40, 0xf5, 0x57, 0x65, 0x2e, 0x60, 0x06, 0x85, 0x57, 0x2e}},
+ {0x0646, 64, { 0x43, 0x34, 0x01, 0x30, 0x07, 0x35, 0xc2, 0xaf, 0x30, 0x02, 0x18, 0x90, 0x7f, 0xb8, 0xe0, 0x20, 0xe1,
+  0x27, 0xe5, 0x39, 0x60, 0x09, 0x90, 0x7f, 0xb7, 0xf0, 0xe4, 0xf5, 0x39, 0xc2, 0x02, 0xc2, 0x07,
+  0x80, 0x16, 0x90, 0x7f, 0xb6, 0xe0, 0x20, 0xe1, 0x0f, 0xe5, 0x39, 0x60, 0x09, 0x90, 0x7f, 0xb9,
+  0xf0, 0xe4, 0xf5, 0x39, 0xd2, 0x02, 0xc2, 0x07, 0xd2, 0xaf, 0x20, 0x05, 0x3d, 0x30, 0x03}},
+ {0x0686, 64, { 0x1e, 0x90, 0x7f, 0xc6, 0xe0, 0x20, 0xe1, 0x33, 0x90, 0x7e, 0x40, 0xe0, 0x13, 0x92, 0x0d, 0x75, 0x3a,
+  0x01, 0x90, 0x7f, 0xc7, 0xe0, 0xf5, 0x53, 0xd2, 0x05, 0x75, 0x12, 0xff, 0x80, 0x1c, 0x90, 0x7f,
+  0xc8, 0xe0, 0x20, 0xe1, 0x15, 0x90, 0x7d, 0xc0, 0xe0, 0x13, 0x92, 0x0d, 0x75, 0x3a, 0x01, 0x90,
+  0x7f, 0xc9, 0xe0, 0xf5, 0x53, 0xd2, 0x05, 0x75, 0x12, 0xff, 0x20, 0x14, 0x33, 0x20, 0x00}},
+ {0x06c6, 64, { 0x06, 0xe5, 0x3a, 0x65, 0x53, 0x70, 0x2a, 0x30, 0x05, 0x1a, 0x30, 0x03, 0x09, 0xe4, 0x90, 0x7f, 0xc7,
+  0xf0, 0xc2, 0x03, 0x80, 0x07, 0xe4, 0x90, 0x7f, 0xc9, 0xf0, 0xd2, 0x03, 0xc2, 0x05, 0xe4, 0xf5,
+  0x53, 0xf5, 0x3a, 0x30, 0x0d, 0x0a, 0xc2, 0x0d, 0xc2, 0x00, 0x90, 0x7f, 0xbb, 0x74, 0x01, 0xf0,
+  0x30, 0x14, 0x03, 0x02, 0x07, 0xbf, 0x20, 0x05, 0x03, 0x02, 0x07, 0xbf, 0x30, 0x0c, 0x03}},
+ {0x0706, 64, { 0x02, 0x07, 0xbf, 0x30, 0x09, 0x03, 0x02, 0x07, 0xbf, 0x90, 0x7f, 0x9b, 0xe0, 0x55, 0x38, 0x60, 0x03,
+  0x02, 0x07, 0xbf, 0x30, 0x03, 0x0c, 0x7e, 0x7e, 0x7f, 0x40, 0x75, 0x58, 0x7e, 0x75, 0x59, 0x40,
+  0x80, 0x0a, 0x7e, 0x7d, 0x7f, 0xc0, 0x75, 0x58, 0x7d, 0x75, 0x59, 0xc0, 0x30, 0x10, 0x12, 0xaf,
+  0x3a, 0x05, 0x3a, 0xe5, 0x59, 0x2f, 0xf5, 0x82, 0xe4, 0x35, 0x58, 0xf5, 0x83, 0xe0, 0x13}},
+ {0x0746, 64, { 0x92, 0x1a, 0xaf, 0x3a, 0x05, 0x3a, 0xe5, 0x59, 0x2f, 0xf5, 0x82, 0xe4, 0x35, 0x58, 0xf5, 0x83, 0xe0,
+  0xf5, 0x57, 0xe5, 0x3a, 0xc3, 0x95, 0x53, 0x50, 0x2a, 0x30, 0x10, 0x12, 0xaf, 0x3a, 0x05, 0x3a,
+  0xe5, 0x59, 0x2f, 0xf5, 0x82, 0xe4, 0x35, 0x58, 0xf5, 0x83, 0xe0, 0x13, 0x92, 0x0e, 0xaf, 0x3a,
+  0x05, 0x3a, 0xe5, 0x59, 0x2f, 0xf5, 0x82, 0xe4, 0x35, 0x58, 0xf5, 0x83, 0xe0, 0xf5, 0x36}},
+ {0x0786, 64, { 0xd2, 0x0b, 0x80, 0x15, 0xc2, 0x0b, 0x30, 0x03, 0x09, 0xe4, 0x90, 0x7f, 0xc7, 0xf0, 0xc2, 0x03, 0x80,
+  0x07, 0xe4, 0x90, 0x7f, 0xc9, 0xf0, 0xd2, 0x03, 0x30, 0x10, 0x04, 0xa2, 0x1a, 0x92, 0x9b, 0xd2,
+  0x14, 0xc2, 0xaf, 0x85, 0x57, 0x99, 0x20, 0x0b, 0x0d, 0x30, 0x0d, 0x0a, 0xc2, 0x0d, 0xc2, 0x00,
+  0x90, 0x7f, 0xbb, 0x74, 0x01, 0xf0, 0xd2, 0xaf, 0x22, 0x90, 0x7f, 0xe9, 0xe0, 0x12, 0x0c}},
+ {0x07c6, 64, { 0xa4, 0x09, 0x1c, 0x00, 0x09, 0x89, 0x01, 0x09, 0xe6, 0x03, 0x07, 0xe3, 0x06, 0x09, 0x0d, 0x08, 0x09,
+  0x01, 0x09, 0x08, 0xe9, 0x0a, 0x08, 0xf8, 0x0b, 0x00, 0x00, 0x0a, 0x24, 0x90, 0x7f, 0xeb, 0xe0,
+  0x24, 0xfe, 0x60, 0x1c, 0x14, 0x70, 0x03, 0x02, 0x08, 0x79, 0x24, 0x02, 0x60, 0x03, 0x02, 0x0a,
+  0x24, 0x74, 0x0d, 0x90, 0x7f, 0xd4, 0xf0, 0x74, 0x87, 0x90, 0x7f, 0xd5, 0xf0, 0x02, 0x0a}},
+ {0x0806, 64, { 0x2b, 0x90, 0x7f, 0xea, 0xe0, 0xb4, 0x01, 0x04, 0x7f, 0x02, 0x80, 0x02, 0x7f, 0x03, 0x75, 0x82, 0xd8,
+  0x75, 0x83, 0x0d, 0xef, 0xf0, 0x75, 0x82, 0xd1, 0x75, 0x83, 0x0d, 0xf0, 0x75, 0x82, 0xca, 0x75,
+  0x83, 0x0d, 0xf0, 0x75, 0x82, 0xc3, 0x75, 0x83, 0x0d, 0xf0, 0x90, 0x7f, 0xea, 0xe0, 0x04, 0x75,
+  0x82, 0x9e, 0x75, 0x83, 0x0d, 0xf0, 0x90, 0x7f, 0xef, 0xe0, 0xfe, 0x90, 0x7f, 0xee, 0xe0}},
+ {0x0846, 64, { 0x7c, 0x00, 0x24, 0x00, 0xf5, 0x5a, 0xec, 0x3e, 0xf5, 0x59, 0x75, 0x15, 0x0d, 0x75, 0x16, 0x99, 0x75,
+  0x82, 0x9b, 0x75, 0x83, 0x0d, 0xe0, 0x75, 0x13, 0x00, 0xf5, 0x14, 0xd3, 0xe5, 0x14, 0x95, 0x5a,
+  0xe5, 0x13, 0x95, 0x59, 0x40, 0x06, 0x85, 0x59, 0x13, 0x85, 0x5a, 0x14, 0x12, 0x0b, 0xba, 0x02,
+  0x0a, 0x2b, 0x90, 0x7f, 0xea, 0xe0, 0x70, 0x0b, 0x75, 0x56, 0xff, 0x75, 0x57, 0x0d, 0x75}},
+ {0x0886, 64, { 0x58, 0xdc, 0x80, 0x2d, 0x90, 0x7f, 0xea, 0xe0, 0xb4, 0x01, 0x0b, 0x75, 0x56, 0xff, 0x75, 0x57, 0x0d,
+  0x75, 0x58, 0xe0, 0x80, 0x1b, 0x90, 0x7f, 0xea, 0xe0, 0xb4, 0x02, 0x0b, 0x75, 0x56, 0xff, 0x75,
+  0x57, 0x0d, 0x75, 0x58, 0xf0, 0x80, 0x09, 0x75, 0x56, 0xff, 0x75, 0x57, 0x0e, 0x75, 0x58, 0x1e,
+  0x90, 0x7f, 0xee, 0xe0, 0x75, 0x59, 0x00, 0xf5, 0x5a, 0xae, 0x57, 0xaf, 0x58, 0x8e, 0x15}},
+ {0x08c6, 64, { 0x8f, 0x16, 0x8f, 0x82, 0x8e, 0x83, 0xe0, 0xfe, 0xa3, 0xe0, 0x8e, 0x13, 0xf5, 0x14, 0xd3, 0x95, 0x5a,
+  0xe5, 0x13, 0x95, 0x59, 0x40, 0x06, 0x85, 0x59, 0x13, 0x85, 0x5a, 0x14, 0x12, 0x0b, 0xba, 0x02,
+  0x0a, 0x2b, 0x90, 0x7f, 0x00, 0xe5, 0x11, 0xf0, 0x90, 0x7f, 0xb5, 0x74, 0x01, 0xf0, 0x02, 0x0a,
+  0x2b, 0x90, 0x7f, 0xea, 0xe0, 0xf5, 0x11, 0x02, 0x0a, 0x2b, 0x12, 0x0c, 0x24, 0x90, 0x7f}},
+ {0x0906, 64, { 0xea, 0xe0, 0xf5, 0x10, 0x02, 0x0a, 0x2b, 0x90, 0x7f, 0x00, 0xe5, 0x10, 0xf0, 0x90, 0x7f, 0xb5, 0x74,
+  0x01, 0xf0, 0x02, 0x0a, 0x2b, 0x90, 0x7f, 0xe8, 0xe0, 0x24, 0x7f, 0x60, 0x27, 0x14, 0x60, 0x34,
+  0x24, 0x02, 0x60, 0x03, 0x02, 0x0a, 0x24, 0xa2, 0x16, 0xe4, 0x33, 0xff, 0x25, 0xe0, 0xff, 0xa2,
+  0x18, 0xe4, 0x33, 0x4f, 0x90, 0x7f, 0x00, 0xf0, 0xe4, 0xa3, 0xf0, 0x90, 0x7f, 0xb5, 0x74}},
+ {0x0946, 64, { 0x02, 0xf0, 0x02, 0x0a, 0x2b, 0xe4, 0x90, 0x7f, 0x00, 0xf0, 0xa3, 0xf0, 0x90, 0x7f, 0xb5, 0x74, 0x02,
+  0xf0, 0x02, 0x0a, 0x2b, 0x90, 0x7f, 0xec, 0xe0, 0xf4, 0x54, 0x80, 0xff, 0xc4, 0x54, 0x0f, 0xff,
+  0xe0, 0x54, 0x07, 0x2f, 0x25, 0xe0, 0x24, 0xb4, 0xf5, 0x82, 0xe4, 0x34, 0x7f, 0xf5, 0x83, 0xe0,
+  0x54, 0xfd, 0x90, 0x7f, 0x00, 0xf0, 0xe4, 0xa3, 0xf0, 0x90, 0x7f, 0xb5, 0x74, 0x02, 0xf0}},
+ {0x0986, 64, { 0x02, 0x0a, 0x2b, 0x90, 0x7f, 0xe8, 0xe0, 0x24, 0xfe, 0x60, 0x17, 0x24, 0x02, 0x60, 0x03, 0x02, 0x0a,
+  0x2b, 0x90, 0x7f, 0xea, 0xe0, 0x64, 0x01, 0x60, 0x03, 0x02, 0x0a, 0x24, 0xc2, 0x16, 0x02, 0x0a,
+  0x2b, 0x90, 0x7f, 0xea, 0xe0, 0x70, 0x76, 0x90, 0x7f, 0xec, 0xe0, 0xf4, 0x54, 0x80, 0xff, 0xc4,
+  0x54, 0x0f, 0xff, 0xe0, 0x54, 0x07, 0x2f, 0x25, 0xe0, 0x24, 0xb4, 0xf5, 0x82, 0xe4, 0x34}},
+ {0x09c6, 64, { 0x7f, 0xf5, 0x83, 0xe4, 0xf0, 0x90, 0x7f, 0xec, 0xe0, 0x54, 0x80, 0xff, 0x13, 0x13, 0x13, 0x54, 0x1f,
+  0xff, 0xe0, 0x54, 0x07, 0x2f, 0x90, 0x7f, 0xd7, 0xf0, 0xe0, 0x44, 0x20, 0xf0, 0x80, 0x45, 0x90,
+  0x7f, 0xe8, 0xe0, 0x24, 0xfe, 0x60, 0x10, 0x24, 0x02, 0x70, 0x39, 0x90, 0x7f, 0xea, 0xe0, 0x64,
+  0x01, 0x70, 0x2a, 0xd2, 0x16, 0x80, 0x2d, 0x90, 0x7f, 0xea, 0xe0, 0x70, 0x20, 0x90, 0x7f}},
+ {0x0a06, 64, { 0xec, 0xe0, 0xf4, 0x54, 0x80, 0xff, 0xc4, 0x54, 0x0f, 0xff, 0xe0, 0x54, 0x07, 0x2f, 0x25, 0xe0, 0x24,
+  0xb4, 0xf5, 0x82, 0xe4, 0x34, 0x7f, 0xf5, 0x83, 0x74, 0x01, 0xf0, 0x80, 0x07, 0x90, 0x7f, 0xb4,
+  0xe0, 0x44, 0x01, 0xf0, 0x90, 0x7f, 0xb4, 0xe0, 0x44, 0x02, 0xf0, 0x22, 0xc2, 0x10, 0xe4, 0xf5,
+  0x10, 0xf5, 0x34, 0xc2, 0x09, 0xc2, 0x0c, 0xc2, 0x0b, 0xc2, 0x14, 0xc2, 0x0d, 0xc2, 0x15}},
+ {0x0a46, 64, { 0xc2, 0x11, 0xc2, 0x07, 0xc2, 0x12, 0xc2, 0x0f, 0xc2, 0x08, 0xf5, 0x35, 0xf5, 0x39, 0xf5, 0x53, 0xf5,
+  0x3a, 0xf5, 0x33, 0xf5, 0x30, 0xf5, 0x2f, 0xf5, 0x2e, 0xf5, 0x2d, 0xf5, 0x2c, 0xf5, 0x2b, 0xf5,
+  0x2a, 0xf5, 0x29, 0xf5, 0x28, 0xf5, 0x27, 0xf5, 0x26, 0xf5, 0x25, 0xf5, 0x24, 0xc2, 0x05, 0xc2,
+  0x17, 0xc2, 0x19, 0xc2, 0x16, 0xc2, 0x18, 0xc2, 0x04, 0xd2, 0x13, 0xc2, 0x06, 0xc2, 0x01}},
+ {0x0a86, 64, { 0x90, 0x7f, 0x92, 0xe0, 0x54, 0xfd, 0xf0, 0xd2, 0xe8, 0x43, 0xd8, 0x20, 0x90, 0x7f, 0xde, 0x74, 0x01,
+  0xf0, 0x90, 0x7f, 0xdf, 0xf0, 0x90, 0x7f, 0xab, 0x74, 0xff, 0xf0, 0x90, 0x7f, 0xa9, 0xf0, 0x90,
+  0x7f, 0xaa, 0xf0, 0x53, 0x91, 0xef, 0x90, 0x7f, 0xaf, 0xe0, 0x44, 0x01, 0xf0, 0x90, 0x7f, 0xae,
+  0xe0, 0x44, 0x0f, 0xf0, 0x90, 0x7f, 0xac, 0x74, 0x0e, 0xf0, 0xd2, 0xaf, 0xd2, 0xbc, 0xd2}},
+ {0x0ac6, 64, { 0x1a, 0x12, 0x0f, 0x7d, 0xc2, 0x17, 0x30, 0x04, 0x03, 0x12, 0x04, 0x48, 0x30, 0x04, 0x2a, 0x30, 0x06,
+  0x27, 0xc2, 0x06, 0xe5, 0x12, 0x60, 0x16, 0x15, 0x12, 0x90, 0x7f, 0xd8, 0xe0, 0x30, 0xe6, 0x04,
+  0x7f, 0x00, 0x80, 0x02, 0x7f, 0x20, 0x90, 0x7f, 0x96, 0xef, 0xf0, 0x80, 0x06, 0x90, 0x7f, 0x96,
+  0x74, 0x20, 0xf0, 0x12, 0x0b, 0x14, 0x80, 0xcd, 0x30, 0x17, 0x07, 0xc2, 0x17, 0x12, 0x07}},
+ {0x0b06, 64, { 0xc0, 0x80, 0xc3, 0x30, 0x19, 0xc0, 0xc2, 0x19, 0x12, 0x0e, 0xdc, 0x80, 0xb9, 0x22, 0xe5, 0x31, 0x60,
+  0x02, 0x15, 0x31, 0xe5, 0x39, 0x60, 0x55, 0x65, 0x35, 0x70, 0x4b, 0xe5, 0x33, 0xf4, 0x60, 0x02,
+  0x05, 0x33, 0xe5, 0x33, 0xc3, 0x95, 0x44, 0x40, 0x43, 0xc2, 0xaf, 0x30, 0x02, 0x1b, 0x90, 0x7f,
+  0xb8, 0xe0, 0x20, 0xe1, 0x2d, 0x90, 0x7f, 0xb7, 0xe5, 0x39, 0xf0, 0xc2, 0x02, 0xe4, 0xf5}},
+ {0x0b46, 64, { 0x39, 0xf5, 0x33, 0xf5, 0x35, 0x75, 0x12, 0xff, 0x80, 0x19, 0x90, 0x7f, 0xb6, 0xe0, 0x20, 0xe1, 0x12,
+  0x90, 0x7f, 0xb9, 0xe5, 0x39, 0xf0, 0xd2, 0x02, 0xe4, 0xf5, 0x39, 0xf5, 0x33, 0xf5, 0x35, 0x75,
+  0x12, 0xff, 0xd2, 0xaf, 0x80, 0x06, 0x85, 0x39, 0x35, 0xe4, 0xf5, 0x33, 0xe5, 0x2c, 0x60, 0x30,
+  0x20, 0x0f, 0x07, 0x90, 0x7f, 0x9b, 0xe0, 0x30, 0xe0, 0x0f, 0xe5, 0x2d, 0x60, 0x06, 0xe4}},
+ {0x0b86, 64, { 0xf5, 0x2d, 0x43, 0x34, 0x01, 0xe4, 0xf5, 0x30, 0x80, 0x14, 0xe5, 0x30, 0xd3, 0x95, 0x45, 0x50, 0x0d,
+  0xe5, 0x30, 0xb5, 0x45, 0x06, 0x75, 0x2d, 0x01, 0x43, 0x34, 0x01, 0x05, 0x30, 0xc2, 0x0f, 0x22,
+  0x90, 0x7f, 0xd9, 0xe0, 0x30, 0xe2, 0x04, 0x7f, 0x00, 0x80, 0x02, 0x7f, 0x20, 0x90, 0x7f, 0x96,
+  0xef, 0xf0, 0x22, 0xe5, 0x14, 0x45, 0x13, 0x60, 0x57, 0xae, 0x13, 0xaf, 0x14, 0xd3, 0xef}},
+ {0x0bc6, 64, { 0x94, 0x40, 0xee, 0x94, 0x00, 0x40, 0x04, 0x7e, 0x00, 0x7f, 0x40, 0xc3, 0xe5, 0x14, 0x9f, 0xf5, 0x14,
+  0xe5, 0x13, 0x9e, 0xf5, 0x13, 0xe4, 0xfd, 0xed, 0xc3, 0x9f, 0xe4, 0x9e, 0x50, 0x1f, 0x85, 0x16,
+  0x82, 0x85, 0x15, 0x83, 0xe0, 0xfc, 0x74, 0x00, 0x2d, 0xf5, 0x82, 0xe4, 0x34, 0x7f, 0xf5, 0x83,
+  0xec, 0xf0, 0x0d, 0x05, 0x16, 0xe5, 0x16, 0x70, 0x02, 0x05, 0x15, 0x80, 0xda, 0x90, 0x7f}},
+ {0x0c06, 64, { 0xa9, 0x74, 0x01, 0xf0, 0x90, 0x7f, 0xac, 0xe0, 0x44, 0x01, 0xf0, 0x90, 0x7f, 0xb5, 0xef, 0xf0, 0x22,
+  0x90, 0x7f, 0xac, 0xe0, 0x54, 0xfe, 0xf0, 0xe4, 0x90, 0x7f, 0xb5, 0xf0, 0x22, 0xe4, 0x90, 0x7f,
+  0x93, 0xf0, 0x90, 0x7f, 0x9c, 0x74, 0x30, 0xf0, 0x90, 0x7f, 0x96, 0x74, 0x20, 0xf0, 0x90, 0x7f,
+  0x94, 0x74, 0x01, 0xf0, 0x90, 0x7f, 0x9d, 0x74, 0xff, 0xf0, 0x90, 0x7f, 0x97, 0x74, 0x86}},
+ {0x0c46, 64, { 0xf0, 0x90, 0x7f, 0x95, 0x74, 0x03, 0xf0, 0x90, 0x7f, 0x9e, 0x74, 0x84, 0xf0, 0x90, 0x7f, 0x98, 0xf0,
+  0xe4, 0x90, 0x7f, 0xc7, 0xf0, 0x90, 0x7f, 0xc9, 0xf0, 0x90, 0x7f, 0xcb, 0xf0, 0x75, 0x98, 0x40,
+  0x43, 0xa8, 0x10, 0x90, 0x7f, 0xde, 0x74, 0x1f, 0xf0, 0x90, 0x7f, 0xdf, 0x74, 0x0f, 0xf0, 0xd2,
+  0x04, 0x22, 0xbb, 0x01, 0x06, 0x89, 0x82, 0x8a, 0x83, 0xe0, 0x22, 0x50, 0x02, 0xe7, 0x22}},
+ {0x0c86, 64, { 0xbb, 0xfe, 0x02, 0xe3, 0x22, 0x89, 0x82, 0x8a, 0x83, 0xe4, 0x93, 0x22, 0xbb, 0x01, 0x06, 0x89, 0x82,
+  0x8a, 0x83, 0xf0, 0x22, 0x50, 0x02, 0xf7, 0x22, 0xbb, 0xfe, 0x01, 0xf3, 0x22, 0xd0, 0x83, 0xd0,
+  0x82, 0xf8, 0xe4, 0x93, 0x70, 0x12, 0x74, 0x01, 0x93, 0x70, 0x0d, 0xa3, 0xa3, 0x93, 0xf8, 0x74,
+  0x01, 0x93, 0xf5, 0x82, 0x88, 0x83, 0xe4, 0x73, 0x74, 0x02, 0x93, 0x68, 0x60, 0xef, 0xa3}},
+ {0x0cc6, 64, { 0xa3, 0xa3, 0x80, 0xdf, 0xe4, 0x90, 0x7f, 0x95, 0xf0, 0x90, 0x7f, 0x94, 0xf0, 0x90, 0x7f, 0x93, 0xf0,
+  0x90, 0x7f, 0x9d, 0xe0, 0x44, 0x02, 0xf0, 0x90, 0x7f, 0x97, 0xe0, 0x44, 0x42, 0xf0, 0x90, 0x7f,
+  0x9c, 0x74, 0x10, 0xf0, 0xe4, 0x90, 0x7f, 0x96, 0xf0, 0x90, 0x7f, 0x9d, 0x74, 0xbe, 0xf0, 0x30,
+  0x16, 0x04, 0x7f, 0x80, 0x80, 0x02, 0x7f, 0x00, 0x90, 0x7f, 0x97, 0xef, 0xf0, 0xe4, 0x90}},
+ {0x0d06, 64, { 0x7f, 0x95, 0xf0, 0x90, 0x7f, 0x9e, 0xf0, 0x90, 0x7f, 0x98, 0xf0, 0x22, 0xc0, 0xe0, 0xc0, 0xf0, 0xc0,
+  0x83, 0xc0, 0x82, 0xc0, 0x85, 0xc0, 0x84, 0xc0, 0x86, 0x75, 0x86, 0x00, 0xc0, 0xd0, 0x75, 0xd0,
+  0x08, 0x53, 0x91, 0xef, 0x90, 0x7f, 0xa9, 0x74, 0x01, 0xf0, 0x12, 0x0b, 0xba, 0xd0, 0xd0, 0xd0,
+  0x86, 0xd0, 0x84, 0xd0, 0x85, 0xd0, 0x82, 0xd0, 0x83, 0xd0, 0xf0, 0xd0, 0xe0, 0x32, 0xc0}},
+ {0x0d46, 64, { 0xe0, 0xc0, 0x83, 0xc0, 0x82, 0xc0, 0x85, 0xc0, 0x84, 0xc0, 0x86, 0x75, 0x86, 0x00, 0x90, 0x7f, 0xc4,
+  0xe4, 0xf0, 0x53, 0x91, 0xef, 0x90, 0x7f, 0xab, 0x74, 0x04, 0xf0, 0xd0, 0x86, 0xd0, 0x84, 0xd0,
+  0x85, 0xd0, 0x82, 0xd0, 0x83, 0xd0, 0xe0, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+ {0x0d86, 64, { 0x00, 0x12, 0x01, 0x10, 0x01, 0xff, 0xff, 0xff, 0x40, 0xcd, 0x06, 0x1c, 0x01, 0x01, 0x00, 0x01, 0x02,
+  0x00, 0x02, 0x09, 0x02, 0x43, 0x00, 0x01, 0x01, 0x00, 0x80, 0x32, 0x09, 0x04, 0x00, 0x00, 0x07,
+  0xff, 0x00, 0x00, 0x00, 0x07, 0x05, 0x01, 0x02, 0x40, 0x00, 0x00, 0x07, 0x05, 0x02, 0x02, 0x40,
+  0x00, 0x00, 0x07, 0x05, 0x03, 0x02, 0x40, 0x00, 0x00, 0x07, 0x05, 0x81, 0x02, 0x40, 0x00}},
+ {0x0dc6, 64, { 0x01, 0x07, 0x05, 0x82, 0x02, 0x40, 0x00, 0x01, 0x07, 0x05, 0x83, 0x02, 0x40, 0x00, 0x01, 0x07, 0x05,
+  0x84, 0x02, 0x40, 0x00, 0x01, 0x04, 0x03, 0x09, 0x04, 0x10, 0x03, 0x4b, 0x00, 0x65, 0x00, 0x79,
+  0x00, 0x73, 0x00, 0x70, 0x00, 0x61, 0x00, 0x6e, 0x00, 0x2e, 0x03, 0x4b, 0x00, 0x65, 0x00, 0x79,
+  0x00, 0x73, 0x00, 0x70, 0x00, 0x61, 0x00, 0x6e, 0x00, 0x20, 0x00, 0x55, 0x00, 0x50, 0x00}},
+ {0x0e06, 64, { 0x53, 0x00, 0x48, 0x00, 0x31, 0x00, 0x31, 0x00, 0x32, 0x00, 0x2d, 0x00, 0x53, 0x00, 0x65, 0x00, 0x72,
+  0x00, 0x69, 0x00, 0x61, 0x00, 0x6c, 0x00, 0x22, 0x03, 0x55, 0x00, 0x53, 0x00, 0x41, 0x00, 0x2d,
+  0x00, 0x35, 0x00, 0x33, 0x00, 0x20, 0x00, 0x32, 0x00, 0x30, 0x00, 0x30, 0x00, 0x32, 0x00, 0x61,
+  0x00, 0x70, 0x00, 0x72, 0x00, 0x32, 0x00, 0x36, 0x00, 0x00, 0x00, 0xc0, 0xe0, 0xc0, 0x83}},
+ {0x0e46, 64, { 0xc0, 0x82, 0xc0, 0x85, 0xc0, 0x84, 0xc0, 0x86, 0x75, 0x86, 0x00, 0x53, 0x91, 0xef, 0x90, 0x7f, 0xab,
+  0x74, 0x02, 0xf0, 0xd2, 0x06, 0xd0, 0x86, 0xd0, 0x84, 0xd0, 0x85, 0xd0, 0x82, 0xd0, 0x83, 0xd0,
+  0xe0, 0x32, 0xc0, 0xe0, 0xc0, 0x83, 0xc0, 0x82, 0xc0, 0x85, 0xc0, 0x84, 0xc0, 0x86, 0x75, 0x86,
+  0x00, 0xd2, 0x17, 0x53, 0x91, 0xef, 0x90, 0x7f, 0xab, 0x74, 0x01, 0xf0, 0xd0, 0x86, 0xd0}},
+ {0x0e86, 64, { 0x84, 0xd0, 0x85, 0xd0, 0x82, 0xd0, 0x83, 0xd0, 0xe0, 0x32, 0xc0, 0xe0, 0xc0, 0x83, 0xc0, 0x82, 0xc0,
+  0x85, 0xc0, 0x84, 0xc0, 0x86, 0x75, 0x86, 0x00, 0xd2, 0x19, 0x53, 0x91, 0xef, 0x90, 0x7f, 0xab,
+  0x74, 0x08, 0xf0, 0xd0, 0x86, 0xd0, 0x84, 0xd0, 0x85, 0xd0, 0x82, 0xd0, 0x83, 0xd0, 0xe0, 0x32,
+  0xc0, 0xe0, 0xc0, 0x83, 0xc0, 0x82, 0xc0, 0x85, 0xc0, 0x84, 0xc0, 0x86, 0x75, 0x86, 0x00}},
+ {0x0ec6, 64, { 0x53, 0x91, 0xef, 0x90, 0x7f, 0xa9, 0x74, 0x02, 0xf0, 0xd0, 0x86, 0xd0, 0x84, 0xd0, 0x85, 0xd0, 0x82,
+  0xd0, 0x83, 0xd0, 0xe0, 0x32, 0x12, 0x0c, 0xca, 0x12, 0x0f, 0xb3, 0x90, 0x7f, 0xd6, 0xe0, 0x30,
+  0xe7, 0x12, 0xe0, 0x44, 0x01, 0xf0, 0x7f, 0x0d, 0x7e, 0x00, 0x12, 0x00, 0x03, 0x90, 0x7f, 0xd6,
+  0xe0, 0x54, 0xfe, 0xf0, 0x12, 0x0c, 0x24, 0x22, 0x00, 0x02, 0x0e, 0x69, 0x00, 0x02, 0x0e}},
+ {0x0f06, 64, { 0x42, 0x00, 0x02, 0x0d, 0x45, 0x00, 0x02, 0x0e, 0x90, 0x00, 0x02, 0x0f, 0x10, 0x00, 0x02, 0x0f, 0x14,
+  0x00, 0x02, 0x0d, 0x12, 0x00, 0x02, 0x0f, 0x1c, 0x00, 0x02, 0x0e, 0xb7, 0x00, 0x02, 0x0f, 0x24,
+  0x00, 0x02, 0x0f, 0x33, 0x00, 0x02, 0x0f, 0x2c, 0x00, 0x02, 0x0f, 0x58, 0xc0, 0xe0, 0xc0, 0x83,
+  0xc0, 0x82, 0xc0, 0x85, 0xc0, 0x84, 0xc0, 0x86, 0x75, 0x86, 0x00, 0x53, 0x91, 0xef, 0x90}},
+ {0x0f46, 64, { 0x7f, 0xa9, 0x74, 0x04, 0xf0, 0xd0, 0x86, 0xd0, 0x84, 0xd0, 0x85, 0xd0, 0x82, 0xd0, 0x83, 0xd0, 0xe0,
+  0x32, 0xc0, 0xe0, 0xc0, 0x83, 0xc0, 0x82, 0xc0, 0x85, 0xc0, 0x84, 0xc0, 0x86, 0x75, 0x86, 0x00,
+  0x53, 0x91, 0xef, 0x90, 0x7f, 0xa9, 0x74, 0x08, 0xf0, 0xd0, 0x86, 0xd0, 0x84, 0xd0, 0x85, 0xd0,
+  0x82, 0xd0, 0x83, 0xd0, 0xe0, 0x32, 0x90, 0x7f, 0xd6, 0xe0, 0x54, 0xfb, 0xf0, 0xe0, 0x44}},
+ {0x0f86, 61, { 0x08, 0xf0, 0x30, 0x1a, 0x04, 0xe0, 0x44, 0x02, 0xf0, 0x7f, 0xf4, 0x7e, 0x01, 0x12, 0x00, 0x03, 0x90,
+  0x7f, 0xd6, 0xe0, 0x54, 0xf7, 0xf0, 0xe0, 0x44, 0x04, 0xf0, 0x22, 0x74, 0x00, 0xf5, 0x86, 0x90,
+  0xfd, 0xa5, 0x7c, 0x05, 0xa3, 0xe5, 0x82, 0x45, 0x83, 0x70, 0xf9, 0x22, 0x90, 0x7f, 0xd6, 0xe0,
+  0x44, 0x80, 0xf0, 0x43, 0x87, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22}},
+ {0xffff,	0,	{0x00}}
+};
+
diff -urN linux-2.4.21-pre5/drivers/usb/serial/keyspan_usa49wlc_fw.h linux-2.4.21-pre6/drivers/usb/serial/keyspan_usa49wlc_fw.h
--- linux-2.4.21-pre5/drivers/usb/serial/keyspan_usa49wlc_fw.h	Wed Dec 31 16:00:00 1969
+++ linux-2.4.21-pre6/drivers/usb/serial/keyspan_usa49wlc_fw.h	Wed Mar 26 16:11:39 2003
@@ -0,0 +1,476 @@
+/* keyspan_usa49w_fw.h
+
+	The firmware contained herein as keyspan_usa49w_fw.h is
+
+		Copyright (C) 1999-2003
+		Keyspan, A division of InnoSys Incorporated ("Keyspan")
+		
+	as an unpublished work. This notice does not imply unrestricted or
+	public access to the source code from which this firmware image is
+	derived.  Except as noted below this firmware image may not be 
+	reproduced, used, sold or transferred to any third party without 
+	Keyspan's prior written consent.  All Rights Reserved.
+
+	Permission is hereby granted for the distribution of this firmware 
+	image as part of a Linux or other Open Source operating system kernel 
+	in text or binary form as required. 
+
+	This firmware may not be modified and may only be used with  
+	Keyspan hardware.  Distribution and/or Modification of the 
+	keyspan.c driver which includes this firmware, in whole or in 
+	part, requires the inclusion of this statement."
+
+static char theFirmwareDate49[] =
+	"02/14/2002  02:37p              19,347 USA49";
+
+
+
+static char theFirmwareDate65[] =
+	"01/31/2003  09:34a              19,331 USA65";
+
+
+*/
+
+static const struct ezusb_hex_record keyspan_usa49wlc_firmware[] = {
+ {0x7f92,  1, { 0x01}},
+ {0x0033,  3, { 0x02, 0x18, 0xfb}},
+ {0x0036, 13, { 0xe5, 0x11, 0x04, 0x90, 0x78, 0x41, 0xf0, 0x90, 0xc0, 0x00, 0xe0, 0xff, 0x22}},
+ {0x0046, 16, { 0xe4, 0xff, 0x74, 0x40, 0x2f, 0xf5, 0x82, 0xe4, 0x34, 0x7b, 0xf5, 0x83, 0xe0, 0xfe, 0xe5, 0x15}},
+ {0x0056, 16, { 0x24, 0x04, 0xfd, 0xe4, 0x35, 0x14, 0xfa, 0xa9, 0x05, 0x7b, 0x01, 0xef, 0x7c, 0x00, 0x29, 0xf9}},
+ {0x0066, 16, { 0xec, 0x3a, 0xfa, 0xee, 0x12, 0x11, 0xec, 0x0f, 0xbf, 0x22, 0xd7, 0xe5, 0x15, 0x24, 0x05, 0xf5}},
+ {0x0076, 16, { 0x82, 0xe4, 0x35, 0x14, 0xf5, 0x83, 0xe0, 0x70, 0x03, 0x02, 0x01, 0x34, 0xe5, 0x15, 0x24, 0x09}},
+ {0x0086, 16, { 0xf5, 0x82, 0xe4, 0x35, 0x14, 0xf5, 0x83, 0xe0, 0x70, 0x0e, 0xe5, 0x15, 0x24, 0x0a, 0xf5, 0x82}},
+ {0x0096, 16, { 0xe4, 0x35, 0x14, 0xf5, 0x83, 0xe0, 0x60, 0x18, 0x7f, 0x01, 0xe4, 0xfd, 0x12, 0x16, 0x6b, 0xe5}},
+ {0x00a6, 16, { 0x15, 0x24, 0x31, 0xf5, 0x82, 0xe4, 0x35, 0x14, 0xf5, 0x83, 0xe0, 0x54, 0xcf, 0xf0, 0x80, 0x41}},
+ {0x00b6, 16, { 0xe5, 0x15, 0x24, 0x06, 0xf5, 0x82, 0xe4, 0x35, 0x14, 0xf5, 0x83, 0xe0, 0xff, 0x12, 0x00, 0x03}},
+ {0x00c6, 16, { 0xe5, 0x15, 0x24, 0x07, 0xf5, 0x82, 0xe4, 0x35, 0x14, 0xf5, 0x83, 0xe0, 0xff, 0x12, 0x15, 0xab}},
+ {0x00d6, 16, { 0x7f, 0x01, 0xe5, 0x15, 0x24, 0x08, 0xf5, 0x82, 0xe4, 0x35, 0x14, 0xf5, 0x83, 0xe0, 0xfd, 0x12}},
+ {0x00e6, 16, { 0x16, 0x6b, 0xe5, 0x15, 0x24, 0x31, 0xf5, 0x82, 0xe4, 0x35, 0x14, 0xf5, 0x83, 0xe0, 0x44, 0x30}},
+ {0x00f6, 16, { 0xf0, 0xe5, 0x15, 0x24, 0x39, 0xf5, 0x82, 0xe4, 0x35, 0x14, 0xf5, 0x83, 0xe0, 0x44, 0x80, 0xf0}},
+ {0x0003, 16, { 0x90, 0x78, 0x41, 0x74, 0xf3, 0xf0, 0x90, 0xc0, 0x00, 0x74, 0xbf, 0xf0, 0x90, 0x78, 0x41, 0x74}},
+ {0x0013, 16, { 0xf0, 0xf0, 0x90, 0xc0, 0x00, 0xef, 0xf0, 0x90, 0x78, 0x41, 0x74, 0xf3, 0xf0, 0xe5, 0x15, 0x24}},
+ {0x0023, 16, { 0x37, 0xf5, 0x82, 0xe4, 0x35, 0x14, 0xf5, 0x83, 0xe0, 0x54, 0x7f, 0x90, 0xc0, 0x00, 0xf0, 0x22}},
+ {0x0043,  3, { 0x02, 0x1b, 0x00}},
+ {0x0000,  3, { 0x02, 0x10, 0x90}},
+ {0x0106, 64, { 0x90, 0x78, 0x41, 0x74, 0xf4, 0xf0, 0xe5, 0x15, 0x24, 0x39, 0xf5, 0x82, 0xe4, 0x35, 0x14, 0xf5, 0x83,
+  0xe0, 0x90, 0xc0, 0x00, 0xf0, 0x90, 0x78, 0x41, 0x74, 0xf2, 0xf0, 0xe5, 0x15, 0x24, 0x36, 0xf5,
+  0x82, 0xe4, 0x35, 0x14, 0xf5, 0x83, 0xe0, 0x44, 0x06, 0x90, 0xc0, 0x00, 0xf0, 0xe5, 0x15, 0x24,
+  0x0b, 0xf5, 0x82, 0xe4, 0x35, 0x14, 0xf5, 0x83, 0xe0, 0x60, 0x32, 0xe5, 0x15, 0x24, 0x0c}},
+ {0x0146, 64, { 0xf5, 0x82, 0xe4, 0x35, 0x14, 0xf5, 0x83, 0xe0, 0x54, 0x3f, 0xff, 0xe5, 0x15, 0x24, 0x37, 0xf5, 0x82,
+  0xe4, 0x35, 0x14, 0xf5, 0x83, 0xef, 0xf0, 0x90, 0x78, 0x41, 0x74, 0xf3, 0xf0, 0xe5, 0x15, 0x24,
+  0x37, 0xf5, 0x82, 0xe4, 0x35, 0x14, 0xf5, 0x83, 0xe0, 0x90, 0xc0, 0x00, 0xf0, 0xe5, 0x15, 0x24,
+  0x0d, 0xf5, 0x82, 0xe4, 0x35, 0x14, 0xf5, 0x83, 0xe0, 0x70, 0x03, 0x02, 0x02, 0x4f, 0xe5}},
+ {0x0186, 64, { 0x15, 0x24, 0x17, 0xf5, 0x82, 0xe4, 0x35, 0x14, 0xf5, 0x83, 0xe0, 0x60, 0x11, 0xe5, 0x15, 0x24, 0x32,
+  0xf5, 0x82, 0xe4, 0x35, 0x14, 0xf5, 0x83, 0xe0, 0x44, 0x04, 0xf0, 0x80, 0x0f, 0xe5, 0x15, 0x24,
+  0x32, 0xf5, 0x82, 0xe4, 0x35, 0x14, 0xf5, 0x83, 0xe0, 0x54, 0xfb, 0xf0, 0xe4, 0xff, 0xe5, 0x15,
+  0x24, 0x32, 0xf5, 0x82, 0xe4, 0x35, 0x14, 0xf5, 0x83, 0xe0, 0xfd, 0x12, 0x16, 0x6b, 0xe5}},
+ {0x01c6, 64, { 0x15, 0x24, 0x0e, 0xf5, 0x82, 0xe4, 0x35, 0x14, 0xf5, 0x83, 0xe0, 0x60, 0x11, 0xe5, 0x15, 0x24, 0x33,
+  0xf5, 0x82, 0xe4, 0x35, 0x14, 0xf5, 0x83, 0xe0, 0x44, 0x80, 0xf0, 0x80, 0x0f, 0xe5, 0x15, 0x24,
+  0x33, 0xf5, 0x82, 0xe4, 0x35, 0x14, 0xf5, 0x83, 0xe0, 0x54, 0x7f, 0xf0, 0xe5, 0x15, 0x24, 0x33,
+  0xf5, 0x82, 0xe4, 0x35, 0x14, 0xf5, 0x83, 0xe0, 0x54, 0xfc, 0xf0, 0xe5, 0x15, 0x24, 0x0f}},
+ {0x0206, 64, { 0xf5, 0x82, 0xe4, 0x35, 0x14, 0xf5, 0x83, 0xe0, 0x60, 0x2f, 0xe5, 0x15, 0x24, 0x33, 0xf5, 0x82, 0xe4,
+  0x35, 0x14, 0xf5, 0x83, 0xe0, 0x44, 0x02, 0xf0, 0xe5, 0x15, 0x24, 0x10, 0xf5, 0x82, 0xe4, 0x35,
+  0x14, 0xf5, 0x83, 0xe0, 0xff, 0x12, 0x16, 0x0b, 0xe5, 0x15, 0x24, 0x11, 0xf5, 0x82, 0xe4, 0x35,
+  0x14, 0xf5, 0x83, 0xe0, 0xff, 0x12, 0x16, 0x3b, 0xe5, 0x15, 0x24, 0x33, 0xf5, 0x82, 0xe4}},
+ {0x0246, 64, { 0x35, 0x14, 0xf5, 0x83, 0xe0, 0xff, 0x12, 0x15, 0xdb, 0xe5, 0x15, 0x24, 0x14, 0xf5, 0x82, 0xe4, 0x35,
+  0x14, 0xf5, 0x83, 0xe0, 0x60, 0x44, 0xe5, 0x15, 0x24, 0x15, 0xf5, 0x82, 0xe4, 0x35, 0x14, 0xf5,
+  0x83, 0xe0, 0x60, 0x11, 0xe5, 0x15, 0x24, 0x39, 0xf5, 0x82, 0xe4, 0x35, 0x14, 0xf5, 0x83, 0xe0,
+  0x44, 0x01, 0xf0, 0x80, 0x0f, 0xe5, 0x15, 0x24, 0x39, 0xf5, 0x82, 0xe4, 0x35, 0x14, 0xf5}},
+ {0x0286, 64, { 0x83, 0xe0, 0x54, 0xfe, 0xf0, 0x90, 0x78, 0x41, 0x74, 0xf4, 0xf0, 0xe5, 0x15, 0x24, 0x39, 0xf5, 0x82,
+  0xe4, 0x35, 0x14, 0xf5, 0x83, 0xe0, 0x90, 0xc0, 0x00, 0xf0, 0xe5, 0x15, 0x24, 0x12, 0xf5, 0x82,
+  0xe4, 0x35, 0x14, 0xf5, 0x83, 0xe0, 0x60, 0x44, 0xe5, 0x15, 0x24, 0x13, 0xf5, 0x82, 0xe4, 0x35,
+  0x14, 0xf5, 0x83, 0xe0, 0x60, 0x11, 0xe5, 0x15, 0x24, 0x39, 0xf5, 0x82, 0xe4, 0x35, 0x14}},
+ {0x02c6, 64, { 0xf5, 0x83, 0xe0, 0x44, 0x02, 0xf0, 0x80, 0x0f, 0xe5, 0x15, 0x24, 0x39, 0xf5, 0x82, 0xe4, 0x35, 0x14,
+  0xf5, 0x83, 0xe0, 0x54, 0xfd, 0xf0, 0x90, 0x78, 0x41, 0x74, 0xf4, 0xf0, 0xe5, 0x15, 0x24, 0x39,
+  0xf5, 0x82, 0xe4, 0x35, 0x14, 0xf5, 0x83, 0xe0, 0x90, 0xc0, 0x00, 0xf0, 0xe5, 0x15, 0x24, 0x16,
+  0xf5, 0x82, 0xe4, 0x35, 0x14, 0xf5, 0x83, 0xe0, 0xff, 0xe5, 0x15, 0x24, 0x35, 0xf5, 0x82}},
+ {0x0306, 64, { 0xe4, 0x35, 0x14, 0xf5, 0x83, 0xef, 0xf0, 0xe5, 0x15, 0x24, 0x17, 0xf5, 0x82, 0xe4, 0x35, 0x14, 0xf5,
+  0x83, 0xe0, 0x30, 0xe0, 0x11, 0xe5, 0x15, 0x24, 0x31, 0xf5, 0x82, 0xe4, 0x35, 0x14, 0xf5, 0x83,
+  0xe0, 0x44, 0x40, 0xf0, 0x80, 0x0f, 0xe5, 0x15, 0x24, 0x31, 0xf5, 0x82, 0xe4, 0x35, 0x14, 0xf5,
+  0x83, 0xe0, 0x54, 0xbf, 0xf0, 0xe5, 0x15, 0x24, 0x18, 0xf5, 0x82, 0xe4, 0x35, 0x14, 0xf5}},
+ {0x0346, 64, { 0x83, 0xe0, 0xff, 0xe5, 0x15, 0x24, 0x3b, 0xf5, 0x82, 0xe4, 0x35, 0x14, 0xf5, 0x83, 0xef, 0xf0, 0xe5,
+  0x15, 0x24, 0x19, 0xf5, 0x82, 0xe4, 0x35, 0x14, 0xf5, 0x83, 0xe0, 0x60, 0x11, 0xe5, 0x15, 0x24,
+  0x39, 0xf5, 0x82, 0xe4, 0x35, 0x14, 0xf5, 0x83, 0xe0, 0x44, 0x10, 0xf0, 0x80, 0x0f, 0xe5, 0x15,
+  0x24, 0x39, 0xf5, 0x82, 0xe4, 0x35, 0x14, 0xf5, 0x83, 0xe0, 0x54, 0xef, 0xf0, 0x90, 0x78}},
+ {0x0386, 64, { 0x41, 0x74, 0xf4, 0xf0, 0xe5, 0x15, 0x24, 0x39, 0xf5, 0x82, 0xe4, 0x35, 0x14, 0xf5, 0x83, 0xe0, 0x90,
+  0xc0, 0x00, 0xf0, 0xe5, 0x15, 0x24, 0x1a, 0xf5, 0x82, 0xe4, 0x35, 0x14, 0xf5, 0x83, 0xe0, 0x60,
+  0x6b, 0xe5, 0x15, 0x24, 0x37, 0xf5, 0x82, 0xe4, 0x35, 0x14, 0xf5, 0x83, 0xe0, 0x54, 0xbf, 0xf0,
+  0x90, 0x78, 0x41, 0x74, 0xf3, 0xf0, 0xe5, 0x15, 0x24, 0x37, 0xf5, 0x82, 0xe4, 0x35, 0x14}},
+ {0x03c6, 64, { 0xf5, 0x83, 0xe0, 0x54, 0x7f, 0x90, 0xc0, 0x00, 0xf0, 0x90, 0x78, 0x41, 0x74, 0xf1, 0xf0, 0x12, 0x00,
+  0x36, 0xef, 0x54, 0xfe, 0x90, 0xc0, 0x00, 0xf0, 0xe5, 0x15, 0x24, 0x32, 0xf5, 0x82, 0xe4, 0x35,
+  0x14, 0xf5, 0x83, 0xe0, 0x54, 0xfd, 0xff, 0xf0, 0xfd, 0xe4, 0xff, 0x12, 0x16, 0x6b, 0xe5, 0x15,
+  0x24, 0x2c, 0xf5, 0x82, 0xe4, 0x35, 0x14, 0xf5, 0x83, 0xe4, 0xf0, 0xe5, 0x15, 0x24, 0x2b}},
+ {0x0406, 64, { 0xf5, 0x82, 0xe4, 0x35, 0x14, 0xf5, 0x83, 0xe4, 0xf0, 0xe5, 0x16, 0x42, 0x13, 0xe5, 0x15, 0x24, 0x1b,
+  0xf5, 0x82, 0xe4, 0x35, 0x14, 0xf5, 0x83, 0xe0, 0x70, 0x0e, 0xe5, 0x15, 0x24, 0x25, 0xf5, 0x82,
+  0xe4, 0x35, 0x14, 0xf5, 0x83, 0xe0, 0x60, 0x28, 0xe5, 0x15, 0x24, 0x32, 0xf5, 0x82, 0xe4, 0x35,
+  0x14, 0xf5, 0x83, 0xe0, 0x44, 0x02, 0xff, 0xf0, 0xfd, 0xe4, 0xff, 0x12, 0x16, 0x6b, 0xe5}},
+ {0x0446, 64, { 0x15, 0x24, 0x2b, 0xf5, 0x82, 0xe4, 0x35, 0x14, 0xf5, 0x83, 0x74, 0x01, 0xf0, 0xe5, 0x16, 0x42, 0x13,
+  0xe5, 0x15, 0x24, 0x1c, 0xf5, 0x82, 0xe4, 0x35, 0x14, 0xf5, 0x83, 0xe0, 0xff, 0x70, 0x0e, 0xe5,
+  0x15, 0x24, 0x25, 0xf5, 0x82, 0xe4, 0x35, 0x14, 0xf5, 0x83, 0xe0, 0x60, 0x2a, 0x90, 0x78, 0x41,
+  0x74, 0xf2, 0xf0, 0xe5, 0x15, 0x24, 0x36, 0xf5, 0x82, 0xe4, 0x35, 0x14, 0xf5, 0x83, 0xe0}},
+ {0x0486, 64, { 0x44, 0x04, 0x90, 0xc0, 0x00, 0xf0, 0xef, 0x60, 0x0f, 0xe5, 0x15, 0x24, 0x31, 0xf5, 0x82, 0xe4, 0x35,
+  0x14, 0xf5, 0x83, 0xe0, 0x44, 0x04, 0xf0, 0xe5, 0x15, 0x24, 0x1d, 0xf5, 0x82, 0xe4, 0x35, 0x14,
+  0xf5, 0x83, 0xe0, 0x60, 0x27, 0xe5, 0x15, 0x24, 0x37, 0xf5, 0x82, 0xe4, 0x35, 0x14, 0xf5, 0x83,
+  0xe0, 0x44, 0x40, 0xf0, 0x90, 0x78, 0x41, 0x74, 0xf3, 0xf0, 0xe5, 0x15, 0x24, 0x37, 0xf5}},
+ {0x04c6, 64, { 0x82, 0xe4, 0x35, 0x14, 0xf5, 0x83, 0xe0, 0x54, 0x7f, 0x90, 0xc0, 0x00, 0xf0, 0xe5, 0x15, 0x24, 0x1e,
+  0xf5, 0x82, 0xe4, 0x35, 0x14, 0xf5, 0x83, 0xe0, 0x60, 0x28, 0xe5, 0x15, 0x24, 0x32, 0xf5, 0x82,
+  0xe4, 0x35, 0x14, 0xf5, 0x83, 0xe0, 0x54, 0xfe, 0xff, 0xf0, 0xfd, 0xe4, 0xff, 0x12, 0x16, 0x6b,
+  0xe5, 0x15, 0x24, 0x2d, 0xf5, 0x82, 0xe4, 0x35, 0x14, 0xf5, 0x83, 0x74, 0x01, 0xf0, 0xe5}},
+ {0x0506, 64, { 0x16, 0x42, 0x13, 0xe5, 0x15, 0x24, 0x1f, 0xf5, 0x82, 0xe4, 0x35, 0x14, 0xf5, 0x83, 0xe0, 0x70, 0x0e,
+  0xe5, 0x15, 0x24, 0x25, 0xf5, 0x82, 0xe4, 0x35, 0x14, 0xf5, 0x83, 0xe0, 0x60, 0x27, 0xe5, 0x15,
+  0x24, 0x32, 0xf5, 0x82, 0xe4, 0x35, 0x14, 0xf5, 0x83, 0xe0, 0x44, 0x01, 0xff, 0xf0, 0xfd, 0xe4,
+  0xff, 0x12, 0x16, 0x6b, 0xe5, 0x15, 0x24, 0x2d, 0xf5, 0x82, 0xe4, 0x35, 0x14, 0xf5, 0x83}},
+ {0x0546, 64, { 0xe4, 0xf0, 0xe5, 0x16, 0x42, 0x13, 0xe5, 0x15, 0x24, 0x20, 0xf5, 0x82, 0xe4, 0x35, 0x14, 0xf5, 0x83,
+  0xe0, 0x70, 0x0e, 0xe5, 0x15, 0x24, 0x25, 0xf5, 0x82, 0xe4, 0x35, 0x14, 0xf5, 0x83, 0xe0, 0x60,
+  0x18, 0x90, 0x78, 0x41, 0x74, 0xf2, 0xf0, 0xe5, 0x15, 0x24, 0x36, 0xf5, 0x82, 0xe4, 0x35, 0x14,
+  0xf5, 0x83, 0xe0, 0x44, 0x02, 0x90, 0xc0, 0x00, 0xf0, 0xe5, 0x15, 0x24, 0x21, 0xf5, 0x82}},
+ {0x0586, 64, { 0xe4, 0x35, 0x14, 0xf5, 0x83, 0xe0, 0x60, 0x0f, 0xe5, 0x15, 0x24, 0x31, 0xf5, 0x82, 0xe4, 0x35, 0x14,
+  0xf5, 0x83, 0xe0, 0x44, 0x02, 0xf0, 0xe5, 0x15, 0x24, 0x22, 0xf5, 0x82, 0xe4, 0x35, 0x14, 0xf5,
+  0x83, 0xe0, 0x60, 0x1f, 0xe5, 0x15, 0x24, 0x2e, 0xf5, 0x82, 0xe4, 0x35, 0x14, 0xf5, 0x83, 0x74,
+  0x01, 0xf0, 0xe5, 0x15, 0x24, 0x3a, 0xf5, 0x82, 0xe4, 0x35, 0x14, 0xf5, 0x83, 0xe4, 0xf0}},
+ {0x05c6, 64, { 0xe5, 0x16, 0x42, 0x13, 0xe5, 0x15, 0x24, 0x23, 0xf5, 0x82, 0xe4, 0x35, 0x14, 0xf5, 0x83, 0xe0, 0x60,
+  0x03, 0x12, 0x18, 0x7a, 0xe5, 0x15, 0x24, 0x24, 0xf5, 0x82, 0xe4, 0x35, 0x14, 0xf5, 0x83, 0xe0,
+  0x60, 0x23, 0xe5, 0x15, 0x24, 0x31, 0xf5, 0x82, 0xe4, 0x35, 0x14, 0xf5, 0x83, 0xe0, 0x44, 0x08,
+  0xf0, 0xe5, 0x16, 0xc4, 0x54, 0xf0, 0xff, 0x42, 0x11, 0x90, 0x7f, 0x96, 0xe0, 0x4f, 0xf0}},
+ {0x0606, 64, { 0x90, 0x78, 0x41, 0xe0, 0x4f, 0xf0, 0xe5, 0x15, 0x24, 0x25, 0xf5, 0x82, 0xe4, 0x35, 0x14, 0xf5, 0x83,
+  0xe0, 0x60, 0x24, 0xe5, 0x15, 0x24, 0x31, 0xf5, 0x82, 0xe4, 0x35, 0x14, 0xf5, 0x83, 0xe0, 0x54,
+  0xf7, 0xf0, 0xe5, 0x16, 0xc4, 0x54, 0xf0, 0xf4, 0xff, 0x52, 0x11, 0x90, 0x7f, 0x96, 0xe0, 0x5f,
+  0xf0, 0x90, 0x78, 0x41, 0xe0, 0x5f, 0xf0, 0x22, 0x90, 0x7f, 0xe9, 0xe0, 0x12, 0x11, 0xfe}},
+ {0x0646, 64, { 0x07, 0x99, 0x00, 0x08, 0x0d, 0x01, 0x08, 0x79, 0x03, 0x06, 0x62, 0x06, 0x07, 0x8a, 0x08, 0x07, 0x7e,
+  0x09, 0x07, 0x66, 0x0a, 0x07, 0x75, 0x0b, 0x00, 0x00, 0x08, 0xc8, 0x90, 0x7f, 0xeb, 0xe0, 0x24,
+  0xfe, 0x60, 0x1c, 0x14, 0x70, 0x03, 0x02, 0x07, 0x14, 0x24, 0x02, 0x60, 0x03, 0x02, 0x07, 0x5c,
+  0x74, 0x19, 0x90, 0x7f, 0xd4, 0xf0, 0x74, 0x00, 0x90, 0x7f, 0xd5, 0xf0, 0x02, 0x08, 0xcf}},
+ {0x0686, 64, { 0x90, 0x7f, 0xea, 0xe0, 0x04, 0x75, 0x82, 0x17, 0x75, 0x83, 0x19, 0xf0, 0x90, 0x7f, 0xea, 0xe0, 0x30,
+  0xe0, 0x04, 0x7f, 0x02, 0x80, 0x02, 0x7f, 0x03, 0x75, 0x82, 0x82, 0x75, 0x83, 0x19, 0xef, 0xf0,
+  0x75, 0x82, 0x6d, 0x75, 0x83, 0x19, 0xf0, 0x75, 0x82, 0x66, 0x75, 0x83, 0x19, 0xf0, 0x75, 0x82,
+  0x5f, 0x75, 0x83, 0x19, 0xf0, 0x75, 0x82, 0x58, 0x75, 0x83, 0x19, 0xf0, 0x90, 0x7f, 0xea}},
+ {0x06c6, 64, { 0xe0, 0x30, 0xe1, 0x04, 0x7f, 0x64, 0x80, 0x02, 0x7f, 0x32, 0x75, 0x82, 0x1a, 0x75, 0x83, 0x19, 0xef,
+  0xf0, 0x90, 0x7f, 0xef, 0xe0, 0xfe, 0x90, 0x7f, 0xee, 0xe0, 0x7c, 0x00, 0x24, 0x00, 0xf5, 0x18,
+  0xec, 0x3e, 0xf5, 0x17, 0x75, 0x33, 0x19, 0x75, 0x34, 0x12, 0x75, 0x82, 0x14, 0x75, 0x83, 0x19,
+  0xe0, 0x75, 0x27, 0x00, 0xf5, 0x28, 0xd3, 0xe5, 0x28, 0x95, 0x18, 0xe5, 0x27, 0x95, 0x17}},
+ {0x0706, 64, { 0x40, 0x06, 0x85, 0x17, 0x27, 0x85, 0x18, 0x28, 0x12, 0x13, 0x0d, 0x02, 0x08, 0xcf, 0x90, 0x7f, 0xea,
+  0xe0, 0xff, 0x12, 0x14, 0x5f, 0xea, 0x49, 0x60, 0x32, 0x90, 0x7f, 0xee, 0xe0, 0x75, 0x17, 0x00,
+  0xf5, 0x18, 0xae, 0x02, 0xaf, 0x01, 0x8e, 0x33, 0x8f, 0x34, 0x8f, 0x82, 0x8e, 0x83, 0xe0, 0xfe,
+  0xa3, 0xe0, 0x8e, 0x27, 0xf5, 0x28, 0xd3, 0x95, 0x18, 0xe5, 0x27, 0x95, 0x17, 0x40, 0x06}},
+ {0x0746, 64, { 0x85, 0x17, 0x27, 0x85, 0x18, 0x28, 0x12, 0x13, 0x0d, 0x02, 0x08, 0xcf, 0x90, 0x7f, 0xb4, 0xe0, 0x44,
+  0x01, 0xf0, 0x02, 0x08, 0xcf, 0x90, 0x7f, 0xb4, 0xe0, 0x44, 0x01, 0xf0, 0x02, 0x08, 0xcf, 0x90,
+  0x7f, 0x00, 0xe5, 0x25, 0xf0, 0x90, 0x7f, 0xb5, 0x74, 0x01, 0xf0, 0x02, 0x08, 0xcf, 0x90, 0x7f,
+  0xea, 0xe0, 0xf5, 0x25, 0x02, 0x08, 0xcf, 0x90, 0x7f, 0xea, 0xe0, 0xf5, 0x22, 0x12, 0x0a}},
+ {0x0786, 64, { 0xce, 0x02, 0x08, 0xcf, 0x90, 0x7f, 0x00, 0xe5, 0x22, 0xf0, 0x90, 0x7f, 0xb5, 0x74, 0x01, 0xf0, 0x02,
+  0x08, 0xcf, 0x90, 0x7f, 0xe8, 0xe0, 0x24, 0x7f, 0x60, 0x24, 0x14, 0x60, 0x31, 0x24, 0x02, 0x70,
+  0x5b, 0xa2, 0x00, 0xe4, 0x33, 0xff, 0x25, 0xe0, 0xff, 0xa2, 0x06, 0xe4, 0x33, 0x4f, 0x90, 0x7f,
+  0x00, 0xf0, 0xe4, 0xa3, 0xf0, 0x90, 0x7f, 0xb5, 0x74, 0x02, 0xf0, 0x02, 0x08, 0xcf, 0xe4}},
+ {0x07c6, 64, { 0x90, 0x7f, 0x00, 0xf0, 0xa3, 0xf0, 0x90, 0x7f, 0xb5, 0x74, 0x02, 0xf0, 0x02, 0x08, 0xcf, 0x90, 0x7f,
+  0xec, 0xe0, 0xf4, 0x54, 0x80, 0xff, 0xc4, 0x54, 0x0f, 0xff, 0xe0, 0x54, 0x07, 0x2f, 0x25, 0xe0,
+  0x24, 0xb4, 0xf5, 0x82, 0xe4, 0x34, 0x7f, 0xf5, 0x83, 0xe0, 0x54, 0xfd, 0x90, 0x7f, 0x00, 0xf0,
+  0xe4, 0xa3, 0xf0, 0x90, 0x7f, 0xb5, 0x74, 0x02, 0xf0, 0x02, 0x08, 0xcf, 0x90, 0x7f, 0xb4}},
+ {0x0806, 64, { 0xe0, 0x44, 0x01, 0xf0, 0x02, 0x08, 0xcf, 0x90, 0x7f, 0xe8, 0xe0, 0x24, 0xfe, 0x60, 0x1d, 0x24, 0x02,
+  0x60, 0x03, 0x02, 0x08, 0xcf, 0x90, 0x7f, 0xea, 0xe0, 0xb4, 0x01, 0x05, 0xc2, 0x00, 0x02, 0x08,
+  0xcf, 0x90, 0x7f, 0xb4, 0xe0, 0x44, 0x01, 0xf0, 0x02, 0x08, 0xcf, 0x90, 0x7f, 0xea, 0xe0, 0x70,
+  0x38, 0x90, 0x7f, 0xec, 0xe0, 0xf4, 0x54, 0x80, 0xff, 0xc4, 0x54, 0x0f, 0xff, 0xe0, 0x54}},
+ {0x0846, 64, { 0x07, 0x2f, 0x25, 0xe0, 0x24, 0xb4, 0xf5, 0x82, 0xe4, 0x34, 0x7f, 0xf5, 0x83, 0xe4, 0xf0, 0x90, 0x7f,
+  0xec, 0xe0, 0x54, 0x80, 0xff, 0x13, 0x13, 0x13, 0x54, 0x1f, 0xff, 0xe0, 0x54, 0x07, 0x2f, 0x90,
+  0x7f, 0xd7, 0xf0, 0xe0, 0x44, 0x20, 0xf0, 0x80, 0x5f, 0x90, 0x7f, 0xb4, 0xe0, 0x44, 0x01, 0xf0,
+  0x80, 0x56, 0x90, 0x7f, 0xe8, 0xe0, 0x24, 0xfe, 0x60, 0x18, 0x24, 0x02, 0x70, 0x4a, 0x90}},
+ {0x0886, 64, { 0x7f, 0xea, 0xe0, 0xb4, 0x01, 0x04, 0xd2, 0x00, 0x80, 0x3f, 0x90, 0x7f, 0xb4, 0xe0, 0x44, 0x01, 0xf0,
+  0x80, 0x36, 0x90, 0x7f, 0xea, 0xe0, 0x70, 0x20, 0x90, 0x7f, 0xec, 0xe0, 0xf4, 0x54, 0x80, 0xff,
+  0xc4, 0x54, 0x0f, 0xff, 0xe0, 0x54, 0x07, 0x2f, 0x25, 0xe0, 0x24, 0xb4, 0xf5, 0x82, 0xe4, 0x34,
+  0x7f, 0xf5, 0x83, 0x74, 0x01, 0xf0, 0x80, 0x10, 0x90, 0x7f, 0xb4, 0xe0, 0x44, 0x01, 0xf0}},
+ {0x08c6, 64, { 0x80, 0x07, 0x90, 0x7f, 0xb4, 0xe0, 0x44, 0x01, 0xf0, 0x90, 0x7f, 0xb4, 0xe0, 0x44, 0x02, 0xf0, 0x22,
+  0xe5, 0x12, 0x54, 0x0f, 0x70, 0x03, 0x02, 0x09, 0xc8, 0x12, 0x16, 0x9a, 0xef, 0x20, 0xe1, 0x75,
+  0x12, 0x16, 0xf8, 0xef, 0x14, 0xf5, 0x18, 0x12, 0x18, 0xc5, 0xef, 0x25, 0x18, 0xff, 0xe4, 0x33,
+  0xfe, 0xc3, 0xef, 0x94, 0x80, 0xee, 0x64, 0x80, 0x94, 0x80, 0x50, 0x59, 0x85, 0x15, 0x82}},
+ {0x0906, 64, { 0x85, 0x14, 0x83, 0xe0, 0xfe, 0xa3, 0xe0, 0xff, 0xf5, 0x82, 0x8e, 0x83, 0xe0, 0x30, 0xe0, 0x11, 0xe5,
+  0x15, 0x24, 0x31, 0xf5, 0x82, 0xe4, 0x35, 0x14, 0xf5, 0x83, 0xe0, 0x44, 0x80, 0xf0, 0x80, 0x0f,
+  0xe5, 0x15, 0x24, 0x31, 0xf5, 0x82, 0xe4, 0x35, 0x14, 0xf5, 0x83, 0xe0, 0x54, 0x7f, 0xf0, 0xe5,
+  0x15, 0x24, 0x31, 0xf5, 0x82, 0xe4, 0x35, 0x14, 0xf5, 0x83, 0xe0, 0x20, 0xe2, 0x12, 0xe5}},
+ {0x0946, 64, { 0x18, 0x60, 0x0e, 0xf5, 0x23, 0xef, 0x24, 0x01, 0xf5, 0x2d, 0xe4, 0x3e, 0xf5, 0x2c, 0x12, 0x14, 0xa0,
+  0xe4, 0xff, 0x12, 0x14, 0xd7, 0xe5, 0x15, 0x24, 0x31, 0xf5, 0x82, 0xe4, 0x35, 0x14, 0xf5, 0x83,
+  0xe0, 0x30, 0xe7, 0x5d, 0x12, 0x18, 0xc5, 0xe5, 0x15, 0x24, 0x3b, 0xf5, 0x82, 0xe4, 0x35, 0x14,
+  0xf5, 0x83, 0xe0, 0xfe, 0xef, 0xc3, 0x9e, 0x50, 0x48, 0xe5, 0x15, 0x24, 0x2f, 0xf5, 0x82}},
+ {0x0986, 64, { 0xe4, 0x35, 0x14, 0xf5, 0x83, 0x74, 0x01, 0xf0, 0xe5, 0x15, 0x24, 0x31, 0xf5, 0x82, 0xe4, 0x35, 0x14,
+  0xf5, 0x83, 0xe0, 0x54, 0x7b, 0xf0, 0xe5, 0x15, 0x24, 0x3a, 0xf5, 0x82, 0xe4, 0x35, 0x14, 0xf5,
+  0x83, 0xe4, 0xf0, 0xe5, 0x16, 0x42, 0x13, 0x90, 0x7f, 0xc2, 0xe0, 0x30, 0xe1, 0x10, 0xe5, 0x15,
+  0x24, 0x26, 0xf5, 0x82, 0xe4, 0x35, 0x14, 0xf5, 0x83, 0xe0, 0xf5, 0x24, 0x80, 0x03, 0x12}},
+ {0x09c6, 64, { 0x12, 0x9c, 0x12, 0x16, 0xc9, 0xef, 0x30, 0xe1, 0x03, 0x02, 0x0a, 0xcd, 0x12, 0x17, 0xc7, 0x8f, 0x18,
+  0x12, 0x18, 0xd3, 0xe5, 0x15, 0x24, 0x38, 0xf5, 0x82, 0xe4, 0x35, 0x14, 0xf5, 0x83, 0xef, 0xf0,
+  0xe5, 0x15, 0x24, 0x35, 0xf5, 0x82, 0xe4, 0x35, 0x14, 0xf5, 0x83, 0xe0, 0xff, 0xe5, 0x18, 0xc3,
+  0x9f, 0x50, 0x28, 0x12, 0x18, 0xa9, 0xef, 0x30, 0xe0, 0x21, 0xe5, 0x15, 0x24, 0x38, 0xf5}},
+ {0x0a06, 64, { 0x82, 0xe4, 0x35, 0x14, 0xf5, 0x83, 0xe0, 0x20, 0xe7, 0x12, 0xe5, 0x15, 0x24, 0x31, 0xf5, 0x82, 0xe4,
+  0x35, 0x14, 0xf5, 0x83, 0xe0, 0x20, 0xe1, 0x03, 0x02, 0x0a, 0xcd, 0xe5, 0x15, 0x24, 0x31, 0xf5,
+  0x82, 0xe4, 0x35, 0x14, 0xf5, 0x83, 0xe0, 0x54, 0xfd, 0xf0, 0xe5, 0x18, 0x70, 0x03, 0x02, 0x0a,
+  0xcd, 0xb4, 0x80, 0x0f, 0xe5, 0x15, 0x24, 0x38, 0xf5, 0x82, 0xe4, 0x35, 0x14, 0xf5, 0x83}},
+ {0x0a46, 64, { 0xe0, 0x44, 0x02, 0xf0, 0xe5, 0x15, 0x24, 0x38, 0xf5, 0x82, 0xe4, 0x35, 0x14, 0xf5, 0x83, 0xe0, 0xff,
+  0x30, 0xe7, 0x29, 0xe5, 0x18, 0xd3, 0x94, 0x20, 0x40, 0x03, 0x75, 0x18, 0x20, 0x85, 0x18, 0x23,
+  0x85, 0x15, 0x82, 0x85, 0x14, 0x83, 0xa3, 0xa3, 0xe0, 0xfc, 0xa3, 0xe0, 0x8c, 0x2c, 0xf5, 0x2d,
+  0x12, 0x13, 0xcf, 0xe5, 0x18, 0x25, 0xe0, 0xff, 0x12, 0x15, 0x0d, 0x22, 0xe5, 0x18, 0xd3}},
+ {0x0a86, 64, { 0x94, 0x3f, 0x40, 0x03, 0x75, 0x18, 0x3f, 0x85, 0x18, 0x23, 0xe5, 0x15, 0x24, 0x38, 0xf5, 0x82, 0xe4,
+  0x35, 0x14, 0xf5, 0x83, 0xe0, 0xff, 0x85, 0x15, 0x82, 0x85, 0x14, 0x83, 0xa3, 0xa3, 0xe0, 0xfc,
+  0xa3, 0xe0, 0xf5, 0x82, 0x8c, 0x83, 0xef, 0xf0, 0x85, 0x15, 0x82, 0x85, 0x14, 0x83, 0xa3, 0xa3,
+  0xe0, 0xfe, 0xa3, 0xe0, 0x24, 0x01, 0xf5, 0x2d, 0xe4, 0x3e, 0xf5, 0x2c, 0x12, 0x14, 0x1d}},
+ {0x0ac6, 64, { 0xe5, 0x18, 0x04, 0xff, 0x12, 0x15, 0x0d, 0x22, 0xe4, 0x90, 0x7f, 0x93, 0xf0, 0x90, 0x7f, 0x9c, 0x74,
+  0xff, 0xf0, 0xe4, 0x90, 0x7f, 0x96, 0xf0, 0x90, 0x7f, 0x94, 0xf0, 0x90, 0x78, 0x4a, 0x04, 0xf0,
+  0xf5, 0x8e, 0x90, 0x7f, 0x95, 0x74, 0xc0, 0xf0, 0x90, 0x7f, 0x9e, 0x74, 0x3f, 0xf0, 0x90, 0x7f,
+  0x98, 0x74, 0x2f, 0xf0, 0x90, 0x78, 0x43, 0x74, 0xf7, 0xf0, 0xe4, 0x90, 0x78, 0x41, 0xf0}},
+ {0x0b06, 64, { 0x90, 0x7f, 0xdf, 0x74, 0x9f, 0xf0, 0x90, 0x7f, 0xde, 0xf0, 0x90, 0x7f, 0x92, 0xe0, 0x44, 0x02, 0xf0,
+  0x7e, 0x7b, 0x7f, 0xc0, 0x75, 0x14, 0x7b, 0x75, 0x15, 0xc0, 0x90, 0x7f, 0x98, 0x74, 0x2e, 0xf0,
+  0x75, 0x16, 0x01, 0x12, 0x0f, 0x28, 0x7e, 0x7b, 0x7f, 0xc0, 0x75, 0x14, 0x7b, 0x75, 0x15, 0xc0,
+  0x90, 0x7f, 0x98, 0x74, 0x2e, 0xf0, 0x75, 0x16, 0x01, 0xe5, 0x15, 0x24, 0x26, 0xf5, 0x82}},
+ {0x0b46, 64, { 0xe4, 0x35, 0x14, 0xf5, 0x83, 0xe4, 0xf0, 0x7e, 0x7e, 0x7f, 0x40, 0x85, 0x15, 0x82, 0x85, 0x14, 0x83,
+  0x74, 0x7e, 0xf0, 0xa3, 0x74, 0x40, 0xf0, 0x7e, 0x7e, 0x7f, 0x80, 0x85, 0x15, 0x82, 0x85, 0x14,
+  0x83, 0xa3, 0xa3, 0x74, 0x7e, 0xf0, 0xa3, 0x74, 0x80, 0xf0, 0x7e, 0x7c, 0x7f, 0x00, 0x75, 0x14,
+  0x7c, 0x75, 0x15, 0x00, 0x90, 0x7f, 0x98, 0x74, 0x2d, 0xf0, 0x75, 0x16, 0x02, 0x12, 0x0f}},
+ {0x0b86, 64, { 0x28, 0x7e, 0x7c, 0x7f, 0x00, 0x75, 0x14, 0x7c, 0x75, 0x15, 0x00, 0x90, 0x7f, 0x98, 0x74, 0x2d, 0xf0,
+  0x75, 0x16, 0x02, 0xe5, 0x15, 0x24, 0x26, 0xf5, 0x82, 0xe4, 0x35, 0x14, 0xf5, 0x83, 0x74, 0x01,
+  0xf0, 0x7e, 0x7d, 0x7f, 0xc0, 0x85, 0x15, 0x82, 0x85, 0x14, 0x83, 0x74, 0x7d, 0xf0, 0xa3, 0x74,
+  0xc0, 0xf0, 0x7e, 0x7e, 0x7f, 0x00, 0x85, 0x15, 0x82, 0x85, 0x14, 0x83, 0xa3, 0xa3, 0x74}},
+ {0x0bc6, 64, { 0x7e, 0xf0, 0xa3, 0x74, 0x00, 0xf0, 0x7e, 0x7c, 0x7f, 0x40, 0x75, 0x14, 0x7c, 0x75, 0x15, 0x40, 0x90,
+  0x7f, 0x98, 0x74, 0x2b, 0xf0, 0x75, 0x16, 0x04, 0x12, 0x0f, 0x28, 0x7e, 0x7c, 0x7f, 0x40, 0x75,
+  0x14, 0x7c, 0x75, 0x15, 0x40, 0x90, 0x7f, 0x98, 0x74, 0x2b, 0xf0, 0x75, 0x16, 0x04, 0xe5, 0x15,
+  0x24, 0x26, 0xf5, 0x82, 0xe4, 0x35, 0x14, 0xf5, 0x83, 0x74, 0x02, 0xf0, 0x7e, 0x7d, 0x7f}},
+ {0x0c06, 64, { 0x40, 0x85, 0x15, 0x82, 0x85, 0x14, 0x83, 0x74, 0x7d, 0xf0, 0xa3, 0x74, 0x40, 0xf0, 0x7e, 0x7d, 0x7f,
+  0x80, 0x85, 0x15, 0x82, 0x85, 0x14, 0x83, 0xa3, 0xa3, 0x74, 0x7d, 0xf0, 0xa3, 0x74, 0x80, 0xf0,
+  0x7e, 0x7c, 0x7f, 0x80, 0x75, 0x14, 0x7c, 0x75, 0x15, 0x80, 0x90, 0x7f, 0x98, 0x74, 0x27, 0xf0,
+  0x75, 0x16, 0x08, 0x12, 0x0f, 0x28, 0x7e, 0x7c, 0x7f, 0x80, 0x75, 0x14, 0x7c, 0x75, 0x15}},
+ {0x0c46, 64, { 0x80, 0x90, 0x7f, 0x98, 0x74, 0x27, 0xf0, 0x75, 0x16, 0x08, 0xe5, 0x15, 0x24, 0x26, 0xf5, 0x82, 0xe4,
+  0x35, 0x14, 0xf5, 0x83, 0x74, 0x03, 0xf0, 0x7e, 0x7c, 0x7f, 0xc0, 0x85, 0x15, 0x82, 0x85, 0x14,
+  0x83, 0x74, 0x7c, 0xf0, 0xa3, 0x74, 0xc0, 0xf0, 0x7e, 0x7d, 0x7f, 0x00, 0x85, 0x15, 0x82, 0x85,
+  0x14, 0x83, 0xa3, 0xa3, 0x74, 0x7d, 0xf0, 0xa3, 0x74, 0x00, 0xf0, 0xc2, 0x0a, 0xc2, 0x09}},
+ {0x0c86, 64, { 0xe4, 0xf5, 0x11, 0xd2, 0x02, 0x22, 0xe5, 0x10, 0x04, 0x54, 0x03, 0xf5, 0x10, 0x14, 0x60, 0x1f, 0x14,
+  0x60, 0x31, 0x14, 0x60, 0x43, 0x24, 0x03, 0x70, 0x52, 0x7e, 0x7b, 0x7f, 0xc0, 0x75, 0x14, 0x7b,
+  0x75, 0x15, 0xc0, 0x90, 0x7f, 0x98, 0x74, 0x2e, 0xf0, 0x75, 0x16, 0x01, 0x80, 0x3d, 0x7e, 0x7c,
+  0x7f, 0x00, 0x75, 0x14, 0x7c, 0x75, 0x15, 0x00, 0x90, 0x7f, 0x98, 0x74, 0x2d, 0xf0, 0x75}},
+ {0x0cc6, 64, { 0x16, 0x02, 0x80, 0x28, 0x7e, 0x7c, 0x7f, 0x40, 0x75, 0x14, 0x7c, 0x75, 0x15, 0x40, 0x90, 0x7f, 0x98,
+  0x74, 0x2b, 0xf0, 0x75, 0x16, 0x04, 0x80, 0x13, 0x7e, 0x7c, 0x7f, 0x80, 0x75, 0x14, 0x7c, 0x75,
+  0x15, 0x80, 0x90, 0x7f, 0x98, 0x74, 0x27, 0xf0, 0x75, 0x16, 0x08, 0xe5, 0x32, 0x55, 0x16, 0x70,
+  0x03, 0x02, 0x0e, 0x27, 0xe5, 0x16, 0xf4, 0x52, 0x32, 0xe5, 0x26, 0x54, 0x7f, 0xff, 0x70}},
+ {0x0d06, 64, { 0x17, 0xe5, 0x2a, 0x55, 0x16, 0x60, 0x34, 0x90, 0x7f, 0x96, 0xe0, 0xfe, 0xe5, 0x16, 0xc4, 0x54, 0xf0,
+  0xf4, 0xfd, 0xee, 0x5d, 0xf0, 0x80, 0x23, 0xbf, 0x20, 0x20, 0xe5, 0x15, 0x24, 0x31, 0xf5, 0x82,
+  0xe4, 0x35, 0x14, 0xf5, 0x83, 0xe0, 0x30, 0xe3, 0x11, 0xe4, 0xf5, 0x2a, 0x90, 0x7f, 0x96, 0xe0,
+  0xff, 0xe5, 0x16, 0xc4, 0x54, 0xf0, 0xfe, 0xef, 0x4e, 0xf0, 0xe5, 0x15, 0x24, 0x3a, 0xf5}},
+ {0x0d46, 64, { 0x82, 0xe4, 0x35, 0x14, 0xf5, 0x83, 0xe0, 0x60, 0x03, 0xe0, 0x14, 0xf0, 0xe5, 0x15, 0x24, 0x34, 0xf5,
+  0x82, 0xe4, 0x35, 0x14, 0xf5, 0x83, 0xe0, 0x60, 0x03, 0xe0, 0x14, 0xf0, 0xe0, 0x60, 0x03, 0x02,
+  0x0e, 0x27, 0x74, 0x0a, 0xf0, 0x12, 0x00, 0x36, 0xef, 0x54, 0x01, 0xff, 0xf5, 0x18, 0xe5, 0x15,
+  0x24, 0x2c, 0xf5, 0x82, 0xe4, 0x35, 0x14, 0xf5, 0x83, 0xe0, 0x6f, 0x60, 0x07, 0xe5, 0x18}},
+ {0x0d86, 64, { 0xf0, 0xe5, 0x16, 0x42, 0x13, 0x12, 0x18, 0xe1, 0x8f, 0x18, 0xe5, 0x15, 0x24, 0x27, 0xf5, 0x82, 0xe4,
+  0x35, 0x14, 0xf5, 0x83, 0xe0, 0xff, 0xe5, 0x18, 0x54, 0x10, 0xfe, 0x6f, 0x60, 0x06, 0xee, 0xf0,
+  0xe5, 0x16, 0x42, 0x13, 0xe5, 0x15, 0x24, 0x28, 0xf5, 0x82, 0xe4, 0x35, 0x14, 0xf5, 0x83, 0xe0,
+  0xff, 0xe5, 0x18, 0x54, 0x80, 0xfe, 0x6f, 0x60, 0x06, 0xee, 0xf0, 0xe5, 0x16, 0x42, 0x13}},
+ {0x0dc6, 64, { 0xe5, 0x15, 0x24, 0x29, 0xf5, 0x82, 0xe4, 0x35, 0x14, 0xf5, 0x83, 0xe0, 0xff, 0xe5, 0x18, 0x54, 0x20,
+  0xfe, 0x6f, 0x60, 0x15, 0xee, 0xf0, 0xe5, 0x15, 0x24, 0x31, 0xf5, 0x82, 0xe4, 0x35, 0x14, 0xf5,
+  0x83, 0xe0, 0x30, 0xe4, 0x04, 0xe5, 0x16, 0x42, 0x13, 0xe5, 0x15, 0x24, 0x2a, 0xf5, 0x82, 0xe4,
+  0x35, 0x14, 0xf5, 0x83, 0xe0, 0xff, 0xe5, 0x18, 0x54, 0x40, 0xfe, 0x6f, 0x60, 0x15, 0xee}},
+ {0x0e06, 64, { 0xf0, 0xe5, 0x15, 0x24, 0x31, 0xf5, 0x82, 0xe4, 0x35, 0x14, 0xf5, 0x83, 0xe0, 0x30, 0xe5, 0x04, 0xe5,
+  0x16, 0x42, 0x13, 0xe5, 0x15, 0x24, 0x30, 0xf5, 0x82, 0xe4, 0x35, 0x14, 0xf5, 0x83, 0xe4, 0xf0,
+  0x22, 0x30, 0x09, 0x03, 0x02, 0x0f, 0x27, 0xe5, 0x24, 0x14, 0x60, 0x2a, 0x14, 0x60, 0x41, 0x14,
+  0x60, 0x58, 0x14, 0x60, 0x6f, 0x24, 0x04, 0x60, 0x03, 0x02, 0x0e, 0xe5, 0x7e, 0x7b, 0x7f}},
+ {0x0e46, 64, { 0xc0, 0x75, 0x14, 0x7b, 0x75, 0x15, 0xc0, 0x90, 0x7f, 0x98, 0x74, 0x2e, 0xf0, 0x75, 0x16, 0x01, 0x12,
+  0x12, 0x9c, 0x75, 0x24, 0x01, 0x22, 0x7e, 0x7c, 0x7f, 0x00, 0x75, 0x14, 0x7c, 0x75, 0x15, 0x00,
+  0x90, 0x7f, 0x98, 0x74, 0x2d, 0xf0, 0x75, 0x16, 0x02, 0x12, 0x12, 0x9c, 0x75, 0x24, 0x02, 0x22,
+  0x7e, 0x7c, 0x7f, 0x40, 0x75, 0x14, 0x7c, 0x75, 0x15, 0x40, 0x90, 0x7f, 0x98, 0x74, 0x2b}},
+ {0x0e86, 64, { 0xf0, 0x75, 0x16, 0x04, 0x12, 0x12, 0x9c, 0x75, 0x24, 0x03, 0x22, 0x7e, 0x7c, 0x7f, 0x80, 0x75, 0x14,
+  0x7c, 0x75, 0x15, 0x80, 0x90, 0x7f, 0x98, 0x74, 0x27, 0xf0, 0x75, 0x16, 0x08, 0x12, 0x12, 0x9c,
+  0x75, 0x24, 0x04, 0x22, 0x30, 0x04, 0x33, 0xc2, 0x04, 0x53, 0x13, 0xdf, 0xe4, 0xf5, 0x18, 0x7e,
+  0x00, 0x7b, 0x00, 0x74, 0x2e, 0x25, 0x18, 0xf9, 0xee, 0x34, 0x00, 0xfa, 0x12, 0x11, 0xa6}},
+ {0x0ec6, 64, { 0xff, 0x74, 0x80, 0x25, 0x18, 0xf5, 0x82, 0xe4, 0x34, 0x7b, 0xf5, 0x83, 0xef, 0xf0, 0x05, 0x18, 0xe5,
+  0x18, 0xb4, 0x03, 0xdb, 0x90, 0x7f, 0xc3, 0x74, 0x03, 0xf0, 0x75, 0x24, 0x05, 0x22, 0xe5, 0x36,
+  0x60, 0x3b, 0xd5, 0x36, 0x0a, 0x53, 0x13, 0xef, 0x30, 0x0a, 0x04, 0xd2, 0x09, 0xc2, 0x0a, 0xe4,
+  0xf5, 0x18, 0x7e, 0x00, 0x7b, 0x00, 0x74, 0x35, 0x25, 0x18, 0xf9, 0xee, 0x34, 0x00, 0xfa}},
+ {0x0f06, 64, { 0x12, 0x11, 0xa6, 0xff, 0x74, 0x80, 0x25, 0x18, 0xf5, 0x82, 0xe4, 0x34, 0x7b, 0xf5, 0x83, 0xef, 0xf0,
+  0x05, 0x18, 0xe5, 0x18, 0xb4, 0x03, 0xdb, 0x90, 0x7f, 0xc3, 0x74, 0x03, 0xf0, 0xe4, 0xf5, 0x24,
+  0x22, 0xe4, 0xf5, 0x19, 0x7e, 0x00, 0x7b, 0x01, 0xe5, 0x15, 0x25, 0x19, 0xf9, 0xee, 0x35, 0x14,
+  0xfa, 0xe4, 0x12, 0x11, 0xec, 0x05, 0x19, 0xe5, 0x19, 0xb4, 0x3c, 0xe8, 0xe5, 0x15, 0x24}},
+ {0x0f46, 64, { 0x35, 0xf5, 0x82, 0xe4, 0x35, 0x14, 0xf5, 0x83, 0x74, 0x01, 0xf0, 0x90, 0x78, 0x41, 0x74, 0xf3, 0xf0,
+  0xe5, 0x15, 0x24, 0x37, 0xf5, 0x82, 0xe4, 0x35, 0x14, 0xf5, 0x83, 0x74, 0x03, 0xf0, 0x90, 0xc0,
+  0x00, 0xf0, 0x7f, 0x0c, 0xe4, 0xfd, 0x12, 0x16, 0x6b, 0x7f, 0x10, 0xe5, 0x15, 0x24, 0x33, 0xf5,
+  0x82, 0xe4, 0x35, 0x14, 0xf5, 0x83, 0xef, 0xf0, 0x12, 0x15, 0xdb, 0x90, 0x78, 0x41, 0x74}},
+ {0x0f86, 64, { 0xf2, 0xf0, 0x7f, 0x01, 0xe5, 0x15, 0x24, 0x36, 0xf5, 0x82, 0xe4, 0x35, 0x14, 0xf5, 0x83, 0xef, 0xf0,
+  0x44, 0x06, 0x90, 0xc0, 0x00, 0xf0, 0x90, 0x78, 0x41, 0x74, 0xf4, 0xf0, 0xe5, 0x15, 0x24, 0x39,
+  0xf5, 0x82, 0xe4, 0x35, 0x14, 0xf5, 0x83, 0x74, 0x80, 0xf0, 0x90, 0xc0, 0x00, 0xf0, 0x0f, 0xe4,
+  0xfd, 0x12, 0x16, 0x6b, 0xe4, 0xff, 0x7e, 0xa3, 0xe5, 0x15, 0x24, 0x32, 0xf5, 0x82, 0xe4}},
+ {0x0fc6, 64, { 0x35, 0x14, 0xf5, 0x83, 0xee, 0xf0, 0xfd, 0x12, 0x16, 0x6b, 0x90, 0x78, 0x41, 0x74, 0xf1, 0xf0, 0x90,
+  0xc0, 0x00, 0xe4, 0xf0, 0x7f, 0x05, 0x7d, 0x7f, 0x12, 0x16, 0x6b, 0x7f, 0x01, 0x12, 0x15, 0x43,
+  0x7f, 0x03, 0x7d, 0x07, 0x12, 0x16, 0x6b, 0x22, 0x53, 0x13, 0x3f, 0x90, 0x7b, 0xf1, 0xe0, 0x30,
+  0xe3, 0x16, 0x7e, 0x7b, 0x7f, 0xc0, 0x75, 0x14, 0x7b, 0x75, 0x15, 0xc0, 0x90, 0x7f, 0x98}},
+ {0x1006, 64, { 0x74, 0x2e, 0xf0, 0x75, 0x16, 0x01, 0x12, 0x08, 0xd7, 0x90, 0x7c, 0x31, 0xe0, 0x30, 0xe3, 0x16, 0x7e,
+  0x7c, 0x7f, 0x00, 0x75, 0x14, 0x7c, 0x75, 0x15, 0x00, 0x90, 0x7f, 0x98, 0x74, 0x2d, 0xf0, 0x75,
+  0x16, 0x02, 0x12, 0x08, 0xd7, 0x90, 0x7c, 0x71, 0xe0, 0x30, 0xe3, 0x16, 0x7e, 0x7c, 0x7f, 0x40,
+  0x75, 0x14, 0x7c, 0x75, 0x15, 0x40, 0x90, 0x7f, 0x98, 0x74, 0x2b, 0xf0, 0x75, 0x16, 0x04}},
+ {0x1046, 64, { 0x12, 0x08, 0xd7, 0x90, 0x7c, 0xb1, 0xe0, 0x30, 0xe3, 0x16, 0x7e, 0x7c, 0x7f, 0x80, 0x75, 0x14, 0x7c,
+  0x75, 0x15, 0x80, 0x90, 0x7f, 0x98, 0x74, 0x27, 0xf0, 0x75, 0x16, 0x08, 0x12, 0x08, 0xd7, 0x05,
+  0x12, 0xe5, 0x12, 0x54, 0x0f, 0xf5, 0x17, 0x70, 0x04, 0x12, 0x11, 0x1c, 0x22, 0xe5, 0x17, 0xb4,
+  0x01, 0x04, 0x12, 0x0c, 0x8c, 0x22, 0x90, 0x7f, 0xc2, 0xe0, 0x20, 0xe1, 0x08, 0xe5, 0x13}},
+ {0x1086, 64, { 0x60, 0x04, 0x12, 0x0e, 0x28, 0x22, 0x12, 0x0c, 0x8c, 0x22, 0x78, 0x7f, 0xe4, 0xf6, 0xd8, 0xfd, 0x75,
+  0x81, 0x37, 0x02, 0x10, 0xd7, 0x02, 0x12, 0x24, 0xe4, 0x93, 0xa3, 0xf8, 0xe4, 0x93, 0xa3, 0x40,
+  0x03, 0xf6, 0x80, 0x01, 0xf2, 0x08, 0xdf, 0xf4, 0x80, 0x29, 0xe4, 0x93, 0xa3, 0xf8, 0x54, 0x07,
+  0x24, 0x0c, 0xc8, 0xc3, 0x33, 0xc4, 0x54, 0x0f, 0x44, 0x20, 0xc8, 0x83, 0x40, 0x04, 0xf4}},
+ {0x10c6, 64, { 0x56, 0x80, 0x01, 0x46, 0xf6, 0xdf, 0xe4, 0x80, 0x0b, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80,
+  0x90, 0x18, 0x45, 0xe4, 0x7e, 0x01, 0x93, 0x60, 0xbc, 0xa3, 0xff, 0x54, 0x3f, 0x30, 0xe5, 0x09,
+  0x54, 0x1f, 0xfe, 0xe4, 0x93, 0xa3, 0x60, 0x01, 0x0e, 0xcf, 0x54, 0xc0, 0x25, 0xe0, 0x60, 0xa8,
+  0x40, 0xb8, 0xe4, 0x93, 0xa3, 0xfa, 0xe4, 0x93, 0xa3, 0xf8, 0xe4, 0x93, 0xa3, 0xc8, 0xc5}},
+ {0x1106, 64, { 0x82, 0xc8, 0xca, 0xc5, 0x83, 0xca, 0xf0, 0xa3, 0xc8, 0xc5, 0x82, 0xc8, 0xca, 0xc5, 0x83, 0xca, 0xdf,
+  0xe9, 0xde, 0xe7, 0x80, 0xbe, 0x90, 0x7f, 0xd2, 0xe0, 0x30, 0xe1, 0x03, 0x02, 0x11, 0xa5, 0xc2,
+  0x09, 0x90, 0x7b, 0x40, 0xe0, 0x14, 0x60, 0x26, 0x14, 0x60, 0x3b, 0x14, 0x60, 0x50, 0x24, 0x83,
+  0x60, 0x64, 0x24, 0x80, 0x70, 0x63, 0x7e, 0x7b, 0x7f, 0xc0, 0x75, 0x14, 0x7b, 0x75, 0x15}},
+ {0x1146, 64, { 0xc0, 0x90, 0x7f, 0x98, 0x74, 0x2e, 0xf0, 0x75, 0x16, 0x01, 0x12, 0x00, 0x46, 0x80, 0x4b, 0x7e, 0x7c,
+  0x7f, 0x00, 0x75, 0x14, 0x7c, 0x75, 0x15, 0x00, 0x90, 0x7f, 0x98, 0x74, 0x2d, 0xf0, 0x75, 0x16,
+  0x02, 0x12, 0x00, 0x46, 0x80, 0x33, 0x7e, 0x7c, 0x7f, 0x40, 0x75, 0x14, 0x7c, 0x75, 0x15, 0x40,
+  0x90, 0x7f, 0x98, 0x74, 0x2b, 0xf0, 0x75, 0x16, 0x04, 0x12, 0x00, 0x46, 0x80, 0x1b, 0x7e}},
+ {0x1186, 64, { 0x7c, 0x7f, 0x80, 0x75, 0x14, 0x7c, 0x75, 0x15, 0x80, 0x90, 0x7f, 0x98, 0x74, 0x27, 0xf0, 0x75, 0x16,
+  0x08, 0x12, 0x00, 0x46, 0x80, 0x03, 0x12, 0x17, 0x51, 0xe4, 0x90, 0x7f, 0xd3, 0xf0, 0x22, 0xbb,
+  0x01, 0x06, 0x89, 0x82, 0x8a, 0x83, 0xe0, 0x22, 0x50, 0x02, 0xe7, 0x22, 0xbb, 0xfe, 0x02, 0xe3,
+  0x22, 0x89, 0x82, 0x8a, 0x83, 0xe4, 0x93, 0x22, 0xbb, 0x01, 0x0c, 0xe5, 0x82, 0x29, 0xf5}},
+ {0x11c6, 64, { 0x82, 0xe5, 0x83, 0x3a, 0xf5, 0x83, 0xe0, 0x22, 0x50, 0x06, 0xe9, 0x25, 0x82, 0xf8, 0xe6, 0x22, 0xbb,
+  0xfe, 0x06, 0xe9, 0x25, 0x82, 0xf8, 0xe2, 0x22, 0xe5, 0x82, 0x29, 0xf5, 0x82, 0xe5, 0x83, 0x3a,
+  0xf5, 0x83, 0xe4, 0x93, 0x22, 0xbb, 0x01, 0x06, 0x89, 0x82, 0x8a, 0x83, 0xf0, 0x22, 0x50, 0x02,
+  0xf7, 0x22, 0xbb, 0xfe, 0x01, 0xf3, 0x22, 0xd0, 0x83, 0xd0, 0x82, 0xf8, 0xe4, 0x93, 0x70}},
+ {0x1206, 64, { 0x12, 0x74, 0x01, 0x93, 0x70, 0x0d, 0xa3, 0xa3, 0x93, 0xf8, 0x74, 0x01, 0x93, 0xf5, 0x82, 0x88, 0x83,
+  0xe4, 0x73, 0x74, 0x02, 0x93, 0x68, 0x60, 0xef, 0xa3, 0xa3, 0xa3, 0x80, 0xdf, 0x90, 0x7f, 0xae,
+  0xe0, 0xff, 0xd3, 0x92, 0x00, 0xe4, 0x33, 0xfe, 0xef, 0x4e, 0xf0, 0xd2, 0xe8, 0x43, 0xd8, 0x20,
+  0x90, 0x7f, 0xde, 0x74, 0x01, 0xf0, 0x90, 0x7f, 0xdf, 0xf0, 0x90, 0x7f, 0xab, 0x74, 0xff}},
+ {0x1246, 64, { 0xf0, 0x90, 0x7f, 0xa9, 0xf0, 0x90, 0x7f, 0xaa, 0xf0, 0x53, 0x91, 0xef, 0x90, 0x7f, 0xaf, 0x74, 0x01,
+  0xf0, 0x90, 0x7f, 0xae, 0x74, 0x0d, 0xf0, 0xd2, 0xaf, 0xd2, 0x0b, 0x12, 0x18, 0x09, 0xc2, 0x01,
+  0xe4, 0xf5, 0x2b, 0xf5, 0x31, 0xc2, 0x07, 0xc2, 0x02, 0x75, 0x29, 0x0f, 0x90, 0x7f, 0xd8, 0xe0,
+  0x65, 0x26, 0x60, 0x06, 0x75, 0x32, 0x0f, 0xe0, 0xf5, 0x26, 0x30, 0x02, 0x03, 0x12, 0x0f}},
+ {0x1286, 64, { 0xef, 0x30, 0x01, 0x07, 0xc2, 0x01, 0x12, 0x06, 0x3f, 0x80, 0xe2, 0x30, 0x08, 0xdf, 0xc2, 0x08, 0x12,
+  0x18, 0x2a, 0x80, 0xd8, 0x22, 0xe5, 0x13, 0x55, 0x16, 0x60, 0x6a, 0xe5, 0x15, 0x24, 0x3a, 0xf5,
+  0x82, 0xe4, 0x35, 0x14, 0xf5, 0x83, 0xe0, 0x70, 0x5c, 0xe5, 0x16, 0xf4, 0x52, 0x13, 0xe5, 0x15,
+  0x24, 0x26, 0xff, 0xe4, 0x35, 0x14, 0xfe, 0xe4, 0xfd, 0x0f, 0xef, 0xaa, 0x06, 0x70, 0x01}},
+ {0x12c6, 64, { 0x0e, 0x14, 0xf5, 0x82, 0x8a, 0x83, 0xe0, 0xfc, 0x74, 0x80, 0x2d, 0xf5, 0x82, 0xe4, 0x34, 0x7b, 0xf5,
+  0x83, 0xec, 0xf0, 0x0d, 0xbd, 0x0b, 0xe2, 0x90, 0x7f, 0xc3, 0x74, 0x0b, 0xf0, 0xe5, 0x15, 0x24,
+  0x3a, 0xf5, 0x82, 0xe4, 0x35, 0x14, 0xf5, 0x83, 0x74, 0x10, 0xf0, 0xe5, 0x15, 0x24, 0x2e, 0xf5,
+  0x82, 0xe4, 0x35, 0x14, 0xf5, 0x83, 0xe4, 0xf0, 0xe5, 0x15, 0x24, 0x2f, 0xf5, 0x82, 0xe4}},
+ {0x1306, 64, { 0x35, 0x14, 0xf5, 0x83, 0xe4, 0xf0, 0x22, 0xe5, 0x28, 0x45, 0x27, 0x60, 0x57, 0xae, 0x27, 0xaf, 0x28,
+  0xd3, 0xef, 0x94, 0x40, 0xee, 0x94, 0x00, 0x40, 0x04, 0x7e, 0x00, 0x7f, 0x40, 0xc3, 0xe5, 0x28,
+  0x9f, 0xf5, 0x28, 0xe5, 0x27, 0x9e, 0xf5, 0x27, 0xe4, 0xfd, 0xed, 0xc3, 0x9f, 0xe4, 0x9e, 0x50,
+  0x1f, 0x85, 0x34, 0x82, 0x85, 0x33, 0x83, 0xe0, 0xfc, 0x74, 0x00, 0x2d, 0xf5, 0x82, 0xe4}},
+ {0x1346, 64, { 0x34, 0x7f, 0xf5, 0x83, 0xec, 0xf0, 0x0d, 0x05, 0x34, 0xe5, 0x34, 0x70, 0x02, 0x05, 0x33, 0x80, 0xda,
+  0x90, 0x7f, 0xa9, 0x74, 0x01, 0xf0, 0x90, 0x7f, 0xac, 0xe0, 0x44, 0x01, 0xf0, 0x90, 0x7f, 0xb5,
+  0xef, 0xf0, 0x22, 0x90, 0x7f, 0xac, 0xe0, 0x54, 0xfe, 0xf0, 0xe4, 0x90, 0x7f, 0xb5, 0xf0, 0x22,
+  0x90, 0x7f, 0x98, 0x74, 0x0f, 0xf0, 0xe4, 0x90, 0x78, 0x4a, 0xf0, 0x90, 0x7f, 0x94, 0xf0}},
+ {0x1386, 64, { 0x90, 0x7f, 0x9d, 0x74, 0xff, 0xf0, 0xe4, 0x90, 0x7f, 0x97, 0xf0, 0x90, 0x78, 0x41, 0xf0, 0x90, 0x7f,
+  0x93, 0xf0, 0x90, 0x7f, 0x9c, 0x74, 0xff, 0xf0, 0x30, 0x00, 0x07, 0xe5, 0x29, 0x54, 0x0f, 0xff,
+  0x80, 0x02, 0x7f, 0x00, 0x90, 0x7f, 0x96, 0xef, 0xf0, 0x90, 0x7f, 0x98, 0x74, 0x1f, 0xf0, 0xe4,
+  0x90, 0x7f, 0x95, 0xf0, 0x90, 0x7f, 0x9e, 0x74, 0x3f, 0xf0, 0x90, 0x7f, 0x98, 0x74, 0xdf}},
+ {0x13c6, 64, { 0xf0, 0x90, 0x7f, 0x92, 0xe0, 0x54, 0xfd, 0xf0, 0x22, 0x8f, 0x19, 0x05, 0x2d, 0xe5, 0x2d, 0xae, 0x2c,
+  0x70, 0x02, 0x05, 0x2c, 0x14, 0xf5, 0x82, 0x8e, 0x83, 0xe5, 0x19, 0xf0, 0x12, 0x18, 0xef, 0x05,
+  0x2d, 0xe5, 0x2d, 0xac, 0x2c, 0x70, 0x02, 0x05, 0x2c, 0x14, 0xf5, 0x82, 0x8c, 0x83, 0xef, 0xf0,
+  0x15, 0x23, 0xe5, 0x23, 0x60, 0x1f, 0xe5, 0x15, 0x24, 0x38, 0xf5, 0x82, 0xe4, 0x35, 0x14}},
+ {0x1406, 64, { 0xf5, 0x83, 0xc0, 0x83, 0xc0, 0x82, 0xe0, 0xfe, 0x12, 0x18, 0xd3, 0x8f, 0x19, 0xee, 0x4f, 0xd0, 0x82,
+  0xd0, 0x83, 0xf0, 0x80, 0xb5, 0x22, 0x90, 0x78, 0x41, 0xe5, 0x11, 0xf0, 0x90, 0x78, 0x4f, 0x74,
+  0xc0, 0xf0, 0xe4, 0x90, 0x78, 0x50, 0xf0, 0xe5, 0x2c, 0x90, 0x78, 0x51, 0xf0, 0xae, 0x2c, 0xe5,
+  0x2d, 0x90, 0x78, 0x52, 0xf0, 0x90, 0x78, 0x54, 0xe5, 0x23, 0xf0, 0x90, 0x78, 0x57, 0x74}},
+ {0x1446, 64, { 0x04, 0xf0, 0x90, 0x7f, 0xe2, 0xe0, 0x44, 0x10, 0xf0, 0xe0, 0x54, 0xf7, 0xf0, 0xe4, 0x90, 0x78, 0x55,
+  0xf0, 0x90, 0x78, 0x55, 0xe0, 0x60, 0xfa, 0x22, 0x8f, 0x19, 0xe4, 0xf5, 0x1a, 0x75, 0x1b, 0xff,
+  0x75, 0x1c, 0x19, 0x75, 0x1d, 0x86, 0xab, 0x1b, 0xaa, 0x1c, 0xa9, 0x1d, 0x90, 0x00, 0x01, 0x12,
+  0x11, 0xbf, 0xb4, 0x03, 0x1d, 0xaf, 0x1a, 0x05, 0x1a, 0xef, 0xb5, 0x19, 0x01, 0x22, 0x12}},
+ {0x1486, 64, { 0x11, 0xa6, 0x7e, 0x00, 0x29, 0xff, 0xee, 0x3a, 0xa9, 0x07, 0x75, 0x1b, 0xff, 0xf5, 0x1c, 0x89, 0x1d,
+  0x80, 0xd4, 0x7b, 0x00, 0x7a, 0x00, 0x79, 0x00, 0x22, 0x90, 0x78, 0x41, 0xe5, 0x11, 0xf0, 0xe5,
+  0x2c, 0x90, 0x78, 0x4f, 0xf0, 0xae, 0x2c, 0xe5, 0x2d, 0x90, 0x78, 0x50, 0xf0, 0x90, 0x78, 0x51,
+  0x74, 0xc0, 0xf0, 0xe4, 0x90, 0x78, 0x52, 0xf0, 0x90, 0x78, 0x54, 0xe5, 0x23, 0xf0, 0x90}},
+ {0x14c6, 64, { 0x78, 0x57, 0x74, 0x04, 0xf0, 0xe4, 0x90, 0x78, 0x55, 0xf0, 0x90, 0x78, 0x55, 0xe0, 0x60, 0xfa, 0x22,
+  0xe5, 0x15, 0x24, 0x04, 0xf5, 0x82, 0xe4, 0x35, 0x14, 0xf5, 0x83, 0xe0, 0x14, 0x60, 0x0f, 0x14,
+  0x60, 0x13, 0x14, 0x60, 0x17, 0x80, 0x00, 0x90, 0x7f, 0xc7, 0xef, 0xf0, 0x80, 0x13, 0x90, 0x7f,
+  0xc9, 0xef, 0xf0, 0x80, 0x0c, 0x90, 0x7f, 0xcb, 0xef, 0xf0, 0x80, 0x05, 0x90, 0x7f, 0xcd}},
+ {0x1506, 64, { 0xef, 0xf0, 0xe5, 0x16, 0x42, 0x2a, 0x22, 0xe5, 0x15, 0x24, 0x04, 0xf5, 0x82, 0xe4, 0x35, 0x14, 0xf5,
+  0x83, 0xe0, 0x14, 0x60, 0x0f, 0x14, 0x60, 0x13, 0x14, 0x60, 0x17, 0x80, 0x00, 0x90, 0x7f, 0xb7,
+  0xef, 0xf0, 0x80, 0x13, 0x90, 0x7f, 0xb9, 0xef, 0xf0, 0x80, 0x0c, 0x90, 0x7f, 0xbb, 0xef, 0xf0,
+  0x80, 0x05, 0x90, 0x7f, 0xbd, 0xef, 0xf0, 0xe5, 0x16, 0x42, 0x2a, 0x22, 0xae, 0x07, 0xe4}},
+ {0x1546, 64, { 0xff, 0xe5, 0x15, 0x24, 0x32, 0xf5, 0x82, 0xe4, 0x35, 0x14, 0xf5, 0x83, 0xe0, 0x54, 0x7f, 0xfd, 0x12,
+  0x16, 0x6b, 0x90, 0x78, 0x41, 0x74, 0xf1, 0xf0, 0x90, 0xc0, 0x00, 0xee, 0xf0, 0xe4, 0xe5, 0x15,
+  0x24, 0x32, 0xf5, 0x82, 0xe4, 0x35, 0x14, 0xf5, 0x83, 0xe0, 0x44, 0x80, 0xfd, 0x12, 0x16, 0x6b,
+  0x22, 0xc0, 0xe0, 0xc0, 0xf0, 0xc0, 0x83, 0xc0, 0x82, 0xc0, 0x85, 0xc0, 0x84, 0xc0, 0x86}},
+ {0x1586, 64, { 0x75, 0x86, 0x00, 0xc0, 0xd0, 0x75, 0xd0, 0x08, 0x53, 0x91, 0xef, 0x90, 0x7f, 0xa9, 0x74, 0x01, 0xf0,
+  0x12, 0x13, 0x0d, 0xd0, 0xd0, 0xd0, 0x86, 0xd0, 0x84, 0xd0, 0x85, 0xd0, 0x82, 0xd0, 0x83, 0xd0,
+  0xf0, 0xd0, 0xe0, 0x32, 0x90, 0x78, 0x41, 0x74, 0xf3, 0xf0, 0x90, 0xc0, 0x00, 0x74, 0xbf, 0xf0,
+  0x90, 0x78, 0x41, 0x74, 0xf1, 0xf0, 0x90, 0xc0, 0x00, 0xef, 0xf0, 0x90, 0x78, 0x41, 0x74}},
+ {0x15c6, 64, { 0xf3, 0xf0, 0xe5, 0x15, 0x24, 0x37, 0xf5, 0x82, 0xe4, 0x35, 0x14, 0xf5, 0x83, 0xe0, 0x54, 0x7f, 0x90,
+  0xc0, 0x00, 0xf0, 0x22, 0x90, 0x78, 0x41, 0x74, 0xf3, 0xf0, 0x90, 0xc0, 0x00, 0x74, 0xbf, 0xf0,
+  0x90, 0x78, 0x41, 0x74, 0xf2, 0xf0, 0x90, 0xc0, 0x00, 0xef, 0xf0, 0x90, 0x78, 0x41, 0x74, 0xf3,
+  0xf0, 0xe5, 0x15, 0x24, 0x37, 0xf5, 0x82, 0xe4, 0x35, 0x14, 0xf5, 0x83, 0xe0, 0x54, 0x7f}},
+ {0x1606, 64, { 0x90, 0xc0, 0x00, 0xf0, 0x22, 0x90, 0x78, 0x41, 0x74, 0xf3, 0xf0, 0x90, 0xc0, 0x00, 0x74, 0xbf, 0xf0,
+  0x90, 0x78, 0x41, 0x74, 0xf4, 0xf0, 0x90, 0xc0, 0x00, 0xef, 0xf0, 0x90, 0x78, 0x41, 0x74, 0xf3,
+  0xf0, 0xe5, 0x15, 0x24, 0x37, 0xf5, 0x82, 0xe4, 0x35, 0x14, 0xf5, 0x83, 0xe0, 0x54, 0x7f, 0x90,
+  0xc0, 0x00, 0xf0, 0x22, 0x90, 0x78, 0x41, 0x74, 0xf3, 0xf0, 0x90, 0xc0, 0x00, 0x74, 0xbf}},
+ {0x1646, 64, { 0xf0, 0x90, 0x78, 0x41, 0x74, 0xf6, 0xf0, 0x90, 0xc0, 0x00, 0xef, 0xf0, 0x90, 0x78, 0x41, 0x74, 0xf3,
+  0xf0, 0xe5, 0x15, 0x24, 0x37, 0xf5, 0x82, 0xe4, 0x35, 0x14, 0xf5, 0x83, 0xe0, 0x54, 0x7f, 0x90,
+  0xc0, 0x00, 0xf0, 0x22, 0x90, 0x78, 0x41, 0x74, 0xf3, 0xf0, 0xe5, 0x15, 0x24, 0x37, 0xf5, 0x82,
+  0xe4, 0x35, 0x14, 0xf5, 0x83, 0xe0, 0x54, 0x7f, 0x90, 0xc0, 0x00, 0xf0, 0x90, 0x78, 0x41}},
+ {0x1686, 64, { 0x74, 0xf7, 0xf0, 0x90, 0xc0, 0x00, 0xef, 0xf0, 0x90, 0x78, 0x41, 0x74, 0xf5, 0xf0, 0x90, 0xc0, 0x00,
+  0xed, 0xf0, 0x22, 0xe5, 0x15, 0x24, 0x04, 0xf5, 0x82, 0xe4, 0x35, 0x14, 0xf5, 0x83, 0xe0, 0x14,
+  0x60, 0x0e, 0x14, 0x60, 0x11, 0x14, 0x60, 0x14, 0x80, 0x00, 0x90, 0x7f, 0xc6, 0xe0, 0xff, 0x22,
+  0x90, 0x7f, 0xc8, 0xe0, 0xff, 0x22, 0x90, 0x7f, 0xca, 0xe0, 0xff, 0x22, 0x90, 0x7f, 0xcc}},
+ {0x16c6, 64, { 0xe0, 0xff, 0x22, 0xe5, 0x15, 0x24, 0x04, 0xf5, 0x82, 0xe4, 0x35, 0x14, 0xf5, 0x83, 0xe0, 0x14, 0x60,
+  0x0e, 0x14, 0x60, 0x11, 0x14, 0x60, 0x14, 0x80, 0x00, 0x90, 0x7f, 0xb6, 0xe0, 0xff, 0x22, 0x90,
+  0x7f, 0xb8, 0xe0, 0xff, 0x22, 0x90, 0x7f, 0xba, 0xe0, 0xff, 0x22, 0x90, 0x7f, 0xbc, 0xe0, 0xff,
+  0x22, 0xe5, 0x15, 0x24, 0x04, 0xf5, 0x82, 0xe4, 0x35, 0x14, 0xf5, 0x83, 0xe0, 0x14, 0x60}},
+ {0x1706, 64, { 0x0e, 0x14, 0x60, 0x11, 0x14, 0x60, 0x14, 0x80, 0x00, 0x90, 0x7f, 0xc7, 0xe0, 0xff, 0x22, 0x90, 0x7f,
+  0xc9, 0xe0, 0xff, 0x22, 0x90, 0x7f, 0xcb, 0xe0, 0xff, 0x22, 0x90, 0x7f, 0xcd, 0xe0, 0xff, 0x22,
+  0xc0, 0xe0, 0xc0, 0x83, 0xc0, 0x82, 0xc0, 0x85, 0xc0, 0x84, 0xc0, 0x86, 0x75, 0x86, 0x00, 0x90,
+  0x7f, 0xc4, 0xe4, 0xf0, 0x53, 0x91, 0xef, 0x90, 0x7f, 0xab, 0x74, 0x04, 0xf0, 0xd0, 0x86}},
+ {0x1746, 64, { 0xd0, 0x84, 0xd0, 0x85, 0xd0, 0x82, 0xd0, 0x83, 0xd0, 0xe0, 0x32, 0x90, 0x7b, 0x41, 0xe0, 0xf5, 0x36,
+  0x43, 0x13, 0x10, 0xa3, 0xe0, 0x60, 0x09, 0x90, 0x7f, 0xd7, 0x74, 0x17, 0xf0, 0x74, 0x37, 0xf0,
+  0x90, 0x7b, 0x43, 0xe0, 0xf5, 0x37, 0xa3, 0xe0, 0x54, 0xf0, 0xf5, 0x29, 0xe0, 0x60, 0x02, 0xd2,
+  0x0a, 0x22, 0xc0, 0xe0, 0xc0, 0x83, 0xc0, 0x82, 0xc0, 0x85, 0xc0, 0x84, 0xc0, 0x86, 0x75}},
+ {0x1786, 64, { 0x86, 0x00, 0xd2, 0x01, 0x53, 0x91, 0xef, 0x90, 0x7f, 0xab, 0x74, 0x01, 0xf0, 0xd0, 0x86, 0xd0, 0x84,
+  0xd0, 0x85, 0xd0, 0x82, 0xd0, 0x83, 0xd0, 0xe0, 0x32, 0xc0, 0xe0, 0xc0, 0x83, 0xc0, 0x82, 0xc0,
+  0x85, 0xc0, 0x84, 0xc0, 0x86, 0x75, 0x86, 0x00, 0xd2, 0x08, 0x53, 0x91, 0xef, 0x90, 0x7f, 0xab,
+  0x74, 0x08, 0xf0, 0xd0, 0x86, 0xd0, 0x84, 0xd0, 0x85, 0xd0, 0x82, 0xd0, 0x83, 0xd0, 0xe0}},
+ {0x17c6, 64, { 0x32, 0x12, 0x18, 0xb7, 0xae, 0x07, 0x12, 0x18, 0xb7, 0xad, 0x07, 0xee, 0x6d, 0x60, 0x10, 0x12, 0x18,
+  0xb7, 0xae, 0x07, 0xee, 0x6d, 0x60, 0x07, 0x12, 0x18, 0xb7, 0xad, 0x07, 0x80, 0xec, 0xaf, 0x06,
+  0x22, 0x74, 0x00, 0xf5, 0x86, 0x90, 0xfd, 0xa5, 0x7c, 0x05, 0xa3, 0xe5, 0x82, 0x45, 0x83, 0x70,
+  0xf9, 0x22, 0x90, 0x7f, 0xd6, 0xe0, 0x44, 0x80, 0xf0, 0x43, 0x87, 0x01, 0x00, 0x00, 0x00}},
+ {0x1806, 64, { 0x00, 0x00, 0x22, 0x90, 0x7f, 0xd6, 0xe0, 0x44, 0x04, 0xf0, 0xe0, 0x44, 0x08, 0xf0, 0x30, 0x0b, 0x04,
+  0xe0, 0x44, 0x02, 0xf0, 0x7f, 0xf4, 0x7e, 0x01, 0x12, 0x18, 0x60, 0x90, 0x7f, 0xd6, 0xe0, 0x54,
+  0xf7, 0xf0, 0x22, 0x12, 0x13, 0x77, 0x12, 0x17, 0xf9, 0x90, 0x7f, 0xd6, 0xe0, 0x30, 0xe7, 0x0a,
+  0x7f, 0x05, 0x7e, 0x00, 0x12, 0x18, 0x60, 0x12, 0x18, 0x93, 0x12, 0x0a, 0xce, 0x22, 0x03}},
+ {0x1846, 64, { 0x35, 0x80, 0x00, 0x00, 0x03, 0x2e, 0x81, 0x00, 0x00, 0xc1, 0x85, 0xc1, 0x81, 0xc1, 0x08, 0xc1, 0x00,
+  0xc1, 0x06, 0x01, 0x22, 0x00, 0x01, 0x24, 0x00, 0x00, 0x8e, 0x17, 0x8f, 0x18, 0xe5, 0x18, 0x15,
+  0x18, 0xae, 0x17, 0x70, 0x02, 0x15, 0x17, 0x4e, 0x60, 0x08, 0x12, 0x17, 0xe8, 0x12, 0x17, 0xe8,
+  0x80, 0xeb, 0x22, 0xe5, 0x15, 0x24, 0x04, 0xf5, 0x82, 0xe4, 0x35, 0x14, 0xf5, 0x83, 0xe0}},
+ {0x1886, 64, { 0x04, 0xff, 0x44, 0x10, 0x90, 0x7f, 0xd7, 0xf0, 0xef, 0x44, 0x30, 0xf0, 0x22, 0x90, 0x7f, 0xd6, 0xe0,
+  0x44, 0x01, 0xf0, 0x7f, 0x0d, 0x7e, 0x00, 0x12, 0x18, 0x60, 0x90, 0x7f, 0xd6, 0xe0, 0x54, 0xfe,
+  0xf0, 0x22, 0xe5, 0x11, 0x24, 0x02, 0x90, 0x78, 0x41, 0xf0, 0x90, 0xc0, 0x00, 0xe0, 0xff, 0x22,
+  0xe5, 0x11, 0x24, 0x03, 0x90, 0x78, 0x41, 0xf0, 0x90, 0xc0, 0x00, 0xe0, 0xff, 0x22, 0xe5}},
+ {0x18c6, 64, { 0x11, 0x24, 0x04, 0x90, 0x78, 0x41, 0xf0, 0x90, 0xc0, 0x00, 0xe0, 0xff, 0x22, 0xe5, 0x11, 0x24, 0x05,
+  0x90, 0x78, 0x41, 0xf0, 0x90, 0xc0, 0x00, 0xe0, 0xff, 0x22, 0xe5, 0x11, 0x24, 0x06, 0x90, 0x78,
+  0x41, 0xf0, 0x90, 0xc0, 0x00, 0xe0, 0xff, 0x22, 0x90, 0x78, 0x41, 0xe5, 0x11, 0xf0, 0x90, 0xc0,
+  0x00, 0xe0, 0xff, 0x22, 0x53, 0xd8, 0xef, 0x32, 0x00, 0x12, 0x01, 0x10, 0x01, 0xff, 0xff}},
+ {0x1906, 64, { 0xff, 0x40, 0xcd, 0x06, 0x2a, 0x01, 0x00, 0x00, 0x01, 0x02, 0x00, 0x04, 0x09, 0x02, 0x74, 0x00, 0x01,
+  0x01, 0x00, 0xa0, 0x32, 0x09, 0x04, 0x00, 0x00, 0x0e, 0xff, 0x00, 0x00, 0x00, 0x07, 0x05, 0x01,
+  0x02, 0x40, 0x00, 0x00, 0x07, 0x05, 0x02, 0x02, 0x40, 0x00, 0x00, 0x07, 0x05, 0x03, 0x02, 0x40,
+  0x00, 0x00, 0x07, 0x05, 0x04, 0x02, 0x40, 0x00, 0x00, 0x07, 0x05, 0x05, 0x02, 0x40, 0x00}},
+ {0x1946, 64, { 0x00, 0x07, 0x05, 0x06, 0x02, 0x40, 0x00, 0x00, 0x07, 0x05, 0x07, 0x02, 0x40, 0x00, 0x00, 0x07, 0x05,
+  0x81, 0x02, 0x40, 0x00, 0x01, 0x07, 0x05, 0x82, 0x02, 0x40, 0x00, 0x01, 0x07, 0x05, 0x83, 0x02,
+  0x40, 0x00, 0x01, 0x07, 0x05, 0x84, 0x02, 0x40, 0x00, 0x01, 0x07, 0x05, 0x85, 0x02, 0x40, 0x00,
+  0x01, 0x07, 0x05, 0x86, 0x02, 0x40, 0x00, 0x01, 0x07, 0x05, 0x87, 0x02, 0x40, 0x00, 0x01}},
+ {0x1986, 64, { 0x04, 0x03, 0x09, 0x04, 0x48, 0x03, 0x4b, 0x00, 0x65, 0x00, 0x79, 0x00, 0x73, 0x00, 0x70, 0x00, 0x61,
+  0x00, 0x6e, 0x00, 0x2c, 0x00, 0x20, 0x00, 0x61, 0x00, 0x20, 0x00, 0x64, 0x00, 0x69, 0x00, 0x76,
+  0x00, 0x69, 0x00, 0x73, 0x00, 0x69, 0x00, 0x6f, 0x00, 0x6e, 0x00, 0x20, 0x00, 0x6f, 0x00, 0x66,
+  0x00, 0x20, 0x00, 0x49, 0x00, 0x6e, 0x00, 0x6e, 0x00, 0x6f, 0x00, 0x53, 0x00, 0x79, 0x00}},
+ {0x19c6, 64, { 0x73, 0x00, 0x20, 0x00, 0x49, 0x00, 0x6e, 0x00, 0x63, 0x00, 0x2e, 0x00, 0x24, 0x03, 0x4b, 0x00, 0x65,
+  0x00, 0x79, 0x00, 0x73, 0x00, 0x70, 0x00, 0x61, 0x00, 0x6e, 0x00, 0x20, 0x00, 0x55, 0x00, 0x53,
+  0x00, 0x41, 0x00, 0x2d, 0x00, 0x34, 0x00, 0x39, 0x00, 0x57, 0x00, 0x4c, 0x00, 0x43, 0x00, 0x22,
+  0x03, 0x55, 0x00, 0x53, 0x00, 0x41, 0x00, 0x2d, 0x00, 0x36, 0x00, 0x35, 0x00, 0x20, 0x00}},
+ {0x1a06, 64, { 0x32, 0x00, 0x30, 0x00, 0x30, 0x00, 0x33, 0x00, 0x6a, 0x00, 0x61, 0x00, 0x6e, 0x00, 0x33, 0x00, 0x31,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+ {0x1a46, 64, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+ {0x1a86, 64, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+ {0x1ac6, 64, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x17, 0x79, 0x00, 0x02, 0x1b}},
+ {0x1b06, 21, { 0x04, 0x00, 0x02, 0x17, 0x27, 0x00, 0x02, 0x17, 0xa0, 0x00, 0x02, 0x1b, 0x10, 0x00, 0x02, 0x1b, 0x14,
+  0x00, 0x02, 0x15, 0x78}},
+ {0xffff,  0, { 0x00}},
+};
+
+
diff -urN linux-2.4.21-pre5/drivers/usb/serial/pl2303.c linux-2.4.21-pre6/drivers/usb/serial/pl2303.c
--- linux-2.4.21-pre5/drivers/usb/serial/pl2303.c	Wed Mar 26 16:11:29 2003
+++ linux-2.4.21-pre6/drivers/usb/serial/pl2303.c	Wed Mar 26 16:11:39 2003
@@ -71,6 +71,7 @@
 	{ USB_DEVICE(MA620_VENDOR_ID, MA620_PRODUCT_ID) },
 	{ USB_DEVICE(RATOC_VENDOR_ID, RATOC_PRODUCT_ID) },
 	{ USB_DEVICE(TRIPP_VENDOR_ID, TRIPP_PRODUCT_ID) },
+	{ USB_DEVICE(RADIOSHACK_VENDOR_ID, RADIOSHACK_PRODUCT_ID) },
 	{ }					/* Terminating entry */
 };
 
diff -urN linux-2.4.21-pre5/drivers/usb/serial/pl2303.h linux-2.4.21-pre6/drivers/usb/serial/pl2303.h
--- linux-2.4.21-pre5/drivers/usb/serial/pl2303.h	Wed Mar 26 16:11:29 2003
+++ linux-2.4.21-pre6/drivers/usb/serial/pl2303.h	Wed Mar 26 16:11:39 2003
@@ -31,3 +31,6 @@
 
 #define TRIPP_VENDOR_ID		0x2478
 #define TRIPP_PRODUCT_ID	0x2008
+
+#define RADIOSHACK_VENDOR_ID	0x1453
+#define RADIOSHACK_PRODUCT_ID	0x4026
diff -urN linux-2.4.21-pre5/drivers/usb/storage/transport.c linux-2.4.21-pre6/drivers/usb/storage/transport.c
--- linux-2.4.21-pre5/drivers/usb/storage/transport.c	Wed Mar 26 16:11:29 2003
+++ linux-2.4.21-pre6/drivers/usb/storage/transport.c	Wed Mar 26 16:11:39 2003
@@ -700,20 +700,26 @@
 		unsigned short old_sg;
 		unsigned old_request_bufflen;
 		unsigned char old_sc_data_direction;
+		unsigned char old_cmd_len;
 		unsigned char old_cmnd[MAX_COMMAND_SIZE];
 
 		US_DEBUGP("Issuing auto-REQUEST_SENSE\n");
 
 		/* save the old command */
 		memcpy(old_cmnd, srb->cmnd, MAX_COMMAND_SIZE);
+		old_cmd_len = srb->cmd_len;
 
 		/* set the command and the LUN */
+		memset(srb->cmnd, 0, MAX_COMMAND_SIZE);
 		srb->cmnd[0] = REQUEST_SENSE;
 		srb->cmnd[1] = old_cmnd[1] & 0xE0;
-		srb->cmnd[2] = 0;
-		srb->cmnd[3] = 0;
 		srb->cmnd[4] = 18;
-		srb->cmnd[5] = 0;
+
+		/* FIXME: we must do the protocol translation here */
+		if (us->subclass == US_SC_RBC || us->subclass == US_SC_SCSI)
+			srb->cmd_len = 6;
+		else
+			srb->cmd_len = 12;
 
 		/* set the transfer direction */
 		old_sc_data_direction = srb->sc_data_direction;
@@ -739,6 +745,7 @@
 		srb->request_bufflen = old_request_bufflen;
 		srb->use_sg = old_sg;
 		srb->sc_data_direction = old_sc_data_direction;
+		srb->cmd_len = old_cmd_len;
 		memcpy(srb->cmnd, old_cmnd, MAX_COMMAND_SIZE);
 
 		if (temp_result == USB_STOR_TRANSPORT_ABORTED) {
diff -urN linux-2.4.21-pre5/drivers/usb/storage/unusual_devs.h linux-2.4.21-pre6/drivers/usb/storage/unusual_devs.h
--- linux-2.4.21-pre5/drivers/usb/storage/unusual_devs.h	Wed Mar 26 16:11:29 2003
+++ linux-2.4.21-pre6/drivers/usb/storage/unusual_devs.h	Wed Mar 26 16:11:39 2003
@@ -255,6 +255,12 @@
 		US_SC_UFI, US_PR_CB, NULL,
 		US_FL_SINGLE_LUN | US_FL_START_STOP ),
 		
+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 ),
+
 /* Submitted by Nathan Babb <nathan@lexi.com> */
 UNUSUAL_DEV(  0x054c, 0x006d, 0x0000, 0x9999,
                 "Sony",
diff -urN linux-2.4.21-pre5/drivers/usb/uhci-debug.h linux-2.4.21-pre6/drivers/usb/uhci-debug.h
--- linux-2.4.21-pre5/drivers/usb/uhci-debug.h	Thu Nov 22 11:49:54 2001
+++ linux-2.4.21-pre6/drivers/usb/uhci-debug.h	Wed Dec 31 16:00:00 1969
@@ -1,573 +0,0 @@
-/*
- * UHCI-specific debugging code. Invaluable when something
- * goes wrong, but don't get in my face.
- *
- * Kernel visible pointers are surrounded in []'s and bus
- * visible pointers are surrounded in ()'s
- *
- * (C) Copyright 1999 Linus Torvalds
- * (C) Copyright 1999-2001 Johannes Erdfelt
- */
-
-#include <linux/config.h>
-#include <linux/kernel.h>
-#include <linux/proc_fs.h>
-#include <asm/io.h>
-
-#include "uhci.h"
-
-/* Handle REALLY large printk's so we don't overflow buffers */
-static void inline lprintk(char *buf)
-{
-	char *p;
-
-	/* Just write one line at a time */
-	while (buf) {
-		p = strchr(buf, '\n');
-		if (p)
-			*p = 0;
-		printk("%s\n", buf);
-		buf = p;
-		if (buf)
-			buf++;
-	}
-}
-
-static int inline uhci_is_skeleton_td(struct uhci *uhci, struct uhci_td *td)
-{
-	int i;
-
-	for (i = 0; i < UHCI_NUM_SKELTD; i++)
-		if (td == uhci->skeltd[i])
-			return 1;
-
-	return 0;
-}
-
-static int inline uhci_is_skeleton_qh(struct uhci *uhci, struct uhci_qh *qh)
-{
-	int i;
-
-	for (i = 0; i < UHCI_NUM_SKELQH; i++)
-		if (qh == uhci->skelqh[i])
-			return 1;
-
-	return 0;
-}
-
-static int uhci_show_td(struct uhci_td *td, char *buf, int len, int space)
-{
-	char *out = buf;
-	char *spid;
-
-	/* Try to make sure there's enough memory */
-	if (len < 160)
-		return 0;
-
-	out += sprintf(out, "%*s[%p] link (%08x) ", space, "", td, td->link);
-	out += sprintf(out, "e%d %s%s%s%s%s%s%s%s%s%sLength=%x ",
-		((td->status >> 27) & 3),
-		(td->status & TD_CTRL_SPD) ?      "SPD " : "",
-		(td->status & TD_CTRL_LS) ?       "LS " : "",
-		(td->status & TD_CTRL_IOC) ?      "IOC " : "",
-		(td->status & TD_CTRL_ACTIVE) ?   "Active " : "",
-		(td->status & TD_CTRL_STALLED) ?  "Stalled " : "",
-		(td->status & TD_CTRL_DBUFERR) ?  "DataBufErr " : "",
-		(td->status & TD_CTRL_BABBLE) ?   "Babble " : "",
-		(td->status & TD_CTRL_NAK) ?      "NAK " : "",
-		(td->status & TD_CTRL_CRCTIMEO) ? "CRC/Timeo " : "",
-		(td->status & TD_CTRL_BITSTUFF) ? "BitStuff " : "",
-		td->status & 0x7ff);
-
-	switch (td->info & 0xff) {
-		case USB_PID_SETUP:
-			spid = "SETUP";
-			break;
-		case USB_PID_OUT:
-			spid = "OUT";
-			break;
-		case USB_PID_IN:
-			spid = "IN";
-			break;
-		default:
-			spid = "?";
-			break;
-	}
-
-	out += sprintf(out, "MaxLen=%x DT%d EndPt=%x Dev=%x, PID=%x(%s) ",
-		td->info >> 21,
-		((td->info >> 19) & 1),
-		(td->info >> 15) & 15,
-		(td->info >> 8) & 127,
-		(td->info & 0xff),
-		spid);
-	out += sprintf(out, "(buf=%08x)\n", td->buffer);
-
-	return out - buf;
-}
-
-static int uhci_show_sc(int port, unsigned short status, char *buf, int len)
-{
-	char *out = buf;
-
-	/* Try to make sure there's enough memory */
-	if (len < 80)
-		return 0;
-
-	out += sprintf(out, "  stat%d     =     %04x   %s%s%s%s%s%s%s%s\n",
-		port,
-		status,
-		(status & USBPORTSC_SUSP) ? "PortSuspend " : "",
-		(status & USBPORTSC_PR) ?   "PortReset " : "",
-		(status & USBPORTSC_LSDA) ? "LowSpeed " : "",
-		(status & USBPORTSC_RD) ?   "ResumeDetect " : "",
-		(status & USBPORTSC_PEC) ?  "EnableChange " : "",
-		(status & USBPORTSC_PE) ?   "PortEnabled " : "",
-		(status & USBPORTSC_CSC) ?  "ConnectChange " : "",
-		(status & USBPORTSC_CCS) ?  "PortConnected " : "");
-
-	return out - buf;
-}
-
-static int uhci_show_status(struct uhci *uhci, char *buf, int len)
-{
-	char *out = buf;
-	unsigned int io_addr = uhci->io_addr;
-	unsigned short usbcmd, usbstat, usbint, usbfrnum;
-	unsigned int flbaseadd;
-	unsigned char sof;
-	unsigned short portsc1, portsc2;
-
-	/* Try to make sure there's enough memory */
-	if (len < 80 * 6)
-		return 0;
-
-	usbcmd    = inw(io_addr + 0);
-	usbstat   = inw(io_addr + 2);
-	usbint    = inw(io_addr + 4);
-	usbfrnum  = inw(io_addr + 6);
-	flbaseadd = inl(io_addr + 8);
-	sof       = inb(io_addr + 12);
-	portsc1   = inw(io_addr + 16);
-	portsc2   = inw(io_addr + 18);
-
-	out += sprintf(out, "  usbcmd    =     %04x   %s%s%s%s%s%s%s%s\n",
-		usbcmd,
-		(usbcmd & USBCMD_MAXP) ?    "Maxp64 " : "Maxp32 ",
-		(usbcmd & USBCMD_CF) ?      "CF " : "",
-		(usbcmd & USBCMD_SWDBG) ?   "SWDBG " : "",
-		(usbcmd & USBCMD_FGR) ?     "FGR " : "",
-		(usbcmd & USBCMD_EGSM) ?    "EGSM " : "",
-		(usbcmd & USBCMD_GRESET) ?  "GRESET " : "",
-		(usbcmd & USBCMD_HCRESET) ? "HCRESET " : "",
-		(usbcmd & USBCMD_RS) ?      "RS " : "");
-
-	out += sprintf(out, "  usbstat   =     %04x   %s%s%s%s%s%s\n",
-		usbstat,
-		(usbstat & USBSTS_HCH) ?    "HCHalted " : "",
-		(usbstat & USBSTS_HCPE) ?   "HostControllerProcessError " : "",
-		(usbstat & USBSTS_HSE) ?    "HostSystemError " : "",
-		(usbstat & USBSTS_RD) ?     "ResumeDetect " : "",
-		(usbstat & USBSTS_ERROR) ?  "USBError " : "",
-		(usbstat & USBSTS_USBINT) ? "USBINT " : "");
-
-	out += sprintf(out, "  usbint    =     %04x\n", usbint);
-	out += sprintf(out, "  usbfrnum  =   (%d)%03x\n", (usbfrnum >> 10) & 1,
-		0xfff & (4*(unsigned int)usbfrnum));
-	out += sprintf(out, "  flbaseadd = %08x\n", flbaseadd);
-	out += sprintf(out, "  sof       =       %02x\n", sof);
-	out += uhci_show_sc(1, portsc1, out, len - (out - buf));
-	out += uhci_show_sc(2, portsc2, out, len - (out - buf));
-
-	return out - buf;
-}
-
-static int uhci_show_qh(struct uhci_qh *qh, char *buf, int len, int space)
-{
-	char *out = buf;
-	struct urb_priv *urbp;
-	struct list_head *head, *tmp;
-	struct uhci_td *td;
-	int i = 0, checked = 0, prevactive = 0;
-
-	/* Try to make sure there's enough memory */
-	if (len < 80 * 6)
-		return 0;
-
-	out += sprintf(out, "%*s[%p] link (%08x) element (%08x)\n", space, "",
-			qh, qh->link, qh->element);
-
-	if (qh->element & UHCI_PTR_QH)
-		out += sprintf(out, "%*s  Element points to QH (bug?)\n", space, "");
-
-	if (qh->element & UHCI_PTR_DEPTH)
-		out += sprintf(out, "%*s  Depth traverse\n", space, "");
-
-	if (qh->element & 8)
-		out += sprintf(out, "%*s  Bit 3 set (bug?)\n", space, "");
-
-	if (!(qh->element & ~(UHCI_PTR_QH | UHCI_PTR_DEPTH)))
-		out += sprintf(out, "%*s  Element is NULL (bug?)\n", space, "");
-
-	if (!qh->urbp) {
-		out += sprintf(out, "%*s  urbp == NULL\n", space, "");
-		goto out;
-	}
-
-	urbp = qh->urbp;
-
-	head = &urbp->td_list;
-	tmp = head->next;
-
-	td = list_entry(tmp, struct uhci_td, list);
-
-	if (td->dma_handle != (qh->element & ~UHCI_PTR_BITS))
-		out += sprintf(out, "%*s Element != First TD\n", space, "");
-
-	while (tmp != head) {
-		struct uhci_td *td = list_entry(tmp, struct uhci_td, list);
-
-		tmp = tmp->next;
-
-		out += sprintf(out, "%*s%d: ", space + 2, "", i++);
-		out += uhci_show_td(td, out, len - (out - buf), 0);
-
-		if (i > 10 && !checked && prevactive && tmp != head &&
-		    debug <= 2) {
-			struct list_head *ntmp = tmp;
-			struct uhci_td *ntd = td;
-			int active = 1, ni = i;
-
-			checked = 1;
-
-			while (ntmp != head && ntmp->next != head && active) {
-				ntd = list_entry(ntmp, struct uhci_td, list);
-
-				ntmp = ntmp->next;
-
-				active = ntd->status & TD_CTRL_ACTIVE;
-
-				ni++;
-			}
-
-			if (active && ni > i) {
-				out += sprintf(out, "%*s[skipped %d active TD's]\n", space, "", ni - i);
-				tmp = ntmp;
-				td = ntd;
-				i = ni;
-			}
-		}
-
-		prevactive = td->status & TD_CTRL_ACTIVE;
-	}
-
-	if (list_empty(&urbp->queue_list) || urbp->queued)
-		goto out;
-
-	out += sprintf(out, "%*sQueued QH's:\n", -space, "--");
-
-	head = &urbp->queue_list;
-	tmp = head->next;
-
-	while (tmp != head) {
-		struct urb_priv *nurbp = list_entry(tmp, struct urb_priv,
-						queue_list);
-		tmp = tmp->next;
-
-		out += uhci_show_qh(nurbp->qh, out, len - (out - buf), space);
-	}
-
-out:
-	return out - buf;
-}
-
-static const char *td_names[] = {"skel_int1_td", "skel_int2_td",
-				 "skel_int4_td", "skel_int8_td",
-				 "skel_int16_td", "skel_int32_td",
-				 "skel_int64_td", "skel_int128_td",
-				 "skel_int256_td", "skel_term_td" };
-static const char *qh_names[] = { "skel_ls_control_qh", "skel_hs_control_qh",
-				  "skel_bulk_qh", "skel_term_qh" };
-
-#define show_frame_num()	\
-	if (!shown) {		\
-	  shown = 1;		\
-	  out += sprintf(out, "- Frame %d\n", i); \
-	}
-
-#define show_td_name()		\
-	if (!shown) {		\
-	  shown = 1;		\
-	  out += sprintf(out, "- %s\n", td_names[i]); \
-	}
-
-#define show_qh_name()		\
-	if (!shown) {		\
-	  shown = 1;		\
-	  out += sprintf(out, "- %s\n", qh_names[i]); \
-	}
-
-static int uhci_sprint_schedule(struct uhci *uhci, char *buf, int len)
-{
-	char *out = buf;
-	int i;
-	struct uhci_qh *qh;
-	struct uhci_td *td;
-	struct list_head *tmp, *head;
-
-	out += sprintf(out, "HC status\n");
-	out += uhci_show_status(uhci, out, len - (out - buf));
-
-	out += sprintf(out, "Frame List\n");
-	for (i = 0; i < UHCI_NUMFRAMES; ++i) {
-		int shown = 0;
-		td = uhci->fl->frame_cpu[i];
-		if (!td)
-			continue;
-
-		if (td->dma_handle != (dma_addr_t)uhci->fl->frame[i]) {
-			show_frame_num();
-			out += sprintf(out, "    frame list does not match td->dma_handle!\n");
-		}
-		if (uhci_is_skeleton_td(uhci, td))
-			continue;
-		show_frame_num();
-
-		head = &td->fl_list;
-		tmp = head;
-		do {
-			td = list_entry(tmp, struct uhci_td, fl_list);
-			tmp = tmp->next;
-			out += uhci_show_td(td, out, len - (out - buf), 4);
-		} while (tmp != head);
-	}
-
-	out += sprintf(out, "Skeleton TD's\n");
-	for (i = UHCI_NUM_SKELTD - 1; i >= 0; i--) {
-		int shown = 0;
-
-		td = uhci->skeltd[i];
-
-		if (debug > 1) {
-			show_td_name();
-			out += uhci_show_td(td, out, len - (out - buf), 4);
-		}
-
-		if (list_empty(&td->fl_list)) {
-			/* TD 0 is the int1 TD and links to control_ls_qh */
-			if (!i) {
-				if (td->link !=
-				    (uhci->skel_ls_control_qh->dma_handle | UHCI_PTR_QH)) {
-					show_td_name();
-					out += sprintf(out, "    skeleton TD not linked to ls_control QH!\n");
-				}
-			} else if (i < 9) {
-				if (td->link != uhci->skeltd[i - 1]->dma_handle) {
-					show_td_name();
-					out += sprintf(out, "    skeleton TD not linked to next skeleton TD!\n");
-				}
-			} else {
-				show_td_name();
-
-				if (td->link != td->dma_handle)
-					out += sprintf(out, "    skel_term_td does not link to self\n");
-
-				/* Don't show it twice */
-				if (debug <= 1)
-					out += uhci_show_td(td, out, len - (out - buf), 4);
-			}
-
-			continue;
-		}
-
-		show_td_name();
-
-		head = &td->fl_list;
-		tmp = head->next;
-
-		while (tmp != head) {
-			td = list_entry(tmp, struct uhci_td, fl_list);
-
-			tmp = tmp->next;
-
-			out += uhci_show_td(td, out, len - (out - buf), 4);
-		}
-
-		if (!i) {
-			if (td->link !=
-			    (uhci->skel_ls_control_qh->dma_handle | UHCI_PTR_QH))
-				out += sprintf(out, "    last TD not linked to ls_control QH!\n");
-		} else if (i < 9) {
-			if (td->link != uhci->skeltd[i - 1]->dma_handle)
-				out += sprintf(out, "    last TD not linked to next skeleton!\n");
-		}
-	}
-
-	out += sprintf(out, "Skeleton QH's\n");
-
-	for (i = 0; i < UHCI_NUM_SKELQH; ++i) {
-		int shown = 0;
-
-		qh = uhci->skelqh[i];
-
-		if (debug > 1) {
-			show_qh_name();
-			out += uhci_show_qh(qh, out, len - (out - buf), 4);
-		}
-
-		/* QH 3 is the Terminating QH, it's different */
-		if (i == 3) {
-			if (qh->link != UHCI_PTR_TERM) {
-				show_qh_name();
-				out += sprintf(out, "    bandwidth reclamation on!\n");
-			}
-
-			if (qh->element != uhci->skel_term_td->dma_handle) {
-				show_qh_name();
-				out += sprintf(out, "    skel_term_qh element is not set to skel_term_td\n");
-			}
-		}
-
-		if (list_empty(&qh->list)) {
-			if (i < 3) {
-				if (qh->link !=
-				    (uhci->skelqh[i + 1]->dma_handle | UHCI_PTR_QH)) {
-					show_qh_name();
-					out += sprintf(out, "    skeleton QH not linked to next skeleton QH!\n");
-				}
-			}
-
-			continue;
-		}
-
-		show_qh_name();
-
-		head = &qh->list;
-		tmp = head->next;
-
-		while (tmp != head) {
-			qh = list_entry(tmp, struct uhci_qh, list);
-
-			tmp = tmp->next;
-
-			out += uhci_show_qh(qh, out, len - (out - buf), 4);
-		}
-
-		if (i < 3) {
-			if (qh->link !=
-			    (uhci->skelqh[i + 1]->dma_handle | UHCI_PTR_QH))
-				out += sprintf(out, "    last QH not linked to next skeleton!\n");
-		}
-	}
-
-	return out - buf;
-}
-
-#ifdef CONFIG_PROC_FS
-#define MAX_OUTPUT	(PAGE_SIZE * 8)
-
-static struct proc_dir_entry *uhci_proc_root = NULL;
-
-struct uhci_proc {
-	int size;
-	char *data;
-	struct uhci *uhci;
-};
-
-static int uhci_proc_open(struct inode *inode, struct file *file)
-{
-	const struct proc_dir_entry *dp = inode->u.generic_ip;
-	struct uhci *uhci = dp->data;
-	struct uhci_proc *up;
-	unsigned long flags;
-	int ret = -ENOMEM;
-
-	lock_kernel();
-	up = kmalloc(sizeof(*up), GFP_KERNEL);
-	if (!up)
-		goto out;
-
-	up->data = kmalloc(MAX_OUTPUT, GFP_KERNEL);
-	if (!up->data) {
-		kfree(up);
-		goto out;
-	}
-
-	spin_lock_irqsave(&uhci->frame_list_lock, flags);
-	up->size = uhci_sprint_schedule(uhci, up->data, MAX_OUTPUT);
-	spin_unlock_irqrestore(&uhci->frame_list_lock, flags);
-
-	file->private_data = up;
-
-	ret = 0;
-out:
-	unlock_kernel();
-	return ret;
-}
-
-static loff_t uhci_proc_lseek(struct file *file, loff_t off, int whence)
-{
-	struct uhci_proc *up = file->private_data;
-	loff_t new;
-
-	switch (whence) {
-	case 0:
-		new = off;
-		break;
-	case 1:
-		new = file->f_pos + off;
-		break;
-	case 2:
-	default:
-		return -EINVAL;
-	}
-	if (new < 0 || new > up->size)
-		return -EINVAL;
-	return (file->f_pos = new);
-}
-
-static ssize_t uhci_proc_read(struct file *file, char *buf, size_t nbytes,
-			loff_t *ppos)
-{
-	struct uhci_proc *up = file->private_data;
-	unsigned int pos;
-	unsigned int size;
-
-	pos = *ppos;
-	size = up->size;
-	if (pos >= size)
-		return 0;
-	if (nbytes >= size)
-		nbytes = size;
-	if (pos + nbytes > size)
-		nbytes = size - pos;
-
-	if (!access_ok(VERIFY_WRITE, buf, nbytes))
-		return -EINVAL;
-
-	copy_to_user(buf, up->data + pos, nbytes);
-
-	*ppos += nbytes;
-
-	return nbytes;
-}
-
-static int uhci_proc_release(struct inode *inode, struct file *file)
-{
-	struct uhci_proc *up = file->private_data;
-
-	kfree(up->data);
-	kfree(up);
-
-	return 0;
-}
-
-static struct file_operations uhci_proc_operations = {
-	open:		uhci_proc_open,
-	llseek:		uhci_proc_lseek,
-	read:		uhci_proc_read,
-//	write:		uhci_proc_write,
-	release:	uhci_proc_release,
-};
-#endif
-
diff -urN linux-2.4.21-pre5/drivers/usb/uhci.c linux-2.4.21-pre6/drivers/usb/uhci.c
--- linux-2.4.21-pre5/drivers/usb/uhci.c	Wed Mar 26 16:11:29 2003
+++ linux-2.4.21-pre6/drivers/usb/uhci.c	Wed Dec 31 16:00:00 1969
@@ -1,3172 +0,0 @@
-/*
- * Universal Host Controller Interface driver for USB.
- *
- * Maintainer: Johannes Erdfelt <johannes@erdfelt.com>
- *
- * (C) Copyright 1999 Linus Torvalds
- * (C) Copyright 1999-2002 Johannes Erdfelt, johannes@erdfelt.com
- * (C) Copyright 1999 Randy Dunlap
- * (C) Copyright 1999 Georg Acher, acher@in.tum.de
- * (C) Copyright 1999 Deti Fliegl, deti@fliegl.de
- * (C) Copyright 1999 Thomas Sailer, sailer@ife.ee.ethz.ch
- * (C) Copyright 1999 Roman Weissgaerber, weissg@vienna.at
- * (C) Copyright 2000 Yggdrasil Computing, Inc. (port of new PCI interface
- *               support from usb-ohci.c by Adam Richter, adam@yggdrasil.com).
- * (C) Copyright 1999 Gregory P. Smith (from usb-ohci.c)
- *
- * Intel documents this fairly well, and as far as I know there
- * are no royalties or anything like that, but even so there are
- * people who decided that they want to do the same thing in a
- * completely different way.
- *
- * WARNING! The USB documentation is downright evil. Most of it
- * is just crap, written by a committee. You're better off ignoring
- * most of it, the important stuff is:
- *  - the low-level protocol (fairly simple but lots of small details)
- *  - working around the horridness of the rest
- */
-
-#include <linux/config.h>
-#include <linux/module.h>
-#include <linux/pci.h>
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/delay.h>
-#include <linux/ioport.h>
-#include <linux/sched.h>
-#include <linux/slab.h>
-#include <linux/smp_lock.h>
-#include <linux/errno.h>
-#include <linux/unistd.h>
-#include <linux/interrupt.h>
-#include <linux/spinlock.h>
-#include <linux/proc_fs.h>
-#ifdef CONFIG_USB_DEBUG
-#define DEBUG
-#else
-#undef DEBUG
-#endif
-#include <linux/usb.h>
-
-#include <asm/uaccess.h>
-#include <asm/io.h>
-#include <asm/irq.h>
-#include <asm/system.h>
-
-#include "uhci.h"
-
-#include <linux/pm.h>
-
-#include "hcd.h"
-
-/*
- * Version Information
- */
-#define DRIVER_VERSION "v1.1"
-#define DRIVER_AUTHOR "Linus 'Frodo Rabbit' Torvalds, Johannes Erdfelt, Randy Dunlap, Georg Acher, Deti Fliegl, Thomas Sailer, Roman Weissgaerber"
-#define DRIVER_DESC "USB Universal Host Controller Interface driver"
-
-/*
- * debug = 0, no debugging messages
- * debug = 1, dump failed URB's except for stalls
- * debug = 2, dump all failed URB's (including stalls)
- *            show all queues in /proc/uhci/hc*
- * debug = 3, show all TD's in URB's when dumping
- */
-#ifdef DEBUG
-static int debug = 1;
-#else
-static int debug = 0;
-#endif
-MODULE_PARM(debug, "i");
-MODULE_PARM_DESC(debug, "Debug level");
-static char *errbuf;
-#define ERRBUF_LEN    (PAGE_SIZE * 8)
-
-#include "uhci-debug.h"
-
-static kmem_cache_t *uhci_up_cachep;	/* urb_priv */
-
-static int rh_submit_urb(struct urb *urb);
-static int rh_unlink_urb(struct urb *urb);
-static int uhci_get_current_frame_number(struct usb_device *dev);
-static int uhci_unlink_urb(struct urb *urb);
-static void uhci_unlink_generic(struct uhci *uhci, struct urb *urb);
-static void uhci_call_completion(struct urb *urb);
-
-static int  ports_active(struct uhci *uhci);
-static void suspend_hc(struct uhci *uhci);
-static void wakeup_hc(struct uhci *uhci);
-
-/* If a transfer is still active after this much time, turn off FSBR */
-#define IDLE_TIMEOUT	(HZ / 20)	/* 50 ms */
-#define FSBR_DELAY	(HZ / 20)	/* 50 ms */
-
-/* When we timeout an idle transfer for FSBR, we'll switch it over to */
-/* depth first traversal. We'll do it in groups of this number of TD's */
-/* to make sure it doesn't hog all of the bandwidth */
-#define DEPTH_INTERVAL	5
-
-#define MAX_URB_LOOP	2048		/* Maximum number of linked URB's */
-
-/*
- * Only the USB core should call uhci_alloc_dev and uhci_free_dev
- */
-static int uhci_alloc_dev(struct usb_device *dev)
-{
-	return 0;
-}
-
-static int uhci_free_dev(struct usb_device *dev)
-{
-	return 0;
-}
-
-/*
- * Technically, updating td->status here is a race, but it's not really a
- * problem. The worst that can happen is that we set the IOC bit again
- * generating a spurios interrupt. We could fix this by creating another
- * QH and leaving the IOC bit always set, but then we would have to play
- * games with the FSBR code to make sure we get the correct order in all
- * the cases. I don't think it's worth the effort
- */
-static inline void uhci_set_next_interrupt(struct uhci *uhci)
-{
-	unsigned long flags;
-
-	spin_lock_irqsave(&uhci->frame_list_lock, flags);
-	uhci->skel_term_td->status |= TD_CTRL_IOC;
-	spin_unlock_irqrestore(&uhci->frame_list_lock, flags);
-}
-
-static inline void uhci_clear_next_interrupt(struct uhci *uhci)
-{
-	unsigned long flags;
-
-	spin_lock_irqsave(&uhci->frame_list_lock, flags);
-	uhci->skel_term_td->status &= ~TD_CTRL_IOC;
-	spin_unlock_irqrestore(&uhci->frame_list_lock, flags);
-}
-
-static inline void uhci_add_complete(struct urb *urb)
-{
-	struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv;
-	struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
-	unsigned long flags;
-
-	spin_lock_irqsave(&uhci->complete_list_lock, flags);
-	list_add(&urbp->complete_list, &uhci->complete_list);
-	spin_unlock_irqrestore(&uhci->complete_list_lock, flags);
-}
-
-static struct uhci_td *uhci_alloc_td(struct uhci *uhci, struct usb_device *dev)
-{
-	dma_addr_t dma_handle;
-	struct uhci_td *td;
-
-	td = pci_pool_alloc(uhci->td_pool, GFP_DMA | GFP_ATOMIC, &dma_handle);
-	if (!td)
-		return NULL;
-
-	td->dma_handle = dma_handle;
-
-	td->link = UHCI_PTR_TERM;
-	td->buffer = 0;
-
-	td->frame = -1;
-	td->dev = dev;
-
-	INIT_LIST_HEAD(&td->list);
-	INIT_LIST_HEAD(&td->fl_list);
-
-	usb_inc_dev_use(dev);
-
-	return td;
-}
-
-static void inline uhci_fill_td(struct uhci_td *td, __u32 status,
-		__u32 info, __u32 buffer)
-{
-	td->status = status;
-	td->info = info;
-	td->buffer = buffer;
-}
-
-static void uhci_insert_td(struct uhci *uhci, struct uhci_td *skeltd, struct uhci_td *td)
-{
-	unsigned long flags;
-	struct uhci_td *ltd;
-
-	spin_lock_irqsave(&uhci->frame_list_lock, flags);
-
-	ltd = list_entry(skeltd->fl_list.prev, struct uhci_td, fl_list);
-
-	td->link = ltd->link;
-	mb();
-	ltd->link = td->dma_handle;
-
-	list_add_tail(&td->fl_list, &skeltd->fl_list);
-
-	spin_unlock_irqrestore(&uhci->frame_list_lock, flags);
-}
-
-/*
- * We insert Isochronous transfers directly into the frame list at the
- * beginning
- * The layout looks as follows:
- * frame list pointer -> iso td's (if any) ->
- * periodic interrupt td (if frame 0) -> irq td's -> control qh -> bulk qh
- */
-static void uhci_insert_td_frame_list(struct uhci *uhci, struct uhci_td *td, unsigned framenum)
-{
-	unsigned long flags;
-
-	framenum %= UHCI_NUMFRAMES;
-
-	spin_lock_irqsave(&uhci->frame_list_lock, flags);
-
-	td->frame = framenum;
-
-	/* Is there a TD already mapped there? */
-	if (uhci->fl->frame_cpu[framenum]) {
-		struct uhci_td *ftd, *ltd;
-
-		ftd = uhci->fl->frame_cpu[framenum];
-		ltd = list_entry(ftd->fl_list.prev, struct uhci_td, fl_list);
-
-		list_add_tail(&td->fl_list, &ftd->fl_list);
-
-		td->link = ltd->link;
-		mb();
-		ltd->link = td->dma_handle;
-	} else {
-		td->link = uhci->fl->frame[framenum];
-		mb();
-		uhci->fl->frame[framenum] = td->dma_handle;
-		uhci->fl->frame_cpu[framenum] = td;
-	}
-
-	spin_unlock_irqrestore(&uhci->frame_list_lock, flags);
-}
-
-static void uhci_remove_td(struct uhci *uhci, struct uhci_td *td)
-{
-	unsigned long flags;
-
-	/* If it's not inserted, don't remove it */
-	spin_lock_irqsave(&uhci->frame_list_lock, flags);
-	if (td->frame == -1 && list_empty(&td->fl_list))
-		goto out;
-
-	if (td->frame != -1 && uhci->fl->frame_cpu[td->frame] == td) {
-		if (list_empty(&td->fl_list)) {
-			uhci->fl->frame[td->frame] = td->link;
-			uhci->fl->frame_cpu[td->frame] = NULL;
-		} else {
-			struct uhci_td *ntd;
-
-			ntd = list_entry(td->fl_list.next, struct uhci_td, fl_list);
-			uhci->fl->frame[td->frame] = ntd->dma_handle;
-			uhci->fl->frame_cpu[td->frame] = ntd;
-		}
-	} else {
-		struct uhci_td *ptd;
-
-		ptd = list_entry(td->fl_list.prev, struct uhci_td, fl_list);
-		ptd->link = td->link;
-	}
-
-	mb();
-	td->link = UHCI_PTR_TERM;
-
-	list_del_init(&td->fl_list);
-	td->frame = -1;
-
-out:
-	spin_unlock_irqrestore(&uhci->frame_list_lock, flags);
-}
-
-/*
- * Inserts a td into qh list at the top.
- */
-static void uhci_insert_tds_in_qh(struct uhci_qh *qh, struct urb *urb, int breadth)
-{
-	struct list_head *tmp, *head;
-	struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
-	struct uhci_td *td, *ptd;
-
-	if (list_empty(&urbp->td_list))
-		return;
-
-	head = &urbp->td_list;
-	tmp = head->next;
-
-	/* Ordering isn't important here yet since the QH hasn't been */
-	/*  inserted into the schedule yet */
-	td = list_entry(tmp, struct uhci_td, list);
-
-	/* Add the first TD to the QH element pointer */
-	qh->element = td->dma_handle | (breadth ? 0 : UHCI_PTR_DEPTH);
-
-	ptd = td;
-
-	/* Then link the rest of the TD's */
-	tmp = tmp->next;
-	while (tmp != head) {
-		td = list_entry(tmp, struct uhci_td, list);
-
-		tmp = tmp->next;
-
-		ptd->link = td->dma_handle | (breadth ? 0 : UHCI_PTR_DEPTH);
-
-		ptd = td;
-	}
-
-	ptd->link = UHCI_PTR_TERM;
-}
-
-static void uhci_free_td(struct uhci *uhci, struct uhci_td *td)
-{
-	if (!list_empty(&td->list) || !list_empty(&td->fl_list))
-		dbg("td is still in URB list!");
-
-	if (td->dev)
-		usb_dec_dev_use(td->dev);
-
-	pci_pool_free(uhci->td_pool, td, td->dma_handle);
-}
-
-static struct uhci_qh *uhci_alloc_qh(struct uhci *uhci, struct usb_device *dev)
-{
-	dma_addr_t dma_handle;
-	struct uhci_qh *qh;
-
-	qh = pci_pool_alloc(uhci->qh_pool, GFP_DMA | GFP_ATOMIC, &dma_handle);
-	if (!qh)
-		return NULL;
-
-	qh->dma_handle = dma_handle;
-
-	qh->element = UHCI_PTR_TERM;
-	qh->link = UHCI_PTR_TERM;
-
-	qh->dev = dev;
-	qh->urbp = NULL;
-
-	INIT_LIST_HEAD(&qh->list);
-	INIT_LIST_HEAD(&qh->remove_list);
-
-	usb_inc_dev_use(dev);
-
-	return qh;
-}
-
-static void uhci_free_qh(struct uhci *uhci, struct uhci_qh *qh)
-{
-	if (!list_empty(&qh->list))
-		dbg("qh list not empty!");
-	if (!list_empty(&qh->remove_list))
-		dbg("qh still in remove_list!");
-
-	if (qh->dev)
-		usb_dec_dev_use(qh->dev);
-
-	pci_pool_free(uhci->qh_pool, qh, qh->dma_handle);
-}
-
-/*
- * MUST be called with uhci->frame_list_lock acquired
- */
-static void _uhci_insert_qh(struct uhci *uhci, struct uhci_qh *skelqh, struct urb *urb)
-{
-	struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
-	struct list_head *head, *tmp;
-	struct uhci_qh *lqh;
-
-	/* Grab the last QH */
-	lqh = list_entry(skelqh->list.prev, struct uhci_qh, list);
-
-	if (lqh->urbp) {
-		head = &lqh->urbp->queue_list;
-		tmp = head->next;
-		while (head != tmp) {
-			struct urb_priv *turbp =
-				list_entry(tmp, struct urb_priv, queue_list);
-
-			tmp = tmp->next;
-
-			turbp->qh->link = urbp->qh->dma_handle | UHCI_PTR_QH;
-		}
-	}
-
-	head = &urbp->queue_list;
-	tmp = head->next;
-	while (head != tmp) {
-		struct urb_priv *turbp =
-			list_entry(tmp, struct urb_priv, queue_list);
-
-		tmp = tmp->next;
-
-		turbp->qh->link = lqh->link;
-	}
-
-	urbp->qh->link = lqh->link;
-	mb();				/* Ordering is important */
-	lqh->link = urbp->qh->dma_handle | UHCI_PTR_QH;
-
-	list_add_tail(&urbp->qh->list, &skelqh->list);
-}
-
-static void uhci_insert_qh(struct uhci *uhci, struct uhci_qh *skelqh, struct urb *urb)
-{
-	unsigned long flags;
-
-	spin_lock_irqsave(&uhci->frame_list_lock, flags);
-	_uhci_insert_qh(uhci, skelqh, urb);
-	spin_unlock_irqrestore(&uhci->frame_list_lock, flags);
-}
-
-static void uhci_remove_qh(struct uhci *uhci, struct uhci_qh *qh)
-{
-	unsigned long flags;
-	struct uhci_qh *pqh;
-
-	if (!qh)
-		return;
-
-	qh->urbp = NULL;
-
-	/* Only go through the hoops if it's actually linked in */
-	spin_lock_irqsave(&uhci->frame_list_lock, flags);
-	if (!list_empty(&qh->list)) {
-		pqh = list_entry(qh->list.prev, struct uhci_qh, list);
-
-		if (pqh->urbp) {
-			struct list_head *head, *tmp;
-
-			head = &pqh->urbp->queue_list;
-			tmp = head->next;
-			while (head != tmp) {
-				struct urb_priv *turbp =
-					list_entry(tmp, struct urb_priv, queue_list);
-
-				tmp = tmp->next;
-
-				turbp->qh->link = qh->link;
-			}
-		}
-
-		pqh->link = qh->link;
-		mb();
-		qh->element = qh->link = UHCI_PTR_TERM;
-
-		list_del_init(&qh->list);
-	}
-	spin_unlock_irqrestore(&uhci->frame_list_lock, flags);
-
-	spin_lock_irqsave(&uhci->qh_remove_list_lock, flags);
-
-	/* Check to see if the remove list is empty. Set the IOC bit */
-	/* to force an interrupt so we can remove the QH */
-	if (list_empty(&uhci->qh_remove_list))
-		uhci_set_next_interrupt(uhci);
-
-	list_add(&qh->remove_list, &uhci->qh_remove_list);
-
-	spin_unlock_irqrestore(&uhci->qh_remove_list_lock, flags);
-}
-
-static int uhci_fixup_toggle(struct urb *urb, unsigned int toggle)
-{
-	struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
-	struct list_head *head, *tmp;
-
-	head = &urbp->td_list;
-	tmp = head->next;
-	while (head != tmp) {
-		struct uhci_td *td = list_entry(tmp, struct uhci_td, list);
-
-		tmp = tmp->next;
-
-		if (toggle)
-			td->info |= TD_TOKEN_TOGGLE;
-		else
-			td->info &= ~TD_TOKEN_TOGGLE;
-
-		toggle ^= 1;
-	}
-
-	return toggle;
-}
-
-/* This function will append one URB's QH to another URB's QH. This is for */
-/*  USB_QUEUE_BULK support for bulk transfers and soon implicitily for */
-/*  control transfers */
-static void uhci_append_queued_urb(struct uhci *uhci, struct urb *eurb, struct urb *urb)
-{
-	struct urb_priv *eurbp, *urbp, *furbp, *lurbp;
-	struct list_head *tmp;
-	struct uhci_td *lltd;
-	unsigned long flags;
-
-	eurbp = eurb->hcpriv;
-	urbp = urb->hcpriv;
-
-	spin_lock_irqsave(&uhci->frame_list_lock, flags);
-
-	/* Find the first URB in the queue */
-	if (eurbp->queued) {
-		struct list_head *head = &eurbp->queue_list;
-
-		tmp = head->next;
-		while (tmp != head) {
-			struct urb_priv *turbp =
-				list_entry(tmp, struct urb_priv, queue_list);
-
-			if (!turbp->queued)
-				break;
-
-			tmp = tmp->next;
-		}
-	} else
-		tmp = &eurbp->queue_list;
-
-	furbp = list_entry(tmp, struct urb_priv, queue_list);
-	lurbp = list_entry(furbp->queue_list.prev, struct urb_priv, queue_list);
-
-	lltd = list_entry(lurbp->td_list.prev, struct uhci_td, list);
-
-	usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe),
-		uhci_fixup_toggle(urb, uhci_toggle(lltd->info) ^ 1));
-
-	/* All qh's in the queue need to link to the next queue */
-	urbp->qh->link = eurbp->qh->link;
-
-	mb();			/* Make sure we flush everything */
-	/* Only support bulk right now, so no depth */
-	lltd->link = urbp->qh->dma_handle | UHCI_PTR_QH;
-
-	list_add_tail(&urbp->queue_list, &furbp->queue_list);
-
-	urbp->queued = 1;
-
-	spin_unlock_irqrestore(&uhci->frame_list_lock, flags);
-}
-
-static void uhci_delete_queued_urb(struct uhci *uhci, struct urb *urb)
-{
-	struct urb_priv *urbp, *nurbp;
-	struct list_head *head, *tmp;
-	struct urb_priv *purbp;
-	struct uhci_td *pltd;
-	unsigned int toggle;
-	unsigned long flags;
-
-	urbp = urb->hcpriv;
-
-	spin_lock_irqsave(&uhci->frame_list_lock, flags);
-
-	if (list_empty(&urbp->queue_list))
-		goto out;
-
-	nurbp = list_entry(urbp->queue_list.next, struct urb_priv, queue_list);
-
-	/* Fix up the toggle for the next URB's */
-	if (!urbp->queued)
-		/* We set the toggle when we unlink */
-		toggle = usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe));
-	else {
-		/* If we're in the middle of the queue, grab the toggle */
-		/*  from the TD previous to us */
-		purbp = list_entry(urbp->queue_list.prev, struct urb_priv,
-				queue_list);
-
-		pltd = list_entry(purbp->td_list.prev, struct uhci_td, list);
-
-		toggle = uhci_toggle(pltd->info) ^ 1;
-	}
-
-	head = &urbp->queue_list;
-	tmp = head->next;
-	while (head != tmp) {
-		struct urb_priv *turbp;
-
-		turbp = list_entry(tmp, struct urb_priv, queue_list);
-
-		tmp = tmp->next;
-
-		if (!turbp->queued)
-			break;
-
-		toggle = uhci_fixup_toggle(turbp->urb, toggle);
-	}
-
-	usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe),
-		usb_pipeout(urb->pipe), toggle);
-
-	if (!urbp->queued) {
-		nurbp->queued = 0;
-
-		_uhci_insert_qh(uhci, uhci->skel_bulk_qh, nurbp->urb);
-	} else {
-		/* We're somewhere in the middle (or end). A bit trickier */
-		/*  than the head scenario */
-		purbp = list_entry(urbp->queue_list.prev, struct urb_priv,
-				queue_list);
-
-		pltd = list_entry(purbp->td_list.prev, struct uhci_td, list);
-		if (nurbp->queued)
-			pltd->link = nurbp->qh->dma_handle | UHCI_PTR_QH;
-		else
-			/* The next URB happens to be the beginning, so */
-			/*  we're the last, end the chain */
-			pltd->link = UHCI_PTR_TERM;
-	}
-
-	list_del_init(&urbp->queue_list);
-
-out:
-	spin_unlock_irqrestore(&uhci->frame_list_lock, flags);
-}
-
-static struct urb_priv *uhci_alloc_urb_priv(struct uhci *uhci, struct urb *urb)
-{
-	struct urb_priv *urbp;
-
-	urbp = kmem_cache_alloc(uhci_up_cachep, SLAB_ATOMIC);
-	if (!urbp) {
-		err("uhci_alloc_urb_priv: couldn't allocate memory for urb_priv\n");
-		return NULL;
-	}
-
-	memset((void *)urbp, 0, sizeof(*urbp));
-
-	urbp->inserttime = jiffies;
-	urbp->fsbrtime = jiffies;
-	urbp->urb = urb;
-	urbp->dev = urb->dev;
-	
-	INIT_LIST_HEAD(&urbp->td_list);
-	INIT_LIST_HEAD(&urbp->queue_list);
-	INIT_LIST_HEAD(&urbp->complete_list);
-
-	urb->hcpriv = urbp;
-
-	if (urb->dev != uhci->rh.dev) {
-		if (urb->transfer_buffer_length) {
-			urbp->transfer_buffer_dma_handle = pci_map_single(uhci->dev,
-				urb->transfer_buffer, urb->transfer_buffer_length,
-				usb_pipein(urb->pipe) ? PCI_DMA_FROMDEVICE :
-				PCI_DMA_TODEVICE);
-			if (!urbp->transfer_buffer_dma_handle)
-				return NULL;
-		}
-
-		if (usb_pipetype(urb->pipe) == PIPE_CONTROL && urb->setup_packet) {
-			urbp->setup_packet_dma_handle = pci_map_single(uhci->dev,
-				urb->setup_packet, sizeof(struct usb_ctrlrequest),
-				PCI_DMA_TODEVICE);
-			if (!urbp->setup_packet_dma_handle)
-				return NULL;
-		}
-	}
-
-	return urbp;
-}
-
-/*
- * MUST be called with urb->lock acquired
- */
-static void uhci_add_td_to_urb(struct urb *urb, struct uhci_td *td)
-{
-	struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
-
-	td->urb = urb;
-
-	list_add_tail(&td->list, &urbp->td_list);
-}
-
-/*
- * MUST be called with urb->lock acquired
- */
-static void uhci_remove_td_from_urb(struct uhci_td *td)
-{
-	if (list_empty(&td->list))
-		return;
-
-	list_del_init(&td->list);
-
-	td->urb = NULL;
-}
-
-/*
- * MUST be called with urb->lock acquired
- */
-static void uhci_destroy_urb_priv(struct urb *urb)
-{
-	struct list_head *head, *tmp;
-	struct urb_priv *urbp;
-	struct uhci *uhci;
-
-	urbp = (struct urb_priv *)urb->hcpriv;
-	if (!urbp)
-		return;
-
-	if (!urbp->dev || !urbp->dev->bus || !urbp->dev->bus->hcpriv) {
-		warn("uhci_destroy_urb_priv: urb %p belongs to disconnected device or bus?", urb);
-		return;
-	}
-
-	if (!list_empty(&urb->urb_list))
-		warn("uhci_destroy_urb_priv: urb %p still on uhci->urb_list or uhci->remove_list", urb);
-
-	if (!list_empty(&urbp->complete_list))
-		warn("uhci_destroy_urb_priv: urb %p still on uhci->complete_list", urb);
-
-	uhci = urbp->dev->bus->hcpriv;
-
-	head = &urbp->td_list;
-	tmp = head->next;
-	while (tmp != head) {
-		struct uhci_td *td = list_entry(tmp, struct uhci_td, list);
-
-		tmp = tmp->next;
-
-		uhci_remove_td_from_urb(td);
-		uhci_remove_td(uhci, td);
-		uhci_free_td(uhci, td);
-	}
-
-	if (urbp->setup_packet_dma_handle) {
-		pci_unmap_single(uhci->dev, urbp->setup_packet_dma_handle,
-			sizeof(struct usb_ctrlrequest), PCI_DMA_TODEVICE);
-		urbp->setup_packet_dma_handle = 0;
-	}
-
-	if (urbp->transfer_buffer_dma_handle) {
-		pci_unmap_single(uhci->dev, urbp->transfer_buffer_dma_handle,
-			urb->transfer_buffer_length, usb_pipein(urb->pipe) ?
-			PCI_DMA_FROMDEVICE : PCI_DMA_TODEVICE);
-		urbp->transfer_buffer_dma_handle = 0;
-	}
-
-	urb->hcpriv = NULL;
-	kmem_cache_free(uhci_up_cachep, urbp);
-}
-
-static void uhci_inc_fsbr(struct uhci *uhci, struct urb *urb)
-{
-	unsigned long flags;
-	struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
-
-	spin_lock_irqsave(&uhci->frame_list_lock, flags);
-
-	if ((!(urb->transfer_flags & USB_NO_FSBR)) && !urbp->fsbr) {
-		urbp->fsbr = 1;
-		if (!uhci->fsbr++ && !uhci->fsbrtimeout)
-			uhci->skel_term_qh->link = uhci->skel_hs_control_qh->dma_handle | UHCI_PTR_QH;
-	}
-
-	spin_unlock_irqrestore(&uhci->frame_list_lock, flags);
-}
-
-static void uhci_dec_fsbr(struct uhci *uhci, struct urb *urb)
-{
-	unsigned long flags;
-	struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
-
-	spin_lock_irqsave(&uhci->frame_list_lock, flags);
-
-	if ((!(urb->transfer_flags & USB_NO_FSBR)) && urbp->fsbr) {
-		urbp->fsbr = 0;
-		if (!--uhci->fsbr)
-			uhci->fsbrtimeout = jiffies + FSBR_DELAY;
-	}
-
-	spin_unlock_irqrestore(&uhci->frame_list_lock, flags);
-}
-
-/*
- * Map status to standard result codes
- *
- * <status> is (td->status & 0xFE0000) [a.k.a. uhci_status_bits(td->status)]
- * <dir_out> is True for output TDs and False for input TDs.
- */
-static int uhci_map_status(int status, int dir_out)
-{
-	if (!status)
-		return 0;
-	if (status & TD_CTRL_BITSTUFF)			/* Bitstuff error */
-		return -EPROTO;
-	if (status & TD_CTRL_CRCTIMEO) {		/* CRC/Timeout */
-		if (dir_out)
-			return -ETIMEDOUT;
-		else
-			return -EILSEQ;
-	}
-	if (status & TD_CTRL_NAK)			/* NAK */
-		return -ETIMEDOUT;
-	if (status & TD_CTRL_BABBLE)			/* Babble */
-		return -EOVERFLOW;
-	if (status & TD_CTRL_DBUFERR)			/* Buffer error */
-		return -ENOSR;
-	if (status & TD_CTRL_STALLED)			/* Stalled */
-		return -EPIPE;
-	if (status & TD_CTRL_ACTIVE)			/* Active */
-		return 0;
-
-	return -EINVAL;
-}
-
-/*
- * Control transfers
- */
-static int uhci_submit_control(struct urb *urb)
-{
-	struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
-	struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv;
-	struct uhci_td *td;
-	struct uhci_qh *qh;
-	unsigned long destination, status;
-	int maxsze = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe));
-	int len = urb->transfer_buffer_length;
-	dma_addr_t data = urbp->transfer_buffer_dma_handle;
-
-	/* The "pipe" thing contains the destination in bits 8--18 */
-	destination = (urb->pipe & PIPE_DEVEP_MASK) | USB_PID_SETUP;
-
-	/* 3 errors */
-	status = (urb->pipe & TD_CTRL_LS) | TD_CTRL_ACTIVE | (3 << 27);
-
-	/*
-	 * Build the TD for the control request
-	 */
-	td = uhci_alloc_td(uhci, urb->dev);
-	if (!td)
-		return -ENOMEM;
-
-	uhci_add_td_to_urb(urb, td);
-	uhci_fill_td(td, status, destination | (7 << 21),
-		urbp->setup_packet_dma_handle);
-
-	/*
-	 * If direction is "send", change the frame from SETUP (0x2D)
-	 * to OUT (0xE1). Else change it from SETUP to IN (0x69).
-	 */
-	destination ^= (USB_PID_SETUP ^ usb_packetid(urb->pipe));
-
-	if (!(urb->transfer_flags & USB_DISABLE_SPD))
-		status |= TD_CTRL_SPD;
-
-	/*
-	 * Build the DATA TD's
-	 */
-	while (len > 0) {
-		int pktsze = len;
-
-		if (pktsze > maxsze)
-			pktsze = maxsze;
-
-		td = uhci_alloc_td(uhci, urb->dev);
-		if (!td)
-			return -ENOMEM;
-
-		/* Alternate Data0/1 (start with Data1) */
-		destination ^= TD_TOKEN_TOGGLE;
-	
-		uhci_add_td_to_urb(urb, td);
-		uhci_fill_td(td, status, destination | ((pktsze - 1) << 21),
-			data);
-
-		data += pktsze;
-		len -= pktsze;
-	}
-
-	/*
-	 * Build the final TD for control status 
-	 */
-	td = uhci_alloc_td(uhci, urb->dev);
-	if (!td)
-		return -ENOMEM;
-
-	/*
-	 * It's IN if the pipe is an output pipe or we're not expecting
-	 * data back.
-	 */
-	destination &= ~TD_TOKEN_PID_MASK;
-	if (usb_pipeout(urb->pipe) || !urb->transfer_buffer_length)
-		destination |= USB_PID_IN;
-	else
-		destination |= USB_PID_OUT;
-
-	destination |= TD_TOKEN_TOGGLE;		/* End in Data1 */
-
-	status &= ~TD_CTRL_SPD;
-
-	uhci_add_td_to_urb(urb, td);
-	uhci_fill_td(td, status | TD_CTRL_IOC,
-		destination | (UHCI_NULL_DATA_SIZE << 21), 0);
-
-	qh = uhci_alloc_qh(uhci, urb->dev);
-	if (!qh)
-		return -ENOMEM;
-
-	urbp->qh = qh;
-	qh->urbp = urbp;
-
-	/* Low speed or small transfers gets a different queue and treatment */
-	if (urb->pipe & TD_CTRL_LS) {
-		uhci_insert_tds_in_qh(qh, urb, 0);
-		uhci_insert_qh(uhci, uhci->skel_ls_control_qh, urb);
-	} else {
-		uhci_insert_tds_in_qh(qh, urb, 1);
-		uhci_insert_qh(uhci, uhci->skel_hs_control_qh, urb);
-		uhci_inc_fsbr(uhci, urb);
-	}
-
-	return -EINPROGRESS;
-}
-
-static int usb_control_retrigger_status(struct urb *urb);
-
-static int uhci_result_control(struct urb *urb)
-{
-	struct list_head *tmp, *head;
-	struct urb_priv *urbp = urb->hcpriv;
-	struct uhci_td *td;
-	unsigned int status;
-	int ret = 0;
-
-	if (list_empty(&urbp->td_list))
-		return -EINVAL;
-
-	head = &urbp->td_list;
-
-	if (urbp->short_control_packet) {
-		tmp = head->prev;
-		goto status_phase;
-	}
-
-	tmp = head->next;
-	td = list_entry(tmp, struct uhci_td, list);
-
-	/* The first TD is the SETUP phase, check the status, but skip */
-	/*  the count */
-	status = uhci_status_bits(td->status);
-	if (status & TD_CTRL_ACTIVE)
-		return -EINPROGRESS;
-
-	if (status)
-		goto td_error;
-
-	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 uhci_td, list);
-
-		tmp = tmp->next;
-
-		status = uhci_status_bits(td->status);
-		if (status & TD_CTRL_ACTIVE)
-			return -EINPROGRESS;
-
-		urb->actual_length += uhci_actual_length(td->status);
-
-		if (status)
-			goto td_error;
-
-		/* Check to see if we received a short packet */
-		if (uhci_actual_length(td->status) < uhci_expected_length(td->info)) {
-			if (urb->transfer_flags & USB_DISABLE_SPD) {
-				ret = -EREMOTEIO;
-				goto err;
-			}
-
-			if (uhci_packetid(td->info) == USB_PID_IN)
-				return usb_control_retrigger_status(urb);
-			else
-				return 0;
-		}
-	}
-
-status_phase:
-	td = list_entry(tmp, struct uhci_td, list);
-
-	/* Control status phase */
-	status = uhci_status_bits(td->status);
-
-#ifdef I_HAVE_BUGGY_APC_BACKUPS
-	/* APC BackUPS Pro kludge */
-	/* It tries to send all of the descriptor instead of the amount */
-	/*  we requested */
-	if (td->status & TD_CTRL_IOC &&	/* IOC is masked out by uhci_status_bits */
-	    status & TD_CTRL_ACTIVE &&
-	    status & TD_CTRL_NAK)
-		return 0;
-#endif
-
-	if (status & TD_CTRL_ACTIVE)
-		return -EINPROGRESS;
-
-	if (status)
-		goto td_error;
-
-	return 0;
-
-td_error:
-	ret = uhci_map_status(status, uhci_packetout(td->info));
-	if (ret == -EPIPE)
-		/* endpoint has stalled - mark it halted */
-		usb_endpoint_halt(urb->dev, uhci_endpoint(td->info),
-	    			uhci_packetout(td->info));
-
-err:
-	if ((debug == 1 && ret != -EPIPE) || debug > 1) {
-		/* Some debugging code */
-		dbg("uhci_result_control() failed with status %x", status);
-
-		if (errbuf) {
-			/* Print the chain for debugging purposes */
-			uhci_show_qh(urbp->qh, errbuf, ERRBUF_LEN, 0);
-
-			lprintk(errbuf);
-		}
-	}
-
-	return ret;
-}
-
-static int usb_control_retrigger_status(struct urb *urb)
-{
-	struct list_head *tmp, *head;
-	struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
-	struct uhci *uhci = urb->dev->bus->hcpriv;
-
-	urbp->short_control_packet = 1;
-
-	/* Create a new QH to avoid pointer overwriting problems */
-	uhci_remove_qh(uhci, urbp->qh);
-
-	/* Delete all of the TD's except for the status TD at the end */
-	head = &urbp->td_list;
-	tmp = head->next;
-	while (tmp != head && tmp->next != head) {
-		struct uhci_td *td = list_entry(tmp, struct uhci_td, list);
-
-		tmp = tmp->next;
-
-		uhci_remove_td_from_urb(td);
-		uhci_remove_td(uhci, td);
-		uhci_free_td(uhci, td);
-	}
-
-	urbp->qh = uhci_alloc_qh(uhci, urb->dev);
-	if (!urbp->qh) {
-		err("unable to allocate new QH for control retrigger");
-		return -ENOMEM;
-	}
-
-	urbp->qh->urbp = urbp;
-
-	/* One TD, who cares about Breadth first? */
-	uhci_insert_tds_in_qh(urbp->qh, urb, 0);
-
-	/* Low speed or small transfers gets a different queue and treatment */
-	if (urb->pipe & TD_CTRL_LS)
-		uhci_insert_qh(uhci, uhci->skel_ls_control_qh, urb);
-	else
-		uhci_insert_qh(uhci, uhci->skel_hs_control_qh, urb);
-
-	return -EINPROGRESS;
-}
-
-/*
- * Interrupt transfers
- */
-static int uhci_submit_interrupt(struct urb *urb)
-{
-	struct uhci_td *td;
-	unsigned long destination, status;
-	struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv;
-	struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
-
-	if (urb->transfer_buffer_length > usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe)))
-		return -EINVAL;
-
-	/* The "pipe" thing contains the destination in bits 8--18 */
-	destination = (urb->pipe & PIPE_DEVEP_MASK) | usb_packetid(urb->pipe);
-
-	status = (urb->pipe & TD_CTRL_LS) | TD_CTRL_ACTIVE | TD_CTRL_IOC;
-
-	td = uhci_alloc_td(uhci, urb->dev);
-	if (!td)
-		return -ENOMEM;
-
-	destination |= (usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe)) << TD_TOKEN_TOGGLE_SHIFT);
-	destination |= ((urb->transfer_buffer_length - 1) << 21);
-
-	usb_dotoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe));
-
-	uhci_add_td_to_urb(urb, td);
-	uhci_fill_td(td, status, destination, urbp->transfer_buffer_dma_handle);
-
-	uhci_insert_td(uhci, uhci->skeltd[__interval_to_skel(urb->interval)], td);
-
-	return -EINPROGRESS;
-}
-
-static int uhci_result_interrupt(struct urb *urb)
-{
-	struct list_head *tmp, *head;
-	struct urb_priv *urbp = urb->hcpriv;
-	struct uhci_td *td;
-	unsigned int status;
-	int ret = 0;
-
-	urb->actual_length = 0;
-
-	head = &urbp->td_list;
-	tmp = head->next;
-	while (tmp != head) {
-		td = list_entry(tmp, struct uhci_td, list);
-
-		tmp = tmp->next;
-
-		status = uhci_status_bits(td->status);
-		if (status & TD_CTRL_ACTIVE)
-			return -EINPROGRESS;
-
-		urb->actual_length += uhci_actual_length(td->status);
-
-		if (status)
-			goto td_error;
-
-		if (uhci_actual_length(td->status) < uhci_expected_length(td->info)) {
-			if (urb->transfer_flags & USB_DISABLE_SPD) {
-				ret = -EREMOTEIO;
-				goto err;
-			} else
-				return 0;
-		}
-	}
-
-	return 0;
-
-td_error:
-	ret = uhci_map_status(status, uhci_packetout(td->info));
-	if (ret == -EPIPE)
-		/* endpoint has stalled - mark it halted */
-		usb_endpoint_halt(urb->dev, uhci_endpoint(td->info),
-	    			uhci_packetout(td->info));
-
-err:
-	if ((debug == 1 && ret != -EPIPE) || debug > 1) {
-		/* Some debugging code */
-		dbg("uhci_result_interrupt/bulk() failed with status %x",
-			status);
-
-		if (errbuf) {
-			/* Print the chain for debugging purposes */
-			if (urbp->qh)
-				uhci_show_qh(urbp->qh, errbuf, ERRBUF_LEN, 0);
-			else
-				uhci_show_td(td, errbuf, ERRBUF_LEN, 0);
-
-			lprintk(errbuf);
-		}
-	}
-
-	return ret;
-}
-
-static void uhci_reset_interrupt(struct urb *urb)
-{
-	struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv;
-	struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
-	struct uhci_td *td;
-	unsigned long flags;
-
-	spin_lock_irqsave(&urb->lock, flags);
-
-	/* Root hub is special */
-	if (urb->dev == uhci->rh.dev)
-		goto out;
-
-	td = list_entry(urbp->td_list.next, struct uhci_td, list);
-
-	td->status = (td->status & 0x2F000000) | TD_CTRL_ACTIVE | TD_CTRL_IOC;
-	td->info &= ~TD_TOKEN_TOGGLE;
-	td->info |= (usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe)) << TD_TOKEN_TOGGLE_SHIFT);
-	usb_dotoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe));
-
-out:
-	urb->status = -EINPROGRESS;
-
-	spin_unlock_irqrestore(&urb->lock, flags);
-}
-
-/*
- * Bulk transfers
- */
-static int uhci_submit_bulk(struct urb *urb, struct urb *eurb)
-{
-	struct uhci_td *td;
-	struct uhci_qh *qh;
-	unsigned long destination, status;
-	struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv;
-	int maxsze = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe));
-	int len = urb->transfer_buffer_length;
-	struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
-	dma_addr_t data = urbp->transfer_buffer_dma_handle;
-
-	if (len < 0)
-		return -EINVAL;
-
-	/* Can't have low speed bulk transfers */
-	if (urb->pipe & TD_CTRL_LS)
-		return -EINVAL;
-
-	/* The "pipe" thing contains the destination in bits 8--18 */
-	destination = (urb->pipe & PIPE_DEVEP_MASK) | usb_packetid(urb->pipe);
-
-	/* 3 errors */
-	status = TD_CTRL_ACTIVE | (3 << TD_CTRL_C_ERR_SHIFT);
-
-	if (!(urb->transfer_flags & USB_DISABLE_SPD))
-		status |= TD_CTRL_SPD;
-
-	/*
-	 * Build the DATA TD's
-	 */
-	do {	/* Allow zero length packets */
-		int pktsze = len;
-
-		if (pktsze > maxsze)
-			pktsze = maxsze;
-
-		td = uhci_alloc_td(uhci, urb->dev);
-		if (!td)
-			return -ENOMEM;
-
-		uhci_add_td_to_urb(urb, td);
-		uhci_fill_td(td, status, destination |
-			(((pktsze - 1) & UHCI_NULL_DATA_SIZE) << 21) |
-			(usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe),
-			 usb_pipeout(urb->pipe)) << TD_TOKEN_TOGGLE_SHIFT),
-			data);
-
-		data += pktsze;
-		len -= maxsze;
-
-		usb_dotoggle(urb->dev, usb_pipeendpoint(urb->pipe),
-			usb_pipeout(urb->pipe));
-	} 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 = uhci_alloc_td(uhci, urb->dev);
-		if (!td)
-			return -ENOMEM;
-
-		uhci_add_td_to_urb(urb, td);
-		uhci_fill_td(td, status, destination |
-			(UHCI_NULL_DATA_SIZE << 21) |
-			(usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe),
-			 usb_pipeout(urb->pipe)) << TD_TOKEN_TOGGLE_SHIFT),
-			data);
-
-		usb_dotoggle(urb->dev, usb_pipeendpoint(urb->pipe),
-			usb_pipeout(urb->pipe));
-	}
-
-	/* Set the flag on the last packet */
-	td->status |= TD_CTRL_IOC;
-
-	qh = uhci_alloc_qh(uhci, urb->dev);
-	if (!qh)
-		return -ENOMEM;
-
-	urbp->qh = qh;
-	qh->urbp = urbp;
-
-	/* Always assume breadth first */
-	uhci_insert_tds_in_qh(qh, urb, 1);
-
-	if (urb->transfer_flags & USB_QUEUE_BULK && eurb)
-		uhci_append_queued_urb(uhci, eurb, urb);
-	else
-		uhci_insert_qh(uhci, uhci->skel_bulk_qh, urb);
-
-	uhci_inc_fsbr(uhci, urb);
-
-	return -EINPROGRESS;
-}
-
-/* We can use the result interrupt since they're identical */
-#define uhci_result_bulk uhci_result_interrupt
-
-/*
- * Isochronous transfers
- */
-static int isochronous_find_limits(struct urb *urb, unsigned int *start, unsigned int *end)
-{
-	struct urb *last_urb = NULL;
-	struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv;
-	struct list_head *tmp, *head;
-	int ret = 0;
-
-	head = &uhci->urb_list;
-	tmp = head->next;
-	while (tmp != head) {
-		struct urb *u = list_entry(tmp, struct urb, urb_list);
-
-		tmp = tmp->next;
-
-		/* look for pending URB's with identical pipe handle */
-		if ((urb->pipe == u->pipe) && (urb->dev == u->dev) &&
-		    (u->status == -EINPROGRESS) && (u != urb)) {
-			if (!last_urb)
-				*start = u->start_frame;
-			last_urb = u;
-		}
-	}
-
-	if (last_urb) {
-		*end = (last_urb->start_frame + last_urb->number_of_packets) & 1023;
-		ret = 0;
-	} else
-		ret = -1;	/* no previous urb found */
-
-	return ret;
-}
-
-static int isochronous_find_start(struct urb *urb)
-{
-	int limits;
-	unsigned int start = 0, end = 0;
-
-	if (urb->number_of_packets > 900)	/* 900? Why? */
-		return -EFBIG;
-
-	limits = isochronous_find_limits(urb, &start, &end);
-
-	if (urb->transfer_flags & USB_ISO_ASAP) {
-		if (limits) {
-			int curframe;
-
-			curframe = uhci_get_current_frame_number(urb->dev) % UHCI_NUMFRAMES;
-			urb->start_frame = (curframe + 10) % UHCI_NUMFRAMES;
-		} else
-			urb->start_frame = end;
-	} else {
-		urb->start_frame %= UHCI_NUMFRAMES;
-		/* FIXME: Sanity check */
-	}
-
-	return 0;
-}
-
-/*
- * Isochronous transfers
- */
-static int uhci_submit_isochronous(struct urb *urb)
-{
-	struct uhci_td *td;
-	struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv;
-	int i, ret, framenum;
-	int status, destination;
-	struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
-
-	status = TD_CTRL_ACTIVE | TD_CTRL_IOS;
-	destination = (urb->pipe & PIPE_DEVEP_MASK) | usb_packetid(urb->pipe);
-
-	ret = isochronous_find_start(urb);
-	if (ret)
-		return ret;
-
-	framenum = urb->start_frame;
-	for (i = 0; i < urb->number_of_packets; i++, framenum++) {
-		if (!urb->iso_frame_desc[i].length)
-			continue;
-
-		td = uhci_alloc_td(uhci, urb->dev);
-		if (!td)
-			return -ENOMEM;
-
-		uhci_add_td_to_urb(urb, td);
-		uhci_fill_td(td, status, destination | ((urb->iso_frame_desc[i].length - 1) << 21),
-			urbp->transfer_buffer_dma_handle + urb->iso_frame_desc[i].offset);
-
-		if (i + 1 >= urb->number_of_packets)
-			td->status |= TD_CTRL_IOC;
-
-		uhci_insert_td_frame_list(uhci, td, framenum);
-	}
-
-	return -EINPROGRESS;
-}
-
-static int uhci_result_isochronous(struct urb *urb)
-{
-	struct list_head *tmp, *head;
-	struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
-	int status;
-	int i, ret = 0;
-
-	urb->actual_length = 0;
-
-	i = 0;
-	head = &urbp->td_list;
-	tmp = head->next;
-	while (tmp != head) {
-		struct uhci_td *td = list_entry(tmp, struct uhci_td, list);
-		int actlength;
-
-		tmp = tmp->next;
-
-		if (td->status & TD_CTRL_ACTIVE)
-			return -EINPROGRESS;
-
-		actlength = uhci_actual_length(td->status);
-		urb->iso_frame_desc[i].actual_length = actlength;
-		urb->actual_length += actlength;
-
-		status = uhci_map_status(uhci_status_bits(td->status), usb_pipeout(urb->pipe));
-		urb->iso_frame_desc[i].status = status;
-		if (status) {
-			urb->error_count++;
-			ret = status;
-		}
-
-		i++;
-	}
-
-	return ret;
-}
-
-/*
- * MUST be called with uhci->urb_list_lock acquired
- */
-static struct urb *uhci_find_urb_ep(struct uhci *uhci, struct urb *urb)
-{
-	struct list_head *tmp, *head;
-
-	/* We don't match Isoc transfers since they are special */
-	if (usb_pipeisoc(urb->pipe))
-		return NULL;
-
-	head = &uhci->urb_list;
-	tmp = head->next;
-	while (tmp != head) {
-		struct urb *u = list_entry(tmp, struct urb, urb_list);
-
-		tmp = tmp->next;
-
-		if (u->dev == urb->dev && u->pipe == urb->pipe &&
-		    u->status == -EINPROGRESS)
-			return u;
-	}
-
-	return NULL;
-}
-
-static int uhci_submit_urb(struct urb *urb)
-{
-	int ret = -EINVAL;
-	struct uhci *uhci;
-	unsigned long flags;
-	struct urb *eurb;
-	int bustime;
-
-	if (!urb)
-		return -EINVAL;
-
-	if (!urb->dev || !urb->dev->bus || !urb->dev->bus->hcpriv) {
-		warn("uhci_submit_urb: urb %p belongs to disconnected device or bus?", urb);
-		return -ENODEV;
-	}
-
-	uhci = (struct uhci *)urb->dev->bus->hcpriv;
-
-	usb_inc_dev_use(urb->dev);
-
-	spin_lock_irqsave(&uhci->urb_list_lock, flags);
-	spin_lock(&urb->lock);
-
-	if (urb->status == -EINPROGRESS || urb->status == -ECONNRESET ||
-	    urb->status == -ECONNABORTED) {
-		dbg("uhci_submit_urb: urb not available to submit (status = %d)", urb->status);
-		/* Since we can have problems on the out path */
-		spin_unlock(&urb->lock);
-		spin_unlock_irqrestore(&uhci->urb_list_lock, flags);
-		usb_dec_dev_use(urb->dev);
-
-		return ret;
-	}
-
-	INIT_LIST_HEAD(&urb->urb_list);
-	if (!uhci_alloc_urb_priv(uhci, urb)) {
-		ret = -ENOMEM;
-
-		goto out;
-	}
-
-	eurb = uhci_find_urb_ep(uhci, urb);
-	if (eurb && !(urb->transfer_flags & USB_QUEUE_BULK)) {
-		ret = -ENXIO;
-
-		goto out;
-	}
-
-	/* Short circuit the virtual root hub */
-	if (urb->dev == uhci->rh.dev) {
-		ret = rh_submit_urb(urb);
-
-		goto out;
-	}
-
-	switch (usb_pipetype(urb->pipe)) {
-	case PIPE_CONTROL:
-		ret = uhci_submit_control(urb);
-		break;
-	case PIPE_INTERRUPT:
-		if (urb->bandwidth == 0) {	/* not yet checked/allocated */
-			bustime = usb_check_bandwidth(urb->dev, urb);
-			if (bustime < 0)
-				ret = bustime;
-			else {
-				ret = uhci_submit_interrupt(urb);
-				if (ret == -EINPROGRESS)
-					usb_claim_bandwidth(urb->dev, urb, bustime, 0);
-			}
-		} else		/* bandwidth is already set */
-			ret = uhci_submit_interrupt(urb);
-		break;
-	case PIPE_BULK:
-		ret = uhci_submit_bulk(urb, eurb);
-		break;
-	case PIPE_ISOCHRONOUS:
-		if (urb->bandwidth == 0) {	/* not yet checked/allocated */
-			if (urb->number_of_packets <= 0) {
-				ret = -EINVAL;
-				break;
-			}
-			bustime = usb_check_bandwidth(urb->dev, urb);
-			if (bustime < 0) {
-				ret = bustime;
-				break;
-			}
-
-			ret = uhci_submit_isochronous(urb);
-			if (ret == -EINPROGRESS)
-				usb_claim_bandwidth(urb->dev, urb, bustime, 1);
-		} else		/* bandwidth is already set */
-			ret = uhci_submit_isochronous(urb);
-		break;
-	}
-
-out:
-	urb->status = ret;
-
-	if (ret == -EINPROGRESS) {
-		/* We use _tail to make find_urb_ep more efficient */
-		list_add_tail(&urb->urb_list, &uhci->urb_list);
-
-		spin_unlock(&urb->lock);
-		spin_unlock_irqrestore(&uhci->urb_list_lock, flags);
-
-		return 0;
-	}
-
-	uhci_unlink_generic(uhci, urb);
-
-	spin_unlock(&urb->lock);
-	spin_unlock_irqrestore(&uhci->urb_list_lock, flags);
-
-	/* Only call completion if it was successful */
-	if (!ret)
-		uhci_call_completion(urb);
-
-	return ret;
-}
-
-/*
- * Return the result of a transfer
- *
- * MUST be called with urb_list_lock acquired
- */
-static void uhci_transfer_result(struct uhci *uhci, struct urb *urb)
-{
-	int ret = -EINVAL;
-	unsigned long flags;
-	struct urb_priv *urbp;
-
-	/* The root hub is special */
-	if (urb->dev == uhci->rh.dev)
-		return;
-
-	spin_lock_irqsave(&urb->lock, flags);
-
-	urbp = (struct urb_priv *)urb->hcpriv;
-
-	if (urb->status != -EINPROGRESS) {
-		info("uhci_transfer_result: called for URB %p not in flight?", urb);
-		goto out;
-	}
-
-	switch (usb_pipetype(urb->pipe)) {
-	case PIPE_CONTROL:
-		ret = uhci_result_control(urb);
-		break;
-	case PIPE_INTERRUPT:
-		ret = uhci_result_interrupt(urb);
-		break;
-	case PIPE_BULK:
-		ret = uhci_result_bulk(urb);
-		break;
-	case PIPE_ISOCHRONOUS:
-		ret = uhci_result_isochronous(urb);
-		break;
-	}
-
-	urbp->status = ret;
-
-	if (ret == -EINPROGRESS)
-		goto out;
-
-	switch (usb_pipetype(urb->pipe)) {
-	case PIPE_CONTROL:
-	case PIPE_BULK:
-	case PIPE_ISOCHRONOUS:
-		/* Release bandwidth for Interrupt or Isoc. transfers */
-		/* Spinlock needed ? */
-		if (urb->bandwidth)
-			usb_release_bandwidth(urb->dev, urb, 1);
-		uhci_unlink_generic(uhci, urb);
-		break;
-	case PIPE_INTERRUPT:
-		/* Interrupts are an exception */
-		if (urb->interval)
-			goto out_complete;
-
-		/* Release bandwidth for Interrupt or Isoc. transfers */
-		/* Spinlock needed ? */
-		if (urb->bandwidth)
-			usb_release_bandwidth(urb->dev, urb, 0);
-		uhci_unlink_generic(uhci, urb);
-		break;
-	default:
-		info("uhci_transfer_result: unknown pipe type %d for urb %p\n",
-			usb_pipetype(urb->pipe), urb);
-	}
-
-	/* Remove it from uhci->urb_list */
-	list_del_init(&urb->urb_list);
-
-out_complete:
-	uhci_add_complete(urb);
-
-out:
-	spin_unlock_irqrestore(&urb->lock, flags);
-}
-
-/*
- * MUST be called with urb->lock acquired
- */
-static void uhci_unlink_generic(struct uhci *uhci, struct urb *urb)
-{
-	struct list_head *head, *tmp;
-	struct urb_priv *urbp = urb->hcpriv;
-	int prevactive = 1;
-
-	/* We can get called when urbp allocation fails, so check */
-	if (!urbp)
-		return;
-
-	uhci_dec_fsbr(uhci, urb);	/* Safe since it checks */
-
-	/*
-	 * Now we need to find out what the last successful toggle was
-	 * so we can update the local data toggle for the next transfer
-	 *
-	 * There's 3 way's the last successful completed TD is found:
-	 *
-	 * 1) The TD is NOT active and the actual length < expected length
-	 * 2) The TD is NOT active and it's the last TD in the chain
-	 * 3) The TD is active and the previous TD is NOT active
-	 *
-	 * Control and Isochronous ignore the toggle, so this is safe
-	 * for all types
-	 */
-	head = &urbp->td_list;
-	tmp = head->next;
-	while (tmp != head) {
-		struct uhci_td *td = list_entry(tmp, struct uhci_td, list);
-
-		tmp = tmp->next;
-
-		if (!(td->status & TD_CTRL_ACTIVE) &&
-		    (uhci_actual_length(td->status) < uhci_expected_length(td->info) ||
-		    tmp == head))
-			usb_settoggle(urb->dev, uhci_endpoint(td->info),
-				uhci_packetout(td->info),
-				uhci_toggle(td->info) ^ 1);
-		else if ((td->status & TD_CTRL_ACTIVE) && !prevactive)
-			usb_settoggle(urb->dev, uhci_endpoint(td->info),
-				uhci_packetout(td->info),
-				uhci_toggle(td->info));
-
-		prevactive = td->status & TD_CTRL_ACTIVE;
-	}
-
-	uhci_delete_queued_urb(uhci, urb);
-
-	/* The interrupt loop will reclaim the QH's */
-	uhci_remove_qh(uhci, urbp->qh);
-	urbp->qh = NULL;
-}
-
-static int uhci_unlink_urb(struct urb *urb)
-{
-	struct uhci *uhci;
-	unsigned long flags;
-	struct urb_priv *urbp = urb->hcpriv;
-
-	if (!urb)
-		return -EINVAL;
-
-	if (!urb->dev || !urb->dev->bus || !urb->dev->bus->hcpriv)
-		return -ENODEV;
-
-	uhci = (struct uhci *)urb->dev->bus->hcpriv;
-
-	spin_lock_irqsave(&uhci->urb_list_lock, flags);
-	spin_lock(&urb->lock);
-
-	/* Release bandwidth for Interrupt or Isoc. transfers */
-	/* Spinlock needed ? */
-	if (urb->bandwidth) {
-		switch (usb_pipetype(urb->pipe)) {
-		case PIPE_INTERRUPT:
-			usb_release_bandwidth(urb->dev, urb, 0);
-			break;
-		case PIPE_ISOCHRONOUS:
-			usb_release_bandwidth(urb->dev, urb, 1);
-			break;
-		default:
-			break;
-		}
-	}
-
-	if (urb->status != -EINPROGRESS) {
-		spin_unlock(&urb->lock);
-		spin_unlock_irqrestore(&uhci->urb_list_lock, flags);
-		return 0;
-	}
-
-	list_del_init(&urb->urb_list);
-
-	uhci_unlink_generic(uhci, urb);
-
-	/* Short circuit the virtual root hub */
-	if (urb->dev == uhci->rh.dev) {
-		rh_unlink_urb(urb);
-
-		spin_unlock(&urb->lock);
-		spin_unlock_irqrestore(&uhci->urb_list_lock, flags);
-
-		uhci_call_completion(urb);
-	} else {
-		if (urb->transfer_flags & USB_ASYNC_UNLINK) {
-			urbp->status = urb->status = -ECONNABORTED;
-
-			spin_lock(&uhci->urb_remove_list_lock);
-
-			/* If we're the first, set the next interrupt bit */
-			if (list_empty(&uhci->urb_remove_list))
-				uhci_set_next_interrupt(uhci);
-			
-			list_add(&urb->urb_list, &uhci->urb_remove_list);
-
-			spin_unlock(&uhci->urb_remove_list_lock);
-
-			spin_unlock(&urb->lock);
-			spin_unlock_irqrestore(&uhci->urb_list_lock, flags);
-
-		} else {
-			urb->status = -ENOENT;
-
-			spin_unlock(&urb->lock);
-			spin_unlock_irqrestore(&uhci->urb_list_lock, flags);
-
-			if (in_interrupt()) {	/* wait at least 1 frame */
-				static int errorcount = 10;
-
-				if (errorcount--)
-					dbg("uhci_unlink_urb called from interrupt for urb %p", urb);
-				udelay(1000);
-			} else
-				schedule_timeout(1+1*HZ/1000); 
-
-			uhci_call_completion(urb);
-		}
-	}
-
-	return 0;
-}
-
-static int uhci_fsbr_timeout(struct uhci *uhci, struct urb *urb)
-{
-	struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
-	struct list_head *head, *tmp;
-	int count = 0;
-
-	uhci_dec_fsbr(uhci, urb);
-
-	urbp->fsbr_timeout = 1;
-
-	/*
-	 * Ideally we would want to fix qh->element as well, but it's
-	 * read/write by the HC, so that can introduce a race. It's not
-	 * really worth the hassle
-	 */
-
-	head = &urbp->td_list;
-	tmp = head->next;
-	while (tmp != head) {
-		struct uhci_td *td = list_entry(tmp, struct uhci_td, list);
-
-		tmp = tmp->next;
-
-		/*
-		 * Make sure we don't do the last one (since it'll have the
-		 * TERM bit set) as well as we skip every so many TD's to
-		 * make sure it doesn't hog the bandwidth
-		 */
-		if (tmp != head && (count % DEPTH_INTERVAL) == (DEPTH_INTERVAL - 1))
-			td->link |= UHCI_PTR_DEPTH;
-
-		count++;
-	}
-
-	return 0;
-}
-
-/*
- * uhci_get_current_frame_number()
- *
- * returns the current frame number for a USB bus/controller.
- */
-static int uhci_get_current_frame_number(struct usb_device *dev)
-{
-	struct uhci *uhci = (struct uhci *)dev->bus->hcpriv;
-
-	return inw(uhci->io_addr + USBFRNUM);
-}
-
-struct usb_operations uhci_device_operations = {
-	uhci_alloc_dev,
-	uhci_free_dev,
-	uhci_get_current_frame_number,
-	uhci_submit_urb,
-	uhci_unlink_urb
-};
-
-/* Virtual Root Hub */
-
-static __u8 root_hub_dev_des[] =
-{
- 	0x12,			/*  __u8  bLength; */
-	0x01,			/*  __u8  bDescriptorType; Device */
-	0x00,			/*  __u16 bcdUSB; v1.0 */
-	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 root_hub_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,
-					Bit 5 Remote-wakeup, 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; 8 Bytes */
-	0x00,
-	0xff			/*  __u8  ep_bInterval; 255 ms */
-};
-
-static __u8 root_hub_hub_des[] =
-{
-	0x09,			/*  __u8  bLength; */
-	0x29,			/*  __u8  bDescriptorType; Hub-descriptor */
-	0x02,			/*  __u8  bNbrPorts; */
-	0x00,			/* __u16  wHubCharacteristics; */
-	0x00,
-	0x01,			/*  __u8  bPwrOn2pwrGood; 2ms */
-	0x00,			/*  __u8  bHubContrCurrent; 0 mA */
-	0x00,			/*  __u8  DeviceRemovable; *** 7 Ports max *** */
-	0xff			/*  __u8  PortPwrCtrlMask; *** 7 ports max *** */
-};
-
-/* prepare Interrupt pipe transaction data; HUB INTERRUPT ENDPOINT */
-static int rh_send_irq(struct urb *urb)
-{
-	struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv;
-	struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
-	unsigned int io_addr = uhci->io_addr;
-	unsigned long flags;
-	int i, len = 1;
-	__u16 data = 0;
-
-	spin_lock_irqsave(&urb->lock, flags);
-	for (i = 0; i < uhci->rh.numports; i++) {
-		data |= ((inw(io_addr + USBPORTSC1 + i * 2) & 0xa) > 0 ? (1 << (i + 1)) : 0);
-		len = (i + 1) / 8 + 1;
-	}
-
-	*(__u16 *) urb->transfer_buffer = cpu_to_le16(data);
-	urb->actual_length = len;
-	urbp->status = 0;
-
-	spin_unlock_irqrestore(&urb->lock, flags);
-
-	if ((data > 0) && (uhci->rh.send != 0)) {
-		dbg("root-hub INT complete: port1: %x port2: %x data: %x",
-			inw(io_addr + USBPORTSC1), inw(io_addr + USBPORTSC2), data);
-		uhci_call_completion(urb);
-	}
-
-	return 0;
-}
-
-/* Virtual Root Hub INTs are polled by this timer every "interval" ms */
-static int rh_init_int_timer(struct urb *urb);
-
-static void rh_int_timer_do(unsigned long ptr)
-{
-	struct urb *urb = (struct urb *)ptr;
-	struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv;
-	struct list_head list, *tmp, *head;
-	unsigned long flags;
-
-	if (uhci->rh.send)
-		rh_send_irq(urb);
-
-	INIT_LIST_HEAD(&list);
-
-	spin_lock_irqsave(&uhci->urb_list_lock, flags);
-	head = &uhci->urb_list;
-	tmp = head->next;
-	while (tmp != head) {
-		struct urb *u = list_entry(tmp, struct urb, urb_list);
-		struct urb_priv *up = (struct urb_priv *)u->hcpriv;
-
-		tmp = tmp->next;
-
-		spin_lock(&u->lock);
-
-		/* Check if the FSBR timed out */
-		if (up->fsbr && !up->fsbr_timeout && time_after_eq(jiffies, up->fsbrtime + IDLE_TIMEOUT))
-			uhci_fsbr_timeout(uhci, u);
-
-		/* Check if the URB timed out */
-		if (u->timeout && time_after_eq(jiffies, up->inserttime + u->timeout)) {
-			list_del(&u->urb_list);
-			list_add_tail(&u->urb_list, &list);
-		}
-
-		spin_unlock(&u->lock);
-	}
-	spin_unlock_irqrestore(&uhci->urb_list_lock, flags);
-
-	head = &list;
-	tmp = head->next;
-	while (tmp != head) {
-		struct urb *u = list_entry(tmp, struct urb, urb_list);
-
-		tmp = tmp->next;
-
-		u->transfer_flags |= USB_ASYNC_UNLINK | USB_TIMEOUT_KILLED;
-		uhci_unlink_urb(u);
-	}
-
-	/* Really disable FSBR */
-	if (!uhci->fsbr && uhci->fsbrtimeout && time_after_eq(jiffies, uhci->fsbrtimeout)) {
-		uhci->fsbrtimeout = 0;
-		uhci->skel_term_qh->link = UHCI_PTR_TERM;
-	}
-
-	/* enter global suspend if nothing connected */
-	if (!uhci->is_suspended && !ports_active(uhci))
-		suspend_hc(uhci);
-
-	rh_init_int_timer(urb);
-}
-
-/* Root Hub INTs are polled by this timer */
-static int rh_init_int_timer(struct urb *urb)
-{
-	struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv;
-
-	uhci->rh.interval = urb->interval;
-	init_timer(&uhci->rh.rh_int_timer);
-	uhci->rh.rh_int_timer.function = rh_int_timer_do;
-	uhci->rh.rh_int_timer.data = (unsigned long)urb;
-	uhci->rh.rh_int_timer.expires = jiffies + (HZ * (urb->interval < 30 ? 30 : urb->interval)) / 1000;
-	add_timer(&uhci->rh.rh_int_timer);
-
-	return 0;
-}
-
-#define OK(x)			len = (x); break
-
-#define CLR_RH_PORTSTAT(x) \
-	status = inw(io_addr + USBPORTSC1 + 2 * (wIndex-1)); \
-	status = (status & 0xfff5) & ~(x); \
-	outw(status, io_addr + USBPORTSC1 + 2 * (wIndex-1))
-
-#define SET_RH_PORTSTAT(x) \
-	status = inw(io_addr + USBPORTSC1 + 2 * (wIndex-1)); \
-	status = (status & 0xfff5) | (x); \
-	outw(status, io_addr + USBPORTSC1 + 2 * (wIndex-1))
-
-
-/* Root Hub Control Pipe */
-static int rh_submit_urb(struct urb *urb)
-{
-	struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv;
-	unsigned int pipe = urb->pipe;
-	struct usb_ctrlrequest *cmd = (struct usb_ctrlrequest *)urb->setup_packet;
-	void *data = urb->transfer_buffer;
-	int leni = urb->transfer_buffer_length;
-	int len = 0;
-	int status = 0;
-	int stat = 0;
-	int i;
-	unsigned int io_addr = uhci->io_addr;
-	__u16 cstatus;
-	__u16 bmRType_bReq;
-	__u16 wValue;
-	__u16 wIndex;
-	__u16 wLength;
-
-	if (usb_pipetype(pipe) == PIPE_INTERRUPT) {
-		uhci->rh.urb = urb;
-		uhci->rh.send = 1;
-		uhci->rh.interval = urb->interval;
-		rh_init_int_timer(urb);
-
-		return -EINPROGRESS;
-	}
-
-	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);
-
-	for (i = 0; i < 8; i++)
-		uhci->rh.c_p_r[i] = 0;
-
-	switch (bmRType_bReq) {
-		/* Request Destination:
-		   without flags: Device,
-		   RH_INTERFACE: interface,
-		   RH_ENDPOINT: endpoint,
-		   RH_CLASS means HUB here,
-		   RH_OTHER | RH_CLASS  almost ever means HUB_PORT here
-		*/
-
-	case RH_GET_STATUS:
-		*(__u16 *)data = cpu_to_le16(1);
-		OK(2);
-	case RH_GET_STATUS | RH_INTERFACE:
-		*(__u16 *)data = cpu_to_le16(0);
-		OK(2);
-	case RH_GET_STATUS | RH_ENDPOINT:
-		*(__u16 *)data = cpu_to_le16(0);
-		OK(2);
-	case RH_GET_STATUS | RH_CLASS:
-		*(__u32 *)data = cpu_to_le32(0);
-		OK(4);		/* hub power */
-	case RH_GET_STATUS | RH_OTHER | RH_CLASS:
-		status = inw(io_addr + USBPORTSC1 + 2 * (wIndex - 1));
-		cstatus = ((status & USBPORTSC_CSC) >> (1 - 0)) |
-			((status & USBPORTSC_PEC) >> (3 - 1)) |
-			(uhci->rh.c_p_r[wIndex - 1] << (0 + 4));
-			status = (status & USBPORTSC_CCS) |
-			((status & USBPORTSC_PE) >> (2 - 1)) |
-			((status & USBPORTSC_SUSP) >> (12 - 2)) |
-			((status & USBPORTSC_PR) >> (9 - 4)) |
-			(1 << 8) |      /* power on */
-			((status & USBPORTSC_LSDA) << (-8 + 9));
-
-		*(__u16 *)data = cpu_to_le16(status);
-		*(__u16 *)(data + 2) = cpu_to_le16(cstatus);
-		OK(4);
-	case RH_CLEAR_FEATURE | RH_ENDPOINT:
-		switch (wValue) {
-		case RH_ENDPOINT_STALL:
-			OK(0);
-		}
-		break;
-	case RH_CLEAR_FEATURE | RH_CLASS:
-		switch (wValue) {
-		case RH_C_HUB_OVER_CURRENT:
-			OK(0);	/* hub power over current */
-		}
-		break;
-	case RH_CLEAR_FEATURE | RH_OTHER | RH_CLASS:
-		switch (wValue) {
-		case RH_PORT_ENABLE:
-			CLR_RH_PORTSTAT(USBPORTSC_PE);
-			OK(0);
-		case RH_PORT_SUSPEND:
-			CLR_RH_PORTSTAT(USBPORTSC_SUSP);
-			OK(0);
-		case RH_PORT_POWER:
-			OK(0);	/* port power */
-		case RH_C_PORT_CONNECTION:
-			SET_RH_PORTSTAT(USBPORTSC_CSC);
-			OK(0);
-		case RH_C_PORT_ENABLE:
-			SET_RH_PORTSTAT(USBPORTSC_PEC);
-			OK(0);
-		case RH_C_PORT_SUSPEND:
-			/*** WR_RH_PORTSTAT(RH_PS_PSSC); */
-			OK(0);
-		case RH_C_PORT_OVER_CURRENT:
-			OK(0);	/* port power over current */
-		case RH_C_PORT_RESET:
-			uhci->rh.c_p_r[wIndex - 1] = 0;
-			OK(0);
-		}
-		break;
-	case RH_SET_FEATURE | RH_OTHER | RH_CLASS:
-		switch (wValue) {
-		case RH_PORT_SUSPEND:
-			SET_RH_PORTSTAT(USBPORTSC_SUSP);
-			OK(0);
-		case RH_PORT_RESET:
-			SET_RH_PORTSTAT(USBPORTSC_PR);
-			mdelay(50);	/* USB v1.1 7.1.7.3 */
-			uhci->rh.c_p_r[wIndex - 1] = 1;
-			CLR_RH_PORTSTAT(USBPORTSC_PR);
-			udelay(10);
-			SET_RH_PORTSTAT(USBPORTSC_PE);
-			mdelay(10);
-			SET_RH_PORTSTAT(0xa);
-			OK(0);
-		case RH_PORT_POWER:
-			OK(0); /* port power ** */
-		case RH_PORT_ENABLE:
-			SET_RH_PORTSTAT(USBPORTSC_PE);
-			OK(0);
-		}
-		break;
-	case RH_SET_ADDRESS:
-		uhci->rh.devnum = wValue;
-		OK(0);
-	case RH_GET_DESCRIPTOR:
-		switch ((wValue & 0xff00) >> 8) {
-		case 0x01:	/* device descriptor */
-			len = min_t(unsigned int, leni,
-				  min_t(unsigned int,
-				      sizeof(root_hub_dev_des), wLength));
-			memcpy(data, root_hub_dev_des, len);
-			OK(len);
-		case 0x02:	/* configuration descriptor */
-			len = min_t(unsigned int, leni,
-				  min_t(unsigned int,
-				      sizeof(root_hub_config_des), wLength));
-			memcpy (data, root_hub_config_des, len);
-			OK(len);
-		case 0x03:	/* string descriptors */
-			len = usb_root_hub_string (wValue & 0xff,
-				uhci->io_addr, "UHCI-alt",
-				data, wLength);
-			if (len > 0) {
-				OK(min_t(int, leni, len));
-			} else 
-				stat = -EPIPE;
-		}
-		break;
-	case RH_GET_DESCRIPTOR | RH_CLASS:
-		root_hub_hub_des[2] = uhci->rh.numports;
-		len = min_t(unsigned int, leni,
-			  min_t(unsigned int, sizeof(root_hub_hub_des), wLength));
-		memcpy(data, root_hub_hub_des, len);
-		OK(len);
-	case RH_GET_CONFIGURATION:
-		*(__u8 *)data = 0x01;
-		OK(1);
-	case RH_SET_CONFIGURATION:
-		OK(0);
-	case RH_GET_INTERFACE | RH_INTERFACE:
-		*(__u8 *)data = 0x00;
-		OK(1);
-	case RH_SET_INTERFACE | RH_INTERFACE:
-		OK(0);
-	default:
-		stat = -EPIPE;
-	}
-
-	urb->actual_length = len;
-
-	return stat;
-}
-
-/*
- * MUST be called with urb->lock acquired
- */
-static int rh_unlink_urb(struct urb *urb)
-{
-	struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv;
-
-	if (uhci->rh.urb == urb) {
-		urb->status = -ENOENT;
-		uhci->rh.send = 0;
-		uhci->rh.urb = NULL;
-		del_timer(&uhci->rh.rh_int_timer);
-	}
-	return 0;
-}
-
-static void uhci_free_pending_qhs(struct uhci *uhci)
-{
-	struct list_head *tmp, *head;
-	unsigned long flags;
-
-	spin_lock_irqsave(&uhci->qh_remove_list_lock, flags);
-	head = &uhci->qh_remove_list;
-	tmp = head->next;
-	while (tmp != head) {
-		struct uhci_qh *qh = list_entry(tmp, struct uhci_qh, remove_list);
-
-		tmp = tmp->next;
-
-		list_del_init(&qh->remove_list);
-
-		uhci_free_qh(uhci, qh);
-	}
-	spin_unlock_irqrestore(&uhci->qh_remove_list_lock, flags);
-}
-
-static void uhci_call_completion(struct urb *urb)
-{
-	struct urb_priv *urbp;
-	struct usb_device *dev = urb->dev;
-	struct uhci *uhci = (struct uhci *)dev->bus->hcpriv;
-	int is_ring = 0, killed, resubmit_interrupt, status;
-	struct urb *nurb;
-	unsigned long flags;
-
-	spin_lock_irqsave(&urb->lock, flags);
-
-	urbp = (struct urb_priv *)urb->hcpriv;
-	if (!urbp || !urb->dev) {
-		spin_unlock_irqrestore(&urb->lock, flags);
-		return;
-	}
-
-	killed = (urb->status == -ENOENT || urb->status == -ECONNABORTED ||
-			urb->status == -ECONNRESET);
-	resubmit_interrupt = (usb_pipetype(urb->pipe) == PIPE_INTERRUPT &&
-			urb->interval);
-
-	nurb = urb->next;
-	if (nurb && !killed) {
-		int count = 0;
-
-		while (nurb && nurb != urb && count < MAX_URB_LOOP) {
-			if (nurb->status == -ENOENT ||
-			    nurb->status == -ECONNABORTED ||
-			    nurb->status == -ECONNRESET) {
-				killed = 1;
-				break;
-			}
-
-			nurb = nurb->next;
-			count++;
-		}
-
-		if (count == MAX_URB_LOOP)
-			err("uhci_call_completion: too many linked URB's, loop? (first loop)");
-
-		/* Check to see if chain is a ring */
-		is_ring = (nurb == urb);
-	}
-
-	if (urbp->transfer_buffer_dma_handle)
-		pci_dma_sync_single(uhci->dev, urbp->transfer_buffer_dma_handle,
-			urb->transfer_buffer_length, usb_pipein(urb->pipe) ?
-			PCI_DMA_FROMDEVICE : PCI_DMA_TODEVICE);
-
-	if (urbp->setup_packet_dma_handle)
-		pci_dma_sync_single(uhci->dev, urbp->setup_packet_dma_handle,
-			sizeof(struct usb_ctrlrequest), PCI_DMA_TODEVICE);
-
-	status = urbp->status;
-	if (!resubmit_interrupt || killed)
-		/* We don't need urb_priv anymore */
-		uhci_destroy_urb_priv(urb);
-
-	if (!killed)
-		urb->status = status;
-
-	urb->dev = NULL;
-	spin_unlock_irqrestore(&urb->lock, flags);
-
-	if (urb->complete)
-		urb->complete(urb);
-
-	if (resubmit_interrupt)
-		/* Recheck the status. The completion handler may have */
-		/*  unlinked the resubmitting interrupt URB */
-		killed = (urb->status == -ENOENT ||
-			  urb->status == -ECONNABORTED ||
-			  urb->status == -ECONNRESET);
-
-	if (resubmit_interrupt && !killed) {
-		urb->dev = dev;
-		uhci_reset_interrupt(urb);
-	} else {
-		if (is_ring && !killed) {
-			urb->dev = dev;
-			uhci_submit_urb(urb);
-		} else {
-			/* We decrement the usage count after we're done */
-			/*  with everything */
-			usb_dec_dev_use(dev);
-		}
-	}
-}
-
-static void uhci_finish_completion(struct uhci *uhci)
-{
-	struct list_head *tmp, *head;
-	unsigned long flags;
-
-	spin_lock_irqsave(&uhci->complete_list_lock, flags);
-	head = &uhci->complete_list;
-	tmp = head->next;
-	while (tmp != head) {
-		struct urb_priv *urbp = list_entry(tmp, struct urb_priv, complete_list);
-		struct urb *urb = urbp->urb;
-
-		list_del_init(&urbp->complete_list);
-		spin_unlock_irqrestore(&uhci->complete_list_lock, flags);
-
-		uhci_call_completion(urb);
-
-		spin_lock_irqsave(&uhci->complete_list_lock, flags);
-		head = &uhci->complete_list;
-		tmp = head->next;
-	}
-	spin_unlock_irqrestore(&uhci->complete_list_lock, flags);
-}
-
-static void uhci_remove_pending_qhs(struct uhci *uhci)
-{
-	struct list_head *tmp, *head;
-	unsigned long flags;
-
-	spin_lock_irqsave(&uhci->urb_remove_list_lock, flags);
-	head = &uhci->urb_remove_list;
-	tmp = head->next;
-	while (tmp != head) {
-		struct urb *urb = list_entry(tmp, struct urb, urb_list);
-		struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
-
-		tmp = tmp->next;
-
-		list_del_init(&urb->urb_list);
-
-		urbp->status = urb->status = -ECONNRESET;
-
-		uhci_add_complete(urb);
-	}
-	spin_unlock_irqrestore(&uhci->urb_remove_list_lock, flags);
-}
-
-static void uhci_interrupt(int irq, void *__uhci, struct pt_regs *regs)
-{
-	struct uhci *uhci = __uhci;
-	unsigned int io_addr = uhci->io_addr;
-	unsigned short status;
-	struct list_head *tmp, *head;
-
-	/*
-	 * Read the interrupt status, and write it back to clear the
-	 * interrupt cause
-	 */
-	status = inw(io_addr + USBSTS);
-	if (!status)	/* shared interrupt, not mine */
-		return;
-	outw(status, io_addr + USBSTS);		/* Clear it */
-
-	if (status & ~(USBSTS_USBINT | USBSTS_ERROR | USBSTS_RD)) {
-		if (status & USBSTS_HSE)
-			err("%x: host system error, PCI problems?", io_addr);
-		if (status & USBSTS_HCPE)
-			err("%x: host controller process error. something bad happened", io_addr);
-		if ((status & USBSTS_HCH) && !uhci->is_suspended) {
-			err("%x: host controller halted. very bad", io_addr);
-			/* FIXME: Reset the controller, fix the offending TD */
-		}
-	}
-
-	if (status & USBSTS_RD)
-		wakeup_hc(uhci);
-
-	uhci_free_pending_qhs(uhci);
-
-	uhci_remove_pending_qhs(uhci);
-
-	uhci_clear_next_interrupt(uhci);
-
-	/* Walk the list of pending URB's to see which ones completed */
-	spin_lock(&uhci->urb_list_lock);
-	head = &uhci->urb_list;
-	tmp = head->next;
-	while (tmp != head) {
-		struct urb *urb = list_entry(tmp, struct urb, urb_list);
-
-		tmp = tmp->next;
-
-		/* Checks the status and does all of the magic necessary */
-		uhci_transfer_result(uhci, urb);
-	}
-	spin_unlock(&uhci->urb_list_lock);
-
-	uhci_finish_completion(uhci);
-}
-
-static void reset_hc(struct uhci *uhci)
-{
-	unsigned int io_addr = uhci->io_addr;
-
-	/* Global reset for 50ms */
-	outw(USBCMD_GRESET, io_addr + USBCMD);
-	wait_ms(50);
-	outw(0, io_addr + USBCMD);
-	wait_ms(10);
-}
-
-static void suspend_hc(struct uhci *uhci)
-{
-	unsigned int io_addr = uhci->io_addr;
-
-	dbg("%x: suspend_hc", io_addr);
-
-	outw(USBCMD_EGSM, io_addr + USBCMD);
-
-	uhci->is_suspended = 1;
-}
-
-static void wakeup_hc(struct uhci *uhci)
-{
-	unsigned int io_addr = uhci->io_addr;
-	unsigned int status;
-
-	dbg("%x: wakeup_hc", io_addr);
-
-	outw(0, io_addr + USBCMD);
-	
-	/* wait for EOP to be sent */
-	status = inw(io_addr + USBCMD);
-	while (status & USBCMD_FGR)
-		status = inw(io_addr + USBCMD);
-
-	uhci->is_suspended = 0;
-
-	/* Run and mark it configured with a 64-byte max packet */
-	outw(USBCMD_RS | USBCMD_CF | USBCMD_MAXP, io_addr + USBCMD);
-}
-
-static int ports_active(struct uhci *uhci)
-{
-	unsigned int io_addr = uhci->io_addr;
-	int connection = 0;
-	int i;
-
-	for (i = 0; i < uhci->rh.numports; i++)
-		connection |= (inw(io_addr + USBPORTSC1 + i * 2) & 0x1);
-
-	return connection;
-}
-
-static void start_hc(struct uhci *uhci)
-{
-	unsigned int io_addr = uhci->io_addr;
-	int timeout = 1000;
-
-	/*
-	 * Reset the HC - this will force us to get a
-	 * new notification of any already connected
-	 * ports due to the virtual disconnect that it
-	 * implies.
-	 */
-	outw(USBCMD_HCRESET, io_addr + USBCMD);
-	while (inw(io_addr + USBCMD) & USBCMD_HCRESET) {
-		if (!--timeout) {
-			printk(KERN_ERR "uhci: USBCMD_HCRESET timed out!\n");
-			break;
-		}
-	}
-
-	/* Turn on all interrupts */
-	outw(USBINTR_TIMEOUT | USBINTR_RESUME | USBINTR_IOC | USBINTR_SP,
-		io_addr + USBINTR);
-
-	/* Start at frame 0 */
-	outw(0, io_addr + USBFRNUM);
-	outl(uhci->fl->dma_handle, io_addr + USBFLBASEADD);
-
-	/* Run and mark it configured with a 64-byte max packet */
-	outw(USBCMD_RS | USBCMD_CF | USBCMD_MAXP, io_addr + USBCMD);
-}
-
-#ifdef CONFIG_PROC_FS
-static int uhci_num = 0;
-#endif
-
-static void free_uhci(struct uhci *uhci)
-{
-	kfree(uhci);
-}
-
-/*
- * De-allocate all resources..
- */
-static void release_uhci(struct uhci *uhci)
-{
-	int i;
-#ifdef CONFIG_PROC_FS
-	char buf[8];
-#endif
-
-	if (uhci->irq >= 0) {
-		free_irq(uhci->irq, uhci);
-		uhci->irq = -1;
-	}
-
-	for (i = 0; i < UHCI_NUM_SKELQH; i++)
-		if (uhci->skelqh[i]) {
-			uhci_free_qh(uhci, uhci->skelqh[i]);
-			uhci->skelqh[i] = NULL;
-		}
-
-	for (i = 0; i < UHCI_NUM_SKELTD; i++)
-		if (uhci->skeltd[i]) {
-			uhci_free_td(uhci, uhci->skeltd[i]);
-			uhci->skeltd[i] = NULL;
-		}
-
-	if (uhci->qh_pool) {
-		pci_pool_destroy(uhci->qh_pool);
-		uhci->qh_pool = NULL;
-	}
-
-	if (uhci->td_pool) {
-		pci_pool_destroy(uhci->td_pool);
-		uhci->td_pool = NULL;
-	}
-
-	if (uhci->fl) {
-		pci_free_consistent(uhci->dev, sizeof(*uhci->fl), uhci->fl, uhci->fl->dma_handle);
-		uhci->fl = NULL;
-	}
-
-	if (uhci->bus) {
-		usb_free_bus(uhci->bus);
-		uhci->bus = NULL;
-	}
-
-#ifdef CONFIG_PROC_FS
-	if (uhci->proc_entry) {
-		sprintf(buf, "hc%d", uhci->num);
-
-		remove_proc_entry(buf, uhci_proc_root);
-		uhci->proc_entry = NULL;
-	}
-#endif
-
-	free_uhci(uhci);
-}
-
-/*
- * Allocate a frame list, and then setup the skeleton
- *
- * The hardware doesn't really know any difference
- * in the queues, but the order does matter for the
- * protocols higher up. The order is:
- *
- *  - any isochronous events handled before any
- *    of the queues. We don't do that here, because
- *    we'll create the actual TD entries on demand.
- *  - The first queue is the interrupt queue.
- *  - The second queue is the control queue, split into low and high speed
- *  - The third queue is bulk queue.
- *  - The fourth queue is the bandwidth reclamation queue, which loops back
- *    to the high speed control queue.
- */
-static int alloc_uhci(struct pci_dev *dev, unsigned int io_addr, unsigned int io_size)
-{
-	struct uhci *uhci;
-	int retval;
-	char buf[8], *bufp = buf;
-	int i, port;
-	struct usb_bus *bus;
-	dma_addr_t dma_handle;
-#ifdef CONFIG_PROC_FS
-	struct proc_dir_entry *ent;
-#endif
-
-	retval = -ENODEV;
-	if (pci_enable_device(dev) < 0) {
-		err("couldn't enable PCI device");
-		goto err_enable_device;
-	}
-
-	if (!dev->irq) {
-		err("found UHCI device with no IRQ assigned. check BIOS settings!");
-		goto err_invalid_irq;
-	}
-
-	if (!pci_dma_supported(dev, 0xFFFFFFFF)) {
-		err("PCI subsystem doesn't support 32 bit addressing?");
-		goto err_pci_dma_supported;
-	}
-
-	retval = -EBUSY;
-	if (!request_region(io_addr, io_size, "usb-uhci")) {
-		err("couldn't allocate I/O range %x - %x", io_addr,
-			io_addr + io_size - 1);
-		goto err_request_region;
-	}
-
-	pci_set_master(dev);
-
-#ifndef __sparc__
-	sprintf(buf, "%d", dev->irq);
-#else
-	bufp = __irq_itoa(dev->irq);
-#endif
-	printk(KERN_INFO __FILE__ ": USB UHCI at I/O 0x%x, IRQ %s\n",
-		io_addr, bufp);
-
-	if (pci_set_dma_mask(dev, 0xFFFFFFFF)) {
-		err("couldn't set PCI dma mask");
-		retval = -ENODEV;
-		goto err_pci_set_dma_mask;
-	}
-
-	uhci = kmalloc(sizeof(*uhci), GFP_KERNEL);
-	if (!uhci) {
-		err("couldn't allocate uhci structure");
-		retval = -ENOMEM;
-		goto err_alloc_uhci;
-	}
-
-	uhci->dev = dev;
-	uhci->irq = dev->irq;
-	uhci->io_addr = io_addr;
-	uhci->io_size = io_size;
-	pci_set_drvdata(dev, uhci);
-
-#ifdef CONFIG_PROC_FS
-	uhci->num = uhci_num++;
-
-	sprintf(buf, "hc%d", uhci->num);
-
-	ent = create_proc_entry(buf, S_IFREG|S_IRUGO|S_IWUSR, uhci_proc_root);
-	if (!ent) {
-		err("couldn't create uhci proc entry");
-		retval = -ENOMEM;
-		goto err_create_proc_entry;
-	}
-
-	ent->data = uhci;
-	ent->proc_fops = &uhci_proc_operations;
-	ent->size = 0;
-	uhci->proc_entry = ent;
-#endif
-
-	/* Reset here so we don't get any interrupts from an old setup */
-	/*  or broken setup */
-	reset_hc(uhci);
-
-	uhci->fsbr = 0;
-	uhci->fsbrtimeout = 0;
-
-	uhci->is_suspended = 0;
-
-	spin_lock_init(&uhci->qh_remove_list_lock);
-	INIT_LIST_HEAD(&uhci->qh_remove_list);
-
-	spin_lock_init(&uhci->urb_remove_list_lock);
-	INIT_LIST_HEAD(&uhci->urb_remove_list);
-
-	spin_lock_init(&uhci->urb_list_lock);
-	INIT_LIST_HEAD(&uhci->urb_list);
-
-	spin_lock_init(&uhci->complete_list_lock);
-	INIT_LIST_HEAD(&uhci->complete_list);
-
-	spin_lock_init(&uhci->frame_list_lock);
-
-	/* We need exactly one page (per UHCI specs), how convenient */
-	/* We assume that one page is atleast 4k (1024 frames * 4 bytes) */
-#if PAGE_SIZE < (4 * 1024)
-#error PAGE_SIZE is not atleast 4k
-#endif
-	uhci->fl = pci_alloc_consistent(uhci->dev, sizeof(*uhci->fl), &dma_handle);
-	if (!uhci->fl) {
-		err("unable to allocate consistent memory for frame list");
-		goto err_alloc_fl;
-	}
-
-	memset((void *)uhci->fl, 0, sizeof(*uhci->fl));
-
-	uhci->fl->dma_handle = dma_handle;
-
-	uhci->td_pool = pci_pool_create("uhci_td", uhci->dev,
-		sizeof(struct uhci_td), 16, 0, GFP_DMA | GFP_ATOMIC);
-	if (!uhci->td_pool) {
-		err("unable to create td pci_pool");
-		goto err_create_td_pool;
-	}
-
-	uhci->qh_pool = pci_pool_create("uhci_qh", uhci->dev,
-		sizeof(struct uhci_qh), 16, 0, GFP_DMA | GFP_ATOMIC);
-	if (!uhci->qh_pool) {
-		err("unable to create qh pci_pool");
-		goto err_create_qh_pool;
-	}
-
-	bus = usb_alloc_bus(&uhci_device_operations);
-	if (!bus) {
-		err("unable to allocate bus");
-		goto err_alloc_bus;
-	}
-
-	uhci->bus = bus;
-	bus->bus_name = dev->slot_name;
-	bus->hcpriv = uhci;
-
-	usb_register_bus(uhci->bus);
-
-	/* Initialize the root hub */
-
-	/* UHCI specs says devices must have 2 ports, but goes on to say */
-	/*  they may have more but give no way to determine how many they */
-	/*  have. However, according to the UHCI spec, Bit 7 is always set */
-	/*  to 1. So we try to use this to our advantage */
-	for (port = 0; port < (uhci->io_size - 0x10) / 2; port++) {
-		unsigned int portstatus;
-
-		portstatus = inw(uhci->io_addr + 0x10 + (port * 2));
-		if (!(portstatus & 0x0080))
-			break;
-	}
-	if (debug)
-		info("detected %d ports", port);
-
-	/* This is experimental so anything less than 2 or greater than 8 is */
-	/*  something weird and we'll ignore it */
-	if (port < 2 || port > 8) {
-		info("port count misdetected? forcing to 2 ports");
-		port = 2;
-	}
-
-	uhci->rh.numports = port;
-
-	uhci->bus->root_hub = uhci->rh.dev = usb_alloc_dev(NULL, uhci->bus);
-	if (!uhci->rh.dev) {
-		err("unable to allocate root hub");
-		goto err_alloc_root_hub;
-	}
-
-	uhci->skeltd[0] = uhci_alloc_td(uhci, uhci->rh.dev);
-	if (!uhci->skeltd[0]) {
-		err("unable to allocate TD 0");
-		goto err_alloc_skeltd;
-	}
-
-	/*
-	 * 9 Interrupt queues; link int2 to int1, int4 to int2, etc
-	 * then link int1 to control and control to bulk
-	 */
-	for (i = 1; i < 9; i++) {
-		struct uhci_td *td;
-
-		td = uhci->skeltd[i] = uhci_alloc_td(uhci, uhci->rh.dev);
-		if (!td) {
-			err("unable to allocate TD %d", i);
-			goto err_alloc_skeltd;
-		}
-
-		uhci_fill_td(td, 0, (UHCI_NULL_DATA_SIZE << 21) | (0x7f << 8) | USB_PID_IN, 0);
-		td->link = uhci->skeltd[i - 1]->dma_handle;
-	}
-
-	uhci->skel_term_td = uhci_alloc_td(uhci, uhci->rh.dev);
-	if (!uhci->skel_term_td) {
-		err("unable to allocate skel TD term");
-		goto err_alloc_skeltd;
-	}
-
-	for (i = 0; i < UHCI_NUM_SKELQH; i++) {
-		uhci->skelqh[i] = uhci_alloc_qh(uhci, uhci->rh.dev);
-		if (!uhci->skelqh[i]) {
-			err("unable to allocate QH %d", i);
-			goto err_alloc_skelqh;
-		}
-	}
-
-	uhci_fill_td(uhci->skel_int1_td, 0, (UHCI_NULL_DATA_SIZE << 21) | (0x7f << 8) | USB_PID_IN, 0);
-	uhci->skel_int1_td->link = uhci->skel_ls_control_qh->dma_handle | UHCI_PTR_QH;
-
-	uhci->skel_ls_control_qh->link = uhci->skel_hs_control_qh->dma_handle | UHCI_PTR_QH;
-	uhci->skel_ls_control_qh->element = UHCI_PTR_TERM;
-
-	uhci->skel_hs_control_qh->link = uhci->skel_bulk_qh->dma_handle | UHCI_PTR_QH;
-	uhci->skel_hs_control_qh->element = UHCI_PTR_TERM;
-
-	uhci->skel_bulk_qh->link = uhci->skel_term_qh->dma_handle | UHCI_PTR_QH;
-	uhci->skel_bulk_qh->element = UHCI_PTR_TERM;
-
-	/* This dummy TD is to work around a bug in Intel PIIX controllers */
-	uhci_fill_td(uhci->skel_term_td, 0, (UHCI_NULL_DATA_SIZE << 21) | (0x7f << 8) | USB_PID_IN, 0);
-	uhci->skel_term_td->link = uhci->skel_term_td->dma_handle;
-
-	uhci->skel_term_qh->link = UHCI_PTR_TERM;
-	uhci->skel_term_qh->element = uhci->skel_term_td->dma_handle;
-
-	/*
-	 * Fill the frame list: make all entries point to
-	 * the proper interrupt queue.
-	 *
-	 * This is probably silly, but it's a simple way to
-	 * scatter the interrupt queues in a way that gives
-	 * us a reasonable dynamic range for irq latencies.
-	 */
-	for (i = 0; i < UHCI_NUMFRAMES; i++) {
-		int irq = 0;
-
-		if (i & 1) {
-			irq++;
-			if (i & 2) {
-				irq++;
-				if (i & 4) { 
-					irq++;
-					if (i & 8) { 
-						irq++;
-						if (i & 16) {
-							irq++;
-							if (i & 32) {
-								irq++;
-								if (i & 64)
-									irq++;
-							}
-						}
-					}
-				}
-			}
-		}
-
-		/* Only place we don't use the frame list routines */
-		uhci->fl->frame[i] =  uhci->skeltd[irq]->dma_handle;
-	}
-
-	start_hc(uhci);
-
-	if (request_irq(dev->irq, uhci_interrupt, SA_SHIRQ, "usb-uhci", uhci))
-		goto err_request_irq;
-
-	/* disable legacy emulation */
-	pci_write_config_word(uhci->dev, USBLEGSUP, USBLEGSUP_DEFAULT);
-
-	usb_connect(uhci->rh.dev);
-
-	if (usb_new_device(uhci->rh.dev) != 0) {
-		err("unable to start root hub");
-		retval = -ENOMEM;
-		goto err_start_root_hub;
-	}
-
-	return 0;
-
-/*
- * error exits:
- */
-err_start_root_hub:
-	free_irq(uhci->irq, uhci);
-	uhci->irq = -1;
-
-err_request_irq:
-	for (i = 0; i < UHCI_NUM_SKELQH; i++)
-		if (uhci->skelqh[i]) {
-			uhci_free_qh(uhci, uhci->skelqh[i]);
-			uhci->skelqh[i] = NULL;
-		}
-
-err_alloc_skelqh:
-	for (i = 0; i < UHCI_NUM_SKELTD; i++)
-		if (uhci->skeltd[i]) {
-			uhci_free_td(uhci, uhci->skeltd[i]);
-			uhci->skeltd[i] = NULL;
-		}
-
-err_alloc_skeltd:
-	usb_free_dev(uhci->rh.dev);
-	uhci->rh.dev = NULL;
-
-err_alloc_root_hub:
-	usb_free_bus(uhci->bus);
-	uhci->bus = NULL;
-
-err_alloc_bus:
-	pci_pool_destroy(uhci->qh_pool);
-	uhci->qh_pool = NULL;
-
-err_create_qh_pool:
-	pci_pool_destroy(uhci->td_pool);
-	uhci->td_pool = NULL;
-
-err_create_td_pool:
-	pci_free_consistent(uhci->dev, sizeof(*uhci->fl), uhci->fl, uhci->fl->dma_handle);
-	uhci->fl = NULL;
-
-err_alloc_fl:
-#ifdef CONFIG_PROC_FS
-	remove_proc_entry(buf, uhci_proc_root);
-	uhci->proc_entry = NULL;
-
-err_create_proc_entry:
-	free_uhci(uhci);
-#endif
-
-err_alloc_uhci:
-
-err_pci_set_dma_mask:
-	release_region(io_addr, io_size);
-
-err_request_region:
-
-err_pci_dma_supported:
-
-err_invalid_irq:
-
-err_enable_device:
-
-	return retval;
-}
-
-static int __devinit uhci_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
-{
-	int i;
-
-	/* Search for the IO base address.. */
-	for (i = 0; i < 6; i++) {
-		unsigned int io_addr = pci_resource_start(dev, i);
-		unsigned int io_size = pci_resource_len(dev, i);
-
-		/* IO address? */
-		if (!(pci_resource_flags(dev, i) & IORESOURCE_IO))
-			continue;
-
-		return alloc_uhci(dev, io_addr, io_size);
-	}
-
-	return -ENODEV;
-}
-
-static void __devexit uhci_pci_remove(struct pci_dev *dev)
-{
-	struct uhci *uhci = pci_get_drvdata(dev);
-
-	if (uhci->bus->root_hub)
-		usb_disconnect(&uhci->bus->root_hub);
-
-	usb_deregister_bus(uhci->bus);
-
-	/*
-	 * At this point, we're guaranteed that no new connects can be made
-	 * to this bus since there are no more parents
-	 */
-	uhci_free_pending_qhs(uhci);
-	uhci_remove_pending_qhs(uhci);
-
-	reset_hc(uhci);
-	release_region(uhci->io_addr, uhci->io_size);
-
-	uhci_free_pending_qhs(uhci);
-
-	release_uhci(uhci);
-}
-
-#ifdef CONFIG_PM
-static int uhci_pci_suspend(struct pci_dev *dev, u32 state)
-{
-	suspend_hc((struct uhci *) pci_get_drvdata(dev));
-	return 0;
-}
-
-static int uhci_pci_resume(struct pci_dev *dev)
-{
-	reset_hc((struct uhci *) pci_get_drvdata(dev));
-	start_hc((struct uhci *) pci_get_drvdata(dev));
-	return 0;
-}
-#endif
-
-static const struct pci_device_id __devinitdata uhci_pci_ids[] = { {
-
-	/* handle any USB UHCI controller */
-	class: 		((PCI_CLASS_SERIAL_USB << 8) | 0x00),
-	class_mask: 	~0,
-
-	/* no matter who makes it */
-	vendor:		PCI_ANY_ID,
-	device:		PCI_ANY_ID,
-	subvendor:	PCI_ANY_ID,
-	subdevice:	PCI_ANY_ID,
-
-	}, { /* end: all zeroes */ }
-};
-
-MODULE_DEVICE_TABLE(pci, uhci_pci_ids);
-
-static struct pci_driver uhci_pci_driver = {
-	name:		"usb-uhci",
-	id_table:	uhci_pci_ids,
-
-	probe:		uhci_pci_probe,
-	remove:		__devexit_p(uhci_pci_remove),
-
-#ifdef	CONFIG_PM
-	suspend:	uhci_pci_suspend,
-	resume:		uhci_pci_resume,
-#endif	/* PM */
-};
-
- 
-static int __init uhci_hcd_init(void)
-{
-	int retval = -ENOMEM;
-
-	info(DRIVER_DESC " " DRIVER_VERSION);
-
-	if (debug) {
-		errbuf = kmalloc(ERRBUF_LEN, GFP_KERNEL);
-		if (!errbuf)
-			goto errbuf_failed;
-	}
-
-#ifdef CONFIG_PROC_FS
-	uhci_proc_root = create_proc_entry("driver/uhci", S_IFDIR, 0);
-	if (!uhci_proc_root)
-		goto proc_failed;
-#endif
-
-	uhci_up_cachep = kmem_cache_create("uhci_urb_priv",
-		sizeof(struct urb_priv), 0, 0, NULL, NULL);
-	if (!uhci_up_cachep)
-		goto up_failed;
-
-	retval = pci_module_init(&uhci_pci_driver);
-	if (retval)
-		goto init_failed;
-
-	return 0;
-
-init_failed:
-	if (kmem_cache_destroy(uhci_up_cachep))
-		printk(KERN_INFO "uhci: not all urb_priv's were freed\n");
-
-up_failed:
-
-#ifdef CONFIG_PROC_FS
-	remove_proc_entry("driver/uhci", 0);
-
-proc_failed:
-#endif
-	if (errbuf)
-		kfree(errbuf);
-
-errbuf_failed:
-
-	return retval;
-}
-
-static void __exit uhci_hcd_cleanup(void) 
-{
-	pci_unregister_driver(&uhci_pci_driver);
-	
-	if (kmem_cache_destroy(uhci_up_cachep))
-		printk(KERN_INFO "uhci: not all urb_priv's were freed\n");
-
-#ifdef CONFIG_PROC_FS
-	remove_proc_entry("driver/uhci", 0);
-#endif
-
-	if (errbuf)
-		kfree(errbuf);
-}
-
-module_init(uhci_hcd_init);
-module_exit(uhci_hcd_cleanup);
-
-MODULE_AUTHOR(DRIVER_AUTHOR);
-MODULE_DESCRIPTION(DRIVER_DESC);
-MODULE_LICENSE("GPL");
-
diff -urN linux-2.4.21-pre5/drivers/usb/uhci.h linux-2.4.21-pre6/drivers/usb/uhci.h
--- linux-2.4.21-pre5/drivers/usb/uhci.h	Thu Nov 28 15:53:15 2002
+++ linux-2.4.21-pre6/drivers/usb/uhci.h	Wed Dec 31 16:00:00 1969
@@ -1,441 +0,0 @@
-#ifndef __LINUX_UHCI_H
-#define __LINUX_UHCI_H
-
-#include <linux/list.h>
-#include <linux/usb.h>
-
-/*
- * Universal Host Controller Interface data structures and defines
- */
-
-/* Command register */
-#define USBCMD		0
-#define   USBCMD_RS		0x0001	/* Run/Stop */
-#define   USBCMD_HCRESET	0x0002	/* Host reset */
-#define   USBCMD_GRESET		0x0004	/* Global reset */
-#define   USBCMD_EGSM		0x0008	/* Global Suspend Mode */
-#define   USBCMD_FGR		0x0010	/* Force Global Resume */
-#define   USBCMD_SWDBG		0x0020	/* SW Debug mode */
-#define   USBCMD_CF		0x0040	/* Config Flag (sw only) */
-#define   USBCMD_MAXP		0x0080	/* Max Packet (0 = 32, 1 = 64) */
-
-/* Status register */
-#define USBSTS		2
-#define   USBSTS_USBINT		0x0001	/* Interrupt due to IOC */
-#define   USBSTS_ERROR		0x0002	/* Interrupt due to error */
-#define   USBSTS_RD		0x0004	/* Resume Detect */
-#define   USBSTS_HSE		0x0008	/* Host System Error - basically PCI problems */
-#define   USBSTS_HCPE		0x0010	/* Host Controller Process Error - the scripts were buggy */
-#define   USBSTS_HCH		0x0020	/* HC Halted */
-
-/* Interrupt enable register */
-#define USBINTR		4
-#define   USBINTR_TIMEOUT	0x0001	/* Timeout/CRC error enable */
-#define   USBINTR_RESUME	0x0002	/* Resume interrupt enable */
-#define   USBINTR_IOC		0x0004	/* Interrupt On Complete enable */
-#define   USBINTR_SP		0x0008	/* Short packet interrupt enable */
-
-#define USBFRNUM	6
-#define USBFLBASEADD	8
-#define USBSOF		12
-
-/* USB port status and control registers */
-#define USBPORTSC1	16
-#define USBPORTSC2	18
-#define   USBPORTSC_CCS		0x0001	/* Current Connect Status ("device present") */
-#define   USBPORTSC_CSC		0x0002	/* Connect Status Change */
-#define   USBPORTSC_PE		0x0004	/* Port Enable */
-#define   USBPORTSC_PEC		0x0008	/* Port Enable Change */
-#define   USBPORTSC_LS		0x0030	/* Line Status */
-#define   USBPORTSC_RD		0x0040	/* Resume Detect */
-#define   USBPORTSC_LSDA	0x0100	/* Low Speed Device Attached */
-#define   USBPORTSC_PR		0x0200	/* Port Reset */
-#define   USBPORTSC_SUSP	0x1000	/* Suspend */
-
-/* Legacy support register */
-#define USBLEGSUP		0xc0
-#define   USBLEGSUP_DEFAULT	0x2000	/* only PIRQ enable set */
-
-#define UHCI_NULL_DATA_SIZE	0x7FF	/* for UHCI controller TD */
-
-#define UHCI_PTR_BITS		0x000F
-#define UHCI_PTR_TERM		0x0001
-#define UHCI_PTR_QH		0x0002
-#define UHCI_PTR_DEPTH		0x0004
-
-#define UHCI_NUMFRAMES		1024	/* in the frame list [array] */
-#define UHCI_MAX_SOF_NUMBER	2047	/* in an SOF packet */
-#define CAN_SCHEDULE_FRAMES	1000	/* how far future frames can be scheduled */
-
-struct uhci_frame_list {
-	__u32 frame[UHCI_NUMFRAMES];
-
-	void *frame_cpu[UHCI_NUMFRAMES];
-
-	dma_addr_t dma_handle;
-};
-
-struct urb_priv;
-
-struct uhci_qh {
-	/* Hardware fields */
-	__u32 link;			/* Next queue */
-	__u32 element;			/* Queue element pointer */
-
-	/* Software fields */
-	dma_addr_t dma_handle;
-
-	struct usb_device *dev;
-	struct urb_priv *urbp;
-
-	struct list_head list;		/* P: uhci->frame_list_lock */
-	struct list_head remove_list;	/* P: uhci->remove_list_lock */
-} __attribute__((aligned(16)));
-
-/*
- * for TD <status>:
- */
-#define TD_CTRL_SPD		(1 << 29)	/* Short Packet Detect */
-#define TD_CTRL_C_ERR_MASK	(3 << 27)	/* Error Counter bits */
-#define TD_CTRL_C_ERR_SHIFT	27
-#define TD_CTRL_LS		(1 << 26)	/* Low Speed Device */
-#define TD_CTRL_IOS		(1 << 25)	/* Isochronous Select */
-#define TD_CTRL_IOC		(1 << 24)	/* Interrupt on Complete */
-#define TD_CTRL_ACTIVE		(1 << 23)	/* TD Active */
-#define TD_CTRL_STALLED		(1 << 22)	/* TD Stalled */
-#define TD_CTRL_DBUFERR		(1 << 21)	/* Data Buffer Error */
-#define TD_CTRL_BABBLE		(1 << 20)	/* Babble Detected */
-#define TD_CTRL_NAK		(1 << 19)	/* NAK Received */
-#define TD_CTRL_CRCTIMEO	(1 << 18)	/* CRC/Time Out Error */
-#define TD_CTRL_BITSTUFF	(1 << 17)	/* Bit Stuff Error */
-#define TD_CTRL_ACTLEN_MASK	0x7FF		/* actual length, encoded as n - 1 */
-
-#define TD_CTRL_ANY_ERROR	(TD_CTRL_STALLED | TD_CTRL_DBUFERR | \
-				 TD_CTRL_BABBLE | TD_CTRL_CRCTIME | TD_CTRL_BITSTUFF)
-
-#define uhci_status_bits(ctrl_sts)	(ctrl_sts & 0xFE0000)
-#define uhci_actual_length(ctrl_sts)	((ctrl_sts + 1) & TD_CTRL_ACTLEN_MASK) /* 1-based */
-
-/*
- * for TD <info>: (a.k.a. Token)
- */
-#define TD_TOKEN_TOGGLE_SHIFT	19
-#define TD_TOKEN_TOGGLE		(1 << 19)
-#define TD_TOKEN_PID_MASK	0xFF
-#define TD_TOKEN_EXPLEN_MASK	0x7FF		/* expected length, encoded as n - 1 */
-
-#define uhci_maxlen(token)	((token) >> 21)
-#define uhci_expected_length(info) (((info >> 21) + 1) & TD_TOKEN_EXPLEN_MASK) /* 1-based */
-#define uhci_toggle(token)	(((token) >> TD_TOKEN_TOGGLE_SHIFT) & 1)
-#define uhci_endpoint(token)	(((token) >> 15) & 0xf)
-#define uhci_devaddr(token)	(((token) >> 8) & 0x7f)
-#define uhci_devep(token)	(((token) >> 8) & 0x7ff)
-#define uhci_packetid(token)	((token) & TD_TOKEN_PID_MASK)
-#define uhci_packetout(token)	(uhci_packetid(token) != USB_PID_IN)
-#define uhci_packetin(token)	(uhci_packetid(token) == USB_PID_IN)
-
-/*
- * The documentation says "4 words for hardware, 4 words for software".
- *
- * That's silly, the hardware doesn't care. The hardware only cares that
- * the hardware words are 16-byte aligned, and we can have any amount of
- * sw space after the TD entry as far as I can tell.
- *
- * But let's just go with the documentation, at least for 32-bit machines.
- * On 64-bit machines we probably want to take advantage of the fact that
- * hw doesn't really care about the size of the sw-only area.
- *
- * Alas, not anymore, we have more than 4 words for software, woops.
- * Everything still works tho, surprise! -jerdfelt
- */
-struct uhci_td {
-	/* Hardware fields */
-	__u32 link;
-	__u32 status;
-	__u32 info;
-	__u32 buffer;
-
-	/* Software fields */
-	dma_addr_t dma_handle;
-
-	struct usb_device *dev;
-	struct urb *urb;
-
-	struct list_head list;		/* P: urb->lock */
-
-	int frame;
-	struct list_head fl_list;	/* P: uhci->frame_list_lock */
-} __attribute__((aligned(16)));
-
-/*
- * There are various standard queues. We set up several different
- * queues for each of the three basic queue types: interrupt,
- * control, and bulk.
- *
- *  - There are various different interrupt latencies: ranging from
- *    every other USB frame (2 ms apart) to every 256 USB frames (ie
- *    256 ms apart). Make your choice according to how obnoxious you
- *    want to be on the wire, vs how critical latency is for you.
- *  - The control list is done every frame.
- *  - There are 4 bulk lists, so that up to four devices can have a
- *    bulk list of their own and when run concurrently all four lists
- *    will be be serviced.
- *
- * This is a bit misleading, there are various interrupt latencies, but they
- * vary a bit, interrupt2 isn't exactly 2ms, it can vary up to 4ms since the
- * other queues can "override" it. interrupt4 can vary up to 8ms, etc. Minor
- * problem
- *
- * In the case of the root hub, these QH's are just head's of qh's. Don't
- * be scared, it kinda makes sense. Look at this wonderful picture care of
- * Linus:
- *
- *  generic-  ->  dev1-  ->  generic-  ->  dev1-  ->  control-  ->  bulk- -> ...
- *   iso-QH      iso-QH       irq-QH      irq-QH        QH           QH
- *      |           |            |           |           |            |
- *     End     dev1-iso-TD1     End     dev1-irq-TD1    ...          ... 
- *                  |
- *             dev1-iso-TD2
- *                  |
- *                ....
- *
- * This may vary a bit (the UHCI docs don't explicitly say you can put iso
- * transfers in QH's and all of their pictures don't have that either) but
- * other than that, that is what we're doing now
- *
- * And now we don't put Iso transfers in QH's, so we don't waste one on it
- * --jerdfelt
- *
- * To keep with Linus' nomenclature, this is called the QH skeleton. These
- * labels (below) are only signficant to the root hub's QH's
- */
-
-#define UHCI_NUM_SKELTD		10
-#define skel_int1_td		skeltd[0]
-#define skel_int2_td		skeltd[1]
-#define skel_int4_td		skeltd[2]
-#define skel_int8_td		skeltd[3]
-#define skel_int16_td		skeltd[4]
-#define skel_int32_td		skeltd[5]
-#define skel_int64_td		skeltd[6]
-#define skel_int128_td		skeltd[7]
-#define skel_int256_td		skeltd[8]
-#define skel_term_td		skeltd[9]	/* To work around PIIX UHCI bug */
-
-#define UHCI_NUM_SKELQH		4
-#define skel_ls_control_qh	skelqh[0]
-#define skel_hs_control_qh	skelqh[1]
-#define skel_bulk_qh		skelqh[2]
-#define skel_term_qh		skelqh[3]
-
-/*
- * Search tree for determining where <interval> fits in the
- * skelqh[] skeleton.
- *
- * An interrupt request should be placed into the slowest skelqh[]
- * which meets the interval/period/frequency requirement.
- * An interrupt request is allowed to be faster than <interval> but not slower.
- *
- * For a given <interval>, this function returns the appropriate/matching
- * skelqh[] index value.
- *
- * NOTE: For UHCI, we don't really need int256_qh since the maximum interval
- * is 255 ms.  However, we do need an int1_qh since 1 is a valid interval
- * and we should meet that frequency when requested to do so.
- * This will require some change(s) to the UHCI skeleton.
- */
-static inline int __interval_to_skel(int interval)
-{
-	if (interval < 16) {
-		if (interval < 4) {
-			if (interval < 2)
-				return 0;	/* int1 for 0-1 ms */
-			return 1;		/* int2 for 2-3 ms */
-		}
-		if (interval < 8)
-			return 2;		/* int4 for 4-7 ms */
-		return 3;			/* int8 for 8-15 ms */
-	}
-	if (interval < 64) {
-		if (interval < 32)
-			return 4;		/* int16 for 16-31 ms */
-		return 5;			/* int32 for 32-63 ms */
-	}
-	if (interval < 128)
-		return 6;			/* int64 for 64-127 ms */
-	return 7;				/* int128 for 128-255 ms (Max.) */
-}
-
-struct virt_root_hub {
-	struct usb_device *dev;
-	int devnum;		/* Address of Root Hub endpoint */
-	struct urb *urb;
-	void *int_addr;
-	int send;
-	int interval;
-	int numports;
-	int c_p_r[8];
-	struct timer_list rh_int_timer;
-};
-
-/*
- * This describes the full uhci information.
- *
- * Note how the "proper" USB information is just
- * a subset of what the full implementation needs.
- */
-struct uhci {
-	struct pci_dev *dev;
-
-#ifdef CONFIG_PROC_FS
-	/* procfs */
-	int num;
-	struct proc_dir_entry *proc_entry;
-#endif
-
-	/* Grabbed from PCI */
-	int irq;
-	unsigned int io_addr;
-	unsigned int io_size;
-
-	struct pci_pool *qh_pool;
-	struct pci_pool *td_pool;
-
-	struct usb_bus *bus;
-
-	struct uhci_td *skeltd[UHCI_NUM_SKELTD];	/* Skeleton TD's */
-	struct uhci_qh *skelqh[UHCI_NUM_SKELQH];	/* Skeleton QH's */
-
-	spinlock_t frame_list_lock;
-	struct uhci_frame_list *fl;		/* P: uhci->frame_list_lock */
-	int fsbr;				/* Full speed bandwidth reclamation */
-	unsigned long fsbrtimeout;		/* FSBR delay */
-	int is_suspended;
-
-	/* Main list of URB's currently controlled by this HC */
-	spinlock_t urb_list_lock;
-	struct list_head urb_list;		/* P: uhci->urb_list_lock */
-
-	/* List of QH's that are done, but waiting to be unlinked (race) */
-	spinlock_t qh_remove_list_lock;
-	struct list_head qh_remove_list;	/* P: uhci->qh_remove_list_lock */
-
-	/* List of asynchronously unlinked URB's */
-	spinlock_t urb_remove_list_lock;
-	struct list_head urb_remove_list;	/* P: uhci->urb_remove_list_lock */
-
-	/* List of URB's awaiting completion callback */
-	spinlock_t complete_list_lock;
-	struct list_head complete_list;		/* P: uhci->complete_list_lock */
-
-	struct virt_root_hub rh;	/* private data of the virtual root hub */
-};
-
-struct urb_priv {
-	struct urb *urb;
-	struct usb_device *dev;
-
-	dma_addr_t setup_packet_dma_handle;
-	dma_addr_t transfer_buffer_dma_handle;
-
-	struct uhci_qh *qh;		/* QH for this URB */
-	struct list_head td_list;	/* P: urb->lock */
-
-	int fsbr : 1;			/* URB turned on FSBR */
-	int fsbr_timeout : 1;		/* URB timed out on FSBR */
-	int queued : 1;			/* QH was queued (not linked in) */
-	int short_control_packet : 1;	/* If we get a short packet during */
-					/*  a control transfer, retrigger */
-					/*  the status phase */
-
-	int status;			/* Final status */
-
-	unsigned long inserttime;	/* In jiffies */
-	unsigned long fsbrtime;		/* In jiffies */
-
-	struct list_head queue_list;	/* P: uhci->frame_list_lock */
-	struct list_head complete_list;	/* P: uhci->complete_list_lock */
-};
-
-/*
- * Locking in uhci.c
- *
- * spinlocks are used extensively to protect the many lists and data
- * structures we have. It's not that pretty, but it's necessary. We
- * need to be done with all of the locks (except complete_list_lock) when
- * we call urb->complete. I've tried to make it simple enough so I don't
- * have to spend hours racking my brain trying to figure out if the
- * locking is safe.
- *
- * Here's the safe locking order to prevent deadlocks:
- *
- * #1 uhci->urb_list_lock
- * #2 urb->lock
- * #3 uhci->urb_remove_list_lock, uhci->frame_list_lock, 
- *   uhci->qh_remove_list_lock
- * #4 uhci->complete_list_lock
- *
- * If you're going to grab 2 or more locks at once, ALWAYS grab the lock
- * at the lowest level FIRST and NEVER grab locks at the same level at the
- * same time.
- * 
- * So, if you need uhci->urb_list_lock, grab it before you grab urb->lock
- */
-
-/* -------------------------------------------------------------------------
-   Virtual Root HUB
-   ------------------------------------------------------------------------- */
-/* destination of request */
-#define RH_DEVICE		0x00
-#define RH_INTERFACE		0x01
-#define RH_ENDPOINT		0x02
-#define RH_OTHER		0x03
-
-#define RH_CLASS		0x20
-#define RH_VENDOR		0x40
-
-/* 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
-/* Our Vendor Specific Request */
-#define RH_SET_EP		0x2000
-
-/* Hub port features */
-#define RH_PORT_CONNECTION	0x00
-#define RH_PORT_ENABLE		0x01
-#define RH_PORT_SUSPEND		0x02
-#define RH_PORT_OVER_CURRENT	0x03
-#define RH_PORT_RESET		0x04
-#define RH_PORT_POWER		0x08
-#define RH_PORT_LOW_SPEED	0x09
-#define RH_C_PORT_CONNECTION	0x10
-#define RH_C_PORT_ENABLE	0x11
-#define RH_C_PORT_SUSPEND	0x12
-#define RH_C_PORT_OVER_CURRENT	0x13
-#define RH_C_PORT_RESET		0x14
-
-/* Hub features */
-#define RH_C_HUB_LOCAL_POWER	0x00
-#define RH_C_HUB_OVER_CURRENT	0x01
-#define RH_DEVICE_REMOTE_WAKEUP	0x00
-#define RH_ENDPOINT_STALL	0x01
-
-/* Our Vendor Specific feature */
-#define RH_REMOVE_EP		0x00
-
-#define RH_ACK			0x01
-#define RH_REQ_ERR		-1
-#define RH_NACK			0x00
-
-#endif
-
diff -urN linux-2.4.21-pre5/drivers/usb/usb-midi.h linux-2.4.21-pre6/drivers/usb/usb-midi.h
--- linux-2.4.21-pre5/drivers/usb/usb-midi.h	Wed Mar 26 16:11:29 2003
+++ linux-2.4.21-pre6/drivers/usb/usb-midi.h	Wed Mar 26 16:11:39 2003
@@ -37,23 +37,35 @@
 /* ------------------------------------------------------------------------- */
 /* Roland MIDI Devices */
 
+#ifndef USB_VENDOR_ID_ROLAND
 #define USB_VENDOR_ID_ROLAND		0x0582
+#endif
 #define USBMIDI_ROLAND_UA100G		0x0000
 #define USBMIDI_ROLAND_MPU64		0x0002
 #define USBMIDI_ROLAND_SC8850		0x0003
-#define USBMIDI_ROLAND_SC8820		0x0007
 #define USBMIDI_ROLAND_UM2		0x0005
-#define USBMIDI_ROLAND_UM1		0x0009
+#define USBMIDI_ROLAND_SC8820		0x0007
 #define USBMIDI_ROLAND_PC300		0x0008
+#define USBMIDI_ROLAND_UM1		0x0009
 
 /* YAMAHA MIDI Devices */
+#ifndef USB_VENDOR_ID_YAMAHA
 #define USB_VENDOR_ID_YAMAHA		0x0499
+#endif
 #define USBMIDI_YAMAHA_MU1000		0x1001
 
 /* Steinberg MIDI Devices */
+#ifndef USB_VENDOR_ID_STEINBERG
 #define USB_VENDOR_ID_STEINBERG		0x0763
+#endif
 #define USBMIDI_STEINBERG_USB2MIDI	0x1001
 
+/* Mark of the Unicorn devices */
+#ifndef USB_VENDOR_ID_MOTU
+#define USB_VENDOR_ID_MOTU		0x07fd
+#endif
+#define USBMIDI_MOTU_FASTLANE		0x0001
+
 /* ------------------------------------------------------------------------- */
 /* Supported devices */
 
@@ -101,15 +113,15 @@
   { /* Roland SC8850 */
     "Roland SC8850",
     USB_VENDOR_ID_ROLAND, USBMIDI_ROLAND_SC8850, 2, -1,
-    { { 0x81, 15 }, {-1, -1} }, /** cables 0,1,2, and 3 **/
-    { { 0x01, 15 }, {-1, -1} },
+    { { 0x81, 0x3f }, {-1, -1} },
+    { { 0x01, 0x3f }, {-1, -1} },
   },
 
   { /* Roland SC8820 */
     "Roland SC8820",
     USB_VENDOR_ID_ROLAND, USBMIDI_ROLAND_SC8820, 2, -1,
-    { { 0x81, 17 }, {-1, -1} },
-    { { 0x01, 17 }, {-1, -1} },
+    { { 0x81, 0x13 }, {-1, -1} },
+    { { 0x01, 0x13 }, {-1, -1} },
   },
 
   { /* YAMAHA MU1000 */
@@ -123,6 +135,12 @@
     USB_VENDOR_ID_ROLAND, USBMIDI_ROLAND_PC300, 2, -1, 
     { { 0x81, 1 }, {-1, -1} },
     { { 0x01, 1 }, {-1, -1} },
+  },
+  { /* MOTU Fastlane USB */
+    "MOTU Fastlane USB",
+    USB_VENDOR_ID_MOTU, USBMIDI_MOTU_FASTLANE, 1, 0,
+    { { 0x82, 3 }, {-1, -1} },
+    { { 0x02, 3 }, {-1, -1} },
   }
 };
 
@@ -138,7 +156,9 @@
 	{ USB_DEVICE( USB_VENDOR_ID_ROLAND, USBMIDI_ROLAND_UA100G ) },
 	{ USB_DEVICE( USB_VENDOR_ID_ROLAND, USBMIDI_ROLAND_PC300 ) },
 	{ USB_DEVICE( USB_VENDOR_ID_ROLAND, USBMIDI_ROLAND_SC8850 ) },
+	{ USB_DEVICE( USB_VENDOR_ID_ROLAND, USBMIDI_ROLAND_SC8820 ) },
 	{ USB_DEVICE( USB_VENDOR_ID_YAMAHA, USBMIDI_YAMAHA_MU1000 ) },
+	{ USB_DEVICE( USB_VENDOR_ID_MOTU,   USBMIDI_MOTU_FASTLANE ) },
 /*	{ USB_DEVICE( USB_VENDOR_ID_STEINBERG, USBMIDI_STEINBERG_USB2MIDI ) },*/
 	{ } /* Terminating entry */
 };
diff -urN linux-2.4.21-pre5/drivers/usb/usb-ohci.c linux-2.4.21-pre6/drivers/usb/usb-ohci.c
--- linux-2.4.21-pre5/drivers/usb/usb-ohci.c	Wed Mar 26 16:11:29 2003
+++ linux-2.4.21-pre6/drivers/usb/usb-ohci.c	Wed Dec 31 16:00:00 1969
@@ -1,2938 +0,0 @@
-/*
- * URB OHCI HCD (Host Controller Driver) for USB.
- *
- * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
- * (C) Copyright 2000-2001 David Brownell <dbrownell@users.sourceforge.net>
- * 
- * [ Initialisation is based on Linus'  ]
- * [ uhci code and gregs ohci fragments ]
- * [ (C) Copyright 1999 Linus Torvalds  ]
- * [ (C) Copyright 1999 Gregory P. Smith]
- * 
- * 
- * History:
- *
- * 2002/10/22 OHCI_USB_OPER for ALi lockup in IBM i1200 (ALEX <thchou@ali>)
- * 2002/03/08 interrupt unlink fix (Matt Hughes), better cleanup on
- *	load failure (Matthew Frederickson)
- * 2002/01/20 async unlink fixes:  return -EINPROGRESS (per spec) and
- *	make interrupt unlink-in-completion work (db)
- * 2001/09/19 USB_ZERO_PACKET support (Jean Tourrilhes)
- * 2001/07/17 power management and pmac cleanup (Benjamin Herrenschmidt)
- * 2001/03/24 td/ed hashing to remove bus_to_virt (Steve Longerbeam);
- 	pci_map_single (db)
- * 2001/03/21 td and dev/ed allocation uses new pci_pool API (db)
- * 2001/03/07 hcca allocation uses pci_alloc_consistent (Steve Longerbeam)
- *
- * 2000/09/26 fixed races in removing the private portion of the urb
- * 2000/09/07 disable bulk and control lists when unlinking the last
- *	endpoint descriptor in order to avoid unrecoverable errors on
- *	the Lucent chips. (rwc@sgi)
- * 2000/08/29 use bandwidth claiming hooks (thanks Randy!), fix some
- *	urb unlink probs, indentation fixes
- * 2000/08/11 various oops fixes mostly affecting iso and cleanup from
- *	device unplugs.
- * 2000/06/28 use PCI hotplug framework, for better power management
- *	and for Cardbus support (David Brownell)
- * 2000/earlier:  fixes for NEC/Lucent chips; suspend/resume handling
- *	when the controller loses power; handle UE; cleanup; ...
- *
- * v5.2 1999/12/07 URB 3rd preview, 
- * v5.1 1999/11/30 URB 2nd preview, cpia, (usb-scsi)
- * v5.0 1999/11/22 URB Technical preview, Paul Mackerras powerbook susp/resume 
- * 	i386: HUB, Keyboard, Mouse, Printer 
- *
- * v4.3 1999/10/27 multiple HCs, bulk_request
- * v4.2 1999/09/05 ISO API alpha, new dev alloc, neg Error-codes
- * v4.1 1999/08/27 Randy Dunlap's - ISO API first impl.
- * v4.0 1999/08/18 
- * v3.0 1999/06/25 
- * v2.1 1999/05/09  code clean up
- * v2.0 1999/05/04 
- * v1.0 1999/04/27 initial release
- */
- 
-#include <linux/config.h>
-#include <linux/module.h>
-#include <linux/pci.h>
-#include <linux/kernel.h>
-#include <linux/delay.h>
-#include <linux/ioport.h>
-#include <linux/sched.h>
-#include <linux/slab.h>
-#include <linux/smp_lock.h>
-#include <linux/errno.h>
-#include <linux/init.h>
-#include <linux/timer.h>
-#include <linux/list.h>
-#include <linux/interrupt.h>  /* for in_interrupt() */
-#undef DEBUG
-#include <linux/usb.h>
-
-#include <asm/io.h>
-#include <asm/irq.h>
-#include <asm/system.h>
-#include <asm/unaligned.h>
-
-#define OHCI_USE_NPS		// force NoPowerSwitching mode
-// #define OHCI_VERBOSE_DEBUG	/* not always helpful */
-
-#include "usb-ohci.h"
-
-#include "hcd.h"
-
-#ifdef CONFIG_PMAC_PBOOK
-#include <asm/machdep.h>
-#include <asm/pmac_feature.h>
-#include <asm/pci-bridge.h>
-#ifndef CONFIG_PM
-#define CONFIG_PM
-#endif
-#endif
-
-
-/*
- * Version Information
- */
-#define DRIVER_VERSION "v5.3"
-#define DRIVER_AUTHOR "Roman Weissgaerber <weissg@vienna.at>, David Brownell"
-#define DRIVER_DESC "USB OHCI Host Controller Driver"
-
-/* For initializing controller (mask in an HCFS mode too) */
-#define	OHCI_CONTROL_INIT \
-	(OHCI_CTRL_CBSR & 0x3) | OHCI_CTRL_IE | OHCI_CTRL_PLE
-
-#define OHCI_UNLINK_TIMEOUT	(HZ / 10)
-
-static LIST_HEAD (ohci_hcd_list);
-static spinlock_t usb_ed_lock = SPIN_LOCK_UNLOCKED;
-
-
-/*-------------------------------------------------------------------------*/
-
-/* AMD-756 (D2 rev) reports corrupt register contents in some cases.
- * The erratum (#4) description is incorrect.  AMD's workaround waits
- * till some bits (mostly reserved) are clear; ok for all revs.
- */
-#define read_roothub(hc, register, mask) ({ \
-	u32 temp = readl (&hc->regs->roothub.register); \
-	if (hc->flags & OHCI_QUIRK_AMD756) \
-		while (temp & mask) \
-			temp = readl (&hc->regs->roothub.register); \
-	temp; })
-
-static u32 roothub_a (struct ohci *hc)
-	{ return read_roothub (hc, a, 0xfc0fe000); }
-static inline u32 roothub_b (struct ohci *hc)
-	{ return readl (&hc->regs->roothub.b); }
-static inline u32 roothub_status (struct ohci *hc)
-	{ return readl (&hc->regs->roothub.status); }
-static u32 roothub_portstatus (struct ohci *hc, int i)
-	{ return read_roothub (hc, portstatus [i], 0xffe0fce0); }
-
-
-/*-------------------------------------------------------------------------*
- * URB support functions 
- *-------------------------------------------------------------------------*/ 
- 
-/* free HCD-private data associated with this URB */
-
-static void urb_free_priv (struct ohci *hc, urb_priv_t * urb_priv)
-{
-	int		i;
-	int		last = urb_priv->length - 1;
-	int		len;
-	int		dir;
-	struct td	*td;
-
-	if (last >= 0) {
-
-		/* ISOC, BULK, INTR data buffer starts at td 0 
-		 * CTRL setup starts at td 0 */
-		td = urb_priv->td [0];
-
-		len = td->urb->transfer_buffer_length,
-		dir = usb_pipeout (td->urb->pipe)
-					? PCI_DMA_TODEVICE
-					: PCI_DMA_FROMDEVICE;
-
-		/* unmap CTRL URB setup */
-		if (usb_pipecontrol (td->urb->pipe)) {
-			pci_unmap_single (hc->ohci_dev, 
-					td->data_dma, 8, PCI_DMA_TODEVICE);
-			
-			/* CTRL data buffer starts at td 1 if len > 0 */
-			if (len && last > 0)
-				td = urb_priv->td [1]; 		
-		} 
-
-		/* unmap data buffer */
-		if (len && td->data_dma)
-			pci_unmap_single (hc->ohci_dev, td->data_dma, len, dir);
-		
-		for (i = 0; i <= last; i++) {
-			td = urb_priv->td [i];
-			if (td)
-				td_free (hc, td);
-		}
-	}
-
-	kfree (urb_priv);
-}
- 
-static void urb_rm_priv_locked (struct urb * urb) 
-{
-	urb_priv_t * urb_priv = urb->hcpriv;
-	
-	if (urb_priv) {
-		urb->hcpriv = NULL;
-
-#ifdef	DO_TIMEOUTS
-		if (urb->timeout) {
-			list_del (&urb->urb_list);
-			urb->timeout -= jiffies;
-		}
-#endif
-
-		/* Release int/iso bandwidth */
-		if (urb->bandwidth) {
-			switch (usb_pipetype(urb->pipe)) {
-			case PIPE_INTERRUPT:
-				usb_release_bandwidth (urb->dev, urb, 0);
-				break;
-			case PIPE_ISOCHRONOUS:
-				usb_release_bandwidth (urb->dev, urb, 1);
-				break;
-			default:
-				break;
-			}
-		}
-
-		urb_free_priv ((struct ohci *)urb->dev->bus->hcpriv, urb_priv);
-		usb_dec_dev_use (urb->dev);
-		urb->dev = NULL;
-	}
-}
-
-static void urb_rm_priv (struct urb * urb)
-{
-	unsigned long flags;
-
-	spin_lock_irqsave (&usb_ed_lock, flags);
-	urb_rm_priv_locked (urb);
-	spin_unlock_irqrestore (&usb_ed_lock, flags);
-}
-
-/*-------------------------------------------------------------------------*/
- 
-#ifdef DEBUG
-static int sohci_get_current_frame_number (struct usb_device * dev);
-
-/* debug| print the main components of an URB     
- * small: 0) header + data packets 1) just header */
- 
-static void urb_print (struct urb * urb, char * str, int small)
-{
-	unsigned int pipe= urb->pipe;
-	
-	if (!urb->dev || !urb->dev->bus) {
-		dbg("%s URB: no dev", str);
-		return;
-	}
-	
-#ifndef	OHCI_VERBOSE_DEBUG
-	if (urb->status != 0)
-#endif
-	dbg("%s URB:[%4x] dev:%2d,ep:%2d-%c,type:%s,flags:%4x,len:%d/%d,stat:%d(%x)", 
-			str,
-		 	sohci_get_current_frame_number (urb->dev), 
-		 	usb_pipedevice (pipe),
-		 	usb_pipeendpoint (pipe), 
-		 	usb_pipeout (pipe)? 'O': 'I',
-		 	usb_pipetype (pipe) < 2? (usb_pipeint (pipe)? "INTR": "ISOC"):
-		 		(usb_pipecontrol (pipe)? "CTRL": "BULK"),
-		 	urb->transfer_flags, 
-		 	urb->actual_length, 
-		 	urb->transfer_buffer_length,
-		 	urb->status, urb->status);
-#ifdef	OHCI_VERBOSE_DEBUG
-	if (!small) {
-		int i, len;
-
-		if (usb_pipecontrol (pipe)) {
-			printk (KERN_DEBUG __FILE__ ": cmd(8):");
-			for (i = 0; i < 8 ; i++) 
-				printk (" %02x", ((__u8 *) urb->setup_packet) [i]);
-			printk ("\n");
-		}
-		if (urb->transfer_buffer_length > 0 && urb->transfer_buffer) {
-			printk (KERN_DEBUG __FILE__ ": data(%d/%d):", 
-				urb->actual_length, 
-				urb->transfer_buffer_length);
-			len = usb_pipeout (pipe)? 
-						urb->transfer_buffer_length: urb->actual_length;
-			for (i = 0; i < 16 && i < len; i++) 
-				printk (" %02x", ((__u8 *) urb->transfer_buffer) [i]);
-			printk ("%s stat:%d\n", i < len? "...": "", urb->status);
-		}
-	} 
-#endif
-}
-
-/* just for debugging; prints non-empty branches of the int ed tree inclusive iso eds*/
-void ep_print_int_eds (ohci_t * ohci, char * str) {
-	int i, j;
-	 __u32 * ed_p;
-	for (i= 0; i < 32; i++) {
-		j = 5;
-		ed_p = &(ohci->hcca->int_table [i]);
-		if (*ed_p == 0)
-		    continue;
-		printk (KERN_DEBUG __FILE__ ": %s branch int %2d(%2x):", str, i, i);
-		while (*ed_p != 0 && j--) {
-			ed_t *ed = dma_to_ed (ohci, le32_to_cpup(ed_p));
-			printk (" ed: %4x;", ed->hwINFO);
-			ed_p = &ed->hwNextED;
-		}
-		printk ("\n");
-	}
-}
-
-
-static void ohci_dump_intr_mask (char *label, __u32 mask)
-{
-	dbg ("%s: 0x%08x%s%s%s%s%s%s%s%s%s",
-		label,
-		mask,
-		(mask & OHCI_INTR_MIE) ? " MIE" : "",
-		(mask & OHCI_INTR_OC) ? " OC" : "",
-		(mask & OHCI_INTR_RHSC) ? " RHSC" : "",
-		(mask & OHCI_INTR_FNO) ? " FNO" : "",
-		(mask & OHCI_INTR_UE) ? " UE" : "",
-		(mask & OHCI_INTR_RD) ? " RD" : "",
-		(mask & OHCI_INTR_SF) ? " SF" : "",
-		(mask & OHCI_INTR_WDH) ? " WDH" : "",
-		(mask & OHCI_INTR_SO) ? " SO" : ""
-		);
-}
-
-static void maybe_print_eds (char *label, __u32 value)
-{
-	if (value)
-		dbg ("%s %08x", label, value);
-}
-
-static char *hcfs2string (int state)
-{
-	switch (state) {
-		case OHCI_USB_RESET:	return "reset";
-		case OHCI_USB_RESUME:	return "resume";
-		case OHCI_USB_OPER:	return "operational";
-		case OHCI_USB_SUSPEND:	return "suspend";
-	}
-	return "?";
-}
-
-// dump control and status registers
-static void ohci_dump_status (ohci_t *controller)
-{
-	struct ohci_regs	*regs = controller->regs;
-	__u32			temp;
-
-	temp = readl (&regs->revision) & 0xff;
-	if (temp != 0x10)
-		dbg ("spec %d.%d", (temp >> 4), (temp & 0x0f));
-
-	temp = readl (&regs->control);
-	dbg ("control: 0x%08x%s%s%s HCFS=%s%s%s%s%s CBSR=%d", temp,
-		(temp & OHCI_CTRL_RWE) ? " RWE" : "",
-		(temp & OHCI_CTRL_RWC) ? " RWC" : "",
-		(temp & OHCI_CTRL_IR) ? " IR" : "",
-		hcfs2string (temp & OHCI_CTRL_HCFS),
-		(temp & OHCI_CTRL_BLE) ? " BLE" : "",
-		(temp & OHCI_CTRL_CLE) ? " CLE" : "",
-		(temp & OHCI_CTRL_IE) ? " IE" : "",
-		(temp & OHCI_CTRL_PLE) ? " PLE" : "",
-		temp & OHCI_CTRL_CBSR
-		);
-
-	temp = readl (&regs->cmdstatus);
-	dbg ("cmdstatus: 0x%08x SOC=%d%s%s%s%s", temp,
-		(temp & OHCI_SOC) >> 16,
-		(temp & OHCI_OCR) ? " OCR" : "",
-		(temp & OHCI_BLF) ? " BLF" : "",
-		(temp & OHCI_CLF) ? " CLF" : "",
-		(temp & OHCI_HCR) ? " HCR" : ""
-		);
-
-	ohci_dump_intr_mask ("intrstatus", readl (&regs->intrstatus));
-	ohci_dump_intr_mask ("intrenable", readl (&regs->intrenable));
-	// intrdisable always same as intrenable
-	// ohci_dump_intr_mask ("intrdisable", readl (&regs->intrdisable));
-
-	maybe_print_eds ("ed_periodcurrent", readl (&regs->ed_periodcurrent));
-
-	maybe_print_eds ("ed_controlhead", readl (&regs->ed_controlhead));
-	maybe_print_eds ("ed_controlcurrent", readl (&regs->ed_controlcurrent));
-
-	maybe_print_eds ("ed_bulkhead", readl (&regs->ed_bulkhead));
-	maybe_print_eds ("ed_bulkcurrent", readl (&regs->ed_bulkcurrent));
-
-	maybe_print_eds ("donehead", readl (&regs->donehead));
-}
-
-static void ohci_dump_roothub (ohci_t *controller, int verbose)
-{
-	__u32			temp, ndp, i;
-
-	temp = roothub_a (controller);
-	if (temp == ~(u32)0)
-		return;
-	ndp = (temp & RH_A_NDP);
-
-	if (verbose) {
-		dbg ("roothub.a: %08x POTPGT=%d%s%s%s%s%s NDP=%d", temp,
-			((temp & RH_A_POTPGT) >> 24) & 0xff,
-			(temp & RH_A_NOCP) ? " NOCP" : "",
-			(temp & RH_A_OCPM) ? " OCPM" : "",
-			(temp & RH_A_DT) ? " DT" : "",
-			(temp & RH_A_NPS) ? " NPS" : "",
-			(temp & RH_A_PSM) ? " PSM" : "",
-			ndp
-			);
-		temp = roothub_b (controller);
-		dbg ("roothub.b: %08x PPCM=%04x DR=%04x",
-			temp,
-			(temp & RH_B_PPCM) >> 16,
-			(temp & RH_B_DR)
-			);
-		temp = roothub_status (controller);
-		dbg ("roothub.status: %08x%s%s%s%s%s%s",
-			temp,
-			(temp & RH_HS_CRWE) ? " CRWE" : "",
-			(temp & RH_HS_OCIC) ? " OCIC" : "",
-			(temp & RH_HS_LPSC) ? " LPSC" : "",
-			(temp & RH_HS_DRWE) ? " DRWE" : "",
-			(temp & RH_HS_OCI) ? " OCI" : "",
-			(temp & RH_HS_LPS) ? " LPS" : ""
-			);
-	}
-	
-	for (i = 0; i < ndp; i++) {
-		temp = roothub_portstatus (controller, i);
-		dbg ("roothub.portstatus [%d] = 0x%08x%s%s%s%s%s%s%s%s%s%s%s%s",
-			i,
-			temp,
-			(temp & RH_PS_PRSC) ? " PRSC" : "",
-			(temp & RH_PS_OCIC) ? " OCIC" : "",
-			(temp & RH_PS_PSSC) ? " PSSC" : "",
-			(temp & RH_PS_PESC) ? " PESC" : "",
-			(temp & RH_PS_CSC) ? " CSC" : "",
-
-			(temp & RH_PS_LSDA) ? " LSDA" : "",
-			(temp & RH_PS_PPS) ? " PPS" : "",
-			(temp & RH_PS_PRS) ? " PRS" : "",
-			(temp & RH_PS_POCI) ? " POCI" : "",
-			(temp & RH_PS_PSS) ? " PSS" : "",
-
-			(temp & RH_PS_PES) ? " PES" : "",
-			(temp & RH_PS_CCS) ? " CCS" : ""
-			);
-	}
-}
-
-static void ohci_dump (ohci_t *controller, int verbose)
-{
-	dbg ("OHCI controller usb-%s state", controller->ohci_dev->slot_name);
-
-	// dumps some of the state we know about
-	ohci_dump_status (controller);
-	if (verbose)
-		ep_print_int_eds (controller, "hcca");
-	dbg ("hcca frame #%04x", controller->hcca->frame_no);
-	ohci_dump_roothub (controller, 1);
-}
-
-
-#endif
-
-/*-------------------------------------------------------------------------*
- * Interface functions (URB)
- *-------------------------------------------------------------------------*/
-
-/* return a request to the completion handler */
- 
-static int sohci_return_urb (struct ohci *hc, struct urb * urb)
-{
-	urb_priv_t * urb_priv = urb->hcpriv;
-	struct urb * urbt;
-	unsigned long flags;
-	int i;
-	
-	if (!urb_priv)
-		return -1; /* urb already unlinked */
-
-	/* just to be sure */
-	if (!urb->complete) {
-		urb_rm_priv (urb);
-		return -1;
-	}
-	
-#ifdef DEBUG
-	urb_print (urb, "RET", usb_pipeout (urb->pipe));
-#endif
-
-	switch (usb_pipetype (urb->pipe)) {
-  		case PIPE_INTERRUPT:
-			pci_unmap_single (hc->ohci_dev,
-				urb_priv->td [0]->data_dma,
-				urb->transfer_buffer_length,
-				usb_pipeout (urb->pipe)
-					? PCI_DMA_TODEVICE
-					: PCI_DMA_FROMDEVICE);
-			urb->complete (urb);
-
-			/* implicitly requeued */
-  			urb->actual_length = 0;
-			urb->status = -EINPROGRESS;
-			td_submit_urb (urb);
-  			break;
-  			
-		case PIPE_ISOCHRONOUS:
-			for (urbt = urb->next; urbt && (urbt != urb); urbt = urbt->next);
-			if (urbt) { /* send the reply and requeue URB */	
-				pci_unmap_single (hc->ohci_dev,
-					urb_priv->td [0]->data_dma,
-					urb->transfer_buffer_length,
-					usb_pipeout (urb->pipe)
-						? PCI_DMA_TODEVICE
-						: PCI_DMA_FROMDEVICE);
-				urb->complete (urb);
-				spin_lock_irqsave (&usb_ed_lock, flags);
-				urb->actual_length = 0;
-  				urb->status = USB_ST_URB_PENDING;
-  				urb->start_frame = urb_priv->ed->last_iso + 1;
-  				if (urb_priv->state != URB_DEL) {
-  					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_submit_urb (urb);
-  				}
-  				spin_unlock_irqrestore (&usb_ed_lock, flags);
-  				
-  			} else { /* unlink URB, call complete */
-				urb_rm_priv (urb);
-				urb->complete (urb); 	
-			}		
-			break;
-  				
-		case PIPE_BULK:
-		case PIPE_CONTROL: /* unlink URB, call complete */
-			urb_rm_priv (urb);
-			urb->complete (urb);	
-			break;
-	}
-	return 0;
-}
-
-/*-------------------------------------------------------------------------*/
-
-/* get a transfer request */
- 
-static int sohci_submit_urb (struct urb * urb)
-{
-	ohci_t * ohci;
-	ed_t * ed;
-	urb_priv_t * urb_priv;
-	unsigned int pipe = urb->pipe;
-	int maxps = usb_maxpacket (urb->dev, pipe, usb_pipeout (pipe));
-	int i, size = 0;
-	unsigned long flags;
-	int bustime = 0;
-	int mem_flags = ALLOC_FLAGS;
-	
-	if (!urb->dev || !urb->dev->bus)
-		return -ENODEV;
-	
-	if (urb->hcpriv)			/* urb already in use */
-		return -EINVAL;
-
-//	if(usb_endpoint_halted (urb->dev, usb_pipeendpoint (pipe), usb_pipeout (pipe))) 
-//		return -EPIPE;
-	
-	usb_inc_dev_use (urb->dev);
-	ohci = (ohci_t *) urb->dev->bus->hcpriv;
-	
-#ifdef DEBUG
-	urb_print (urb, "SUB", usb_pipein (pipe));
-#endif
-	
-	/* handle a request to the virtual root hub */
-	if (usb_pipedevice (pipe) == ohci->rh.devnum) 
-		return rh_submit_urb (urb);
-	
-	/* when controller's hung, permit only roothub cleanup attempts
-	 * such as powering down ports */
-	if (ohci->disabled) {
-		usb_dec_dev_use (urb->dev);	
-		return -ESHUTDOWN;
-	}
-
-	/* every endpoint has a ed, locate and fill it */
-	if (!(ed = ep_add_ed (urb->dev, pipe, urb->interval, 1, mem_flags))) {
-		usb_dec_dev_use (urb->dev);	
-		return -ENOMEM;
-	}
-
-	/* for the private part of the URB we need the number of TDs (size) */
-	switch (usb_pipetype (pipe)) {
-		case PIPE_BULK:	/* one TD for every 4096 Byte */
-			size = (urb->transfer_buffer_length - 1) / 4096 + 1;
-
-			/* If the transfer size is multiple of the pipe mtu,
-			 * we may need an extra TD to create a empty frame
-			 * Jean II */
-			if ((urb->transfer_flags & USB_ZERO_PACKET) &&
-			    usb_pipeout (pipe) &&
-			    (urb->transfer_buffer_length != 0) && 
-			    ((urb->transfer_buffer_length % maxps) == 0))
-				size++;
-			break;
-		case PIPE_ISOCHRONOUS: /* number of packets from URB */
-			size = urb->number_of_packets;
-			if (size <= 0) {
-				usb_dec_dev_use (urb->dev);	
-				return -EINVAL;
-			}
-			for (i = 0; i < urb->number_of_packets; i++) {
-  				urb->iso_frame_desc[i].actual_length = 0;
-  				urb->iso_frame_desc[i].status = -EXDEV;
-  			}
-			break;
-		case PIPE_CONTROL: /* 1 TD for setup, 1 for ACK and 1 for every 4096 B */
-			size = (urb->transfer_buffer_length == 0)? 2: 
-						(urb->transfer_buffer_length - 1) / 4096 + 3;
-			break;
-		case PIPE_INTERRUPT: /* one TD */
-			size = 1;
-			break;
-	}
-
-	/* allocate the private part of the URB */
-	urb_priv = kmalloc (sizeof (urb_priv_t) + size * sizeof (td_t *), 
-							in_interrupt() ? GFP_ATOMIC : GFP_KERNEL);
-	if (!urb_priv) {
-		usb_dec_dev_use (urb->dev);	
-		return -ENOMEM;
-	}
-	memset (urb_priv, 0, sizeof (urb_priv_t) + size * sizeof (td_t *));
-	
-	/* fill the private part of the URB */
-	urb_priv->length = size;
-	urb_priv->ed = ed;	
-
-	/* allocate the TDs (updating hash chains) */
-	spin_lock_irqsave (&usb_ed_lock, flags);
-	for (i = 0; i < size; i++) { 
-		urb_priv->td[i] = td_alloc (ohci, SLAB_ATOMIC);
-		if (!urb_priv->td[i]) {
-			urb_priv->length = i;
-			urb_free_priv (ohci, urb_priv);
-			spin_unlock_irqrestore (&usb_ed_lock, flags);
-			usb_dec_dev_use (urb->dev);	
-			return -ENOMEM;
-		}
-	}	
-
-	if (ed->state == ED_NEW || (ed->state & ED_DEL)) {
-		urb_free_priv (ohci, urb_priv);
-		spin_unlock_irqrestore (&usb_ed_lock, flags);
-		usb_dec_dev_use (urb->dev);	
-		return -EINVAL;
-	}
-	
-	/* allocate and claim bandwidth if needed; ISO
-	 * needs start frame index if it was't provided.
-	 */
-	switch (usb_pipetype (pipe)) {
-		case PIPE_ISOCHRONOUS:
-			if (urb->transfer_flags & USB_ISO_ASAP) { 
-				urb->start_frame = ((ed->state == ED_OPER)
-					? (ed->last_iso + 1)
-					: (le16_to_cpu (ohci->hcca->frame_no) + 10)) & 0xffff;
-			}	
-			/* FALLTHROUGH */
-		case PIPE_INTERRUPT:
-			if (urb->bandwidth == 0) {
-				bustime = usb_check_bandwidth (urb->dev, urb);
-			}
-			if (bustime < 0) {
-				urb_free_priv (ohci, urb_priv);
-				spin_unlock_irqrestore (&usb_ed_lock, flags);
-				usb_dec_dev_use (urb->dev);	
-				return bustime;
-			}
-			usb_claim_bandwidth (urb->dev, urb, bustime, usb_pipeisoc (urb->pipe));
-#ifdef	DO_TIMEOUTS
-			urb->timeout = 0;
-#endif
-	}
-
-	urb->actual_length = 0;
-	urb->hcpriv = urb_priv;
-	urb->status = USB_ST_URB_PENDING;
-
-	/* link the ed into a chain if is not already */
-	if (ed->state != ED_OPER)
-		ep_link (ohci, ed);
-
-	/* fill the TDs and link it to the ed */
-	td_submit_urb (urb);
-
-#ifdef	DO_TIMEOUTS
-	/* maybe add to ordered list of timeouts */
-	if (urb->timeout) {
-		struct list_head	*entry;
-
-		urb->timeout += jiffies;
-
-		list_for_each (entry, &ohci->timeout_list) {
-			struct urb	*next_urb;
-
-			next_urb = list_entry (entry, struct urb, urb_list);
-			if (time_after_eq (urb->timeout, next_urb->timeout))
-				break;
-		}
-		list_add (&urb->urb_list, entry);
-
-		/* drive timeouts by SF (messy, but works) */
-		writel (OHCI_INTR_SF, &ohci->regs->intrenable);	
-	}
-#endif
-
-	spin_unlock_irqrestore (&usb_ed_lock, flags);
-
-	return 0;	
-}
-
-/*-------------------------------------------------------------------------*/
-
-/* deactivate all TDs and remove the private part of the URB */
-/* interrupt callers must use async unlink mode */
-
-static int sohci_unlink_urb (struct urb * urb)
-{
-	unsigned long flags;
-	ohci_t * ohci;
-	
-	if (!urb) /* just to be sure */ 
-		return -EINVAL;
-		
-	if (!urb->dev || !urb->dev->bus)
-		return -ENODEV;
-
-	ohci = (ohci_t *) urb->dev->bus->hcpriv; 
-
-#ifdef DEBUG
-	urb_print (urb, "UNLINK", 1);
-#endif		  
-
-	/* handle a request to the virtual root hub */
-	if (usb_pipedevice (urb->pipe) == ohci->rh.devnum)
-		return rh_unlink_urb (urb);
-
-	if (urb->hcpriv && (urb->status == USB_ST_URB_PENDING)) { 
-		if (!ohci->disabled) {
-			urb_priv_t  * urb_priv;
-
-			/* interrupt code may not sleep; it must use
-			 * async status return to unlink pending urbs.
-			 */
-			if (!(urb->transfer_flags & USB_ASYNC_UNLINK)
-					&& in_interrupt ()) {
-				err ("bug in call from %p; use async!",
-					__builtin_return_address(0));
-				return -EWOULDBLOCK;
-			}
-
-			/* flag the urb and its TDs for deletion in some
-			 * upcoming SF interrupt delete list processing
-			 */
-			spin_lock_irqsave (&usb_ed_lock, flags);
-			urb_priv = urb->hcpriv;
-
-			if (!urb_priv || (urb_priv->state == URB_DEL)) {
-				spin_unlock_irqrestore (&usb_ed_lock, flags);
-				return 0;
-			}
-				
-			urb_priv->state = URB_DEL; 
-			ep_rm_ed (urb->dev, urb_priv->ed);
-			urb_priv->ed->state |= ED_URB_DEL;
-
-			if (!(urb->transfer_flags & USB_ASYNC_UNLINK)) {
-				DECLARE_WAIT_QUEUE_HEAD (unlink_wakeup); 
-				DECLARE_WAITQUEUE (wait, current);
-				int timeout = OHCI_UNLINK_TIMEOUT;
-
-				add_wait_queue (&unlink_wakeup, &wait);
-				urb_priv->wait = &unlink_wakeup;
-				spin_unlock_irqrestore (&usb_ed_lock, flags);
-
-				/* wait until all TDs are deleted */
-				set_current_state(TASK_UNINTERRUPTIBLE);
-				while (timeout && (urb->status == USB_ST_URB_PENDING))
-					timeout = schedule_timeout (timeout);
-				set_current_state(TASK_RUNNING);
-				remove_wait_queue (&unlink_wakeup, &wait); 
-				if (urb->status == USB_ST_URB_PENDING) {
-					err ("unlink URB timeout");
-					return -ETIMEDOUT;
-				}
-			} else {
-				/* usb_dec_dev_use done in dl_del_list() */
-				urb->status = -EINPROGRESS;
-				spin_unlock_irqrestore (&usb_ed_lock, flags);
-				return -EINPROGRESS;
-			}
-		} else {
-			urb_rm_priv (urb);
-			if (urb->transfer_flags & USB_ASYNC_UNLINK) {
-				urb->status = -ECONNRESET;
-				if (urb->complete)
-					urb->complete (urb); 
-			} else 
-				urb->status = -ENOENT;
-		}	
-	}	
-	return 0;
-}
-
-/*-------------------------------------------------------------------------*/
-
-/* allocate private data space for a usb device */
-
-static int sohci_alloc_dev (struct usb_device *usb_dev)
-{
-	struct ohci_device * dev;
-
-	dev = dev_alloc ((struct ohci *) usb_dev->bus->hcpriv, ALLOC_FLAGS);
-	if (!dev)
-		return -ENOMEM;
-
-	usb_dev->hcpriv = dev;
-	return 0;
-}
-
-/*-------------------------------------------------------------------------*/
-
-/* may be called from interrupt context */
-/* frees private data space of usb device */
-  
-static int sohci_free_dev (struct usb_device * usb_dev)
-{
-	unsigned long flags;
-	int i, cnt = 0;
-	ed_t * ed;
-	struct ohci_device * dev = usb_to_ohci (usb_dev);
-	ohci_t * ohci = usb_dev->bus->hcpriv;
-	
-	if (!dev)
-		return 0;
-	
-	if (usb_dev->devnum >= 0) {
-	
-		/* driver disconnects should have unlinked all urbs
-		 * (freeing all the TDs, unlinking EDs) but we need
-		 * to defend against bugs that prevent that.
-		 */
-		spin_lock_irqsave (&usb_ed_lock, flags);	
-		for(i = 0; i < NUM_EDS; i++) {
-  			ed = &(dev->ed[i]);
-  			if (ed->state != ED_NEW) {
-  				if (ed->state == ED_OPER) {
-					/* driver on that interface didn't unlink an urb */
-					dbg ("driver usb-%s dev %d ed 0x%x unfreed URB",
-						ohci->ohci_dev->slot_name, usb_dev->devnum, i);
-					ep_unlink (ohci, ed);
-				}
-  				ep_rm_ed (usb_dev, ed);
-  				ed->state = ED_DEL;
-  				cnt++;
-  			}
-  		}
-  		spin_unlock_irqrestore (&usb_ed_lock, flags);
-  		
-		/* if the controller is running, tds for those unlinked
-		 * urbs get freed by dl_del_list at the next SF interrupt
-		 */
-		if (cnt > 0) {
-
-			if (ohci->disabled) {
-				/* FIXME: Something like this should kick in,
-				 * though it's currently an exotic case ...
-				 * the controller won't ever be touching
-				 * these lists again!!
-				dl_del_list (ohci,
-					le16_to_cpu (ohci->hcca->frame_no) & 1);
-				 */
-				warn ("TD leak, %d", cnt);
-
-			} else if (!in_interrupt ()) {
-				DECLARE_WAIT_QUEUE_HEAD (freedev_wakeup); 
-				DECLARE_WAITQUEUE (wait, current);
-				int timeout = OHCI_UNLINK_TIMEOUT;
-
-				/* SF interrupt handler calls dl_del_list */
-				add_wait_queue (&freedev_wakeup, &wait);
-				dev->wait = &freedev_wakeup;
-				set_current_state(TASK_UNINTERRUPTIBLE);
-				while (timeout && dev->ed_cnt)
-					timeout = schedule_timeout (timeout);
-				set_current_state(TASK_RUNNING);
-				remove_wait_queue (&freedev_wakeup, &wait);
-				if (dev->ed_cnt) {
-					err ("free device %d timeout", usb_dev->devnum);
-					return -ETIMEDOUT;
-				}
-			} else {
-				/* likely some interface's driver has a refcount bug */
-				err ("bus %s devnum %d deletion in interrupt",
-					ohci->ohci_dev->slot_name, usb_dev->devnum);
-				BUG ();
-			}
-		}
-	}
-
-	/* free device, and associated EDs */
-	dev_free (ohci, dev);
-
-	return 0;
-}
-
-/*-------------------------------------------------------------------------*/
-
-/* tell us the current USB frame number */
-
-static int sohci_get_current_frame_number (struct usb_device *usb_dev) 
-{
-	ohci_t * ohci = usb_dev->bus->hcpriv;
-	
-	return le16_to_cpu (ohci->hcca->frame_no);
-}
-
-/*-------------------------------------------------------------------------*/
-
-struct usb_operations sohci_device_operations = {
-	sohci_alloc_dev,
-	sohci_free_dev,
-	sohci_get_current_frame_number,
-	sohci_submit_urb,
-	sohci_unlink_urb
-};
-
-/*-------------------------------------------------------------------------*
- * ED handling functions
- *-------------------------------------------------------------------------*/  
-		
-/* search for the right branch to insert an interrupt ed into the int tree 
- * do some load ballancing;
- * returns the branch and 
- * sets the interval to interval = 2^integer (ld (interval)) */
-
-static int ep_int_ballance (ohci_t * ohci, int interval, int load)
-{
-	int i, branch = 0;
-   
-	/* search for the least loaded interrupt endpoint branch of all 32 branches */
-	for (i = 0; i < 32; i++) 
-		if (ohci->ohci_int_load [branch] > ohci->ohci_int_load [i]) branch = i; 
-  
-	branch = branch % interval;
-	for (i = branch; i < 32; i += interval) ohci->ohci_int_load [i] += load;
-
-	return branch;
-}
-
-/*-------------------------------------------------------------------------*/
-
-/*  2^int( ld (inter)) */
-
-static int ep_2_n_interval (int inter)
-{	
-	int i;
-	for (i = 0; ((inter >> i) > 1 ) && (i < 5); i++); 
-	return 1 << i;
-}
-
-/*-------------------------------------------------------------------------*/
-
-/* the int tree is a binary tree 
- * in order to process it sequentially the indexes of the branches have to be mapped 
- * the mapping reverses the bits of a word of num_bits length */
- 
-static int ep_rev (int num_bits, int word)
-{
-	int i, wout = 0;
-
-	for (i = 0; i < num_bits; i++) wout |= (((word >> i) & 1) << (num_bits - i - 1));
-	return wout;
-}
-
-/*-------------------------------------------------------------------------*/
-
-/* link an ed into one of the HC chains */
-
-static int ep_link (ohci_t * ohci, ed_t * edi)
-{	 
-	int int_branch;
-	int i;
-	int inter;
-	int interval;
-	int load;
-	__u32 * ed_p;
-	volatile ed_t * ed = edi;
-	
-	ed->state = ED_OPER;
-	
-	switch (ed->type) {
-	case PIPE_CONTROL:
-		ed->hwNextED = 0;
-		if (ohci->ed_controltail == NULL) {
-			writel (ed->dma, &ohci->regs->ed_controlhead);
-		} else {
-			ohci->ed_controltail->hwNextED = cpu_to_le32 (ed->dma);
-		}
-		ed->ed_prev = ohci->ed_controltail;
-		if (!ohci->ed_controltail && !ohci->ed_rm_list[0] &&
-			!ohci->ed_rm_list[1] && !ohci->sleeping) {
-			ohci->hc_control |= OHCI_CTRL_CLE;
-			writel (ohci->hc_control, &ohci->regs->control);
-		}
-		ohci->ed_controltail = edi;	  
-		break;
-		
-	case PIPE_BULK:
-		ed->hwNextED = 0;
-		if (ohci->ed_bulktail == NULL) {
-			writel (ed->dma, &ohci->regs->ed_bulkhead);
-		} else {
-			ohci->ed_bulktail->hwNextED = cpu_to_le32 (ed->dma);
-		}
-		ed->ed_prev = ohci->ed_bulktail;
-		if (!ohci->ed_bulktail && !ohci->ed_rm_list[0] &&
-			!ohci->ed_rm_list[1] && !ohci->sleeping) {
-			ohci->hc_control |= OHCI_CTRL_BLE;
-			writel (ohci->hc_control, &ohci->regs->control);
-		}
-		ohci->ed_bulktail = edi;	  
-		break;
-		
-	case PIPE_INTERRUPT:
-		load = ed->int_load;
-		interval = ep_2_n_interval (ed->int_period);
-		ed->int_interval = interval;
-		int_branch = ep_int_ballance (ohci, interval, load);
-		ed->int_branch = int_branch;
-		
-		for (i = 0; i < ep_rev (6, interval); i += inter) {
-			inter = 1;
-			for (ed_p = &(ohci->hcca->int_table[ep_rev (5, i) + int_branch]); 
-				(*ed_p != 0) && ((dma_to_ed (ohci, le32_to_cpup (ed_p)))->int_interval >= interval); 
-				ed_p = &((dma_to_ed (ohci, le32_to_cpup (ed_p)))->hwNextED)) 
-					inter = ep_rev (6, (dma_to_ed (ohci, le32_to_cpup (ed_p)))->int_interval);
-			ed->hwNextED = *ed_p; 
-			*ed_p = cpu_to_le32 (ed->dma);
-		}
-#ifdef DEBUG
-		ep_print_int_eds (ohci, "LINK_INT");
-#endif
-		break;
-		
-	case PIPE_ISOCHRONOUS:
-		ed->hwNextED = 0;
-		ed->int_interval = 1;
-		if (ohci->ed_isotail != NULL) {
-			ohci->ed_isotail->hwNextED = cpu_to_le32 (ed->dma);
-			ed->ed_prev = ohci->ed_isotail;
-		} else {
-			for ( i = 0; i < 32; i += inter) {
-				inter = 1;
-				for (ed_p = &(ohci->hcca->int_table[ep_rev (5, i)]); 
-					*ed_p != 0; 
-					ed_p = &((dma_to_ed (ohci, le32_to_cpup (ed_p)))->hwNextED)) 
-						inter = ep_rev (6, (dma_to_ed (ohci, le32_to_cpup (ed_p)))->int_interval);
-				*ed_p = cpu_to_le32 (ed->dma);	
-			}	
-			ed->ed_prev = NULL;
-		}	
-		ohci->ed_isotail = edi;  
-#ifdef DEBUG
-		ep_print_int_eds (ohci, "LINK_ISO");
-#endif
-		break;
-	}	 	
-	return 0;
-}
-
-/*-------------------------------------------------------------------------*/
-
-/* scan the periodic table to find and unlink this ED */
-static void periodic_unlink (
-	struct ohci	*ohci,
-	struct ed	*ed,
-	unsigned	index,
-	unsigned	period
-) {
-	for (; index < NUM_INTS; index += period) {
-		__u32	*ed_p = &ohci->hcca->int_table [index];
-
-		/* ED might have been unlinked through another path */
-		while (*ed_p != 0) {
-			if ((dma_to_ed (ohci, le32_to_cpup (ed_p))) == ed) {
-				*ed_p = ed->hwNextED;		
-				break;
-			}
-			ed_p = & ((dma_to_ed (ohci,
-				le32_to_cpup (ed_p)))->hwNextED);
-		}
-	}	
-}
-
-/* unlink an ed from one of the HC chains. 
- * just the link to the ed is unlinked.
- * the link from the ed still points to another operational ed or 0
- * so the HC can eventually finish the processing of the unlinked ed */
-
-static int ep_unlink (ohci_t * ohci, ed_t * ed) 
-{
-	int i;
-
-	ed->hwINFO |= cpu_to_le32 (OHCI_ED_SKIP);
-
-	switch (ed->type) {
-	case PIPE_CONTROL:
-		if (ed->ed_prev == NULL) {
-			if (!ed->hwNextED) {
-				ohci->hc_control &= ~OHCI_CTRL_CLE;
-				writel (ohci->hc_control, &ohci->regs->control);
-			}
-			writel (le32_to_cpup (&ed->hwNextED), &ohci->regs->ed_controlhead);
-		} else {
-			ed->ed_prev->hwNextED = ed->hwNextED;
-		}
-		if (ohci->ed_controltail == ed) {
-			ohci->ed_controltail = ed->ed_prev;
-		} else {
-			(dma_to_ed (ohci, le32_to_cpup (&ed->hwNextED)))->ed_prev = ed->ed_prev;
-		}
-		break;
-      
-	case PIPE_BULK:
-		if (ed->ed_prev == NULL) {
-			if (!ed->hwNextED) {
-				ohci->hc_control &= ~OHCI_CTRL_BLE;
-				writel (ohci->hc_control, &ohci->regs->control);
-			}
-			writel (le32_to_cpup (&ed->hwNextED), &ohci->regs->ed_bulkhead);
-		} else {
-			ed->ed_prev->hwNextED = ed->hwNextED;
-		}
-		if (ohci->ed_bulktail == ed) {
-			ohci->ed_bulktail = ed->ed_prev;
-		} else {
-			(dma_to_ed (ohci, le32_to_cpup (&ed->hwNextED)))->ed_prev = ed->ed_prev;
-		}
-		break;
-      
-	case PIPE_INTERRUPT:
-		periodic_unlink (ohci, ed, 0, 1);
-		for (i = ed->int_branch; i < 32; i += ed->int_interval)
-		    ohci->ohci_int_load[i] -= ed->int_load;
-#ifdef DEBUG
-		ep_print_int_eds (ohci, "UNLINK_INT");
-#endif
-		break;
-		
-	case PIPE_ISOCHRONOUS:
-		if (ohci->ed_isotail == ed)
-			ohci->ed_isotail = ed->ed_prev;
-		if (ed->hwNextED != 0) 
-		    (dma_to_ed (ohci, le32_to_cpup (&ed->hwNextED)))
-		    	->ed_prev = ed->ed_prev;
-				    
-		if (ed->ed_prev != NULL)
-			ed->ed_prev->hwNextED = ed->hwNextED;
-		else
-			periodic_unlink (ohci, ed, 0, 1);
-#ifdef DEBUG
-		ep_print_int_eds (ohci, "UNLINK_ISO");
-#endif
-		break;
-	}
-	ed->state = ED_UNLINK;
-	return 0;
-}
-
-
-/*-------------------------------------------------------------------------*/
-
-/* add/reinit an endpoint; this should be done once at the usb_set_configuration command,
- * but the USB stack is a little bit stateless  so we do it at every transaction
- * if the state of the ed is ED_NEW then a dummy td is added and the state is changed to ED_UNLINK
- * in all other cases the state is left unchanged
- * the ed info fields are setted anyway even though most of them should not change */
- 
-static ed_t * ep_add_ed (
-	struct usb_device * usb_dev,
-	unsigned int pipe,
-	int interval,
-	int load,
-	int mem_flags
-)
-{
-   	ohci_t * ohci = usb_dev->bus->hcpriv;
-	td_t * td;
-	ed_t * ed_ret;
-	volatile ed_t * ed; 
-	unsigned long flags;
- 	
- 	
-	spin_lock_irqsave (&usb_ed_lock, flags);
-
-	ed = ed_ret = &(usb_to_ohci (usb_dev)->ed[(usb_pipeendpoint (pipe) << 1) | 
-			(usb_pipecontrol (pipe)? 0: usb_pipeout (pipe))]);
-
-	if ((ed->state & ED_DEL) || (ed->state & ED_URB_DEL)) {
-		/* pending delete request */
-		spin_unlock_irqrestore (&usb_ed_lock, flags);
-		return NULL;
-	}
-	
-	if (ed->state == ED_NEW) {
-		ed->hwINFO = cpu_to_le32 (OHCI_ED_SKIP); /* skip ed */
-  		/* dummy td; end of td list for ed */
-		td = td_alloc (ohci, SLAB_ATOMIC);
-		/* hash the ed for later reverse mapping */
- 		if (!td || !hash_add_ed (ohci, (ed_t *)ed)) {
-			/* out of memory */
-		        if (td)
-		            td_free(ohci, td);
-			spin_unlock_irqrestore (&usb_ed_lock, flags);
-			return NULL;
-		}
-		ed->hwTailP = cpu_to_le32 (td->td_dma);
-		ed->hwHeadP = ed->hwTailP;	
-		ed->state = ED_UNLINK;
-		ed->type = usb_pipetype (pipe);
-		usb_to_ohci (usb_dev)->ed_cnt++;
-	}
-
-	ohci->dev[usb_pipedevice (pipe)] = usb_dev;
-	
-	ed->hwINFO = cpu_to_le32 (usb_pipedevice (pipe)
-			| usb_pipeendpoint (pipe) << 7
-			| (usb_pipeisoc (pipe)? 0x8000: 0)
-			| (usb_pipecontrol (pipe)? 0: (usb_pipeout (pipe)? 0x800: 0x1000)) 
-			| usb_pipeslow (pipe) << 13
-			| usb_maxpacket (usb_dev, pipe, usb_pipeout (pipe)) << 16);
-  
-  	if (ed->type == PIPE_INTERRUPT && ed->state == ED_UNLINK) {
-  		ed->int_period = interval;
-  		ed->int_load = load;
-  	}
-  	
-	spin_unlock_irqrestore (&usb_ed_lock, flags);
-	return ed_ret; 
-}
-
-/*-------------------------------------------------------------------------*/
- 
-/* request the removal of an endpoint
- * put the ep on the rm_list and request a stop of the bulk or ctrl list 
- * real removal is done at the next start frame (SF) hardware interrupt */
- 
-static void ep_rm_ed (struct usb_device * usb_dev, ed_t * ed)
-{    
-	unsigned int frame;
-	ohci_t * ohci = usb_dev->bus->hcpriv;
-
-	if ((ed->state & ED_DEL) || (ed->state & ED_URB_DEL))
-		return;
-	
-	ed->hwINFO |= cpu_to_le32 (OHCI_ED_SKIP);
-
-	if (!ohci->disabled) {
-		switch (ed->type) {
-			case PIPE_CONTROL: /* stop control list */
-				ohci->hc_control &= ~OHCI_CTRL_CLE;
-				writel (ohci->hc_control, &ohci->regs->control); 
-  				break;
-			case PIPE_BULK: /* stop bulk list */
-				ohci->hc_control &= ~OHCI_CTRL_BLE;
-				writel (ohci->hc_control, &ohci->regs->control); 
-				break;
-		}
-	}
-
-	frame = le16_to_cpu (ohci->hcca->frame_no) & 0x1;
-	ed->ed_rm_list = ohci->ed_rm_list[frame];
-	ohci->ed_rm_list[frame] = ed;
-
-	if (!ohci->disabled && !ohci->sleeping) {
-		/* enable SOF interrupt */
-		writel (OHCI_INTR_SF, &ohci->regs->intrstatus);
-		writel (OHCI_INTR_SF, &ohci->regs->intrenable);
-	}
-}
-
-/*-------------------------------------------------------------------------*
- * TD handling functions
- *-------------------------------------------------------------------------*/
-
-/* enqueue next TD for this URB (OHCI spec 5.2.8.2) */
-
-static void
-td_fill (ohci_t * ohci, unsigned int info,
-	dma_addr_t data, int len,
-	struct urb * urb, int index)
-{
-	volatile td_t  * td, * td_pt;
-	urb_priv_t * urb_priv = urb->hcpriv;
-	
-	if (index >= urb_priv->length) {
-		err("internal OHCI error: TD index > length");
-		return;
-	}
-	
-	/* use this td as the next dummy */
-	td_pt = urb_priv->td [index];
-	td_pt->hwNextTD = 0;
-
-	/* fill the old dummy TD */
-	td = urb_priv->td [index] = dma_to_td (ohci,
-			le32_to_cpup (&urb_priv->ed->hwTailP) & ~0xf);
-
-	td->ed = urb_priv->ed;
-	td->next_dl_td = NULL;
-	td->index = index;
-	td->urb = urb; 
-	td->data_dma = data;
-	if (!len)
-		data = 0;
-
-	td->hwINFO = cpu_to_le32 (info);
-	if ((td->ed->type) == PIPE_ISOCHRONOUS) {
-		td->hwCBP = cpu_to_le32 (data & 0xFFFFF000);
-		td->ed->last_iso = info & 0xffff;
-	} else {
-		td->hwCBP = cpu_to_le32 (data); 
-	}			
-	if (data)
-		td->hwBE = cpu_to_le32 (data + len - 1);
-	else
-		td->hwBE = 0;
-	td->hwNextTD = cpu_to_le32 (td_pt->td_dma);
-	td->hwPSW [0] = cpu_to_le16 ((data & 0x0FFF) | 0xE000);
-
-	/* append to queue */
-	wmb();
-	td->ed->hwTailP = td->hwNextTD;
-}
-
-/*-------------------------------------------------------------------------*/
- 
-/* prepare all TDs of a transfer */
-
-static void td_submit_urb (struct urb * urb)
-{ 
-	urb_priv_t * urb_priv = urb->hcpriv;
-	ohci_t * ohci = (ohci_t *) urb->dev->bus->hcpriv;
-	dma_addr_t data;
-	int data_len = urb->transfer_buffer_length;
-	int maxps = usb_maxpacket (urb->dev, urb->pipe, usb_pipeout (urb->pipe));
-	int cnt = 0; 
-	__u32 info = 0;
-  	unsigned int toggle = 0;
-
-	/* OHCI handles the DATA-toggles itself, we just use the USB-toggle bits for reseting */
-  	if(usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe))) {
-  		toggle = TD_T_TOGGLE;
-	} else {
-  		toggle = TD_T_DATA0;
-		usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe), 1);
-	}
-	
-	urb_priv->td_cnt = 0;
-
-	if (data_len) {
-		data = pci_map_single (ohci->ohci_dev,
-			urb->transfer_buffer, data_len,
-			usb_pipeout (urb->pipe)
-				? PCI_DMA_TODEVICE
-				: PCI_DMA_FROMDEVICE
-			);
-	} else
-		data = 0;
-	
-	switch (usb_pipetype (urb->pipe)) {
-		case PIPE_BULK:
-			info = usb_pipeout (urb->pipe)? 
-				TD_CC | TD_DP_OUT : TD_CC | TD_DP_IN ;
-			while(data_len > 4096) {		
-				td_fill (ohci, info | (cnt? TD_T_TOGGLE:toggle), data, 4096, urb, cnt);
-				data += 4096; data_len -= 4096; cnt++;
-			}
-			info = usb_pipeout (urb->pipe)?
-				TD_CC | TD_DP_OUT : TD_CC | TD_R | TD_DP_IN ;
-			td_fill (ohci, info | (cnt? TD_T_TOGGLE:toggle), data, data_len, urb, cnt);
-			cnt++;
-
-			/* If the transfer size is multiple of the pipe mtu,
-			 * we may need an extra TD to create a empty frame
-			 * Note : another way to check this condition is
-			 * to test if(urb_priv->length > cnt) - Jean II */
-			if ((urb->transfer_flags & USB_ZERO_PACKET) &&
-			    usb_pipeout (urb->pipe) &&
-			    (urb->transfer_buffer_length != 0) && 
-			    ((urb->transfer_buffer_length % maxps) == 0)) {
-				td_fill (ohci, info | (cnt? TD_T_TOGGLE:toggle), 0, 0, urb, cnt);
-				cnt++;
-			}
-
-			if (!ohci->sleeping) {
-				wmb();
-				writel (OHCI_BLF, &ohci->regs->cmdstatus); /* start bulk list */
-			}
-			break;
-
-		case PIPE_INTERRUPT:
-			info = usb_pipeout (urb->pipe)? 
-				TD_CC | TD_DP_OUT | toggle: TD_CC | TD_R | TD_DP_IN | toggle;
-			td_fill (ohci, info, data, data_len, urb, cnt++);
-			break;
-
-		case PIPE_CONTROL:
-			info = TD_CC | TD_DP_SETUP | TD_T_DATA0;
-			td_fill (ohci, info,
-				pci_map_single (ohci->ohci_dev,
-					urb->setup_packet, 8,
-					PCI_DMA_TODEVICE),
-				8, urb, cnt++); 
-			if (data_len > 0) {  
-				info = usb_pipeout (urb->pipe)? 
-					TD_CC | TD_R | TD_DP_OUT | TD_T_DATA1 : TD_CC | TD_R | TD_DP_IN | TD_T_DATA1;
-				/* NOTE:  mishandles transfers >8K, some >4K */
-				td_fill (ohci, info, data, data_len, urb, cnt++);  
-			} 
-			info = usb_pipeout (urb->pipe)? 
- 				TD_CC | TD_DP_IN | TD_T_DATA1: TD_CC | TD_DP_OUT | TD_T_DATA1;
-			td_fill (ohci, info, data, 0, urb, cnt++);
-			if (!ohci->sleeping) {
-				wmb();
-				writel (OHCI_CLF, &ohci->regs->cmdstatus); /* start Control list */
-			}
-			break;
-
-		case PIPE_ISOCHRONOUS:
-			for (cnt = 0; cnt < urb->number_of_packets; cnt++) {
-				td_fill (ohci, TD_CC|TD_ISO | ((urb->start_frame + cnt) & 0xffff), 
-					data + urb->iso_frame_desc[cnt].offset, 
-					urb->iso_frame_desc[cnt].length, urb, cnt); 
-			}
-			break;
-	} 
-	if (urb_priv->length != cnt) 
-		dbg("TD LENGTH %d != CNT %d", urb_priv->length, cnt);
-}
-
-/*-------------------------------------------------------------------------*
- * Done List handling functions
- *-------------------------------------------------------------------------*/
-
-
-/* calculate the transfer length and update the urb */
-
-static void dl_transfer_length(td_t * td)
-{
-	__u32 tdINFO, tdBE, tdCBP;
- 	__u16 tdPSW;
- 	struct urb * urb = td->urb;
- 	urb_priv_t * urb_priv = urb->hcpriv;
-	int dlen = 0;
-	int cc = 0;
-	
-	tdINFO = le32_to_cpup (&td->hwINFO);
-  	tdBE   = le32_to_cpup (&td->hwBE);
-  	tdCBP  = le32_to_cpup (&td->hwCBP);
-
-
-  	if (tdINFO & TD_ISO) {
- 		tdPSW = le16_to_cpu (td->hwPSW[0]);
- 		cc = (tdPSW >> 12) & 0xF;
-		if (cc < 0xE)  {
-			if (usb_pipeout(urb->pipe)) {
-				dlen = urb->iso_frame_desc[td->index].length;
-			} else {
-				dlen = tdPSW & 0x3ff;
-			}
-			urb->actual_length += dlen;
-			urb->iso_frame_desc[td->index].actual_length = dlen;
-			if (!(urb->transfer_flags & USB_DISABLE_SPD) && (cc == TD_DATAUNDERRUN))
-				cc = TD_CC_NOERROR;
-					 
-			urb->iso_frame_desc[td->index].status = cc_to_error[cc];
-		}
-	} else { /* BULK, INT, CONTROL DATA */
-		if (!(usb_pipetype (urb->pipe) == PIPE_CONTROL && 
-				((td->index == 0) || (td->index == urb_priv->length - 1)))) {
- 			if (tdBE != 0) {
- 				if (td->hwCBP == 0)
-					urb->actual_length += tdBE - td->data_dma + 1;
-  				else
-					urb->actual_length += tdCBP - td->data_dma;
-			}
-  		}
-  	}
-}
-
-/* handle an urb that is being unlinked */
-
-static void dl_del_urb (struct urb * urb)
-{
-	wait_queue_head_t * wait_head = ((urb_priv_t *)(urb->hcpriv))->wait;
-
-	urb_rm_priv_locked (urb);
-
-	if (urb->transfer_flags & USB_ASYNC_UNLINK) {
-		urb->status = -ECONNRESET;
-		if (urb->complete)
-			urb->complete (urb);
-	} else {
-		urb->status = -ENOENT;
-		if (urb->complete)
-			urb->complete (urb);
-
-		/* unblock sohci_unlink_urb */
-		if (wait_head)
-			wake_up (wait_head);
-	}
-}
-
-/*-------------------------------------------------------------------------*/
-
-/* replies to the request have to be on a FIFO basis so
- * we reverse the reversed done-list */
- 
-static td_t * dl_reverse_done_list (ohci_t * ohci)
-{
-	__u32 td_list_hc;
-	td_t * td_rev = NULL;
-	td_t * td_list = NULL;
-  	urb_priv_t * urb_priv = NULL;
-  	unsigned long flags;
-  	
-  	spin_lock_irqsave (&usb_ed_lock, flags);
-  	
-	td_list_hc = le32_to_cpup (&ohci->hcca->done_head) & 0xfffffff0;
-	ohci->hcca->done_head = 0;
-	
-	while (td_list_hc) {		
-		td_list = dma_to_td (ohci, td_list_hc);
-
-		if (TD_CC_GET (le32_to_cpup (&td_list->hwINFO))) {
-			urb_priv = (urb_priv_t *) td_list->urb->hcpriv;
-			dbg(" USB-error/status: %x : %p", 
-					TD_CC_GET (le32_to_cpup (&td_list->hwINFO)), td_list);
-			if (td_list->ed->hwHeadP & cpu_to_le32 (0x1)) {
-				if (urb_priv && ((td_list->index + 1) < urb_priv->length)) {
-					td_list->ed->hwHeadP = 
-						(urb_priv->td[urb_priv->length - 1]->hwNextTD & cpu_to_le32 (0xfffffff0)) |
-									(td_list->ed->hwHeadP & cpu_to_le32 (0x2));
-					urb_priv->td_cnt += urb_priv->length - td_list->index - 1;
-				} else 
-					td_list->ed->hwHeadP &= cpu_to_le32 (0xfffffff2);
-			}
-		}
-
-		td_list->next_dl_td = td_rev;	
-		td_rev = td_list;
-		td_list_hc = le32_to_cpup (&td_list->hwNextTD) & 0xfffffff0;	
-	}	
-	spin_unlock_irqrestore (&usb_ed_lock, flags);
-	return td_list;
-}
-
-/*-------------------------------------------------------------------------*/
-
-/* there are some pending requests to remove 
- * - some of the eds (if ed->state & ED_DEL (set by sohci_free_dev)
- * - some URBs/TDs if urb_priv->state == URB_DEL */
- 
-static void dl_del_list (ohci_t  * ohci, unsigned int frame)
-{
-	unsigned long flags;
-	ed_t * ed;
-	__u32 edINFO;
-	__u32 tdINFO;
-	td_t * td = NULL, * td_next = NULL, * tdHeadP = NULL, * tdTailP;
-	__u32 * td_p;
-	int ctrl = 0, bulk = 0;
-
-	spin_lock_irqsave (&usb_ed_lock, flags);
-
-	for (ed = ohci->ed_rm_list[frame]; ed != NULL; ed = ed->ed_rm_list) {
-
-		tdTailP = dma_to_td (ohci, le32_to_cpup (&ed->hwTailP) & 0xfffffff0);
-		tdHeadP = dma_to_td (ohci, le32_to_cpup (&ed->hwHeadP) & 0xfffffff0);
-		edINFO = le32_to_cpup (&ed->hwINFO);
-		td_p = &ed->hwHeadP;
-
-		for (td = tdHeadP; td != tdTailP; td = td_next) { 
-			struct urb * urb = td->urb;
-			urb_priv_t * urb_priv = td->urb->hcpriv;
-			
-			td_next = dma_to_td (ohci, le32_to_cpup (&td->hwNextTD) & 0xfffffff0);
-			if ((urb_priv->state == URB_DEL) || (ed->state & ED_DEL)) {
-				tdINFO = le32_to_cpup (&td->hwINFO);
-				if (TD_CC_GET (tdINFO) < 0xE)
-					dl_transfer_length (td);
-				*td_p = td->hwNextTD | (*td_p & cpu_to_le32 (0x3));
-
-				/* URB is done; clean up */
-				if (++(urb_priv->td_cnt) == urb_priv->length)
-					dl_del_urb (urb);
-			} else {
-				td_p = &td->hwNextTD;
-			}
-		}
-
-		if (ed->state & ED_DEL) { /* set by sohci_free_dev */
-			struct ohci_device * dev = usb_to_ohci (ohci->dev[edINFO & 0x7F]);
-			td_free (ohci, tdTailP); /* free dummy td */
-   	 		ed->hwINFO = cpu_to_le32 (OHCI_ED_SKIP); 
-			ed->state = ED_NEW;
-			hash_free_ed(ohci, ed);
-   	 		/* if all eds are removed wake up sohci_free_dev */
-   	 		if (!--dev->ed_cnt) {
-				wait_queue_head_t *wait_head = dev->wait;
-
-				dev->wait = 0;
-				if (wait_head)
-					wake_up (wait_head);
-			}
-   	 	} else {
-   	 		ed->state &= ~ED_URB_DEL;
-			tdHeadP = dma_to_td (ohci, le32_to_cpup (&ed->hwHeadP) & 0xfffffff0);
-
-			if (tdHeadP == tdTailP) {
-				if (ed->state == ED_OPER)
-					ep_unlink(ohci, ed);
-			} else
-   	 			ed->hwINFO &= ~cpu_to_le32 (OHCI_ED_SKIP);
-   	 	}
-
-		switch (ed->type) {
-			case PIPE_CONTROL:
-				ctrl = 1;
-				break;
-			case PIPE_BULK:
-				bulk = 1;
-				break;
-		}
-   	}
-   	
-	/* maybe reenable control and bulk lists */ 
-	if (!ohci->disabled) {
-		if (ctrl) 	/* reset control list */
-			writel (0, &ohci->regs->ed_controlcurrent);
-		if (bulk)	/* reset bulk list */
-			writel (0, &ohci->regs->ed_bulkcurrent);
-		if (!ohci->ed_rm_list[!frame] && !ohci->sleeping) {
-			if (ohci->ed_controltail)
-				ohci->hc_control |= OHCI_CTRL_CLE;
-			if (ohci->ed_bulktail)
-				ohci->hc_control |= OHCI_CTRL_BLE;
-			writel (ohci->hc_control, &ohci->regs->control);   
-		}
-	}
-
-   	ohci->ed_rm_list[frame] = NULL;
-   	spin_unlock_irqrestore (&usb_ed_lock, flags);
-}
-
-
-  		
-/*-------------------------------------------------------------------------*/
-
-/* td done list */
-
-static void dl_done_list (ohci_t * ohci, td_t * td_list)
-{
-  	td_t * td_list_next = NULL;
-	ed_t * ed;
-	int cc = 0;
-	struct urb * urb;
-	urb_priv_t * urb_priv;
- 	__u32 tdINFO, edHeadP, edTailP;
- 	
- 	unsigned long flags;
- 	
-  	while (td_list) {
-   		td_list_next = td_list->next_dl_td;
-   		
-  		urb = td_list->urb;
-  		urb_priv = urb->hcpriv;
-  		tdINFO = le32_to_cpup (&td_list->hwINFO);
-  		
-   		ed = td_list->ed;
-   		
-   		dl_transfer_length(td_list);
- 			
-  		/* error code of transfer */
-  		cc = TD_CC_GET (tdINFO);
-  		if (cc == TD_CC_STALL)
-			usb_endpoint_halt(urb->dev,
-				usb_pipeendpoint(urb->pipe),
-				usb_pipeout(urb->pipe));
-  		
-  		if (!(urb->transfer_flags & USB_DISABLE_SPD)
-				&& (cc == TD_DATAUNDERRUN))
-			cc = TD_CC_NOERROR;
-
-  		if (++(urb_priv->td_cnt) == urb_priv->length) {
-			if ((ed->state & (ED_OPER | ED_UNLINK))
-					&& (urb_priv->state != URB_DEL)) {
-  				urb->status = cc_to_error[cc];
-  				sohci_return_urb (ohci, urb);
-  			} else {
-				spin_lock_irqsave (&usb_ed_lock, flags);
-  				dl_del_urb (urb);
-				spin_unlock_irqrestore (&usb_ed_lock, flags);
-			}
-  		}
-  		
-  		spin_lock_irqsave (&usb_ed_lock, flags);
-  		if (ed->state != ED_NEW) { 
-  			edHeadP = le32_to_cpup (&ed->hwHeadP) & 0xfffffff0;
-  			edTailP = le32_to_cpup (&ed->hwTailP);
-
-			/* unlink eds if they are not busy */
-     			if ((edHeadP == edTailP) && (ed->state == ED_OPER)) 
-     				ep_unlink (ohci, ed);
-     		}	
-     		spin_unlock_irqrestore (&usb_ed_lock, flags);
-     	
-    		td_list = td_list_next;
-  	}  
-}
-
-
-
-
-/*-------------------------------------------------------------------------*
- * Virtual Root Hub 
- *-------------------------------------------------------------------------*/
- 
-/* Device descriptor */
-static __u8 root_hub_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 root_hub_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 */
- 	0x02,       /*  __u16 ep_wMaxPacketSize; ((MAX_ROOT_PORTS + 1) / 8 */
- 	0x00,
-	0xff        /*  __u8  ep_bInterval; 255 ms */
-};
-
-/* Hub class-specific descriptor is constructed dynamically */
-
-
-/*-------------------------------------------------------------------------*/
-
-/* prepare Interrupt pipe data; HUB INTERRUPT ENDPOINT */ 
- 
-static int rh_send_irq (ohci_t * ohci, void * rh_data, int rh_len)
-{
-	int num_ports;
-	int i;
-	int ret;
-	int len;
-
-	__u8 data[8];
-
-	num_ports = roothub_a (ohci) & RH_A_NDP; 
-	if (num_ports > MAX_ROOT_PORTS) {
-		err ("bogus NDP=%d for OHCI usb-%s", num_ports,
-			ohci->ohci_dev->slot_name);
-		err ("rereads as NDP=%d",
-			readl (&ohci->regs->roothub.a) & RH_A_NDP);
-		/* retry later; "should not happen" */
-		return 0;
-	}
-	*(__u8 *) data = (roothub_status (ohci) & (RH_HS_LPSC | RH_HS_OCIC))
-		? 1: 0;
-	ret = *(__u8 *) data;
-
-	for ( i = 0; i < num_ports; i++) {
-		*(__u8 *) (data + (i + 1) / 8) |= 
-			((roothub_portstatus (ohci, i) &
-				(RH_PS_CSC | RH_PS_PESC | RH_PS_PSSC | RH_PS_OCIC | RH_PS_PRSC))
-			    ? 1: 0) << ((i + 1) % 8);
-		ret += *(__u8 *) (data + (i + 1) / 8);
-	}
-	len = i/8 + 1;
-  
-	if (ret > 0) { 
-		memcpy(rh_data, data,
-		       min_t(unsigned int, len,
-			   min_t(unsigned int, rh_len, sizeof(data))));
-		return len;
-	}
-	return 0;
-}
-
-/*-------------------------------------------------------------------------*/
-
-/* Virtual Root Hub INTs are polled by this timer every "interval" ms */
- 
-static void rh_int_timer_do (unsigned long ptr)
-{
-	int len; 
-
-	struct urb * urb = (struct urb *) ptr;
-	ohci_t * ohci = urb->dev->bus->hcpriv;
-
-	if (ohci->disabled)
-		return;
-
-	/* ignore timers firing during PM suspend, etc */
-	if ((ohci->hc_control & OHCI_CTRL_HCFS) != OHCI_USB_OPER)
-		goto out;
-
-	if(ohci->rh.send) { 
-		len = rh_send_irq (ohci, urb->transfer_buffer, urb->transfer_buffer_length);
-		if (len > 0) {
-			urb->actual_length = len;
-#ifdef DEBUG
-			urb_print (urb, "RET-t(rh)", usb_pipeout (urb->pipe));
-#endif
-			if (urb->complete)
-				urb->complete (urb);
-		}
-	}
- out:
-	rh_init_int_timer (urb);
-}
-
-/*-------------------------------------------------------------------------*/
-
-/* Root Hub INTs are polled by this timer */
-
-static int rh_init_int_timer (struct urb * urb) 
-{
-	ohci_t * ohci = urb->dev->bus->hcpriv;
-
-	ohci->rh.interval = urb->interval;
-	init_timer (&ohci->rh.rh_int_timer);
-	ohci->rh.rh_int_timer.function = rh_int_timer_do;
-	ohci->rh.rh_int_timer.data = (unsigned long) urb;
-	ohci->rh.rh_int_timer.expires = 
-			jiffies + (HZ * (urb->interval < 30? 30: urb->interval)) / 1000;
-	add_timer (&ohci->rh.rh_int_timer);
-	
-	return 0;
-}
-
-/*-------------------------------------------------------------------------*/
-
-#define OK(x) 			len = (x); break
-#define WR_RH_STAT(x) 		writel((x), &ohci->regs->roothub.status)
-#define WR_RH_PORTSTAT(x) 	writel((x), &ohci->regs->roothub.portstatus[wIndex-1])
-#define RD_RH_STAT		roothub_status(ohci)
-#define RD_RH_PORTSTAT		roothub_portstatus(ohci,wIndex-1)
-
-/* request to virtual root hub */
-
-static int rh_submit_urb (struct urb * urb)
-{
-	struct usb_device * usb_dev = urb->dev;
-	ohci_t * ohci = usb_dev->bus->hcpriv;
-	unsigned int pipe = urb->pipe;
-	struct usb_ctrlrequest * cmd = (struct usb_ctrlrequest *) urb->setup_packet;
-	void * data = urb->transfer_buffer;
-	int leni = urb->transfer_buffer_length;
-	int len = 0;
-	int status = TD_CC_NOERROR;
-	
-	__u32 datab[4];
-	__u8  * data_buf = (__u8 *) datab;
-	
- 	__u16 bmRType_bReq;
-	__u16 wValue; 
-	__u16 wIndex;
-	__u16 wLength;
-
-	if (usb_pipeint(pipe)) {
-		ohci->rh.urb =  urb;
-		ohci->rh.send = 1;
-		ohci->rh.interval = urb->interval;
-		rh_init_int_timer(urb);
-		urb->status = cc_to_error [TD_CC_NOERROR];
-		
-		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);
-
-	switch (bmRType_bReq) {
-	/* Request Destination:
-	   without flags: Device, 
-	   RH_INTERFACE: interface, 
-	   RH_ENDPOINT: endpoint,
-	   RH_CLASS means HUB here, 
-	   RH_OTHER | RH_CLASS  almost ever means HUB_PORT here 
-	*/
-  
-		case RH_GET_STATUS: 				 		
-				*(__u16 *) data_buf = cpu_to_le16 (1); OK (2);
-		case RH_GET_STATUS | RH_INTERFACE: 	 		
-				*(__u16 *) data_buf = cpu_to_le16 (0); OK (2);
-		case RH_GET_STATUS | RH_ENDPOINT:	 		
-				*(__u16 *) data_buf = cpu_to_le16 (0); OK (2);   
-		case RH_GET_STATUS | RH_CLASS: 				
-				*(__u32 *) data_buf = cpu_to_le32 (
-					RD_RH_STAT & ~(RH_HS_CRWE | RH_HS_DRWE));
-				OK (4);
-		case RH_GET_STATUS | RH_OTHER | RH_CLASS: 	
-				*(__u32 *) data_buf = cpu_to_le32 (RD_RH_PORTSTAT); OK (4);
-
-		case RH_CLEAR_FEATURE | RH_ENDPOINT:  
-			switch (wValue) {
-				case (RH_ENDPOINT_STALL): OK (0);
-			}
-			break;
-
-		case RH_CLEAR_FEATURE | RH_CLASS:
-			switch (wValue) {
-				case RH_C_HUB_LOCAL_POWER:
-					OK(0);
-				case (RH_C_HUB_OVER_CURRENT): 
-						WR_RH_STAT(RH_HS_OCIC); OK (0);
-			}
-			break;
-		
-		case RH_CLEAR_FEATURE | RH_OTHER | RH_CLASS:
-			switch (wValue) {
-				case (RH_PORT_ENABLE): 			
-						WR_RH_PORTSTAT (RH_PS_CCS ); OK (0);
-				case (RH_PORT_SUSPEND):			
-						WR_RH_PORTSTAT (RH_PS_POCI); OK (0);
-				case (RH_PORT_POWER):			
-						WR_RH_PORTSTAT (RH_PS_LSDA); OK (0);
-				case (RH_C_PORT_CONNECTION):	
-						WR_RH_PORTSTAT (RH_PS_CSC ); OK (0);
-				case (RH_C_PORT_ENABLE):		
-						WR_RH_PORTSTAT (RH_PS_PESC); OK (0);
-				case (RH_C_PORT_SUSPEND):		
-						WR_RH_PORTSTAT (RH_PS_PSSC); OK (0);
-				case (RH_C_PORT_OVER_CURRENT):	
-						WR_RH_PORTSTAT (RH_PS_OCIC); OK (0);
-				case (RH_C_PORT_RESET):			
-						WR_RH_PORTSTAT (RH_PS_PRSC); OK (0); 
-			}
-			break;
- 
-		case RH_SET_FEATURE | RH_OTHER | RH_CLASS:
-			switch (wValue) {
-				case (RH_PORT_SUSPEND):			
-						WR_RH_PORTSTAT (RH_PS_PSS ); OK (0); 
-				case (RH_PORT_RESET): /* BUG IN HUP CODE *********/
-						if (RD_RH_PORTSTAT & RH_PS_CCS)
-						    WR_RH_PORTSTAT (RH_PS_PRS);
-						OK (0);
-				case (RH_PORT_POWER):			
-						WR_RH_PORTSTAT (RH_PS_PPS ); OK (0); 
-				case (RH_PORT_ENABLE): /* BUG IN HUP CODE *********/
-						if (RD_RH_PORTSTAT & RH_PS_CCS)
-						    WR_RH_PORTSTAT (RH_PS_PES );
-						OK (0);
-			}
-			break;
-
-		case RH_SET_ADDRESS: ohci->rh.devnum = wValue; OK(0);
-
-		case RH_GET_DESCRIPTOR:
-			switch ((wValue & 0xff00) >> 8) {
-				case (0x01): /* device descriptor */
-					len = min_t(unsigned int,
-						  leni,
-						  min_t(unsigned int,
-						      sizeof (root_hub_dev_des),
-						      wLength));
-					data_buf = root_hub_dev_des; OK(len);
-				case (0x02): /* configuration descriptor */
-					len = min_t(unsigned int,
-						  leni,
-						  min_t(unsigned int,
-						      sizeof (root_hub_config_des),
-						      wLength));
-					data_buf = root_hub_config_des; OK(len);
-				case (0x03): /* string descriptors */
-					len = usb_root_hub_string (wValue & 0xff,
-						(int)(long) ohci->regs, "OHCI",
-						data, wLength);
-					if (len > 0) {
-						data_buf = data;
-						OK(min_t(int, leni, len));
-					}
-					// else fallthrough
-				default: 
-					status = TD_CC_STALL;
-			}
-			break;
-		
-		case RH_GET_DESCRIPTOR | RH_CLASS:
-		    {
-			    __u32 temp = roothub_a (ohci);
-
-			    data_buf [0] = 9;		// min length;
-			    data_buf [1] = 0x29;
-			    data_buf [2] = temp & RH_A_NDP;
-			    data_buf [3] = 0;
-			    if (temp & RH_A_PSM) 	/* per-port power switching? */
-				data_buf [3] |= 0x1;
-			    if (temp & RH_A_NOCP)	/* no overcurrent reporting? */
-				data_buf [3] |= 0x10;
-			    else if (temp & RH_A_OCPM)	/* per-port overcurrent reporting? */
-				data_buf [3] |= 0x8;
-
-			    datab [1] = 0;
-			    data_buf [5] = (temp & RH_A_POTPGT) >> 24;
-			    temp = roothub_b (ohci);
-			    data_buf [7] = temp & RH_B_DR;
-			    if (data_buf [2] < 7) {
-				data_buf [8] = 0xff;
-			    } else {
-				data_buf [0] += 2;
-				data_buf [8] = (temp & RH_B_DR) >> 8;
-				data_buf [10] = data_buf [9] = 0xff;
-			    }
-				
-			    len = min_t(unsigned int, leni,
-				      min_t(unsigned int, data_buf [0], wLength));
-			    OK (len);
-			}
- 
-		case RH_GET_CONFIGURATION: 	*(__u8 *) data_buf = 0x01; OK (1);
-
-		case RH_SET_CONFIGURATION: 	WR_RH_STAT (0x10000); OK (0);
-
-		default: 
-			dbg ("unsupported root hub command");
-			status = TD_CC_STALL;
-	}
-	
-#ifdef	DEBUG
-	// ohci_dump_roothub (ohci, 0);
-#endif
-
-	len = min_t(int, len, leni);
-	if (data != data_buf)
-	    memcpy (data, data_buf, len);
-  	urb->actual_length = len;
-	urb->status = cc_to_error [status];
-	
-#ifdef DEBUG
-	urb_print (urb, "RET(rh)", usb_pipeout (urb->pipe));
-#endif
-
-	urb->hcpriv = NULL;
-	usb_dec_dev_use (usb_dev);
-	urb->dev = NULL;
-	if (urb->complete)
-	    	urb->complete (urb);
-	return 0;
-}
-
-/*-------------------------------------------------------------------------*/
-
-static int rh_unlink_urb (struct urb * urb)
-{
-	ohci_t * ohci = urb->dev->bus->hcpriv;
- 
-	if (ohci->rh.urb == urb) {
-		ohci->rh.send = 0;
-		del_timer (&ohci->rh.rh_int_timer);
-		ohci->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;
-}
- 
-/*-------------------------------------------------------------------------*
- * HC functions
- *-------------------------------------------------------------------------*/
-
-/* reset the HC and BUS */
-
-static int hc_reset (ohci_t * ohci)
-{
-	int timeout = 30;
-	int smm_timeout = 50; /* 0,5 sec */
-	 	
-#ifndef __hppa__
-	/* PA-RISC doesn't have SMM, but PDC might leave IR set */
-	if (readl (&ohci->regs->control) & OHCI_CTRL_IR) { /* SMM owns the HC */
-		writel (OHCI_OCR, &ohci->regs->cmdstatus); /* request ownership */
-		dbg("USB HC TakeOver from SMM");
-		while (readl (&ohci->regs->control) & OHCI_CTRL_IR) {
-			wait_ms (10);
-			if (--smm_timeout == 0) {
-				err("USB HC TakeOver failed!");
-				return -1;
-			}
-		}
-	}
-#endif	
-		
-	/* Disable HC interrupts */
-	writel (OHCI_INTR_MIE, &ohci->regs->intrdisable);
-
-	dbg("USB HC reset_hc usb-%s: ctrl = 0x%x ;",
-		ohci->ohci_dev->slot_name,
-		readl (&ohci->regs->control));
-
-  	/* Reset USB (needed by some controllers) */
-	writel (0, &ohci->regs->control);
-
-	/* Force a state change from USBRESET to USBOPERATIONAL for ALi */
-	(void) readl (&ohci->regs->control);	/* PCI posting */
-	writel (ohci->hc_control = OHCI_USB_OPER, &ohci->regs->control);
-
-	/* HC Reset requires max 10 ms delay */
-	writel (OHCI_HCR,  &ohci->regs->cmdstatus);
-	while ((readl (&ohci->regs->cmdstatus) & OHCI_HCR) != 0) {
-		if (--timeout == 0) {
-			err("USB HC reset timed out!");
-			return -1;
-		}	
-		udelay (1);
-	}	 
-	return 0;
-}
-
-/*-------------------------------------------------------------------------*/
-
-/* Start an OHCI controller, set the BUS operational
- * enable interrupts 
- * connect the virtual root hub */
-
-static int hc_start (ohci_t * ohci)
-{
-  	__u32 mask;
-  	unsigned int fminterval;
-  	struct usb_device  * usb_dev;
-	struct ohci_device * dev;
-	
-	ohci->disabled = 1;
-
-	/* Tell the controller where the control and bulk lists are
-	 * The lists are empty now. */
-	 
-	writel (0, &ohci->regs->ed_controlhead);
-	writel (0, &ohci->regs->ed_bulkhead);
-	
-	writel (ohci->hcca_dma, &ohci->regs->hcca); /* a reset clears this */
-   
-  	fminterval = 0x2edf;
-	writel ((fminterval * 9) / 10, &ohci->regs->periodicstart);
-	fminterval |= ((((fminterval - 210) * 6) / 7) << 16); 
-	writel (fminterval, &ohci->regs->fminterval);	
-	writel (0x628, &ohci->regs->lsthresh);
-
- 	/* start controller operations */
- 	ohci->hc_control = OHCI_CONTROL_INIT | OHCI_USB_OPER;
-	ohci->disabled = 0;
- 	writel (ohci->hc_control, &ohci->regs->control);
- 
-	/* Choose the interrupts we care about now, others later on demand */
-	mask = OHCI_INTR_MIE | OHCI_INTR_UE | OHCI_INTR_WDH | OHCI_INTR_SO;
-	writel (mask, &ohci->regs->intrenable);
-	writel (mask, &ohci->regs->intrstatus);
-
-#ifdef	OHCI_USE_NPS
-	if(ohci->flags & OHCI_QUIRK_SUCKYIO)
-	{
-		/* NSC 87560 at least requires different setup .. */
-		writel ((roothub_a (ohci) | RH_A_NOCP) &
-			~(RH_A_OCPM | RH_A_POTPGT | RH_A_PSM | RH_A_NPS),
-			&ohci->regs->roothub.a);
-	}
-	else
-	{
-		/* required for AMD-756 and some Mac platforms */
-		writel ((roothub_a (ohci) | RH_A_NPS) & ~RH_A_PSM,
-			&ohci->regs->roothub.a);
-	}
-	writel (RH_HS_LPSC, &ohci->regs->roothub.status);
-#endif	/* OHCI_USE_NPS */
-
-	// POTPGT delay is bits 24-31, in 2 ms units.
-	mdelay ((roothub_a (ohci) >> 23) & 0x1fe);
- 
-	/* connect the virtual root hub */
-	ohci->rh.devnum = 0;
-	usb_dev = usb_alloc_dev (NULL, ohci->bus);
-	if (!usb_dev) {
-	    ohci->disabled = 1;
-	    return -ENOMEM;
-	}
-
-	dev = usb_to_ohci (usb_dev);
-	ohci->bus->root_hub = usb_dev;
-	usb_connect (usb_dev);
-	if (usb_new_device (usb_dev) != 0) {
-		usb_free_dev (usb_dev); 
-		ohci->disabled = 1;
-		return -ENODEV;
-	}
-	
-	return 0;
-}
-
-/*-------------------------------------------------------------------------*/
-
-/* called only from interrupt handler */
-
-static void check_timeouts (struct ohci *ohci)
-{
-	spin_lock (&usb_ed_lock);
-	while (!list_empty (&ohci->timeout_list)) {
-		struct urb	*urb;
-
-		urb = list_entry (ohci->timeout_list.next, struct urb, urb_list);
-		if (time_after (jiffies, urb->timeout))
-			break;
-
-		list_del_init (&urb->urb_list);
-		if (urb->status != -EINPROGRESS)
-			continue;
-
-		urb->transfer_flags |= USB_TIMEOUT_KILLED | USB_ASYNC_UNLINK;
-		spin_unlock (&usb_ed_lock);
-
-		// outside the interrupt handler (in a timer...)
-		// this reference would race interrupts
-		sohci_unlink_urb (urb);
-
-		spin_lock (&usb_ed_lock);
-	}
-	spin_unlock (&usb_ed_lock);
-}
-
-
-/*-------------------------------------------------------------------------*/
-
-/* an interrupt happens */
-
-static void hc_interrupt (int irq, void * __ohci, struct pt_regs * r)
-{
-	ohci_t * ohci = __ohci;
-	struct ohci_regs * regs = ohci->regs;
- 	int ints; 
-
-	/* avoid (slow) readl if only WDH happened */
-	if ((ohci->hcca->done_head != 0)
-			&& !(le32_to_cpup (&ohci->hcca->done_head) & 0x01)) {
-		ints =  OHCI_INTR_WDH;
-
-	/* cardbus/... hardware gone before remove() */
-	} else if ((ints = readl (&regs->intrstatus)) == ~(u32)0) {
-		ohci->disabled++;
-		err ("%s device removed!", ohci->ohci_dev->slot_name);
-		return;
-
-	/* interrupt for some other device? */
-	} else if ((ints &= readl (&regs->intrenable)) == 0) {
-		return;
-	} 
-
-	// dbg("Interrupt: %x frame: %x", ints, le16_to_cpu (ohci->hcca->frame_no));
-
-	if (ints & OHCI_INTR_UE) {
-		ohci->disabled++;
-		err ("OHCI Unrecoverable Error, controller usb-%s disabled",
-			ohci->ohci_dev->slot_name);
-		// e.g. due to PCI Master/Target Abort
-
-#ifdef	DEBUG
-		ohci_dump (ohci, 1);
-#else
-		// FIXME: be optimistic, hope that bug won't repeat often.
-		// Make some non-interrupt context restart the controller.
-		// Count and limit the retries though; either hardware or
-		// software errors can go forever...
-#endif
-		hc_reset (ohci);
-	}
-  
-	if (ints & OHCI_INTR_WDH) {
-		writel (OHCI_INTR_WDH, &regs->intrdisable);	
-		dl_done_list (ohci, dl_reverse_done_list (ohci));
-		writel (OHCI_INTR_WDH, &regs->intrenable); 
-	}
-  
-	if (ints & OHCI_INTR_SO) {
-		dbg("USB Schedule overrun");
-		writel (OHCI_INTR_SO, &regs->intrenable); 	 
-	}
-
-	// FIXME:  this assumes SOF (1/ms) interrupts don't get lost...
-	if (ints & OHCI_INTR_SF) { 
-		unsigned int frame = le16_to_cpu (ohci->hcca->frame_no) & 1;
-		writel (OHCI_INTR_SF, &regs->intrdisable);	
-		if (ohci->ed_rm_list[!frame] != NULL) {
-			dl_del_list (ohci, !frame);
-		}
-		if (ohci->ed_rm_list[frame] != NULL)
-			writel (OHCI_INTR_SF, &regs->intrenable);	
-	}
-
-	if (!list_empty (&ohci->timeout_list)) {
-		check_timeouts (ohci);
-// FIXME:  enable SF as needed in a timer;
-// don't make lots of 1ms interrupts
-// On unloaded USB, think 4k ~= 4-5msec
-		if (!list_empty (&ohci->timeout_list))
-			writel (OHCI_INTR_SF, &regs->intrenable);	
-	}
-
-	writel (ints, &regs->intrstatus);
-	writel (OHCI_INTR_MIE, &regs->intrenable);	
-}
-
-/*-------------------------------------------------------------------------*/
-
-/* allocate OHCI */
-
-static ohci_t * __devinit hc_alloc_ohci (struct pci_dev *dev, void * mem_base)
-{
-	ohci_t * ohci;
-
-	ohci = (ohci_t *) kmalloc (sizeof *ohci, GFP_KERNEL);
-	if (!ohci)
-		return NULL;
-		
-	memset (ohci, 0, sizeof (ohci_t));
-
-	ohci->hcca = pci_alloc_consistent (dev, sizeof *ohci->hcca,
-			&ohci->hcca_dma);
-        if (!ohci->hcca) {
-                kfree (ohci);
-                return NULL;
-        }
-        memset (ohci->hcca, 0, sizeof (struct ohci_hcca));
-
-	ohci->disabled = 1;
-	ohci->sleeping = 0;
-	ohci->irq = -1;
-	ohci->regs = mem_base;   
-
-	ohci->ohci_dev = dev;
-	pci_set_drvdata(dev, ohci);
- 
-	INIT_LIST_HEAD (&ohci->ohci_hcd_list);
-	list_add (&ohci->ohci_hcd_list, &ohci_hcd_list);
-
-	INIT_LIST_HEAD (&ohci->timeout_list);
-
-	ohci->bus = usb_alloc_bus (&sohci_device_operations);
-	if (!ohci->bus) {
-		pci_set_drvdata (dev, NULL);
-		pci_free_consistent (ohci->ohci_dev, sizeof *ohci->hcca,
-				ohci->hcca, ohci->hcca_dma);
-		kfree (ohci);
-		return NULL;
-	}
-	ohci->bus->bus_name = dev->slot_name;
-	ohci->bus->hcpriv = (void *) ohci;
-
-	return ohci;
-} 
-
-
-/*-------------------------------------------------------------------------*/
-
-/* De-allocate all resources.. */
-
-static void hc_release_ohci (ohci_t * ohci)
-{	
-	dbg ("USB HC release ohci usb-%s", ohci->ohci_dev->slot_name);
-
-	/* disconnect all devices */    
-	if (ohci->bus->root_hub)
-		usb_disconnect (&ohci->bus->root_hub);
-
-	if (!ohci->disabled)
-		hc_reset (ohci);
-	
-	if (ohci->irq >= 0) {
-		free_irq (ohci->irq, ohci);
-		ohci->irq = -1;
-	}
-	pci_set_drvdata(ohci->ohci_dev, NULL);
-	if (ohci->bus) {
-		if (ohci->bus->busnum != -1)
-			usb_deregister_bus (ohci->bus);
-
-		usb_free_bus (ohci->bus);
-	}
-
-	list_del (&ohci->ohci_hcd_list);
-	INIT_LIST_HEAD (&ohci->ohci_hcd_list);
-
-	ohci_mem_cleanup (ohci);
-    
-	/* unmap the IO address space */
-	iounmap (ohci->regs);
-
-	pci_free_consistent (ohci->ohci_dev, sizeof *ohci->hcca,
-		ohci->hcca, ohci->hcca_dma);
-	kfree (ohci);
-}
-
-/*-------------------------------------------------------------------------*/
-
-/* Increment the module usage count, start the control thread and
- * return success. */
-
-static struct pci_driver ohci_pci_driver;
- 
-static int __devinit
-hc_found_ohci (struct pci_dev *dev, int irq,
-	void *mem_base, const struct pci_device_id *id)
-{
-	ohci_t * ohci;
-	char buf[8], *bufp = buf;
-	int ret;
-
-#ifndef __sparc__
-	sprintf(buf, "%d", irq);
-#else
-	bufp = __irq_itoa(irq);
-#endif
-	printk(KERN_INFO __FILE__ ": USB OHCI at membase 0x%lx, IRQ %s\n",
-		(unsigned long)	mem_base, bufp);
-	printk(KERN_INFO __FILE__ ": usb-%s, %s\n", dev->slot_name, dev->name);
-    
-	ohci = hc_alloc_ohci (dev, mem_base);
-	if (!ohci) {
-		return -ENOMEM;
-	}
-	if ((ret = ohci_mem_init (ohci)) < 0) {
-		hc_release_ohci (ohci);
-		return ret;
-	}
-	ohci->flags = id->driver_data;
-	
-	/* Check for NSC87560. We have to look at the bridge (fn1) to identify
-	   the USB (fn2). This quirk might apply to more or even all NSC stuff
-	   I don't know.. */
-	   
-	if(dev->vendor == PCI_VENDOR_ID_NS)
-	{
-		struct pci_dev *fn1  = pci_find_slot(dev->bus->number, PCI_DEVFN(PCI_SLOT(dev->devfn), 1));
-		if(fn1 && fn1->vendor == PCI_VENDOR_ID_NS && fn1->device == PCI_DEVICE_ID_NS_87560_LIO)
-			ohci->flags |= OHCI_QUIRK_SUCKYIO;
-		
-	}
-	
-	if (ohci->flags & OHCI_QUIRK_SUCKYIO)
-		printk (KERN_INFO __FILE__ ": Using NSC SuperIO setup\n");
-	if (ohci->flags & OHCI_QUIRK_AMD756)
-		printk (KERN_INFO __FILE__ ": AMD756 erratum 4 workaround\n");
-
-	if (hc_reset (ohci) < 0) {
-		hc_release_ohci (ohci);
-		return -ENODEV;
-	}
-
-	/* FIXME this is a second HC reset; why?? */
-	writel (ohci->hc_control = OHCI_USB_RESET, &ohci->regs->control);
-	wait_ms (10);
-
-	usb_register_bus (ohci->bus);
-	
-	if (request_irq (irq, hc_interrupt, SA_SHIRQ,
-			ohci_pci_driver.name, ohci) != 0) {
-		err ("request interrupt %s failed", bufp);
-		hc_release_ohci (ohci);
-		return -EBUSY;
-	}
-	ohci->irq = irq;     
-
-	if (hc_start (ohci) < 0) {
-		err ("can't start usb-%s", dev->slot_name);
-		hc_release_ohci (ohci);
-		return -EBUSY;
-	}
-
-#ifdef	DEBUG
-	ohci_dump (ohci, 1);
-#endif
-	return 0;
-}
-
-/*-------------------------------------------------------------------------*/
-
-#ifdef	CONFIG_PM
-
-/* controller died; cleanup debris, then restart */
-/* must not be called from interrupt context */
-
-static void hc_restart (ohci_t *ohci)
-{
-	int temp;
-	int i;
-
-	if (ohci->pci_latency)
-		pci_write_config_byte (ohci->ohci_dev, PCI_LATENCY_TIMER, ohci->pci_latency);
-
-	ohci->disabled = 1;
-	ohci->sleeping = 0;
-	if (ohci->bus->root_hub)
-		usb_disconnect (&ohci->bus->root_hub);
-	
-	/* empty the interrupt branches */
-	for (i = 0; i < NUM_INTS; i++) ohci->ohci_int_load[i] = 0;
-	for (i = 0; i < NUM_INTS; i++) ohci->hcca->int_table[i] = 0;
-	
-	/* no EDs to remove */
-	ohci->ed_rm_list [0] = NULL;
-	ohci->ed_rm_list [1] = NULL;
-
-	/* empty control and bulk lists */	 
-	ohci->ed_isotail     = NULL;
-	ohci->ed_controltail = NULL;
-	ohci->ed_bulktail    = NULL;
-
-	if ((temp = hc_reset (ohci)) < 0 || (temp = hc_start (ohci)) < 0) {
-		err ("can't restart usb-%s, %d", ohci->ohci_dev->slot_name, temp);
-	} else
-		dbg ("restart usb-%s completed", ohci->ohci_dev->slot_name);
-}
-
-#endif	/* CONFIG_PM */
-
-/*-------------------------------------------------------------------------*/
-
-/* configured so that an OHCI device is always provided */
-/* always called with process context; sleeping is OK */
-
-static int __devinit
-ohci_pci_probe (struct pci_dev *dev, const struct pci_device_id *id)
-{
-	unsigned long mem_resource, mem_len;
-	void *mem_base;
-	int status;
-
-	if (pci_enable_device(dev) < 0)
-		return -ENODEV;
-
-        if (!dev->irq) {
-        	err("found OHCI device with no IRQ assigned. check BIOS settings!");
-		pci_disable_device (dev);
-   	        return -ENODEV;
-        }
-	
-	/* we read its hardware registers as memory */
-	mem_resource = pci_resource_start(dev, 0);
-	mem_len = pci_resource_len(dev, 0);
-	if (!request_mem_region (mem_resource, mem_len, ohci_pci_driver.name)) {
-		dbg ("controller already in use");
-		pci_disable_device (dev);
-		return -EBUSY;
-	}
-
-	mem_base = ioremap_nocache (mem_resource, mem_len);
-	if (!mem_base) {
-		err("Error mapping OHCI memory");
-		release_mem_region (mem_resource, mem_len);
-		pci_disable_device (dev);
-		return -EFAULT;
-	}
-
-	/* controller writes into our memory */
-	pci_set_master (dev);
-
-	status = hc_found_ohci (dev, dev->irq, mem_base, id);
-	if (status < 0) {
-		iounmap (mem_base);
-		release_mem_region (mem_resource, mem_len);
-		pci_disable_device (dev);
-	}
-	return status;
-} 
-
-/*-------------------------------------------------------------------------*/
-
-/* may be called from interrupt context [interface spec] */
-/* may be called without controller present */
-/* may be called with controller, bus, and devices active */
-
-static void __devexit
-ohci_pci_remove (struct pci_dev *dev)
-{
-	ohci_t		*ohci = pci_get_drvdata(dev);
-
-	dbg ("remove %s controller usb-%s%s%s",
-		hcfs2string (ohci->hc_control & OHCI_CTRL_HCFS),
-		dev->slot_name,
-		ohci->disabled ? " (disabled)" : "",
-		in_interrupt () ? " in interrupt" : ""
-		);
-#ifdef	DEBUG
-	ohci_dump (ohci, 1);
-#endif
-
-	/* don't wake up sleeping controllers, or block in interrupt context */
-	if ((ohci->hc_control & OHCI_CTRL_HCFS) != OHCI_USB_OPER || in_interrupt ()) {
-		dbg ("controller being disabled");
-		ohci->disabled = 1;
-	}
-
-	/* on return, USB will always be reset (if present) */
-	if (ohci->disabled)
-		writel (ohci->hc_control = OHCI_USB_RESET,
-			&ohci->regs->control);
-
-	hc_release_ohci (ohci);
-
-	release_mem_region (pci_resource_start (dev, 0), pci_resource_len (dev, 0));
-	pci_disable_device (dev);
-}
-
-
-#ifdef	CONFIG_PM
-
-/*-------------------------------------------------------------------------*/
-
-static int
-ohci_pci_suspend (struct pci_dev *dev, u32 state)
-{
-	ohci_t			*ohci = pci_get_drvdata(dev);
-	unsigned long		flags;
-	u16 cmd;
-
-	if ((ohci->hc_control & OHCI_CTRL_HCFS) != OHCI_USB_OPER) {
-		dbg ("can't suspend usb-%s (state is %s)", dev->slot_name,
-			hcfs2string (ohci->hc_control & OHCI_CTRL_HCFS));
-		return -EIO;
-	}
-
-	/* act as if usb suspend can always be used */
-	info ("USB suspend: usb-%s", dev->slot_name);
-	ohci->sleeping = 1;
-
-	/* First stop processing */
-  	spin_lock_irqsave (&usb_ed_lock, flags);
-	ohci->hc_control &= ~(OHCI_CTRL_PLE|OHCI_CTRL_CLE|OHCI_CTRL_BLE|OHCI_CTRL_IE);
-	writel (ohci->hc_control, &ohci->regs->control);
-	writel (OHCI_INTR_SF, &ohci->regs->intrstatus);
-	(void) readl (&ohci->regs->intrstatus);
-  	spin_unlock_irqrestore (&usb_ed_lock, flags);
-
-	/* Wait a frame or two */
-	mdelay(1);
-	if (!readl (&ohci->regs->intrstatus) & OHCI_INTR_SF)
-		mdelay (1);
-		
-#ifdef CONFIG_PMAC_PBOOK
-	if (_machine == _MACH_Pmac)
-		disable_irq (ohci->irq);
-	/* else, 2.4 assumes shared irqs -- don't disable */
-#endif
-	/* Enable remote wakeup */
-	writel (readl(&ohci->regs->intrenable) | OHCI_INTR_RD, &ohci->regs->intrenable);
-
-	/* Suspend chip and let things settle down a bit */
-	ohci->hc_control = OHCI_USB_SUSPEND;
-	writel (ohci->hc_control, &ohci->regs->control);
-	(void) readl (&ohci->regs->control);
-	mdelay (500); /* No schedule here ! */
-	switch (readl (&ohci->regs->control) & OHCI_CTRL_HCFS) {
-		case OHCI_USB_RESET:
-			dbg("Bus in reset phase ???");
-			break;
-		case OHCI_USB_RESUME:
-			dbg("Bus in resume phase ???");
-			break;
-		case OHCI_USB_OPER:
-			dbg("Bus in operational phase ???");
-			break;
-		case OHCI_USB_SUSPEND:
-			dbg("Bus suspended");
-			break;
-	}
-	/* In some rare situations, Apple's OHCI have happily trashed
-	 * memory during sleep. We disable it's bus master bit during
-	 * suspend
-	 */
-	pci_read_config_word (dev, PCI_COMMAND, &cmd);
-	cmd &= ~PCI_COMMAND_MASTER;
-	pci_write_config_word (dev, PCI_COMMAND, cmd);
-#ifdef CONFIG_PMAC_PBOOK
-	{
-	   	struct device_node	*of_node;
-
-		/* Disable USB PAD & cell clock */
-		of_node = pci_device_to_OF_node (ohci->ohci_dev);
-		if (of_node)
-			pmac_call_feature(PMAC_FTR_USB_ENABLE, of_node, 0, 0);
-	}
-#endif
-	return 0;
-}
-
-/*-------------------------------------------------------------------------*/
-
-static int
-ohci_pci_resume (struct pci_dev *dev)
-{
-	ohci_t		*ohci = pci_get_drvdata(dev);
-	int		temp;
-	unsigned long	flags;
-
-	/* guard against multiple resumes */
-	atomic_inc (&ohci->resume_count);
-	if (atomic_read (&ohci->resume_count) != 1) {
-		err ("concurrent PCI resumes for usb-%s", dev->slot_name);
-		atomic_dec (&ohci->resume_count);
-		return 0;
-	}
-
-#ifdef CONFIG_PMAC_PBOOK
-	{
-		struct device_node *of_node;
-
-		/* Re-enable USB PAD & cell clock */
-		of_node = pci_device_to_OF_node (ohci->ohci_dev);
-		if (of_node)
-			pmac_call_feature(PMAC_FTR_USB_ENABLE, of_node, 0, 1);
-	}
-#endif
-
-	/* did we suspend, or were we powered off? */
-	ohci->hc_control = readl (&ohci->regs->control);
-	temp = ohci->hc_control & OHCI_CTRL_HCFS;
-
-#ifdef DEBUG
-	/* the registers may look crazy here */
-	ohci_dump_status (ohci);
-#endif
-
-	/* Re-enable bus mastering */
-	pci_set_master(ohci->ohci_dev);
-	
-	switch (temp) {
-
-	case OHCI_USB_RESET:	// lost power
-		info ("USB restart: usb-%s", dev->slot_name);
-		hc_restart (ohci);
-		break;
-
-	case OHCI_USB_SUSPEND:	// host wakeup
-	case OHCI_USB_RESUME:	// remote wakeup
-		info ("USB continue: usb-%s from %s wakeup", dev->slot_name,
-			(temp == OHCI_USB_SUSPEND)
-				? "host" : "remote");
-		ohci->hc_control = OHCI_USB_RESUME;
-		writel (ohci->hc_control, &ohci->regs->control);
-		(void) readl (&ohci->regs->control);
-		mdelay (20); /* no schedule here ! */
-		/* Some controllers (lucent) need a longer delay here */
-		mdelay (15);
-		temp = readl (&ohci->regs->control);
-		temp = ohci->hc_control & OHCI_CTRL_HCFS;
-		if (temp != OHCI_USB_RESUME) {
-			err ("controller usb-%s won't resume", dev->slot_name);
-			ohci->disabled = 1;
-			return -EIO;
-		}
-
-		/* Some chips likes being resumed first */
-		writel (OHCI_USB_OPER, &ohci->regs->control);
-		(void) readl (&ohci->regs->control);
-		mdelay (3);
-
-		/* Then re-enable operations */
-		spin_lock_irqsave (&usb_ed_lock, flags);
-		ohci->disabled = 0;
-		ohci->sleeping = 0;
-		ohci->hc_control = OHCI_CONTROL_INIT | OHCI_USB_OPER;
-		if (!ohci->ed_rm_list[0] && !ohci->ed_rm_list[1]) {
-			if (ohci->ed_controltail)
-				ohci->hc_control |= OHCI_CTRL_CLE;
-			if (ohci->ed_bulktail)
-				ohci->hc_control |= OHCI_CTRL_BLE;
-		}
-		writel (ohci->hc_control, &ohci->regs->control);
-		writel (OHCI_INTR_SF, &ohci->regs->intrstatus);
-		writel (OHCI_INTR_SF, &ohci->regs->intrenable);
-		/* Check for a pending done list */
-		writel (OHCI_INTR_WDH, &ohci->regs->intrdisable);	
-		(void) readl (&ohci->regs->intrdisable);
-		spin_unlock_irqrestore (&usb_ed_lock, flags);
-#ifdef CONFIG_PMAC_PBOOK
-		if (_machine == _MACH_Pmac)
-			enable_irq (ohci->irq);
-#endif
-		if (ohci->hcca->done_head)
-			dl_done_list (ohci, dl_reverse_done_list (ohci));
-		writel (OHCI_INTR_WDH, &ohci->regs->intrenable); 
-		writel (OHCI_BLF, &ohci->regs->cmdstatus); /* start bulk list */
-		writel (OHCI_CLF, &ohci->regs->cmdstatus); /* start Control list */
-		break;
-
-	default:
-		warn ("odd PCI resume for usb-%s", dev->slot_name);
-	}
-
-	/* controller is operational, extra resumes are harmless */
-	atomic_dec (&ohci->resume_count);
-
-	return 0;
-}
-
-#endif	/* CONFIG_PM */
-
-
-/*-------------------------------------------------------------------------*/
-
-static const struct pci_device_id __devinitdata ohci_pci_ids [] = { {
-
-	/*
-	 * AMD-756 [Viper] USB has a serious erratum when used with
-	 * lowspeed devices like mice.
-	 */
-	vendor:		0x1022,
-	device:		0x740c,
-	subvendor:	PCI_ANY_ID,
-	subdevice:	PCI_ANY_ID,
-
-	driver_data:	OHCI_QUIRK_AMD756,
-
-} , {
-
-	/* handle any USB OHCI controller */
-	class: 		((PCI_CLASS_SERIAL_USB << 8) | 0x10),
-	class_mask: 	~0,
-
-	/* no matter who makes it */
-	vendor:		PCI_ANY_ID,
-	device:		PCI_ANY_ID,
-	subvendor:	PCI_ANY_ID,
-	subdevice:	PCI_ANY_ID,
-
-	}, { /* end: all zeroes */ }
-};
-
-MODULE_DEVICE_TABLE (pci, ohci_pci_ids);
-
-static struct pci_driver ohci_pci_driver = {
-	name:		"usb-ohci",
-	id_table:	&ohci_pci_ids [0],
-
-	probe:		ohci_pci_probe,
-	remove:		__devexit_p(ohci_pci_remove),
-
-#ifdef	CONFIG_PM
-	suspend:	ohci_pci_suspend,
-	resume:		ohci_pci_resume,
-#endif	/* PM */
-};
-
- 
-/*-------------------------------------------------------------------------*/
-
-static int __init ohci_hcd_init (void) 
-{
-	return pci_module_init (&ohci_pci_driver);
-}
-
-/*-------------------------------------------------------------------------*/
-
-static void __exit ohci_hcd_cleanup (void) 
-{	
-	pci_unregister_driver (&ohci_pci_driver);
-}
-
-module_init (ohci_hcd_init);
-module_exit (ohci_hcd_cleanup);
-
-
-MODULE_AUTHOR( DRIVER_AUTHOR );
-MODULE_DESCRIPTION( DRIVER_DESC );
-MODULE_LICENSE("GPL");
diff -urN linux-2.4.21-pre5/drivers/usb/usb-ohci.h linux-2.4.21-pre6/drivers/usb/usb-ohci.h
--- linux-2.4.21-pre5/drivers/usb/usb-ohci.h	Wed Mar 26 16:11:29 2003
+++ linux-2.4.21-pre6/drivers/usb/usb-ohci.h	Wed Dec 31 16:00:00 1969
@@ -1,643 +0,0 @@
-/*
- * URB OHCI HCD (Host Controller Driver) for USB.
- * 
- * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
- * (C) Copyright 2000-2001 David Brownell <dbrownell@users.sourceforge.net>
- * 
- * usb-ohci.h
- */
-
- 
-static int cc_to_error[16] = { 
-
-/* mapping of the OHCI CC status to error codes */ 
-	/* No  Error  */               USB_ST_NOERROR,
-	/* CRC Error  */               USB_ST_CRC,
-	/* Bit Stuff  */               USB_ST_BITSTUFF,
-	/* Data Togg  */               USB_ST_CRC,
-	/* Stall      */               USB_ST_STALL,
-	/* DevNotResp */               USB_ST_NORESPONSE,
-	/* PIDCheck   */               USB_ST_BITSTUFF,
-	/* UnExpPID   */               USB_ST_BITSTUFF,
-	/* DataOver   */               USB_ST_DATAOVERRUN,
-	/* DataUnder  */               USB_ST_DATAUNDERRUN,
-	/* reservd    */               USB_ST_NORESPONSE,
-	/* reservd    */               USB_ST_NORESPONSE,
-	/* BufferOver */               USB_ST_BUFFEROVERRUN,
-	/* BuffUnder  */               USB_ST_BUFFERUNDERRUN,
-	/* Not Access */               USB_ST_NORESPONSE,
-	/* Not Access */               USB_ST_NORESPONSE 
-};
-
-#include <linux/config.h>
-
-/* ED States */
-
-#define ED_NEW 		0x00
-#define ED_UNLINK 	0x01
-#define ED_OPER		0x02
-#define ED_DEL		0x04
-#define ED_URB_DEL  	0x08
-
-/* usb_ohci_ed */
-struct ed {
-	__u32 hwINFO;       
-	__u32 hwTailP;
-	__u32 hwHeadP;
-	__u32 hwNextED;
-
-	struct ed * ed_prev;  
-	__u8 int_period;
-	__u8 int_branch;
-	__u8 int_load; 
-	__u8 int_interval;
-	__u8 state;
-	__u8 type; 
-	__u16 last_iso;
-	struct ed * ed_rm_list;
-
-	dma_addr_t dma;
-	__u32 unused[3];
-} __attribute((aligned(16)));
-typedef struct ed ed_t;
-
- 
-/* TD info field */
-#define TD_CC       0xf0000000
-#define TD_CC_GET(td_p) ((td_p >>28) & 0x0f)
-#define TD_CC_SET(td_p, cc) (td_p) = ((td_p) & 0x0fffffff) | (((cc) & 0x0f) << 28)
-#define TD_EC       0x0C000000
-#define TD_T        0x03000000
-#define TD_T_DATA0  0x02000000
-#define TD_T_DATA1  0x03000000
-#define TD_T_TOGGLE 0x00000000
-#define TD_R        0x00040000
-#define TD_DI       0x00E00000
-#define TD_DI_SET(X) (((X) & 0x07)<< 21)
-#define TD_DP       0x00180000
-#define TD_DP_SETUP 0x00000000
-#define TD_DP_IN    0x00100000
-#define TD_DP_OUT   0x00080000
-
-#define TD_ISO	    0x00010000
-#define TD_DEL      0x00020000
-
-/* CC Codes */
-#define TD_CC_NOERROR      0x00
-#define TD_CC_CRC          0x01
-#define TD_CC_BITSTUFFING  0x02
-#define TD_CC_DATATOGGLEM  0x03
-#define TD_CC_STALL        0x04
-#define TD_DEVNOTRESP      0x05
-#define TD_PIDCHECKFAIL    0x06
-#define TD_UNEXPECTEDPID   0x07
-#define TD_DATAOVERRUN     0x08
-#define TD_DATAUNDERRUN    0x09
-#define TD_BUFFEROVERRUN   0x0C
-#define TD_BUFFERUNDERRUN  0x0D
-#define TD_NOTACCESSED     0x0F
-
-
-#define MAXPSW 1
-
-struct td {
-	__u32 hwINFO;
-  	__u32 hwCBP;		/* Current Buffer Pointer */
-  	__u32 hwNextTD;		/* Next TD Pointer */
-  	__u32 hwBE;		/* Memory Buffer End Pointer */
-
-  	__u16 hwPSW[MAXPSW];
-  	__u8 unused;
-  	__u8 index;
-  	struct ed * ed;
-  	struct td * next_dl_td;
-  	struct urb * urb;
-
-	dma_addr_t td_dma;
-	dma_addr_t data_dma;
-	__u32 unused2[2];
-} __attribute((aligned(32)));	/* normally 16, iso needs 32 */
-typedef struct td td_t;
-
-#define OHCI_ED_SKIP	(1 << 14)
-
-/*
- * The HCCA (Host Controller Communications Area) is a 256 byte
- * structure defined in the OHCI spec. that the host controller is
- * told the base address of.  It must be 256-byte aligned.
- */
- 
-#define NUM_INTS 32	/* part of the OHCI standard */
-struct ohci_hcca {
-	__u32	int_table[NUM_INTS];	/* Interrupt ED table */
-	__u16	frame_no;		/* current frame number */
-	__u16	pad1;			/* set to 0 on each frame_no change */
-	__u32	done_head;		/* info returned for an interrupt */
-	u8		reserved_for_hc[116];
-} __attribute((aligned(256)));
-
-  
-/*
- * Maximum number of root hub ports.  
- */
-#define MAX_ROOT_PORTS	15	/* maximum OHCI root hub ports */
-
-/*
- * This is the structure of the OHCI controller's memory mapped I/O
- * region.  This is Memory Mapped I/O.  You must use the readl() and
- * writel() macros defined in asm/io.h to access these!!
- */
-struct ohci_regs {
-	/* control and status registers */
-	__u32	revision;
-	__u32	control;
-	__u32	cmdstatus;
-	__u32	intrstatus;
-	__u32	intrenable;
-	__u32	intrdisable;
-	/* memory pointers */
-	__u32	hcca;
-	__u32	ed_periodcurrent;
-	__u32	ed_controlhead;
-	__u32	ed_controlcurrent;
-	__u32	ed_bulkhead;
-	__u32	ed_bulkcurrent;
-	__u32	donehead;
-	/* frame counters */
-	__u32	fminterval;
-	__u32	fmremaining;
-	__u32	fmnumber;
-	__u32	periodicstart;
-	__u32	lsthresh;
-	/* Root hub ports */
-	struct	ohci_roothub_regs {
-		__u32	a;
-		__u32	b;
-		__u32	status;
-		__u32	portstatus[MAX_ROOT_PORTS];
-	} roothub;
-} __attribute((aligned(32)));
-
-
-/* OHCI CONTROL AND STATUS REGISTER MASKS */
-
-/*
- * HcControl (control) register masks
- */
-#define OHCI_CTRL_CBSR	(3 << 0)	/* control/bulk service ratio */
-#define OHCI_CTRL_PLE	(1 << 2)	/* periodic list enable */
-#define OHCI_CTRL_IE	(1 << 3)	/* isochronous enable */
-#define OHCI_CTRL_CLE	(1 << 4)	/* control list enable */
-#define OHCI_CTRL_BLE	(1 << 5)	/* bulk list enable */
-#define OHCI_CTRL_HCFS	(3 << 6)	/* host controller functional state */
-#define OHCI_CTRL_IR	(1 << 8)	/* interrupt routing */
-#define OHCI_CTRL_RWC	(1 << 9)	/* remote wakeup connected */
-#define OHCI_CTRL_RWE	(1 << 10)	/* remote wakeup enable */
-
-/* pre-shifted values for HCFS */
-#	define OHCI_USB_RESET	(0 << 6)
-#	define OHCI_USB_RESUME	(1 << 6)
-#	define OHCI_USB_OPER	(2 << 6)
-#	define OHCI_USB_SUSPEND	(3 << 6)
-
-/*
- * HcCommandStatus (cmdstatus) register masks
- */
-#define OHCI_HCR	(1 << 0)	/* host controller reset */
-#define OHCI_CLF  	(1 << 1)	/* control list filled */
-#define OHCI_BLF  	(1 << 2)	/* bulk list filled */
-#define OHCI_OCR  	(1 << 3)	/* ownership change request */
-#define OHCI_SOC  	(3 << 16)	/* scheduling overrun count */
-
-/*
- * masks used with interrupt registers:
- * HcInterruptStatus (intrstatus)
- * HcInterruptEnable (intrenable)
- * HcInterruptDisable (intrdisable)
- */
-#define OHCI_INTR_SO	(1 << 0)	/* scheduling overrun */
-#define OHCI_INTR_WDH	(1 << 1)	/* writeback of done_head */
-#define OHCI_INTR_SF	(1 << 2)	/* start frame */
-#define OHCI_INTR_RD	(1 << 3)	/* resume detect */
-#define OHCI_INTR_UE	(1 << 4)	/* unrecoverable error */
-#define OHCI_INTR_FNO	(1 << 5)	/* frame number overflow */
-#define OHCI_INTR_RHSC	(1 << 6)	/* root hub status change */
-#define OHCI_INTR_OC	(1 << 30)	/* ownership change */
-#define OHCI_INTR_MIE	(1 << 31)	/* master interrupt enable */
-
-
-
-/* Virtual Root HUB */
-struct virt_root_hub {
-	int devnum; /* Address of Root Hub endpoint */ 
-	void * urb;
-	void * int_addr;
-	int send;
-	int interval;
-	struct timer_list rh_int_timer;
-};
-
-
-/* USB HUB CONSTANTS (not OHCI-specific; see hub.h) */
- 
-/* destination of request */
-#define RH_INTERFACE               0x01
-#define RH_ENDPOINT                0x02
-#define RH_OTHER                   0x03
-
-#define RH_CLASS                   0x20
-#define RH_VENDOR                  0x40
-
-/* 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
-/* Our Vendor Specific Request */
-#define RH_SET_EP               0x2000
-
-
-/* Hub port features */
-#define RH_PORT_CONNECTION         0x00
-#define RH_PORT_ENABLE             0x01
-#define RH_PORT_SUSPEND            0x02
-#define RH_PORT_OVER_CURRENT       0x03
-#define RH_PORT_RESET              0x04
-#define RH_PORT_POWER              0x08
-#define RH_PORT_LOW_SPEED          0x09
-
-#define RH_C_PORT_CONNECTION       0x10
-#define RH_C_PORT_ENABLE           0x11
-#define RH_C_PORT_SUSPEND          0x12
-#define RH_C_PORT_OVER_CURRENT     0x13
-#define RH_C_PORT_RESET            0x14  
-
-/* Hub features */
-#define RH_C_HUB_LOCAL_POWER       0x00
-#define RH_C_HUB_OVER_CURRENT      0x01
-
-#define RH_DEVICE_REMOTE_WAKEUP    0x00
-#define RH_ENDPOINT_STALL          0x01
-
-#define RH_ACK                     0x01
-#define RH_REQ_ERR                 -1
-#define RH_NACK                    0x00
-
-
-/* OHCI ROOT HUB REGISTER MASKS */
- 
-/* roothub.portstatus [i] bits */
-#define RH_PS_CCS            0x00000001   	/* current connect status */
-#define RH_PS_PES            0x00000002   	/* port enable status*/
-#define RH_PS_PSS            0x00000004   	/* port suspend status */
-#define RH_PS_POCI           0x00000008   	/* port over current indicator */
-#define RH_PS_PRS            0x00000010  	/* port reset status */
-#define RH_PS_PPS            0x00000100   	/* port power status */
-#define RH_PS_LSDA           0x00000200    	/* low speed device attached */
-#define RH_PS_CSC            0x00010000 	/* connect status change */
-#define RH_PS_PESC           0x00020000   	/* port enable status change */
-#define RH_PS_PSSC           0x00040000    	/* port suspend status change */
-#define RH_PS_OCIC           0x00080000    	/* over current indicator change */
-#define RH_PS_PRSC           0x00100000   	/* port reset status change */
-
-/* roothub.status bits */
-#define RH_HS_LPS	     0x00000001		/* local power status */
-#define RH_HS_OCI	     0x00000002		/* over current indicator */
-#define RH_HS_DRWE	     0x00008000		/* device remote wakeup enable */
-#define RH_HS_LPSC	     0x00010000		/* local power status change */
-#define RH_HS_OCIC	     0x00020000		/* over current indicator change */
-#define RH_HS_CRWE	     0x80000000		/* clear remote wakeup enable */
-
-/* roothub.b masks */
-#define RH_B_DR		0x0000ffff		/* device removable flags */
-#define RH_B_PPCM	0xffff0000		/* port power control mask */
-
-/* roothub.a masks */
-#define	RH_A_NDP	(0xff << 0)		/* number of downstream ports */
-#define	RH_A_PSM	(1 << 8)		/* power switching mode */
-#define	RH_A_NPS	(1 << 9)		/* no power switching */
-#define	RH_A_DT		(1 << 10)		/* device type (mbz) */
-#define	RH_A_OCPM	(1 << 11)		/* over current protection mode */
-#define	RH_A_NOCP	(1 << 12)		/* no over current protection */
-#define	RH_A_POTPGT	(0xff << 24)		/* power on to power good time */
-
-/* urb */
-typedef struct 
-{
-	ed_t * ed;
-	__u16 length;	// number of tds associated with this request
-	__u16 td_cnt;	// number of tds already serviced
-	int   state;
-	wait_queue_head_t * wait;
-	td_t * td[0];	// list pointer to all corresponding TDs associated with this request
-
-} urb_priv_t;
-#define URB_DEL 1
-
-
-/* Hash struct used for TD/ED hashing */
-struct hash_t {
-	void		*virt;
-	dma_addr_t	dma;
-	struct hash_t	*next; // chaining for collision cases
-};
-
-/* List of TD/ED hash entries */
-struct hash_list_t {
-	struct hash_t	*head;
-	struct hash_t	*tail;
-};
-
-#define TD_HASH_SIZE    64    /* power'o'two */
-#define ED_HASH_SIZE    64    /* power'o'two */
-
-#define TD_HASH_FUNC(td_dma) ((td_dma ^ (td_dma >> 5)) % TD_HASH_SIZE)
-#define ED_HASH_FUNC(ed_dma) ((ed_dma ^ (ed_dma >> 5)) % ED_HASH_SIZE)
-
-
-/*
- * This is the full ohci controller description
- *
- * Note how the "proper" USB information is just
- * a subset of what the full implementation needs. (Linus)
- */
-
-
-typedef struct ohci {
-	struct ohci_hcca *hcca;		/* hcca */
-	dma_addr_t hcca_dma;
-
-	int irq;
-	int disabled;			/* e.g. got a UE, we're hung */
-	int sleeping;
-	atomic_t resume_count;		/* defending against multiple resumes */
-	unsigned long flags;		/* for HC bugs */
-#define	OHCI_QUIRK_AMD756	0x01		/* erratum #4 */
-#define OHCI_QUIRK_SUCKYIO	0x02		/* NSC superio */
-
-	struct ohci_regs * regs;	/* OHCI controller's memory */
-	struct list_head ohci_hcd_list;	/* list of all ohci_hcd */
-
-	struct list_head timeout_list;
-	// struct list_head urb_list; 	// list of all pending urbs
-	// spinlock_t urb_list_lock; 	// lock to keep consistency 
-  
-	int ohci_int_load[32];		/* load of the 32 Interrupt Chains (for load balancing)*/
-	ed_t * ed_rm_list[2];     /* lists of all endpoints to be removed */
-	ed_t * ed_bulktail;       /* last endpoint of bulk list */
-	ed_t * ed_controltail;    /* last endpoint of control list */
- 	ed_t * ed_isotail;        /* last endpoint of iso list */
-	int intrstatus;
-	__u32 hc_control;		/* copy of the hc control reg */
-	struct usb_bus * bus;    
-	struct usb_device * dev[128];
-	struct virt_root_hub rh;
-
-	/* PCI device handle, settings, ... */
-	struct pci_dev	*ohci_dev;
-	u8		pci_latency;
-	struct pci_pool	*td_cache;
-	struct pci_pool	*dev_cache;
-	struct hash_list_t	td_hash[TD_HASH_SIZE];
-	struct hash_list_t	ed_hash[ED_HASH_SIZE];
-
-} ohci_t;
-
-#define NUM_EDS 32		/* num of preallocated endpoint descriptors */
-
-struct ohci_device {
-	ed_t 	ed[NUM_EDS];
-	dma_addr_t dma;
-	int ed_cnt;
-	wait_queue_head_t * wait;
-};
-
-// #define ohci_to_usb(ohci)	((ohci)->usb)
-#define usb_to_ohci(usb)	((struct ohci_device *)(usb)->hcpriv)
-
-/* hcd */
-/* endpoint */
-static int ep_link(ohci_t * ohci, ed_t * ed);
-static int ep_unlink(ohci_t * ohci, ed_t * ed);
-static ed_t * ep_add_ed(struct usb_device * usb_dev, unsigned int pipe, int interval, int load, int mem_flags);
-static void ep_rm_ed(struct usb_device * usb_dev, ed_t * ed);
-/* td */
-static void td_fill(ohci_t * ohci, unsigned int info, dma_addr_t data, int len, struct urb * urb, int index);
-static void td_submit_urb(struct urb * urb);
-/* root hub */
-static int rh_submit_urb(struct urb * urb);
-static int rh_unlink_urb(struct urb * urb);
-static int rh_init_int_timer(struct urb * urb);
-
-/*-------------------------------------------------------------------------*/
-
-#define ALLOC_FLAGS (in_interrupt () || current->state != TASK_RUNNING ? GFP_ATOMIC : GFP_KERNEL)
-
-#ifdef DEBUG
-#	define OHCI_MEM_FLAGS	SLAB_POISON
-#else
-#	define OHCI_MEM_FLAGS	0
-#endif
- 
-#ifndef CONFIG_PCI
-#	error "usb-ohci currently requires PCI-based controllers"
-	/* to support non-PCI OHCIs, you need custom bus/mem/... glue */
-#endif
-
-
-/* Recover a TD/ED using its collision chain */
-static inline void *
-dma_to_ed_td (struct hash_list_t * entry, dma_addr_t dma)
-{
-	struct hash_t * scan = entry->head;
-	while (scan && scan->dma != dma)
-		scan = scan->next;
-	if (!scan)
-		BUG();
-	return scan->virt;
-}
-
-static inline struct ed *
-dma_to_ed (struct ohci * hc, dma_addr_t ed_dma)
-{
-	return (struct ed *) dma_to_ed_td(&(hc->ed_hash[ED_HASH_FUNC(ed_dma)]),
-				      ed_dma);
-}
-
-static inline struct td *
-dma_to_td (struct ohci * hc, dma_addr_t td_dma)
-{
-	return (struct td *) dma_to_ed_td(&(hc->td_hash[TD_HASH_FUNC(td_dma)]),
-				      td_dma);
-}
-
-/* Add a hash entry for a TD/ED; return true on success */
-static inline int
-hash_add_ed_td(struct hash_list_t * entry, void * virt, dma_addr_t dma)
-{
-	struct hash_t * scan;
-	
-	scan = (struct hash_t *)kmalloc(sizeof(struct hash_t), ALLOC_FLAGS);
-	if (!scan)
-		return 0;
-	
-	if (!entry->tail) {
-		entry->head = entry->tail = scan;
-	} else {
-		entry->tail->next = scan;
-		entry->tail = scan;
-	}
-	
-	scan->virt = virt;
-	scan->dma = dma;
-	scan->next = NULL;
-	return 1;
-}
-
-static inline int
-hash_add_ed (struct ohci * hc, struct ed * ed)
-{
-	return hash_add_ed_td (&(hc->ed_hash[ED_HASH_FUNC(ed->dma)]),
-			ed, ed->dma);
-}
-
-static inline int
-hash_add_td (struct ohci * hc, struct td * td)
-{
-	return hash_add_ed_td (&(hc->td_hash[TD_HASH_FUNC(td->td_dma)]),
-			td, td->td_dma);
-}
-
-
-static inline void
-hash_free_ed_td (struct hash_list_t * entry, void * virt)
-{
-	struct hash_t *scan, *prev;
-	scan = prev = entry->head;
-
-	// Find and unlink hash entry
-	while (scan && scan->virt != virt) {
-		prev = scan;
-		scan = scan->next;
-	}
-	if (scan) {
-		if (scan == entry->head) {
-			if (entry->head == entry->tail)
-				entry->head = entry->tail = NULL;
-			else
-				entry->head = scan->next;
-		} else if (scan == entry->tail) {
-			entry->tail = prev;
-			prev->next = NULL;
-		} else
-			prev->next = scan->next;
-		kfree(scan);
-	}
-}
-
-static inline void
-hash_free_ed (struct ohci * hc, struct ed * ed)
-{
-	hash_free_ed_td (&(hc->ed_hash[ED_HASH_FUNC(ed->dma)]), ed);
-}
-
-static inline void
-hash_free_td (struct ohci * hc, struct td * td)
-{
-	hash_free_ed_td (&(hc->td_hash[TD_HASH_FUNC(td->td_dma)]), td);
-}
-
-
-static int ohci_mem_init (struct ohci *ohci)
-{
-	ohci->td_cache = pci_pool_create ("ohci_td", ohci->ohci_dev,
-		sizeof (struct td),
-		32 /* byte alignment */,
-		0 /* no page-crossing issues */,
-		GFP_KERNEL | OHCI_MEM_FLAGS);
-	if (!ohci->td_cache)
-		return -ENOMEM;
-	ohci->dev_cache = pci_pool_create ("ohci_dev", ohci->ohci_dev,
-		sizeof (struct ohci_device),
-		16 /* byte alignment */,
-		0 /* no page-crossing issues */,
-		GFP_KERNEL | OHCI_MEM_FLAGS);
-	if (!ohci->dev_cache)
-		return -ENOMEM;
-	return 0;
-}
-
-static void ohci_mem_cleanup (struct ohci *ohci)
-{
-	if (ohci->td_cache) {
-		pci_pool_destroy (ohci->td_cache);
-		ohci->td_cache = 0;
-	}
-	if (ohci->dev_cache) {
-		pci_pool_destroy (ohci->dev_cache);
-		ohci->dev_cache = 0;
-	}
-}
-
-/* TDs ... */
-static inline struct td *
-td_alloc (struct ohci *hc, int mem_flags)
-{
-	dma_addr_t	dma;
-	struct td	*td;
-
-	td = pci_pool_alloc (hc->td_cache, mem_flags, &dma);
-	if (td) {
-		td->td_dma = dma;
-
-		/* hash it for later reverse mapping */
-		if (!hash_add_td (hc, td)) {
-			pci_pool_free (hc->td_cache, td, dma);
-			return NULL;
-		}
-	}
-	return td;
-}
-
-static inline void
-td_free (struct ohci *hc, struct td *td)
-{
-	hash_free_td (hc, td);
-	pci_pool_free (hc->td_cache, td, td->td_dma);
-}
-
-
-/* DEV + EDs ... only the EDs need to be consistent */
-static inline struct ohci_device *
-dev_alloc (struct ohci *hc, int mem_flags)
-{
-	dma_addr_t		dma;
-	struct ohci_device	*dev;
-	int			i, offset;
-
-	dev = pci_pool_alloc (hc->dev_cache, mem_flags, &dma);
-	if (dev) {
-		memset (dev, 0, sizeof (*dev));
-		dev->dma = dma;
-		offset = ((char *)&dev->ed) - ((char *)dev);
-		for (i = 0; i < NUM_EDS; i++, offset += sizeof dev->ed [0])
-			dev->ed [i].dma = dma + offset;
-		/* add to hashtable if used */
-	}
-	return dev;
-}
-
-static inline void
-dev_free (struct ohci *hc, struct ohci_device *dev)
-{
-	pci_pool_free (hc->dev_cache, dev, dev->dma);
-}
-
diff -urN linux-2.4.21-pre5/drivers/usb/usb-uhci-debug.h linux-2.4.21-pre6/drivers/usb/usb-uhci-debug.h
--- linux-2.4.21-pre5/drivers/usb/usb-uhci-debug.h	Tue Oct 30 15:08:12 2001
+++ linux-2.4.21-pre6/drivers/usb/usb-uhci-debug.h	Wed Dec 31 16:00:00 1969
@@ -1,195 +0,0 @@
-#ifdef DEBUG
-static void __attribute__((__unused__)) uhci_show_qh (puhci_desc_t qh)
-{
-	if (qh->type != QH_TYPE) {
-		dbg("qh has not QH_TYPE");
-		return;
-	}
-	dbg("QH @ %p/%08llX:", qh, (unsigned long long)qh->dma_addr);
-
-	if (qh->hw.qh.head & UHCI_PTR_TERM)
-		dbg("    Head Terminate");
-	else 
-		dbg("    Head: %s @ %08X",
-		    (qh->hw.qh.head & UHCI_PTR_QH?"QH":"TD"),
-		    qh->hw.qh.head & ~UHCI_PTR_BITS);
-
-	if (qh->hw.qh.element & UHCI_PTR_TERM)
-		dbg("    Element Terminate");
-	else 
-		dbg("    Element: %s @ %08X",
-		    (qh->hw.qh.element & UHCI_PTR_QH?"QH":"TD"),
-		    qh->hw.qh.element & ~UHCI_PTR_BITS);
-}
-#endif
-
-#if 0
-static void uhci_show_td (puhci_desc_t td)
-{
-	char *spid;
-	
-	switch (td->hw.td.info & 0xff) {
-	case USB_PID_SETUP:
-		spid = "SETUP";
-		break;
-	case USB_PID_OUT:
-		spid = " OUT ";
-		break;
-	case USB_PID_IN:
-		spid = " IN  ";
-		break;
-	default:
-		spid = "  ?  ";
-		break;
-	}
-
-	warn("  TD @ %p/%08X, MaxLen=%02x DT%d EP=%x Dev=%x PID=(%s) buf=%08x",
-	     td, td->dma_addr,
-	     td->hw.td.info >> 21,
-	     ((td->hw.td.info >> 19) & 1),
-	     (td->hw.td.info >> 15) & 15,
-	     (td->hw.td.info >> 8) & 127,
-	     spid,
-	     td->hw.td.buffer);
-
-	warn("    Len=%02x e%d %s%s%s%s%s%s%s%s%s%s",
-	     td->hw.td.status & 0x7ff,
-	     ((td->hw.td.status >> 27) & 3),
-	     (td->hw.td.status & TD_CTRL_SPD) ? "SPD " : "",
-	     (td->hw.td.status & TD_CTRL_LS) ? "LS " : "",
-	     (td->hw.td.status & TD_CTRL_IOC) ? "IOC " : "",
-	     (td->hw.td.status & TD_CTRL_ACTIVE) ? "Active " : "",
-	     (td->hw.td.status & TD_CTRL_STALLED) ? "Stalled " : "",
-	     (td->hw.td.status & TD_CTRL_DBUFERR) ? "DataBufErr " : "",
-	     (td->hw.td.status & TD_CTRL_BABBLE) ? "Babble " : "",
-	     (td->hw.td.status & TD_CTRL_NAK) ? "NAK " : "",
-	     (td->hw.td.status & TD_CTRL_CRCTIMEO) ? "CRC/Timeo " : "",
-	     (td->hw.td.status & TD_CTRL_BITSTUFF) ? "BitStuff " : ""
-		);
-
-	if (td->hw.td.link & UHCI_PTR_TERM)
-		warn("   TD Link Terminate");
-	else 
-		warn("    Link points to %s @ %08x, %s",
-		     (td->hw.td.link & UHCI_PTR_QH?"QH":"TD"),
-		     td->hw.td.link & ~UHCI_PTR_BITS,
-		     (td->hw.td.link & UHCI_PTR_DEPTH ? "Depth first" : "Breadth first"));
-}
-#endif
-
-#ifdef DEBUG
-static void __attribute__((__unused__)) uhci_show_td_queue (puhci_desc_t td)
-{
-	//dbg("uhci_show_td_queue %p (%08lX):", td, td->dma_addr);
-#if 1
-	return;
-#else
-	while (1) {
-		uhci_show_td (td);
-		if (td->hw.td.link & UHCI_PTR_TERM)
-			break;
-		if (td != bus_to_virt (td->hw.td.link & ~UHCI_PTR_BITS))
-			td = bus_to_virt (td->hw.td.link & ~UHCI_PTR_BITS);
-		else {
-			dbg("td points to itself!");
-			break;
-		}
-	}
-#endif
-}
-
-static void __attribute__((__unused__)) uhci_show_queue (puhci_desc_t qh)
-{
-#if 0
-	uhci_desc_t *start_qh=qh;
-#endif
-
-	dbg("uhci_show_queue %p:", qh);
-#if 1
-	return;
-#else
-	while (1) {
-		uhci_show_qh (qh);
-
-		if (!(qh->hw.qh.element & UHCI_PTR_TERM))
-			uhci_show_td_queue (bus_to_virt (qh->hw.qh.element & ~UHCI_PTR_BITS));
-
-		if (qh->hw.qh.head & UHCI_PTR_TERM)
-			break;
-
-		if (qh != bus_to_virt (qh->hw.qh.head & ~UHCI_PTR_BITS))
-			qh = bus_to_virt (qh->hw.qh.head & ~UHCI_PTR_BITS);
-		else {
-			dbg("qh points to itself!");
-			break;
-		}
-		
-		if (qh==start_qh) { // avoid loop
-			dbg("Loop detect");
-			break;
-		}
-	}		
-#endif
-}
-
-static void __attribute__((__unused__)) uhci_show_sc (int port, unsigned short status)
-{
-	dbg("  stat%d     =     %04x   %s%s%s%s%s%s%s%s",
-	     port,
-	     status,
-	     (status & USBPORTSC_SUSP) ? "PortSuspend " : "",
-	     (status & USBPORTSC_PR) ? "PortReset " : "",
-	     (status & USBPORTSC_LSDA) ? "LowSpeed " : "",
-	     (status & USBPORTSC_RD) ? "ResumeDetect " : "",
-	     (status & USBPORTSC_PEC) ? "EnableChange " : "",
-	     (status & USBPORTSC_PE) ? "PortEnabled " : "",
-	     (status & USBPORTSC_CSC) ? "ConnectChange " : "",
-	     (status & USBPORTSC_CCS) ? "PortConnected " : "");
-}
-
-void uhci_show_status (puhci_t s)
-{
-	unsigned int io_addr = s->io_addr;
-	unsigned short usbcmd, usbstat, usbint, usbfrnum;
-	unsigned int flbaseadd;
-	unsigned char sof;
-	unsigned short portsc1, portsc2;
-
-	usbcmd = inw (io_addr + 0);
-	usbstat = inw (io_addr + 2);
-	usbint = inw (io_addr + 4);
-	usbfrnum = inw (io_addr + 6);
-	flbaseadd = inl (io_addr + 8);
-	sof = inb (io_addr + 12);
-	portsc1 = inw (io_addr + 16);
-	portsc2 = inw (io_addr + 18);
-
-	dbg("  usbcmd    =     %04x   %s%s%s%s%s%s%s%s",
-	     usbcmd,
-	     (usbcmd & USBCMD_MAXP) ? "Maxp64 " : "Maxp32 ",
-	     (usbcmd & USBCMD_CF) ? "CF " : "",
-	     (usbcmd & USBCMD_SWDBG) ? "SWDBG " : "",
-	     (usbcmd & USBCMD_FGR) ? "FGR " : "",
-	     (usbcmd & USBCMD_EGSM) ? "EGSM " : "",
-	     (usbcmd & USBCMD_GRESET) ? "GRESET " : "",
-	     (usbcmd & USBCMD_HCRESET) ? "HCRESET " : "",
-	     (usbcmd & USBCMD_RS) ? "RS " : "");
-
-	dbg("  usbstat   =     %04x   %s%s%s%s%s%s",
-	     usbstat,
-	     (usbstat & USBSTS_HCH) ? "HCHalted " : "",
-	     (usbstat & USBSTS_HCPE) ? "HostControllerProcessError " : "",
-	     (usbstat & USBSTS_HSE) ? "HostSystemError " : "",
-	     (usbstat & USBSTS_RD) ? "ResumeDetect " : "",
-	     (usbstat & USBSTS_ERROR) ? "USBError " : "",
-	     (usbstat & USBSTS_USBINT) ? "USBINT " : "");
-
-	dbg("  usbint    =     %04x", usbint);
-	dbg("  usbfrnum  =   (%d)%03x", (usbfrnum >> 10) & 1,
-	     0xfff & (4 * (unsigned int) usbfrnum));
-	dbg("  flbaseadd = %08x", flbaseadd);
-	dbg("  sof       =       %02x", sof);
-	uhci_show_sc (1, portsc1);
-	uhci_show_sc (2, portsc2);
-}
-#endif
diff -urN linux-2.4.21-pre5/drivers/usb/usb-uhci.c linux-2.4.21-pre6/drivers/usb/usb-uhci.c
--- linux-2.4.21-pre5/drivers/usb/usb-uhci.c	Wed Mar 26 16:11:29 2003
+++ linux-2.4.21-pre6/drivers/usb/usb-uhci.c	Wed Dec 31 16:00:00 1969
@@ -1,3143 +0,0 @@
-/* 
- * Universal Host Controller Interface driver for USB (take II).
- *
- * (c) 1999-2001 Georg Acher, acher@in.tum.de (executive slave) (base guitar)
- *               Deti Fliegl, deti@fliegl.de (executive slave) (lead voice)
- *               Thomas Sailer, sailer@ife.ee.ethz.ch (chief consultant) (cheer leader)
- *               Roman Weissgaerber, weissg@vienna.at (virt root hub) (studio porter)
- * (c) 2000      Yggdrasil Computing, Inc. (port of new PCI interface support
- *               from usb-ohci.c by Adam Richter, adam@yggdrasil.com).
- * (C) 2000      David Brownell, david-b@pacbell.net (usb-ohci.c)
- *          
- * HW-initalization based on material of
- *
- * (C) Copyright 1999 Linus Torvalds
- * (C) Copyright 1999 Johannes Erdfelt
- * (C) Copyright 1999 Randy Dunlap
- * (C) Copyright 1999 Gregory P. Smith
- *
- * $Id: usb-uhci.c,v 1.275 2002/01/19 20:57:33 acher Exp $
- */
-
-#include <linux/config.h>
-#include <linux/module.h>
-#include <linux/pci.h>
-#include <linux/kernel.h>
-#include <linux/delay.h>
-#include <linux/ioport.h>
-#include <linux/sched.h>
-#include <linux/slab.h>
-#include <linux/smp_lock.h>
-#include <linux/errno.h>
-#include <linux/unistd.h>
-#include <linux/interrupt.h>	/* for in_interrupt() */
-#include <linux/init.h>
-#include <linux/version.h>
-#include <linux/pm.h>
-#include <linux/timer.h>
-
-#include <asm/uaccess.h>
-#include <asm/io.h>
-#include <asm/irq.h>
-#include <asm/system.h>
-
-/* This enables more detailed sanity checks in submit_iso */
-//#define ISO_SANITY_CHECK
-
-/* This enables debug printks */
-#define DEBUG
-
-/* This enables all symbols to be exported, to ease debugging oopses */
-//#define DEBUG_SYMBOLS
-
-/* This enables an extra UHCI slab for memory debugging */
-#define DEBUG_SLAB
-
-#define VERSTR "$Revision: 1.275 $ time " __TIME__ " " __DATE__
-
-#include <linux/usb.h>
-#include "usb-uhci.h"
-#include "usb-uhci-debug.h"
-
-#include "hcd.h"
-
-/*
- * Version Information
- */
-#define DRIVER_VERSION "v1.275"
-#define DRIVER_AUTHOR "Georg Acher, Deti Fliegl, Thomas Sailer, Roman Weissgaerber"
-#define DRIVER_DESC "USB Universal Host Controller Interface driver"
-
-#undef DEBUG
-#undef dbg
-#define dbg(format, arg...) do {} while (0)
-#define DEBUG_SYMBOLS
-#ifdef DEBUG_SYMBOLS
-	#define _static
-	#ifndef EXPORT_SYMTAB
-		#define EXPORT_SYMTAB
-	#endif
-#else
-	#define _static static
-#endif
-
-#define queue_dbg dbg //err
-#define async_dbg dbg //err
-
-#ifdef DEBUG_SLAB
-	static kmem_cache_t *urb_priv_kmem;
-#endif
-
-#define SLAB_FLAG     (in_interrupt () || current->state != TASK_RUNNING ? SLAB_ATOMIC : SLAB_KERNEL)
-#define KMALLOC_FLAG  (in_interrupt () || current->state != TASK_RUNNING ? GFP_ATOMIC : GFP_KERNEL)
-
-/* CONFIG_USB_UHCI_HIGH_BANDWITH turns on Full Speed Bandwidth
- * Reclamation: feature that puts loop on descriptor loop when
- * there's some transfer going on. With FSBR, USB performance
- * is optimal, but PCI can be slowed down up-to 5 times, slowing down
- * system performance (eg. framebuffer devices).
- */
-#define CONFIG_USB_UHCI_HIGH_BANDWIDTH
-
-/* *_DEPTH_FIRST puts descriptor in depth-first mode. This has
- * somehow similar effect to FSBR (higher speed), but does not
- * slow PCI down. OTOH USB performace is slightly slower than
- * in FSBR case and single device could hog whole USB, starving
- * other devices.
- */
-#define USE_CTRL_DEPTH_FIRST 0  // 0: Breadth first, 1: Depth first
-#define USE_BULK_DEPTH_FIRST 0  // 0: Breadth first, 1: Depth first
-
-/* Turning off both CONFIG_USB_UHCI_HIGH_BANDWITH and *_DEPTH_FIRST
- * will lead to <64KB/sec performance over USB for bulk transfers targeting
- * one device's endpoint. You probably do not want to do that.
- */
-
-// stop bandwidth reclamation after (roughly) 50ms
-#define IDLE_TIMEOUT  (HZ/20)
-
-// Suppress HC interrupt error messages for 5s
-#define ERROR_SUPPRESSION_TIME (HZ*5)
-
-_static int rh_submit_urb (struct urb *urb);
-_static int rh_unlink_urb (struct urb *urb);
-_static int delete_qh (uhci_t *s, uhci_desc_t *qh);
-_static int process_transfer (uhci_t *s, struct urb *urb, int mode);
-_static int process_interrupt (uhci_t *s, struct urb *urb);
-_static int process_iso (uhci_t *s, struct urb *urb, int force);
-
-// How much URBs with ->next are walked
-#define MAX_NEXT_COUNT 2048
-
-static uhci_t *devs = NULL;
-
-/* used by userspace UHCI data structure dumper */
-uhci_t **uhci_devices = &devs;
-
-/*-------------------------------------------------------------------*/
-// Cleans up collected QHs, but not more than 100 in one go
-void clean_descs(uhci_t *s, int force)
-{
-	struct list_head *q;
-	uhci_desc_t *qh;
-	int now=UHCI_GET_CURRENT_FRAME(s), n=0;
-
-	q=s->free_desc.prev;
-
-	while (q != &s->free_desc && (force || n<100)) {
-		qh = list_entry (q, uhci_desc_t, horizontal);
-		q=qh->horizontal.prev;
-
-		if ((qh->last_used!=now) || force)
-			delete_qh(s,qh);
-		n++;
-	}
-}
-/*-------------------------------------------------------------------*/
-_static void uhci_switch_timer_int(uhci_t *s)
-{
-
-	if (!list_empty(&s->urb_unlinked))
-		set_td_ioc(s->td1ms);
-	else
-		clr_td_ioc(s->td1ms);
-
-	if (s->timeout_urbs)
-		set_td_ioc(s->td32ms);
-	else
-		clr_td_ioc(s->td32ms);
-	wmb();
-}
-/*-------------------------------------------------------------------*/
-#ifdef CONFIG_USB_UHCI_HIGH_BANDWIDTH
-_static void enable_desc_loop(uhci_t *s, struct urb *urb)
-{
-	unsigned long flags;
-
-	if (urb->transfer_flags & USB_NO_FSBR)
-		return;
-
-	spin_lock_irqsave (&s->qh_lock, flags);
-	s->chain_end->hw.qh.head&=cpu_to_le32(~UHCI_PTR_TERM);
-	mb();
-	s->loop_usage++;
-	((urb_priv_t*)urb->hcpriv)->use_loop=1;
-	spin_unlock_irqrestore (&s->qh_lock, flags);
-}
-/*-------------------------------------------------------------------*/
-_static void disable_desc_loop(uhci_t *s, struct urb *urb)
-{
-	unsigned long flags;
-
-	if (urb->transfer_flags & USB_NO_FSBR)
-		return;
-
-	spin_lock_irqsave (&s->qh_lock, flags);
-	if (((urb_priv_t*)urb->hcpriv)->use_loop) {
-		s->loop_usage--;
-
-		if (!s->loop_usage) {
-			s->chain_end->hw.qh.head|=cpu_to_le32(UHCI_PTR_TERM);
-			mb();
-		}
-		((urb_priv_t*)urb->hcpriv)->use_loop=0;
-	}
-	spin_unlock_irqrestore (&s->qh_lock, flags);
-}
-#endif
-/*-------------------------------------------------------------------*/
-_static void queue_urb_unlocked (uhci_t *s, struct urb *urb)
-{
-	struct list_head *p=&urb->urb_list;
-#ifdef CONFIG_USB_UHCI_HIGH_BANDWIDTH
-	{
-		int type;
-		type=usb_pipetype (urb->pipe);
-
-		if ((type == PIPE_BULK) || (type == PIPE_CONTROL))
-			enable_desc_loop(s, urb);
-	}
-#endif
-	urb->status = -EINPROGRESS;
-	((urb_priv_t*)urb->hcpriv)->started=jiffies;
-	list_add (p, &s->urb_list);
-	if (urb->timeout)
-		s->timeout_urbs++;
-	uhci_switch_timer_int(s);
-}
-/*-------------------------------------------------------------------*/
-_static void queue_urb (uhci_t *s, struct urb *urb)
-{
-	unsigned long flags=0;
-
-	spin_lock_irqsave (&s->urb_list_lock, flags);
-	queue_urb_unlocked(s,urb);
-	spin_unlock_irqrestore (&s->urb_list_lock, flags);
-}
-/*-------------------------------------------------------------------*/
-_static void dequeue_urb (uhci_t *s, struct urb *urb)
-{
-#ifdef CONFIG_USB_UHCI_HIGH_BANDWIDTH
-	int type;
-
-	type=usb_pipetype (urb->pipe);
-
-	if ((type == PIPE_BULK) || (type == PIPE_CONTROL))
-		disable_desc_loop(s, urb);
-#endif
-
-	list_del (&urb->urb_list);
-	if (urb->timeout && s->timeout_urbs)
-		s->timeout_urbs--;
-
-}
-/*-------------------------------------------------------------------*/
-_static int alloc_td (uhci_t *s, uhci_desc_t ** new, int flags)
-{
-	dma_addr_t dma_handle;
-
-	*new = pci_pool_alloc(s->desc_pool, GFP_DMA | GFP_ATOMIC, &dma_handle);
-	if (!*new)
-		return -ENOMEM;
-	memset (*new, 0, sizeof (uhci_desc_t));
-	(*new)->dma_addr = dma_handle;
-	set_td_link((*new), UHCI_PTR_TERM | (flags & UHCI_PTR_BITS));	// last by default
-	(*new)->type = TD_TYPE;
-	mb();
-	INIT_LIST_HEAD (&(*new)->vertical);
-	INIT_LIST_HEAD (&(*new)->horizontal);
-	
-	return 0;
-}
-/*-------------------------------------------------------------------*/
-// append a qh to td.link physically, the SW linkage is not affected
-_static void append_qh(uhci_t *s, uhci_desc_t *td, uhci_desc_t* qh, int  flags)
-{
-	unsigned long xxx;
-	
-	spin_lock_irqsave (&s->td_lock, xxx);
-
-	set_td_link(td, qh->dma_addr | (flags & UHCI_PTR_DEPTH) | UHCI_PTR_QH);
-       
-	mb();
-	spin_unlock_irqrestore (&s->td_lock, xxx);
-}
-/*-------------------------------------------------------------------*/
-/* insert td at last position in td-list of qh (vertical) */
-_static int insert_td (uhci_t *s, uhci_desc_t *qh, uhci_desc_t* new, int flags)
-{
-	uhci_desc_t *prev;
-	unsigned long xxx;
-	
-	spin_lock_irqsave (&s->td_lock, xxx);
-
-	list_add_tail (&new->vertical, &qh->vertical);
-
-	prev = list_entry (new->vertical.prev, uhci_desc_t, vertical);
-
-	if (qh == prev ) {
-		// virgin qh without any tds
-		set_qh_element(qh, new->dma_addr | UHCI_PTR_TERM);
-	}
-	else {
-		// already tds inserted, implicitely remove TERM bit of prev
-		set_td_link(prev, new->dma_addr | (flags & UHCI_PTR_DEPTH));
-	}
-	mb();
-	spin_unlock_irqrestore (&s->td_lock, xxx);
-	
-	return 0;
-}
-/*-------------------------------------------------------------------*/
-/* insert new_td after td (horizontal) */
-_static int insert_td_horizontal (uhci_t *s, uhci_desc_t *td, uhci_desc_t* new)
-{
-	uhci_desc_t *next;
-	unsigned long flags;
-	
-	spin_lock_irqsave (&s->td_lock, flags);
-
-	next = list_entry (td->horizontal.next, uhci_desc_t, horizontal);
-	list_add (&new->horizontal, &td->horizontal);
-	new->hw.td.link = td->hw.td.link;
-	set_td_link(td, new->dma_addr);
-	mb();
-	spin_unlock_irqrestore (&s->td_lock, flags);	
-	
-	return 0;
-}
-/*-------------------------------------------------------------------*/
-_static int unlink_td (uhci_t *s, uhci_desc_t *element, int phys_unlink)
-{
-	uhci_desc_t *next, *prev;
-	int dir = 0;
-	unsigned long flags;
-	
-	spin_lock_irqsave (&s->td_lock, flags);
-	
-	next = list_entry (element->vertical.next, uhci_desc_t, vertical);
-	
-	if (next == element) {
-		dir = 1;
-		prev = list_entry (element->horizontal.prev, uhci_desc_t, horizontal);
-	}
-	else 
-		prev = list_entry (element->vertical.prev, uhci_desc_t, vertical);
-	
-	if (phys_unlink) {
-		// really remove HW linking
-		if (prev->type == TD_TYPE)
-			prev->hw.td.link = element->hw.td.link;
-		else
-			prev->hw.qh.element = element->hw.td.link;
-	}
-
-	mb ();
-
-	if (dir == 0)
-		list_del (&element->vertical);
-	else
-		list_del (&element->horizontal);
-	
-	spin_unlock_irqrestore (&s->td_lock, flags);	
-	
-	return 0;
-}
-
-/*-------------------------------------------------------------------*/
-_static int delete_desc (uhci_t *s, uhci_desc_t *element)
-{
-	pci_pool_free(s->desc_pool, element, element->dma_addr);
-	return 0;
-}
-/*-------------------------------------------------------------------*/
-// Allocates qh element
-_static int alloc_qh (uhci_t *s, uhci_desc_t ** new)
-{
-	dma_addr_t dma_handle;
-
-	*new = pci_pool_alloc(s->desc_pool, GFP_DMA | GFP_ATOMIC, &dma_handle);
-	if (!*new)
-		return -ENOMEM;
-	memset (*new, 0, sizeof (uhci_desc_t));
-	(*new)->dma_addr = dma_handle;
-	set_qh_head(*new, UHCI_PTR_TERM);
-	set_qh_element(*new, UHCI_PTR_TERM);
-	(*new)->type = QH_TYPE;
-	
-	mb();
-	INIT_LIST_HEAD (&(*new)->horizontal);
-	INIT_LIST_HEAD (&(*new)->vertical);
-	
-	dbg("Allocated qh @ %p", *new);
-	
-	return 0;
-}
-/*-------------------------------------------------------------------*/
-// inserts new qh before/after the qh at pos
-// flags: 0: insert before pos, 1: insert after pos (for low speed transfers)
-_static int insert_qh (uhci_t *s, uhci_desc_t *pos, uhci_desc_t *new, int order)
-{
-	uhci_desc_t *old;
-	unsigned long flags;
-
-	spin_lock_irqsave (&s->qh_lock, flags);
-
-	if (!order) {
-		// (OLD) (POS) -> (OLD) (NEW) (POS)
-		old = list_entry (pos->horizontal.prev, uhci_desc_t, horizontal);
-		list_add_tail (&new->horizontal, &pos->horizontal);
-		set_qh_head(new, MAKE_QH_ADDR (pos)) ;
-		if (!(old->hw.qh.head & cpu_to_le32(UHCI_PTR_TERM)))
-			set_qh_head(old, MAKE_QH_ADDR (new)) ;
-	}
-	else {
-		// (POS) (OLD) -> (POS) (NEW) (OLD)
-		old = list_entry (pos->horizontal.next, uhci_desc_t, horizontal);
-		list_add (&new->horizontal, &pos->horizontal);
-		set_qh_head(new, MAKE_QH_ADDR (old));
-		set_qh_head(pos, MAKE_QH_ADDR (new)) ;
-	}
-
-	mb ();
-	
-	spin_unlock_irqrestore (&s->qh_lock, flags);
-
-	return 0;
-}
-
-/*-------------------------------------------------------------------*/
-_static int unlink_qh (uhci_t *s, uhci_desc_t *element)
-{
-	uhci_desc_t  *prev;
-	unsigned long flags;
-
-	spin_lock_irqsave (&s->qh_lock, flags);
-	
-	prev = list_entry (element->horizontal.prev, uhci_desc_t, horizontal);
-	prev->hw.qh.head = element->hw.qh.head;
-
-	dbg("unlink qh %p, pqh %p, nxqh %p, to %08x", element, prev, 
-	    list_entry (element->horizontal.next, uhci_desc_t, horizontal),le32_to_cpu(element->hw.qh.head) &~15);
-	
-	list_del(&element->horizontal);
-
-	mb ();
-	spin_unlock_irqrestore (&s->qh_lock, flags);
-	
-	return 0;
-}
-/*-------------------------------------------------------------------*/
-_static int delete_qh (uhci_t *s, uhci_desc_t *qh)
-{
-	uhci_desc_t *td;
-	struct list_head *p;
-	
-	list_del (&qh->horizontal);
-
-	while ((p = qh->vertical.next) != &qh->vertical) {
-		td = list_entry (p, uhci_desc_t, vertical);
-		dbg("unlink td @ %p",td);
-		unlink_td (s, td, 0); // no physical unlink
-		delete_desc (s, td);
-	}
-
-	delete_desc (s, qh);
-	
-	return 0;
-}
-/*-------------------------------------------------------------------*/
-_static void clean_td_chain (uhci_t *s, uhci_desc_t *td)
-{
-	struct list_head *p;
-	uhci_desc_t *td1;
-
-	if (!td)
-		return;
-	
-	while ((p = td->horizontal.next) != &td->horizontal) {
-		td1 = list_entry (p, uhci_desc_t, horizontal);
-		delete_desc (s, td1);
-	}
-	
-	delete_desc (s, td);
-}
-
-/*-------------------------------------------------------------------*/
-_static void fill_td (uhci_desc_t *td, int status, int info, __u32 buffer)
-{
-	td->hw.td.status = cpu_to_le32(status);
-	td->hw.td.info = cpu_to_le32(info);
-	td->hw.td.buffer = cpu_to_le32(buffer);
-}
-/*-------------------------------------------------------------------*/
-// Removes ALL qhs in chain (paranoia!)
-_static void cleanup_skel (uhci_t *s)
-{
-	unsigned int n;
-	uhci_desc_t *td;
-
-	dbg("cleanup_skel");
-
-	clean_descs(s,1);
-
-	
-	if (s->td32ms) {
-	
-		unlink_td(s,s->td32ms,1);
-		delete_desc(s, s->td32ms);
-	}
-
-	for (n = 0; n < 8; n++) {
-		td = s->int_chain[n];
-		clean_td_chain (s, td);
-	}
-
-	if (s->iso_td) {
-		for (n = 0; n < 1024; n++) {
-			td = s->iso_td[n];
-			clean_td_chain (s, td);
-		}
-		kfree (s->iso_td);
-	}
-
-	if (s->framelist)
-		pci_free_consistent(s->uhci_pci, PAGE_SIZE,
-				    s->framelist, s->framelist_dma);
-
-	if (s->control_chain) {
-		// completed init_skel?
-		struct list_head *p;
-		uhci_desc_t *qh, *qh1;
-
-		qh = s->control_chain;
-		while ((p = qh->horizontal.next) != &qh->horizontal) {
-			qh1 = list_entry (p, uhci_desc_t, horizontal);
-			delete_qh (s, qh1);
-		}
-
-		delete_qh (s, qh);
-	}
-	else {
-		if (s->ls_control_chain)
-			delete_desc (s, s->ls_control_chain);
-		if (s->control_chain)
-			delete_desc (s, s->control_chain);
-		if (s->bulk_chain)
-			delete_desc (s, s->bulk_chain);
-		if (s->chain_end)
-			delete_desc (s, s->chain_end);
-	}
-
-	if (s->desc_pool) {
-		pci_pool_destroy(s->desc_pool);
-		s->desc_pool = NULL;
-	}
-
-	dbg("cleanup_skel finished");	
-}
-/*-------------------------------------------------------------------*/
-// allocates framelist and qh-skeletons
-// only HW-links provide continous linking, SW-links stay in their domain (ISO/INT)
-_static int init_skel (uhci_t *s)
-{
-	int n, ret;
-	uhci_desc_t *qh, *td;
-	
-	dbg("init_skel");
-	
-	s->framelist = pci_alloc_consistent(s->uhci_pci, PAGE_SIZE,
-					    &s->framelist_dma);
-
-	if (!s->framelist)
-		return -ENOMEM;
-
-	memset (s->framelist, 0, 4096);
-
-	dbg("creating descriptor pci_pool");
-
-	s->desc_pool = pci_pool_create("uhci_desc", s->uhci_pci,
-				       sizeof(uhci_desc_t), 16, 0,
-				       GFP_DMA | GFP_ATOMIC);	
-	if (!s->desc_pool)
-		goto init_skel_cleanup;
-
-	dbg("allocating iso desc pointer list");
-	s->iso_td = (uhci_desc_t **) kmalloc (1024 * sizeof (uhci_desc_t*), GFP_KERNEL);
-	
-	if (!s->iso_td)
-		goto init_skel_cleanup;
-
-	s->ls_control_chain = NULL;
-	s->control_chain = NULL;
-	s->bulk_chain = NULL;
-	s->chain_end = NULL;
-
-	dbg("allocating iso descs");
-	for (n = 0; n < 1024; n++) {
-	 	// allocate skeleton iso/irq-tds
-		if (alloc_td (s, &td, 0))
-			goto init_skel_cleanup;
-
-		s->iso_td[n] = td;
-		s->framelist[n] = cpu_to_le32((__u32) td->dma_addr);
-	}
-
-	dbg("allocating qh: chain_end");
-	if (alloc_qh (s, &qh))	
-		goto init_skel_cleanup;
-				
-	s->chain_end = qh;
-
-	if (alloc_td (s, &td, 0))
-		goto init_skel_cleanup;
-	
-	fill_td (td, 0 * TD_CTRL_IOC, 0, 0); // generate 1ms interrupt (enabled on demand)
-	insert_td (s, qh, td, 0);
-	qh->hw.qh.element &= cpu_to_le32(~UHCI_PTR_TERM); // remove TERM bit
-	s->td1ms=td;
-
-	dbg("allocating qh: bulk_chain");
-	if (alloc_qh (s, &qh))
-		goto init_skel_cleanup;
-	
-	insert_qh (s, s->chain_end, qh, 0);
-	s->bulk_chain = qh;
-
-	dbg("allocating qh: control_chain");
-	ret = alloc_qh (s, &qh);
-	if (ret)
-		goto init_skel_cleanup;
-	
-	insert_qh (s, s->bulk_chain, qh, 0);
-	s->control_chain = qh;
-
-#ifdef	CONFIG_USB_UHCI_HIGH_BANDWIDTH
-	// disabled reclamation loop
-	set_qh_head(s->chain_end, s->control_chain->dma_addr | UHCI_PTR_QH | UHCI_PTR_TERM);
-#endif
-
-	dbg("allocating qh: ls_control_chain");
-	if (alloc_qh (s, &qh))
-		goto init_skel_cleanup;
-	
-	insert_qh (s, s->control_chain, qh, 0);
-	s->ls_control_chain = qh;
-
-	for (n = 0; n < 8; n++)
-		s->int_chain[n] = 0;
-
-	dbg("allocating skeleton INT-TDs");
-	
-	for (n = 0; n < 8; n++) {
-		uhci_desc_t *td;
-
-		if (alloc_td (s, &td, 0))
-			goto init_skel_cleanup;
-
-		s->int_chain[n] = td;
-		if (n == 0) {
-			set_td_link(s->int_chain[0], s->ls_control_chain->dma_addr | UHCI_PTR_QH);
-		}
-		else {
-			set_td_link(s->int_chain[n], s->int_chain[0]->dma_addr);
-		}
-	}
-
-	dbg("Linking skeleton INT-TDs");
-	
-	for (n = 0; n < 1024; n++) {
-		// link all iso-tds to the interrupt chains
-		int m, o;
-		dbg("framelist[%i]=%x",n,le32_to_cpu(s->framelist[n]));
-		if ((n&127)==127) 
-			((uhci_desc_t*) s->iso_td[n])->hw.td.link = cpu_to_le32(s->int_chain[0]->dma_addr);
-		else 
-			for (o = 1, m = 2; m <= 128; o++, m += m)
-				if ((n & (m - 1)) == ((m - 1) / 2))
-					set_td_link(((uhci_desc_t*) s->iso_td[n]), s->int_chain[o]->dma_addr);
-	}
-
-	if (alloc_td (s, &td, 0))
-		goto init_skel_cleanup;
-	
-	fill_td (td, 0 * TD_CTRL_IOC, 0, 0); // generate 32ms interrupt (activated later)
-	s->td32ms=td;
-
-	insert_td_horizontal (s, s->int_chain[5], td);
-
-	mb();
-	//uhci_show_queue(s->control_chain);   
-	dbg("init_skel exit");
-	return 0;
-
-      init_skel_cleanup:
-	cleanup_skel (s);
-	return -ENOMEM;
-}
-
-/*-------------------------------------------------------------------*/
-//                         LOW LEVEL STUFF
-//          assembles QHs und TDs for control, bulk and iso
-/*-------------------------------------------------------------------*/
-_static int uhci_submit_control_urb (struct urb *urb)
-{
-	uhci_desc_t *qh, *td;
-	uhci_t *s = (uhci_t*) urb->dev->bus->hcpriv;
-	urb_priv_t *urb_priv = urb->hcpriv;
-	unsigned long destination, status;
-	int maxsze = usb_maxpacket (urb->dev, urb->pipe, usb_pipeout (urb->pipe));
-	unsigned long len;
-	char *data;
-	int depth_first=USE_CTRL_DEPTH_FIRST;  // UHCI descriptor chasing method
-
-	dbg("uhci_submit_control start");
-	if (alloc_qh (s, &qh))		// alloc qh for this request
-		return -ENOMEM;
-
-	if (alloc_td (s, &td, UHCI_PTR_DEPTH * depth_first))		// get td for setup stage
-	{
-		delete_qh (s, qh);
-		return -ENOMEM;
-	}
-
-	/* The "pipe" thing contains the destination in bits 8--18 */
-	destination = (urb->pipe & PIPE_DEVEP_MASK) | USB_PID_SETUP;
-
-	/* 3 errors */
-	status = (urb->pipe & TD_CTRL_LS) | TD_CTRL_ACTIVE |
-		(urb->transfer_flags & USB_DISABLE_SPD ? 0 : TD_CTRL_SPD) | (3 << 27);
-
-	/*  Build the TD for the control request, try forever, 8 bytes of data */
-	fill_td (td, status, destination | (7 << 21), urb_priv->setup_packet_dma);
-
-	insert_td (s, qh, td, 0);	// queue 'setup stage'-td in qh
-#if 0
-	{
-		char *sp=urb->setup_packet;
-		dbg("SETUP to pipe %x: %x %x %x %x %x %x %x %x", urb->pipe,
-		    sp[0],sp[1],sp[2],sp[3],sp[4],sp[5],sp[6],sp[7]);
-	}
-	//uhci_show_td(td);
-#endif
-
-	len = urb->transfer_buffer_length;
-	data = urb->transfer_buffer;
-
-	/* If direction is "send", change the frame from SETUP (0x2D)
-	   to OUT (0xE1). Else change it from SETUP to IN (0x69). */
-
-	destination = (urb->pipe & PIPE_DEVEP_MASK) | (usb_pipeout (urb->pipe)?USB_PID_OUT:USB_PID_IN);
-
-	while (len > 0) {
-		int pktsze = len;
-
-		if (alloc_td (s, &td, UHCI_PTR_DEPTH * depth_first))
-			goto fail_unmap_enomem;
-
-		if (pktsze > maxsze)
-			pktsze = maxsze;
-
-		destination ^= 1 << TD_TOKEN_TOGGLE;	// toggle DATA0/1
-
-		// Status, pktsze bytes of data
-		fill_td (td, status, destination | ((pktsze - 1) << 21),
-			 urb_priv->transfer_buffer_dma + (data - (char *)urb->transfer_buffer));
-
-		insert_td (s, qh, td, UHCI_PTR_DEPTH * depth_first);	// queue 'data stage'-td in qh
-
-		data += pktsze;
-		len -= pktsze;
-	}
-
-	/* Build the final TD for control status */
-	/* It's only IN if the pipe is out AND we aren't expecting data */
-
-	destination &= ~UHCI_PID;
-
-	if (usb_pipeout (urb->pipe) || (urb->transfer_buffer_length == 0))
-		destination |= USB_PID_IN;
-	else
-		destination |= USB_PID_OUT;
-
-	destination |= 1 << TD_TOKEN_TOGGLE;	/* End in Data1 */
-
-	if (alloc_td (s, &td, UHCI_PTR_DEPTH))
-		goto fail_unmap_enomem;
-
-	status &=~TD_CTRL_SPD;
-
-	/* no limit on errors on final packet , 0 bytes of data */
-	fill_td (td, status | TD_CTRL_IOC, destination | (UHCI_NULL_DATA_SIZE << 21),
-		 0);
-
-	insert_td (s, qh, td, UHCI_PTR_DEPTH * depth_first);	// queue status td
-
-	list_add (&qh->desc_list, &urb_priv->desc_list);
-
-	queue_urb (s, urb);	// queue before inserting in desc chain
-
-	qh->hw.qh.element &= cpu_to_le32(~UHCI_PTR_TERM);
-
-	//uhci_show_queue(qh);
-	/* Start it up... put low speed first */
-	if (urb->pipe & TD_CTRL_LS)
-		insert_qh (s, s->control_chain, qh, 0);
-	else
-		insert_qh (s, s->bulk_chain, qh, 0);
-
-	dbg("uhci_submit_control end");
-	return 0;
-
-fail_unmap_enomem:
-	delete_qh(s, qh);
-	return -ENOMEM;
-}
-/*-------------------------------------------------------------------*/
-// For queued bulk transfers, two additional QH helpers are allocated (nqh, bqh)
-// Due to the linking with other bulk urbs, it has to be locked with urb_list_lock!
-
-_static int uhci_submit_bulk_urb (struct urb *urb, struct urb *bulk_urb)
-{
-	uhci_t *s = (uhci_t*) urb->dev->bus->hcpriv;
-	urb_priv_t *urb_priv = urb->hcpriv, *upriv, *bpriv=NULL;
-	uhci_desc_t *qh, *td, *nqh=NULL, *bqh=NULL, *first_td=NULL;
-	unsigned long destination, status;
-	char *data;
-	unsigned int pipe = urb->pipe;
-	int maxsze = usb_maxpacket (urb->dev, pipe, usb_pipeout (pipe));
-	int info, len, last;
-	int depth_first=USE_BULK_DEPTH_FIRST;  // UHCI descriptor chasing method
-
-	if (usb_endpoint_halted (urb->dev, usb_pipeendpoint (pipe), usb_pipeout (pipe)))
-		return -EPIPE;
-
-	queue_dbg("uhci_submit_bulk_urb: urb %p, old %p, pipe %08x, len %i",
-		  urb,bulk_urb,urb->pipe,urb->transfer_buffer_length);
-
-	upriv = (urb_priv_t*)urb->hcpriv;
-
-	if (!bulk_urb) {
-		if (alloc_qh (s, &qh))		// get qh for this request
-			return -ENOMEM;
-
-		if (urb->transfer_flags & USB_QUEUE_BULK) {
-			if (alloc_qh(s, &nqh)) // placeholder for clean unlink
-			{
-				delete_desc (s, qh);
-				return -ENOMEM;
-			}
-			upriv->next_qh = nqh;
-			queue_dbg("new next qh %p",nqh);
-		}
-	}
-	else { 
-		bpriv = (urb_priv_t*)bulk_urb->hcpriv;
-		qh = bpriv->bottom_qh;  // re-use bottom qh and next qh
-		nqh = bpriv->next_qh;
-		upriv->next_qh=nqh;	
-		upriv->prev_queued_urb=bulk_urb;
-	}
-
-	if (urb->transfer_flags & USB_QUEUE_BULK) {
-		if (alloc_qh (s, &bqh))  // "bottom" QH
-		{
-			if (!bulk_urb) { 
-				delete_desc(s, qh);
-				delete_desc(s, nqh);
-			}
-			return -ENOMEM;
-		}
-		set_qh_element(bqh, UHCI_PTR_TERM);
-		set_qh_head(bqh, nqh->dma_addr | UHCI_PTR_QH); // element
-		upriv->bottom_qh = bqh;
-	}
-	queue_dbg("uhci_submit_bulk: qh %p bqh %p nqh %p",qh, bqh, nqh);
-
-	/* The "pipe" thing contains the destination in bits 8--18. */
-	destination = (pipe & PIPE_DEVEP_MASK) | usb_packetid (pipe);
-
-	/* 3 errors */
-	status = (pipe & TD_CTRL_LS) | TD_CTRL_ACTIVE |
-		((urb->transfer_flags & USB_DISABLE_SPD) ? 0 : TD_CTRL_SPD) | (3 << 27);
-
-	/* Build the TDs for the bulk request */
-	len = urb->transfer_buffer_length;
-	data = urb->transfer_buffer;
-	
-	do {					// TBD: Really allow zero-length packets?
-		int pktsze = len;
-
-		if (alloc_td (s, &td, UHCI_PTR_DEPTH * depth_first))
-		{
-			delete_qh (s, qh);
-			return -ENOMEM;
-		}
-
-		if (pktsze > maxsze)
-			pktsze = maxsze;
-
-		// pktsze bytes of data 
-		info = destination | (((pktsze - 1)&UHCI_NULL_DATA_SIZE) << 21) |
-			(usb_gettoggle (urb->dev, usb_pipeendpoint (pipe), usb_pipeout (pipe)) << TD_TOKEN_TOGGLE);
-
-		fill_td (td, status, info,
-			 urb_priv->transfer_buffer_dma + (data - (char *)urb->transfer_buffer));
-
-		data += pktsze;
-		len -= pktsze;
-		// Use USB_ZERO_PACKET to finish bulk OUTs always with a zero length packet
-		last = (len == 0 && (usb_pipein(pipe) || pktsze < maxsze || !(urb->transfer_flags & USB_ZERO_PACKET)));
-
-		if (last)
-			set_td_ioc(td);	// last one generates INT
-
-		insert_td (s, qh, td, UHCI_PTR_DEPTH * depth_first);
-		if (!first_td)
-			first_td=td;
-		usb_dotoggle (urb->dev, usb_pipeendpoint (pipe), usb_pipeout (pipe));
-
-	} while (!last);
-
-	if (bulk_urb && bpriv)   // everything went OK, link with old bulk URB
-		bpriv->next_queued_urb=urb;
-
-	list_add (&qh->desc_list, &urb_priv->desc_list);
-
-	if (urb->transfer_flags & USB_QUEUE_BULK)
-		append_qh(s, td, bqh, UHCI_PTR_DEPTH * depth_first);
-
-	queue_urb_unlocked (s, urb);
-	
-	if (urb->transfer_flags & USB_QUEUE_BULK)
-		set_qh_element(qh, first_td->dma_addr);
-	else
-		qh->hw.qh.element &= cpu_to_le32(~UHCI_PTR_TERM);    // arm QH
-
-	if (!bulk_urb) { 					// new bulk queue	
-		if (urb->transfer_flags & USB_QUEUE_BULK) {
-			spin_lock (&s->td_lock);		// both QHs in one go
-			insert_qh (s, s->chain_end, qh, 0);	// Main QH
-			insert_qh (s, s->chain_end, nqh, 0);	// Helper QH
-			spin_unlock (&s->td_lock);
-		}
-		else
-			insert_qh (s, s->chain_end, qh, 0);
-	}
-	
-	//uhci_show_queue(s->bulk_chain);
-	//dbg("uhci_submit_bulk_urb: exit\n");
-	return 0;
-}
-/*-------------------------------------------------------------------*/
-_static void uhci_clean_iso_step1(uhci_t *s, urb_priv_t *urb_priv)
-{
-	struct list_head *p;
-	uhci_desc_t *td;
-
-	for (p = urb_priv->desc_list.next; p != &urb_priv->desc_list; p = p->next) {
-				td = list_entry (p, uhci_desc_t, desc_list);
-				unlink_td (s, td, 1);
-	}
-}
-/*-------------------------------------------------------------------*/
-_static void uhci_clean_iso_step2(uhci_t *s, urb_priv_t *urb_priv)
-{
-	struct list_head *p;
-	uhci_desc_t *td;
-
-	while ((p = urb_priv->desc_list.next) != &urb_priv->desc_list) {
-				td = list_entry (p, uhci_desc_t, desc_list);
-				list_del (p);
-				delete_desc (s, td);
-	}
-}
-/*-------------------------------------------------------------------*/
-/* mode: CLEAN_TRANSFER_NO_DELETION: unlink but no deletion mark (step 1 of async_unlink)
-         CLEAN_TRANSFER_REGULAR: regular (unlink/delete-mark)
-         CLEAN_TRANSFER_DELETION_MARK: deletion mark for QH (step 2 of async_unlink)
- looks a bit complicated because of all the bulk queueing goodies
-*/
-
-_static void uhci_clean_transfer (uhci_t *s, struct urb *urb, uhci_desc_t *qh, int mode)
-{
-	uhci_desc_t *bqh, *nqh, *prevqh, *prevtd;
-	int now;
-	urb_priv_t *priv=(urb_priv_t*)urb->hcpriv;
-
-	now=UHCI_GET_CURRENT_FRAME(s);
-
-	bqh=priv->bottom_qh;	
-	
-	if (!priv->next_queued_urb)  { // no more appended bulk queues
-
-		queue_dbg("uhci_clean_transfer: No more bulks for urb %p, qh %p, bqh %p, nqh %p", urb, qh, bqh, priv->next_qh);	
-	
-		if (priv->prev_queued_urb && mode != CLEAN_TRANSFER_DELETION_MARK) {  // qh not top of the queue
-				unsigned long flags; 
-				urb_priv_t* ppriv=(urb_priv_t*)priv->prev_queued_urb->hcpriv;
-
-				spin_lock_irqsave (&s->qh_lock, flags);
-				prevqh = list_entry (ppriv->desc_list.next, uhci_desc_t, desc_list);
-				prevtd = list_entry (prevqh->vertical.prev, uhci_desc_t, vertical);
-				set_td_link(prevtd, priv->bottom_qh->dma_addr | UHCI_PTR_QH); // skip current qh
-				mb();
-				queue_dbg("uhci_clean_transfer: relink pqh %p, ptd %p",prevqh, prevtd);
-				spin_unlock_irqrestore (&s->qh_lock, flags);
-
-				ppriv->bottom_qh = priv->bottom_qh;
-				ppriv->next_queued_urb = NULL;
-			}
-		else {   // queue is dead, qh is top of the queue
-			
-			if (mode != CLEAN_TRANSFER_DELETION_MARK) 				
-				unlink_qh(s, qh); // remove qh from horizontal chain
-
-			if (bqh) {  // remove remainings of bulk queue
-				nqh=priv->next_qh;
-
-				if (mode != CLEAN_TRANSFER_DELETION_MARK) 
-					unlink_qh(s, nqh);  // remove nqh from horizontal chain
-				
-				if (mode != CLEAN_TRANSFER_NO_DELETION) {  // add helper QHs to free desc list
-					nqh->last_used = bqh->last_used = now;
-					list_add_tail (&nqh->horizontal, &s->free_desc);
-					list_add_tail (&bqh->horizontal, &s->free_desc);
-				}			
-			}
-		}
-	}
-	else { // there are queued urbs following
-	
-	  queue_dbg("uhci_clean_transfer: urb %p, prevurb %p, nexturb %p, qh %p, bqh %p, nqh %p",
-		       urb, priv->prev_queued_urb,  priv->next_queued_urb, qh, bqh, priv->next_qh);	
-       	
-		if (mode != CLEAN_TRANSFER_DELETION_MARK) {	// no work for cleanup at unlink-completion
-			struct urb *nurb;
-			unsigned long flags;
-
-			nurb = priv->next_queued_urb;
-			spin_lock_irqsave (&s->qh_lock, flags);		
-
-			if (!priv->prev_queued_urb) { // top QH
-				
-				prevqh = list_entry (qh->horizontal.prev, uhci_desc_t, horizontal);
-				set_qh_head(prevqh, bqh->dma_addr | UHCI_PTR_QH);
-				list_del (&qh->horizontal);  // remove this qh form horizontal chain
-				list_add (&bqh->horizontal, &prevqh->horizontal); // insert next bqh in horizontal chain
-			}
-			else {		// intermediate QH
-				urb_priv_t* ppriv=(urb_priv_t*)priv->prev_queued_urb->hcpriv;
-				urb_priv_t* npriv=(urb_priv_t*)nurb->hcpriv;
-				uhci_desc_t * bnqh;
-				
-				bnqh = list_entry (npriv->desc_list.next, uhci_desc_t, desc_list);
-				ppriv->bottom_qh = bnqh;
-				ppriv->next_queued_urb = nurb;				
-				prevqh = list_entry (ppriv->desc_list.next, uhci_desc_t, desc_list);
-				set_qh_head(prevqh, bqh->dma_addr | UHCI_PTR_QH);
-			}
-
-			mb();
-			((urb_priv_t*)nurb->hcpriv)->prev_queued_urb=priv->prev_queued_urb;
-			spin_unlock_irqrestore (&s->qh_lock, flags);
-		}		
-	}
-
-	if (mode != CLEAN_TRANSFER_NO_DELETION) {
-		qh->last_used = now;	
-		list_add_tail (&qh->horizontal, &s->free_desc); // mark qh for later deletion/kfree
-	}
-}
-/*-------------------------------------------------------------------*/
-// Release bandwidth for Interrupt or Isoc. transfers 
-_static void uhci_release_bandwidth(struct urb *urb)
-{       
-	if (urb->bandwidth) {
-		switch (usb_pipetype(urb->pipe)) {
-		case PIPE_INTERRUPT:
-			usb_release_bandwidth (urb->dev, urb, 0);
-			break;
-		case PIPE_ISOCHRONOUS:
-			usb_release_bandwidth (urb->dev, urb, 1);
-			break;
-		default:
-			break;
-		}
-	}	
-}
-
-_static void uhci_urb_dma_sync(uhci_t *s, struct urb *urb, urb_priv_t *urb_priv)
-{
-	if (urb_priv->setup_packet_dma)
-		pci_dma_sync_single(s->uhci_pci, urb_priv->setup_packet_dma,
-				    sizeof(struct usb_ctrlrequest), PCI_DMA_TODEVICE);
-
-	if (urb_priv->transfer_buffer_dma)
-		pci_dma_sync_single(s->uhci_pci, urb_priv->transfer_buffer_dma,
-				    urb->transfer_buffer_length,
-				    usb_pipein(urb->pipe) ?
-				    PCI_DMA_FROMDEVICE :
-				    PCI_DMA_TODEVICE);
-}
-
-_static void uhci_urb_dma_unmap(uhci_t *s, struct urb *urb, urb_priv_t *urb_priv)
-{
-	if (urb_priv->setup_packet_dma) {
-		pci_unmap_single(s->uhci_pci, urb_priv->setup_packet_dma,
-				 sizeof(struct usb_ctrlrequest), PCI_DMA_TODEVICE);
-		urb_priv->setup_packet_dma = 0;
-	}
-	if (urb_priv->transfer_buffer_dma) {
-		pci_unmap_single(s->uhci_pci, urb_priv->transfer_buffer_dma,
-				 urb->transfer_buffer_length,
-				 usb_pipein(urb->pipe) ?
-				 PCI_DMA_FROMDEVICE :
-				 PCI_DMA_TODEVICE);
-		urb_priv->transfer_buffer_dma = 0;
-	}
-}
-/*-------------------------------------------------------------------*/
-/* needs urb_list_lock!
-   mode: UNLINK_ASYNC_STORE_URB: unlink and move URB into unlinked list
-         UNLINK_ASYNC_DONT_STORE: unlink, don't move URB into unlinked list
-*/
-_static int uhci_unlink_urb_async (uhci_t *s,struct urb *urb, int mode)
-{
-	uhci_desc_t *qh;
-	urb_priv_t *urb_priv;
-	
-	async_dbg("unlink_urb_async called %p",urb);
-
-	if ((urb->status == -EINPROGRESS) ||
-	    ((usb_pipetype (urb->pipe) ==  PIPE_INTERRUPT) && ((urb_priv_t*)urb->hcpriv)->flags))
-	{
-		((urb_priv_t*)urb->hcpriv)->started = ~0;  // mark
-		dequeue_urb (s, urb);
-
-		if (mode==UNLINK_ASYNC_STORE_URB)
-			list_add_tail (&urb->urb_list, &s->urb_unlinked); // store urb
-
-		uhci_switch_timer_int(s);
-       		s->unlink_urb_done = 1;
-		uhci_release_bandwidth(urb);
-
-		urb->status = -ECONNABORTED;	// mark urb as "waiting to be killed"	
-		urb_priv = (urb_priv_t*)urb->hcpriv;
-
-		switch (usb_pipetype (urb->pipe)) {
-		case PIPE_INTERRUPT:
-			usb_dotoggle (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe));
-
-		case PIPE_ISOCHRONOUS:
-			uhci_clean_iso_step1 (s, urb_priv);
-			break;
-
-		case PIPE_BULK:
-		case PIPE_CONTROL:
-			qh = list_entry (urb_priv->desc_list.next, uhci_desc_t, desc_list);
-			uhci_clean_transfer (s, urb, qh, CLEAN_TRANSFER_NO_DELETION);
-			break;
-		}
-		((urb_priv_t*)urb->hcpriv)->started = UHCI_GET_CURRENT_FRAME(s);
-		return -EINPROGRESS;  // completion will follow
-	}		
-
-	return 0;    // URB already dead
-}
-/*-------------------------------------------------------------------*/
-// kills an urb by unlinking descriptors and waiting for at least one frame
-_static int uhci_unlink_urb_sync (uhci_t *s, struct urb *urb)
-{
-	uhci_desc_t *qh;
-	urb_priv_t *urb_priv;
-	unsigned long flags=0;
-	struct usb_device *usb_dev;
-
-	spin_lock_irqsave (&s->urb_list_lock, flags);
-
-	if (urb->status == -EINPROGRESS) {
-
-		// move descriptors out of the running chains, dequeue urb
-		uhci_unlink_urb_async(s, urb, UNLINK_ASYNC_DONT_STORE);
-
-		urb_priv = urb->hcpriv;
-		urb->status = -ENOENT;	// prevent from double deletion after unlock		
-		spin_unlock_irqrestore (&s->urb_list_lock, flags);
-		
-		// cleanup the rest
-		switch (usb_pipetype (urb->pipe)) {
-
-		case PIPE_INTERRUPT:
-		case PIPE_ISOCHRONOUS:
-			uhci_wait_ms(1);
-			uhci_clean_iso_step2(s, urb_priv);
-			break;
-
-		case PIPE_BULK:
-		case PIPE_CONTROL:
-			qh = list_entry (urb_priv->desc_list.next, uhci_desc_t, desc_list);
-			uhci_clean_transfer(s, urb, qh, CLEAN_TRANSFER_DELETION_MARK);
-			uhci_wait_ms(1);
-		}
-		urb->status = -ENOENT;	// mark urb as killed		
-					
-		uhci_urb_dma_unmap(s, urb, urb->hcpriv);
-
-#ifdef DEBUG_SLAB
-		kmem_cache_free (urb_priv_kmem, urb->hcpriv);
-#else
-		kfree (urb->hcpriv);
-#endif
-		usb_dev = urb->dev;
-		if (urb->complete) {
-			dbg("unlink_urb: calling completion");
-			urb->dev = NULL;
-			urb->complete ((struct urb *) urb);
-		}
-		usb_dec_dev_use (usb_dev);
-	}
-	else
-		spin_unlock_irqrestore (&s->urb_list_lock, flags);
-
-	return 0;
-}
-/*-------------------------------------------------------------------*/
-// async unlink_urb completion/cleanup work
-// has to be protected by urb_list_lock!
-// features: if set in transfer_flags, the resulting status of the killed
-// transaction is not overwritten
-
-_static void uhci_cleanup_unlink(uhci_t *s, int force)
-{
-	struct list_head *q;
-	struct urb *urb;
-	struct usb_device *dev;
-	int now, type;
-	urb_priv_t *urb_priv;
-
-	q=s->urb_unlinked.next;
-	now=UHCI_GET_CURRENT_FRAME(s);
-
-	while (q != &s->urb_unlinked) {
-
-		urb = list_entry (q, struct urb, urb_list);
-
-		urb_priv = (urb_priv_t*)urb->hcpriv;
-		q = urb->urb_list.next;
-		
-		if (!urb_priv) // avoid crash when URB is corrupted
-			break;
-			
-		if (force || ((urb_priv->started != ~0) && (urb_priv->started != now))) {
-			async_dbg("async cleanup %p",urb);
-			type=usb_pipetype (urb->pipe);
-
-			switch (type) { // process descriptors
-			case PIPE_CONTROL:
-				process_transfer (s, urb, CLEAN_TRANSFER_DELETION_MARK);  // don't unlink (already done)
-				break;
-			case PIPE_BULK:
-				if (!s->avoid_bulk.counter)
-					process_transfer (s, urb, CLEAN_TRANSFER_DELETION_MARK); // don't unlink (already done)
-				else
-					continue;
-				break;
-			case PIPE_ISOCHRONOUS:
-				process_iso (s, urb, PROCESS_ISO_FORCE); // force, don't unlink
-				break;
-			case PIPE_INTERRUPT:
-				process_interrupt (s, urb);
-				break;
-			}
-
-			if (!(urb->transfer_flags & USB_TIMEOUT_KILLED))
-		  		urb->status = -ECONNRESET; // mark as asynchronously killed
-
-			dev = urb->dev;	// completion may destroy all...
-			urb_priv = urb->hcpriv;
-			list_del (&urb->urb_list);
-			
-			uhci_urb_dma_sync(s, urb, urb_priv);
-			if (urb->complete) {
-				spin_unlock(&s->urb_list_lock);
-				urb->dev = NULL;
-				urb->complete ((struct urb *) urb);
-				spin_lock(&s->urb_list_lock);
-			}
-
-			if (!(urb->transfer_flags & USB_TIMEOUT_KILLED))
-				urb->status = -ENOENT;  // now the urb is really dead
-
-			switch (type) {
-			case PIPE_ISOCHRONOUS:
-			case PIPE_INTERRUPT:
-				uhci_clean_iso_step2(s, urb_priv);
-				break;
-			}
-	
-			uhci_urb_dma_unmap(s, urb, urb_priv);
-
-			usb_dec_dev_use (dev);
-#ifdef DEBUG_SLAB
-			kmem_cache_free (urb_priv_kmem, urb_priv);
-#else
-			kfree (urb_priv);
-#endif
-
-		}
-	}
-}
- 
-/*-------------------------------------------------------------------*/
-_static int uhci_unlink_urb (struct urb *urb)
-{
-	uhci_t *s;
-	unsigned long flags=0;
-	dbg("uhci_unlink_urb called for %p",urb);
-	if (!urb || !urb->dev)		// you never know...
-		return -EINVAL;
-	
-	s = (uhci_t*) urb->dev->bus->hcpriv;
-
-	if (usb_pipedevice (urb->pipe) == s->rh.devnum)
-		return rh_unlink_urb (urb);
-
-	if (!urb->hcpriv)
-		return -EINVAL;
-
-	if (urb->transfer_flags & USB_ASYNC_UNLINK) {
-		int ret;
-       		spin_lock_irqsave (&s->urb_list_lock, flags);
-       		
-		uhci_release_bandwidth(urb);
-		ret = uhci_unlink_urb_async(s, urb, UNLINK_ASYNC_STORE_URB);
-
-		spin_unlock_irqrestore (&s->urb_list_lock, flags);	
-		return ret;
-	}
-	else
-		return uhci_unlink_urb_sync(s, urb);
-}
-/*-------------------------------------------------------------------*/
-// In case of ASAP iso transfer, search the URB-list for already queued URBs
-// for this EP and calculate the earliest start frame for the new
-// URB (easy seamless URB continuation!)
-_static int find_iso_limits (struct urb *urb, unsigned int *start, unsigned int *end)
-{
-	struct urb *u, *last_urb = NULL;
-	uhci_t *s = (uhci_t*) urb->dev->bus->hcpriv;
-	struct list_head *p;
-	int ret=-1;
-	unsigned long flags;
-	
-	spin_lock_irqsave (&s->urb_list_lock, flags);
-	p=s->urb_list.prev;
-
-	for (; p != &s->urb_list; p = p->prev) {
-		u = list_entry (p, struct urb, urb_list);
-		// look for pending URBs with identical pipe handle
-		// works only because iso doesn't toggle the data bit!
-		if ((urb->pipe == u->pipe) && (urb->dev == u->dev) && (u->status == -EINPROGRESS)) {
-			if (!last_urb)
-				*start = u->start_frame;
-			last_urb = u;
-		}
-	}
-	
-	if (last_urb) {
-		*end = (last_urb->start_frame + last_urb->number_of_packets) & 1023;
-		ret=0;
-	}
-	
-	spin_unlock_irqrestore(&s->urb_list_lock, flags);
-	
-	return ret;
-}
-/*-------------------------------------------------------------------*/
-// adjust start_frame according to scheduling constraints (ASAP etc)
-
-_static int iso_find_start (struct urb *urb)
-{
-	uhci_t *s = (uhci_t*) urb->dev->bus->hcpriv;
-	unsigned int now;
-	unsigned int start_limit = 0, stop_limit = 0, queued_size;
-	int limits;
-
-	now = UHCI_GET_CURRENT_FRAME (s) & 1023;
-
-	if ((unsigned) urb->number_of_packets > 900)
-		return -EFBIG;
-	
-	limits = find_iso_limits (urb, &start_limit, &stop_limit);
-	queued_size = (stop_limit - start_limit) & 1023;
-
-	if (urb->transfer_flags & USB_ISO_ASAP) {
-		// first iso
-		if (limits) {
-			// 10ms setup should be enough //FIXME!
-			urb->start_frame = (now + 10) & 1023;
-		}
-		else {
-			urb->start_frame = stop_limit;		//seamless linkage
-
-			if (((now - urb->start_frame) & 1023) <= (unsigned) urb->number_of_packets) {
-				info("iso_find_start: gap in seamless isochronous scheduling");
-				dbg("iso_find_start: now %u start_frame %u number_of_packets %u pipe 0x%08x",
-					now, urb->start_frame, urb->number_of_packets, urb->pipe);
-				urb->start_frame = (now + 5) & 1023;	// 5ms setup should be enough //FIXME!
-			}
-		}
-	}
-	else {
-		urb->start_frame &= 1023;
-		if (((now - urb->start_frame) & 1023) < (unsigned) urb->number_of_packets) {
-			dbg("iso_find_start: now between start_frame and end");
-			return -EAGAIN;
-		}
-	}
-
-	/* check if either start_frame or start_frame+number_of_packets-1 lies between start_limit and stop_limit */
-	if (limits)
-		return 0;
-
-	if (((urb->start_frame - start_limit) & 1023) < queued_size ||
-	    ((urb->start_frame + urb->number_of_packets - 1 - start_limit) & 1023) < queued_size) {
-		dbg("iso_find_start: start_frame %u number_of_packets %u start_limit %u stop_limit %u",
-			urb->start_frame, urb->number_of_packets, start_limit, stop_limit);
-		return -EAGAIN;
-	}
-
-	return 0;
-}
-/*-------------------------------------------------------------------*/
-// submits USB interrupt (ie. polling ;-) 
-// ASAP-flag set implicitely
-// if period==0, the transfer is only done once
-
-_static int uhci_submit_int_urb (struct urb *urb)
-{
-	uhci_t *s = (uhci_t*) urb->dev->bus->hcpriv;
-	urb_priv_t *urb_priv = urb->hcpriv;
-	int nint, n;
-	uhci_desc_t *td;
-	int status, destination;
-	int info;
-	unsigned int pipe = urb->pipe;
-
-	if (urb->interval < 0 || urb->interval >= 256)
-		return -EINVAL;
-
-	if (urb->interval == 0)
-		nint = 0;
-	else {
-		for (nint = 0, n = 1; nint <= 8; nint++, n += n)	// round interval down to 2^n
-		 {
-			if (urb->interval < n) {
-				urb->interval = n / 2;
-				break;
-			}
-		}
-		nint--;
-	}
-
-	dbg("Rounded interval to %i, chain  %i", urb->interval, nint);
-
-	urb->start_frame = UHCI_GET_CURRENT_FRAME (s) & 1023;	// remember start frame, just in case...
-
-	urb->number_of_packets = 1;
-
-	// INT allows only one packet
-	if (urb->transfer_buffer_length > usb_maxpacket (urb->dev, pipe, usb_pipeout (pipe)))
-		return -EINVAL;
-
-	if (alloc_td (s, &td, UHCI_PTR_DEPTH))
-		return -ENOMEM;
-
-	status = (pipe & TD_CTRL_LS) | TD_CTRL_ACTIVE | TD_CTRL_IOC |
-		(urb->transfer_flags & USB_DISABLE_SPD ? 0 : TD_CTRL_SPD) | (3 << 27);
-
-	destination = (urb->pipe & PIPE_DEVEP_MASK) | usb_packetid (urb->pipe) |
-		(((urb->transfer_buffer_length - 1) & 0x7ff) << 21);
-
-
-	info = destination | (usb_gettoggle (urb->dev, usb_pipeendpoint (pipe), usb_pipeout (pipe)) << TD_TOKEN_TOGGLE);
-
-	fill_td (td, status, info, urb_priv->transfer_buffer_dma);
-	list_add_tail (&td->desc_list, &urb_priv->desc_list);
-
-	queue_urb (s, urb);
-
-	insert_td_horizontal (s, s->int_chain[nint], td);	// store in INT-TDs
-
-	usb_dotoggle (urb->dev, usb_pipeendpoint (pipe), usb_pipeout (pipe));
-
-	return 0;
-}
-/*-------------------------------------------------------------------*/
-_static int uhci_submit_iso_urb (struct urb *urb)
-{
-	uhci_t *s = (uhci_t*) urb->dev->bus->hcpriv;
-	urb_priv_t *urb_priv = urb->hcpriv;
-#ifdef ISO_SANITY_CHECK
-	int pipe=urb->pipe;
-	int maxsze = usb_maxpacket (urb->dev, pipe, usb_pipeout (pipe));
-#endif
-	int n, ret, last=0;
-	uhci_desc_t *td, **tdm;
-	int status, destination;
-	unsigned long flags;
-
-	__save_flags(flags);
-	__cli();		      // Disable IRQs to schedule all ISO-TDs in time
-	ret = iso_find_start (urb);	// adjusts urb->start_frame for later use
-	
-	if (ret)
-		goto err;
-
-	tdm = (uhci_desc_t **) kmalloc (urb->number_of_packets * sizeof (uhci_desc_t*), KMALLOC_FLAG);
-
-	if (!tdm) {
-		ret = -ENOMEM;
-		goto err;
-	}
-
-	memset(tdm, 0, urb->number_of_packets * sizeof (uhci_desc_t*));
-
-	// First try to get all TDs. Cause: Removing already inserted TDs can only be done 
-	// racefree in three steps: unlink TDs, wait one frame, delete TDs. 
-	// So, this solutions seems simpler...
-
-	for (n = 0; n < urb->number_of_packets; n++) {
-		dbg("n:%d urb->iso_frame_desc[n].length:%d", n, urb->iso_frame_desc[n].length);
-		if (!urb->iso_frame_desc[n].length)
-			continue;  // allows ISO striping by setting length to zero in iso_descriptor
-
-
-#ifdef ISO_SANITY_CHECK
-		if(urb->iso_frame_desc[n].length > maxsze) {
-
-			err("submit_iso: urb->iso_frame_desc[%d].length(%d)>%d",n , urb->iso_frame_desc[n].length, maxsze);
-			ret=-EINVAL;		
-		}
-		else
-#endif
-		if (alloc_td (s, &td, UHCI_PTR_DEPTH)) {
-			int i;	// Cleanup allocated TDs
-
-			for (i = 0; i < n; n++)
-				if (tdm[i])
-					 delete_desc(s, tdm[i]);
-			kfree (tdm);
-			goto err;
-		}
-		last=n;
-		tdm[n] = td;
-	}
-
-	status = TD_CTRL_ACTIVE | TD_CTRL_IOS;
-
-	destination = (urb->pipe & PIPE_DEVEP_MASK) | usb_packetid (urb->pipe);
-
-	// Queue all allocated TDs
-	for (n = 0; n < urb->number_of_packets; n++) {
-		td = tdm[n];
-		if (!td)
-			continue;
-			
-		if (n  == last) {
-			status |= TD_CTRL_IOC;
-			queue_urb (s, urb);
-		}
-
-		fill_td (td, status, destination | (((urb->iso_frame_desc[n].length - 1) & 0x7ff) << 21),
-			 urb_priv->transfer_buffer_dma + urb->iso_frame_desc[n].offset);
-		list_add_tail (&td->desc_list, &urb_priv->desc_list);
-	
-		insert_td_horizontal (s, s->iso_td[(urb->start_frame + n) & 1023], td);	// store in iso-tds
-	}
-
-	kfree (tdm);
-	dbg("ISO-INT# %i, start %i, now %i", urb->number_of_packets, urb->start_frame, UHCI_GET_CURRENT_FRAME (s) & 1023);
-	ret = 0;
-
-      err:
-	__restore_flags(flags);
-	return ret;
-}
-/*-------------------------------------------------------------------*/
-// returns: 0 (no transfer queued), urb* (this urb already queued)
- 
-_static struct urb* search_dev_ep (uhci_t *s, struct urb *urb)
-{
-	struct list_head *p;
-	struct urb *tmp;
-	unsigned int mask = usb_pipecontrol(urb->pipe) ? (~USB_DIR_IN) : (~0);
-
-	dbg("search_dev_ep:");
-
-	p=s->urb_list.next;
-
-	for (; p != &s->urb_list; p = p->next) {
-		tmp = list_entry (p, struct urb, urb_list);
-		dbg("urb: %p", tmp);
-		// we can accept this urb if it is not queued at this time 
-		// or if non-iso transfer requests should be scheduled for the same device and pipe
-		if ((!usb_pipeisoc(urb->pipe) && (tmp->dev == urb->dev) && !((tmp->pipe ^ urb->pipe) & mask)) ||
-		    (urb == tmp)) {
-			return tmp;	// found another urb already queued for processing
-		}
-	}
-
-	return 0;
-}
-/*-------------------------------------------------------------------*/
-_static int uhci_submit_urb (struct urb *urb)
-{
-	uhci_t *s;
-	urb_priv_t *urb_priv;
-	int ret = 0, type;
-	unsigned long flags;
-	struct urb *queued_urb=NULL;
-	int bustime;
-		
-	if (!urb->dev || !urb->dev->bus)
-		return -ENODEV;
-
-	s = (uhci_t*) urb->dev->bus->hcpriv;
-	//dbg("submit_urb: %p type %d",urb,usb_pipetype(urb->pipe));
-	
-	if (!s->running)
-		return -ENODEV;
-	
-	type = usb_pipetype (urb->pipe);
-
-	if (usb_pipedevice (urb->pipe) == s->rh.devnum)
-		return rh_submit_urb (urb);	/* virtual root hub */
-
-	// Sanity checks
-	if (usb_maxpacket (urb->dev, urb->pipe, usb_pipeout (urb->pipe)) <= 0) {		
-		err("uhci_submit_urb: pipesize for pipe %x is zero", urb->pipe);
-		return -EMSGSIZE;
-	}
-
-	if (urb->transfer_buffer_length < 0 && type != PIPE_ISOCHRONOUS) {
-		err("uhci_submit_urb: Negative transfer length for urb %p", urb);
-		return -EINVAL;
-	}
-
-	usb_inc_dev_use (urb->dev);
-
-	spin_lock_irqsave (&s->urb_list_lock, flags);
-
-	queued_urb = search_dev_ep (s, urb); // returns already queued urb for that pipe
-
-	if (queued_urb) {
-
-		queue_dbg("found bulk urb %p\n", queued_urb);
-
-		if (( type != PIPE_BULK) ||
-		    ((type == PIPE_BULK) &&
-		     (!(urb->transfer_flags & USB_QUEUE_BULK) || !(queued_urb->transfer_flags & USB_QUEUE_BULK)))) {
-			spin_unlock_irqrestore (&s->urb_list_lock, flags);
-			usb_dec_dev_use (urb->dev);
-			err("ENXIO %08x, flags %x, urb %p, burb %p",urb->pipe,urb->transfer_flags,urb,queued_urb);
-			return -ENXIO;	// urb already queued
-		}
-	}
-
-#ifdef DEBUG_SLAB
-	urb_priv = kmem_cache_alloc(urb_priv_kmem, SLAB_FLAG);
-#else
-	urb_priv = kmalloc (sizeof (urb_priv_t), KMALLOC_FLAG);
-#endif
-	if (!urb_priv) {
-		usb_dec_dev_use (urb->dev);
-		spin_unlock_irqrestore (&s->urb_list_lock, flags);
-		return -ENOMEM;
-	}
-
-	memset(urb_priv, 0, sizeof(urb_priv_t));
-	urb->hcpriv = urb_priv;
-	INIT_LIST_HEAD (&urb_priv->desc_list);
-
-	dbg("submit_urb: scheduling %p", urb);
-	
-	if (type == PIPE_CONTROL)
-		urb_priv->setup_packet_dma = pci_map_single(s->uhci_pci, urb->setup_packet,
-							    sizeof(struct usb_ctrlrequest), PCI_DMA_TODEVICE);
-
-	if (urb->transfer_buffer_length)
-		urb_priv->transfer_buffer_dma = pci_map_single(s->uhci_pci,
-							       urb->transfer_buffer,
-							       urb->transfer_buffer_length,
-							       usb_pipein(urb->pipe) ?
-							       PCI_DMA_FROMDEVICE :
-							       PCI_DMA_TODEVICE);
-
-	if (type == PIPE_BULK) {
-	
-		if (queued_urb) {
-			while (((urb_priv_t*)queued_urb->hcpriv)->next_queued_urb)  // find last queued bulk
-				queued_urb=((urb_priv_t*)queued_urb->hcpriv)->next_queued_urb;
-			
-			((urb_priv_t*)queued_urb->hcpriv)->next_queued_urb=urb;
-		}
-		atomic_inc (&s->avoid_bulk);
-		ret = uhci_submit_bulk_urb (urb, queued_urb);
-		atomic_dec (&s->avoid_bulk);
-		spin_unlock_irqrestore (&s->urb_list_lock, flags);
-	}
-	else {
-		spin_unlock_irqrestore (&s->urb_list_lock, flags);
-		switch (type) {
-		case PIPE_ISOCHRONOUS:			
-			if (urb->bandwidth == 0) {      /* not yet checked/allocated */
-				if (urb->number_of_packets <= 0) {
-					ret = -EINVAL;
-					break;
-				}
-
-				bustime = usb_check_bandwidth (urb->dev, urb);
-				if (bustime < 0) 
-					ret = bustime;
-				else {
-					ret = uhci_submit_iso_urb(urb);
-					if (ret == 0)
-						usb_claim_bandwidth (urb->dev, urb, bustime, 1);
-				}
-			} else {        /* bandwidth is already set */
-				ret = uhci_submit_iso_urb(urb);
-			}
-			break;
-		case PIPE_INTERRUPT:
-			if (urb->bandwidth == 0) {      /* not yet checked/allocated */
-				bustime = usb_check_bandwidth (urb->dev, urb);
-				if (bustime < 0)
-					ret = bustime;
-				else {
-					ret = uhci_submit_int_urb(urb);
-					if (ret == 0)
-						usb_claim_bandwidth (urb->dev, urb, bustime, 0);
-				}
-			} else {        /* bandwidth is already set */
-				ret = uhci_submit_int_urb(urb);
-			}
-			break;
-		case PIPE_CONTROL:
-			ret = uhci_submit_control_urb (urb);
-			break;
-		default:
-			ret = -EINVAL;
-		}
-	}
-
-	dbg("submit_urb: scheduled with ret: %d", ret);
-	
-	if (ret != 0) {
-		uhci_urb_dma_unmap(s, urb, urb_priv);
-		usb_dec_dev_use (urb->dev);
-#ifdef DEBUG_SLAB
-		kmem_cache_free(urb_priv_kmem, urb_priv);
-#else
-		kfree (urb_priv);
-#endif
-		return ret;
-	}
-
-	return 0;
-}
-
-// Checks for URB timeout and removes bandwidth reclamation if URB idles too long
-_static void uhci_check_timeouts(uhci_t *s)
-{
-	struct list_head *p,*p2;
-	struct urb *urb;
-	int type;	
-
-	p = s->urb_list.prev;	
-
-	while (p != &s->urb_list) {
-		urb_priv_t *hcpriv;
-
-		p2 = p;
-		p = p->prev;
-		urb = list_entry (p2, struct urb, urb_list);
-		type = usb_pipetype (urb->pipe);
-
-		hcpriv = (urb_priv_t*)urb->hcpriv;
-		
-		if ( urb->timeout && time_after(jiffies, hcpriv->started + urb->timeout)) {
-			urb->transfer_flags |= USB_TIMEOUT_KILLED | USB_ASYNC_UNLINK;
-			async_dbg("uhci_check_timeout: timeout for %p",urb);
-			uhci_unlink_urb_async(s, urb, UNLINK_ASYNC_STORE_URB);
-		}
-#ifdef CONFIG_USB_UHCI_HIGH_BANDWIDTH
-		else if (((type == PIPE_BULK) || (type == PIPE_CONTROL)) &&  
-			 (hcpriv->use_loop) && time_after(jiffies, hcpriv->started + IDLE_TIMEOUT))
-			disable_desc_loop(s, urb);
-#endif
-
-	}
-	s->timeout_check=jiffies;
-}
-
-/*-------------------------------------------------------------------
- Virtual Root Hub
- -------------------------------------------------------------------*/
-
-_static __u8 root_hub_dev_des[] =
-{
-	0x12,			/*  __u8  bLength; */
-	0x01,			/*  __u8  bDescriptorType; Device */
-	0x00,			/*  __u16 bcdUSB; v1.0 */
-	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 root_hub_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; 8 Bytes */
-	0x00,
-	0xff			/*  __u8  ep_bInterval; 255 ms */
-};
-
-
-_static __u8 root_hub_hub_des[] =
-{
-	0x09,			/*  __u8  bLength; */
-	0x29,			/*  __u8  bDescriptorType; Hub-descriptor */
-	0x02,			/*  __u8  bNbrPorts; */
-	0x00,			/* __u16  wHubCharacteristics; */
-	0x00,
-	0x01,			/*  __u8  bPwrOn2pwrGood; 2ms */
-	0x00,			/*  __u8  bHubContrCurrent; 0 mA */
-	0x00,			/*  __u8  DeviceRemovable; *** 7 Ports max *** */
-	0xff			/*  __u8  PortPwrCtrlMask; *** 7 ports max *** */
-};
-
-/*-------------------------------------------------------------------------*/
-/* prepare Interrupt pipe transaction data; HUB INTERRUPT ENDPOINT */
-_static int rh_send_irq (struct urb *urb)
-{
-	int len = 1;
-	int i;
-	uhci_t *uhci = urb->dev->bus->hcpriv;
-	unsigned int io_addr = uhci->io_addr;
-	__u16 data = 0;
-
-	for (i = 0; i < uhci->rh.numports; i++) {
-		data |= ((inw (io_addr + USBPORTSC1 + i * 2) & 0xa) > 0 ? (1 << (i + 1)) : 0);
-		len = (i + 1) / 8 + 1;
-	}
-
-	*(__u16 *) urb->transfer_buffer = cpu_to_le16 (data);
-	urb->actual_length = len;
-	urb->status = 0;
-	
-	if ((data > 0) && (uhci->rh.send != 0)) {
-		dbg("Root-Hub INT complete: port1: %x port2: %x data: %x",
-		     inw (io_addr + USBPORTSC1), inw (io_addr + USBPORTSC2), data);
-		urb->complete (urb);
-	}
-	return 0;
-}
-
-/*-------------------------------------------------------------------------*/
-/* Virtual Root Hub INTs are polled by this timer every "intervall" ms */
-_static int rh_init_int_timer (struct urb *urb);
-
-_static void rh_int_timer_do (unsigned long ptr)
-{
-	int len;
-	struct urb *urb = (struct urb*) ptr;
-	uhci_t *uhci = urb->dev->bus->hcpriv;
-
-	if (uhci->rh.send) {
-		len = rh_send_irq (urb);
-		if (len > 0) {
-			urb->actual_length = len;
-			if (urb->complete)
-				urb->complete (urb);
-		}
-	}
-	rh_init_int_timer (urb);
-}
-
-/*-------------------------------------------------------------------------*/
-/* Root Hub INTs are polled by this timer, polling interval 20ms */
-
-_static int rh_init_int_timer (struct urb *urb)
-{
-	uhci_t *uhci = urb->dev->bus->hcpriv;
-
-	uhci->rh.interval = urb->interval;
-	init_timer (&uhci->rh.rh_int_timer);
-	uhci->rh.rh_int_timer.function = rh_int_timer_do;
-	uhci->rh.rh_int_timer.data = (unsigned long) urb;
-	uhci->rh.rh_int_timer.expires = jiffies + (HZ * 20) / 1000;
-	add_timer (&uhci->rh.rh_int_timer);
-
-	return 0;
-}
-
-/*-------------------------------------------------------------------------*/
-#define OK(x) 			len = (x); break
-
-#define CLR_RH_PORTSTAT(x) \
-		status = inw(io_addr+USBPORTSC1+2*(wIndex-1)); \
-		status = (status & 0xfff5) & ~(x); \
-		outw(status, io_addr+USBPORTSC1+2*(wIndex-1))
-
-#define SET_RH_PORTSTAT(x) \
-		status = inw(io_addr+USBPORTSC1+2*(wIndex-1)); \
-		status = (status & 0xfff5) | (x); \
-		outw(status, io_addr+USBPORTSC1+2*(wIndex-1))
-
-
-/*-------------------------------------------------------------------------*/
-/****
- ** Root Hub Control Pipe
- *************************/
-
-
-_static int rh_submit_urb (struct urb *urb)
-{
-	struct usb_device *usb_dev = urb->dev;
-	uhci_t *uhci = usb_dev->bus->hcpriv;
-	unsigned int pipe = urb->pipe;
-	struct usb_ctrlrequest *cmd = (struct usb_ctrlrequest *) urb->setup_packet;
-	void *data = urb->transfer_buffer;
-	int leni = urb->transfer_buffer_length;
-	int len = 0;
-	int status = 0;
-	int stat = 0;
-	int i;
-	unsigned int io_addr = uhci->io_addr;
-	__u16 cstatus;
-
-	__u16 bmRType_bReq;
-	__u16 wValue;
-	__u16 wIndex;
-	__u16 wLength;
-
-	if (usb_pipetype (pipe) == PIPE_INTERRUPT) {
-		dbg("Root-Hub submit IRQ: every %d ms", urb->interval);
-		uhci->rh.urb = urb;
-		uhci->rh.send = 1;
-		uhci->rh.interval = urb->interval;
-		rh_init_int_timer (urb);
-
-		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);
-
-	for (i = 0; i < 8; i++)
-		uhci->rh.c_p_r[i] = 0;
-
-	dbg("Root-Hub: adr: %2x cmd(%1x): %04x %04x %04x %04x",
-	     uhci->rh.devnum, 8, bmRType_bReq, wValue, wIndex, wLength);
-
-	switch (bmRType_bReq) {
-		/* Request Destination:
-		   without flags: Device, 
-		   RH_INTERFACE: interface, 
-		   RH_ENDPOINT: endpoint,
-		   RH_CLASS means HUB here, 
-		   RH_OTHER | RH_CLASS  almost ever means HUB_PORT here 
-		 */
-
-	case RH_GET_STATUS:
-		*(__u16 *) data = cpu_to_le16 (1);
-		OK (2);
-	case RH_GET_STATUS | RH_INTERFACE:
-		*(__u16 *) data = cpu_to_le16 (0);
-		OK (2);
-	case RH_GET_STATUS | RH_ENDPOINT:
-		*(__u16 *) data = cpu_to_le16 (0);
-		OK (2);
-	case RH_GET_STATUS | RH_CLASS:
-		*(__u32 *) data = cpu_to_le32 (0);
-		OK (4);		/* hub power ** */
-	case RH_GET_STATUS | RH_OTHER | RH_CLASS:
-		status = inw (io_addr + USBPORTSC1 + 2 * (wIndex - 1));
-		cstatus = ((status & USBPORTSC_CSC) >> (1 - 0)) |
-			((status & USBPORTSC_PEC) >> (3 - 1)) |
-			(uhci->rh.c_p_r[wIndex - 1] << (0 + 4));
-		status = (status & USBPORTSC_CCS) |
-			((status & USBPORTSC_PE) >> (2 - 1)) |
-			((status & USBPORTSC_SUSP) >> (12 - 2)) |
-			((status & USBPORTSC_PR) >> (9 - 4)) |
-			(1 << 8) |	/* power on ** */
-			((status & USBPORTSC_LSDA) << (-8 + 9));
-
-		*(__u16 *) data = cpu_to_le16 (status);
-		*(__u16 *) (data + 2) = cpu_to_le16 (cstatus);
-		OK (4);
-
-	case RH_CLEAR_FEATURE | RH_ENDPOINT:
-		switch (wValue) {
-		case (RH_ENDPOINT_STALL):
-			OK (0);
-		}
-		break;
-
-	case RH_CLEAR_FEATURE | RH_CLASS:
-		switch (wValue) {
-		case (RH_C_HUB_OVER_CURRENT):
-			OK (0);	/* hub power over current ** */
-		}
-		break;
-
-	case RH_CLEAR_FEATURE | RH_OTHER | RH_CLASS:
-		switch (wValue) {
-		case (RH_PORT_ENABLE):
-			CLR_RH_PORTSTAT (USBPORTSC_PE);
-			OK (0);
-		case (RH_PORT_SUSPEND):
-			CLR_RH_PORTSTAT (USBPORTSC_SUSP);
-			OK (0);
-		case (RH_PORT_POWER):
-			OK (0);	/* port power ** */
-		case (RH_C_PORT_CONNECTION):
-			SET_RH_PORTSTAT (USBPORTSC_CSC);
-			OK (0);
-		case (RH_C_PORT_ENABLE):
-			SET_RH_PORTSTAT (USBPORTSC_PEC);
-			OK (0);
-		case (RH_C_PORT_SUSPEND):
-/*** WR_RH_PORTSTAT(RH_PS_PSSC); */
-			OK (0);
-		case (RH_C_PORT_OVER_CURRENT):
-			OK (0);	/* port power over current ** */
-		case (RH_C_PORT_RESET):
-			uhci->rh.c_p_r[wIndex - 1] = 0;
-			OK (0);
-		}
-		break;
-
-	case RH_SET_FEATURE | RH_OTHER | RH_CLASS:
-		switch (wValue) {
-		case (RH_PORT_SUSPEND):
-			SET_RH_PORTSTAT (USBPORTSC_SUSP);
-			OK (0);
-		case (RH_PORT_RESET):
-			SET_RH_PORTSTAT (USBPORTSC_PR);
-			uhci_wait_ms (10);
-			uhci->rh.c_p_r[wIndex - 1] = 1;
-			CLR_RH_PORTSTAT (USBPORTSC_PR);
-			udelay (10);
-			SET_RH_PORTSTAT (USBPORTSC_PE);
-			uhci_wait_ms (10);
-			SET_RH_PORTSTAT (0xa);
-			OK (0);
-		case (RH_PORT_POWER):
-			OK (0);	/* port power ** */
-		case (RH_PORT_ENABLE):
-			SET_RH_PORTSTAT (USBPORTSC_PE);
-			OK (0);
-		}
-		break;
-
-	case RH_SET_ADDRESS:
-		uhci->rh.devnum = wValue;
-		OK (0);
-
-	case RH_GET_DESCRIPTOR:
-		switch ((wValue & 0xff00) >> 8) {
-		case (0x01):	/* device descriptor */
-			len = min_t(unsigned int, leni,
-				  min_t(unsigned int,
-				      sizeof (root_hub_dev_des), wLength));
-			memcpy (data, root_hub_dev_des, len);
-			OK (len);
-		case (0x02):	/* configuration descriptor */
-			len = min_t(unsigned int, leni,
-				  min_t(unsigned int,
-				      sizeof (root_hub_config_des), wLength));
-			memcpy (data, root_hub_config_des, len);
-			OK (len);
-		case (0x03):	/* string descriptors */
-			len = usb_root_hub_string (wValue & 0xff,
-			        uhci->io_addr, "UHCI",
-				data, wLength);
-			if (len > 0) {
-				OK(min_t(int, leni, len));
-			} else 
-				stat = -EPIPE;
-		}
-		break;
-
-	case RH_GET_DESCRIPTOR | RH_CLASS:
-		root_hub_hub_des[2] = uhci->rh.numports;
-		len = min_t(unsigned int, leni,
-			  min_t(unsigned int, sizeof (root_hub_hub_des), wLength));
-		memcpy (data, root_hub_hub_des, len);
-		OK (len);
-
-	case RH_GET_CONFIGURATION:
-		*(__u8 *) data = 0x01;
-		OK (1);
-
-	case RH_SET_CONFIGURATION:
-		OK (0);
-	default:
-		stat = -EPIPE;
-	}
-
-	dbg("Root-Hub stat port1: %x port2: %x",
-	     inw (io_addr + USBPORTSC1), inw (io_addr + USBPORTSC2));
-
-	urb->actual_length = len;
-	urb->status = stat;
-	urb->dev=NULL;
-	if (urb->complete)
-		urb->complete (urb);
-	return 0;
-}
-/*-------------------------------------------------------------------------*/
-
-_static int rh_unlink_urb (struct urb *urb)
-{
-	uhci_t *uhci = urb->dev->bus->hcpriv;
-
-	if (uhci->rh.urb==urb) {
-		dbg("Root-Hub unlink IRQ");
-		uhci->rh.send = 0;
-		del_timer (&uhci->rh.rh_int_timer);
-	}
-	return 0;
-}
-/*-------------------------------------------------------------------*/
-
-/*
- * Map status to standard result codes
- *
- * <status> is (td->status & 0xFE0000) [a.k.a. uhci_status_bits(td->status)
- * <dir_out> is True for output TDs and False for input TDs.
- */
-_static int uhci_map_status (int status, int dir_out)
-{
-	if (!status)
-		return 0;
-	if (status & TD_CTRL_BITSTUFF)	/* Bitstuff error */
-		return -EPROTO;
-	if (status & TD_CTRL_CRCTIMEO) {	/* CRC/Timeout */
-		if (dir_out)
-			return -ETIMEDOUT;
-		else
-			return -EILSEQ;
-	}
-	if (status & TD_CTRL_NAK)	/* NAK */
-		return -ETIMEDOUT;
-	if (status & TD_CTRL_BABBLE)	/* Babble */
-		return -EOVERFLOW;
-	if (status & TD_CTRL_DBUFERR)	/* Buffer error */
-		return -ENOSR;
-	if (status & TD_CTRL_STALLED)	/* Stalled */
-		return -EPIPE;
-	if (status & TD_CTRL_ACTIVE)	/* Active */
-		return 0;
-
-	return -EPROTO;
-}
-
-/*
- * Only the USB core should call uhci_alloc_dev and uhci_free_dev
- */
-_static int uhci_alloc_dev (struct usb_device *usb_dev)
-{
-	return 0;
-}
-
-_static void uhci_unlink_urbs(uhci_t *s, struct usb_device *usb_dev, int remove_all)
-{
-	unsigned long flags;
-	struct list_head *p;
-	struct list_head *p2;
-	struct urb *urb;
-
-	spin_lock_irqsave (&s->urb_list_lock, flags);
-	p = s->urb_list.prev;	
-	while (p != &s->urb_list) {
-		p2 = p;
-		p = p->prev ;
-		urb = list_entry (p2, struct urb, urb_list);
-		dbg("urb: %p, dev %p, %p", urb, usb_dev,urb->dev);
-		
-		//urb->transfer_flags |=USB_ASYNC_UNLINK; 
-			
-		if (remove_all || (usb_dev == urb->dev)) {
-			spin_unlock_irqrestore (&s->urb_list_lock, flags);
-			warn("forced removing of queued URB %p due to disconnect",urb);
-			uhci_unlink_urb(urb);
-			urb->dev = NULL; // avoid further processing of this URB
-			spin_lock_irqsave (&s->urb_list_lock, flags);
-			p = s->urb_list.prev;	
-		}
-	}
-	spin_unlock_irqrestore (&s->urb_list_lock, flags);
-}
-
-_static int uhci_free_dev (struct usb_device *usb_dev)
-{
-	uhci_t *s;
-	
-
-	if(!usb_dev || !usb_dev->bus || !usb_dev->bus->hcpriv)
-		return -EINVAL;
-	
-	s=(uhci_t*) usb_dev->bus->hcpriv;	
-	uhci_unlink_urbs(s, usb_dev, 0);
-
-	return 0;
-}
-
-/*
- * uhci_get_current_frame_number()
- *
- * returns the current frame number for a USB bus/controller.
- */
-_static int uhci_get_current_frame_number (struct usb_device *usb_dev)
-{
-	return UHCI_GET_CURRENT_FRAME ((uhci_t*) usb_dev->bus->hcpriv);
-}
-
-struct usb_operations uhci_device_operations =
-{
-	uhci_alloc_dev,
-	uhci_free_dev,
-	uhci_get_current_frame_number,
-	uhci_submit_urb,
-	uhci_unlink_urb
-};
-
-_static void correct_data_toggles(struct urb *urb)
-{
-	usb_settoggle (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe), 
-		       !usb_gettoggle (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe)));
-
-	while(urb) {
-		urb_priv_t *priv=urb->hcpriv;		
-		uhci_desc_t *qh = list_entry (priv->desc_list.next, uhci_desc_t, desc_list);
-		struct list_head *p = qh->vertical.next;
-		uhci_desc_t *td;
-		dbg("URB to correct %p\n", urb);
-	
-		for (; p != &qh->vertical; p = p->next) {
-			td = list_entry (p, uhci_desc_t, vertical);
-			td->hw.td.info^=cpu_to_le32(1<<TD_TOKEN_TOGGLE);
-		}
-		urb=priv->next_queued_urb;
-	}
-}
-
-/* 
- * For IN-control transfers, process_transfer gets a bit more complicated,
- * since there are devices that return less data (eg. strings) than they
- * have announced. This leads to a queue abort due to the short packet,
- * the status stage is not executed. If this happens, the status stage
- * is manually re-executed.
- * mode: PROCESS_TRANSFER_REGULAR: regular (unlink QH)
- *       PROCESS_TRANSFER_DONT_UNLINK: QHs already unlinked (for async unlink_urb)
- */
-
-_static int process_transfer (uhci_t *s, struct urb *urb, int mode)
-{
-	int ret = 0;
-	urb_priv_t *urb_priv = urb->hcpriv;
-	struct list_head *qhl = urb_priv->desc_list.next;
-	uhci_desc_t *qh = list_entry (qhl, uhci_desc_t, desc_list);
-	struct list_head *p = qh->vertical.next;
-	uhci_desc_t *desc= list_entry (urb_priv->desc_list.prev, uhci_desc_t, desc_list);
-	uhci_desc_t *last_desc = list_entry (desc->vertical.prev, uhci_desc_t, vertical);
-	int data_toggle = usb_gettoggle (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe));	// save initial data_toggle
-	int maxlength; 	// extracted and remapped info from TD
-	int actual_length;
-	int status = 0;
-
-	//dbg("process_transfer: urb %p, urb_priv %p, qh %p last_desc %p\n",urb,urb_priv, qh, last_desc);
-
-	/* if the status phase has been retriggered and the
-	   queue is empty or the last status-TD is inactive, the retriggered
-	   status stage is completed
-	 */
-
-	if (urb_priv->flags && 
-	    ((qh->hw.qh.element == cpu_to_le32(UHCI_PTR_TERM)) || !is_td_active(desc)))
-		goto transfer_finished;
-
-	urb->actual_length=0;
-
-	for (; p != &qh->vertical; p = p->next) {
-		desc = list_entry (p, uhci_desc_t, vertical);
-
-		if (is_td_active(desc)) {	// do not process active TDs
-			if (mode == CLEAN_TRANSFER_DELETION_MARK) // if called from async_unlink
-				uhci_clean_transfer(s, urb, qh, CLEAN_TRANSFER_DELETION_MARK);
-			return ret;
-		}
-	
-		actual_length = uhci_actual_length(le32_to_cpu(desc->hw.td.status));		// extract transfer parameters from TD
-		maxlength = (((le32_to_cpu(desc->hw.td.info) >> 21) & 0x7ff) + 1) & 0x7ff;
-		status = uhci_map_status (uhci_status_bits (le32_to_cpu(desc->hw.td.status)), usb_pipeout (urb->pipe));
-
-		if (status == -EPIPE) { 		// see if EP is stalled
-			// set up stalled condition
-			usb_endpoint_halt (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe));
-		}
-
-		if (status && (status != -EPIPE)) {	// if any error occurred stop processing of further TDs
-			// only set ret if status returned an error
-  is_error:
-			ret = status;
-			urb->error_count++;
-			break;
-		}
-		else if ((le32_to_cpu(desc->hw.td.info) & 0xff) != USB_PID_SETUP)
-			urb->actual_length += actual_length;
-
-		// got less data than requested
-		if ( (actual_length < maxlength)) {
-			if (urb->transfer_flags & USB_DISABLE_SPD) {
-				status = -EREMOTEIO;	// treat as real error
-				dbg("process_transfer: SPD!!");
-				break;	// exit after this TD because SP was detected
-			}
-
-			// short read during control-IN: re-start status stage
-			if ((usb_pipetype (urb->pipe) == PIPE_CONTROL)) {
-				if (uhci_packetid(le32_to_cpu(last_desc->hw.td.info)) == USB_PID_OUT) {
-			
-					set_qh_element(qh, last_desc->dma_addr);  // re-trigger status stage
-					dbg("short packet during control transfer, retrigger status stage @ %p",last_desc);
-					urb_priv->flags = 1; // mark as short control packet
-					return 0;
-				}
-			}
-			// all other cases: short read is OK
-			data_toggle = uhci_toggle (le32_to_cpu(desc->hw.td.info));
-			break;
-		}
-		else if (status)
-			goto is_error;
-
-		data_toggle = uhci_toggle (le32_to_cpu(desc->hw.td.info));
-		queue_dbg("process_transfer: len:%d status:%x mapped:%x toggle:%d", actual_length, le32_to_cpu(desc->hw.td.status),status, data_toggle);      
-
-	}
-
-	if (usb_pipetype (urb->pipe) == PIPE_BULK ) {  /* toggle correction for short bulk transfers (nonqueued/queued) */
-
-		urb_priv_t *priv=(urb_priv_t*)urb->hcpriv;
-		struct urb *next_queued_urb=priv->next_queued_urb;
-
-		if (next_queued_urb) {
-			urb_priv_t *next_priv=(urb_priv_t*)next_queued_urb->hcpriv;
-			uhci_desc_t *qh = list_entry (next_priv->desc_list.next, uhci_desc_t, desc_list);
-			uhci_desc_t *first_td=list_entry (qh->vertical.next, uhci_desc_t, vertical);
-
-			if (data_toggle == uhci_toggle (le32_to_cpu(first_td->hw.td.info))) {
-				err("process_transfer: fixed toggle");
-				correct_data_toggles(next_queued_urb);
-			}						
-		}
-		else
-			usb_settoggle (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe), !data_toggle);		
-	}
-
- transfer_finished:
-	
-	uhci_clean_transfer(s, urb, qh, mode);
-
-	urb->status = status;
-
-#ifdef CONFIG_USB_UHCI_HIGH_BANDWIDTH	
-	disable_desc_loop(s,urb);
-#endif	
-
-	queue_dbg("process_transfer: (end) urb %p, wanted len %d, len %d status %x err %d",
-		urb,urb->transfer_buffer_length,urb->actual_length, urb->status, urb->error_count);
-	return ret;
-}
-
-_static int process_interrupt (uhci_t *s, struct urb *urb)
-{
-	int i, ret = -EINPROGRESS;
-	urb_priv_t *urb_priv = urb->hcpriv;
-	struct list_head *p = urb_priv->desc_list.next;
-	uhci_desc_t *desc = list_entry (urb_priv->desc_list.prev, uhci_desc_t, desc_list);
-
-	int actual_length;
-	int status = 0;
-
-	//dbg("urb contains interrupt request");
-
-	for (i = 0; p != &urb_priv->desc_list; p = p->next, i++)	// Maybe we allow more than one TD later ;-)
-	{
-		desc = list_entry (p, uhci_desc_t, desc_list);
-
-		if (is_td_active(desc)) {
-			// do not process active TDs
-			//dbg("TD ACT Status @%p %08x",desc,le32_to_cpu(desc->hw.td.status));
-			break;
-		}
-
-		if (!(desc->hw.td.status & cpu_to_le32(TD_CTRL_IOC))) {
-			// do not process one-shot TDs, no recycling
-			break;
-		}
-		// extract transfer parameters from TD
-
-		actual_length = uhci_actual_length(le32_to_cpu(desc->hw.td.status));
-		status = uhci_map_status (uhci_status_bits (le32_to_cpu(desc->hw.td.status)), usb_pipeout (urb->pipe));
-
-		// see if EP is stalled
-		if (status == -EPIPE) {
-			// set up stalled condition
-			usb_endpoint_halt (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe));
-		}
-
-		// if any error occurred: ignore this td, and continue
-		if (status != 0) {
-			//uhci_show_td (desc);
-			urb->error_count++;
-			goto recycle;
-		}
-		else
-			urb->actual_length = actual_length;
-
-	recycle:
-		uhci_urb_dma_sync(s, urb, urb->hcpriv);
-		if (urb->complete) {
-			//dbg("process_interrupt: calling completion, status %i",status);
-			urb->status = status;
-			((urb_priv_t*)urb->hcpriv)->flags=1; // if unlink_urb is called during completion
-
-			spin_unlock(&s->urb_list_lock);
-			
-			urb->complete ((struct urb *) urb);
-			
-			spin_lock(&s->urb_list_lock);
-
-			((urb_priv_t*)urb->hcpriv)->flags=0;		       			
-		}
-		
-		if ((urb->status != -ECONNABORTED) && (urb->status != ECONNRESET) &&
-			    (urb->status != -ENOENT)) {
-
-			urb->status = -EINPROGRESS;
-
-			// Recycle INT-TD if interval!=0, else mark TD as one-shot
-			if (urb->interval) {
-				
-				desc->hw.td.info &= cpu_to_le32(~(1 << TD_TOKEN_TOGGLE));
-				if (status==0) {
-					((urb_priv_t*)urb->hcpriv)->started=jiffies;
-					desc->hw.td.info |= cpu_to_le32((usb_gettoggle (urb->dev, usb_pipeendpoint (urb->pipe),
-									    usb_pipeout (urb->pipe)) << TD_TOKEN_TOGGLE));
-					usb_dotoggle (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe));
-				} else {
-					desc->hw.td.info |= cpu_to_le32((!usb_gettoggle (urb->dev, usb_pipeendpoint (urb->pipe),
-									     usb_pipeout (urb->pipe)) << TD_TOKEN_TOGGLE));
-				}
-				desc->hw.td.status= cpu_to_le32((urb->pipe & TD_CTRL_LS) | TD_CTRL_ACTIVE | TD_CTRL_IOC |
-					(urb->transfer_flags & USB_DISABLE_SPD ? 0 : TD_CTRL_SPD) | (3 << 27));
-				mb();
-			}
-			else {
-				uhci_unlink_urb_async(s, urb, UNLINK_ASYNC_STORE_URB);
-				uhci_clean_iso_step2(s, urb_priv);
-				// correct toggle after unlink
-				usb_dotoggle (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe));
-				clr_td_ioc(desc); // inactivate TD
-			}
-		}
-	}
-
-	return ret;
-}
-
-// mode: PROCESS_ISO_REGULAR: processing only for done TDs, unlink TDs
-// mode: PROCESS_ISO_FORCE: force processing, don't unlink TDs (already unlinked)
-
-_static int process_iso (uhci_t *s, struct urb *urb, int mode)
-{
-	int i;
-	int ret = 0;
-	urb_priv_t *urb_priv = urb->hcpriv;
-	struct list_head *p = urb_priv->desc_list.next, *p_tmp;
-	uhci_desc_t *desc = list_entry (urb_priv->desc_list.prev, uhci_desc_t, desc_list);
-
-	dbg("urb contains iso request");
-	if (is_td_active(desc) && mode==PROCESS_ISO_REGULAR)
-		return -EXDEV;	// last TD not finished
-
-	urb->error_count = 0;
-	urb->actual_length = 0;
-	urb->status = 0;
-	dbg("process iso urb %p, %li, %i, %i, %i %08x",urb,jiffies,UHCI_GET_CURRENT_FRAME(s),
-	    urb->number_of_packets,mode,le32_to_cpu(desc->hw.td.status));
-
-	for (i = 0; p != &urb_priv->desc_list;  i++) {
-		desc = list_entry (p, uhci_desc_t, desc_list);
-		
-		//uhci_show_td(desc);
-		if (is_td_active(desc)) {
-			// means we have completed the last TD, but not the TDs before
-			desc->hw.td.status &= cpu_to_le32(~TD_CTRL_ACTIVE);
-			dbg("TD still active (%x)- grrr. paranoia!", le32_to_cpu(desc->hw.td.status));
-			ret = -EXDEV;
-			urb->iso_frame_desc[i].status = ret;
-			unlink_td (s, desc, 1);
-			// FIXME: immediate deletion may be dangerous
-			goto err;
-		}
-
-		if (mode == PROCESS_ISO_REGULAR)
-			unlink_td (s, desc, 1);
-
-		if (urb->number_of_packets <= i) {
-			dbg("urb->number_of_packets (%d)<=(%d)", urb->number_of_packets, i);
-			ret = -EINVAL;
-			goto err;
-		}
-
-		urb->iso_frame_desc[i].actual_length = uhci_actual_length(le32_to_cpu(desc->hw.td.status));
-		urb->iso_frame_desc[i].status = uhci_map_status (uhci_status_bits (le32_to_cpu(desc->hw.td.status)), usb_pipeout (urb->pipe));
-		urb->actual_length += urb->iso_frame_desc[i].actual_length;
-
-	      err:
-
-		if (urb->iso_frame_desc[i].status != 0) {
-			urb->error_count++;
-			urb->status = urb->iso_frame_desc[i].status;
-		}
-		dbg("process_iso: %i: len:%d %08x status:%x",
-		     i, urb->iso_frame_desc[i].actual_length, le32_to_cpu(desc->hw.td.status),urb->iso_frame_desc[i].status);
-
-		p_tmp = p;
-		p = p->next;
-		list_del (p_tmp);
-		delete_desc (s, desc);
-	}
-	
-	dbg("process_iso: exit %i (%d), actual_len %i", i, ret,urb->actual_length);
-	return ret;
-}
-
-
-_static int process_urb (uhci_t *s, struct list_head *p)
-{
-	int ret = 0;
-	struct urb *urb;
-
-	urb=list_entry (p, struct urb, urb_list);
-	//dbg("process_urb: found queued urb: %p", urb);
-
-	switch (usb_pipetype (urb->pipe)) {
-	case PIPE_CONTROL:
-		ret = process_transfer (s, urb, CLEAN_TRANSFER_REGULAR);
-		break;
-	case PIPE_BULK:
-		if (!s->avoid_bulk.counter)
-			ret = process_transfer (s, urb, CLEAN_TRANSFER_REGULAR);
-		else
-			return 0;
-		break;
-	case PIPE_ISOCHRONOUS:
-		ret = process_iso (s, urb, PROCESS_ISO_REGULAR);
-		break;
-	case PIPE_INTERRUPT:
-		ret = process_interrupt (s, urb);
-		break;
-	}
-
-	if (urb->status != -EINPROGRESS) {
-		urb_priv_t *urb_priv;
-		struct usb_device *usb_dev;
-		
-		usb_dev=urb->dev;
-
-		/* Release bandwidth for Interrupt or Iso transfers */
-		if (urb->bandwidth) {
-			if (usb_pipetype(urb->pipe)==PIPE_ISOCHRONOUS)
-				usb_release_bandwidth (urb->dev, urb, 1);
-			else if (usb_pipetype(urb->pipe)==PIPE_INTERRUPT && urb->interval)
-				usb_release_bandwidth (urb->dev, urb, 0);
-		}
-
-		dbg("dequeued urb: %p", urb);
-		dequeue_urb (s, urb);
-
-		urb_priv = urb->hcpriv;
-
-		uhci_urb_dma_unmap(s, urb, urb_priv);
-
-#ifdef DEBUG_SLAB
-		kmem_cache_free(urb_priv_kmem, urb_priv);
-#else
-		kfree (urb_priv);
-#endif
-
-		if ((usb_pipetype (urb->pipe) != PIPE_INTERRUPT)) {  // process_interrupt does completion on its own		
-			struct urb *next_urb = urb->next;
-			int is_ring = 0;
-			int contains_killed = 0;
-			int loop_count=0;
-			
-			if (next_urb) {
-				// Find out if the URBs are linked to a ring
-				while  (next_urb != NULL && next_urb != urb && loop_count < MAX_NEXT_COUNT) {
-					if (next_urb->status == -ENOENT) {// killed URBs break ring structure & resubmission
-						contains_killed = 1;
-						break;
-					}	
-					next_urb = next_urb->next;
-					loop_count++;
-				}
-				
-				if (loop_count == MAX_NEXT_COUNT)
-					err("process_urb: Too much linked URBs in ring detection!");
-
-				if (next_urb == urb)
-					is_ring=1;
-			}			
-
-			// Submit idle/non-killed URBs linked with urb->next
-			// Stop before the current URB				
-			
-			next_urb = urb->next;	
-			if (next_urb && !contains_killed) {
-				int ret_submit;
-				next_urb = urb->next;	
-				
-				loop_count=0;
-				while (next_urb != NULL && next_urb != urb && loop_count < MAX_NEXT_COUNT) {
-					if (next_urb->status != -EINPROGRESS) {
-					
-						if (next_urb->status == -ENOENT) 
-							break;
-
-						spin_unlock(&s->urb_list_lock);
-
-						ret_submit=uhci_submit_urb(next_urb);
-						spin_lock(&s->urb_list_lock);
-						
-						if (ret_submit)
-							break;						
-					}
-					loop_count++;
-					next_urb = next_urb->next;
-				}
-				if (loop_count == MAX_NEXT_COUNT)
-					err("process_urb: Too much linked URBs in resubmission!");
-			}
-
-			// Completion
-			if (urb->complete) {
-				int was_unlinked = (urb->status == -ENOENT);
-				urb->dev = NULL;
-				spin_unlock(&s->urb_list_lock);
-
-				urb->complete ((struct urb *) urb);
-
-				// Re-submit the URB if ring-linked
-				if (is_ring && !was_unlinked && !contains_killed) {
-					urb->dev=usb_dev;
-					uhci_submit_urb (urb);
-				}
-				spin_lock(&s->urb_list_lock);
-			}
-			
-			usb_dec_dev_use (usb_dev);
-		}
-	}
-
-	return ret;
-}
-
-_static void uhci_interrupt (int irq, void *__uhci, struct pt_regs *regs)
-{
-	uhci_t *s = __uhci;
-	unsigned int io_addr = s->io_addr;
-	unsigned short status;
-	struct list_head *p, *p2;
-	int restarts, work_done;
-	
-	/*
-	 * Read the interrupt status, and write it back to clear the
-	 * interrupt cause
-	 */
-
-	status = inw (io_addr + USBSTS);
-
-	if (!status)		/* shared interrupt, not mine */
-		return;
-
-	dbg("interrupt");
-
-	if (status != 1) {
-		// Avoid too much error messages at a time
-		if (time_after(jiffies, s->last_error_time + ERROR_SUPPRESSION_TIME)) {
-			warn("interrupt, status %x, frame# %i", status, 
-			     UHCI_GET_CURRENT_FRAME(s));
-			s->last_error_time = jiffies;
-		}
-		
-		// remove host controller halted state
-		if ((status&0x20) && (s->running)) {
-			err("Host controller halted, trying to restart.");
-			outw (USBCMD_RS | inw(io_addr + USBCMD), io_addr + USBCMD);
-		}
-		//uhci_show_status (s);
-	}
-	/*
-	 * traverse the list in *reverse* direction, because new entries
-	 * may be added at the end.
-	 * also, because process_urb may unlink the current urb,
-	 * we need to advance the list before
-	 * New: check for max. workload and restart count
-	 */
-
-	spin_lock (&s->urb_list_lock);
-
-	restarts=0;
-	work_done=0;
-
-restart:
-	s->unlink_urb_done=0;
-	p = s->urb_list.prev;	
-
-	while (p != &s->urb_list && (work_done < 1024)) {
-		p2 = p;
-		p = p->prev;
-
-		process_urb (s, p2);
-		
-		work_done++;
-
-		if (s->unlink_urb_done) {
-			s->unlink_urb_done=0;
-			restarts++;
-			
-			if (restarts<16)	// avoid endless restarts
-				goto restart;
-			else 
-				break;
-		}
-	}
-	if (time_after(jiffies, s->timeout_check + (HZ/30)))
-		uhci_check_timeouts(s);
-
-	clean_descs(s, CLEAN_NOT_FORCED);
-	uhci_cleanup_unlink(s, CLEAN_NOT_FORCED);
-	uhci_switch_timer_int(s);
-							
-	spin_unlock (&s->urb_list_lock);
-	
-	outw (status, io_addr + USBSTS);
-
-	//dbg("uhci_interrupt: done");
-}
-
-_static void reset_hc (uhci_t *s)
-{
-	unsigned int io_addr = s->io_addr;
-
-	s->apm_state = 0;
-	/* Global reset for 50ms */
-	outw (USBCMD_GRESET, io_addr + USBCMD);
-	uhci_wait_ms (50);
-	outw (0, io_addr + USBCMD);
-	uhci_wait_ms (10);
-}
-
-_static void start_hc (uhci_t *s)
-{
-	unsigned int io_addr = s->io_addr;
-	int timeout = 10;
-
-	/*
-	 * Reset the HC - this will force us to get a
-	 * new notification of any already connected
-	 * ports due to the virtual disconnect that it
-	 * implies.
-	 */
-	outw (USBCMD_HCRESET, io_addr + USBCMD);
-
-	while (inw (io_addr + USBCMD) & USBCMD_HCRESET) {
-		if (!--timeout) {
-			err("USBCMD_HCRESET timed out!");
-			break;
-		}
-		udelay(1);
-	}
-
-	/* Turn on all interrupts */
-	outw (USBINTR_TIMEOUT | USBINTR_RESUME | USBINTR_IOC | USBINTR_SP, io_addr + USBINTR);
-
-	/* Start at frame 0 */
-	outw (0, io_addr + USBFRNUM);
-	outl (s->framelist_dma, io_addr + USBFLBASEADD);
-
-	/* Run and mark it configured with a 64-byte max packet */
-	outw (USBCMD_RS | USBCMD_CF | USBCMD_MAXP, io_addr + USBCMD);
-	s->apm_state = 1;
-	s->running = 1;
-}
-
-/* No  __devexit, since it maybe called from alloc_uhci() */
-_static void
-uhci_pci_remove (struct pci_dev *dev)
-{
-	uhci_t *s = pci_get_drvdata(dev);
-	struct usb_device *root_hub = s->bus->root_hub;
-
-	s->running = 0;		    // Don't allow submit_urb
-
-	if (root_hub)
-		usb_disconnect (&root_hub);
-
-	reset_hc (s);
-	wait_ms (1);
-
-	uhci_unlink_urbs (s, 0, CLEAN_FORCED);  // Forced unlink of remaining URBs
-	uhci_cleanup_unlink (s, CLEAN_FORCED);  // force cleanup of async killed URBs
-	
-	usb_deregister_bus (s->bus);
-
-	release_region (s->io_addr, s->io_size);
-	free_irq (s->irq, s);
-	usb_free_bus (s->bus);
-	cleanup_skel (s);
-	kfree (s);
-}
-
-_static int __init uhci_start_usb (uhci_t *s)
-{				/* start it up */
-	/* connect the virtual root hub */
-	struct usb_device *usb_dev;
-
-	usb_dev = usb_alloc_dev (NULL, s->bus);
-	if (!usb_dev)
-		return -1;
-
-	s->bus->root_hub = usb_dev;
-	usb_connect (usb_dev);
-
-	if (usb_new_device (usb_dev) != 0) {
-		usb_free_dev (usb_dev);
-		return -1;
-	}
-
-	return 0;
-}
-
-#ifdef CONFIG_PM
-_static int
-uhci_pci_suspend (struct pci_dev *dev, u32 state)
-{
-	reset_hc((uhci_t *) pci_get_drvdata(dev));
-	return 0;
-}
-
-_static int
-uhci_pci_resume (struct pci_dev *dev)
-{
-	start_hc((uhci_t *) pci_get_drvdata(dev));
-	return 0;
-}
-#endif
-
-_static int __devinit alloc_uhci (struct pci_dev *dev, int irq, unsigned int io_addr, unsigned int io_size)
-{
-	uhci_t *s;
-	struct usb_bus *bus;
-	char buf[8], *bufp = buf;
-
-#ifndef __sparc__
-	sprintf(buf, "%d", irq);
-#else
-	bufp = __irq_itoa(irq);
-#endif
-	printk(KERN_INFO __FILE__ ": USB UHCI at I/O 0x%x, IRQ %s\n",
-		io_addr, bufp);
-
-	s = kmalloc (sizeof (uhci_t), GFP_KERNEL);
-	if (!s)
-		return -1;
-
-	memset (s, 0, sizeof (uhci_t));
-	INIT_LIST_HEAD (&s->free_desc);
-	INIT_LIST_HEAD (&s->urb_list);
-	INIT_LIST_HEAD (&s->urb_unlinked);
-	spin_lock_init (&s->urb_list_lock);
-	spin_lock_init (&s->qh_lock);
-	spin_lock_init (&s->td_lock);
-	atomic_set(&s->avoid_bulk, 0);
-	s->irq = -1;
-	s->io_addr = io_addr;
-	s->io_size = io_size;
-	s->uhci_pci=dev;
-
-	bus = usb_alloc_bus (&uhci_device_operations);
-	if (!bus) {
-		kfree (s);
-		return -1;
-	}
-
-	s->bus = bus;
-	bus->bus_name = dev->slot_name;
-	bus->hcpriv = s;
-
-	/* UHCI specs says devices must have 2 ports, but goes on to say */
-	/* they may have more but give no way to determine how many they */
-	/* have, so default to 2 */
-	/* According to the UHCI spec, Bit 7 is always set to 1. So we try */
-	/* to use this to our advantage */
-
-	for (s->maxports = 0; s->maxports < (io_size - 0x10) / 2; s->maxports++) {
-		unsigned int portstatus;
-
-		portstatus = inw (io_addr + 0x10 + (s->maxports * 2));
-		dbg("port %i, adr %x status %x", s->maxports,
-			io_addr + 0x10 + (s->maxports * 2), portstatus);
-		if (!(portstatus & 0x0080))
-			break;
-	}
-	warn("Detected %d ports", s->maxports);
-
-	/* This is experimental so anything less than 2 or greater than 8 is */
-	/*  something weird and we'll ignore it */
-	if (s->maxports < 2 || s->maxports > 8) {
-		dbg("Port count misdetected, forcing to 2 ports");
-		s->maxports = 2;
-	}
-
-	s->rh.numports = s->maxports;
-	s->loop_usage=0;
-	if (init_skel (s)) {
-		usb_free_bus (bus);
-		kfree(s);
-		return -1;
-	}
-
-	request_region (s->io_addr, io_size, MODNAME);
-	reset_hc (s);
-	usb_register_bus (s->bus);
-
-	start_hc (s);
-
-	if (request_irq (irq, uhci_interrupt, SA_SHIRQ, MODNAME, s)) {
-		err("request_irq %d failed!",irq);
-		usb_free_bus (bus);
-		reset_hc (s);
-		release_region (s->io_addr, s->io_size);
-		cleanup_skel(s);
-		kfree(s);
-		return -1;
-	}
-
-	/* Enable PIRQ */
-	pci_write_config_word (dev, USBLEGSUP, USBLEGSUP_DEFAULT);
-
-	s->irq = irq;
-
-	if(uhci_start_usb (s) < 0) {
-		uhci_pci_remove(dev);
-		return -1;
-	}
-
-	//chain new uhci device into global list
-	pci_set_drvdata(dev, s);
-	devs=s;
-
-	return 0;
-}
-
-_static int __devinit
-uhci_pci_probe (struct pci_dev *dev, const struct pci_device_id *id)
-{
-	int i;
-
-	if (pci_enable_device(dev) < 0)
-		return -ENODEV;
-		
-	if (!dev->irq) {
-		err("found UHCI device with no IRQ assigned. check BIOS settings!");
-		return -ENODEV;
-	}
-	
-	pci_set_master(dev);
-
-	/* Search for the IO base address.. */
-	for (i = 0; i < 6; i++) {
-
-		unsigned int io_addr = pci_resource_start(dev, i);
-		unsigned int io_size = pci_resource_len(dev, i);
-		if (!(pci_resource_flags(dev,i) & IORESOURCE_IO))
-			continue;
-
-		/* Is it already in use? */
-		if (check_region (io_addr, io_size))
-			break;
-		/* disable legacy emulation */
-		pci_write_config_word (dev, USBLEGSUP, 0);
-	
-		return alloc_uhci(dev, dev->irq, io_addr, io_size);
-	}
-	return -ENODEV;
-}
-
-/*-------------------------------------------------------------------------*/
-
-static const struct pci_device_id __devinitdata uhci_pci_ids [] = { {
-
-	/* handle any USB UHCI controller */
-	class: 		((PCI_CLASS_SERIAL_USB << 8) | 0x00),
-	class_mask: 	~0,
-
-	/* no matter who makes it */
-	vendor:		PCI_ANY_ID,
-	device:		PCI_ANY_ID,
-	subvendor:	PCI_ANY_ID,
-	subdevice:	PCI_ANY_ID,
-
-	}, { /* end: all zeroes */ }
-};
-
-MODULE_DEVICE_TABLE (pci, uhci_pci_ids);
-
-static struct pci_driver uhci_pci_driver = {
-	name:		"usb-uhci",
-	id_table:	&uhci_pci_ids [0],
-
-	probe:		uhci_pci_probe,
-	remove:		uhci_pci_remove,
-
-#ifdef	CONFIG_PM
-	suspend:	uhci_pci_suspend,
-	resume:		uhci_pci_resume,
-#endif	/* PM */
-
-};
-
-/*-------------------------------------------------------------------------*/
-
-static int __init uhci_hcd_init (void) 
-{
-	int retval;
-
-#ifdef DEBUG_SLAB
-	urb_priv_kmem = kmem_cache_create("urb_priv", sizeof(urb_priv_t), 0, SLAB_HWCACHE_ALIGN, NULL, NULL);
-	
-	if(!urb_priv_kmem) {
-		err("kmem_cache_create for urb_priv_t failed (out of memory)");
-		return -ENOMEM;
-	}
-#endif	
-	info(VERSTR);
-
-#ifdef CONFIG_USB_UHCI_HIGH_BANDWIDTH
-	info("High bandwidth mode enabled");	
-#endif
-
-	retval = pci_module_init (&uhci_pci_driver);
-
-#ifdef DEBUG_SLAB
-	if (retval < 0 ) {
-		if (kmem_cache_destroy(urb_priv_kmem))
-			err("urb_priv_kmem remained");
-	}
-#endif
-	
-	info(DRIVER_VERSION ":" DRIVER_DESC);
-
-	return retval;
-}
-
-static void __exit uhci_hcd_cleanup (void) 
-{      
-	pci_unregister_driver (&uhci_pci_driver);
-	
-#ifdef DEBUG_SLAB
-	if(kmem_cache_destroy(urb_priv_kmem))
-		err("urb_priv_kmem remained");
-#endif
-}
-
-module_init (uhci_hcd_init);
-module_exit (uhci_hcd_cleanup);
-
-
-MODULE_AUTHOR( DRIVER_AUTHOR );
-MODULE_DESCRIPTION( DRIVER_DESC );
-MODULE_LICENSE("GPL");
diff -urN linux-2.4.21-pre5/drivers/usb/usb-uhci.h linux-2.4.21-pre6/drivers/usb/usb-uhci.h
--- linux-2.4.21-pre5/drivers/usb/usb-uhci.h	Thu Nov 28 15:53:15 2002
+++ linux-2.4.21-pre6/drivers/usb/usb-uhci.h	Wed Dec 31 16:00:00 1969
@@ -1,308 +0,0 @@
-#ifndef __LINUX_UHCI_H
-#define __LINUX_UHCI_H
-
-/*
-   $Id: usb-uhci.h,v 1.58 2001/08/28 16:45:00 acher Exp $
- */
-#define MODNAME "usb-uhci"
-#define UHCI_LATENCY_TIMER 0
-
-static __inline__ void uhci_wait_ms(unsigned int ms)
-{
-	if(!in_interrupt())
-	{
-		set_current_state(TASK_UNINTERRUPTIBLE);
-		schedule_timeout(1 + ms * HZ / 1000);
-	}
-	else
-		mdelay(ms);
-}
-
-/* Command register */
-#define USBCMD		0
-#define   USBCMD_RS		0x0001	/* Run/Stop */
-#define   USBCMD_HCRESET	0x0002	/* Host reset */
-#define   USBCMD_GRESET		0x0004	/* Global reset */
-#define   USBCMD_EGSM		0x0008	/* Global Suspend Mode */
-#define   USBCMD_FGR		0x0010	/* Force Global Resume */
-#define   USBCMD_SWDBG		0x0020	/* SW Debug mode */
-#define   USBCMD_CF		0x0040	/* Config Flag (sw only) */
-#define   USBCMD_MAXP		0x0080	/* Max Packet (0 = 32, 1 = 64) */
-
-/* Status register */
-#define USBSTS		2
-#define   USBSTS_USBINT		0x0001	/* Interrupt due to IOC */
-#define   USBSTS_ERROR		0x0002	/* Interrupt due to error */
-#define   USBSTS_RD		0x0004	/* Resume Detect */
-#define   USBSTS_HSE		0x0008	/* Host System Error - basically PCI problems */
-#define   USBSTS_HCPE		0x0010	/* Host Controller Process Error - the scripts were buggy */
-#define   USBSTS_HCH		0x0020	/* HC Halted */
-
-/* Interrupt enable register */
-#define USBINTR		4
-#define   USBINTR_TIMEOUT	0x0001	/* Timeout/CRC error enable */
-#define   USBINTR_RESUME	0x0002	/* Resume interrupt enable */
-#define   USBINTR_IOC		0x0004	/* Interrupt On Complete enable */
-#define   USBINTR_SP		0x0008	/* Short packet interrupt enable */
-
-#define USBFRNUM	6
-#define USBFLBASEADD	8
-#define USBSOF		12
-
-/* USB port status and control registers */
-#define USBPORTSC1	16
-#define USBPORTSC2	18
-#define   USBPORTSC_CCS		0x0001	/* Current Connect Status ("device present") */
-#define   USBPORTSC_CSC		0x0002	/* Connect Status Change */
-#define   USBPORTSC_PE		0x0004	/* Port Enable */
-#define   USBPORTSC_PEC		0x0008	/* Port Enable Change */
-#define   USBPORTSC_LS		0x0030	/* Line Status */
-#define   USBPORTSC_RD		0x0040	/* Resume Detect */
-#define   USBPORTSC_LSDA	0x0100	/* Low Speed Device Attached */
-#define   USBPORTSC_PR		0x0200	/* Port Reset */
-#define   USBPORTSC_SUSP	0x1000	/* Suspend */
-
-/* Legacy support register */
-#define USBLEGSUP 0xc0
-#define USBLEGSUP_DEFAULT 0x2000	/* only PIRQ enable set */
-
-#define UHCI_NULL_DATA_SIZE	0x7ff	/* for UHCI controller TD */
-#define UHCI_PID 		0xff	/* PID MASK */
-
-#define UHCI_PTR_BITS		0x000F
-#define UHCI_PTR_TERM		0x0001
-#define UHCI_PTR_QH		0x0002
-#define UHCI_PTR_DEPTH		0x0004
-
-#define UHCI_NUMFRAMES		1024	/* in the frame list [array] */
-#define UHCI_MAX_SOF_NUMBER	2047	/* in an SOF packet */
-#define CAN_SCHEDULE_FRAMES	1000	/* how far future frames can be scheduled */
-
-/*
- * for TD <status>:
- */
-#define TD_CTRL_SPD		(1 << 29)	/* Short Packet Detect */
-#define TD_CTRL_C_ERR_MASK	(3 << 27)	/* Error Counter bits */
-#define TD_CTRL_LS		(1 << 26)	/* Low Speed Device */
-#define TD_CTRL_IOS		(1 << 25)	/* Isochronous Select */
-#define TD_CTRL_IOC		(1 << 24)	/* Interrupt on Complete */
-#define TD_CTRL_ACTIVE		(1 << 23)	/* TD Active */
-#define TD_CTRL_STALLED		(1 << 22)	/* TD Stalled */
-#define TD_CTRL_DBUFERR		(1 << 21)	/* Data Buffer Error */
-#define TD_CTRL_BABBLE		(1 << 20)	/* Babble Detected */
-#define TD_CTRL_NAK		(1 << 19)	/* NAK Received */
-#define TD_CTRL_CRCTIMEO	(1 << 18)	/* CRC/Time Out Error */
-#define TD_CTRL_BITSTUFF	(1 << 17)	/* Bit Stuff Error */
-#define TD_CTRL_ACTLEN_MASK	0x7ff	/* actual length, encoded as n - 1 */
-
-#define TD_CTRL_ANY_ERROR	(TD_CTRL_STALLED | TD_CTRL_DBUFERR | \
-				 TD_CTRL_BABBLE | TD_CTRL_CRCTIME | TD_CTRL_BITSTUFF)
-
-#define uhci_status_bits(ctrl_sts)	(ctrl_sts & 0xFE0000)
-#define uhci_actual_length(ctrl_sts)	((ctrl_sts + 1) & TD_CTRL_ACTLEN_MASK)	/* 1-based */
-#define uhci_ptr_to_virt(x)	bus_to_virt(x & ~UHCI_PTR_BITS)
-
-/*
- * for TD <flags>:
- */
-#define UHCI_TD_REMOVE		0x0001	/* Remove when done */
-
-/*
- * for TD <info>: (a.k.a. Token)
- */
-#define TD_TOKEN_TOGGLE		19
-
-#define uhci_maxlen(token)	((token) >> 21)
-#define uhci_toggle(token)	(((token) >> TD_TOKEN_TOGGLE) & 1)
-#define uhci_endpoint(token)	(((token) >> 15) & 0xf)
-#define uhci_devaddr(token)	(((token) >> 8) & 0x7f)
-#define uhci_devep(token)	(((token) >> 8) & 0x7ff)
-#define uhci_packetid(token)	((token) & 0xff)
-#define uhci_packetout(token)	(uhci_packetid(token) != USB_PID_IN)
-#define uhci_packetin(token)	(uhci_packetid(token) == USB_PID_IN)
-
-/* ------------------------------------------------------------------------------------
-   New TD/QH-structures
-   ------------------------------------------------------------------------------------ */
-typedef enum {
-	TD_TYPE, QH_TYPE
-} uhci_desc_type_t;
-
-typedef struct {
-	__u32 link;
-	__u32 status;
-	__u32 info;
-	__u32 buffer;
-} uhci_td_t, *puhci_td_t;
-
-typedef struct {
-	__u32 head;
-	__u32 element;		/* Queue element pointer */
-} uhci_qh_t, *puhci_qh_t;
-
-typedef struct {
-	union {
-		uhci_td_t td;
-		uhci_qh_t qh;
-	} hw;
-	uhci_desc_type_t type;
-	dma_addr_t dma_addr;
-	struct list_head horizontal;
-	struct list_head vertical;
-	struct list_head desc_list;
-	int last_used;
-} uhci_desc_t, *puhci_desc_t;
-
-typedef struct {
-	struct list_head desc_list;	// list pointer to all corresponding TDs/QHs associated with this request
-	dma_addr_t setup_packet_dma;
-	dma_addr_t transfer_buffer_dma;
-	unsigned long started;
-	struct urb *next_queued_urb;	// next queued urb for this EP
-	struct urb *prev_queued_urb;
-	uhci_desc_t *bottom_qh;
-	uhci_desc_t *next_qh;       	// next helper QH
-	char use_loop;
-	char flags;
-} urb_priv_t, *purb_priv_t;
-
-struct virt_root_hub {
-	int devnum;		/* Address of Root Hub endpoint */
-	void *urb;
-	void *int_addr;
-	int send;
-	int interval;
-	int numports;
-	int c_p_r[8];
-	struct timer_list rh_int_timer;
-};
-
-typedef struct uhci {
-	int irq;
-	unsigned int io_addr;
-	unsigned int io_size;
-	unsigned int maxports;
-	int running;
-
-	int apm_state;
-
-	struct uhci *next;	// chain of uhci device contexts
-
-	struct list_head urb_list;	// list of all pending urbs
-
-	spinlock_t urb_list_lock;	// lock to keep consistency 
-
-	int unlink_urb_done;
-	atomic_t avoid_bulk;
-	
-	struct usb_bus *bus;	// our bus
-
-	__u32 *framelist;
-	dma_addr_t framelist_dma;
-	uhci_desc_t **iso_td;
-	uhci_desc_t *int_chain[8];
-	uhci_desc_t *ls_control_chain;
-	uhci_desc_t *control_chain;
-	uhci_desc_t *bulk_chain;
-	uhci_desc_t *chain_end;
-	uhci_desc_t *td1ms;
-	uhci_desc_t *td32ms;
-	struct list_head free_desc;
-	spinlock_t qh_lock;
-	spinlock_t td_lock;
-	struct virt_root_hub rh;	//private data of the virtual root hub
-	int loop_usage;            // URBs using bandwidth reclamation
-
-	struct list_head urb_unlinked;	// list of all unlinked  urbs
-	long timeout_check;
-	int timeout_urbs;
-	struct pci_dev *uhci_pci;
-	struct pci_pool *desc_pool;
-	long last_error_time;          // last error output in uhci_interrupt()
-} uhci_t, *puhci_t;
-
-
-#define MAKE_TD_ADDR(a) ((a)->dma_addr&~UHCI_PTR_QH)
-#define MAKE_QH_ADDR(a) ((a)->dma_addr|UHCI_PTR_QH)
-#define UHCI_GET_CURRENT_FRAME(uhci) (inw ((uhci)->io_addr + USBFRNUM))
-
-#define CLEAN_TRANSFER_NO_DELETION 0
-#define CLEAN_TRANSFER_REGULAR 1
-#define CLEAN_TRANSFER_DELETION_MARK 2
-
-#define CLEAN_NOT_FORCED 0
-#define CLEAN_FORCED 1
-
-#define PROCESS_ISO_REGULAR 0
-#define PROCESS_ISO_FORCE 1
-
-#define UNLINK_ASYNC_STORE_URB 0
-#define UNLINK_ASYNC_DONT_STORE 1
-
-#define is_td_active(desc) (desc->hw.td.status & cpu_to_le32(TD_CTRL_ACTIVE))
-
-#define set_qh_head(desc,val) (desc)->hw.qh.head=cpu_to_le32(val)
-#define set_qh_element(desc,val) (desc)->hw.qh.element=cpu_to_le32(val)
-#define set_td_link(desc,val) (desc)->hw.td.link=cpu_to_le32(val)
-#define set_td_ioc(desc) (desc)->hw.td.status |= cpu_to_le32(TD_CTRL_IOC)
-#define clr_td_ioc(desc) (desc)->hw.td.status &= cpu_to_le32(~TD_CTRL_IOC)
-
-
-/* ------------------------------------------------------------------------------------ 
-   Virtual Root HUB 
-   ------------------------------------------------------------------------------------ */
-/* destination of request */
-#define RH_INTERFACE               0x01
-#define RH_ENDPOINT                0x02
-#define RH_OTHER                   0x03
-
-#define RH_CLASS                   0x20
-#define RH_VENDOR                  0x40
-
-/* 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
-/* Our Vendor Specific Request */
-#define RH_SET_EP               0x2000
-
-
-/* Hub port features */
-#define RH_PORT_CONNECTION         0x00
-#define RH_PORT_ENABLE             0x01
-#define RH_PORT_SUSPEND            0x02
-#define RH_PORT_OVER_CURRENT       0x03
-#define RH_PORT_RESET              0x04
-#define RH_PORT_POWER              0x08
-#define RH_PORT_LOW_SPEED          0x09
-#define RH_C_PORT_CONNECTION       0x10
-#define RH_C_PORT_ENABLE           0x11
-#define RH_C_PORT_SUSPEND          0x12
-#define RH_C_PORT_OVER_CURRENT     0x13
-#define RH_C_PORT_RESET            0x14
-
-/* Hub features */
-#define RH_C_HUB_LOCAL_POWER       0x00
-#define RH_C_HUB_OVER_CURRENT      0x01
-
-#define RH_DEVICE_REMOTE_WAKEUP    0x00
-#define RH_ENDPOINT_STALL          0x01
-
-/* Our Vendor Specific feature */
-#define RH_REMOVE_EP               0x00
-
-
-#define RH_ACK                     0x01
-#define RH_REQ_ERR                 -1
-#define RH_NACK                    0x00
-
-#endif
diff -urN linux-2.4.21-pre5/drivers/usb/usb.c linux-2.4.21-pre6/drivers/usb/usb.c
--- linux-2.4.21-pre5/drivers/usb/usb.c	Wed Mar 26 16:11:29 2003
+++ linux-2.4.21-pre6/drivers/usb/usb.c	Wed Mar 26 16:11:39 2003
@@ -782,7 +782,7 @@
  * cases, we know no other thread can recycle our address, since we must
  * already have been serialized enough to prevent that.
  */
-static void call_policy (char *verb, struct usb_device *dev)
+static void call_policy_interface (char *verb, struct usb_device *dev, int interface)
 {
 	char *argv [3], **envp, *buf, *scratch;
 	int i = 0, value;
@@ -861,20 +861,14 @@
 			    dev->descriptor.bDeviceSubClass,
 			    dev->descriptor.bDeviceProtocol) + 1;
 	if (dev->descriptor.bDeviceClass == 0) {
-		int alt = dev->actconfig->interface [0].act_altsetting;
+		int alt = dev->actconfig->interface [interface].act_altsetting;
 
-		/* a simple/common case: one config, one interface, one driver
-		 * with current altsetting being a reasonable setting.
-		 * everything needs a smart agent and usbdevfs; or can rely on
-		 * device-specific binding policies.
-		 */
 		envp [i++] = scratch;
 		scratch += sprintf (scratch, "INTERFACE=%d/%d/%d",
-			dev->actconfig->interface [0].altsetting [alt].bInterfaceClass,
-			dev->actconfig->interface [0].altsetting [alt].bInterfaceSubClass,
-			dev->actconfig->interface [0].altsetting [alt].bInterfaceProtocol)
+			dev->actconfig->interface [interface].altsetting [alt].bInterfaceClass,
+			dev->actconfig->interface [interface].altsetting [alt].bInterfaceSubClass,
+			dev->actconfig->interface [interface].altsetting [alt].bInterfaceProtocol)
 			+ 1;
-		/* INTERFACE-0, INTERFACE-1, ... ? */
 	}
 	envp [i++] = 0;
 	/* assert: (scratch - buf) < sizeof buf */
@@ -889,6 +883,14 @@
 		dbg ("kusbd policy returned 0x%x", value);
 }
 
+static void call_policy (char *verb, struct usb_device *dev)
+{
+	int i;
+	for (i = 0; i < dev->actconfig->bNumInterfaces; i++) {
+		call_policy_interface (verb, dev, i);
+	}
+}
+
 #else
 
 static inline void
diff -urN linux-2.4.21-pre5/drivers/video/Config.in linux-2.4.21-pre6/drivers/video/Config.in
--- linux-2.4.21-pre5/drivers/video/Config.in	Thu Nov 28 15:53:15 2002
+++ linux-2.4.21-pre6/drivers/video/Config.in	Wed Mar 26 16:11:39 2003
@@ -80,7 +80,7 @@
       bool '  Generic STI frame buffer device support' CONFIG_FB_STI
    fi
    if [ "$CONFIG_MAC" = "y" ]; then
-      define_bool CONFIG_FB_MAC y
+      bool '  Generic Macintosh display support' CONFIG_FB_MAC
       bool '  Apple "valkyrie" display support' CONFIG_FB_VALKYRIE
 #      bool '  Apple DAFB display support' CONFIG_FB_DAFB
    fi
@@ -124,14 +124,20 @@
 	 if [ "$CONFIG_FB_MATROX" != "n" ]; then
 	    bool '    Millennium I/II support' CONFIG_FB_MATROX_MILLENIUM
 	    bool '    Mystique support' CONFIG_FB_MATROX_MYSTIQUE
-	    bool '    G100/G200/G400/G450/G550 support' CONFIG_FB_MATROX_G100
+ 	    bool '    G100/G200/G400/G450/G550 support' CONFIG_FB_MATROX_G450
+ 	    if [ "$CONFIG_FB_MATROX_G450" = "n" ]; then
+ 	       bool '    G100/G200/G400 support' CONFIG_FB_MATROX_G100A
+ 	    fi
+ 	    if [ "$CONFIG_FB_MATROX_G450" = "y" -o "$CONFIG_FB_MATROX_G100A" = "y" ]; then
+ 	       define_bool CONFIG_FB_MATROX_G100 y
+ 	    fi
             if [ "$CONFIG_I2C" != "n" ]; then
 	       dep_tristate '      Matrox I2C support' CONFIG_FB_MATROX_I2C $CONFIG_FB_MATROX $CONFIG_I2C_ALGOBIT
 	       if [ "$CONFIG_FB_MATROX_G100" = "y" ]; then
 	          dep_tristate '      G400 second head support' CONFIG_FB_MATROX_MAVEN $CONFIG_FB_MATROX_I2C
 	       fi
             fi
-            dep_tristate '      G450/G550 second head support (mandatory for G550)' CONFIG_FB_MATROX_G450 $CONFIG_FB_MATROX_G100
+            dep_tristate '    Matrox /proc interface' CONFIG_FB_MATROX_PROC $CONFIG_FB_MATROX
 	    bool '    Multihead support' CONFIG_FB_MATROX_MULTIHEAD
 	 fi
 	 tristate '  ATI Mach64 display support (EXPERIMENTAL)' CONFIG_FB_ATY
diff -urN linux-2.4.21-pre5/drivers/video/Makefile linux-2.4.21-pre6/drivers/video/Makefile
--- linux-2.4.21-pre5/drivers/video/Makefile	Thu Nov 28 15:53:15 2002
+++ linux-2.4.21-pre6/drivers/video/Makefile	Wed Mar 26 16:11:39 2003
@@ -102,6 +102,11 @@
 obj-y				  += matrox/matrox.o
 endif
 
+subdir-$(CONFIG_FB_INTEL)	  += intel
+ifeq ($(CONFIG_FB_INTEL),y)
+obj-y				  += intel/intelfb.o
+endif
+
 subdir-$(CONFIG_FB_RIVA)	  += riva
 ifeq ($(CONFIG_FB_RIVA),y)
 obj-y				  += riva/rivafb.o
diff -urN linux-2.4.21-pre5/drivers/video/S3triofb.c linux-2.4.21-pre6/drivers/video/S3triofb.c
--- linux-2.4.21-pre5/drivers/video/S3triofb.c	Thu Sep 13 16:04:43 2001
+++ linux-2.4.21-pre6/drivers/video/S3triofb.c	Wed Mar 26 16:11:39 2003
@@ -17,8 +17,7 @@
  */
 
 /*
-	Bugs : + OF dependencies should be removed.
-               + This driver should be merged with the CyberVision driver. The
+	Bugs : + This driver should be merged with the CyberVision driver. The
                  CyberVision is a Zorro III implementation of the S3Trio64 chip.
 
 */
@@ -37,9 +36,6 @@
 #include <linux/fb.h>
 #include <linux/init.h>
 #include <linux/selection.h>
-#include <asm/io.h>
-#include <asm/prom.h>
-#include <asm/pci-bridge.h>
 #include <linux/pci.h>
 #ifdef CONFIG_FB_COMPAT_XPMAC
 #include <asm/vc_ioctl.h>
@@ -60,7 +56,9 @@
 
 #define IO_OUT16VAL(v, r)       (((v) << 8) | (r))
 
+
 static int currcon = 0;
+static int disabled;
 static struct display disp;
 static struct fb_info fb_info;
 static struct { u_char red, green, blue, pad; } palette[256];
@@ -68,14 +66,20 @@
 static char *s3trio_base;
 
 static struct fb_fix_screeninfo fb_fix;
-static struct fb_var_screeninfo fb_var = { 0, };
-
+static struct fb_var_screeninfo S3triofb_default_var = {
+	640, 480, 640, 480, 0, 0, 8, 0,
+	{0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
+	0, 0, -1, FB_ACCELF_TEXT, 39722, 40, 24, 32, 11, 96, 2,
+	FB_SYNC_COMP_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT,
+	FB_VMODE_NONINTERLACED
+};
+static struct fb_var_screeninfo fb_var = { 0,};
 
     /*
      *  Interface used by the world
      */
 
-static void __init s3triofb_of_init(struct device_node *dp);
+static void __init s3triofb_pci_init(struct pci_dev *dp);
 static int s3trio_get_fix(struct fb_fix_screeninfo *fix, int con,
 			  struct fb_info *info);
 static int s3trio_get_var(struct fb_var_screeninfo *var, int con,
@@ -259,15 +263,42 @@
     return 0;
 }
 
+int __init s3triofb_setup(char *options) {
+	char *this_opt;
+
+	if (!options || !*options)
+		return 0;
+
+	while ((this_opt = strsep(&options, ",")) != NULL) {
+		if (!*this_opt)
+			continue;
+
+		if (!strcmp(this_opt, "disabled"))
+			disabled = 1;
+	}
+	return 0;
+}
 
 int __init s3triofb_init(void)
 {
-	struct device_node *dp;
+	struct pci_dev *dp = NULL;
 
-	dp = find_devices("S3Trio");
-	if (dp != 0)
-	    s3triofb_of_init(dp);
-	return 0;
+	if (disabled)
+		return -ENXIO;
+
+	dp = pci_find_device(PCI_VENDOR_ID_S3, PCI_DEVICE_ID_S3_TRIO, dp);
+	if ((dp != 0) && ((dp->class >> 16) == PCI_BASE_CLASS_DISPLAY)) {
+		s3triofb_pci_init(dp);
+		return 0;
+	} else
+		return -ENODEV;
+}
+
+static void __exit s3triofb_exit(void)
+{
+    unregister_framebuffer(&fb_info);
+    iounmap(s3trio_base);
+    /* XXX unshare VGA regions */
 }
 
 void __init s3trio_resetaccel(void){
@@ -313,111 +344,69 @@
 	outw( MF_PIX_CONTROL | MFA_SRC_FOREGR_MIX,  0xbee8);
 }
 
-int __init s3trio_init(struct device_node *dp){
-
-    u_char bus, dev;
-    unsigned int t32;
-    unsigned short cmd;
-
-	pci_device_loc(dp,&bus,&dev);
-                pcibios_read_config_dword(bus, dev, PCI_VENDOR_ID, &t32);
-                if(t32 == (PCI_DEVICE_ID_S3_TRIO << 16) + PCI_VENDOR_ID_S3) {
-                        pcibios_read_config_dword(bus, dev, PCI_BASE_ADDRESS_0, &t32);
-                        pcibios_read_config_dword(bus, dev, PCI_BASE_ADDRESS_1, &t32);
-			pcibios_read_config_word(bus, dev, PCI_COMMAND,&cmd);
-
-			pcibios_write_config_word(bus, dev, PCI_COMMAND, PCI_COMMAND_IO | PCI_COMMAND_MEMORY);
-
-			pcibios_write_config_dword(bus, dev, PCI_BASE_ADDRESS_0,0xffffffff);
-                        pcibios_read_config_dword(bus, dev, PCI_BASE_ADDRESS_0, &t32);
-
-/* This is a gross hack as OF only maps enough memory for the framebuffer and
-   we want to use MMIO too. We should find out which chunk of address space
-   we can use here */
-			pcibios_write_config_dword(bus,dev,PCI_BASE_ADDRESS_0,0xc6000000);
-
-			/* unlock s3 */
-
-			outb(0x01, 0x3C3);
-
-			outb(inb(0x03CC) | 1, 0x3c2);
-
-			outw(IO_OUT16VAL(0x48, 0x38),0x03D4);
-			outw(IO_OUT16VAL(0xA0, 0x39),0x03D4);
-			outb(0x33,0x3d4);
-			outw(IO_OUT16VAL((inb(0x3d5) & ~(0x2 | 0x10 |  0x40)) |
-					  0x20, 0x33), 0x3d4);
-
-			outw(IO_OUT16VAL(0x6, 0x8), 0x3c4);
-
-			/* switch to MMIO only mode */
-
-			outb(0x58, 0x3d4);
-			outw(IO_OUT16VAL(inb(0x3d5) | 3 | 0x10, 0x58), 0x3d4);
-			outw(IO_OUT16VAL(8, 0x53), 0x3d4);
-
-			/* switch off I/O accesses */
-
-#if 0
-			pcibios_write_config_word(bus, dev, PCI_COMMAND,
-				        PCI_COMMAND_IO | PCI_COMMAND_MEMORY);
-#endif
-			return 1;
-                }
+int __init s3trio_init(struct pci_dev *dp)
+{
+	/* unlock s3 */
+	outb(0x01, 0x3C3);
+	outb(inb(0x03CC) | 1, 0x3c2);
+	outw(IO_OUT16VAL(0x48, 0x38),0x03D4);
+	outw(IO_OUT16VAL(0xA0, 0x39),0x03D4);
+	outb(0x33,0x3d4);
+	outw(IO_OUT16VAL((inb(0x3d5) & ~(0x2 | 0x10 |  0x40)) |
+			  0x20, 0x33), 0x3d4);
+
+	outw(IO_OUT16VAL(0x6, 0x8), 0x3c4);
+	printk("S3trio: unlocked s3\n");
+
+	/* switch to MMIO only mode */
+	outb(0x58, 0x3d4);
+	outw(IO_OUT16VAL(inb(0x3d5) | 3 | 0x10, 0x58), 0x3d4);
+	outw(IO_OUT16VAL(8, 0x53), 0x3d4);
+	printk("S3trio: switched to mmio only mode\n");
 
-	return 0;
+	return 1;
 }
 
 
     /*
      *  Initialisation
-     *  We heavily rely on OF for the moment. This needs fixing.
      */
 
-static void __init s3triofb_of_init(struct device_node *dp)
+static void __init s3triofb_pci_init(struct pci_dev *dp)
 {
-    int i, *pp, len;
+    int i;
     unsigned long address, size;
     u_long *CursorBase;
+    u16 cmd;
 
-    strncat(s3trio_name, dp->name, sizeof(s3trio_name));
-    s3trio_name[sizeof(s3trio_name)-1] = '\0';
     strcpy(fb_fix.id, s3trio_name);
 
-    if((pp = (int *)get_property(dp, "vendor-id", &len)) != NULL
-	&& *pp!=PCI_VENDOR_ID_S3) {
-	printk("%s: can't find S3 Trio board\n", dp->full_name);
-	return;
-    }
+    fb_var = S3triofb_default_var;
+    fb_fix.line_length = fb_var.xres_virtual;
+    fb_fix.smem_len = fb_fix.line_length*fb_var.yres;
 
-    if((pp = (int *)get_property(dp, "device-id", &len)) != NULL
-	&& *pp!=PCI_DEVICE_ID_S3_TRIO) {
-	printk("%s: can't find S3 Trio board\n", dp->full_name);
-	return;
+    /* This driver cannot cope if the firmware has not initialised the
+     * device.  So, if the device isn't enabled we simply return
+     */
+    pci_read_config_word(dp, PCI_COMMAND, &cmd);
+    if (!(cmd & PCI_COMMAND_MEMORY)) {
+	    printk(KERN_NOTICE "S3trio: card was not initialised by firmware\n");
+	    return;
     }
 
-    if ((pp = (int *)get_property(dp, "depth", &len)) != NULL
-	&& len == sizeof(int) && *pp != 8) {
-	printk("%s: can't use depth = %d\n", dp->full_name, *pp);
-	return;
+    /* Enable it anyway */
+    if (pci_enable_device(dp)) {
+	    printk(KERN_ERR "S3trio: failed to enable PCI device\n");
+	    return;
     }
-    if ((pp = (int *)get_property(dp, "width", &len)) != NULL
-	&& len == sizeof(int))
-	fb_var.xres = fb_var.xres_virtual = *pp;
-    if ((pp = (int *)get_property(dp, "height", &len)) != NULL
-	&& len == sizeof(int))
-	fb_var.yres = fb_var.yres_virtual = *pp;
-    if ((pp = (int *)get_property(dp, "linebytes", &len)) != NULL
-	&& len == sizeof(int))
-	fb_fix.line_length = *pp;
-    else
-	fb_fix.line_length = fb_var.xres_virtual;
-    fb_fix.smem_len = fb_fix.line_length*fb_var.yres;
 
-    address = 0xc6000000;
-    size = 64*1024*1024;
-    if (!request_mem_region(address, size, "S3triofb"))
+    /* There is only one memory region and it covers the mmio and fb areas */
+    address = pci_resource_start(dp, 0);
+    size    = pci_resource_len(dp, 0); /* size = 64*1024*1024; */
+    if (!request_mem_region(address, size, "S3triofb")) {
+	printk("S3trio: failed to allocate memory region\n");
 	return;
+    }
 
     s3trio_init(dp);
     s3trio_base = ioremap(address, size);
@@ -552,8 +541,7 @@
 #endif
     disp.scrollmode = fb_var.accel_flags & FB_ACCELF_TEXT ? 0 : SCROLL_YREDRAW;
 
-    strcpy(fb_info.modename, "Trio64 ");
-    strncat(fb_info.modename, dp->full_name, sizeof(fb_info.modename));
+    strcpy(fb_info.modename, "Trio64");
     fb_info.node = -1;
     fb_info.fbops = &s3trio_ops;
 #if 0
@@ -591,7 +579,7 @@
 	return;
 
     printk("fb%d: S3 Trio frame buffer device on %s\n",
-	   GET_FB_IDX(fb_info.node), dp->full_name);
+	   GET_FB_IDX(fb_info.node), dp->name);
 }
 
 
@@ -859,4 +847,8 @@
 };
 #endif
 
+#ifdef MODULE
+module_init(s3triofb_init);
 MODULE_LICENSE("GPL");
+#endif
+module_exit(s3triofb_exit);
diff -urN linux-2.4.21-pre5/drivers/video/amifb.c linux-2.4.21-pre6/drivers/video/amifb.c
--- linux-2.4.21-pre5/drivers/video/amifb.c	Fri Aug  2 17:39:45 2002
+++ linux-2.4.21-pre6/drivers/video/amifb.c	Wed Mar 26 16:11:39 2003
@@ -1785,7 +1785,7 @@
 
 	ami_init_copper();
 
-	if (request_irq(IRQ_AMIGA_VERTB, amifb_interrupt, 0,
+	if (request_irq(IRQ_AMIGA_COPPER, amifb_interrupt, 0,
 	                "fb vertb handler", &currentpar)) {
 		err = -EBUSY;
 		goto amifb_error;
diff -urN linux-2.4.21-pre5/drivers/video/atafb.c linux-2.4.21-pre6/drivers/video/atafb.c
--- linux-2.4.21-pre5/drivers/video/atafb.c	Thu Oct 25 00:02:26 2001
+++ linux-2.4.21-pre6/drivers/video/atafb.c	Wed Mar 26 16:11:39 2003
@@ -1193,7 +1193,7 @@
 	par->HBB = gend2 - par->HHT - 2;
 #if 0
 	/* One more Videl constraint: data fetch of two lines must not overlap */
-	if (par->HDB & 0x200  &&  par->HDB & ~0x200 - par->HDE <= 5) {
+	if ((par->HDB & 0x200)  &&  (par->HDB & ~0x200) - par->HDE <= 5) {
 		/* if this happens increase margins, decrease hfreq. */
 	}
 #endif
diff -urN linux-2.4.21-pre5/drivers/video/aty128fb.c linux-2.4.21-pre6/drivers/video/aty128fb.c
--- linux-2.4.21-pre5/drivers/video/aty128fb.c	Thu Nov 28 15:53:15 2002
+++ linux-2.4.21-pre6/drivers/video/aty128fb.c	Wed Mar 26 16:11:39 2003
@@ -2531,7 +2531,7 @@
 	reg |= LVDS_BL_MOD_EN | LVDS_BLON;
 	if (on && level > BACKLIGHT_OFF) {
 		reg |= LVDS_DIGION;
-		if (!reg & LVDS_ON) {
+		if ((reg & LVDS_ON) == 0) {
 			reg &= ~LVDS_BLON;
 			aty_st_le32(LVDS_GEN_CNTL, reg);
 			(void)aty_ld_le32(LVDS_GEN_CNTL);
diff -urN linux-2.4.21-pre5/drivers/video/dnfb.c linux-2.4.21-pre6/drivers/video/dnfb.c
--- linux-2.4.21-pre5/drivers/video/dnfb.c	Thu Nov 28 15:53:15 2002
+++ linux-2.4.21-pre6/drivers/video/dnfb.c	Wed Mar 26 16:11:39 2003
@@ -107,12 +107,6 @@
 
 #define SWAP(A) ((A>>8) | ((A&0xff) <<8))
 
-#if 0
-#define outb(a,d) *(char *)(a)=(d)
-#define outw(a,d) *(unsigned short *)a=d
-#endif
-
-
 /* frame buffer operations */
 
 static int dn_fb_get_fix(struct fb_fix_screeninfo *fix, int con, 
@@ -322,12 +316,12 @@
  
 	/* now we have registered we can safely setup the hardware */
 
-        outb(RESET_CREG,  AP_CONTROL_3A);
-        outw(0x0,  AP_WRITE_ENABLE);
-        outb(NORMAL_MODE, AP_CONTROL_0); 
-        outb((AD_BLT | DST_EQ_SRC | NORM_CREG1),  AP_CONTROL_1);
-        outb(S_DATA_PLN,  AP_CONTROL_2);
-        outw(SWAP(0x3), AP_ROP_1);
+        out_8(AP_CONTROL_3A, RESET_CREG);
+        out_be16(AP_WRITE_ENABLE, 0x0);
+        out_8(AP_CONTROL_0, NORMAL_MODE); 
+        out_8(AP_CONTROL_1, (AD_BLT | DST_EQ_SRC | NORM_CREG1));
+        out_8(AP_CONTROL_2, S_DATA_PLN);
+        out_be16(AP_ROP_1, SWAP(0x3));
 
         printk("apollo frame buffer alive and kicking !\n");
 
@@ -354,10 +348,10 @@
 static void dnfbcon_blank(int blank,  struct fb_info *info) {
 
 	if(blank)  {
-        	outb(0x0,  AP_CONTROL_3A);
+		out_8(AP_CONTROL_3A, 0x0);
 	}
 	else {
-	        outb(0x1,  AP_CONTROL_3A);
+	        out_8(AP_CONTROL_3A, 0x1);
 	}
 
 	return ;
@@ -383,7 +377,8 @@
 		x_word_count=(x_end>>4) - (x_dest >> 4) + 1;
 		start_mask=0xffff0000 >> (x_dest & 0xf);
 		end_mask=0x7ffff >> (x_end & 0xf);
-		outb((((x_dest & 0xf) - (x_src &0xf))  % 16)|(0x4 << 5),AP_CONTROL_0);
+		out_8(AP_CONTROL_0,
+		      (((x_dest & 0xf) - (x_src &0xf))  % 16)|(0x4 << 5));
 		if((x_dest & 0xf) < (x_src & 0xf))
 			pre_read=1;
 	}
@@ -393,26 +388,27 @@
 		x_word_count=(x_dest>>4) - (x_end >> 4) + 1;
 		start_mask=0x7ffff >> (x_dest & 0xf);
 		end_mask=0xffff0000 >> (x_end & 0xf);
-		outb(((-((x_src & 0xf) - (x_dest &0xf))) % 16)|(0x4 << 5),AP_CONTROL_0);
+		out_8(AP_CONTROL_0,
+		      ((-((x_src & 0xf) - (x_dest &0xf))) % 16)|(0x4 << 5));
 		if((x_dest & 0xf) > (x_src & 0xf))
 			pre_read=1;
 	}
 
 	for(i=0;i<y_count;i++) {
 
-		outb(0xc | (dest >> 16), AP_CONTROL_3A);
-			
+		out_8(AP_CONTROL_3A, 0xc | (dest >> 16));
+
 		if(pre_read) {
 			dummy=*src;
 			src+=incr;
 		}
 
 		if(x_word_count) {
-			outb(start_mask,AP_WRITE_ENABLE);
+			out_8(AP_WRITE_ENABLE, start_mask);
 			*src=dest;
 			src+=incr;
 			dest+=incr;
-			outb(0,AP_WRITE_ENABLE);
+			out_8(AP_WRITE_ENABLE, 0);
 
 			for(j=1;j<(x_word_count-1);j++) {
 				*src=dest;
@@ -420,13 +416,13 @@
 				dest+=incr;
 			}
 
-			outb(start_mask,AP_WRITE_ENABLE);
+			out_8(AP_WRITE_ENABLE, start_mask);
 			*src=dest;
 			dest+=incr;
 			src+=incr;
 		}
 		else {
-			outb(start_mask | end_mask, AP_WRITE_ENABLE);
+			out_8(AP_WRITE_ENABLE, start_mask | end_mask);
 			*src=dest;
 			dest+=incr;
 			src+=incr;
@@ -434,7 +430,7 @@
 		src+=(y_delta/16);
 		dest+=(y_delta/16);
 	}
-	outb(NORMAL_MODE,AP_CONTROL_0);
+	out_8(AP_CONTROL_0, NORMAL_MODE);
 }
 
 static void bmove_apollofb(struct display *p, int sy, int sx, int dy, int dx,
diff -urN linux-2.4.21-pre5/drivers/video/intel/Makefile linux-2.4.21-pre6/drivers/video/intel/Makefile
--- linux-2.4.21-pre5/drivers/video/intel/Makefile	Wed Dec 31 16:00:00 1969
+++ linux-2.4.21-pre6/drivers/video/intel/Makefile	Wed Mar 26 16:11:39 2003
@@ -0,0 +1,18 @@
+# $DHD: linux/drivers/video/intel/Makefile,v 1.1.2.2 2003/02/05 00:07:45 dawes Exp $
+# $TG$
+
+O_TARGET := intelfb.o
+
+obj-$(CONFIG_FB_INTEL)	+= intelfbdrv.o intelfbhw.o
+
+include $(TOPDIR)/Rules.make
+
+intelfbdrv.o: builtinmodes.c
+
+builtinmodes.c: modes.c
+	rm -f builtinmodes.c
+	ln -s modes.c builtinmodes.c
+
+clean:
+	rm -f core *.o *.a *.s builtinmodes.c
+
diff -urN linux-2.4.21-pre5/drivers/video/intel/builtinmodes.c linux-2.4.21-pre6/drivers/video/intel/builtinmodes.c
--- linux-2.4.21-pre5/drivers/video/intel/builtinmodes.c	Wed Dec 31 16:00:00 1969
+++ linux-2.4.21-pre6/drivers/video/intel/builtinmodes.c	Wed Mar 26 16:11:39 2003
@@ -0,0 +1,222 @@
+
+/*
+ * THIS FILE IS AUTOMATICALLY GENERATED BY fbmode.pl -- DO NOT EDIT
+ */
+
+static struct fb_videomode modedb[] = {
+    {
+	/* 640x350 @ 85 Hz, 37.9 kHz hsync */
+	"640x350@85", 85, 640, 350, 31746, 96, 32, 60, 32, 64, 3,
+	FB_SYNC_HOR_HIGH_ACT, FB_VMODE_NONINTERLACED
+    },
+    {
+	/* 640x400 @ 85 Hz, 37.9 kHz hsync */
+	"640x400@85", 85, 640, 400, 31746, 96, 32, 41, 1, 64, 3,
+	FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
+    },
+    {
+	/* 720x400 @ 85 Hz, 37.9 kHz hsync */
+	"720x400@85", 85, 720, 400, 28169, 108, 36, 42, 1, 72, 3,
+	FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
+    },
+    {
+	/* 640x480 @ 60 Hz, 31.5 kHz hsync */
+	"640x480@60", 60, 640, 480, 39683, 48, 16, 33, 10, 96, 2,
+	0, FB_VMODE_NONINTERLACED
+    },
+    {
+	/* 640x480 @ 73 Hz, 37.9 kHz hsync */
+	"640x480@73", 73, 640, 480, 31746, 128, 24, 29, 9, 40, 2,
+	0, FB_VMODE_NONINTERLACED
+    },
+    {
+	/* 640x480 @ 75 Hz, 37.5 kHz hsync */
+	"640x480@75", 75, 640, 480, 31746, 120, 16, 16, 1, 64, 3,
+	0, FB_VMODE_NONINTERLACED
+    },
+    {
+	/* 640x480 @ 85 Hz, 43.3 kHz hsync */
+	"640x480@85", 85, 640, 480, 27778, 80, 56, 25, 1, 56, 3,
+	0, FB_VMODE_NONINTERLACED
+    },
+    {
+	/* 800x600 @ 56 Hz, 35.2 kHz hsync */
+	"800x600@56", 56, 800, 600, 27778, 128, 24, 22, 1, 72, 2,
+	FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
+    },
+    {
+	/* 800x600 @ 60 Hz, 37.9 kHz hsync */
+	"800x600@60", 60, 800, 600, 25000, 88, 40, 23, 1, 128, 4,
+	FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
+    },
+    {
+	/* 800x600 @ 72 Hz, 48.1 kHz hsync */
+	"800x600@72", 72, 800, 600, 20000, 64, 56, 23, 37, 120, 6,
+	FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
+    },
+    {
+	/* 800x600 @ 75 Hz, 46.9 kHz hsync */
+	"800x600@75", 75, 800, 600, 20202, 160, 16, 21, 1, 80, 3,
+	FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
+    },
+    {
+	/* 800x600 @ 85 Hz, 53.7 kHz hsync */
+	"800x600@85", 85, 800, 600, 17762, 152, 32, 27, 1, 64, 3,
+	FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
+    },
+    {
+	/* 1024x768 @ 60 Hz, 48.4 kHz hsync */
+	"1024x768@60", 60, 1024, 768, 15385, 160, 24, 29, 3, 136, 6,
+	0, FB_VMODE_NONINTERLACED
+    },
+    {
+	/* 1024x768 @ 70 Hz, 56.5 kHz hsync */
+	"1024x768@70", 70, 1024, 768, 13333, 144, 24, 29, 3, 136, 6,
+	0, FB_VMODE_NONINTERLACED
+    },
+    {
+	/* 1024x768 @ 75 Hz, 60.1 kHz hsync */
+	"1024x768@75", 75, 1024, 768, 12690, 176, 16, 28, 1, 96, 3,
+	FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
+    },
+    {
+	/* 1024x768 @ 85 Hz, 68.7 kHz hsync */
+	"1024x768@85", 85, 1024, 768, 10582, 208, 48, 36, 1, 96, 3,
+	FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
+    },
+    {
+	/* 1152x864 @ 75 Hz, 67.5 kHz hsync */
+	"1152x864@75", 75, 1152, 864, 9259, 256, 64, 32, 1, 128, 3,
+	FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
+    },
+    {
+	/* 1280x960 @ 60 Hz, 60.0 kHz hsync */
+	"1280x960@60", 60, 1280, 960, 9259, 312, 96, 36, 1, 112, 3,
+	FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
+    },
+    {
+	/* 1280x960 @ 85 Hz, 85.9 kHz hsync */
+	"1280x960@85", 85, 1280, 960, 6734, 224, 64, 47, 1, 160, 3,
+	FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
+    },
+    {
+	/* 1280x1024 @ 60 Hz, 64.0 kHz hsync */
+	"1280x1024@60", 60, 1280, 1024, 9259, 248, 48, 38, 1, 112, 3,
+	FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
+    },
+    {
+	/* 1280x1024 @ 75 Hz, 80.0 kHz hsync */
+	"1280x1024@75", 75, 1280, 1024, 7407, 248, 16, 38, 1, 144, 3,
+	FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
+    },
+    {
+	/* 1280x1024 @ 85 Hz, 91.1 kHz hsync */
+	"1280x1024@85", 85, 1280, 1024, 6349, 224, 64, 44, 1, 160, 3,
+	FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
+    },
+    {
+	/* 1600x1200 @ 60 Hz, 75.0 kHz hsync */
+	"1600x1200@60", 60, 1600, 1200, 6173, 304, 64, 46, 1, 192, 3,
+	FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
+    },
+    {
+	/* 1600x1200 @ 65 Hz, 81.2 kHz hsync */
+	"1600x1200@65", 65, 1600, 1200, 5698, 304, 64, 46, 1, 192, 3,
+	FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
+    },
+    {
+	/* 1600x1200 @ 70 Hz, 87.5 kHz hsync */
+	"1600x1200@70", 70, 1600, 1200, 5291, 304, 64, 46, 1, 192, 3,
+	FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
+    },
+    {
+	/* 1600x1200 @ 75 Hz, 93.8 kHz hsync */
+	"1600x1200@75", 75, 1600, 1200, 4938, 304, 64, 46, 1, 192, 3,
+	FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
+    },
+    {
+	/* 1600x1200 @ 85 Hz, 106.2 kHz hsync */
+	"1600x1200@85", 85, 1600, 1200, 4357, 304, 64, 46, 1, 192, 3,
+	FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
+    },
+    {
+	/* 1792x1344 @ 60 Hz, 83.7 kHz hsync */
+	"1792x1344@60", 60, 1792, 1344, 4883, 328, 128, 46, 1, 200, 3,
+	FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
+    },
+    {
+	/* 1792x1344 @ 75 Hz, 106.3 kHz hsync */
+	"1792x1344@75", 75, 1792, 1344, 3831, 352, 96, 69, 1, 216, 3,
+	FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
+    },
+    {
+	/* 1856x1392 @ 60 Hz, 86.4 kHz hsync */
+	"1856x1392@60", 60, 1856, 1392, 4581, 352, 96, 43, 1, 224, 3,
+	FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
+    },
+    {
+	/* 1856x1392 @ 75 Hz, 112.5 kHz hsync */
+	"1856x1392@75", 75, 1856, 1392, 3472, 352, 128, 104, 1, 224, 3,
+	FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
+    },
+    {
+	/* 1920x1440 @ 60 Hz, 90.0 kHz hsync */
+	"1920x1440@60", 60, 1920, 1440, 4274, 344, 128, 56, 1, 208, 3,
+	FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
+    },
+    {
+	/* 1920x1440 @ 75 Hz, 112.5 kHz hsync */
+	"1920x1440@75", 75, 1920, 1440, 3367, 352, 144, 56, 1, 224, 3,
+	FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
+    },
+    {
+	/* 832x624 @ 75 Hz, 49.7 kHz hsync */
+	"832x624@75", 75, 832, 624, 17457, 224, 32, 39, 1, 64, 3,
+	0, FB_VMODE_NONINTERLACED
+    },
+    {
+	/* 1152x768 @ 55 Hz, 44.2 kHz hsync */
+	"1152x768@55", 55, 1152, 768, 15386, 158, 26, 29, 3, 136, 6,
+	FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
+    },
+    {
+	/* 1400x1050 @ 60 Hz, 64.9 kHz hsync */
+	"1400x1050@60", 60, 1400, 1050, 8197, 240, 88, 18, 2, 152, 12,
+	FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
+    },
+    {
+	/* 1400x1050 @ 75 Hz, 81.5 kHz hsync */
+	"1400x1050@75", 75, 1400, 1050, 6418, 128, 64, 26, 2, 320, 12,
+	FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
+    },
+    {
+	/* 1600x1024 @ 60 Hz, 64.0 kHz hsync */
+	"1600x1024@60", 60, 1600, 1024, 9354, 30, 20, 37, 3, 20, 3,
+	0, FB_VMODE_NONINTERLACED
+    },
+    {
+	/* 1920x1440 @ 85 Hz, 128.5 kHz hsync */
+	"1920x1440@85", 85, 1920, 1440, 2930, 368, 152, 68, 1, 216, 3,
+	FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
+    },
+    {
+	/* 2048x1536 @ 60 Hz, 95.3 kHz hsync */
+	"2048x1536@60", 60, 2048, 1536, 3746, 376, 152, 49, 1, 224, 3,
+	FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
+    },
+    {
+	/* 2048x1536 @ 75 Hz, 120.2 kHz hsync */
+	"2048x1536@75", 75, 2048, 1536, 2937, 392, 168, 63, 1, 224, 3,
+	FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
+    },
+    {
+	/* 2048x1536 @ 85 Hz, 137.0 kHz hsync */
+	"2048x1536@85", 85, 2048, 1536, 2577, 392, 168, 72, 1, 224, 3,
+	FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
+    }
+};
+
+static int num_modes = sizeof(modedb) / sizeof(modedb[0]);
+
+#define DFLT_MODE 3
+
diff -urN linux-2.4.21-pre5/drivers/video/intel/intelfb.h linux-2.4.21-pre6/drivers/video/intel/intelfb.h
--- linux-2.4.21-pre5/drivers/video/intel/intelfb.h	Wed Dec 31 16:00:00 1969
+++ linux-2.4.21-pre6/drivers/video/intel/intelfb.h	Wed Mar 26 16:11:39 2003
@@ -0,0 +1,385 @@
+#ifndef _INTELFB_H
+#define _INTELFB_H
+
+/* $DHD: intelfb/intelfb.h,v 1.38 2003/02/06 17:29:41 dawes Exp $ */
+/* $TG$ */
+
+
+/*** Version/name ***/
+#define INTELFB_VERSION			"0.7.5"
+#define INTELFB_MODULE_NAME		"intelfb"
+#define SUPPORTED_CHIPSETS		"830M/845G/852GM/855GM/865G"
+
+
+/*** Debug/feature defines ***/
+
+#ifndef DEBUG
+#define DEBUG				0
+#endif
+
+#ifndef VERBOSE
+#define VERBOSE				1
+#endif
+
+#ifndef REGDUMP
+#define REGDUMP				0
+#endif
+
+#ifndef DETECT_VGA_CLASS_ONLY
+#define DETECT_VGA_CLASS_ONLY		1
+#endif
+
+#ifndef FIXED_MODE
+#define FIXED_MODE			0
+#endif
+
+#ifndef ALLOCATE_FOR_PANNING
+#define ALLOCATE_FOR_PANNING		0
+#endif
+
+#ifndef BAILOUT_EARLY
+#define BAILOUT_EARLY			0
+#endif
+
+#ifndef TEST_MODE_TO_HW
+#define TEST_MODE_TO_HW			0
+#endif
+
+#ifndef USE_SYNC_PAGE
+#define USE_SYNC_PAGE			1
+#endif
+
+#ifndef MOBILE_HW_CURSOR
+#define MOBILE_HW_CURSOR		0
+#endif
+
+#ifndef PREFERRED_MODE
+#define PREFERRED_MODE			"1024x768-16@60"
+#endif
+
+/*** hw-related values ***/
+
+/* PCI ids for supported devices */
+#define PCI_DEVICE_ID_INTEL_830M	0x3577
+#define PCI_DEVICE_ID_INTEL_845G	0x2562
+#define PCI_DEVICE_ID_INTEL_85XGM	0x3582
+#define PCI_DEVICE_ID_INTEL_865G	0x2572
+
+/* Size of MMIO region */
+#define INTEL_REG_SIZE			0x80000
+
+#define STRIDE_ALIGNMENT		16
+
+#define PAT_ROP_GXCOPY                  0xf0
+#define PAT_ROP_GXXOR                   0x5a
+
+#define PALETTE_8_ENTRIES		256
+
+
+/*** Macros ***/
+
+/* basic arithmetic */
+#define KB(x)			((x) * 1024)
+#define MB(x)			((x) * 1024 * 1024)
+#define BtoKB(x)		((x) / 1024)
+#define BtoMB(x)		((x) / 1024 / 1024)
+
+#define ROUND_UP_TO(x, y)	(((x) + (y) - 1) / (y) * (y))
+#define ROUND_DOWN_TO(x, y)	((x) / (y) * (y))
+#define ROUND_UP_TO_PAGE(x)	ROUND_UP_TO((x), PAGE_SIZE)
+#define ROUND_DOWN_TO_PAGE(x)	ROUND_DOWN_TO((x), PAGE_SIZE)
+
+/* messages */
+#define PFX			INTELFB_MODULE_NAME ": "
+
+#define ERR_MSG(fmt, args...)	printk(KERN_ERR PFX fmt, ## args)
+#define WRN_MSG(fmt, args...)	printk(KERN_WARNING PFX fmt, ## args)
+#define NOT_MSG(fmt, args...)	printk(KERN_NOTICE PFX fmt, ## args)
+#define INF_MSG(fmt, args...)	printk(KERN_INFO PFX fmt, ## args)
+#if DEBUG
+#define DBG_MSG(fmt, args...)	printk(KERN_DEBUG PFX fmt, ## args)
+#else
+#define DBG_MSG(fmt, args...)	while (0) printk(fmt, ## args)
+#endif
+
+/* get commonly used pointers */
+#define GET_DINFO(info)		(struct intelfb_info *)(info)
+#define GET_DISP(info, con)	((con) < 0) ? (info)->disp : &fb_display[con]
+
+/* module parameters */
+#define INTELFB_INT_PARAM(name, default, desc)				\
+	static int name = default;					\
+	MODULE_PARM(name, "i");						\
+	MODULE_PARM_DESC(name, desc);
+
+#define INTELFB_STR_PARAM(name, default, desc)				\
+	static const char *name = default;				\
+	MODULE_PARM(name, "s");						\
+	MODULE_PARM_DESC(name, desc);
+
+/* misc macros */
+#define TEXT_ACCEL(d, v)						\
+	((d)->accel && (d)->ring_active &&				\
+	 ((v)->accel_flags & FB_ACCELF_TEXT))
+
+#define USE_DRAWGLYPH(d)						\
+	((d)->chipset == INTEL_865G)
+
+#define NOACCEL_CHIPSET(d)						\
+	((d)->chipset != INTEL_865G)
+
+#ifndef LockPage
+#define LockPage(page)		set_bit(PG_locked, &(page)->flags)
+#endif
+#ifndef UnlockPage
+#define UnlockPage(page)	unlock_page(page)
+#endif
+
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)
+#define FB_SET_CMAP(a, b, c, d) fb_set_cmap(a, b, d)
+#else
+#define FB_SET_CMAP(a, b, c, d) fb_set_cmap(a, b, c, d)
+#endif
+
+
+/*** Driver paramters ***/
+
+#define RINGBUFFER_SIZE		KB(64)
+#define HW_CURSOR_SIZE		KB(4)
+
+
+/*** Data Types ***/
+
+/* supported chipsets */
+enum intel_chips {
+	INTEL_830M,
+	INTEL_845G,
+	INTEL_85XGM,
+	INTEL_852GM,
+	INTEL_852GME,
+	INTEL_855GM,
+	INTEL_855GME,
+	INTEL_865G
+};
+
+struct intelfb_hwstate {
+	u32 vga0_divisor;
+	u32 vga1_divisor;
+	u32 vga_pd;
+	u32 dpll_a;
+	u32 dpll_b;
+	u32 fpa0;
+	u32 fpa1;
+	u32 fpb0;
+	u32 fpb1;
+	u32 palette_a[PALETTE_8_ENTRIES];
+	u32 palette_b[PALETTE_8_ENTRIES];
+	u32 htotal_a;
+	u32 hblank_a;
+	u32 hsync_a;
+	u32 vtotal_a;
+	u32 vblank_a;
+	u32 vsync_a;
+	u32 src_size_a;
+	u32 bclrpat_a;
+	u32 htotal_b;
+	u32 hblank_b;
+	u32 hsync_b;
+	u32 vtotal_b;
+	u32 vblank_b;
+	u32 vsync_b;
+	u32 src_size_b;
+	u32 bclrpat_b;
+	u32 adpa;
+	u32 dvoa;
+	u32 dvob;
+	u32 dvoc;
+	u32 dvoa_srcdim;
+	u32 dvob_srcdim;
+	u32 dvoc_srcdim;
+	u32 lvds;
+	u32 pipe_a_conf;
+	u32 pipe_b_conf;
+	u32 disp_arb;
+	u32 cursor_a_control;
+	u32 cursor_b_control;
+	u32 cursor_a_base;
+	u32 cursor_b_base;
+	u32 cursor_size;
+	u32 disp_a_ctrl;
+	u32 disp_b_ctrl;
+	u32 disp_a_base;
+	u32 disp_b_base;
+	u32 cursor_a_palette[4];
+	u32 cursor_b_palette[4];
+	u32 disp_a_stride;
+	u32 disp_b_stride;
+	u32 vgacntrl;
+	u32 add_id;
+	u32 swf0x[7];
+	u32 swf1x[7];
+	u32 swf3x[3];
+	u32 fence[8];
+	u32 instpm;
+	u32 mem_mode;
+	u32 fw_blc_0;
+	u32 fw_blc_1;
+};
+
+struct intelfb_info {
+	struct fb_info info;
+
+	const char *name;
+
+	u32 fb_base_phys;
+	u32 mmio_base_phys;
+
+	u32 fb_base;
+	u32 mmio_base;
+
+	u32 fb_offset;
+
+	struct pci_dev *pdev;
+
+	/* Ring buffer */
+	u32 ring_base_phys;
+	u32 ring_base;
+	u32 ring_size;
+	u32 ring_head;
+	u32 ring_tail;
+	u32 ring_tail_mask;
+	u32 ring_space;
+
+	/* HW cursor */
+	u32 cursor_base_phys;
+	u32 cursor_offset;
+	u32 cursor_base;
+	u32 cursor_size;
+	u32 cursor_page_virt;
+	u32 cursor_base_real;
+
+#if USE_SYNC_PAGE
+	/* 2D synchronisation */
+	u32 syncpage_virt;
+	u32 syncpage_phys;
+#endif
+
+	struct display disp;
+	int currcon;
+	struct display *currcon_display;
+
+	struct { u8 red, green, blue, pad; } palette[256];
+
+	int pci_chipset;
+	int chipset;
+	int mobile;
+
+	int video_ram;
+	int aperture_size;
+	int stolen_size;
+	int bpp, depth;
+	u32 visual;
+
+	int xres, yres, pitch;
+	int pixclock;
+
+	int pipe;
+
+	int accel;
+	int hwcursor;
+	int fixed_mode;
+
+	int ring_active;
+
+#if defined(FBCON_HAS_CFB16) || defined(FBCON_HAS_CFB32)
+	union {
+#if defined(FBCON_HAS_CFB16)
+		u_int16_t cfb16[16];
+#endif
+#if defined(FBCON_HAS_CFB32)
+		u_int32_t cfb32[16];
+#endif
+	} con_cmap;
+#endif
+
+	int vc_mode;
+
+	struct intelfb_hwstate save_state;
+
+	int registered;
+
+	int initial_vga;
+	struct fb_var_screeninfo initial_var;
+	u32 initial_fb_base;
+	u32 initial_video_ram;
+	u32 initial_pitch;
+
+	struct {
+		int type;
+		int state;
+		int on;
+		int enabled;
+		int blanked;
+		int w, u, d;
+		int x, y, redraw;
+		unsigned long cursorimage;
+		struct timer_list timer;
+	} cursor;
+	spinlock_t DAClock;
+};
+
+
+/*** Functions ***/
+
+/* intelfb.c */
+extern int intelfb_var_to_depth(const struct fb_var_screeninfo *var);
+extern void intelfb_create_cursor_shape(struct intelfb_info *dinfo,
+					struct display *disp);
+
+/* intelfbhw.c */
+
+extern int intelfbhw_get_chipset(struct pci_dev *pdev, const char **name,
+				 int *chipset, int *mobile);
+extern int intelfbhw_get_memory(struct pci_dev *pdev, int *aperture_size,
+				int *stolen_size);
+extern const char *intelfbhw_check_non_crt(struct intelfb_info *dinfo);
+extern int intelfbhw_validate_mode(struct intelfb_info *dinfo, int con,
+				   struct fb_var_screeninfo *var);
+extern int intelfbhw_pan_display(struct fb_var_screeninfo *var, int con,
+				 struct fb_info *info);
+extern void intelfbhw_do_blank(int blank, struct fb_info *info);
+extern void intelfbhw_setcolreg(struct intelfb_info *dinfo, unsigned regno,
+				unsigned red, unsigned green, unsigned blue,
+				unsigned transp);
+extern int intelfbhw_read_hw_state(struct intelfb_info *dinfo,
+				   struct intelfb_hwstate *hw, int flag);
+extern void intelfbhw_print_hw_state(struct intelfb_info *dinfo,
+				     struct intelfb_hwstate *hw);
+extern int intelfbhw_mode_to_hw(struct intelfb_info *dinfo,
+				struct intelfb_hwstate *hw,
+				struct fb_var_screeninfo *var);
+extern int intelfbhw_program_mode(struct intelfb_info *dinfo,
+				  const struct intelfb_hwstate *hw, int blank);
+extern void intelfbhw_do_sync(struct intelfb_info *dinfo);
+extern void intelfbhw_2d_stop(struct intelfb_info *dinfo);
+extern void intelfbhw_2d_start(struct intelfb_info *dinfo);
+extern void intelfbhw_do_fillrect(struct intelfb_info *dinfo, u32 x, u32 y,
+				  u32 w, u32 h, u32 color, u32 pitch, u32 bpp,
+				  u32 rop);
+extern void intelfbhw_do_bitblt(struct intelfb_info *dinfo, u32 curx, u32 cury,
+				u32 dstx, u32 dsty, u32 w, u32 h, u32 pitch,
+				u32 bpp);
+extern int intelfbhw_do_drawglyph(struct intelfb_info *dinfo, u32 fg, u32 bg,
+				  u32 w, u32 h, u8* cdat, u32 x, u32 y,
+				  u32 pitch, u32 bpp);
+extern void intelfbhw_cursor_init(struct intelfb_info *dinfo);
+extern void intelfbhw_cursor_hide(struct intelfb_info *dinfo);
+extern void intelfbhw_cursor_show(struct intelfb_info *dinfo);
+extern void intelfbhw_cursor_setpos(struct intelfb_info *dinfo, int x, int y);
+extern void intelfbhw_cursor_setcolor(struct intelfb_info *dinfo, u32 bg,
+				      u32 fg);
+extern void intelfbhw_cursor_load(struct intelfb_info *dinfo,
+				  struct display *disp);
+
+
+#endif /* _INTELFB_H */
diff -urN linux-2.4.21-pre5/drivers/video/intel/intelfbdrv.c linux-2.4.21-pre6/drivers/video/intel/intelfbdrv.c
--- linux-2.4.21-pre5/drivers/video/intel/intelfbdrv.c	Wed Dec 31 16:00:00 1969
+++ linux-2.4.21-pre6/drivers/video/intel/intelfbdrv.c	Wed Mar 26 16:11:39 2003
@@ -0,0 +1,2219 @@
+/*
+ * intelfb
+ *
+ * Linux framebuffer driver for Intel(R) 830M/845G/852GM/855GM/865G
+ * integrated graphics chips.
+ *
+ * Copyright (C) 2002, 2003 David Dawes <dawes@tungstengraphics.com>
+ *
+ * This driver consists of two parts.  The first part (intelfbdrv.c) provides
+ * the basic fbdev interfaces, is derived in part from the radeonfb and
+ * vesafb drivers, and is covered by the GPL.  The second part (intelfbhw.c)
+ * provides the code to program the hardware.  Most of it is derived from
+ * the i810/i830 XFree86 driver.  The HW-specific code is covered here
+ * under a dual license (GPL and MIT/XFree86 license).
+ *
+ * Author: David Dawes
+ *
+ */
+
+/* $DHD: intelfb/intelfbdrv.c,v 1.15 2003/02/06 17:50:08 dawes Exp $ */
+/* $TG$ */
+
+/*
+ * Changes:
+ *    01/2003 - Initial driver (0.1.0), no mode switching, no acceleration.
+ *		This initial version is a basic core that works a lot like
+ *		the vesafb driver.  It must be built-in to the kernel,
+ *		and the initial video mode must be set with vga=XXX at
+ *		boot time.  (David Dawes)
+ *
+ *    01/2003 - Version 0.2.0: Mode switching added, colormap support
+ *		implemented, Y panning, and soft screen blanking implemented.
+ *		No acceleration yet.  (David Dawes)
+ *
+ *    01/2003 - Version 0.3.0: fbcon acceleration support added.  Module
+ *		option handling added.  (David Dawes)
+ *
+ *    01/2003 - Version 0.4.0: fbcon HW cursor support added.  (David Dawes)
+ *
+ *    01/2003 - Version 0.4.1: Add auto-generation of built-in modes.
+ *		(David Dawes)
+ *
+ *    02/2003 - Version 0.4.2: Add check for active non-CRT devices, and 
+ *		mode validation checks.  (David Dawes)
+ *
+ *    02/2003 - Version 0.4.3: Check when the VC is in graphics mode so that
+ *		acceleration is disabled while an XFree86 server is running.
+ *		(David Dawes)
+ *
+ *    02/2003 - Version 0.4.4: Monitor DPMS support.  (David Dawes)
+ *
+ *    02/2003 - Version 0.4.5: Basic XFree86 + fbdev working.  (David Dawes)
+ *
+ *    02/2003 - Version 0.5.0: Modify to work with the 2.5.32 kernel as well
+ *		as 2.4.x kernels.  (David Dawes)
+ *
+ *    02/2003 - Version 0.6.0: Split out HW-specifics into a separate file.
+ *		(David Dawes)
+ *
+ *    02/2003 - Version 0.7.0: Test on 852GM/855GM.  Acceleration and HW
+ *		cursor are disabled on this platform.  (David Dawes)
+ *
+ *    02/2003 - Version 0.7.1: Test on 845G.  Acceleration is disabled
+ *		on this platform.  (David Dawes)
+ *
+ *    02/2003 - Version 0.7.2: Test on 830M.  Acceleration and HW
+ *		cursor are disabled on this platform.  (David Dawes)
+ *
+ *    02/2003 - Version 0.7.3: Fix 8-bit modes for mobile platforms
+ *		(David Dawes)
+ *
+ *    02/2003 - Version 0.7.4: Add checks for FB and FBCON_HAS_CFB* configured
+ *		in the kernel, and add mode bpp verification and default
+ *		bpp selection based on which FBCON_HAS_CFB* are configured.
+ *		(David Dawes)
+ *
+ *    02/2003 - Version 0.7.5: Add basic package/install scripts based on the
+ *		DRI packaging scripts.  (David Dawes)
+ *
+ * TODO:
+ *  - 
+ *
+ * Wish List:
+ *  - Check clock limits for 845G and 830M.
+ *  - Test on SMP config.
+ *  - Check if any functions/data should be __devinit, etc.
+ *  - See if it's feasible to get/use DDC/EDID info.
+ *  - MTRR support.
+ *  - See if module unloading can work.
+ *  - See if driver works built-in to 2.5.32 kernel.
+ *  - Check acceleration problems on 830M-855GM.
+ *  - Add gtf support so that arbitrary modes can be calculated.
+ *  - Port driver to latest 2.5.x fbdev interface.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/tty.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/fb.h>
+#include <linux/console.h>
+#include <linux/selection.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/vmalloc.h>
+#include <linux/kd.h>
+#include <linux/vt_kern.h>
+#include <linux/pagemap.h>
+#include <linux/version.h>
+
+#include <asm/io.h>
+
+#include <video/fbcon.h>
+#include <video/fbcon-cfb8.h>
+#include <video/fbcon-cfb16.h>
+#include <video/fbcon-cfb32.h>
+
+#include "intelfb.h"
+
+#include "builtinmodes.c"
+
+/*
+ * Limiting the class to PCI_CLASS_DISPLAY_VGA prevents function 1 of the
+ * mobile chipsets from being registered.
+ */
+#if DETECT_VGA_CLASS_ONLY
+#define INTELFB_CLASS_MASK ~0 << 8
+#else
+#define INTELFB_CLASS_MASK 0
+#endif
+
+static struct pci_device_id intelfb_pci_table[] __devinitdata = {
+	{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_830M, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_DISPLAY_VGA << 8, INTELFB_CLASS_MASK, INTEL_830M },
+	{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_845G, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_DISPLAY_VGA << 8, INTELFB_CLASS_MASK, INTEL_845G },
+	{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_85XGM, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_DISPLAY_VGA << 8, INTELFB_CLASS_MASK, INTEL_85XGM },
+	{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_865G, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_DISPLAY_VGA << 8, INTELFB_CLASS_MASK, INTEL_865G },
+	{ 0, }
+};
+
+/* Global data */
+static int num_registered = 0;
+
+
+/* Forward declarations */
+static int intelfb_get_fix(struct fb_fix_screeninfo *fix, int con,
+			   struct fb_info *info);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+static int intelfb_get_var(struct fb_var_screeninfo *var, int con,
+			   struct fb_info *info);
+#endif
+static int intelfb_set_var(struct fb_var_screeninfo *var, int con,
+			   struct fb_info *info);
+static int intelfb_get_cmap(struct fb_cmap *cmap, int kspc, int con,
+			    struct fb_info *info);
+static int intelfb_set_cmap(struct fb_cmap *cmap, int kspc, int con,
+			    struct fb_info *info);
+static int intelfb_ioctl(struct inode *inode, struct file *file,
+			 unsigned int cmd, unsigned long arg, int con,
+			 struct fb_info *info);
+static int intelfb_switch(int con, struct fb_info *info);
+static int intelfb_updatevar(int con, struct fb_info *info);
+static int intelfb_blank(int blank, struct fb_info *info);
+static int intelfb_getcolreg(unsigned regno, unsigned *red, unsigned *green,
+			     unsigned *blue, unsigned *transp,
+			     struct fb_info *info);
+static int intelfb_setcolreg(unsigned regno, unsigned red, unsigned green,
+			     unsigned blue, unsigned transp,
+			     struct fb_info *info);
+static int intelfb_pci_register(struct pci_dev *pdev,
+				const struct pci_device_id *ent);
+static void __devexit intelfb_pci_unregister(struct pci_dev *pdev);
+static int __devinit intelfb_set_fbinfo(struct intelfb_info *dinfo);
+static void intelfb_set_dispsw(struct intelfb_info *dinfo,
+			       struct display *disp);
+static void mode_to_var(const struct fb_videomode *mode,
+			struct fb_var_screeninfo *var, u32 bpp);
+static int intelfb_set_mode(struct intelfb_info *dinfo,
+			    struct fb_var_screeninfo *var,
+			    struct display *disp, int blank);
+static void intelfb_do_install_cmap(int con, struct fb_info *info);
+static void update_dinfo(struct intelfb_info *dinfo,
+			 struct fb_var_screeninfo *var, struct display *disp);
+static void intelfb_flashcursor(unsigned long ptr);
+static void fbcon_intelfb_setup(struct display *p);
+static void fbcon_intelfb_bmove(struct display *p, int sy, int sx, int dy,
+				int dx, int height, int width);
+static void fbcon_intelfb_clear(struct vc_data *conp, struct display *p,
+				int sy, int sx, int height, int width);
+static void fbcon_intelfb_putc(struct vc_data *conp, struct display *p,
+			       int c, int yy, int xx);
+static void fbcon_intelfb_putcs(struct vc_data *conp, struct display *p,
+				const unsigned short *s, int count,
+				int yy, int xx);
+static void fbcon_intelfb_revc(struct display *p, int xx, int yy);
+static void fbcon_intelfb_clear_margins(struct vc_data *conp,
+					struct display *p, int bottom_only);
+static void fbcon_intelfb_cursor(struct display *disp, int mode, int x, int y);
+
+/* fb ops */
+static struct fb_ops intel_fb_ops = {
+	.owner =		THIS_MODULE,
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+	.fb_get_fix =		intelfb_get_fix,
+	.fb_get_var =		intelfb_get_var,
+#else
+	.fb_blank =		intelfb_blank,
+	.fb_setcolreg =		intelfb_setcolreg,
+#endif
+	.fb_set_var =		intelfb_set_var,
+	.fb_get_cmap =		intelfb_get_cmap,
+	.fb_set_cmap =		intelfb_set_cmap,
+	.fb_pan_display	=	intelfbhw_pan_display,
+	.fb_ioctl =		intelfb_ioctl
+};
+
+/* PCI driver module table */
+static struct pci_driver intelfb_driver = {
+	.name =			INTELFB_MODULE_NAME,
+	.id_table =		intelfb_pci_table,
+	.probe =		intelfb_pci_register,
+	.remove =		__devexit_p(intelfb_pci_unregister)
+};
+
+/* fbcon acceleration */
+static struct display_switch fbcon_intelfb = {
+	.setup =		fbcon_intelfb_setup,
+	.bmove =		fbcon_intelfb_bmove,
+	.clear =		fbcon_intelfb_clear,
+	.putc =			fbcon_intelfb_putc,
+	.putcs =		fbcon_intelfb_putcs,
+	.revc =			fbcon_intelfb_revc,
+	.cursor =		fbcon_intelfb_cursor,
+	.clear_margins =	fbcon_intelfb_clear_margins,
+	.fontwidthmask =	FONTWIDTHRANGE(4, 16)
+};
+
+/* Module description/parameters */
+MODULE_AUTHOR("David Dawes <dawes@tungstengraphics.com>");
+MODULE_DESCRIPTION(
+	"Framebuffer driver for Intel(R) " SUPPORTED_CHIPSETS " chipsets");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_DEVICE_TABLE(pci, intelfb_pci_table);
+
+INTELFB_INT_PARAM(accel, 1, "Enable console acceleration");
+INTELFB_INT_PARAM(hwcursor, 1, "Enable HW cursor");
+INTELFB_INT_PARAM(fixed, 0, "Disable mode switching");
+INTELFB_INT_PARAM(noinit, 0, "Don't initialise graphics mode when loading");
+INTELFB_INT_PARAM(noregister, 0, "Don't register, just probe and exit (debug)");
+INTELFB_INT_PARAM(probeonly, 0, "Do a minimal probe (debug)");
+INTELFB_INT_PARAM(idonly, 0,
+		  "Just identify without doing anything else (debug)");
+INTELFB_INT_PARAM(bailearly, 0, "Bail out early, depending on value (debug)");
+INTELFB_STR_PARAM(mode, NULL,
+		  "Initial video mode \"<xres>x<yres>[-<depth>][@<refresh>]\"");
+INTELFB_STR_PARAM(font, NULL, "Specify which built-in font to use");
+
+
+/* module load/unload entry points */
+int __init
+intelfb_init(void)
+{
+	DBG_MSG("intelfb_init\n");
+
+	INF_MSG("Framebuffer driver for "
+		"Intel(R) " SUPPORTED_CHIPSETS " chipsets\n");
+	INF_MSG("Version " INTELFB_VERSION
+		", written by David Dawes <dawes@tungstengraphics.com>\n");
+
+	if (idonly)
+		return -ENODEV;
+
+	return pci_module_init(&intelfb_driver);
+}
+
+void __exit
+intelfb_exit(void)
+{
+	DBG_MSG("intelfb_exit\n");
+	pci_unregister_driver(&intelfb_driver);
+}
+
+#ifndef MODULE
+#define OPT_EQUAL(opt, name) (!strncmp(opt, name, strlen(name)))
+#define OPT_INTVAL(opt, name) simple_strtoul(opt + strlen(name), NULL, 0)
+#define OPT_STRVAL(opt, name) (opt + strlen(name))
+
+static __inline__ char *
+get_opt_string(const char *this_opt, const char *name)
+{
+	const char *p;
+	int i;
+	char *ret;
+
+	p = OPT_STRVAL(this_opt, name);
+	i = 0;
+	while (p[i] && p[i] != ' ' && p[i] != ',')
+		i++;
+	ret = kmalloc(i + 1, GFP_KERNEL);
+	if (ret) {
+		strncpy(ret, p, i);
+		ret[i] = '\0';
+	}
+	return ret;
+}
+
+static __inline__ int
+get_opt_bool(const char *this_opt, const char *name, int *ret)
+{
+	if (!ret)
+		return 0;
+
+	if (OPT_EQUAL(this_opt, name)) {
+		if (this_opt[strlen(name)] == '=')
+			*ret = simple_strtoul(this_opt + strlen(name) + 1,
+					      NULL, 0);
+		else
+			*ret = 1;
+	} else {
+		if (OPT_EQUAL(this_opt, "no") && OPT_EQUAL(this_opt + 2, name))
+			*ret = 0;
+		else
+			return 0;
+	}
+	return 1;
+}
+
+int __init
+intelfb_setup(char *options)
+{
+	char *this_opt;
+
+	DBG_MSG("intelfb_setup\n");
+
+	if (!options || !*options) {
+		DBG_MSG("no options\n");
+		return 0;
+	} else
+		DBG_MSG("options: %s\n", options);
+
+	/*
+	 * These are the built-in options analogous to the module parameters
+	 * defined above.
+	 *
+	 * The syntax is:
+	 *
+	 *    video=intelfb:[mode][,<param>=<val>] ...
+	 *
+	 * e.g.,
+	 *
+	 *    video=intelfb:1024x768-16@75,accel=0
+	 */
+
+	while ((this_opt = strsep(&options, ","))) {
+		if (!*this_opt)
+			continue;
+		if (get_opt_bool(this_opt, "accel", &accel))
+			;
+		else if (get_opt_bool(this_opt, "hwcursor", &hwcursor))
+			;
+		else if (get_opt_bool(this_opt, "fixed", &fixed))
+			;
+		else if (get_opt_bool(this_opt, "init", &noinit))
+			noinit = !noinit;
+		else if (OPT_EQUAL(this_opt, "font="))
+			font = get_opt_string(this_opt, "font=");
+		else if (OPT_EQUAL(this_opt, "mode="))
+			mode = get_opt_string(this_opt, "mode=");
+		else
+			mode = this_opt;
+	}
+
+	return 0;
+}
+#endif
+
+#ifdef MODULE
+module_init(intelfb_init);
+module_exit(intelfb_exit);
+#endif
+
+static void
+cleanup(struct intelfb_info *dinfo)
+{
+	DBG_MSG("cleanup\n");
+
+	if (!dinfo)
+		return;
+
+	if (dinfo->registered)
+		unregister_framebuffer(&(dinfo->info));
+
+	if (&dinfo->cursor.timer)
+		del_timer_sync(&dinfo->cursor.timer);
+
+#if USE_SYNC_PAGE
+	if (dinfo->syncpage_virt) {
+		struct page *pg = virt_to_page((void *)dinfo->syncpage_virt);
+		if (pg) {
+			put_page(pg);
+			UnlockPage(pg);
+			free_page(dinfo->syncpage_virt);
+		}
+	}
+#endif
+
+	if (dinfo->cursor_base)
+		iounmap((void *)dinfo->cursor_base);
+	if (dinfo->ring_base)
+		iounmap((void *)dinfo->ring_base);
+	if (dinfo->fb_base)
+		iounmap((void *)dinfo->fb_base);
+	if (dinfo->mmio_base)
+		iounmap((void *)dinfo->mmio_base);
+	if (dinfo->mmio_base_phys && dinfo->pdev)
+		release_mem_region(dinfo->mmio_base_phys,
+				   pci_resource_len(dinfo->pdev, 1));
+	if (dinfo->fb_base_phys && dinfo->pdev)
+		release_mem_region(dinfo->fb_base_phys,
+				   pci_resource_len(dinfo->pdev, 0));
+	kfree(dinfo);
+}
+
+#define bailout(dinfo) do {						\
+	DBG_MSG("bailout\n");						\
+	cleanup(dinfo);							\
+	INF_MSG("Not going to register framebuffer, exiting...\n");	\
+	return -ENODEV;							\
+} while (0)
+
+
+int
+intelfb_var_to_depth(const struct fb_var_screeninfo *var)
+{
+#if 0
+	DBG_MSG("intelfb_var_to_depth: bpp: %d, green.length is %d\n",
+		var->bits_per_pixel, var->green.length);
+#endif
+
+	switch (var->bits_per_pixel) {
+	case 16:
+		return (var->green.length == 6) ? 16 : 15;
+	case 32:
+		return 24;
+	default:
+		return var->bits_per_pixel;
+	}
+}
+
+static void
+get_initial_mode(struct intelfb_info *dinfo)
+{
+	struct fb_var_screeninfo *var;
+	int xtot, ytot;
+
+	DBG_MSG("get_initial_mode\n");
+
+	dinfo->initial_vga = 1;
+	dinfo->initial_fb_base = screen_info.lfb_base;
+	dinfo->initial_video_ram = screen_info.lfb_size * KB(64);
+	dinfo->initial_pitch = screen_info.lfb_linelength;
+
+	var = &dinfo->initial_var;
+	memset(var, 0, sizeof(*var));
+	var->xres = screen_info.lfb_width;
+	var->yres = screen_info.lfb_height;
+	var->xres_virtual = var->xres;
+#if ALLOCATE_FOR_PANNING
+	/* Allow use of half of the video ram for panning */
+	var->yres_virtual =
+		dinfo->initial_video_ram / 2 / dinfo->initial_pitch;
+	if (var->yres_virtual < var->yres)
+		var->yres_virtual = var->yres;
+#else
+	var->yres_virtual = var->yres;
+#endif
+	var->bits_per_pixel = screen_info.lfb_depth;
+	switch (screen_info.lfb_depth) {
+	case 15:
+		var->bits_per_pixel = 16;
+		break;
+	case 24:
+		var->bits_per_pixel = 32;
+		break;
+	}
+
+	DBG_MSG("Initial info: FB is 0x%x/0x%x (%d kByte)\n",
+		dinfo->initial_fb_base, dinfo->initial_video_ram,
+		BtoKB(dinfo->initial_video_ram));
+
+	DBG_MSG("Initial info: mode is %dx%d-%d (%d)\n",
+		var->xres, var->yres, var->bits_per_pixel,
+		dinfo->initial_pitch);
+
+	/* Dummy timing values (assume 60Hz) */
+	var->left_margin = (var->xres / 8) & 0xf8;
+	var->right_margin = 32;
+	var->upper_margin = 16;
+	var->lower_margin = 4;
+	var->hsync_len = (var->xres / 8) & 0xf8;
+	var->vsync_len = 4;
+
+	xtot = var->xres + var->left_margin +
+		var->right_margin + var->hsync_len;
+	ytot = var->yres + var->upper_margin +
+		var->lower_margin + var->vsync_len;
+	var->pixclock = 10000000 / xtot * 1000 / ytot * 100 / 60;
+
+	var->height = -1;
+	var->width = -1;
+
+	if (var->bits_per_pixel > 8) {
+		var->red.offset = screen_info.red_pos;
+		var->red.length = screen_info.red_size;
+		var->green.offset = screen_info.green_pos;
+		var->green.length = screen_info.green_size;
+		var->blue.offset = screen_info.blue_pos;
+		var->blue.length = screen_info.blue_size;
+		var->transp.offset = screen_info.rsvd_pos;
+		var->transp.length = screen_info.rsvd_size;
+	} else {
+		var->red.length = 8;
+		var->green.length = 8;
+		var->blue.length = 8;
+	}
+}
+
+static int
+intelfb_pci_register(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+	struct intelfb_info *dinfo;
+	int i, j, err;
+	const char *s;
+
+	DBG_MSG("intelfb_pci_register\n");
+
+	num_registered++;
+	if (num_registered != 1) {
+		ERR_MSG("Attempted to register %d devices "
+			"(should be only 1).\n", num_registered);
+		return -ENODEV;
+	}
+
+	if (!(dinfo = kmalloc(sizeof(struct intelfb_info), GFP_KERNEL))) {
+		ERR_MSG("Could not allocate memory for intelfb_info.\n");
+		return -ENODEV;
+	}
+
+	memset(dinfo, 0, sizeof(*dinfo));
+	dinfo->pdev = pdev;
+
+	/* Enable device. */
+	if ((err = pci_enable_device(pdev))) {
+		ERR_MSG("Cannot enable device.\n");
+		cleanup(dinfo);
+		return -ENODEV;
+	}
+
+	/* Set base addresses. */
+	dinfo->fb_base_phys = pci_resource_start(pdev, 0);
+	dinfo->mmio_base_phys = pci_resource_start(pdev, 1);
+
+	DBG_MSG("fb region: 0x%lx/0x%lx, MMIO region: 0x%lx/0x%lx\n",
+		pci_resource_start(pdev, 0), pci_resource_len(pdev, 0),
+		pci_resource_start(pdev, 1), pci_resource_len(pdev, 1));
+
+	/* Reserve the fb and MMIO regions */
+	if (!request_mem_region(dinfo->fb_base_phys, pci_resource_len(pdev, 0),
+				INTELFB_MODULE_NAME)) {
+		ERR_MSG("Cannot reserve FB region.\n");
+		cleanup(dinfo);
+		return -ENODEV;
+	}
+	if (!request_mem_region(dinfo->mmio_base_phys,
+				pci_resource_len(pdev, 1),
+				INTELFB_MODULE_NAME)) {
+		ERR_MSG("Cannot reserve MMIO region.\n");
+		cleanup(dinfo);
+		return -ENODEV;
+	}
+
+	/* Map the MMIO region. */
+	dinfo->mmio_base = (u32)ioremap(dinfo->mmio_base_phys, INTEL_REG_SIZE);
+	if (!dinfo->mmio_base) {
+		ERR_MSG("Cannot map MMIO.\n");
+		cleanup(dinfo);
+		return -ENODEV;
+	}
+
+	/* Get the chipset info. */
+	dinfo->pci_chipset = pdev->device;
+
+	if (intelfbhw_get_chipset(pdev, &dinfo->name, &dinfo->chipset,
+				  &dinfo->mobile)) {
+		cleanup(dinfo);
+		return -ENODEV;
+	}
+
+	if (intelfbhw_get_memory(pdev, &dinfo->aperture_size,
+				 &dinfo->stolen_size)) {
+		cleanup(dinfo);
+		return -ENODEV;
+	}
+
+	INF_MSG("%02x:%02x.%d: %s, aperture size %dMB, "
+		"stolen memory %dkB\n",
+		pdev->bus->number, PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn),
+		dinfo->name, BtoMB(dinfo->aperture_size),
+		BtoKB(dinfo->stolen_size));
+
+	/* Set these from the options. */
+	dinfo->accel = accel;
+	dinfo->hwcursor = hwcursor;
+
+	if (NOACCEL_CHIPSET(dinfo) && dinfo->accel == 1) {
+		INF_MSG("Acceleration is not supported for the %s chipset.\n",
+			dinfo->name);
+		dinfo->accel = 0;
+	}
+
+	/*
+	 * For now, set the video ram size to the stolen size rounded
+	 * down to a page multiple.  This means the agpgart driver doesn't
+	 * need to get involved.
+	 */
+	dinfo->video_ram = ROUND_DOWN_TO_PAGE(dinfo->stolen_size);
+
+	/* Allocate space for the ring buffer and HW cursor. */
+	if (dinfo->accel) {
+		dinfo->ring_size = RINGBUFFER_SIZE;
+		dinfo->video_ram -= dinfo->ring_size;
+		dinfo->ring_base_phys = dinfo->fb_base_phys + dinfo->video_ram;
+		dinfo->ring_tail_mask = dinfo->ring_size - 1;
+	}
+	if (dinfo->hwcursor && !dinfo->mobile) {
+		dinfo->cursor_size = HW_CURSOR_SIZE;
+		dinfo->video_ram -= dinfo->cursor_size;
+		dinfo->cursor_offset = dinfo->video_ram;
+		dinfo->cursor_base_phys = dinfo->fb_base_phys +
+					  dinfo->cursor_offset;
+	}
+
+	/* Framebuffer starts at offset 0. */
+	dinfo->fb_offset = 0;
+	dinfo->video_ram -= dinfo->fb_offset;
+
+	/* Map the FB region.  Only map the memory used as video ram. */
+	dinfo->fb_base = (u32)ioremap(dinfo->fb_base_phys + dinfo->fb_offset,
+				      dinfo->video_ram);
+	if (!dinfo->fb_base) {
+		ERR_MSG("Cannot map framebuffer.\n");
+		cleanup(dinfo);
+		return -ENODEV;
+	}
+
+	if (dinfo->accel) {
+		/* Map the ring buffer. */
+		dinfo->ring_base = (u32)ioremap(dinfo->ring_base_phys,
+						dinfo->ring_size);
+		if (!dinfo->ring_base) {
+			dinfo->accel = 0;
+			WRN_MSG("Cannot map ring buffer: "
+				"acceleration disabled.\n");
+		}
+	}
+
+	if (dinfo->hwcursor && !dinfo->mobile) {
+		/* Map the HW cursor buffer. */
+		dinfo->cursor_base = (u32)ioremap(dinfo->cursor_base_phys,
+						  dinfo->cursor_size);
+		if (!dinfo->cursor_base) {
+			WRN_MSG("Cannot map the HW cursor space: "
+				"HW cursor disabled.\n");
+			dinfo->hwcursor = 0;
+		}
+	}
+
+	/*
+	 * For mobile platforms, a physical page address is needed for the
+	 * HW cursor.  Allocate it here and insert it into the GTT.
+	 * If/when this driver is modified to be aware of the agpgart
+	 * driver, it can be used to obtain/insert the page.
+	 */
+	if (dinfo->hwcursor && dinfo->mobile) {
+#if !MOBILE_HW_CURSOR
+		dinfo->hwcursor = 0;
+		INF_MSG("HW cursor is not supported for mobile platforms.\n");
+#else
+		struct page *pg;
+
+		/*
+		 * Allocate a page of physical memory for the graphics
+		 * engine to write to for synchronisation purposes.
+		 */
+		pg = alloc_page(GFP_KERNEL);
+		if (!pg) {
+			WRN_MSG("Cannot allocate a page for the HW cursor.  "
+				"Disabling the HW cursor.");
+			dinfo->hwcursor = 0;
+		} else {
+			get_page(pg);
+			LockPage(pg);
+			dinfo->cursor_page_virt = (u32)page_address(pg);
+			dinfo->cursor_base_real =
+						virt_to_phys(page_address(pg));
+
+			/*
+			 * XXX Add code to insert GTT entry and map the
+			 * appropriate part of the aperture as
+			 * dinfo->cursor_base.
+			 */
+		}
+#endif
+	}
+	DBG_MSG("fb: 0x%x(+ 0x%x)/0x%x (0x%x)\n",
+		dinfo->fb_base_phys, dinfo->fb_offset, dinfo->video_ram,
+		dinfo->fb_base);
+	DBG_MSG("MMIO: 0x%x/0x%x (0x%x)\n",
+		dinfo->mmio_base_phys, INTEL_REG_SIZE, dinfo->mmio_base);
+	DBG_MSG("ring buffer: 0x%x/0x%x (0x%x)\n",
+		dinfo->ring_base_phys, dinfo->ring_size, dinfo->ring_base);
+	DBG_MSG("HW cursor: 0x%x/0x%x (0x%x) (offset 0x%x) (phys 0x%x)\n",
+		dinfo->cursor_base_phys, dinfo->cursor_size,
+		dinfo->cursor_base, dinfo->cursor_offset,
+		dinfo->cursor_base_real);
+
+	DBG_MSG("options: accel = %d, fixed = %d, noinit = %d\n",
+		accel, fixed, noinit);
+	DBG_MSG("options: mode = \"%s\", font = \"%s\"\n",
+		mode ? mode : "", font ? font : "");
+
+	if (probeonly)
+		bailout(dinfo);
+
+	dinfo->fixed_mode = fixed;
+
+	/*
+	 * Check if the LVDS port or any DVO ports are enabled.  If so,
+	 * don't allow mode switching.
+	 */
+	if ((s = intelfbhw_check_non_crt(dinfo))) {
+		WRN_MSG("Non-CRT device is enabled (%s).  "
+			"Disabling mode switching.\n", s);
+		dinfo->fixed_mode = 1;
+	}
+
+	if (bailearly == 1)
+		bailout(dinfo);
+
+	if (dinfo->fixed_mode && ORIG_VIDEO_ISVGA != VIDEO_TYPE_VLFB) {
+		ERR_MSG("Video mode must be programmed at boot time.\n");
+		cleanup(dinfo);
+		return -ENODEV;
+	}
+
+	if (bailearly == 2)
+		bailout(dinfo);
+
+	/* Initialise dinfo and related data. */
+	/* If an initial mode was programmed at boot time, get its details. */
+	if (ORIG_VIDEO_ISVGA == VIDEO_TYPE_VLFB)
+		get_initial_mode(dinfo);
+
+	if (bailearly == 3)
+		bailout(dinfo);
+
+#if 0
+	if (dinfo->fixed_mode) {
+		/*
+		 * XXX Check video ram amounts and remap if the region already
+		 * mapped is too small because of ring/cursor allocations.
+		 */
+		update_dinfo(dinfo, &dinfo->initial_var, NULL);
+	}
+#endif
+
+	/* currcon will be set by the first switch. */
+	dinfo->currcon = -1;
+	dinfo->vc_mode = KD_TEXT;
+
+#if USE_SYNC_PAGE
+	if (dinfo->accel) {
+		struct page *pg;
+
+		/*
+		 * Allocate a page of physical memory for the graphics
+		 * engine to write to for synchronisation purposes.
+		 */
+		pg = alloc_page(GFP_KERNEL);
+		if (!pg) {
+			WRN_MSG("Cannot allocate a page for 2D accel.  "
+				"Disabling acceleration.");
+			dinfo->accel = 0;
+		} else {
+			get_page(pg);
+			LockPage(pg);
+			dinfo->syncpage_virt = (u32)page_address(pg);
+			dinfo->syncpage_phys = virt_to_phys(page_address(pg));
+			/* Write zero to the first dword */
+			writel(0, dinfo->syncpage_virt);
+			DBG_MSG("2D sync page: 0x%x (0x%x)\n",
+				dinfo->syncpage_phys, dinfo->syncpage_virt);
+		}
+	}
+#endif
+
+	if (bailearly == 4)
+		bailout(dinfo);
+
+
+	if (intelfb_set_fbinfo(dinfo)) {
+		cleanup(dinfo);
+		return -ENODEV;
+	}
+
+	if (bailearly == 5)
+		bailout(dinfo);
+
+	for (i = 0; i < 16; i++) {
+		j = color_table[i];
+		dinfo->palette[i].red = default_red[j];
+		dinfo->palette[i].green = default_grn[j];
+		dinfo->palette[i].blue = default_blu[j];
+	}
+
+	if (bailearly == 6)
+		bailout(dinfo);
+
+	pci_set_drvdata(pdev, dinfo);
+
+	/* Save the initial register state. */
+	i = intelfbhw_read_hw_state(dinfo, &dinfo->save_state,
+				    bailearly > 6 ? bailearly - 6 : 0);
+	if (i != 0) {
+		DBG_MSG("intelfbhw_read_hw_state returned %d\n", i);
+		bailout(dinfo);
+	}
+
+	intelfbhw_print_hw_state(dinfo, &dinfo->save_state);
+
+	if (bailearly == 18)
+		bailout(dinfo);
+
+#if TEST_MODE_TO_HW
+	{
+		struct intelfb_hwstate hw;
+		struct fb_var_screeninfo var;
+		int i;
+
+		for (i = 0; i < num_modes; i++) {
+			mode_to_var(&modedb[i], &var, 8);
+			intelfbhw_read_hw_state(dinfo, &hw, 0);
+			if (intelfbhw_mode_to_hw(dinfo, &hw, &var)) {
+				DGB_MSG("Failed to set hw for mode %dx%d\n",
+					var.xres, var.yres);
+			} else {
+				DGB_MSG("HW state for mode %dx%d\n",
+					var.xres, var.yres);
+				intelfbhw_print_hw_state(dinfo, &hw);
+			}
+		}
+	}
+#endif
+
+	/* Cursor initialisation */
+	if (dinfo->hwcursor) {
+		init_timer(&dinfo->cursor.timer);
+		dinfo->cursor.timer.function = intelfb_flashcursor;
+		dinfo->cursor.timer.data = (unsigned long)dinfo;
+		dinfo->cursor.state = CM_ERASE;
+		spin_lock_init(&dinfo->DAClock);
+	}
+	
+	if (bailearly == 19)
+		bailout(dinfo);
+
+
+	if (noregister)
+		bailout(dinfo);
+
+	if (register_framebuffer(&(dinfo->info)) < 0) {
+		ERR_MSG("Cannot register framebuffer.\n");
+		cleanup(dinfo);
+		return -ENODEV;
+	}
+
+	dinfo->registered = 1;
+
+	return 0;
+}
+
+static void __devexit
+intelfb_pci_unregister(struct pci_dev *pdev)
+{
+	struct intelfb_info *dinfo = pci_get_drvdata(pdev);
+
+	DBG_MSG("intelfb_pci_unregister\n");
+
+	if (!dinfo)
+		return;
+
+	cleanup(dinfo);
+
+	pci_set_drvdata(pdev, NULL);
+}
+
+/*
+ * A simplified version of fb_find_mode.  The latter doesn't seem to work
+ * too well -- haven't figured out why yet.
+ */
+static int
+intelfb_find_mode(struct fb_var_screeninfo *var,
+		  struct fb_info *info, const char *mode_option,
+		  const struct fb_videomode *db, unsigned int dbsize,
+		  const struct fb_videomode *default_mode,
+		  unsigned int default_bpp)
+{
+	int i;
+	char mname[20] = "", tmp[20] = "", *p, *q;
+	unsigned int bpp = 0;
+
+	DBG_MSG("intelfb_find_mode\n");
+
+	/* Set up defaults */
+	if (!db) {
+		db = modedb;
+		dbsize = sizeof(modedb) / sizeof(*modedb);
+	}
+
+	if (!default_bpp)
+#if defined(FBCON_HAS_CFB8)
+		default_bpp = 8;
+#elif defined(FBCON_HAS_CFB16)
+		default_bpp = 16;
+#elif defined(FBCON_HAS_CFB32)
+		default_bpp = 32;
+#endif
+
+	var->activate = FB_ACTIVATE_TEST;
+	if (mode_option && *mode_option) {
+		if (strlen(mode_option) < sizeof(tmp) - 1) {
+			strcat(tmp, mode_option);
+			q = tmp;
+			p = strsep(&q, "-");
+			strcat(mname, p);
+			if (q) {
+				p = strsep(&q, "@");
+				bpp = simple_strtoul(p, NULL, 10);
+				if (q) {
+					strcat(mname, "@");
+					strcat(mname, q);
+				}
+			}
+		}
+		if (!bpp)
+			bpp = default_bpp;
+		DBG_MSG("Mode is %s, bpp %d\n", mname, bpp);
+	}
+	if (*mname) {
+		for (i = 0; i < dbsize; i++) {
+			if (!strncmp(db[i].name, mname, strlen(mname))) {
+				mode_to_var(&db[i], var, bpp);
+				if (!intelfb_set_var(var, -1, info))
+					return 1;
+			}
+		}
+	}
+
+	if (!default_mode)
+		return 0;
+
+	mode_to_var(default_mode, var, default_bpp);
+	if (!intelfb_set_var(var, -1, info))
+		return 3;
+
+	for (i = 0; i < dbsize; i++) {
+		mode_to_var(&db[i], var, default_bpp);
+		if (!intelfb_set_var(var, -1, info))
+			return 4;
+	}
+
+	return 0;
+}
+
+static __inline__ int
+var_to_refresh(const struct fb_var_screeninfo *var)
+{
+	int xtot = var->xres + var->left_margin + var->right_margin +
+		   var->hsync_len;
+	int ytot = var->yres + var->upper_margin + var->lower_margin +
+		   var->vsync_len;
+
+	return (1000000000 / var->pixclock * 1000 + 500) / xtot / ytot;
+}
+
+/* Various intialisation functions */
+
+static int __devinit
+intelfb_init_disp_var(struct intelfb_info *dinfo)
+{
+	int msrc = 0;
+
+	DBG_MSG("intelfb_init_disp_var\n");
+
+	if (dinfo->fixed_mode) {
+		dinfo->disp.var = dinfo->initial_var;
+		msrc = 5;
+	} else {
+		if (mode) {
+			msrc = intelfb_find_mode(&dinfo->disp.var,
+						 &dinfo->info, mode,
+						 modedb, num_modes, NULL, 0);
+			if (msrc)
+				msrc |= 8;
+		}
+		if (!msrc) {
+			msrc = intelfb_find_mode(&dinfo->disp.var,
+						 &dinfo->info, PREFERRED_MODE,
+						 modedb, num_modes,
+						 &modedb[DFLT_MODE], 0);
+		}
+	}
+
+	if (!msrc) {
+		ERR_MSG("Cannot find a suitable video mode.\n");
+		return 1;
+	}
+
+	INF_MSG("Initial video mode is %dx%d-%d@%d.\n", dinfo->disp.var.xres,
+		dinfo->disp.var.yres, intelfb_var_to_depth(&dinfo->disp.var),
+		var_to_refresh(&dinfo->disp.var));
+
+	DBG_MSG("Initial video mode is from %d.\n", msrc);
+
+	if (dinfo->accel)
+		dinfo->disp.var.accel_flags |= FB_ACCELF_TEXT;
+	else
+		dinfo->disp.var.accel_flags &= ~FB_ACCELF_TEXT;
+
+	return 0;
+}
+
+static void
+intelfb_set_dispsw(struct intelfb_info *dinfo, struct display *disp)
+{
+	DBG_MSG("intelfb_set_dispsw: (bpp is %d)\n", disp->var.bits_per_pixel);
+
+	disp->dispsw_data = NULL;
+	disp->dispsw = NULL;
+
+	switch (disp->var.bits_per_pixel) {
+#ifdef FBCON_HAS_CFB8
+	case 8:
+		break;
+#endif
+#ifdef FBCON_HAS_CFB16
+	case 16:
+		disp->dispsw_data = &dinfo->con_cmap.cfb16;
+		break;
+#endif
+#ifdef FBCON_HAS_CFB32
+	case 32:
+		disp->dispsw_data = &dinfo->con_cmap.cfb32;
+		break;
+#endif
+	default:
+		WRN_MSG("Setting fbcon_dummy renderer (bpp is %d).\n",
+			disp->var.bits_per_pixel);
+		disp->dispsw = &fbcon_dummy;
+	}
+	/* For all supported bpp. */
+	if (!disp->dispsw)
+		disp->dispsw = &fbcon_intelfb;
+}
+
+static int __devinit
+intelfb_init_disp(struct intelfb_info *dinfo)
+{
+	struct fb_info *info;
+	struct display *disp;
+
+	DBG_MSG("intelfb_init_disp\n");
+
+	info = &dinfo->info;
+	disp = &dinfo->disp;
+
+	if (intelfb_init_disp_var(dinfo))
+		return 1;
+
+	info->disp = disp;
+
+	update_dinfo(dinfo, &disp->var, disp);
+
+	intelfb_set_dispsw(dinfo, disp);
+
+	dinfo->currcon_display = disp;
+
+	return 0;
+}
+
+static int __devinit
+intelfb_set_fbinfo(struct intelfb_info *dinfo)
+{
+	struct fb_info *info;
+
+	DBG_MSG("intelfb_set_fbinfo\n");
+
+	info = &dinfo->info;
+
+	strcpy(info->modename, dinfo->name);
+	info->node = NODEV;
+	info->flags = FBINFO_FLAG_DEFAULT;
+	info->fbops = &intel_fb_ops;
+	info->display_fg = NULL;
+	info->changevar = NULL;
+	info->switch_con = intelfb_switch;
+	info->updatevar = intelfb_updatevar;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+	info->blank = intelfbhw_do_blank;
+#endif
+
+	if (intelfb_init_disp(dinfo))
+		return 1;
+	return 0;
+}
+
+/* Update dinfo to match the active video mode. */
+static void
+update_dinfo(struct intelfb_info *dinfo, struct fb_var_screeninfo *var,
+	     struct display *disp)
+{
+	DBG_MSG("update_dinfo\n");
+
+	dinfo->bpp = var->bits_per_pixel;
+	dinfo->depth = intelfb_var_to_depth(var);
+	dinfo->xres = var->xres;
+	dinfo->yres = var->xres;
+	dinfo->pixclock = var->pixclock;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
+	intelfb_get_fix(&dinfo->info.fix, dinfo->currcon, &dinfo->info);
+#endif
+
+	switch (dinfo->bpp) {
+#ifdef FBCON_HAS_CFB8
+	case 8:
+		dinfo->visual = FB_VISUAL_PSEUDOCOLOR;
+		dinfo->pitch = disp->var.xres_virtual;
+		break;
+#endif
+#ifdef FBCON_HAS_CFB16
+	case 16:
+		dinfo->visual = FB_VISUAL_TRUECOLOR;
+		dinfo->pitch = disp->var.xres_virtual * 2;
+		break;
+#endif
+#ifdef FBCON_HAS_CFB32
+	case 32:
+		dinfo->visual = FB_VISUAL_TRUECOLOR;
+		dinfo->pitch = disp->var.xres_virtual * 4;
+		break;
+#endif
+	}
+
+	/* Make sure the line length is a aligned correctly. */
+	dinfo->pitch = ROUND_UP_TO(dinfo->pitch, STRIDE_ALIGNMENT);
+
+	if (dinfo->fixed_mode)
+		dinfo->pitch = dinfo->initial_pitch;
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+	disp->screen_base = (char *)dinfo->fb_base;
+	disp->visual = dinfo->visual;
+	disp->line_length = dinfo->pitch;
+	disp->type = FB_TYPE_PACKED_PIXELS;
+	disp->type_aux = 0;
+	disp->ypanstep = 1;
+	disp->ywrapstep = 0;
+#else
+	dinfo->info.screen_base = (char *)dinfo->fb_base;
+	dinfo->info.fix.line_length = dinfo->pitch;
+	dinfo->info.fix.visual = dinfo->visual;
+#endif
+	disp->can_soft_blank = 1;
+	disp->inverse = 0;
+	DBG_MSG("disp->scrollmode is 0x%x\n", disp->scrollmode);
+	if (disp->var.yres_virtual == disp->var.yres &&
+	    !(disp->var.accel_flags & FB_ACCELF_TEXT)) {
+		/* No space for panning. */
+		disp->scrollmode = SCROLL_YREDRAW;
+	} else {
+		disp->scrollmode = __SCROLL_YMOVE;
+	}
+}
+
+/* fbops functions */
+
+static int
+intelfb_get_fix(struct fb_fix_screeninfo *fix, int con, struct fb_info *info)
+{
+	struct intelfb_info *dinfo = GET_DINFO(info);
+	struct display *disp;
+
+	DBG_MSG("intelfb_get_fix\n");
+
+	disp = GET_DISP(info, con);
+
+	memset(fix, 0, sizeof(*fix));
+	strcpy(fix->id, dinfo->name);
+	fix->smem_start = dinfo->fb_base_phys;
+	fix->smem_len = dinfo->video_ram;
+	fix->type = FB_TYPE_PACKED_PIXELS;
+	fix->type_aux = 0;
+	fix->visual = dinfo->visual;
+	fix->xpanstep = 8;
+	fix->ypanstep = 1;
+	fix->ywrapstep = 0;
+	fix->line_length = dinfo->pitch;
+	fix->mmio_start = dinfo->mmio_base_phys;
+	fix->mmio_len = INTEL_REG_SIZE;
+	fix->accel = FB_ACCEL_NONE;
+	return 0;
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+static int
+intelfb_get_var(struct fb_var_screeninfo *var, int con, struct fb_info *info)
+{
+	struct intelfb_info *dinfo = GET_DINFO(info);
+
+	DBG_MSG("intelfb_get_var\n");
+
+	if (con == -1)
+		*var = dinfo->disp.var;
+	else
+		*var = fb_display[con].var;
+
+	return 0;
+}
+#endif
+
+static int
+intelfb_set_var(struct fb_var_screeninfo *var, int con, struct fb_info *info)
+{
+	struct display *disp;
+	int change_var = 0;
+	struct fb_var_screeninfo v;
+	struct intelfb_info *dinfo;
+	int ret;
+	static int first = 1;
+
+	DBG_MSG("intelfb_set_var: con is %d, accel_flags is %d\n",
+		con, var->accel_flags);
+
+	dinfo = GET_DINFO(info);
+	disp = GET_DISP(info, con);
+
+	if (intelfbhw_validate_mode(dinfo, con, var) != 0)
+		return -EINVAL;
+	
+	memcpy(&v, var, sizeof(v));
+
+	/* Check for a supported bpp. */
+	if (v.bits_per_pixel <= 8) {
+#ifdef FBCON_HAS_CFB8
+		v.bits_per_pixel = 8;
+#else
+		return -EINVAL;
+#endif
+	} else if (v.bits_per_pixel <= 16) {
+#ifdef FBCON_HAS_CFB16
+		if (v.bits_per_pixel == 16)
+			v.green.length = 6;
+		v.bits_per_pixel = 16;
+#else
+		return -EINVAL;
+#endif
+	} else if (v.bits_per_pixel <= 32) {
+#ifdef FBCON_HAS_CFB32
+		v.bits_per_pixel = 32;
+#else
+		return -EINVAL;
+#endif
+	} else
+		return -EINVAL;
+
+	if (con < 0) {
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
+		info->var = *var;
+#endif
+		return 0;
+	}
+
+
+	change_var = ((disp->var.xres != var->xres) ||
+		      (disp->var.yres != var->yres) ||
+		      (disp->var.xres_virtual != var->xres_virtual) ||
+		      (disp->var.yres_virtual != var->yres_virtual) ||
+		      (disp->var.bits_per_pixel != var->bits_per_pixel) ||
+		      memcmp(&disp->var.red, &var->red, sizeof(var->red)) ||
+		      memcmp(&disp->var.green, &var->green,
+			     sizeof(var->green)) ||
+		      memcmp(&disp->var.blue, &var->blue, sizeof(var->blue)));
+
+	if (dinfo->fixed_mode &&
+	    (change_var ||
+	     var->yres_virtual > dinfo->initial_var.yres_virtual ||
+	     var->yres_virtual < dinfo->initial_var.yres ||
+	     var->xoffset || var->nonstd)) {
+		if (first) {
+			ERR_MSG("Changing the video mode is not supported.\n");
+			first = 0;
+		}
+		return -EINVAL;
+	}
+
+	if ((var->activate & FB_ACTIVATE_MASK) != FB_ACTIVATE_NOW)
+		return 0;
+
+	switch (intelfb_var_to_depth(&v)) {
+#ifdef FBCON_HAS_CFB8
+	case 8:
+		v.red.offset = v.green.offset = v.blue.offset = 0;
+		v.red.length = v.green.length = v.blue.length = 8;
+		v.transp.offset = v.transp.length = 0;
+		break;
+#endif
+#ifdef FBCON_HAS_CFB16
+	case 15:
+		v.red.offset = 10;
+		v.green.offset = 5;
+		v.blue.offset = 0;
+		v.red.length = v.green.length = v.blue.length = 5;
+		v.transp.offset = v.transp.length = 0;
+		break;
+	case 16:
+		v.red.offset = 11;
+		v.green.offset = 5;
+		v.blue.offset = 0;
+		v.red.length = 5;
+		v.green.length = 6;
+		v.blue.length = 5;
+		v.transp.offset = v.transp.length = 0;
+		break;
+#endif
+#ifdef FBCON_HAS_CFB32
+	case 24:
+		v.red.offset = 16;
+		v.green.offset = 8;
+		v.blue.offset = 0;
+		v.red.length = v.green.length = v.blue.length = 8;
+		v.transp.offset = v.transp.length = 0;
+		break;
+	case 32:
+		v.red.offset = 16;
+		v.green.offset = 8;
+		v.blue.offset = 0;
+		v.red.length = v.green.length = v.blue.length = 8;
+		v.transp.offset = 24;
+		v.transp.length = 8;
+		break;
+#endif
+	}
+
+	if (v.xoffset < 0)
+		v.xoffset = 0;
+	if (v.yoffset < 0)
+		v.yoffset = 0;
+
+	if (v.xoffset > v.xres_virtual - v.xres)
+		v.xoffset = v.xres_virtual - v.xres;
+	if (v.yoffset > v.yres_virtual - v.yres)
+		v.yoffset = v.yres_virtual - v.yres;
+
+	v.red.msb_right = v.green.msb_right = v.blue.msb_right =
+			  v.transp.msb_right = 0;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
+	info->var = v;
+#endif
+
+	memcpy(&disp->var, &v, sizeof(v));
+
+	update_dinfo(dinfo, &v, disp);
+
+	intelfb_set_dispsw(dinfo, disp);
+
+	if (change_var) {
+		if (info && info->changevar)
+			info->changevar(con);
+	}
+
+	intelfb_blank(1, info);
+
+#if 0
+	if (dinfo->hwcursor)
+		intelfbhw_cursor_hide(dinfo);
+#endif
+
+	intelfbhw_2d_stop(dinfo);
+
+	if (!dinfo->fixed_mode) {
+		mdelay(100);
+		if ((ret = intelfb_set_mode(dinfo, &v, disp, 1)))
+			return ret;
+
+		mdelay(100);
+	}
+
+	if (dinfo->vc_mode == KD_TEXT) {
+		intelfb_do_install_cmap(con, info);
+
+		if (dinfo->hwcursor) {
+			del_timer(&dinfo->cursor.timer);
+			dinfo->cursor.state = CM_ERASE;
+			if (disp->conp) {
+				intelfbhw_cursor_init(dinfo);
+				intelfbhw_cursor_load(dinfo, disp);
+				dinfo->cursor.redraw = 1;
+			}
+		}
+
+		mdelay(100);
+
+		intelfbhw_2d_start(dinfo);
+#if 0
+		if (dinfo->hwcursor)
+			intelfbhw_cursor_show(dinfo);
+#endif
+
+		if (v.yoffset != 0)
+			intelfbhw_pan_display(&v, con, info);
+	} else {
+		dinfo->cursor.enabled = 0;
+	}
+
+	intelfb_blank(0, info);
+
+	return 0;
+}
+
+static int
+intelfb_get_cmap(struct fb_cmap *cmap, int kspc, int con, struct fb_info *info)
+{
+	struct intelfb_info *dinfo = GET_DINFO(info);
+	struct display *disp = GET_DISP(info, con);
+
+	DBG_MSG("intelfb_get_cmap: con = %d, bpp = %d\n", con,
+		disp->var.bits_per_pixel);
+
+	if (con == dinfo->currcon)
+		return fb_get_cmap(cmap, kspc, intelfb_getcolreg, info);
+	else if (disp->cmap.len)
+		fb_copy_cmap(&disp->cmap, cmap, kspc ? 0 : 2);
+	else {
+		int cmap_len = (disp->var.bits_per_pixel > 8) ? 16 : 256;
+		fb_copy_cmap(fb_default_cmap(cmap_len), cmap, kspc ? 0 : 2);
+	}
+	return 0;
+}
+
+static int
+intelfb_set_cmap(struct fb_cmap *cmap, int kspc, int con, struct fb_info *info)
+{
+	struct intelfb_info *dinfo = GET_DINFO(info);
+	struct display *disp = GET_DISP(info, con);
+	unsigned int cmap_len;
+	int err;
+
+	DBG_MSG("intelfb_set_cmap: con = %d, bpp = %d\n", con,
+		disp->var.bits_per_pixel);
+
+	cmap_len = (disp->var.bits_per_pixel > 8) ? 16 : 256;
+	if (!disp->cmap.len) {
+		err = fb_alloc_cmap(&disp->cmap, cmap_len, 0);
+		if (err)
+			return err;
+	}
+	if (con == dinfo->currcon)
+		return FB_SET_CMAP(cmap, kspc, intelfb_setcolreg, info);
+	else
+		fb_copy_cmap(cmap, &disp->cmap, kspc ? 0 : 1);
+	
+	return 0;
+}
+
+/* When/if we have our own ioctls. */
+static int
+intelfb_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
+	      unsigned long arg, int con, struct fb_info *info)
+{
+	DBG_MSG("intelfb_ioctl\n");
+	return -EINVAL;
+}
+
+static int
+intelfb_switch(int con, struct fb_info *info)
+{
+	struct intelfb_info *dinfo = GET_DINFO(info);
+	struct display *disp;
+	struct fb_cmap *cmap;
+	int switchmode = 0;
+
+	DBG_MSG("intelfb_switch: con = %d, currcon = %d\n",
+		con, dinfo->currcon);
+
+	disp = GET_DISP(info, con);
+	if (dinfo->currcon >= 0) {
+		cmap = &dinfo->currcon_display->cmap;
+		if (cmap->len)
+			fb_get_cmap(cmap, 1, intelfb_getcolreg, info);
+	}
+
+	switchmode = (con != dinfo->currcon);
+
+	dinfo->currcon = con;
+	dinfo->vc_mode = con >= 0 ? vt_cons[con]->vc_mode : KD_TEXT;
+	dinfo->currcon_display = disp;
+	disp->var.activate = FB_ACTIVATE_NOW;
+
+	if (switchmode) {
+		intelfb_set_var(&disp->var, con, info);
+		intelfb_do_install_cmap(con, info);
+	}
+
+	return 0;
+}
+
+/* Called whenever there's panning. */
+static int
+intelfb_updatevar(int con, struct fb_info *info)
+{
+	struct display *disp = GET_DISP(info, con);
+
+	DBG_MSG("intelfb_updatevar\n");
+
+	if (con < 0)
+		return -EINVAL;
+	else
+		return intelfbhw_pan_display(&disp->var, con, info);
+}
+
+static int
+intelfb_blank(int blank, struct fb_info *info)
+{
+	intelfbhw_do_blank(blank, info);
+	return 0;
+}
+
+static int
+intelfb_getcolreg(unsigned regno, unsigned *red, unsigned *green,
+		  unsigned *blue, unsigned *transp, struct fb_info *info)
+{
+	struct intelfb_info *dinfo = GET_DINFO(info);
+
+#if 0
+	DBG_MSG("intelfb_getcolreg\n");
+#endif
+
+	if (regno > 255)
+		return 1;
+
+	*red = (dinfo->palette[regno].red<<8) | dinfo->palette[regno].red;
+	*green = (dinfo->palette[regno].green<<8) | dinfo->palette[regno].green;
+	*blue = (dinfo->palette[regno].blue<<8) | dinfo->palette[regno].blue;
+	*transp = 0;
+
+	return 0;
+}
+
+static int
+intelfb_setcolreg(unsigned regno, unsigned red, unsigned green,
+		  unsigned blue, unsigned transp, struct fb_info *info)
+{
+	struct intelfb_info *dinfo = GET_DINFO(info);
+
+#if 1
+	DBG_MSG("intelfb_setcolreg: regno %d, depth %d\n", regno, dinfo->depth);
+#endif
+
+	if ((dinfo->depth > 8 && regno > 16) || regno > 255)
+		return 1;
+
+	switch (dinfo->depth) {
+#ifdef FBCON_HAS_CFB8
+	case 8:
+		{
+			red >>= 8;
+			green >>= 8;
+			blue >>= 8;
+
+			dinfo->palette[regno].red = red;
+			dinfo->palette[regno].green = green;
+			dinfo->palette[regno].blue = blue;
+
+			intelfbhw_setcolreg(dinfo, regno, red, green, blue,
+					    transp);
+		}
+		break;
+#endif
+#ifdef FBCON_HAS_CFB16
+	case 15:
+		dinfo->con_cmap.cfb16[regno] = ((red & 0xf800) >>  1) |
+					       ((green & 0xf800) >>  6) |
+					       ((blue & 0xf800) >> 11);
+		break;
+	case 16:
+		dinfo->con_cmap.cfb16[regno] = (red & 0xf800) |
+					       ((green & 0xfc00) >>  5) |
+					       ((blue  & 0xf800) >> 11);
+		break;
+#endif
+#ifdef FBCON_HAS_CFB32
+	case 24:
+		dinfo->con_cmap.cfb32[regno] = ((red & 0xff00) << 8) |
+					       (green & 0xff00) |
+					       ((blue  & 0xff00) >> 8);
+		break;
+	}
+#endif
+	return 0;
+}
+
+/* Convert a mode to a var, also making the bpp a supported value. */
+static void
+mode_to_var(const struct fb_videomode *mode, struct fb_var_screeninfo *var,
+	    u32 bpp)
+{
+	if (!mode || !var)
+		return;
+
+	var->xres = mode->xres;
+	var->yres = mode->yres;
+	var->xres_virtual = mode->xres;
+	var->yres_virtual = mode->yres;
+	var->xoffset = 0;
+	var->yoffset = 0;
+	if (bpp <= 8)
+		var->bits_per_pixel = 8;
+	else if (bpp <= 16) {
+		if (bpp == 16)
+			var->green.length = 6;
+		var->bits_per_pixel = 16;
+	} else if (bpp <= 32)
+		var->bits_per_pixel = 32;
+	else {
+		WRN_MSG("var_to_mode: bad bpp: %d\n", bpp);
+		var->bits_per_pixel = bpp;
+	}
+	var->pixclock = mode->pixclock;
+	var->left_margin = mode->left_margin;
+	var->right_margin = mode->right_margin;
+	var->upper_margin = mode->upper_margin;
+	var->lower_margin = mode->lower_margin;
+	var->hsync_len = mode->hsync_len;
+	var->vsync_len = mode->vsync_len;
+	var->sync = mode->sync;
+	var->vmode = mode->vmode;
+	var->width = -1;
+	var->height = -1;
+}
+
+static int
+intelfb_set_mode(struct intelfb_info *dinfo, struct fb_var_screeninfo *var,
+		 struct display *disp, int blank)
+{
+	struct intelfb_hwstate hw;
+
+	DBG_MSG("intelfb_set_mode (%dx%d-%d)\n", var->xres, var->yres,
+		intelfb_var_to_depth(var));
+
+	memcpy(&hw, &dinfo->save_state, sizeof(hw));
+	if (intelfbhw_mode_to_hw(dinfo, &hw, var))
+		return -EINVAL;
+	intelfbhw_print_hw_state(dinfo, &hw);
+	if (intelfbhw_program_mode(dinfo, &hw, blank))
+		return -EINVAL;
+
+	update_dinfo(dinfo, var, disp);
+
+	return 0;
+}
+
+static void
+intelfb_do_install_cmap(int con, struct fb_info *info)
+{
+	struct intelfb_info *dinfo = GET_DINFO(info);
+	struct display *disp;
+
+	if (con != dinfo->currcon)
+		return;
+
+	disp = GET_DISP(info, con);
+	if (disp->cmap.len)
+		FB_SET_CMAP(&disp->cmap, 1, intelfb_setcolreg, info);
+	else {
+		int size = (disp->var.bits_per_pixel > 8) ? 16 : 256;
+		FB_SET_CMAP(fb_default_cmap(size), 1, intelfb_setcolreg, info);
+	}
+}
+
+static void
+check_vc_mode(struct intelfb_info *dinfo)
+{
+#if VERBOSE > 1
+	DBG_MSG("check_vc_mode\n");
+#endif
+
+	if (dinfo->currcon >= 0) {
+		if (vt_cons[dinfo->currcon]->vc_mode != dinfo->vc_mode) {
+			dinfo->vc_mode = vt_cons[dinfo->currcon]->vc_mode;
+			if (dinfo->vc_mode == KD_GRAPHICS) {
+				DBG_MSG("vc_mode changed to KD_GRAPHICS\n");
+				intelfbhw_2d_stop(dinfo);
+				if (dinfo->cursor.enabled) {
+					intelfbhw_cursor_hide(dinfo);
+					dinfo->cursor.enabled = 1;
+				}
+			} else {
+				DBG_MSG("vc_mode changed to KD_TEXT\n");
+				intelfbhw_2d_start(dinfo);
+#if 0
+				if (dinfo->cursor.enabled)
+					intelfbhw_cursor_show(dinfo);
+#endif
+			}
+		}
+	}
+}
+
+static void
+fbcon_intelfb_setup(struct display *p)
+{
+	struct intelfb_info *dinfo = GET_DINFO(p->fb_info);
+
+	DBG_MSG("fbcon_intelfb_setup: accel: %d bpp: %d\n",
+		TEXT_ACCEL(dinfo, &p->var), p->var.bits_per_pixel);
+
+	switch (p->var.bits_per_pixel) {
+#ifdef FBCON_HAS_CFB8
+	case 8:
+		fbcon_cfb8.setup(p);
+		break;
+#endif
+#ifdef FBCON_HAS_CFB16
+	case 16:
+		fbcon_cfb16.setup(p);
+		break;
+#endif
+#ifdef FBCON_HAS_CFB32
+	case 32:
+		fbcon_cfb32.setup(p);
+		break;
+#endif
+	}
+}
+
+static void
+fbcon_intelfb_bmove(struct display *p, int sy, int sx, int dy, int dx,
+		    int height, int width)
+{
+	struct intelfb_info *dinfo = GET_DINFO(p->fb_info);
+
+#if VERBOSE > 0
+	DBG_MSG("fbcon_intelfb_bmove: accel: %d, bpp: %d\n",
+		TEXT_ACCEL(dinfo, &p->var), p->var.bits_per_pixel);
+#endif
+
+	if (TEXT_ACCEL(dinfo, &p->var)) {
+		intelfbhw_do_bitblt(dinfo, fontwidth(p) * sx,
+				    fontheight(p) * sy,
+				    fontwidth(p) * dx, fontheight(p) * dy,
+				    fontwidth(p) * width,
+				    fontheight(p) * height,
+				    dinfo->pitch, p->var.bits_per_pixel);
+		intelfbhw_do_sync(dinfo);
+		return;
+	}
+
+	/* Non-accel fallback */
+	switch (p->var.bits_per_pixel) {
+#ifdef FBCON_HAS_CFB8
+	case 8:
+		fbcon_cfb8.bmove(p, sy, sx, dy, dx, height, width);
+		break;
+#endif
+#ifdef FBCON_HAS_CFB16
+	case 16:
+		fbcon_cfb16.bmove(p, sy, sx, dy, dx, height, width);
+		break;
+#endif
+#ifdef FBCON_HAS_CFB32
+	case 32:
+		fbcon_cfb32.bmove(p, sy, sx, dy, dx, height, width);
+		break;
+#endif
+	}
+}
+
+static void
+fbcon_intelfb_clear(struct vc_data *conp, struct display *p, int sy, int sx,
+		    int height, int width)
+{
+	struct intelfb_info *dinfo = GET_DINFO(p->fb_info);
+
+#if VERBOSE > 0
+	DBG_MSG("fbcon_intelfb_clear: accel: %d, bpp: %d\n",
+		TEXT_ACCEL(dinfo, &p->var), p->var.bits_per_pixel);
+#endif
+
+	if (TEXT_ACCEL(dinfo, &p->var)) {
+		u32 bg = 0;
+
+		switch (p->var.bits_per_pixel) {
+		case 8:
+			bg = attr_bgcol_ec(p, conp);
+			break;
+		case 16:
+			bg = ((u16 *)p->dispsw_data)[attr_bgcol_ec(p, conp)];
+			break;
+		case 32:
+			bg = ((u32 *)p->dispsw_data)[attr_bgcol_ec(p, conp)];
+			break;
+		}
+		intelfbhw_do_fillrect(dinfo, fontwidth(p) * sx,
+				      fontheight(p) * sy, fontwidth(p) * width,
+				      fontheight(p) * height, bg, dinfo->pitch,
+				      p->var.bits_per_pixel, PAT_ROP_GXCOPY);
+		intelfbhw_do_sync(dinfo);
+		return;
+	}
+
+	/* Non-accel fallback */
+	switch (p->var.bits_per_pixel) {
+#ifdef FBCON_HAS_CFB8
+	case 8:
+		fbcon_cfb8.clear(conp, p, sy, sx, height, width);
+		break;
+#endif
+#ifdef FBCON_HAS_CFB16
+	case 16:
+		fbcon_cfb16.clear(conp, p, sy, sx, height, width);
+		break;
+#endif
+#ifdef FBCON_HAS_CFB32
+	case 32:
+		fbcon_cfb32.clear(conp, p, sy, sx, height, width);
+		break;
+#endif
+	}
+}
+
+static void
+fbcon_intelfb_putc(struct vc_data *conp, struct display *p, int c,
+		   int yy, int xx)
+{
+	struct intelfb_info *dinfo = GET_DINFO(p->fb_info);
+
+#if VERBOSE > 0
+	DBG_MSG("fbcon_intelfb_putc: accel: %d, bpp: %d\n",
+		TEXT_ACCEL(dinfo, &p->var), p->var.bits_per_pixel);
+#endif
+
+	/* intelfbhw_do_drawglyph() has problems with the 852GM/855GM */
+	if (TEXT_ACCEL(dinfo, &p->var) && USE_DRAWGLYPH(dinfo)) {
+		u32 bg = 0, fg = 0;
+		u8 *cdat;
+		int fw;
+
+		switch (p->var.bits_per_pixel) {
+		case 8:
+			bg = attr_bgcol(p, c);
+			fg = attr_fgcol(p, c);
+			break;
+		case 16:
+			bg = ((u16 *)p->dispsw_data)[attr_bgcol(p, c)];
+			fg = ((u16 *)p->dispsw_data)[attr_fgcol(p, c)];
+			break;
+		case 32:
+			bg = ((u32 *)p->dispsw_data)[attr_bgcol(p, c)];
+			fg = ((u32 *)p->dispsw_data)[attr_fgcol(p, c)];
+			break;
+		}
+
+		fw = ROUND_UP_TO(fontwidth(p), 8) / 8;
+		cdat = p->fontdata + (c & p->charmask) * fontheight(p) * fw;
+
+		if (intelfbhw_do_drawglyph(dinfo, fg, bg, fontwidth(p),
+					   fontheight(p), cdat,
+					   xx * fontwidth(p),
+					   yy * fontheight(p), dinfo->pitch,
+					   p->var.bits_per_pixel)) {
+			intelfbhw_do_sync(dinfo);
+			return;
+		}
+	}
+
+	if (TEXT_ACCEL(dinfo, &p->var))
+		intelfbhw_do_sync(dinfo);
+
+	/* Non-accel fallback */
+	switch (p->var.bits_per_pixel) {
+#ifdef FBCON_HAS_CFB8
+	case 8:
+		fbcon_cfb8.putc(conp, p, c, yy, xx);
+		break;
+#endif
+#ifdef FBCON_HAS_CFB16
+	case 16:
+		fbcon_cfb16.putc(conp, p, c, yy, xx);
+		break;
+#endif
+#ifdef FBCON_HAS_CFB32
+	case 32:
+		fbcon_cfb32.putc(conp, p, c, yy, xx);
+		break;
+#endif
+	}
+}
+
+static void
+fbcon_intelfb_putcs(struct vc_data *conp, struct display *p,
+		    const unsigned short *s, int count, int yy, int xx)
+{
+	struct intelfb_info *dinfo = GET_DINFO(p->fb_info);
+
+#if VERBOSE > 0
+	DBG_MSG("fbcon_intelfb_putcs: accel: %d, bpp: %d, (%d,%d) %d\n",
+		TEXT_ACCEL(dinfo, &p->var), p->var.bits_per_pixel,
+		xx, yy, count);
+#endif
+
+	if (TEXT_ACCEL(dinfo, &p->var) && USE_DRAWGLYPH(dinfo)) {
+		u32 bg = 0, fg = 0;
+		u8 *cdat;
+		int fw;
+		u16 c = scr_readw(s);
+
+		switch (p->var.bits_per_pixel) {
+		case 8:
+			bg = attr_bgcol(p, c);
+			fg = attr_fgcol(p, c);
+			break;
+		case 16:
+			bg = ((u16 *)p->dispsw_data)[attr_bgcol(p, c)];
+			fg = ((u16 *)p->dispsw_data)[attr_fgcol(p, c)];
+			break;
+		case 32:
+			bg = ((u32 *)p->dispsw_data)[attr_bgcol(p, c)];
+			fg = ((u32 *)p->dispsw_data)[attr_fgcol(p, c)];
+			break;
+		}
+		fw = ROUND_UP_TO(fontwidth(p), 8) / 8;
+		while (count--) {
+			cdat = p->fontdata + (scr_readw(s++) & p->charmask) *
+			       fontheight(p) * fw;
+
+			intelfbhw_do_drawglyph(dinfo, fg, bg, fontwidth(p),
+					       fontheight(p), cdat,
+					       xx * fontwidth(p),
+					       yy * fontheight(p),
+					       dinfo->pitch,
+					       p->var.bits_per_pixel);
+			xx++;
+		}
+		intelfbhw_do_sync(dinfo);
+		return;
+	}
+
+	if (TEXT_ACCEL(dinfo, &p->var))
+		intelfbhw_do_sync(dinfo);
+
+	/* Non-accel fallback */
+	switch (p->var.bits_per_pixel) {
+#ifdef FBCON_HAS_CFB8
+	case 8:
+		fbcon_cfb8.putcs(conp, p, s, count, yy, xx);
+		break;
+#endif
+#ifdef FBCON_HAS_CFB16
+	case 16:
+		fbcon_cfb16.putcs(conp, p, s, count, yy, xx);
+		break;
+#endif
+#ifdef FBCON_HAS_CFB32
+	case 32:
+		fbcon_cfb32.putcs(conp, p, s, count, yy, xx);
+		break;
+#endif
+	}
+}
+
+static void
+fbcon_intelfb_revc(struct display *p, int xx, int yy)
+{
+	struct intelfb_info *dinfo = GET_DINFO(p->fb_info);
+
+#if VERBOSE > 0
+	DBG_MSG("fbcon_intelfb_revc: accel: %d, bpp: %d\n",
+		TEXT_ACCEL(dinfo, &p->var), p->var.bits_per_pixel);
+#endif
+
+	if (TEXT_ACCEL(dinfo, &p->var)) {
+		int bpp = p->var.bits_per_pixel;
+		u32 xor_mask = bpp == 8 ? 0x0f : 0xffffffff;
+
+		intelfbhw_do_fillrect(dinfo, xx * fontwidth(p),
+				      yy * fontheight(p), fontwidth(p),
+				      fontheight(p), xor_mask, dinfo->pitch,
+				      bpp, PAT_ROP_GXXOR);
+		intelfbhw_do_sync(dinfo);
+		return;
+	}
+
+	/* Non-accel fallback */
+	switch (p->var.bits_per_pixel) {
+#ifdef FBCON_HAS_CFB8
+	case 8:
+		fbcon_cfb8.revc(p, xx, yy);
+		break;
+#endif
+#ifdef FBCON_HAS_CFB16
+	case 16:
+		fbcon_cfb16.revc(p, xx, yy);
+		break;
+#endif
+#ifdef FBCON_HAS_CFB32
+	case 32:
+		fbcon_cfb32.revc(p, xx, yy);
+		break;
+#endif
+	}
+}
+
+static void
+fbcon_intelfb_clear_margins(struct vc_data *conp, struct display *p,
+			    int bottom_only)
+{
+	struct intelfb_info *dinfo = GET_DINFO(p->fb_info);
+
+#if VERBOSE > 0
+	DBG_MSG("fbcon_intelfb_clear_margins: accel: %d, bpp: %d\n",
+		TEXT_ACCEL(dinfo, &p->var), p->var.bits_per_pixel);
+#endif
+
+	if (TEXT_ACCEL(dinfo, &p->var)) {
+		u32 cw, ch, rw, bh, rs, bs;
+
+		cw = fontwidth(p);
+		ch = fontheight(p);
+		rw = p->var.xres % cw;
+		bh = p->var.yres % ch;
+		rs = p->var.xres - rw;
+		bs = p->var.yres - bh;
+
+		if (!bottom_only && rw) {
+			intelfbhw_do_fillrect(dinfo, p->var.xoffset + rs, 0,
+					      rw, p->var.yres_virtual, 0,
+					      dinfo->pitch,
+					      p->var.bits_per_pixel,
+					      PAT_ROP_GXCOPY);
+		}
+		if (bh) {
+			intelfbhw_do_fillrect(dinfo, p->var.xoffset,
+					      p->var.yoffset + bs, rs, bh, 0,
+					      dinfo->pitch,
+					      p->var.bits_per_pixel,
+					      PAT_ROP_GXCOPY);
+		}
+		if ((!bottom_only && rw) || bh) {
+			intelfbhw_do_sync(dinfo);
+		}
+		return;
+	}
+
+	/* Non-accel fallback */
+	switch (p->var.bits_per_pixel) {
+#ifdef FBCON_HAS_CFB8
+	case 8:
+		fbcon_cfb8.clear_margins(conp, p, bottom_only);
+		break;
+#endif
+#ifdef FBCON_HAS_CFB16
+	case 16:
+		fbcon_cfb16.clear_margins(conp, p, bottom_only);
+		break;
+#endif
+#ifdef FBCON_HAS_CFB32
+	case 32:
+		fbcon_cfb32.clear_margins(conp, p, bottom_only);
+		break;
+#endif
+	}
+}
+
+void
+intelfb_create_cursor_shape(struct intelfb_info *dinfo, struct display *disp)
+{
+	u32 h, cu, cd;
+
+	DBG_MSG("intelfb_create_cursor_shape\n");
+
+	h = fontheight(disp);
+	cd = h;
+	if (cd >= 10)
+		cd --;
+	dinfo->cursor.type = disp->conp->vc_cursor_type & CUR_HWMASK;
+	switch (dinfo->cursor.type) {
+	case CUR_NONE:
+		cu = cd;
+		break;
+	case CUR_UNDERLINE:
+		cu = cd - 2;
+		break;
+	case CUR_LOWER_THIRD:
+		cu = (h * 2) / 3;
+		break;
+	case CUR_LOWER_HALF:
+		cu = h / 2;
+		break;
+	case CUR_TWO_THIRDS:
+		cu = h / 3;
+		break;
+	case CUR_BLOCK:
+	default:
+		cu = 0;
+		cd = h;
+		break;
+	}
+	dinfo->cursor.w = fontwidth(disp);
+	dinfo->cursor.u = cu;
+	dinfo->cursor.d = cd;
+}
+
+static void
+intelfb_flashcursor(unsigned long ptr)
+{
+	struct intelfb_info *dinfo = (struct intelfb_info *)ptr;
+	unsigned long flags;
+
+#if VERBOSE > 2
+	DBG_MSG("intelfb_flashcursor\n");
+#endif
+
+	spin_lock_irqsave(&dinfo->DAClock, flags);
+	if (dinfo->cursor.enabled) {
+		if (dinfo->cursor.on)
+			intelfbhw_cursor_hide(dinfo);
+		else
+			intelfbhw_cursor_show(dinfo);
+		dinfo->cursor.enabled = 1;
+	}
+	dinfo->cursor.timer.expires = jiffies + HZ / 2;
+	add_timer(&dinfo->cursor.timer);
+	spin_unlock_irqrestore(&dinfo->DAClock, flags);
+}
+
+static void
+fbcon_intelfb_cursor(struct display *disp, int mode, int x, int y)
+{
+	unsigned long flags = 0;
+	struct intelfb_info *dinfo = GET_DINFO(disp->fb_info);
+
+#if VERBOSE > 0
+	DBG_MSG("fbcon_intelfb_cursor, mode is %d (%d)\n",
+		mode, dinfo->cursor.state);
+#endif
+
+	/*
+	 * This appears to be the first place that we can detect changes
+	 * in vc_mode.  We need to know this so that acceleration can be
+	 * enabled or disabled so that it won't interfere with an XFree86
+	 * server.
+	 */
+	check_vc_mode(dinfo);
+
+	if (!dinfo->hwcursor) {
+		disp->dispsw->cursor = NULL;
+		fb_con.con_cursor(disp->conp, mode);
+		disp->dispsw->cursor = fbcon_intelfb_cursor;
+		return;
+	}
+
+	if (mode == CM_ERASE) {
+		if (dinfo->cursor.state != CM_ERASE) {
+			spin_lock_irqsave(&dinfo->DAClock, flags);
+			dinfo->cursor.state = CM_ERASE;
+			intelfbhw_cursor_hide(dinfo);
+			del_timer(&dinfo->cursor.timer);
+			spin_unlock_irqrestore(&dinfo->DAClock, flags);
+		}
+		return;
+	}
+	if ((disp->conp->vc_cursor_type & CUR_HWMASK) != dinfo->cursor.type)
+		intelfbhw_cursor_load(dinfo, disp);
+	x *= fontwidth(disp);
+	y *= fontheight(disp);
+	y -= disp->var.yoffset;
+	x -= disp->var.xoffset;
+	spin_lock_irqsave(&dinfo->DAClock, flags);
+	if (x != dinfo->cursor.x || y != dinfo->cursor.y ||
+	    dinfo->cursor.redraw) {
+#if 1
+		intelfbhw_cursor_hide(dinfo);
+#endif
+		intelfbhw_cursor_setcolor(dinfo, 0, 0xffffff);
+		intelfbhw_cursor_setpos(dinfo, x, y);
+	}
+	dinfo->cursor.state = CM_DRAW;
+	mod_timer(&dinfo->cursor.timer, jiffies + HZ / 2);
+	intelfbhw_cursor_show(dinfo);
+	spin_unlock_irqrestore(&dinfo->DAClock, flags);
+}
+
diff -urN linux-2.4.21-pre5/drivers/video/intel/intelfbhw.c linux-2.4.21-pre6/drivers/video/intel/intelfbhw.c
--- linux-2.4.21-pre5/drivers/video/intel/intelfbhw.c	Wed Dec 31 16:00:00 1969
+++ linux-2.4.21-pre6/drivers/video/intel/intelfbhw.c	Wed Mar 26 16:11:39 2003
@@ -0,0 +1,1778 @@
+/*
+ * intelfb
+ *
+ * Linux framebuffer driver for Intel(R) 865G integrated graphics chips.
+ *
+ * Copyright (C) 2002, 2003 David Dawes <dawes@tungstengraphics.com>
+ *
+ * This driver consists of two parts.  The first part (intelfbdrv.c) provides
+ * the basic fbdev interfaces, is derived in part from the radeonfb and
+ * vesafb drivers, and is covered by the GPL.  The second part (intelfbhw.c)
+ * provides the code to program the hardware.  Most of it is derived from
+ * the i810/i830 XFree86 driver.  The HW-specific code is covered here
+ * under a dual license (GPL and MIT/XFree86 license).
+ *
+ * Author: David Dawes
+ *
+ */
+
+/* $DHD: intelfb/intelfbhw.c,v 1.7 2003/02/06 00:53:11 dawes Exp $ */
+/* $TG$ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/tty.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/fb.h>
+#include <linux/console.h>
+#include <linux/selection.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/vmalloc.h>
+#include <linux/kd.h>
+#include <linux/vt_kern.h>
+#include <linux/pagemap.h>
+#include <linux/version.h>
+
+#include <asm/io.h>
+
+#include <video/fbcon.h>
+#include <video/fbcon-cfb8.h>
+#include <video/fbcon-cfb16.h>
+#include <video/fbcon-cfb32.h>
+
+#include "intelfb.h"
+#include "intelfbhw.h"
+
+int
+intelfbhw_get_chipset(struct pci_dev *pdev, const char **name, int *chipset,
+		      int *mobile)
+{
+	u32 tmp;
+
+	if (!pdev || !name || !chipset || !mobile)
+		return 1;
+
+	switch (pdev->device) {
+	case PCI_DEVICE_ID_INTEL_830M:
+		*name = "Intel(R) 830M";
+		*chipset = INTEL_830M;
+		*mobile = 1;
+		return 0;
+	case PCI_DEVICE_ID_INTEL_845G:
+		*name = "Intel(R) 845G";
+		*chipset = INTEL_845G;
+		*mobile = 0;
+		return 0;
+	case PCI_DEVICE_ID_INTEL_85XGM:
+		tmp = 0;
+		*mobile = 1;
+		pci_read_config_dword(pdev, INTEL_85X_CAPID, &tmp);
+		switch ((tmp >> INTEL_85X_VARIANT_SHIFT) &
+			INTEL_85X_VARIANT_MASK) {
+		case INTEL_VAR_855GME:
+			*name = "Intel(R) 855GME";
+			*chipset = INTEL_855GME;
+			return 0;
+		case INTEL_VAR_855GM:
+			*name = "Intel(R) 855GM";
+			*chipset = INTEL_855GM;
+			return 0;
+		case INTEL_VAR_852GME:
+			*name = "Intel(R) 852GME";
+			*chipset = INTEL_852GME;
+			return 0;
+		case INTEL_VAR_852GM:
+			*name = "Intel(R) 852GM";
+			*chipset = INTEL_852GM;
+			return 0;
+		default:
+			*name = "Intel(R) 852GM/855GM";
+			*chipset = INTEL_85XGM;
+			return 0;
+		}
+		break;
+	case PCI_DEVICE_ID_INTEL_865G:
+		*name = "Intel(R) 865G";
+		*chipset = INTEL_865G;
+		*mobile = 0;
+		return 0;
+	default:
+		return 1;
+	}
+}
+
+int
+intelfbhw_get_memory(struct pci_dev *pdev, int *aperture_size,
+		     int *stolen_size)
+{
+	struct pci_dev *bridge_dev;
+	u16 tmp;
+
+	if (!pdev || !aperture_size || !stolen_size)
+		return 1;
+
+	/* Find the bridge device.  It is always 0:0.0 */
+	if (!(bridge_dev = pci_find_slot(0, PCI_DEVFN(0, 0)))) {
+		ERR_MSG("cannot find bridge device\n");
+		return 1;
+	}
+
+	/* Get the fb aperture size and "stolen" memory amount. */
+	tmp = 0;
+	pci_read_config_word(bridge_dev, INTEL_GMCH_CTRL, &tmp);
+	switch (pdev->device) {
+	case PCI_DEVICE_ID_INTEL_830M:
+	case PCI_DEVICE_ID_INTEL_845G:
+		if ((tmp & INTEL_GMCH_MEM_MASK) == INTEL_GMCH_MEM_64M)
+			*aperture_size = MB(64);
+		else
+			*aperture_size = MB(128);
+		switch (tmp & INTEL_830_GMCH_GMS_MASK) {
+		case INTEL_830_GMCH_GMS_STOLEN_512:
+			*stolen_size = KB(512) - KB(132);
+			return 0;
+		case INTEL_830_GMCH_GMS_STOLEN_1024:
+			*stolen_size = MB(1) - KB(132);
+			return 0;
+		case INTEL_830_GMCH_GMS_STOLEN_8192:
+			*stolen_size = MB(8) - KB(132);
+			return 0;
+		case INTEL_830_GMCH_GMS_LOCAL:
+			ERR_MSG("only local memory found\n");
+			return 1;
+		case INTEL_830_GMCH_GMS_DISABLED:
+			ERR_MSG("video memory is disabled\n");
+			return 1;
+		default:
+			ERR_MSG("unexpected GMCH_GMS value: 0x%02x\n",
+				tmp & INTEL_830_GMCH_GMS_MASK);
+			return 1;
+		}
+		break;
+	default:
+		*aperture_size = MB(128);
+		switch (tmp & INTEL_855_GMCH_GMS_MASK) {
+		case INTEL_855_GMCH_GMS_STOLEN_1M:
+			*stolen_size = MB(1) - KB(132);
+			return 0;
+		case INTEL_855_GMCH_GMS_STOLEN_4M:
+			*stolen_size = MB(4) - KB(132);
+			return 0;
+		case INTEL_855_GMCH_GMS_STOLEN_8M:
+			*stolen_size = MB(8) - KB(132);
+			return 0;
+		case INTEL_855_GMCH_GMS_STOLEN_16M:
+			*stolen_size = MB(16) - KB(132);
+			return 0;
+		case INTEL_855_GMCH_GMS_STOLEN_32M:
+			*stolen_size = MB(32) - KB(132);
+			return 0;
+		case INTEL_855_GMCH_GMS_DISABLED:
+			ERR_MSG("video memory is disabled\n");
+			return 0;
+		default:
+			ERR_MSG("unexpected GMCH_GMS value: 0x%02x\n",
+				tmp & INTEL_855_GMCH_GMS_MASK);
+			return 1;
+		}
+	}
+}
+
+const char *
+intelfbhw_check_non_crt(struct intelfb_info *dinfo)
+{
+	if (INREG(LVDS) & PORT_ENABLE)
+		return "LVDS port";
+	else if (INREG(DVOA) & PORT_ENABLE)
+		return "DVO port A";
+	else if (INREG(DVOB) & PORT_ENABLE)
+		return "DVO port B";
+	else if (INREG(DVOC) & PORT_ENABLE)
+		return "DVO port C";
+	else
+		return NULL;
+}
+
+int
+intelfbhw_validate_mode(struct intelfb_info *dinfo, int con,
+			struct fb_var_screeninfo *var)
+{
+	int bytes_per_pixel;
+	int tmp;
+
+	DBG_MSG("intelfbhw_validate_mode\n");
+
+	bytes_per_pixel = var->bits_per_pixel / 8;
+	if (bytes_per_pixel == 3)
+		bytes_per_pixel = 4;
+
+	/* Check if enough video memory. */
+	tmp = var->yres_virtual * var->xres_virtual * bytes_per_pixel;
+	if (tmp > dinfo->video_ram) {
+		if (con >= 0)
+			WRN_MSG("Not enough video ram for mode "
+				"(%d KByte vs %d KByte).\n",
+				BtoKB(tmp), BtoKB(dinfo->video_ram));
+		return 1;
+	}
+
+	/* Check if x/y limits are OK. */
+	if (var->xres - 1 > HACTIVE_MASK) {
+		if (con >= 0)
+			WRN_MSG("X resolution too large (%d vs %d).\n",
+				var->xres, HACTIVE_MASK + 1);
+		return 1;
+	}
+	if (var->yres - 1 > VACTIVE_MASK) {
+		if (con >= 0)
+			WRN_MSG("Y resolution too large (%d vs %d).\n",
+				var->yres, VACTIVE_MASK + 1);
+		return 1;
+	}
+
+	/* Check for interlaced/doublescan modes. */
+	if (var->vmode & FB_VMODE_INTERLACED) {
+		if (con >= 0)
+			WRN_MSG("Mode is interlaced.\n");
+		return 1;
+	}
+	if (var->vmode & FB_VMODE_DOUBLE) {
+		if (con >= 0)
+			WRN_MSG("Mode is double-scan.\n");
+		return 1;
+	}
+
+	/* Check if clock is OK. */
+	tmp = 1000000000 / var->pixclock;
+	if (tmp < MIN_CLOCK) {
+		if (con >= 0)
+			WRN_MSG("Pixel clock is too low (%d MHz vs %d MHz).\n",
+				(tmp + 500) / 1000, MIN_CLOCK / 1000);
+		return 1;
+	}
+	if (tmp > MAX_CLOCK) {
+		if (con >= 0)
+			WRN_MSG("Pixel clock is too high (%d MHz vs %d MHz).\n",
+				(tmp + 500) / 1000, MAX_CLOCK / 1000);
+		return 1;
+	}
+
+	return 0;
+}
+
+int
+intelfbhw_pan_display(struct fb_var_screeninfo *var, int con,
+		      struct fb_info *info)
+{
+	struct intelfb_info *dinfo = GET_DINFO(info);
+	u32 offset, xoffset, yoffset;
+
+	DBG_MSG("intelfbhw_pan_display\n");
+
+	if (con != dinfo->currcon)
+		return 0;
+
+	xoffset = ROUND_DOWN_TO(var->xoffset, 8);
+	yoffset = var->yoffset;
+
+	if ((xoffset + var->xres > var->xres_virtual) ||
+	    (yoffset + var->yres > var->yres_virtual))
+		return EINVAL;
+
+	offset = (yoffset * dinfo->pitch) +
+		 (xoffset * var->bits_per_pixel) / 8;
+
+	OUTREG(DSPABASE, offset);
+
+	return 0;
+}
+
+/* Blank the screen. */
+void
+intelfbhw_do_blank(int blank, struct fb_info *info)
+{
+	struct intelfb_info *dinfo = GET_DINFO(info);
+	u32 tmp;
+
+	DBG_MSG("intelfbhw_do_blank: blank is %d\n", blank);
+
+	/* Turn plane A on or off */
+	tmp = INREG(DSPACNTR);
+	if (blank)
+		tmp &= ~DISPPLANE_PLANE_ENABLE;
+	else
+		tmp |= DISPPLANE_PLANE_ENABLE;
+	OUTREG(DSPACNTR, tmp);
+	/* Flush */
+	tmp = INREG(DSPABASE);
+	OUTREG(DSPABASE, tmp);
+
+	/* Turn off/on the HW cursor */
+#if VERBOSE > 0
+	DBG_MSG("cursor.enabled is %d\n", dinfo->cursor.enabled);
+#endif
+	if (dinfo->cursor.enabled) {
+		if (blank) {
+			intelfbhw_cursor_hide(dinfo);
+		} else {
+			intelfbhw_cursor_show(dinfo);
+		}
+		dinfo->cursor.enabled = 1;
+	}
+	dinfo->cursor.blanked = blank;
+
+	/* Set DPMS level */
+	tmp = INREG(ADPA) & ~ADPA_DPMS_CONTROL_MASK;
+	switch (blank) {
+	case 0:
+	case 1:
+		tmp |= ADPA_DPMS_D0;
+		break;
+	case 2:
+		tmp |= ADPA_DPMS_D1;
+		break;
+	case 3:
+		tmp |= ADPA_DPMS_D2;
+		break;
+	case 4:
+		tmp |= ADPA_DPMS_D3;
+		break;
+	}
+	OUTREG(ADPA, tmp);
+	
+	return;
+}
+
+
+void
+intelfbhw_setcolreg(struct intelfb_info *dinfo, unsigned regno,
+		    unsigned red, unsigned green, unsigned blue,
+		    unsigned transp)
+{
+#if VERBOSE > 1
+	DBG_MSG("intelfbhw_setcolreg: %d: (%d, %d, %d)\n",
+		regno, red, green, blue);
+#endif
+
+	u32 palette_reg = (dinfo->pipe == PIPE_A) ?
+			  PALETTE_A : PALETTE_B;
+
+	OUTREG(palette_reg + (regno << 2), 
+	       (red << PALETTE_8_RED_SHIFT) |
+	       (green << PALETTE_8_GREEN_SHIFT) |
+	       (blue << PALETTE_8_BLUE_SHIFT));
+}
+
+
+int
+intelfbhw_read_hw_state(struct intelfb_info *dinfo, struct intelfb_hwstate *hw,
+			int flag)
+{
+	int i;
+
+	DBG_MSG("intelfbhw_read_hw_state\n");
+
+	if (!hw || !dinfo)
+		return -1;
+
+	/* Read in as much of the HW state as possible. */
+	hw->vga0_divisor = INREG(VGA0_DIVISOR);
+	hw->vga1_divisor = INREG(VGA1_DIVISOR);
+	hw->vga_pd = INREG(VGAPD);
+	hw->dpll_a = INREG(DPLL_A);
+	hw->dpll_b = INREG(DPLL_B);
+	hw->fpa0 = INREG(FPA0);
+	hw->fpa1 = INREG(FPA1);
+	hw->fpb0 = INREG(FPB0);
+	hw->fpb1 = INREG(FPB1);
+
+	if (flag == 1)
+		return flag;
+
+#if 0
+	/* This seems to be a problem with the 852GM/855GM */
+	for (i = 0; i < PALETTE_8_ENTRIES; i++) {
+		hw->palette_a[i] = INREG(PALETTE_A + (i << 2));
+		hw->palette_b[i] = INREG(PALETTE_B + (i << 2));
+	}
+#endif
+
+	if (flag == 2)
+		return flag;
+
+	hw->htotal_a = INREG(HTOTAL_A);
+	hw->hblank_a = INREG(HBLANK_A);
+	hw->hsync_a = INREG(HSYNC_A);
+	hw->vtotal_a = INREG(VTOTAL_A);
+	hw->vblank_a = INREG(VBLANK_A);
+	hw->vsync_a = INREG(VSYNC_A);
+	hw->src_size_a = INREG(SRC_SIZE_A);
+	hw->bclrpat_a = INREG(BCLRPAT_A);
+	hw->htotal_b = INREG(HTOTAL_B);
+	hw->hblank_b = INREG(HBLANK_B);
+	hw->hsync_b = INREG(HSYNC_B);
+	hw->vtotal_b = INREG(VTOTAL_B);
+	hw->vblank_b = INREG(VBLANK_B);
+	hw->vsync_b = INREG(VSYNC_B);
+	hw->src_size_b = INREG(SRC_SIZE_B);
+	hw->bclrpat_b = INREG(BCLRPAT_B);
+
+	if (flag == 3)
+		return flag;
+
+	hw->adpa = INREG(ADPA);
+	hw->dvoa = INREG(DVOA);
+	hw->dvob = INREG(DVOB);
+	hw->dvoc = INREG(DVOC);
+	hw->dvoa_srcdim = INREG(DVOA_SRCDIM);
+	hw->dvob_srcdim = INREG(DVOB_SRCDIM);
+	hw->dvoc_srcdim = INREG(DVOC_SRCDIM);
+	hw->lvds = INREG(LVDS);
+
+	if (flag == 4)
+		return flag;
+
+	hw->pipe_a_conf = INREG(PIPEACONF);
+	hw->pipe_b_conf = INREG(PIPEBCONF);
+	hw->disp_arb = INREG(DISPARB);
+
+	if (flag == 5)
+		return flag;
+
+	hw->cursor_a_control = INREG(CURSOR_A_CONTROL);
+	hw->cursor_b_control = INREG(CURSOR_B_CONTROL);
+	hw->cursor_a_base = INREG(CURSOR_A_BASEADDR);
+	hw->cursor_b_base = INREG(CURSOR_B_BASEADDR);
+
+	if (flag == 6)
+		return flag;
+
+	for (i = 0; i < 4; i++) {
+		hw->cursor_a_palette[i] = INREG(CURSOR_A_PALETTE0 + (i << 2));
+		hw->cursor_b_palette[i] = INREG(CURSOR_B_PALETTE0 + (i << 2));
+	}
+
+	if (flag == 7)
+		return flag;
+
+	hw->cursor_size = INREG(CURSOR_SIZE);
+
+	if (flag == 8)
+		return flag;
+
+	hw->disp_a_ctrl = INREG(DSPACNTR);
+	hw->disp_b_ctrl = INREG(DSPBCNTR);
+	hw->disp_a_base = INREG(DSPABASE);
+	hw->disp_b_base = INREG(DSPBBASE);
+	hw->disp_a_stride = INREG(DSPASTRIDE);
+	hw->disp_b_stride = INREG(DSPBSTRIDE);
+
+	if (flag == 9)
+		return flag;
+
+	hw->vgacntrl = INREG(VGACNTRL);
+
+	if (flag == 10)
+		return flag;
+
+	hw->add_id = INREG(ADD_ID);
+
+	if (flag == 11)
+		return flag;
+
+	for (i = 0; i < 7; i++) {
+		hw->swf0x[i] = INREG(SWF00 + (i << 2));
+		hw->swf1x[i] = INREG(SWF10 + (i << 2));
+		if (i < 3)
+			hw->swf3x[i] = INREG(SWF30 + (i << 2));
+	}
+
+	for (i = 0; i < 8; i++)
+		hw->fence[i] = INREG(FENCE + (i << 2));
+
+	hw->instpm = INREG(INSTPM);
+	hw->mem_mode = INREG(MEM_MODE);
+	hw->fw_blc_0 = INREG(FW_BLC_0);
+	hw->fw_blc_1 = INREG(FW_BLC_1);
+
+	return 0;
+}
+
+
+void
+intelfbhw_print_hw_state(struct intelfb_info *dinfo, struct intelfb_hwstate *hw)
+{
+#if REGDUMP
+	int i, m1, m2, n, p1, p2;
+
+	DBG_MSG("intelfbhw_print_hw_state\n");
+
+	if (!hw || !dinfo)
+		return;
+	/* Read in as much of the HW state as possible. */
+	printk("hw state dump start\n");
+	printk("	VGA0_DIVISOR:		0x%08x\n", hw->vga0_divisor);
+	printk("	VGA1_DIVISOR:		0x%08x\n", hw->vga1_divisor);
+	printk("	VGAPD: 			0x%08x\n", hw->vga_pd);
+	n = (hw->vga0_divisor >> FP_N_DIVISOR_SHIFT) & FP_DIVISOR_MASK;
+	m1 = (hw->vga0_divisor >> FP_M1_DIVISOR_SHIFT) & FP_DIVISOR_MASK;
+	m2 = (hw->vga0_divisor >> FP_M2_DIVISOR_SHIFT) & FP_DIVISOR_MASK;
+	if (hw->vga_pd & VGAPD_0_P1_FORCE_DIV2)
+		p1 = 0;
+	else
+		p1 = (hw->vga_pd >> VGAPD_0_P1_SHIFT) & DPLL_P1_MASK;
+	p2 = (hw->vga_pd >> VGAPD_0_P2_SHIFT) & DPLL_P2_MASK;
+	printk("	VGA0: (m1, m2, n, p1, p2) = (%d, %d, %d, %d, %d)\n",
+		m1, m2, n, p1, p2);
+	printk("	VGA0: clock is %d\n", CALC_VCLOCK(m1, m2, n, p1, p2));
+	
+	n = (hw->vga1_divisor >> FP_N_DIVISOR_SHIFT) & FP_DIVISOR_MASK;
+	m1 = (hw->vga1_divisor >> FP_M1_DIVISOR_SHIFT) & FP_DIVISOR_MASK;
+	m2 = (hw->vga1_divisor >> FP_M2_DIVISOR_SHIFT) & FP_DIVISOR_MASK;
+	if (hw->vga_pd & VGAPD_1_P1_FORCE_DIV2)
+		p1 = 0;
+	else
+		p1 = (hw->vga_pd >> VGAPD_1_P1_SHIFT) & DPLL_P1_MASK;
+	p2 = (hw->vga_pd >> VGAPD_1_P2_SHIFT) & DPLL_P2_MASK;
+	printk("	VGA1: (m1, m2, n, p1, p2) = (%d, %d, %d, %d, %d)\n",
+		m1, m2, n, p1, p2);
+	printk("	VGA1: clock is %d\n", CALC_VCLOCK(m1, m2, n, p1, p2));
+	
+	printk("	DPLL_A:			0x%08x\n", hw->dpll_a);
+	printk("	DPLL_B:			0x%08x\n", hw->dpll_b);
+	printk("	FPA0:			0x%08x\n", hw->fpa0);
+	printk("	FPA1:			0x%08x\n", hw->fpa1);
+	printk("	FPB0:			0x%08x\n", hw->fpb0);
+	printk("	FPB1:			0x%08x\n", hw->fpb1);
+
+	n = (hw->fpa0 >> FP_N_DIVISOR_SHIFT) & FP_DIVISOR_MASK;
+	m1 = (hw->fpa0 >> FP_M1_DIVISOR_SHIFT) & FP_DIVISOR_MASK;
+	m2 = (hw->fpa0 >> FP_M2_DIVISOR_SHIFT) & FP_DIVISOR_MASK;
+	if (hw->dpll_a & DPLL_P1_FORCE_DIV2)
+		p1 = 0;
+	else
+		p1 = (hw->dpll_a >> DPLL_P1_SHIFT) & DPLL_P1_MASK;
+	p2 = (hw->dpll_a >> DPLL_P2_SHIFT) & DPLL_P2_MASK;
+	printk("	PLLA0: (m1, m2, n, p1, p2) = (%d, %d, %d, %d, %d)\n",
+		m1, m2, n, p1, p2);
+	printk("	PLLA0: clock is %d\n", CALC_VCLOCK(m1, m2, n, p1, p2));
+
+	n = (hw->fpa1 >> FP_N_DIVISOR_SHIFT) & FP_DIVISOR_MASK;
+	m1 = (hw->fpa1 >> FP_M1_DIVISOR_SHIFT) & FP_DIVISOR_MASK;
+	m2 = (hw->fpa1 >> FP_M2_DIVISOR_SHIFT) & FP_DIVISOR_MASK;
+	if (hw->dpll_a & DPLL_P1_FORCE_DIV2)
+		p1 = 0;
+	else
+		p1 = (hw->dpll_a >> DPLL_P1_SHIFT) & DPLL_P1_MASK;
+	p2 = (hw->dpll_a >> DPLL_P2_SHIFT) & DPLL_P2_MASK;
+	printk("	PLLA1: (m1, m2, n, p1, p2) = (%d, %d, %d, %d, %d)\n",
+		m1, m2, n, p1, p2);
+	printk("	PLLA1: clock is %d\n", CALC_VCLOCK(m1, m2, n, p1, p2));
+	
+#if 0
+	printk("	PALETTE_A:\n");
+	for (i = 0; i < PALETTE_8_ENTRIES)
+		printk("	%3d:	0x%08x\n", i, hw->palette_a[i];
+	printk("	PALETTE_B:\n");
+	for (i = 0; i < PALETTE_8_ENTRIES)
+		printk("	%3d:	0x%08x\n", i, hw->palette_b[i];
+#endif
+
+	printk("	HTOTAL_A:		0x%08x\n", hw->htotal_a);
+	printk("	HBLANK_A:		0x%08x\n", hw->hblank_a);
+	printk("	HSYNC_A:		0x%08x\n", hw->hsync_a);
+	printk("	VTOTAL_A:		0x%08x\n", hw->vtotal_a);
+	printk("	VBLANK_A:		0x%08x\n", hw->vblank_a);
+	printk("	VSYNC_A:		0x%08x\n", hw->vsync_a);
+	printk("	SRC_SIZE_A:		0x%08x\n", hw->src_size_a);
+	printk("	BCLRPAT_A:		0x%08x\n", hw->bclrpat_a);
+	printk("	HTOTAL_B:		0x%08x\n", hw->htotal_b);
+	printk("	HBLANK_B:		0x%08x\n", hw->hblank_b);
+	printk("	HSYNC_B:		0x%08x\n", hw->hsync_b);
+	printk("	VTOTAL_B:		0x%08x\n", hw->vtotal_b);
+	printk("	VBLANK_B:		0x%08x\n", hw->vblank_b);
+	printk("	VSYNC_B:		0x%08x\n", hw->vsync_b);
+	printk("	SRC_SIZE_B:		0x%08x\n", hw->src_size_b);
+	printk("	BCLRPAT_B:		0x%08x\n", hw->bclrpat_b);
+
+	printk("	ADPA:			0x%08x\n", hw->adpa);
+	printk("	DVOA:			0x%08x\n", hw->dvoa);
+	printk("	DVOB:			0x%08x\n", hw->dvob);
+	printk("	DVOC:			0x%08x\n", hw->dvoc);
+	printk("	DVOA_SRCDIM:		0x%08x\n", hw->dvoa_srcdim);
+	printk("	DVOB_SRCDIM:		0x%08x\n", hw->dvob_srcdim);
+	printk("	DVOC_SRCDIM:		0x%08x\n", hw->dvoc_srcdim);
+	printk("	LVDS:			0x%08x\n", hw->lvds);
+
+	printk("	PIPEACONF:		0x%08x\n", hw->pipe_a_conf);
+	printk("	PIPEBCONF:		0x%08x\n", hw->pipe_b_conf);
+	printk("	DISPARB:		0x%08x\n", hw->disp_arb);
+
+	printk("	CURSOR_A_CONTROL:	0x%08x\n", hw->cursor_a_control);
+	printk("	CURSOR_B_CONTROL:	0x%08x\n", hw->cursor_b_control);
+	printk("	CURSOR_A_BASEADDR:	0x%08x\n", hw->cursor_a_base);
+	printk("	CURSOR_B_BASEADDR:	0x%08x\n", hw->cursor_b_base);
+
+	printk("	CURSOR_A_PALETTE:	");
+	for (i = 0; i < 4; i++) {
+		printk("0x%08x", hw->cursor_a_palette[i]);
+		if (i < 3)
+			printk(", ");
+	}
+	printk("\n");
+	printk("	CURSOR_B_PALETTE:	");
+	for (i = 0; i < 4; i++) {
+		printk("0x%08x", hw->cursor_b_palette[i]);
+		if (i < 3)
+			printk(", ");
+	}
+	printk("\n");
+
+	printk("	CURSOR_SIZE:		0x%08x\n", hw->cursor_size);
+
+	printk("	DSPACNTR:		0x%08x\n", hw->disp_a_ctrl);
+	printk("	DSPBCNTR:		0x%08x\n", hw->disp_b_ctrl);
+	printk("	DSPABASE:		0x%08x\n", hw->disp_a_base);
+	printk("	DSPBBASE:		0x%08x\n", hw->disp_b_base);
+	printk("	DSPASTRIDE:		0x%08x\n", hw->disp_a_stride);
+	printk("	DSPBSTRIDE:		0x%08x\n", hw->disp_b_stride);
+
+	printk("	VGACNTRL:		0x%08x\n", hw->vgacntrl);
+	printk("	ADD_ID:			0x%08x\n", hw->add_id);
+
+	for (i = 0; i < 7; i++) {
+		printk("	SWF0%d			0x%08x\n", i,
+			hw->swf0x[i]);
+	}
+	for (i = 0; i < 7; i++) {
+		printk("	SWF1%d			0x%08x\n", i,
+			hw->swf1x[i]);
+	}
+	for (i = 0; i < 3; i++) {
+		printk("	SWF3%d			0x%08x\n", i,
+			hw->swf3x[i]);
+	}
+	for (i = 0; i < 8; i++)
+		printk("	FENCE%d			0x%08x\n", i,
+			hw->fence[i]);
+
+	printk("	INSTPM			0x%08x\n", hw->instpm);
+	printk("	MEM_MODE		0x%08x\n", hw->mem_mode);
+	printk("	FW_BLC_0		0x%08x\n", hw->fw_blc_0);
+	printk("	FW_BLC_1		0x%08x\n", hw->fw_blc_1);
+
+	printk("hw state dump end\n");
+#endif
+}
+
+/* Split the M parameter into M1 and M2. */
+static int
+splitm(unsigned int m, unsigned int *retm1, unsigned int *retm2)
+{
+	int m1, m2;
+
+	m1 = (m - 2 - (MIN_M2 + MAX_M2) / 2) / 5 - 2;
+	if (m1 < MIN_M1)
+		m1 = MIN_M1;
+	if (m1 > MAX_M1)
+		m1 = MAX_M1;
+	m2 = m - 5 * (m1 + 2) - 2;
+	if (m2 < MIN_M2 || m2 > MAX_M2 || m2 >= m1) {
+		return 1;
+	} else {
+		*retm1 = (unsigned int)m1;
+		*retm2 = (unsigned int)m2;
+		return 0;
+	}
+}
+
+/* Split the P parameter into P1 and P2. */
+static int
+splitp(unsigned int p, unsigned int *retp1, unsigned int *retp2)
+{
+	int p1, p2;
+
+	if (p % 4 == 0)
+		p2 = 1;
+	else
+		p2 = 0;
+	p1 = (p / (1 << (p2 + 1))) - 2;
+	if (p % 4 == 0 && p1 < MIN_P1) {
+		p2 = 0;
+		p1 = (p / (1 << (p2 + 1))) - 2;
+	}
+	if (p1  < MIN_P1 || p1 > MAX_P1 || (p1 + 2) * (1 << (p2 + 1)) != p) {
+		return 1;
+	} else {
+		*retp1 = (unsigned int)p1;
+		*retp2 = (unsigned int)p2;
+		return 0;
+	}
+}
+
+static int
+calc_pll_params(int clock, u32 *retm1, u32 *retm2, u32 *retn, u32 *retp1,
+		u32 *retp2, u32 *retclock)
+{
+	u32 m1, m2, n, p1, p2, n1;
+	u32 f_vco, p, p_best = 0, m, f_out;
+	u32 err_max, err_target, err_best = 10000000;
+	u32 n_best = 0, m_best = 0, f_best, f_err;
+	u32 p_min, p_max, p_inc, div_min, div_max;
+
+	/* Accept 0.5% difference, but aim for 0.1% */
+	err_max = 5 * clock / 1000;
+	err_target = clock / 1000;
+
+	DBG_MSG("Clock is %d\n", clock);
+
+	div_max = MAX_VCO_FREQ / clock;
+	div_min = ROUND_UP_TO(MIN_VCO_FREQ, clock) / clock;
+
+	if (clock <= P_TRANSITION_CLOCK)
+		p_inc = 4;
+	else
+		p_inc = 2;
+	p_min = ROUND_UP_TO(div_min, p_inc);
+	p_max = ROUND_DOWN_TO(div_max, p_inc);
+	if (p_min < MIN_P)
+		p_min = 4;
+	if (p_max > MAX_P)
+		p_max = 128;
+
+	DBG_MSG("p range is %d-%d (%d)\n", p_min, p_max, p_inc);
+
+	p = p_min;
+	do {
+		if (splitp(p, &p1, &p2)) {
+			WRN_MSG("cannot split p = %d\n", p);
+			p += p_inc;
+			continue;
+		}
+		n = MIN_N;
+		f_vco = clock * p;
+
+		do {
+			m = ROUND_UP_TO(f_vco * n, PLL_REFCLK) / PLL_REFCLK;
+			if (m < MIN_M)
+				m = MIN_M;
+			if (m > MAX_M)
+				m = MAX_M;
+			f_out = CALC_VCLOCK3(m, n, p);
+			if (splitm(m, &m1, &m2)) {
+				WRN_MSG("cannot split m = %d\n", m);
+				n++;
+				continue;
+			}
+			if (clock > f_out)
+				f_err = clock - f_out;
+			else
+				f_err = f_out - clock;
+			
+			if (f_err < err_best) {
+				m_best = m;
+				n_best = n;
+				p_best = p;
+				f_best = f_out;
+				err_best = f_err;
+			}
+			n++;
+		} while ((n <= MAX_N) && (f_out >= clock));
+		p += p_inc;
+	} while ((p <= p_max));
+
+	if (!m_best) {
+		WRN_MSG("cannot find parameters for clock %d\n", clock);
+		return 1;
+	}
+	m = m_best;
+	n = n_best;
+	p = p_best;
+	splitm(m, &m1, &m2);
+	splitp(p, &p1, &p2);
+	n1 = n - 2;
+	
+	DBG_MSG("m, n, p: %d (%d,%d), %d (%d), %d (%d,%d), "
+		"f: %d (%d), VCO: %d\n",
+		m, m1, m2, n, n1, p, p1, p2,
+		CALC_VCLOCK3(m, n, p), CALC_VCLOCK(m1, m2, n1, p1, p2),
+		CALC_VCLOCK3(m, n, p) * p);
+	*retm1 = m1;
+	*retm2 = m2;
+	*retn = n1;
+	*retp1 = p1;
+	*retp2 = p2;
+	*retclock = CALC_VCLOCK(m1, m2, n1, p1, p2);
+	
+	return 0;
+}
+
+static __inline__ int
+check_overflow(u32 value, u32 limit, const char *description)
+{
+	if (value > limit) {
+		WRN_MSG("%s value %d exceeds limit %d\n",
+			description, value, limit);
+		return 1;
+	}
+	return 0;
+}
+
+/* It is assumed that hw is filled in with the initial state information. */
+int
+intelfbhw_mode_to_hw(struct intelfb_info *dinfo, struct intelfb_hwstate *hw,
+		     struct fb_var_screeninfo *var)
+{
+	int pipe = PIPE_A;
+	u32 *dpll, *fp0, *fp1;
+	u32 m1, m2, n, p1, p2, clock_target, clock;
+	u32 hsync_start, hsync_end, hblank_start, hblank_end, htotal, hactive;
+	u32 vsync_start, vsync_end, vblank_start, vblank_end, vtotal, vactive;
+	u32 vsync_pol, hsync_pol;
+	u32 *vs, *vb, *vt, *hs, *hb, *ht, *ss, *pipe_conf;
+	struct display *disp;
+
+	DBG_MSG("intelfbhw_mode_to_hw\n");
+
+	disp = GET_DISP(&dinfo->info, dinfo->currcon);
+
+	/* Disable VGA */
+	hw->vgacntrl |= VGA_DISABLE;
+
+	/* Check whether pipe A or pipe B is enabled. */
+	if (hw->pipe_a_conf & PIPECONF_ENABLE)
+		pipe = PIPE_A;
+	else if (hw->pipe_b_conf & PIPECONF_ENABLE)
+		pipe = PIPE_B;
+	
+	/* Set which pipe's registers will be set. */
+	if (pipe == PIPE_B) {
+		dpll = &hw->dpll_b;
+		fp0 = &hw->fpb0;
+		fp1 = &hw->fpb1;
+		hs = &hw->hsync_b;
+		hb = &hw->hblank_b;
+		ht = &hw->htotal_b;
+		vs = &hw->vsync_b;
+		vb = &hw->vblank_b;
+		vt = &hw->vtotal_b;
+		ss = &hw->src_size_b;
+		pipe_conf = &hw->pipe_b_conf;
+	} else {
+		dpll = &hw->dpll_a;
+		fp0 = &hw->fpa0;
+		fp1 = &hw->fpa1;
+		hs = &hw->hsync_a;
+		hb = &hw->hblank_a;
+		ht = &hw->htotal_a;
+		vs = &hw->vsync_a;
+		vb = &hw->vblank_a;
+		vt = &hw->vtotal_a;
+		ss = &hw->src_size_a;
+		pipe_conf = &hw->pipe_a_conf;
+	}
+
+	/* Use ADPA register for sync control. */
+	hw->adpa &= ~ADPA_USE_VGA_HVPOLARITY;
+
+	/* sync polarity */
+	hsync_pol = (var->sync & FB_SYNC_HOR_HIGH_ACT) ?
+			ADPA_SYNC_ACTIVE_HIGH : ADPA_SYNC_ACTIVE_LOW;
+	vsync_pol = (var->sync & FB_SYNC_VERT_HIGH_ACT) ?
+			ADPA_SYNC_ACTIVE_HIGH : ADPA_SYNC_ACTIVE_LOW;
+	hw->adpa &= ~((ADPA_SYNC_ACTIVE_MASK << ADPA_VSYNC_ACTIVE_SHIFT) |
+		      (ADPA_SYNC_ACTIVE_MASK << ADPA_HSYNC_ACTIVE_SHIFT));
+	hw->adpa |= (hsync_pol << ADPA_HSYNC_ACTIVE_SHIFT) |
+		    (vsync_pol << ADPA_VSYNC_ACTIVE_SHIFT);
+
+	/* Connect correct pipe to the analog port DAC */
+	hw->adpa &= ~(PIPE_MASK << ADPA_PIPE_SELECT_SHIFT);
+	hw->adpa |= (pipe << ADPA_PIPE_SELECT_SHIFT);
+
+	/* Set DPMS state to D0 (on) */
+	hw->adpa &= ~ADPA_DPMS_CONTROL_MASK;
+	hw->adpa |= ADPA_DPMS_D0;
+
+	*dpll |= (DPLL_VCO_ENABLE | DPLL_VGA_MODE_DISABLE);
+	*dpll &= ~(DPLL_RATE_SELECT_MASK | DPLL_REFERENCE_SELECT_MASK);
+	*dpll |= (DPLL_REFERENCE_DEFAULT | DPLL_RATE_SELECT_FP0);
+
+	/* Desired clock in kHz */
+	clock_target = 1000000000 / var->pixclock;
+	if (calc_pll_params(clock_target, &m1, &m2, &n, &p1, &p2, &clock)) {
+		WRN_MSG("calc_pll_params failed\n");
+		return 1;
+	}
+
+	/* Check for overflow. */
+	if (check_overflow(p1, DPLL_P1_MASK, "PLL P1 parameter"))
+		return 1;
+	if (check_overflow(p2, DPLL_P2_MASK, "PLL P2 parameter"))
+		return 1;
+	if (check_overflow(m1, FP_DIVISOR_MASK, "PLL M1 parameter"))
+		return 1;
+	if (check_overflow(m2, FP_DIVISOR_MASK, "PLL M2 parameter"))
+		return 1;
+	if (check_overflow(n, FP_DIVISOR_MASK, "PLL N parameter"))
+		return 1;
+
+	*dpll &= ~DPLL_P1_FORCE_DIV2;
+	*dpll &= ~((DPLL_P2_MASK << DPLL_P2_SHIFT) |
+		   (DPLL_P1_MASK << DPLL_P1_SHIFT));
+	*dpll |= (p2 << DPLL_P2_SHIFT) | (p1 << DPLL_P1_SHIFT);
+	*fp0 = (n << FP_N_DIVISOR_SHIFT) |
+	       (m1 << FP_M1_DIVISOR_SHIFT) |
+	       (m2 << FP_M2_DIVISOR_SHIFT);
+	*fp1 = *fp0;
+
+	/* Make sure DVOB and DVOC are disabled for now. */
+	hw->dvob &= ~PORT_ENABLE;
+	hw->dvoc &= ~PORT_ENABLE;
+
+	/* Use display plane A. */
+	hw->disp_a_ctrl |= DISPPLANE_PLANE_ENABLE;
+	hw->disp_a_ctrl &= ~DISPPLANE_GAMMA_ENABLE;
+	hw->disp_a_ctrl &= ~DISPPLANE_PIXFORMAT_MASK;
+	switch (intelfb_var_to_depth(var)) {
+	case 8:
+		hw->disp_a_ctrl |= DISPPLANE_8BPP | DISPPLANE_GAMMA_ENABLE;
+		break;
+	case 15:
+		hw->disp_a_ctrl |= DISPPLANE_15_16BPP;
+		break;
+	case 16:
+		hw->disp_a_ctrl |= DISPPLANE_16BPP;
+		break;
+	case 24:
+		hw->disp_a_ctrl |= DISPPLANE_32BPP_NO_ALPHA;
+		break;
+	}
+	hw->disp_a_ctrl &= ~(PIPE_MASK << DISPPLANE_SEL_PIPE_SHIFT);
+	hw->disp_a_ctrl |= (pipe << DISPPLANE_SEL_PIPE_SHIFT);
+
+	/* Set CRTC registers. */
+	hactive = var->xres;
+	hsync_start = hactive + var->right_margin;
+	hsync_end = hsync_start + var->hsync_len;
+	htotal = hsync_end + var->left_margin;
+	hblank_start = hactive;
+	hblank_end = htotal;
+
+	DBG_MSG("H: act %d, ss %d, se %d, tot %d bs %d, be %d\n",
+		hactive, hsync_start, hsync_end, htotal, hblank_start,
+		hblank_end);
+
+	vactive = var->yres;
+	vsync_start = vactive + var->lower_margin;
+	vsync_end = vsync_start + var->vsync_len;
+	vtotal = vsync_end + var->upper_margin;
+	vblank_start = vactive;
+	vblank_end = vtotal;
+	vblank_end = vsync_end + 1;
+
+	DBG_MSG("V: act %d, ss %d, se %d, tot %d bs %d, be %d\n",
+		vactive, vsync_start, vsync_end, vtotal, vblank_start,
+		vblank_end);
+
+	/* Adjust for register values, and check for overflow. */
+	hactive--;
+	if (check_overflow(hactive, HACTIVE_MASK, "CRTC hactive"))
+		return 1;
+	hsync_start--;
+	if (check_overflow(hsync_start, HSYNCSTART_MASK, "CRTC hsync_start"))
+		return 1;
+	hsync_end--;
+	if (check_overflow(hsync_end, HSYNCEND_MASK, "CRTC hsync_end"))
+		return 1;
+	htotal--;
+	if (check_overflow(htotal, HTOTAL_MASK, "CRTC htotal"))
+		return 1;
+	hblank_start--;
+	if (check_overflow(hblank_start, HBLANKSTART_MASK, "CRTC hblank_start"))
+		return 1;
+	hblank_end--;
+	if (check_overflow(hblank_end, HBLANKEND_MASK, "CRTC hblank_end"))
+		return 1;
+
+	vactive--;
+	if (check_overflow(vactive, VACTIVE_MASK, "CRTC vactive"))
+		return 1;
+	vsync_start--;
+	if (check_overflow(vsync_start, VSYNCSTART_MASK, "CRTC vsync_start"))
+		return 1;
+	vsync_end--;
+	if (check_overflow(vsync_end, VSYNCEND_MASK, "CRTC vsync_end"))
+		return 1;
+	vtotal--;
+	if (check_overflow(vtotal, VTOTAL_MASK, "CRTC vtotal"))
+		return 1;
+	vblank_start--;
+	if (check_overflow(vblank_start, VBLANKSTART_MASK, "CRTC vblank_start"))
+		return 1;
+	vblank_end--;
+	if (check_overflow(vblank_end, VBLANKEND_MASK, "CRTC vblank_end"))
+		return 1;
+
+	*ht = (htotal << HTOTAL_SHIFT) | (hactive << HACTIVE_SHIFT);
+	*hb = (hblank_start << HBLANKSTART_SHIFT) |
+	      (hblank_end << HSYNCEND_SHIFT);
+	*hs = (hsync_start << HSYNCSTART_SHIFT) | (hsync_end << HSYNCEND_SHIFT);
+
+	*vt = (vtotal << VTOTAL_SHIFT) | (vactive << VACTIVE_SHIFT);
+	*vb = (vblank_start << VBLANKSTART_SHIFT) |
+	      (vblank_end << VSYNCEND_SHIFT);
+	*vs = (vsync_start << VSYNCSTART_SHIFT) | (vsync_end << VSYNCEND_SHIFT);
+	*ss = (hactive << SRC_SIZE_HORIZ_SHIFT) |
+	      (vactive << SRC_SIZE_VERT_SHIFT);
+
+	/* Start address and stride. */
+	if (dinfo->pitch)
+		hw->disp_a_stride = dinfo->pitch;
+	else
+		hw->disp_a_stride = var->xres_virtual * var->bits_per_pixel / 8;
+	DBG_MSG("pitch is %d\n", hw->disp_a_stride);
+
+	hw->disp_a_base = hw->disp_a_stride * var->yoffset +
+			  var->xoffset * var->bits_per_pixel / 8;
+
+	/* Check stride alignment. */
+	if (hw->disp_a_stride % STRIDE_ALIGNMENT != 0) {
+		WRN_MSG("display stride %d has bad alignment %d\n",
+			hw->disp_a_stride, STRIDE_ALIGNMENT);
+		return 1;
+	}
+
+	/* Set the palette to 8-bit mode. */
+	*pipe_conf &= ~PIPECONF_GAMMA;
+	return 0;
+}
+
+/* Program a (non-VGA) video mode. */
+int
+intelfbhw_program_mode(struct intelfb_info *dinfo,
+		     const struct intelfb_hwstate *hw, int blank)
+{
+	int pipe = PIPE_A;
+	u32 tmp;
+	const u32 *dpll, *fp0, *fp1, *pipe_conf;
+	const u32 *hs, *ht, *hb, *vs, *vt, *vb, *ss;
+	u32 dpll_reg, fp0_reg, fp1_reg, pipe_conf_reg;
+	u32 hsync_reg, htotal_reg, hblank_reg;
+	u32 vsync_reg, vtotal_reg, vblank_reg;
+	u32 src_size_reg;
+
+	/* Assume single pipe, display plane A, analog CRT. */
+
+	DBG_MSG("intelfbhw_program_mode\n");
+
+	/* Disable VGA */
+	tmp = INREG(VGACNTRL);
+	tmp |= VGA_DISABLE;
+	OUTREG(VGACNTRL, tmp);
+
+	/* Check whether pipe A or pipe B is enabled. */
+	if (hw->pipe_a_conf & PIPECONF_ENABLE)
+		pipe = PIPE_A;
+	else if (hw->pipe_b_conf & PIPECONF_ENABLE)
+		pipe = PIPE_B;
+
+	dinfo->pipe = pipe;
+
+	if (pipe == PIPE_B) {
+		dpll = &hw->dpll_b;
+		fp0 = &hw->fpb0;
+		fp1 = &hw->fpb1;
+		pipe_conf = &hw->pipe_b_conf;
+		hs = &hw->hsync_b;
+		hb = &hw->hblank_b;
+		ht = &hw->htotal_b;
+		vs = &hw->vsync_b;
+		vb = &hw->vblank_b;
+		vt = &hw->vtotal_b;
+		ss = &hw->src_size_b;
+		dpll_reg = DPLL_B;
+		fp0_reg = FPB0;
+		fp1_reg = FPB1;
+		pipe_conf_reg = PIPEBCONF;
+		hsync_reg = HSYNC_B;
+		htotal_reg = HTOTAL_B;
+		hblank_reg = HBLANK_B;
+		vsync_reg = VSYNC_B;
+		vtotal_reg = VTOTAL_B;
+		vblank_reg = VBLANK_B;
+		src_size_reg = SRC_SIZE_B;
+	} else {
+		dpll = &hw->dpll_a;
+		fp0 = &hw->fpa0;
+		fp1 = &hw->fpa1;
+		pipe_conf = &hw->pipe_a_conf;
+		hs = &hw->hsync_a;
+		hb = &hw->hblank_a;
+		ht = &hw->htotal_a;
+		vs = &hw->vsync_a;
+		vb = &hw->vblank_a;
+		vt = &hw->vtotal_a;
+		ss = &hw->src_size_a;
+		dpll_reg = DPLL_A;
+		fp0_reg = FPA0;
+		fp1_reg = FPA1;
+		pipe_conf_reg = PIPEACONF;
+		hsync_reg = HSYNC_A;
+		htotal_reg = HTOTAL_A;
+		hblank_reg = HBLANK_A;
+		vsync_reg = VSYNC_A;
+		vtotal_reg = VTOTAL_A;
+		vblank_reg = VBLANK_A;
+		src_size_reg = SRC_SIZE_A;
+	}
+
+	/* Disable planes A and B. */
+	tmp = INREG(DSPACNTR);
+	tmp &= ~DISPPLANE_PLANE_ENABLE;
+	OUTREG(DSPACNTR, tmp);
+	tmp = INREG(DSPBCNTR);
+	tmp &= ~DISPPLANE_PLANE_ENABLE;
+	OUTREG(DSPBCNTR, tmp);
+
+	/* Wait for vblank.  For now, just wait for a 50Hz cycle (20ms)) */
+	mdelay(20);
+
+	/* Disable Sync */
+	tmp = INREG(ADPA);
+	tmp &= ~ADPA_DPMS_CONTROL_MASK;
+	tmp |= ADPA_DPMS_D3;
+	OUTREG(ADPA, tmp);
+
+	/* turn off pipe */
+	tmp = INREG(pipe_conf_reg);
+	tmp &= ~PIPECONF_ENABLE;
+	OUTREG(pipe_conf_reg, tmp);
+
+	/* turn off PLL */
+	tmp = INREG(dpll_reg);
+	dpll_reg &= ~DPLL_VCO_ENABLE;
+	OUTREG(dpll_reg, tmp);
+
+	/* Set PLL parameters */
+	OUTREG(dpll_reg, *dpll & ~DPLL_VCO_ENABLE);
+	OUTREG(fp0_reg, *fp0);
+	OUTREG(fp1_reg, *fp1);
+
+	/* Set pipe parameters */
+	OUTREG(hsync_reg, *hs);
+	OUTREG(hblank_reg, *hb);
+	OUTREG(htotal_reg, *ht);
+	OUTREG(vsync_reg, *vs);
+	OUTREG(vblank_reg, *vb);
+	OUTREG(vtotal_reg, *vt);
+	OUTREG(src_size_reg, *ss);
+	
+	/* Set ADPA */
+	OUTREG(ADPA, (hw->adpa & ~(ADPA_DPMS_CONTROL_MASK)) | ADPA_DPMS_D3);
+
+	/* Enable PLL */
+	tmp = INREG(dpll_reg);
+	tmp |= DPLL_VCO_ENABLE;
+	OUTREG(dpll_reg, tmp);
+
+	/* Enable pipe */
+	OUTREG(pipe_conf_reg, *pipe_conf | PIPECONF_ENABLE);
+
+	/* Enable sync */
+	tmp = INREG(ADPA);
+	tmp &= ~ADPA_DPMS_CONTROL_MASK;
+	tmp |= ADPA_DPMS_D0;
+	OUTREG(ADPA, tmp);
+
+	/* setup display plane */
+	OUTREG(DSPACNTR, hw->disp_a_ctrl & ~DISPPLANE_PLANE_ENABLE);
+	OUTREG(DSPASTRIDE, hw->disp_a_stride);
+	OUTREG(DSPABASE, hw->disp_a_base);
+
+	/* Enable plane */
+	if (!blank) {
+		tmp = INREG(DSPACNTR);
+		tmp |= DISPPLANE_PLANE_ENABLE;
+		OUTREG(DSPACNTR, tmp);
+		OUTREG(DSPABASE, hw->disp_a_base);
+	}
+
+	return 0;
+}
+
+static int
+wait_ring(struct intelfb_info *dinfo, int n)
+{
+	int i = 0;
+	unsigned long end;
+	u32 last_head = INREG(PRI_RING_HEAD) & RING_HEAD_MASK;
+
+#if 0
+	DBG_MSG("wait_ring: %d\n", n);
+#endif
+
+	end = jiffies + (HZ * 3);
+	while (dinfo->ring_space < n) {
+		dinfo->ring_head = INREG(PRI_RING_HEAD) & RING_HEAD_MASK;
+		dinfo->ring_space = dinfo->ring_head -
+				    (dinfo->ring_tail + RING_MIN_FREE);
+		if (dinfo->ring_space < 0)
+			dinfo->ring_space += dinfo->ring_size;
+		if (dinfo->ring_head != last_head) {
+			end = jiffies + (HZ * 3);
+			last_head = dinfo->ring_head;
+		}
+		i++;
+		if (time_before(end, jiffies)) {
+			WRN_MSG("space: %d wanted %d\n", dinfo->ring_space, n);
+			WRN_MSG("lockup\n");
+			break;
+		}
+		udelay(1);
+	}
+	return i;
+}
+
+void
+intelfbhw_do_sync(struct intelfb_info *dinfo)
+{
+#if USE_SYNC_PAGE
+	u32 newval;
+#endif
+	u32 tmp;
+	int i = 0;
+
+#if VERBOSE > 0
+	DBG_MSG("intelfbhw_do_sync\n");
+#endif
+
+#if USE_SYNC_PAGE
+	/*
+	 * Although doing MI_STORE_DWORD_IMM after the MI_FLUSH is supposed
+	 * to make sure everything is synchronised, there is still some
+	 * mis-ordering of operations when mixing 2D with direct CPU
+	 * writes to the framebuffer.
+	 */
+	newval = readl(dinfo->syncpage_virt);
+	newval++;
+#if VERBOSE > 0
+	DBG_MSG("intelfbhw_do_sync: %d\n", newval);
+#endif
+	START_RING(6);
+	OUT_RING(MI_FLUSH | MI_WRITE_DIRTY_STATE);
+	OUT_RING(MI_NOOP);
+	OUT_RING(MI_STORE_DWORD_IMM);
+	OUT_RING(dinfo->syncpage_phys);
+	OUT_RING(newval);
+	OUT_RING(MI_NOOP);
+	ADVANCE_RING();
+	while ((tmp = readl(dinfo->syncpage_virt)) != newval && i < 10000) {
+		i++;
+		udelay(10);
+	}
+	if (tmp != newval) {
+		DBG_MSG("intelfbhw_do_sync: STORE_DWORD_IMM returns %d "
+			"instead of %d\n", tmp, newval);
+	} else {
+#if VERBOSE > 1
+		DBG_MSG("intelfbhw_do_sync: done in %d iterations\n", i);
+#endif
+	}
+#else
+	/*
+	 * Send a flush, then wait until the ring is empty.  This is what
+	 * the XFree86 driver does, and actually it doesn't seem a lot worse
+	 * than the recommended method (both have problems).
+	 */
+	START_RING(2);
+	OUT_RING(MI_FLUSH | MI_WRITE_DIRTY_STATE);
+	OUT_RING(MI_NOOP);
+	ADVANCE_RING();
+	wait_ring(dinfo, dinfo->ring_size - RING_MIN_FREE);
+	dinfo->ring_space = dinfo->ring_size - RING_MIN_FREE;
+#endif
+}
+
+static void
+refresh_ring(struct intelfb_info *dinfo)
+{
+	DBG_MSG("refresh_ring\n");
+
+	dinfo->ring_head = INREG(PRI_RING_HEAD) & RING_HEAD_MASK;
+	dinfo->ring_tail = INREG(PRI_RING_TAIL) & RING_TAIL_MASK;
+	dinfo->ring_space = dinfo->ring_head -
+			    (dinfo->ring_tail + RING_MIN_FREE);
+	if (dinfo->ring_space < 0)
+		dinfo->ring_space += dinfo->ring_size;
+}
+
+static void
+reset_state(struct intelfb_info *dinfo)
+{
+	int i;
+	u32 tmp;
+
+	DBG_MSG("reset_state\n");
+
+	for (i = 0; i < FENCE_NUM; i++)
+		OUTREG(FENCE + (i << 2), 0);
+
+	/* Flush the ring buffer if it's enabled. */
+	tmp = INREG(PRI_RING_LENGTH);
+	if (tmp & RING_ENABLE) {
+		DBG_MSG("reset_state: ring was enabled\n");
+		refresh_ring(dinfo);
+		intelfbhw_do_sync(dinfo);
+		DO_RING_IDLE();
+	}
+
+	OUTREG(PRI_RING_LENGTH, 0);
+	OUTREG(PRI_RING_HEAD, 0);
+	OUTREG(PRI_RING_TAIL, 0);
+	OUTREG(PRI_RING_START, 0);
+}
+
+/* Stop the 2D engine, and turn off the ring buffer. */
+void
+intelfbhw_2d_stop(struct intelfb_info *dinfo)
+{
+	DBG_MSG("intelfbhw_2d_stop: accel: %d, ring_active: %d\n", dinfo->accel,
+		dinfo->ring_active);
+
+	if (!dinfo->accel)
+		return;
+
+	dinfo->ring_active = 0;
+	reset_state(dinfo);
+}
+
+/*
+ * Enable the ring buffer, and initialise the 2D engine.
+ * It is assumed that the graphics engine has been stopped by previously
+ * calling intelfb_2d_stop().
+ */
+void
+intelfbhw_2d_start(struct intelfb_info *dinfo)
+{
+	DBG_MSG("intelfbhw_2d_start: accel: %d, ring_active: %d\n",
+		dinfo->accel, dinfo->ring_active);
+
+	if (!dinfo->accel)
+		return;
+
+	/* Initialise the primary ring buffer. */
+	OUTREG(PRI_RING_LENGTH, 0);
+	OUTREG(PRI_RING_TAIL, 0);
+	OUTREG(PRI_RING_HEAD, 0);
+
+	OUTREG(PRI_RING_START, dinfo->ring_base_phys & RING_START_MASK);
+	OUTREG(PRI_RING_LENGTH,
+		((dinfo->ring_size - GTT_PAGE_SIZE) & RING_LENGTH_MASK) |
+		RING_NO_REPORT | RING_ENABLE);
+	refresh_ring(dinfo);
+	dinfo->ring_active = 1;
+
+	DBG_MSG("INSTPM was 0x%08x, setting to 0x%08x\n", INREG(INSTPM),
+		0x1f << 16);
+	OUTREG(INSTPM, 0x1f << 16);
+	OUTREG(INSTPM, 0x1f << 16);
+}
+
+/* 2D fillrect (solid fill or invert) */
+void
+intelfbhw_do_fillrect(struct intelfb_info *dinfo, u32 x, u32 y, u32 w, u32 h,
+		      u32 color, u32 pitch, u32 bpp, u32 rop)
+{
+	u32 br00, br09, br13, br14, br16;
+
+#if VERBOSE > 1
+	DBG_MSG("intelfbhw_do_fillrect: (%d,%d) %dx%d, c 0x%06x, p %d bpp %d, "
+		"rop 0x%02x\n", x, y, w, h, color, pitch, bpp, rop);
+#endif
+
+	br00 = COLOR_BLT_CMD;
+	br09 = dinfo->fb_offset + (y * pitch + x * (bpp / 8));
+	br13 = (rop << ROP_SHIFT) | pitch;
+	br14 = (h << HEIGHT_SHIFT) | ((w * bpp / 8) << WIDTH_SHIFT);
+	br16 = color;
+
+	switch (bpp) {
+	case 8:
+		br13 |= COLOR_DEPTH_8;
+		break;
+	case 16:
+		br13 |= COLOR_DEPTH_16;
+		break;
+	case 32:
+		br13 |= COLOR_DEPTH_32;
+		br00 |= WRITE_ALPHA | WRITE_RGB;
+		break;
+	}
+
+	START_RING(6);
+	OUT_RING(br00);
+	OUT_RING(br13);
+	OUT_RING(br14);
+	OUT_RING(br09);
+	OUT_RING(br16);
+	OUT_RING(MI_NOOP);
+	ADVANCE_RING();
+}
+
+void
+intelfbhw_do_bitblt(struct intelfb_info *dinfo, u32 curx, u32 cury,
+		    u32 dstx, u32 dsty, u32 w, u32 h, u32 pitch, u32 bpp)
+{
+	u32 br00, br09, br11, br12, br13, br22, br23, br26;
+
+#if VERBOSE > 0
+	DBG_MSG("intelfbhw_do_bitblt: (%d,%d)->(%d,%d) %dx%d, p %d bpp %d\n",
+		curx, cury, dstx, dsty, w, h, pitch, bpp);
+#endif
+
+	br00 = XY_SRC_COPY_BLT_CMD;
+	br09 = dinfo->fb_offset;
+	br11 = (pitch << PITCH_SHIFT);
+	br12 = dinfo->fb_offset;
+	br13 = (SRC_ROP_GXCOPY << ROP_SHIFT) | (pitch << PITCH_SHIFT);
+	br22 = (dstx << WIDTH_SHIFT) | (dsty << HEIGHT_SHIFT);
+	br23 = ((dstx + w) << WIDTH_SHIFT) |
+	       ((dsty + h) << HEIGHT_SHIFT);
+	br26 = (curx << WIDTH_SHIFT) | (cury << HEIGHT_SHIFT);
+
+	switch (bpp) {
+	case 8:
+		br13 |= COLOR_DEPTH_8;
+		break;
+	case 16:
+		br13 |= COLOR_DEPTH_16;
+		break;
+	case 32:
+		br13 |= COLOR_DEPTH_32;
+		br00 |= WRITE_ALPHA | WRITE_RGB;
+		break;
+	}
+
+	START_RING(8);
+	OUT_RING(br00);
+	OUT_RING(br13);
+	OUT_RING(br22);
+	OUT_RING(br23);
+	OUT_RING(br09);
+	OUT_RING(br26);
+	OUT_RING(br11);
+	OUT_RING(br12);
+	ADVANCE_RING();
+}
+
+int
+intelfbhw_do_drawglyph(struct intelfb_info *dinfo, u32 fg, u32 bg, u32 w, u32 h,
+		       u8* cdat, u32 x, u32 y, u32 pitch, u32 bpp)
+{
+	int i, n = 0, fw_bytes, bytes_per_src_line;
+	int nbytes, ndwords, pad, tmp;
+	u16 *wcdat;
+	u32 *dwcdat;
+	u32 br00, br09, br13, br18, br19, br22, br23;
+
+#if 0
+	DBG_MSG("intelfbhw_do_drawglyph: (%d,%d) %dx%d\n", x, y, w, h);
+#endif
+
+	/* Support fonts up to 32 pixels wide. */
+	if (w > 32)
+		return 0;
+
+	/* Number of bytes required for each cdat scanline. */
+	fw_bytes = ROUND_UP_TO(w, 8) / 8;
+
+	/* Src scanlines are word (16-bit) padded. */
+	bytes_per_src_line = ROUND_UP_TO(fw_bytes, 2);
+
+	/* Total bytes of padded scanline data to write out. */
+	nbytes = h * bytes_per_src_line;
+
+	/*
+	 * Check if the glyph data exceeds the immediate mode limit.
+	 * It would take a large font (1K pixels) to hit this limit.
+	 */
+	if (nbytes > MAX_MONO_IMM_SIZE)
+		return 0;
+
+	/* Src data is packaged a dword (32-bit) at a time. */
+	ndwords = ROUND_UP_TO(nbytes, 4) / 4;
+
+	/* Ring has to be padded to a quad word. */
+	pad = ndwords % 2;
+
+	/* For easy reference of the glyph data in different sized chunks. */
+	wcdat = (u16 *)cdat;
+	dwcdat = (u32 *)cdat;
+
+	tmp = (XY_MONO_SRC_IMM_BLT_CMD & DW_LENGTH_MASK) + ndwords;
+	br00 = (XY_MONO_SRC_IMM_BLT_CMD & ~DW_LENGTH_MASK) | tmp;
+	br09 = dinfo->fb_offset;
+	br13 = (SRC_ROP_GXCOPY << ROP_SHIFT) | (pitch << PITCH_SHIFT);
+	br18 = bg;
+	br19 = fg;
+	br22 = (x << WIDTH_SHIFT) | (y << HEIGHT_SHIFT);
+	br23 = ((x + w) << WIDTH_SHIFT) | ((y + h) << HEIGHT_SHIFT);
+
+	switch (bpp) {
+	case 8:
+		br13 |= COLOR_DEPTH_8;
+		break;
+	case 16:
+		br13 |= COLOR_DEPTH_16;
+		break;
+	case 32:
+		br13 |= COLOR_DEPTH_32;
+		br00 |= WRITE_ALPHA | WRITE_RGB;
+		break;
+	}
+
+#if 0
+	DBG_MSG("ndwords + pad is %d, pad is %d\n", ndwords + pad, pad);
+#endif
+	START_RING(ndwords + pad);
+	OUT_RING(br00);
+	OUT_RING(br13);
+	OUT_RING(br22);
+	OUT_RING(br23);
+	OUT_RING(br09);
+	OUT_RING(br18);
+	OUT_RING(br19);
+	i = h;
+	switch (fw_bytes) {
+	case 1:
+		while (i >= 2) {
+			OUT_RING(cdat[0] | cdat[1] << 16);
+			cdat +=2;
+			i -= 2;
+			n++;
+		}
+		if (i) {
+			OUT_RING(cdat[0]);
+			n++;
+		}
+		break;
+	case 2:
+		while (i >= 2) {
+			OUT_RING(wcdat[0] | wcdat[1] << 16);
+			wcdat += 2;
+			i -= 2;
+			n++;
+		}
+		if (i) {
+			OUT_RING(wcdat[0]);
+			n++;
+		}
+		break;
+	case 3:
+		while (i) {
+			OUT_RING(wcdat[0] | cdat[3] << 16);
+			wcdat += 2;
+			cdat += 4;
+			i--;
+			n++;
+		}
+		break;
+	case 4:
+		while(i) {
+			OUT_RING(dwcdat[0]);
+			i--;
+			n++;
+		}
+	}
+	if (pad) {
+		OUT_RING(MI_NOOP);
+		n++;
+	}
+#if 0
+	DBG_MSG("%d immediate bytes + pad\n", n);
+#endif
+	ADVANCE_RING();
+	return 1;
+}
+
+/* HW cursor functions. */
+void
+intelfbhw_cursor_init(struct intelfb_info *dinfo)
+{
+	u32 tmp;
+
+	DBG_MSG("intelfbhw_cursor_init\n");
+
+	if (!dinfo->cursor_base)
+		return;
+
+	if (dinfo->mobile) {
+		if (!dinfo->cursor_base_real)
+			return;
+		tmp = INREG(CURSOR_A_CONTROL);
+		tmp &= ~(CURSOR_MODE_MASK | CURSOR_MOBILE_GAMMA_ENABLE |
+			 CURSOR_MEM_TYPE_LOCAL |
+			 (1 << CURSOR_PIPE_SELECT_SHIFT));
+		tmp |= CURSOR_MODE_DISABLE;
+		OUTREG(CURSOR_A_CONTROL, tmp);
+		OUTREG(CURSOR_A_BASEADDR, dinfo->cursor_base_real);
+	} else {
+#if 0
+		tmp = INREG(CURSOR_CONTROL);
+		tmp &= ~(CURSOR_FORMAT_MASK | CURSOR_GAMMA_ENABLE |
+			 CURSOR_ENABLE | CURSOR_STRIDE_MASK);
+#endif
+		tmp = CURSOR_FORMAT_3C;
+		OUTREG(CURSOR_CONTROL, tmp);
+		OUTREG(CURSOR_A_BASEADDR, dinfo->cursor_offset);
+		tmp = (64 << CURSOR_SIZE_H_SHIFT) |
+		      (64 << CURSOR_SIZE_V_SHIFT);
+		OUTREG(CURSOR_SIZE, tmp);
+	}
+}
+
+void
+intelfbhw_cursor_hide(struct intelfb_info *dinfo)
+{
+	u32 tmp;
+
+#if VERBOSE > 1
+	DBG_MSG("intelfbhw_cursor_hide\n");
+#endif
+
+	if (!dinfo->cursor_base)
+		return;
+
+	dinfo->cursor.enabled = 0;
+	dinfo->cursor.on = 0;
+	if (dinfo->mobile) {
+		if (!dinfo->cursor_base_real)
+			return;
+		tmp = INREG(CURSOR_A_CONTROL);
+		tmp &= ~CURSOR_MODE_MASK;
+		tmp |= CURSOR_MODE_DISABLE;
+		OUTREG(CURSOR_A_CONTROL, tmp);
+		/* Flush changes */
+		OUTREG(CURSOR_A_BASEADDR, dinfo->cursor_base_real);
+	} else {
+		tmp = INREG(CURSOR_CONTROL);
+		tmp &= ~CURSOR_ENABLE;
+		OUTREG(CURSOR_CONTROL, tmp);
+	}
+}
+
+void
+intelfbhw_cursor_show(struct intelfb_info *dinfo)
+{
+	u32 tmp;
+
+#if VERBOSE > 1
+	DBG_MSG("intelfbhw_cursor_show\n");
+#endif
+
+	if (!dinfo->cursor_base)
+		return;
+
+	dinfo->cursor.on = 1;
+	dinfo->cursor.enabled = 1;
+
+	if (dinfo->cursor.blanked)
+		return;
+
+	if (dinfo->mobile) {
+		if (!dinfo->cursor_base_real)
+			return;
+		tmp = INREG(CURSOR_A_CONTROL);
+		tmp &= ~CURSOR_MODE_MASK;
+		tmp |= CURSOR_MODE_64_4C_AX;
+		OUTREG(CURSOR_A_CONTROL, tmp);
+		/* Flush changes */
+		OUTREG(CURSOR_A_BASEADDR, dinfo->cursor_base_real);
+	} else {
+		tmp = INREG(CURSOR_CONTROL);
+		tmp |= CURSOR_ENABLE;
+		OUTREG(CURSOR_CONTROL, tmp);
+	}
+}
+
+void
+intelfbhw_cursor_setpos(struct intelfb_info *dinfo, int x, int y)
+{
+	u32 tmp;
+
+#if VERBOSE > 1
+	DBG_MSG("intelfbhw_cursor_setpos: (%d, %d)\n", x, y);
+#endif
+
+	/*
+	 * Sets the position.  The coordinates are assumed to already
+	 * have any offset adjusted.  Assume that the cursor is never
+	 * completely off-screen, and that x, y are always >= 0.
+	 */
+
+	if (!dinfo->cursor_base)
+		return;
+
+	tmp = ((x & CURSOR_POS_MASK) << CURSOR_X_SHIFT) |
+	      ((y & CURSOR_POS_MASK) << CURSOR_Y_SHIFT);
+	OUTREG(CURSOR_A_POSITION, tmp);
+}
+
+void
+intelfbhw_cursor_setcolor(struct intelfb_info *dinfo, u32 bg, u32 fg)
+{
+#if VERBOSE > 1
+	DBG_MSG("intelfbhw_cursor_setcolor\n");
+#endif
+
+	if (!dinfo->cursor_base)
+		return;
+
+	OUTREG(CURSOR_A_PALETTE0, bg & CURSOR_PALETTE_MASK);
+	OUTREG(CURSOR_A_PALETTE1, fg & CURSOR_PALETTE_MASK);
+	OUTREG(CURSOR_A_PALETTE2, fg & CURSOR_PALETTE_MASK);
+	OUTREG(CURSOR_A_PALETTE3, bg & CURSOR_PALETTE_MASK);
+}
+
+void
+intelfbhw_cursor_load(struct intelfb_info *dinfo, struct display *disp)
+{
+	u32 xline, mline;
+	int i;
+
+	DBG_MSG("intelfbhw_cursor_load\n");
+
+	if (!dinfo->cursor_base)
+		return;
+
+	intelfb_create_cursor_shape(dinfo, disp);
+	xline = (1 << dinfo->cursor.w) - 1;
+	mline = ~xline;
+	for (i = 0; i < dinfo->cursor.u; i++) {
+		writel(~0, dinfo->cursor_base + i * 16);
+		writel(~0, dinfo->cursor_base + i * 16 + 4);
+		writel(0, dinfo->cursor_base + i * 16 + 8);
+		writel(0, dinfo->cursor_base + i * 16 + 12);
+	}
+	for (; i < dinfo->cursor.d; i++) {
+		writel(mline, dinfo->cursor_base + i * 16);
+		writel(~0, dinfo->cursor_base + i * 16 + 4);
+		writel(xline, dinfo->cursor_base + i * 16 + 8);
+		writel(0, dinfo->cursor_base + i * 16 + 12);
+	}
+	for (; i < 64; i++) {
+		writel(~0, dinfo->cursor_base + i * 16);
+		writel(~0, dinfo->cursor_base + i * 16 + 4);
+		writel(0, dinfo->cursor_base + i * 16 + 8);
+		writel(0, dinfo->cursor_base + i * 16 + 12);
+	}
+}
+
+
diff -urN linux-2.4.21-pre5/drivers/video/intel/intelfbhw.h linux-2.4.21-pre6/drivers/video/intel/intelfbhw.h
--- linux-2.4.21-pre5/drivers/video/intel/intelfbhw.h	Wed Dec 31 16:00:00 1969
+++ linux-2.4.21-pre6/drivers/video/intel/intelfbhw.h	Wed Mar 26 16:11:39 2003
@@ -0,0 +1,513 @@
+#ifndef _INTELFBHW_H
+#define _INTELFBHW_H
+
+/* $DHD: intelfb/intelfbhw.h,v 1.3 2003/02/05 20:23:56 dawes Exp $ */
+/* $TG$ */
+
+
+/*** HW-specific data ***/
+
+/* Information about the 852GM/855GM variants */
+#define INTEL_85X_CAPID		0x44
+#define INTEL_85X_VARIANT_MASK		0x7
+#define INTEL_85X_VARIANT_SHIFT		5
+#define INTEL_VAR_855GME		0x0
+#define INTEL_VAR_855GM			0x4
+#define INTEL_VAR_852GME		0x2
+#define INTEL_VAR_852GM			0x5
+
+/*
+ * The Bridge device's PCI config space has information about the
+ * fb aperture size and the amount of pre-reserved memory.
+ */
+#define INTEL_GMCH_CTRL		0x52
+#define INTEL_GMCH_ENABLED		0x4
+#define INTEL_GMCH_MEM_MASK		0x1
+#define INTEL_GMCH_MEM_64M		0x1
+#define INTEL_GMCH_MEM_128M		0
+
+#define INTEL_830_GMCH_GMS_MASK		(0x7 << 4)
+#define INTEL_830_GMCH_GMS_DISABLED	(0x0 << 4)
+#define INTEL_830_GMCH_GMS_LOCAL	(0x1 << 4)
+#define INTEL_830_GMCH_GMS_STOLEN_512	(0x2 << 4)
+#define INTEL_830_GMCH_GMS_STOLEN_1024	(0x3 << 4)
+#define INTEL_830_GMCH_GMS_STOLEN_8192	(0x4 << 4)
+
+#define INTEL_855_GMCH_GMS_MASK		(0x7 << 4)
+#define INTEL_855_GMCH_GMS_DISABLED	(0x0 << 4)
+#define INTEL_855_GMCH_GMS_STOLEN_1M	(0x1 << 4)
+#define INTEL_855_GMCH_GMS_STOLEN_4M	(0x2 << 4)
+#define INTEL_855_GMCH_GMS_STOLEN_8M	(0x3 << 4)
+#define INTEL_855_GMCH_GMS_STOLEN_16M	(0x4 << 4)
+#define INTEL_855_GMCH_GMS_STOLEN_32M	(0x5 << 4)
+
+/* GTT page size */
+#define GTT_PAGE_SIZE		4096
+
+/* HW registers */
+
+/* Fence registers */
+#define FENCE			0x2000
+#define FENCE_NUM			8
+
+/* Primary ring buffer */
+#define PRI_RING_TAIL		0x2030
+#define RING_TAIL_MASK			0x001ffff8
+#define RING_INUSE			0x1
+
+#define PRI_RING_HEAD		0x2034
+#define RING_HEAD_WRAP_MASK		0x7ff
+#define RING_HEAD_WRAP_SHIFT		21
+#define RING_HEAD_MASK			0x001ffffc
+
+#define PRI_RING_START		0x2038
+#define RING_START_MASK			0xfffff000
+
+#define PRI_RING_LENGTH		0x203c
+#define RING_LENGTH_MASK		0x001ff000
+#define RING_REPORT_MASK		(0x3 << 1)
+#define RING_NO_REPORT			(0x0 << 1)
+#define RING_REPORT_64K			(0x1 << 1)
+#define RING_REPORT_4K			(0x2 << 1)
+#define RING_REPORT_128K		(0x3 << 1)
+#define RING_ENABLE			0x1
+
+/*
+ * Tail can't wrap to any closer than RING_MIN_FREE bytes of the head,
+ * and the last RING_MIN_FREE bytes need to be padded with MI_NOOP
+ */
+#define RING_MIN_FREE			64
+
+#define INSTDONE		0x2090
+#define PRI_RING_EMPTY			1
+
+#define INSTPM			0x20c0
+#define SYNC_FLUSH_ENABLE		(1 << 5)
+
+#define MEM_MODE		0x20cc
+
+#define MASK_SHIFT			16
+
+#define FW_BLC_0		0x20d8
+#define FW_DISPA_WM_SHIFT		0
+#define FW_DISPA_WM_MASK		0x3f
+#define FW_DISPA_BL_SHIFT		8
+#define FW_DISPA_BL_MASK		0xf
+#define FW_DISPB_WM_SHIFT		16
+#define FW_DISPB_WM_MASK		0x1f
+#define FW_DISPB_BL_SHIFT		24
+#define FW_DISPB_BL_MASK		0x7
+
+#define FW_BLC_1		0x20dc
+#define FW_DISPC_WM_SHIFT		0
+#define FW_DISPC_WM_MASK		0x1f
+#define FW_DISPC_BL_SHIFT		8
+#define FW_DISPC_BL_MASK		0x7
+
+
+/* PLL registers */
+#define VGA0_DIVISOR		0x06000
+#define VGA1_DIVISOR		0x06004
+#define VGAPD			0x06010
+#define VGAPD_0_P1_SHIFT		0
+#define VGAPD_0_P1_FORCE_DIV2		(1 << 5)
+#define VGAPD_0_P2_SHIFT		7
+#define VGAPD_1_P1_SHIFT		8
+#define VGAPD_1_P1_FORCE_DIV2		(1 << 13)
+#define VGAPD_1_P2_SHIFT		15
+
+#define DPLL_A			0x06014
+#define DPLL_B			0x06018
+#define DPLL_VCO_ENABLE			(1 << 31)
+#define DPLL_2X_CLOCK_ENABLE		(1 << 30)
+#define DPLL_SYNCLOCK_ENABLE		(1 << 29)
+#define DPLL_VGA_MODE_DISABLE		(1 << 28)
+#define DPLL_P2_MASK			1
+#define DPLL_P2_SHIFT			23
+#define DPLL_P1_FORCE_DIV2		(1 << 21)
+#define DPLL_P1_MASK			0x1f
+#define DPLL_P1_SHIFT			16
+#define DPLL_REFERENCE_SELECT_MASK	(0x3 << 13)
+#define DPLL_REFERENCE_DEFAULT		(0x0 << 13)
+#define DPLL_REFERENCE_TVCLK		(0x2 << 13)
+#define DPLL_RATE_SELECT_MASK		(1 << 8)
+#define DPLL_RATE_SELECT_FP0		(0 << 8)
+#define DPLL_RATE_SELECT_FP1		(1 << 8)
+
+#define FPA0			0x06040
+#define FPA1			0x06044
+#define FPB0			0x06048
+#define FPB1			0x0604c
+#define FP_DIVISOR_MASK			0x3f
+#define FP_N_DIVISOR_SHIFT		16
+#define FP_M1_DIVISOR_SHIFT		8
+#define FP_M2_DIVISOR_SHIFT		0
+
+/* PLL parameters (these are for 852GM/855GM/865G, check earlier chips). */
+/* Clock values are in units of kHz */
+#define PLL_REFCLK		48000
+#define MIN_VCO_FREQ		930000
+#define MAX_VCO_FREQ		1400000
+#define MIN_CLOCK		25000
+#define MAX_CLOCK		350000
+#define P_TRANSITION_CLOCK	165000
+#define MIN_M			108
+#define MAX_M			140
+#define MIN_M1			18
+#define MAX_M1			26
+#define MIN_M2			6
+#define MAX_M2			16
+#define MIN_P			4
+#define MAX_P			128
+#define MIN_P1			0
+#define MAX_P1			31
+#define MIN_N			3
+#define MAX_N			16
+
+#define CALC_VCLOCK(m1, m2, n, p1, p2) \
+        ((PLL_REFCLK * (5 * ((m1) + 2) + ((m2) + 2)) / ((n) + 2)) / \
+        (((p1) + 2) * (1 << (p2 + 1))))
+
+#define CALC_VCLOCK3(m, n, p)	((PLL_REFCLK * (m) / (n)) / (p))
+
+/* Two pipes */
+#define PIPE_A			0
+#define PIPE_B			1
+#define PIPE_MASK		1
+
+/* palette registers */
+#define PALETTE_A		0x0a000
+#define PALETTE_B		0x0a800
+#ifndef PALETTE_8_ENTRIES
+#define PALETTE_8_ENTRIES		256
+#endif
+#define PALETTE_8_SIZE			(PALETTE_8_ENTRIES * 4)
+#define PALETTE_10_ENTRIES		128
+#define PALETTE_8_SIZE			(PALETTE_10_ENTRIES * 8)
+#define PALETTE_8_MASK			0xff
+#define PALETTE_8_RED_SHIFT		16
+#define PALETTE_8_GREEN_SHIFT		8
+#define PALETTE_8_BLUE_SHIFT		0
+
+/* CRTC registers */
+#define HTOTAL_A		0x60000
+#define HBLANK_A		0x60004
+#define HSYNC_A			0x60008
+#define VTOTAL_A		0x6000c
+#define VBLANK_A		0x60010
+#define VSYNC_A			0x60014
+#define SRC_SIZE_A		0x6001c
+#define BCLRPAT_A		0x60020
+
+#define HTOTAL_B		0x61000
+#define HBLANK_B		0x61004
+#define HSYNC_B			0x61008
+#define VTOTAL_B		0x6100c
+#define VBLANK_B		0x61010
+#define VSYNC_B			0x61014
+#define SRC_SIZE_B		0x6101c
+#define BCLRPAT_B		0x61020
+
+#define HTOTAL_MASK			0xfff
+#define HTOTAL_SHIFT			16
+#define HACTIVE_MASK			0x7ff
+#define HACTIVE_SHIFT			0
+#define HBLANKEND_MASK			0xfff
+#define HBLANKEND_SHIFT			16
+#define HBLANKSTART_MASK		0xfff
+#define HBLANKSTART_SHIFT		0
+#define HSYNCEND_MASK			0xfff
+#define HSYNCEND_SHIFT			16
+#define HSYNCSTART_MASK			0xfff
+#define HSYNCSTART_SHIFT		0
+#define VTOTAL_MASK			0xfff
+#define VTOTAL_SHIFT			16
+#define VACTIVE_MASK			0x7ff
+#define VACTIVE_SHIFT			0
+#define VBLANKEND_MASK			0xfff
+#define VBLANKEND_SHIFT			16
+#define VBLANKSTART_MASK		0xfff
+#define VBLANKSTART_SHIFT		0
+#define VSYNCEND_MASK			0xfff
+#define VSYNCEND_SHIFT			16
+#define VSYNCSTART_MASK			0xfff
+#define VSYNCSTART_SHIFT		0
+#define SRC_SIZE_HORIZ_MASK		0x7ff
+#define SRC_SIZE_HORIZ_SHIFT		16
+#define SRC_SIZE_VERT_MASK		0x7ff
+#define SRC_SIZE_VERT_SHIFT		0
+
+#define ADPA			0x61100
+#define ADPA_DAC_ENABLE			(1 << 31)
+#define ADPA_DAC_DISABLE		0
+#define ADPA_PIPE_SELECT_SHIFT		30
+#define ADPA_USE_VGA_HVPOLARITY		(1 << 15)
+#define ADPA_SETS_HVPOLARITY		0
+#define ADPA_DPMS_CONTROL_MASK		(0x3 << 10)
+#define ADPA_DPMS_D0			(0x0 << 10)
+#define ADPA_DPMS_D2			(0x1 << 10)
+#define ADPA_DPMS_D1			(0x2 << 10)
+#define ADPA_DPMS_D3			(0x3 << 10)
+#define ADPA_VSYNC_ACTIVE_SHIFT		4
+#define ADPA_HSYNC_ACTIVE_SHIFT		3
+#define ADPA_SYNC_ACTIVE_MASK		1
+#define ADPA_SYNC_ACTIVE_HIGH		1
+#define ADPA_SYNC_ACTIVE_LOW		0
+
+#define DVOA			0x61120
+#define DVOB			0x61140
+#define DVOC			0x61160
+#define LVDS			0x61180
+#define PORT_ENABLE			(1 << 31)
+
+#define DVOA_SRCDIM		0x61124
+#define DVOB_SRCDIM		0x61144
+#define DVOC_SRCDIM		0x61164
+
+#define PIPEACONF		0x70008
+#define PIPEBCONF		0x71008
+#define PIPECONF_ENABLE			(1 << 31)
+#define PIPECONF_DISABLE		0
+#define PIPECONF_DOUBLE_WIDE		(1 << 30)
+#define PIPECONF_SINGLE_WIDE		0
+#define PIPECONF_LOCKED			(1 << 25)
+#define PIPECONF_UNLOCKED		0
+#define PIPECONF_GAMMA			(1 << 24)
+#define PIPECONF_PALETTE		0
+
+#define DISPARB			0x70030
+#define DISPARB_AEND_MASK		0x1ff
+#define DISPARB_AEND_SHIFT		0
+#define DISPARB_BEND_MASK		0x3ff
+#define DISPARB_BEND_SHIFT		9
+
+/* Desktop HW cursor */
+#define CURSOR_CONTROL		0x70080
+#define CURSOR_ENABLE			(1 << 31)
+#define CURSOR_GAMMA_ENABLE		(1 << 30)
+#define CURSOR_STRIDE_MASK		(0x3 << 28)
+#define CURSOR_STRIDE_256		(0x0 << 28)
+#define CURSOR_STRIDE_512		(0x1 << 28)
+#define CURSOR_STRIDE_1K		(0x2 << 28)
+#define CURSOR_STRIDE_2K		(0x3 << 28)
+#define CURSOR_FORMAT_MASK		(0x7 << 24)
+#define CURSOR_FORMAT_2C		(0x0 << 24)
+#define CURSOR_FORMAT_3C		(0x1 << 24)
+#define CURSOR_FORMAT_4C		(0x2 << 24)
+#define CURSOR_FORMAT_ARGB		(0x4 << 24)
+#define CURSOR_FORMAT_XRGB		(0x5 << 24)
+
+/* Mobile HW cursor (and i810) */
+#define CURSOR_A_CONTROL	CURSOR_CONTROL
+#define CURSOR_B_CONTROL	0x700c0
+#define CURSOR_MODE_MASK		0x27
+#define CURSOR_MODE_DISABLE		0
+#define CURSOR_MODE_64_3C		0x04
+#define CURSOR_MODE_64_4C_AX		0x05
+#define CURSOR_MODE_64_4C		0x06
+#define CURSOR_MODE_64_32B_AX		0x07
+#define CURSOR_MODE_64_ARGB_AX		0x27
+#define CURSOR_PIPE_SELECT_SHIFT	28
+#define CURSOR_MOBILE_GAMMA_ENABLE	(1 << 26)
+#define CURSOR_MEM_TYPE_LOCAL		(1 << 25)
+
+/* All platforms (desktop has no pipe B) */
+#define CURSOR_A_BASEADDR	0x70084
+#define CURSOR_B_BASEADDR	0x700c4
+#define CURSOR_BASE_MASK		0xffffff00
+
+#define CURSOR_A_POSITION	0x70088
+#define CURSOR_B_POSITION	0x700c8
+#define CURSOR_POS_SIGN			(1 << 15)
+#define CURSOR_POS_MASK			0x7ff
+#define CURSOR_X_SHIFT			0
+#define CURSOR_Y_SHIFT			16
+
+#define CURSOR_A_PALETTE0	0x70090
+#define CURSOR_A_PALETTE1	0x70094
+#define CURSOR_A_PALETTE2	0x70098
+#define CURSOR_A_PALETTE3	0x7009c
+#define CURSOR_B_PALETTE0	0x700d0
+#define CURSOR_B_PALETTE1	0x700d4
+#define CURSOR_B_PALETTE2	0x700d8
+#define CURSOR_B_PALETTE3	0x700dc
+#define CURSOR_COLOR_MASK			0xff
+#define CURSOR_RED_SHIFT			16
+#define CURSOR_GREEN_SHIFT			8
+#define CURSOR_BLUE_SHIFT			0
+#define CURSOR_PALETTE_MASK			0xffffff
+
+/* Desktop only */
+#define CURSOR_SIZE		0x700a0
+#define CURSOR_SIZE_MASK		0x3ff
+#define CURSOR_SIZE_H_SHIFT		0
+#define CURSOR_SIZE_V_SHIFT		12
+
+#define DSPACNTR		0x70180
+#define DSPBCNTR		0x71180
+#define DISPPLANE_PLANE_ENABLE		(1 << 31)
+#define DISPPLANE_PLANE_DISABLE		0
+#define DISPPLANE_GAMMA_ENABLE		(1<<30)
+#define DISPPLANE_GAMMA_DISABLE		0
+#define DISPPLANE_PIXFORMAT_MASK	(0xf<<26)
+#define DISPPLANE_8BPP			(0x2<<26)
+#define DISPPLANE_15_16BPP		(0x4<<26)
+#define DISPPLANE_16BPP			(0x5<<26)
+#define DISPPLANE_32BPP_NO_ALPHA 	(0x6<<26)
+#define DISPPLANE_32BPP			(0x7<<26)
+#define DISPPLANE_STEREO_ENABLE		(1<<25)
+#define DISPPLANE_STEREO_DISABLE	0
+#define DISPPLANE_SEL_PIPE_SHIFT	24
+#define DISPPLANE_SRC_KEY_ENABLE	(1<<22)
+#define DISPPLANE_SRC_KEY_DISABLE	0
+#define DISPPLANE_LINE_DOUBLE		(1<<20)
+#define DISPPLANE_NO_LINE_DOUBLE	0
+#define DISPPLANE_STEREO_POLARITY_FIRST	0
+#define DISPPLANE_STEREO_POLARITY_SECOND (1<<18)
+/* plane B only */
+#define DISPPLANE_ALPHA_TRANS_ENABLE	(1<<15)
+#define DISPPLANE_ALPHA_TRANS_DISABLE	0
+#define DISPPLANE_SPRITE_ABOVE_DISPLAYA	0
+#define DISPPLANE_SPRITE_ABOVE_OVERLAY	1
+
+#define DSPABASE		0x70184
+#define DSPASTRIDE		0x70188
+
+#define DSPBBASE		0x71184
+#define DSPBSTRIDE		0x71188
+
+#define VGACNTRL		0x71400
+#define VGA_DISABLE			(1 << 31)
+#define VGA_ENABLE			0
+#define VGA_PIPE_SELECT_SHIFT		29
+#define VGA_PALETTE_READ_SELECT		23
+#define VGA_PALETTE_A_WRITE_DISABLE	(1 << 22)
+#define VGA_PALETTE_B_WRITE_DISABLE	(1 << 21)
+#define VGA_LEGACY_PALETTE		(1 << 20)
+#define VGA_6BIT_DAC			0
+#define VGA_8BIT_DAC			(1 << 20)
+
+#define ADD_ID			0x71408
+#define ADD_ID_MASK			0xff
+
+/* BIOS scratch area registers (830M and 845G). */
+#define SWF0			0x71410
+#define SWF1			0x71414
+#define SWF2			0x71418
+#define SWF3			0x7141c
+#define SWF4			0x71420
+#define SWF5			0x71424
+#define SWF6			0x71428
+
+/* BIOS scratch area registers (852GM, 855GM, 865G). */
+#define SWF00			0x70410
+#define SWF01			0x70414
+#define SWF02			0x70418
+#define SWF03			0x7041c
+#define SWF04			0x70420
+#define SWF05			0x70424
+#define SWF06			0x70428
+
+#define SWF10			SWF0
+#define SWF11			SWF1
+#define SWF12			SWF2
+#define SWF13			SWF3
+#define SWF14			SWF4
+#define SWF15			SWF5
+#define SWF16			SWF6
+
+#define SWF30			0x72414
+#define SWF31			0x72418
+#define SWF32			0x7241c
+
+/* Memory Commands */
+#define MI_NOOP			(0x00 << 23)
+#define MI_NOOP_WRITE_ID		(1 << 22)
+#define MI_NOOP_ID_MASK			((1 << 22) - 1)
+
+#define MI_FLUSH		(0x03 << 23)
+#define MI_WRITE_DIRTY_STATE		(1 << 4)
+#define MI_END_SCENE			(1 << 3)
+#define MI_INHIBIT_RENDER_CACHE_FLUSH	(1 << 2)
+#define MI_INVALIDATE_MAP_CACHE		(1 << 0)
+
+#define MI_STORE_DWORD_IMM	((0x20 << 23) | 1)
+
+/* 2D Commands */
+#define COLOR_BLT_CMD		((2 << 29) | (0x40 << 22) | 3)
+#define XY_COLOR_BLT_CMD	((2 << 29) | (0x50 << 22) | 4)
+#define XY_SETUP_CLIP_BLT_CMD	((2 << 29) | (0x03 << 22) | 1)
+#define XY_SRC_COPY_BLT_CMD	((2 << 29) | (0x53 << 22) | 6)
+#define SRC_COPY_BLT_CMD	((2 << 29) | (0x43 << 22) | 4)
+#define XY_MONO_PAT_BLT_CMD	((2 << 29) | (0x52 << 22) | 7)
+#define XY_MONO_SRC_BLT_CMD	((2 << 29) | (0x54 << 22) | 6)
+#define XY_MONO_SRC_IMM_BLT_CMD	((2 << 29) | (0x71 << 22) | 5)
+
+#define DW_LENGTH_MASK			0xff
+
+#define WRITE_ALPHA			(1 << 21)
+#define WRITE_RGB			(1 << 20)
+#define VERT_SEED			(3 << 8)
+#define HORIZ_SEED			(3 << 12)
+
+#define COLOR_DEPTH_8			(0 << 24)
+#define COLOR_DEPTH_16			(1 << 24)
+#define COLOR_DEPTH_32			(3 << 24)
+
+#define SRC_ROP_GXCOPY			0xcc
+#define SRC_ROP_GXXOR			0x66
+
+#define PITCH_SHIFT			0
+#define ROP_SHIFT			16
+#define WIDTH_SHIFT			0
+#define HEIGHT_SHIFT			16
+
+/* in bytes */
+#define MAX_MONO_IMM_SIZE		128
+
+
+/*** Macros ***/
+
+/* I/O macros */
+#define INREG8(addr)		readb(dinfo->mmio_base + (addr))
+#define INREG(addr)		readl(dinfo->mmio_base + (addr))
+#define OUTREG8(addr, val)	writeb((val), dinfo->mmio_base + (addr))
+#define OUTREG(addr, val)	writel((val), dinfo->mmio_base + (addr))
+
+/* Ring buffer macros */
+#define OUT_RING(n)	do {						\
+	writel((n), dinfo->ring_base + dinfo->ring_tail);		\
+	dinfo->ring_tail += 4;						\
+	dinfo->ring_tail &= dinfo->ring_tail_mask;			\
+} while (0)
+
+#define START_RING(n)	do {						\
+	u32 needed;							\
+	needed = (n) * 4;						\
+	if (dinfo->ring_tail + needed >					\
+	    dinfo->ring_tail_mask - RING_MIN_FREE) {			\
+		needed += dinfo->ring_tail_mask + 1 - dinfo->ring_tail;	\
+	}								\
+	if (dinfo->ring_space < needed)					\
+		wait_ring(dinfo, needed);				\
+	dinfo->ring_space -= needed;					\
+	while (needed > (n) * 4) {					\
+		OUT_RING(MI_NOOP);					\
+		needed -= 4;						\
+	}								\
+} while (0)
+
+#define ADVANCE_RING()	do {						\
+	OUTREG(PRI_RING_TAIL, dinfo->ring_tail);			\
+} while (0)
+
+#define DO_RING_IDLE()	do {						\
+	u32 head, tail;							\
+	do {								\
+		head = INREG(PRI_RING_HEAD) & RING_HEAD_MASK;		\
+		tail = INREG(PRI_RING_TAIL) & RING_TAIL_MASK;		\
+		udelay(10);						\
+	} while (head != tail);						\
+} while (0)
+
+#endif /* _INTELFBHW_H */
diff -urN linux-2.4.21-pre5/drivers/video/intel/modes.c linux-2.4.21-pre6/drivers/video/intel/modes.c
--- linux-2.4.21-pre5/drivers/video/intel/modes.c	Wed Dec 31 16:00:00 1969
+++ linux-2.4.21-pre6/drivers/video/intel/modes.c	Wed Mar 26 16:11:39 2003
@@ -0,0 +1,222 @@
+
+/*
+ * THIS FILE IS AUTOMATICALLY GENERATED BY fbmode.pl -- DO NOT EDIT
+ */
+
+static struct fb_videomode modedb[] = {
+    {
+	/* 640x350 @ 85 Hz, 37.9 kHz hsync */
+	"640x350@85", 85, 640, 350, 31746, 96, 32, 60, 32, 64, 3,
+	FB_SYNC_HOR_HIGH_ACT, FB_VMODE_NONINTERLACED
+    },
+    {
+	/* 640x400 @ 85 Hz, 37.9 kHz hsync */
+	"640x400@85", 85, 640, 400, 31746, 96, 32, 41, 1, 64, 3,
+	FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
+    },
+    {
+	/* 720x400 @ 85 Hz, 37.9 kHz hsync */
+	"720x400@85", 85, 720, 400, 28169, 108, 36, 42, 1, 72, 3,
+	FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
+    },
+    {
+	/* 640x480 @ 60 Hz, 31.5 kHz hsync */
+	"640x480@60", 60, 640, 480, 39683, 48, 16, 33, 10, 96, 2,
+	0, FB_VMODE_NONINTERLACED
+    },
+    {
+	/* 640x480 @ 73 Hz, 37.9 kHz hsync */
+	"640x480@73", 73, 640, 480, 31746, 128, 24, 29, 9, 40, 2,
+	0, FB_VMODE_NONINTERLACED
+    },
+    {
+	/* 640x480 @ 75 Hz, 37.5 kHz hsync */
+	"640x480@75", 75, 640, 480, 31746, 120, 16, 16, 1, 64, 3,
+	0, FB_VMODE_NONINTERLACED
+    },
+    {
+	/* 640x480 @ 85 Hz, 43.3 kHz hsync */
+	"640x480@85", 85, 640, 480, 27778, 80, 56, 25, 1, 56, 3,
+	0, FB_VMODE_NONINTERLACED
+    },
+    {
+	/* 800x600 @ 56 Hz, 35.2 kHz hsync */
+	"800x600@56", 56, 800, 600, 27778, 128, 24, 22, 1, 72, 2,
+	FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
+    },
+    {
+	/* 800x600 @ 60 Hz, 37.9 kHz hsync */
+	"800x600@60", 60, 800, 600, 25000, 88, 40, 23, 1, 128, 4,
+	FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
+    },
+    {
+	/* 800x600 @ 72 Hz, 48.1 kHz hsync */
+	"800x600@72", 72, 800, 600, 20000, 64, 56, 23, 37, 120, 6,
+	FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
+    },
+    {
+	/* 800x600 @ 75 Hz, 46.9 kHz hsync */
+	"800x600@75", 75, 800, 600, 20202, 160, 16, 21, 1, 80, 3,
+	FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
+    },
+    {
+	/* 800x600 @ 85 Hz, 53.7 kHz hsync */
+	"800x600@85", 85, 800, 600, 17762, 152, 32, 27, 1, 64, 3,
+	FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
+    },
+    {
+	/* 1024x768 @ 60 Hz, 48.4 kHz hsync */
+	"1024x768@60", 60, 1024, 768, 15385, 160, 24, 29, 3, 136, 6,
+	0, FB_VMODE_NONINTERLACED
+    },
+    {
+	/* 1024x768 @ 70 Hz, 56.5 kHz hsync */
+	"1024x768@70", 70, 1024, 768, 13333, 144, 24, 29, 3, 136, 6,
+	0, FB_VMODE_NONINTERLACED
+    },
+    {
+	/* 1024x768 @ 75 Hz, 60.1 kHz hsync */
+	"1024x768@75", 75, 1024, 768, 12690, 176, 16, 28, 1, 96, 3,
+	FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
+    },
+    {
+	/* 1024x768 @ 85 Hz, 68.7 kHz hsync */
+	"1024x768@85", 85, 1024, 768, 10582, 208, 48, 36, 1, 96, 3,
+	FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
+    },
+    {
+	/* 1152x864 @ 75 Hz, 67.5 kHz hsync */
+	"1152x864@75", 75, 1152, 864, 9259, 256, 64, 32, 1, 128, 3,
+	FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
+    },
+    {
+	/* 1280x960 @ 60 Hz, 60.0 kHz hsync */
+	"1280x960@60", 60, 1280, 960, 9259, 312, 96, 36, 1, 112, 3,
+	FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
+    },
+    {
+	/* 1280x960 @ 85 Hz, 85.9 kHz hsync */
+	"1280x960@85", 85, 1280, 960, 6734, 224, 64, 47, 1, 160, 3,
+	FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
+    },
+    {
+	/* 1280x1024 @ 60 Hz, 64.0 kHz hsync */
+	"1280x1024@60", 60, 1280, 1024, 9259, 248, 48, 38, 1, 112, 3,
+	FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
+    },
+    {
+	/* 1280x1024 @ 75 Hz, 80.0 kHz hsync */
+	"1280x1024@75", 75, 1280, 1024, 7407, 248, 16, 38, 1, 144, 3,
+	FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
+    },
+    {
+	/* 1280x1024 @ 85 Hz, 91.1 kHz hsync */
+	"1280x1024@85", 85, 1280, 1024, 6349, 224, 64, 44, 1, 160, 3,
+	FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
+    },
+    {
+	/* 1600x1200 @ 60 Hz, 75.0 kHz hsync */
+	"1600x1200@60", 60, 1600, 1200, 6173, 304, 64, 46, 1, 192, 3,
+	FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
+    },
+    {
+	/* 1600x1200 @ 65 Hz, 81.2 kHz hsync */
+	"1600x1200@65", 65, 1600, 1200, 5698, 304, 64, 46, 1, 192, 3,
+	FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
+    },
+    {
+	/* 1600x1200 @ 70 Hz, 87.5 kHz hsync */
+	"1600x1200@70", 70, 1600, 1200, 5291, 304, 64, 46, 1, 192, 3,
+	FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
+    },
+    {
+	/* 1600x1200 @ 75 Hz, 93.8 kHz hsync */
+	"1600x1200@75", 75, 1600, 1200, 4938, 304, 64, 46, 1, 192, 3,
+	FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
+    },
+    {
+	/* 1600x1200 @ 85 Hz, 106.2 kHz hsync */
+	"1600x1200@85", 85, 1600, 1200, 4357, 304, 64, 46, 1, 192, 3,
+	FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
+    },
+    {
+	/* 1792x1344 @ 60 Hz, 83.7 kHz hsync */
+	"1792x1344@60", 60, 1792, 1344, 4883, 328, 128, 46, 1, 200, 3,
+	FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
+    },
+    {
+	/* 1792x1344 @ 75 Hz, 106.3 kHz hsync */
+	"1792x1344@75", 75, 1792, 1344, 3831, 352, 96, 69, 1, 216, 3,
+	FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
+    },
+    {
+	/* 1856x1392 @ 60 Hz, 86.4 kHz hsync */
+	"1856x1392@60", 60, 1856, 1392, 4581, 352, 96, 43, 1, 224, 3,
+	FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
+    },
+    {
+	/* 1856x1392 @ 75 Hz, 112.5 kHz hsync */
+	"1856x1392@75", 75, 1856, 1392, 3472, 352, 128, 104, 1, 224, 3,
+	FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
+    },
+    {
+	/* 1920x1440 @ 60 Hz, 90.0 kHz hsync */
+	"1920x1440@60", 60, 1920, 1440, 4274, 344, 128, 56, 1, 208, 3,
+	FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
+    },
+    {
+	/* 1920x1440 @ 75 Hz, 112.5 kHz hsync */
+	"1920x1440@75", 75, 1920, 1440, 3367, 352, 144, 56, 1, 224, 3,
+	FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
+    },
+    {
+	/* 832x624 @ 75 Hz, 49.7 kHz hsync */
+	"832x624@75", 75, 832, 624, 17457, 224, 32, 39, 1, 64, 3,
+	0, FB_VMODE_NONINTERLACED
+    },
+    {
+	/* 1152x768 @ 55 Hz, 44.2 kHz hsync */
+	"1152x768@55", 55, 1152, 768, 15386, 158, 26, 29, 3, 136, 6,
+	FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
+    },
+    {
+	/* 1400x1050 @ 60 Hz, 64.9 kHz hsync */
+	"1400x1050@60", 60, 1400, 1050, 8197, 240, 88, 18, 2, 152, 12,
+	FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
+    },
+    {
+	/* 1400x1050 @ 75 Hz, 81.5 kHz hsync */
+	"1400x1050@75", 75, 1400, 1050, 6418, 128, 64, 26, 2, 320, 12,
+	FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
+    },
+    {
+	/* 1600x1024 @ 60 Hz, 64.0 kHz hsync */
+	"1600x1024@60", 60, 1600, 1024, 9354, 30, 20, 37, 3, 20, 3,
+	0, FB_VMODE_NONINTERLACED
+    },
+    {
+	/* 1920x1440 @ 85 Hz, 128.5 kHz hsync */
+	"1920x1440@85", 85, 1920, 1440, 2930, 368, 152, 68, 1, 216, 3,
+	FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
+    },
+    {
+	/* 2048x1536 @ 60 Hz, 95.3 kHz hsync */
+	"2048x1536@60", 60, 2048, 1536, 3746, 376, 152, 49, 1, 224, 3,
+	FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
+    },
+    {
+	/* 2048x1536 @ 75 Hz, 120.2 kHz hsync */
+	"2048x1536@75", 75, 2048, 1536, 2937, 392, 168, 63, 1, 224, 3,
+	FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
+    },
+    {
+	/* 2048x1536 @ 85 Hz, 137.0 kHz hsync */
+	"2048x1536@85", 85, 2048, 1536, 2577, 392, 168, 72, 1, 224, 3,
+	FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
+    }
+};
+
+static int num_modes = sizeof(modedb) / sizeof(modedb[0]);
+
+#define DFLT_MODE 3
+
diff -urN linux-2.4.21-pre5/drivers/video/matrox/Makefile linux-2.4.21-pre6/drivers/video/matrox/Makefile
--- linux-2.4.21-pre5/drivers/video/matrox/Makefile	Wed Mar 26 16:11:29 2003
+++ linux-2.4.21-pre6/drivers/video/matrox/Makefile	Wed Mar 26 16:11:39 2003
@@ -12,15 +12,11 @@
 # Each configuration option enables a list of files.
 
 my-obj-$(CONFIG_FB_MATROX_G100)	  := g450_pll.o
+my-obj-$(CONFIG_FB_MATROX_G450)   += matroxfb_g450.o matroxfb_crtc2.o
 
 obj-$(CONFIG_FB_MATROX)           += matroxfb_base.o matroxfb_accel.o matroxfb_DAC1064.o matroxfb_Ti3026.o matroxfb_misc.o $(my-obj-y)
 obj-$(CONFIG_FB_MATROX_I2C)       += i2c-matroxfb.o
-ifeq ($(CONFIG_FB_MATROX_MAVEN),y)
-  obj-$(CONFIG_FB_MATROX)     += matroxfb_maven.o matroxfb_crtc2.o
-endif
-ifeq ($(CONFIG_FB_MATROX_G450),y)
-  obj-$(CONFIG_FB_MATROX)	  += matroxfb_g450.o matroxfb_crtc2.o
-endif
+obj-$(CONFIG_FB_MATROX_MAVEN)     += matroxfb_maven.o matroxfb_crtc2.o
 obj-$(CONFIG_FB_MATROX_PROC)	  += matroxfb_proc.o
 
 include $(TOPDIR)/Rules.make
diff -urN linux-2.4.21-pre5/drivers/video/matrox/matroxfb_misc.c linux-2.4.21-pre6/drivers/video/matrox/matroxfb_misc.c
--- linux-2.4.21-pre5/drivers/video/matrox/matroxfb_misc.c	Wed Mar 26 16:11:29 2003
+++ linux-2.4.21-pre6/drivers/video/matrox/matroxfb_misc.c	Wed Mar 26 16:11:39 2003
@@ -994,6 +994,27 @@
 	parse_bios(vaddr_va(ACCESS_FBINFO(video).vbase), &ACCESS_FBINFO(bios));
 	pci_write_config_dword(pdev, PCI_ROM_ADDRESS, biosbase);
 	pci_write_config_dword(pdev, PCI_OPTION_REG, opt);
+#ifdef CONFIG_X86
+	if (!ACCESS_FBINFO(bios).bios_valid) {
+		unsigned char* b;
+		
+		b = ioremap(0x000C0000, 65536);
+		if (!b) {
+			printk(KERN_INFO "matroxfb: Unable to map legacy BIOS\n");
+		} else {
+			unsigned int ven = readb(b+0x64+0) | (readb(b+0x64+1) << 8);
+			unsigned int dev = readb(b+0x64+2) | (readb(b+0x64+3) << 8);
+			
+			if (ven != pdev->vendor || dev != pdev->device) {
+				printk(KERN_INFO "matroxfb: Legacy BIOS is for %04X:%04X, while this device is %04X:%04X\n",
+					ven, dev, pdev->vendor, pdev->device);
+			} else {
+				parse_bios(b, &ACCESS_FBINFO(bios));
+			}
+			iounmap(b);
+		}
+	}
+#endif
 	matroxfb_set_limits(PMINFO &ACCESS_FBINFO(bios));
 }
 
diff -urN linux-2.4.21-pre5/drivers/video/pmagb-b-fb.h linux-2.4.21-pre6/drivers/video/pmagb-b-fb.h
--- linux-2.4.21-pre5/drivers/video/pmagb-b-fb.h	Wed Apr 18 11:49:12 2001
+++ linux-2.4.21-pre6/drivers/video/pmagb-b-fb.h	Wed Mar 26 16:11:39 2003
@@ -4,7 +4,7 @@
  *      TurboChannel PMAGB-B framebuffer card support,
  *      Copyright (C) 1999, 2000, 2001 by
  *      Michael Engel <engel@unix-ag.org> and 
- *      Karsten Merker <merker@linxutag.org>
+ *      Karsten Merker <merker@linuxtag.org>
  *      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.
diff -urN linux-2.4.21-pre5/drivers/video/radeonfb.c linux-2.4.21-pre6/drivers/video/radeonfb.c
--- linux-2.4.21-pre5/drivers/video/radeonfb.c	Thu Nov 28 15:53:15 2002
+++ linux-2.4.21-pre6/drivers/video/radeonfb.c	Wed Mar 26 16:11:40 2003
@@ -2778,7 +2778,7 @@
 	lvds_gen_cntl |= (LVDS_BL_MOD_EN | LVDS_BLON);
 	if (on && (level > BACKLIGHT_OFF)) {
 		lvds_gen_cntl |= LVDS_DIGON;
-		if (!lvds_gen_cntl & LVDS_ON) {
+		if ((lvds_gen_cntl & LVDS_ON) == 0) {
 			lvds_gen_cntl &= ~LVDS_BLON;
 			OUTREG(LVDS_GEN_CNTL, lvds_gen_cntl);
 			(void)INREG(LVDS_GEN_CNTL);
diff -urN linux-2.4.21-pre5/drivers/video/sis/sis_accel.c linux-2.4.21-pre6/drivers/video/sis/sis_accel.c
--- linux-2.4.21-pre5/drivers/video/sis/sis_accel.c	Thu Nov 28 15:53:15 2002
+++ linux-2.4.21-pre6/drivers/video/sis/sis_accel.c	Wed Mar 26 16:11:40 2003
@@ -40,7 +40,9 @@
 #include <linux/sisfb.h>
 
 #include <asm/io.h>
+#ifdef CONFIG_MTRR
 #include <asm/mtrr.h>
+#endif
 
 #include <video/fbcon.h>
 #if LINUX_VERSION_CODE <= KERNEL_VERSION(2,5,33)
diff -urN linux-2.4.21-pre5/drivers/video/sis/sis_main.c linux-2.4.21-pre6/drivers/video/sis/sis_main.c
--- linux-2.4.21-pre5/drivers/video/sis/sis_main.c	Thu Nov 28 15:53:15 2002
+++ linux-2.4.21-pre6/drivers/video/sis/sis_main.c	Wed Mar 26 16:11:40 2003
@@ -51,7 +51,9 @@
 #include <linux/sisfb.h>
 
 #include <asm/io.h>
+#ifdef CONFIG_MTRR
 #include <asm/mtrr.h>
+#endif
 
 #include <video/fbcon.h>
 #include <video/fbcon-cfb8.h>
diff -urN linux-2.4.21-pre5/fs/exec.c linux-2.4.21-pre6/fs/exec.c
--- linux-2.4.21-pre5/fs/exec.c	Wed Mar 26 16:11:29 2003
+++ linux-2.4.21-pre6/fs/exec.c	Wed Mar 26 16:11:40 2003
@@ -576,8 +576,10 @@
 
 	current->sas_ss_sp = current->sas_ss_size = 0;
 
-	if (current->euid == current->uid && current->egid == current->gid)
+	if (current->euid == current->uid && current->egid == current->gid) {
 		current->mm->dumpable = 1;
+		current->task_dumpable = 1;
+	}
 	name = bprm->filename;
 	for (i=0; (ch = *(name++)) != '\0';) {
 		if (ch == '/')
@@ -1085,7 +1087,7 @@
 	binfmt = current->binfmt;
 	if (!binfmt || !binfmt->core_dump)
 		goto fail;
-	if (!current->mm->dumpable)
+	if (!is_dumpable(current))
 		goto fail;
 	current->mm->dumpable = 0;
 	if (current->rlim[RLIMIT_CORE].rlim_cur < binfmt->min_coredump)
diff -urN linux-2.4.21-pre5/fs/ext2/ialloc.c linux-2.4.21-pre6/fs/ext2/ialloc.c
--- linux-2.4.21-pre5/fs/ext2/ialloc.c	Thu Nov 28 15:53:15 2002
+++ linux-2.4.21-pre6/fs/ext2/ialloc.c	Wed Mar 26 16:11:40 2003
@@ -390,8 +390,7 @@
 	if (S_ISLNK(mode))
 		inode->u.ext2_i.i_flags &= ~(EXT2_IMMUTABLE_FL|EXT2_APPEND_FL);
 	inode->u.ext2_i.i_block_group = group;
-	if (inode->u.ext2_i.i_flags & EXT2_SYNC_FL)
-		inode->i_flags |= S_SYNC;
+	ext2_set_inode_flags(inode);
 	insert_inode_hash(inode);
 	inode->i_generation = event++;
 	mark_inode_dirty(inode);
diff -urN linux-2.4.21-pre5/fs/ext2/inode.c linux-2.4.21-pre6/fs/ext2/inode.c
--- linux-2.4.21-pre5/fs/ext2/inode.c	Thu Nov 28 15:53:15 2002
+++ linux-2.4.21-pre6/fs/ext2/inode.c	Wed Mar 26 16:11:40 2003
@@ -877,6 +877,21 @@
 	}
 }
 
+void ext2_set_inode_flags(struct inode *inode)
+{
+	unsigned int flags = inode->u.ext2_i.i_flags;
+
+	inode->i_flags &= ~(S_SYNC|S_APPEND|S_IMMUTABLE|S_NOATIME);
+	if (flags & EXT2_SYNC_FL)
+		inode->i_flags |= S_SYNC;
+	if (flags & EXT2_APPEND_FL)
+		inode->i_flags |= S_APPEND;
+	if (flags & EXT2_IMMUTABLE_FL)
+		inode->i_flags |= S_IMMUTABLE;
+	if (flags & EXT2_NOATIME_FL)
+		inode->i_flags |= S_NOATIME;
+}
+
 void ext2_read_inode (struct inode * inode)
 {
 	struct buffer_head * bh;
@@ -997,22 +1012,7 @@
 				   le32_to_cpu(raw_inode->i_block[0]));
 	brelse (bh);
 	inode->i_attr_flags = 0;
-	if (inode->u.ext2_i.i_flags & EXT2_SYNC_FL) {
-		inode->i_attr_flags |= ATTR_FLAG_SYNCRONOUS;
-		inode->i_flags |= S_SYNC;
-	}
-	if (inode->u.ext2_i.i_flags & EXT2_APPEND_FL) {
-		inode->i_attr_flags |= ATTR_FLAG_APPEND;
-		inode->i_flags |= S_APPEND;
-	}
-	if (inode->u.ext2_i.i_flags & EXT2_IMMUTABLE_FL) {
-		inode->i_attr_flags |= ATTR_FLAG_IMMUTABLE;
-		inode->i_flags |= S_IMMUTABLE;
-	}
-	if (inode->u.ext2_i.i_flags & EXT2_NOATIME_FL) {
-		inode->i_attr_flags |= ATTR_FLAG_NOATIME;
-		inode->i_flags |= S_NOATIME;
-	}
+	ext2_set_inode_flags(inode);
 	return;
 	
 bad_inode:
diff -urN linux-2.4.21-pre5/fs/ext2/ioctl.c linux-2.4.21-pre6/fs/ext2/ioctl.c
--- linux-2.4.21-pre5/fs/ext2/ioctl.c	Thu Nov 28 15:53:15 2002
+++ linux-2.4.21-pre6/fs/ext2/ioctl.c	Wed Mar 26 16:11:40 2003
@@ -53,22 +53,7 @@
 		flags |= oldflags & ~EXT2_FL_USER_MODIFIABLE;
 		inode->u.ext2_i.i_flags = flags;
 
-		if (flags & EXT2_SYNC_FL)
-			inode->i_flags |= S_SYNC;
-		else
-			inode->i_flags &= ~S_SYNC;
-		if (flags & EXT2_APPEND_FL)
-			inode->i_flags |= S_APPEND;
-		else
-			inode->i_flags &= ~S_APPEND;
-		if (flags & EXT2_IMMUTABLE_FL)
-			inode->i_flags |= S_IMMUTABLE;
-		else
-			inode->i_flags &= ~S_IMMUTABLE;
-		if (flags & EXT2_NOATIME_FL)
-			inode->i_flags |= S_NOATIME;
-		else
-			inode->i_flags &= ~S_NOATIME;
+		ext2_set_inode_flags(inode);
 		inode->i_ctime = CURRENT_TIME;
 		mark_inode_dirty(inode);
 		return 0;
diff -urN linux-2.4.21-pre5/fs/ext3/ialloc.c linux-2.4.21-pre6/fs/ext3/ialloc.c
--- linux-2.4.21-pre5/fs/ext3/ialloc.c	Wed Mar 26 16:11:29 2003
+++ linux-2.4.21-pre6/fs/ext3/ialloc.c	Wed Mar 26 16:11:40 2003
@@ -500,8 +500,7 @@
 #endif
 	inode->u.ext3_i.i_block_group = i;
 	
-	if (inode->u.ext3_i.i_flags & EXT3_SYNC_FL)
-		inode->i_flags |= S_SYNC;
+	ext3_set_inode_flags(inode);
 	if (IS_SYNC(inode))
 		handle->h_sync = 1;
 	insert_inode_hash(inode);
diff -urN linux-2.4.21-pre5/fs/ext3/inode.c linux-2.4.21-pre6/fs/ext3/inode.c
--- linux-2.4.21-pre5/fs/ext3/inode.c	Wed Mar 26 16:11:29 2003
+++ linux-2.4.21-pre6/fs/ext3/inode.c	Wed Mar 26 16:11:40 2003
@@ -2068,6 +2068,22 @@
 	return -EIO;
 }
 
+void ext3_set_inode_flags(struct inode *inode)
+{
+	unsigned int flags = inode->u.ext3_i.i_flags;
+
+	inode->i_flags &= ~(S_SYNC|S_APPEND|S_IMMUTABLE|S_NOATIME);
+	if (flags & EXT3_SYNC_FL)
+		inode->i_flags |= S_SYNC;
+	if (flags & EXT3_APPEND_FL)
+		inode->i_flags |= S_APPEND;
+	if (flags & EXT3_IMMUTABLE_FL)
+		inode->i_flags |= S_IMMUTABLE;
+	if (flags & EXT3_NOATIME_FL)
+		inode->i_flags |= S_NOATIME;
+}
+
+
 void ext3_read_inode(struct inode * inode)
 {
 	struct ext3_iloc iloc;
@@ -2165,23 +2181,7 @@
 	} else 
 		init_special_inode(inode, inode->i_mode,
 				   le32_to_cpu(iloc.raw_inode->i_block[0]));
-	/* inode->i_attr_flags = 0;				unused */
-	if (inode->u.ext3_i.i_flags & EXT3_SYNC_FL) {
-		/* inode->i_attr_flags |= ATTR_FLAG_SYNCRONOUS; unused */
-		inode->i_flags |= S_SYNC;
-	}
-	if (inode->u.ext3_i.i_flags & EXT3_APPEND_FL) {
-		/* inode->i_attr_flags |= ATTR_FLAG_APPEND;	unused */
-		inode->i_flags |= S_APPEND;
-	}
-	if (inode->u.ext3_i.i_flags & EXT3_IMMUTABLE_FL) {
-		/* inode->i_attr_flags |= ATTR_FLAG_IMMUTABLE;	unused */
-		inode->i_flags |= S_IMMUTABLE;
-	}
-	if (inode->u.ext3_i.i_flags & EXT3_NOATIME_FL) {
-		/* inode->i_attr_flags |= ATTR_FLAG_NOATIME;	unused */
-		inode->i_flags |= S_NOATIME;
-	}
+	ext3_set_inode_flags(inode);
 	return;
 	
 bad_inode:
diff -urN linux-2.4.21-pre5/fs/ext3/ioctl.c linux-2.4.21-pre6/fs/ext3/ioctl.c
--- linux-2.4.21-pre5/fs/ext3/ioctl.c	Thu Nov 28 15:53:15 2002
+++ linux-2.4.21-pre6/fs/ext3/ioctl.c	Wed Mar 26 16:11:40 2003
@@ -81,22 +81,7 @@
 		flags |= oldflags & ~EXT3_FL_USER_MODIFIABLE;
 		inode->u.ext3_i.i_flags = flags;
 
-		if (flags & EXT3_SYNC_FL)
-			inode->i_flags |= S_SYNC;
-		else
-			inode->i_flags &= ~S_SYNC;
-		if (flags & EXT3_APPEND_FL)
-			inode->i_flags |= S_APPEND;
-		else
-			inode->i_flags &= ~S_APPEND;
-		if (flags & EXT3_IMMUTABLE_FL)
-			inode->i_flags |= S_IMMUTABLE;
-		else
-			inode->i_flags &= ~S_IMMUTABLE;
-		if (flags & EXT3_NOATIME_FL)
-			inode->i_flags |= S_NOATIME;
-		else
-			inode->i_flags &= ~S_NOATIME;
+		ext3_set_inode_flags(inode);
 		inode->i_ctime = CURRENT_TIME;
 
 		err = ext3_mark_iloc_dirty(handle, inode, &iloc);
diff -urN linux-2.4.21-pre5/fs/jffs/intrep.c linux-2.4.21-pre6/fs/jffs/intrep.c
--- linux-2.4.21-pre5/fs/jffs/intrep.c	Thu Oct  4 15:13:18 2001
+++ linux-2.4.21-pre6/fs/jffs/intrep.c	Wed Mar 26 16:11:40 2003
@@ -1910,7 +1910,7 @@
 	}
 
 	if ((err = flash_safe_writev(fmc->mtd, node_iovec, iovec_cnt,
-				    pos) < 0)) {
+				    pos)) < 0) {
 		jffs_fmfree_partly(fmc, fm, 0);
 		jffs_fm_write_unlock(fmc);
 		printk(KERN_ERR "JFFS: jffs_write_node: Failed to write, "
diff -urN linux-2.4.21-pre5/fs/jfs/jfs_dtree.c linux-2.4.21-pre6/fs/jfs/jfs_dtree.c
--- linux-2.4.21-pre5/fs/jfs/jfs_dtree.c	Wed Mar 26 16:11:29 2003
+++ linux-2.4.21-pre6/fs/jfs/jfs_dtree.c	Wed Mar 26 16:11:40 2003
@@ -2978,7 +2978,6 @@
 	int d_namleft, len, outlen;
 	unsigned long dirent_buf;
 	char *name_ptr;
-	int dtlhdrdatalen;
 	u32 dir_index;
 	int do_index = 0;
 	uint loop_count = 0;
@@ -2998,7 +2997,6 @@
 		 *                      -1 = End of directory
 		 */
 		do_index = 1;
-		dtlhdrdatalen = DTLHDRDATALEN;
 
 		dir_index = (u32) filp->f_pos;
 
@@ -3083,8 +3081,6 @@
 		 * pn > 0:              Real entries, pn=1 -> leftmost page
 		 * pn = index = -1:     No more entries
 		 */
-		dtlhdrdatalen = DTLHDRDATALEN_LEGACY;
-
 		dtpos = filp->f_pos;
 		if (dtpos == 0) {
 			/* build "." entry */
diff -urN linux-2.4.21-pre5/fs/jfs/jfs_extent.c linux-2.4.21-pre6/fs/jfs/jfs_extent.c
--- linux-2.4.21-pre5/fs/jfs/jfs_extent.c	Thu Nov 28 15:53:15 2002
+++ linux-2.4.21-pre6/fs/jfs/jfs_extent.c	Wed Mar 26 16:11:40 2003
@@ -91,7 +91,7 @@
 {
 	struct jfs_sb_info *sbi = JFS_SBI(ip->i_sb);
 	s64 nxlen, nxaddr, xoff, hint, xaddr = 0;
-	int rc, nbperpage;
+	int rc;
 	int xflag;
 
 	/* This blocks if we are low on resources */
@@ -104,9 +104,6 @@
 	if (xlen > MAXXLEN)
 		xlen = MAXXLEN;
 
-	/* get the number of blocks per page */
-	nbperpage = sbi->nbperpage;
-
 	/* get the page's starting extent offset */
 	xoff = pno << sbi->l2nbperpage;
 
diff -urN linux-2.4.21-pre5/fs/jfs/jfs_imap.c linux-2.4.21-pre6/fs/jfs/jfs_imap.c
--- linux-2.4.21-pre5/fs/jfs/jfs_imap.c	Wed Mar 26 16:11:29 2003
+++ linux-2.4.21-pre6/fs/jfs/jfs_imap.c	Wed Mar 26 16:11:40 2003
@@ -2972,7 +2972,6 @@
 	struct buffer_head *bh;
 	struct inode *ip;
 	tid_t tid;
-	int rc;
 
 	/* if AIT2 ipmap2 is bad, do not try to update it */
 	if (JFS_SBI(sb)->mntflag & JFS_BAD_SAIT)	/* s_flag */
@@ -2980,7 +2979,7 @@
 	ip = diReadSpecial(sb, FILESYSTEM_I, 1);
 	if (ip == NULL) {
 		JFS_SBI(sb)->mntflag |= JFS_BAD_SAIT;
-		if ((rc = readSuper(sb, &bh)))
+		if (readSuper(sb, &bh))
 			return;
 		j_sb = (struct jfs_superblock *)bh->b_data;
 		j_sb->s_flag |= JFS_BAD_SAIT;
@@ -2995,7 +2994,7 @@
 	/* start transaction */
 	tid = txBegin(sb, COMMIT_FORCE);
 	/* update the inode map addressing structure to point to it */
-	if ((rc = xtInsert(tid, ip, 0, blkno, xlen, xaddr, 0))) {
+	if (xtInsert(tid, ip, 0, blkno, xlen, xaddr, 0)) {
 		JFS_SBI(sb)->mntflag |= JFS_BAD_SAIT;
 		txAbort(tid, 1);
 		goto cleanup;
@@ -3004,7 +3003,7 @@
 	/* update the inode map's inode to reflect the extension */
 	ip->i_size += PSIZE;
 	ip->i_blocks += LBLK2PBLK(sb, xlen);
-	rc = txCommit(tid, 1, &ip, COMMIT_FORCE);
+	txCommit(tid, 1, &ip, COMMIT_FORCE);
       cleanup:
 	txEnd(tid);
 	diFreeSpecial(ip);
diff -urN linux-2.4.21-pre5/fs/jfs/jfs_logmgr.c linux-2.4.21-pre6/fs/jfs/jfs_logmgr.c
--- linux-2.4.21-pre5/fs/jfs/jfs_logmgr.c	Wed Mar 26 16:11:29 2003
+++ linux-2.4.21-pre6/fs/jfs/jfs_logmgr.c	Wed Mar 26 16:11:40 2003
@@ -673,28 +673,29 @@
 	}
 	jfs_info("lmGroup Commit: tblk = 0x%p, gcrtc = %d", tblk, log->gcrtc);
 
-	if (tblk->xflag & COMMIT_LAZY) {
-		/*
-		 * Lazy transactions can leave now
-		 */
+	if (tblk->xflag & COMMIT_LAZY)
 		tblk->flag |= tblkGC_LAZY;
-		LOGGC_UNLOCK(log);
-		return 0;
-	}
-	/*
-	 * group commit pageout in progress
-	 */
-	if ((!(log->cflag & logGC_PAGEOUT)) && log->cqueue.head) {
+
+	if ((!(log->cflag & logGC_PAGEOUT)) && log->cqueue.head &&
+	    (!(tblk->xflag & COMMIT_LAZY) || test_bit(log_FLUSH, &log->flag))) {
 		/*
-		 * only transaction in the commit queue:
+		 * No pageout in progress
 		 *
-		 * start one-transaction group commit as
-		 * its group leader.
+		 * start group commit as its group leader.
 		 */
 		log->cflag |= logGC_PAGEOUT;
 
 		lmGCwrite(log, 0);
 	}
+
+	if (tblk->xflag & COMMIT_LAZY) {
+		/*
+		 * Lazy transactions can leave now
+		 */
+		LOGGC_UNLOCK(log);
+		return 0;
+	}
+
 	/* lmGCwrite gives up LOGGC_LOCK, check again */
 
 	if (tblk->flag & tblkGC_COMMITTED) {
@@ -893,10 +894,8 @@
 	 * the first transaction entering group commit
 	 * will elect herself as new group leader.
 	 */
-	else {
+	else
 		log->cflag &= ~logGC_PAGEOUT;
-		clear_bit(log_FLUSH, &log->flag);
-	}
 
 	//LOGGC_UNLOCK(log);
 	spin_unlock_irqrestore(&log->gclock, flags);
diff -urN linux-2.4.21-pre5/fs/jfs/jfs_txnmgr.c linux-2.4.21-pre6/fs/jfs/jfs_txnmgr.c
--- linux-2.4.21-pre5/fs/jfs/jfs_txnmgr.c	Wed Mar 26 16:11:29 2003
+++ linux-2.4.21-pre6/fs/jfs/jfs_txnmgr.c	Wed Mar 26 16:11:40 2003
@@ -379,8 +379,8 @@
 
 	tblk = tid_to_tblock(t);
 
-	if ((tblk->next == 0) && (current != jfsCommitTask)) {
-		/* Save one tblk for jfsCommit thread */
+	if ((tblk->next == 0) && !(flag & COMMIT_FORCE)) {
+		/* Don't let a non-forced transaction take the last tblk */
 		jfs_info("txBegin: waiting for free tid");
 		INCREMENT(TxStat.txBegin_freetid);
 		TXN_SLEEP(&TxAnchor.freewait);
@@ -517,22 +517,24 @@
 	/*
 	 * mark the tblock not active
 	 */
-	--log->active;
+	if (--log->active == 0) {
+		clear_bit(log_FLUSH, &log->flag);
 
-	/*
-	 * synchronize with logsync barrier
-	 */
-	if (test_bit(log_SYNCBARRIER, &log->flag) && log->active == 0) {
-		/* forward log syncpt */
-		/* lmSync(log); */
+		/*
+		 * synchronize with logsync barrier
+		 */
+		if (test_bit(log_SYNCBARRIER, &log->flag)) {
+			/* forward log syncpt */
+			/* lmSync(log); */
 
-		jfs_info("     log barrier off: 0x%x", log->lsn);
+			jfs_info("log barrier off: 0x%x", log->lsn);
 
-		/* enable new transactions start */
-		clear_bit(log_SYNCBARRIER, &log->flag);
+			/* enable new transactions start */
+			clear_bit(log_SYNCBARRIER, &log->flag);
 
-		/* wakeup all waitors for logsync barrier */
-		TXN_WAKEUP(&log->syncwait);
+			/* wakeup all waitors for logsync barrier */
+			TXN_WAKEUP(&log->syncwait);
+		}
 	}
 
 	/*
@@ -1539,12 +1541,10 @@
 void dtLog(struct jfs_log * log, struct tblock * tblk, struct lrd * lrd,
 	   struct tlock * tlck)
 {
-	struct inode *ip;
 	struct metapage *mp;
 	struct pxd_lock *pxdlock;
 	pxd_t *pxd;
 
-	ip = tlck->ip;
 	mp = tlck->mp;
 
 	/* initialize as REDOPAGE/NOREDOPAGE record format */
@@ -2886,7 +2886,6 @@
 	struct inode *ip;
 	struct jfs_inode_info *jfs_ip;
 	struct jfs_log *log = JFS_SBI(sb)->log;
-	int rc;
 	tid_t tid;
 
 	set_bit(log_QUIESCE, &log->flag);
@@ -2906,7 +2905,7 @@
 		TXN_UNLOCK();
 		tid = txBegin(ip->i_sb, COMMIT_INODE | COMMIT_FORCE);
 		down(&jfs_ip->commit_sem);
-		rc = txCommit(tid, 1, &ip, 0);
+		txCommit(tid, 1, &ip, 0);
 		txEnd(tid);
 		up(&jfs_ip->commit_sem);
 		/*
@@ -2998,8 +2997,7 @@
 				 * when it is committed
 				 */
 				TXN_UNLOCK();
-				tid = txBegin(ip->i_sb,
-					      COMMIT_INODE | COMMIT_FORCE);
+				tid = txBegin(ip->i_sb, COMMIT_INODE);
 				rc = txCommit(tid, 1, &ip, 0);
 				txEnd(tid);
 				up(&jfs_ip->commit_sem);
diff -urN linux-2.4.21-pre5/fs/jfs/jfs_xtree.c linux-2.4.21-pre6/fs/jfs/jfs_xtree.c
--- linux-2.4.21-pre5/fs/jfs/jfs_xtree.c	Wed Mar 26 16:11:29 2003
+++ linux-2.4.21-pre6/fs/jfs/jfs_xtree.c	Wed Mar 26 16:11:40 2003
@@ -3268,14 +3268,13 @@
 void xtInitRoot(tid_t tid, struct inode *ip)
 {
 	xtpage_t *p;
-	struct tlock *tlck;
 
 	/*
 	 * acquire a transaction lock on the root
 	 *
 	 * action:
 	 */
-	tlck = txLock(tid, ip, (struct metapage *) &JFS_IP(ip)->bxflag,
+	txLock(tid, ip, (struct metapage *) &JFS_IP(ip)->bxflag,
 		      tlckXTREE | tlckNEW);
 	p = &JFS_IP(ip)->i_xtroot;
 
diff -urN linux-2.4.21-pre5/fs/nfs/nfsroot.c linux-2.4.21-pre6/fs/nfs/nfsroot.c
--- linux-2.4.21-pre5/fs/nfs/nfsroot.c	Thu Nov 28 15:53:15 2002
+++ linux-2.4.21-pre6/fs/nfs/nfsroot.c	Wed Mar 26 16:11:40 2003
@@ -384,10 +384,11 @@
 					"number from server, using default\n");
 			port = nfsd_port;
 		}
-		nfs_port = htons(port);
+		nfs_port = port;
 		dprintk("Root-NFS: Portmapper on server returned %d "
 			"as nfsd port\n", port);
 	}
+	nfs_port = htons(nfs_port);
 
 	if ((port = root_nfs_getport(NFS_MNT_PROGRAM, mountd_ver, proto)) < 0) {
 		printk(KERN_ERR "Root-NFS: Unable to get mountd port "
diff -urN linux-2.4.21-pre5/fs/nfs/read.c linux-2.4.21-pre6/fs/nfs/read.c
--- linux-2.4.21-pre5/fs/nfs/read.c	Wed Mar 26 16:11:29 2003
+++ linux-2.4.21-pre6/fs/nfs/read.c	Wed Mar 26 16:11:40 2003
@@ -424,14 +424,9 @@
 				memset(p + count, 0, PAGE_CACHE_SIZE - count);
 				kunmap(page);
 				count = 0;
-				if (data->res.eof)
-					SetPageUptodate(page);
-				else
-					SetPageError(page);
-			} else {
+			} else
 				count -= PAGE_CACHE_SIZE;
-				SetPageUptodate(page);
-			}
+			SetPageUptodate(page);
 		} else
 			SetPageError(page);
 		flush_dcache_page(page);
diff -urN linux-2.4.21-pre5/fs/nfs/symlink.c linux-2.4.21-pre6/fs/nfs/symlink.c
--- linux-2.4.21-pre5/fs/nfs/symlink.c	Thu Nov 28 15:53:15 2002
+++ linux-2.4.21-pre6/fs/nfs/symlink.c	Wed Mar 26 16:11:40 2003
@@ -46,7 +46,6 @@
 
 error:
 	SetPageError(page);
-	kunmap(page);
 	UnlockPage(page);
 	return -EIO;
 }
diff -urN linux-2.4.21-pre5/fs/reiserfs/journal.c linux-2.4.21-pre6/fs/reiserfs/journal.c
--- linux-2.4.21-pre5/fs/reiserfs/journal.c	Wed Mar 26 16:11:29 2003
+++ linux-2.4.21-pre6/fs/reiserfs/journal.c	Wed Mar 26 16:11:40 2003
@@ -1401,6 +1401,10 @@
 		     *newest_mount_id) ;
       return -1 ;
     }
+    if ( le32_to_cpu(desc->j_len) > sb_journal_trans_max(SB_DISK_SUPER_BLOCK(p_s_sb)) ) {
+      reiserfs_warning("journal-2018: Bad transaction length %d encountered, ignoring transaction\n", le32_to_cpu(desc->j_len));
+      return -1 ;
+    }
     offset = d_bh->b_blocknr - reiserfs_get_journal_block(p_s_sb) ;
 
     /* ok, we have a journal description block, lets see if the transaction was valid */
@@ -1416,11 +1420,12 @@
 		     le32_to_cpu(commit->j_trans_id), 
 		     le32_to_cpu(commit->j_len));
       brelse(c_bh) ;
-      if (oldest_invalid_trans_id)
-        *oldest_invalid_trans_id = le32_to_cpu(desc->j_trans_id) ;
+      if (oldest_invalid_trans_id) {
+	*oldest_invalid_trans_id = le32_to_cpu(desc->j_trans_id) ;
 	reiserfs_debug(p_s_sb, REISERFS_DEBUG_CODE, "journal-1004: "
 	               "transaction_is_valid setting oldest invalid trans_id "
 		       "to %d\n", le32_to_cpu(desc->j_trans_id)) ;
+	}
       return -1; 
     }
     brelse(c_bh) ;
@@ -1517,9 +1522,14 @@
     } else {
       real_blocks[i] = sb_getblk(p_s_sb, le32_to_cpu(commit->j_realblock[i - JOURNAL_TRANS_HALF])) ;
     }
+    if ( real_blocks[i]->b_blocknr > SB_BLOCK_COUNT(p_s_sb) ) {
+      reiserfs_warning("journal-1207: REPLAY FAILURE fsck required! Block to replay is outside of filesystem\n");
+      goto abort_replay;
+    }
     if (real_blocks[i]->b_blocknr >= reiserfs_get_journal_block(p_s_sb) &&
         real_blocks[i]->b_blocknr < (reiserfs_get_journal_block(p_s_sb)+JOURNAL_BLOCK_COUNT)) {
       reiserfs_warning("journal-1204: REPLAY FAILURE fsck required! Trying to replay onto a log block\n") ;
+abort_replay:
       brelse_array(log_blocks, i) ;
       brelse_array(real_blocks, i) ;
       brelse(c_bh) ;
diff -urN linux-2.4.21-pre5/fs/reiserfs/stree.c linux-2.4.21-pre6/fs/reiserfs/stree.c
--- linux-2.4.21-pre5/fs/reiserfs/stree.c	Thu Nov 28 15:53:15 2002
+++ linux-2.4.21-pre6/fs/reiserfs/stree.c	Wed Mar 26 16:11:40 2003
@@ -1125,6 +1125,21 @@
 		journal_mark_dirty (th, p_s_sb, p_s_bh);
 		inode->i_blocks -= p_s_sb->s_blocksize / 512;
 		reiserfs_free_block(th, tmp);
+		/* In case of big fragmentation it is possible that each block
+		   freed will cause dirtying of one more bitmap and then we will
+		   quickly overflow our transaction space. This is a
+		   counter-measure against that scenario */
+		if (journal_transaction_should_end(th, th->t_blocks_allocated)) {
+		    int orig_len_alloc = th->t_blocks_allocated ;
+		    pathrelse(p_s_path) ;
+
+		    journal_end(th, p_s_sb, orig_len_alloc) ;
+		    journal_begin(th, p_s_sb, orig_len_alloc) ;
+		    reiserfs_update_inode_transaction(inode) ;
+		    need_research = 1;
+		    break;
+		}
+
 		if ( item_moved (&s_ih, p_s_path) )  {
 			need_research = 1;
 			break ;
diff -urN linux-2.4.21-pre5/fs/reiserfs/super.c linux-2.4.21-pre6/fs/reiserfs/super.c
--- linux-2.4.21-pre5/fs/reiserfs/super.c	Thu Nov 28 15:53:15 2002
+++ linux-2.4.21-pre6/fs/reiserfs/super.c	Wed Mar 26 16:11:40 2003
@@ -5,6 +5,7 @@
 #include <linux/config.h>
 #include <linux/module.h>
 #include <linux/sched.h>
+#include <linux/blkdev.h>
 #include <linux/vmalloc.h>
 #include <asm/uaccess.h>
 #include <linux/reiserfs_fs.h>
@@ -420,7 +421,7 @@
 
 /* possible values for "-o hash=" and bits which are to be set in s_mount_opt
    of reiserfs specific part of in-core super block */
-const arg_desc_t hash[] = {
+static const arg_desc_t hash[] = {
     {"rupasov", FORCE_RUPASOV_HASH},
     {"tea", FORCE_TEA_HASH},
     {"r5", FORCE_R5_HASH},
@@ -431,7 +432,7 @@
 
 /* possible values for "-o block-allocator=" and bits which are to be set in
    s_mount_opt of reiserfs specific part of in-core super block */
-const arg_desc_t balloc[] = {
+static const arg_desc_t balloc[] = {
     {"noborder", REISERFS_NO_BORDER},
     {"no_unhashed_relocation", REISERFS_NO_UNHASHED_RELOCATION},
     {"hashed_relocation", REISERFS_HASHED_RELOCATION},
@@ -439,7 +440,7 @@
     {NULL, 0}
 };
 
-const arg_desc_t tails[] = {
+static const arg_desc_t tails[] = {
     {"on", REISERFS_LARGETAIL},
     {"off", -1},
     {"small", REISERFS_SMALLTAIL},
@@ -865,7 +866,7 @@
     brelse (bh);
 
     if (s->s_blocksize != 4096) {
-	printk("Unsupported reiserfs blocksize: %d on %s, only 4096 bytes "
+	printk("Unsupported reiserfs blocksize: %ld on %s, only 4096 bytes "
 	       "blocksize is supported.\n", s->s_blocksize, kdevname (s->s_dev));
 	return 1;
     }
@@ -941,8 +942,7 @@
     ll_rw_block(READ, 1, &(SB_AP_BITMAP(s)[i].bh)) ;
     wait_on_buffer(SB_AP_BITMAP(s)[i].bh) ;
     if (!buffer_uptodate(SB_AP_BITMAP(s)[i].bh)) {
-      printk("reread_meta_blocks, error reading bitmap block number %d at
-      %ld\n", i, SB_AP_BITMAP(s)[i].bh->b_blocknr) ;
+      printk("reread_meta_blocks, error reading bitmap block number %d at %ld\n", i, SB_AP_BITMAP(s)[i].bh->b_blocknr) ;
       return 1 ;
     }
   }
@@ -1112,7 +1112,6 @@
     struct inode *root_inode;
     kdev_t dev = s->s_dev;
     int j;
-    extern int *blksize_size[];
     struct reiserfs_transaction_handle th ;
     int old_format = 0;
     unsigned long blocks;
@@ -1161,6 +1160,16 @@
 
     rs = SB_DISK_SUPER_BLOCK (s);
 
+    /* Let's do basic sanity check to verify that underlying device is not
+       smaller than the filesystem. If the check fails then abort and scream,
+       because bad stuff will happen otherwise. */
+   if ( blk_size[MAJOR(dev)][MINOR(dev)] < sb_block_count(rs)*(sb_blocksize(rs)>>10) ) {
+	printk("Filesystem on %s cannot be mounted because it is bigger than the device\n", kdevname(dev));
+	printk("You may need to run fsck or increase size of your LVM partition\n");
+	printk("Or may be you forgot to reboot after fdisk when it told you to\n");
+	return NULL;
+    }
+
     s->u.reiserfs_sb.s_mount_state = SB_REISERFS_STATE(s);
     s->u.reiserfs_sb.s_mount_state = REISERFS_VALID_FS ;
 
diff -urN linux-2.4.21-pre5/fs/seq_file.c linux-2.4.21-pre6/fs/seq_file.c
--- linux-2.4.21-pre5/fs/seq_file.c	Sat Nov 17 18:16:22 2001
+++ linux-2.4.21-pre6/fs/seq_file.c	Wed Mar 26 16:11:40 2003
@@ -94,8 +94,10 @@
 		m->buf = kmalloc(m->size <<= 1, GFP_KERNEL);
 		if (!m->buf)
 			goto Enomem;
+		m->count = 0;
 	}
 	m->op->stop(m, p);
+	m->count = 0;
 	goto Done;
 Fill:
 	/* they want more? let's try to get some more */
diff -urN linux-2.4.21-pre5/include/asm-alpha/io.h linux-2.4.21-pre6/include/asm-alpha/io.h
--- linux-2.4.21-pre5/include/asm-alpha/io.h	Wed Mar 26 16:11:29 2003
+++ linux-2.4.21-pre6/include/asm-alpha/io.h	Wed Mar 26 16:11:40 2003
@@ -457,7 +457,9 @@
 
 #define eth_io_copy_and_sum(skb,src,len,unused) \
   memcpy_fromio((skb)->data,(src),(len))
-
+#define isa_eth_io_copy_and_sum(skb,src,len,unused) \
+  isa_memcpy_fromio((skb)->data,(src),(len))
+ 
 static inline int
 check_signature(unsigned long io_addr, const unsigned char *signature,
 		int length)
diff -urN linux-2.4.21-pre5/include/asm-alpha/processor.h linux-2.4.21-pre6/include/asm-alpha/processor.h
--- linux-2.4.21-pre5/include/asm-alpha/processor.h	Fri Oct  5 12:11:05 2001
+++ linux-2.4.21-pre6/include/asm-alpha/processor.h	Wed Mar 26 16:11:40 2003
@@ -119,7 +119,7 @@
 extern void release_thread(struct task_struct *);
 
 /* Create a kernel thread without removing it from tasklists.  */
-extern long kernel_thread(int (*fn)(void *), void *arg, unsigned long flags);
+extern long arch_kernel_thread(int (*fn)(void *), void *arg, unsigned long flags);
 
 #define copy_segments(tsk, mm)		do { } while (0)
 #define release_segments(mm)		do { } while (0)
diff -urN linux-2.4.21-pre5/include/asm-arm/processor.h linux-2.4.21-pre6/include/asm-arm/processor.h
--- linux-2.4.21-pre5/include/asm-arm/processor.h	Fri Aug  2 17:39:45 2002
+++ linux-2.4.21-pre6/include/asm-arm/processor.h	Wed Mar 26 16:11:40 2003
@@ -117,7 +117,7 @@
 /*
  * Create a new kernel thread
  */
-extern int kernel_thread(int (*fn)(void *), void *arg, unsigned long flags);
+extern int arch_kernel_thread(int (*fn)(void *), void *arg, unsigned long flags);
 
 #endif
 
diff -urN linux-2.4.21-pre5/include/asm-cris/processor.h linux-2.4.21-pre6/include/asm-cris/processor.h
--- linux-2.4.21-pre5/include/asm-cris/processor.h	Wed Mar 26 16:11:29 2003
+++ linux-2.4.21-pre6/include/asm-cris/processor.h	Wed Mar 26 16:11:40 2003
@@ -81,7 +81,7 @@
 #define INIT_THREAD  { \
    0, 0, 0x20 }  /* ccr = int enable, nothing else */
 
-extern int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags);
+extern int arch_kernel_thread(int (*fn)(void *), void * arg, unsigned long flags);
 
 /* give the thread a program location
  * set user-mode (The 'U' flag (User mode flag) is CCR/DCCR bit 8) 
diff -urN linux-2.4.21-pre5/include/asm-i386/byteorder.h linux-2.4.21-pre6/include/asm-i386/byteorder.h
--- linux-2.4.21-pre5/include/asm-i386/byteorder.h	Thu Nov 22 11:46:18 2001
+++ linux-2.4.21-pre6/include/asm-i386/byteorder.h	Wed Mar 26 16:11:40 2003
@@ -24,21 +24,41 @@
 	return x;
 }
 
+/* gcc should generate this for open coded C now too. May be worth switching to 
+   it because inline assembly cannot be scheduled. -AK */
 static __inline__ __const__ __u16 ___arch__swab16(__u16 x)
 {
-	__asm__("xchgb %b0,%h0"		/* swap bytes		*/ \
-		: "=q" (x) \
-		:  "0" (x)); \
+	__asm__("xchgb %b0,%h0"		/* swap bytes		*/
+		: "=q" (x)
+		:  "0" (x));
 		return x;
 }
 
+
+static inline __u64 ___arch__swab64(__u64 val) 
+{ 
+	union { 
+		struct { __u32 a,b; } s;
+		__u64 u;
+	} v;
+	v.u = val;
+#ifdef CONFIG_X86_BSWAP
+	asm("bswapl %0 ; bswapl %1 ; xchgl %0,%1" 
+	    : "=r" (v.s.a), "=r" (v.s.b) 
+	    : "0" (v.s.a), "1" (v.s.b)); 
+#else
+   v.s.a = ___arch__swab32(v.s.a); 
+	v.s.b = ___arch__swab32(v.s.b); 
+	asm("xchgl %0,%1" : "=r" (v.s.a), "=r" (v.s.b) : "0" (v.s.a), "1" (v.s.b));
+#endif
+	return v.u;	
+} 
+
+#define __arch__swab64(x) ___arch__swab64(x)
 #define __arch__swab32(x) ___arch__swab32(x)
 #define __arch__swab16(x) ___arch__swab16(x)
 
-#if !defined(__STRICT_ANSI__) || defined(__KERNEL__)
-#  define __BYTEORDER_HAS_U64__
-#  define __SWAB_64_THRU_32__
-#endif
+#define __BYTEORDER_HAS_U64__
 
 #endif /* __GNUC__ */
 
diff -urN linux-2.4.21-pre5/include/asm-i386/delay.h linux-2.4.21-pre6/include/asm-i386/delay.h
--- linux-2.4.21-pre5/include/asm-i386/delay.h	Wed Mar 26 16:11:29 2003
+++ linux-2.4.21-pre6/include/asm-i386/delay.h	Wed Mar 26 16:11:40 2003
@@ -11,7 +11,7 @@
 extern void __bad_ndelay(void);
 
 extern void __udelay(unsigned long usecs);
-extern void __ndelay(unsigned long usecs);
+extern void __ndelay(unsigned long nsecs);
 extern void __const_udelay(unsigned long usecs);
 extern void __delay(unsigned long loops);
 
diff -urN linux-2.4.21-pre5/include/asm-i386/processor.h linux-2.4.21-pre6/include/asm-i386/processor.h
--- linux-2.4.21-pre5/include/asm-i386/processor.h	Fri Aug  2 17:39:45 2002
+++ linux-2.4.21-pre6/include/asm-i386/processor.h	Wed Mar 26 16:11:40 2003
@@ -433,7 +433,7 @@
 /*
  * create a kernel thread without removing it from tasklists
  */
-extern int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags);
+extern int arch_kernel_thread(int (*fn)(void *), void * arg, unsigned long flags);
 
 /* Copy and release all segment info associated with a VM */
 extern void copy_segments(struct task_struct *p, struct mm_struct * mm);
@@ -483,7 +483,7 @@
 #define cpu_relax()	rep_nop()
 
 /* Prefetch instructions for Pentium III and AMD Athlon */
-#ifdef 	CONFIG_MPENTIUMIII
+#if defined(CONFIG_MPENTIUMIII) || defined (CONFIG_MPENTIUM4)
 
 #define ARCH_HAS_PREFETCH
 extern inline void prefetch(const void *x)
diff -urN linux-2.4.21-pre5/include/asm-ia64/processor.h linux-2.4.21-pre6/include/asm-ia64/processor.h
--- linux-2.4.21-pre5/include/asm-ia64/processor.h	Wed Mar 26 16:11:29 2003
+++ linux-2.4.21-pre6/include/asm-ia64/processor.h	Wed Mar 26 16:11:40 2003
@@ -372,7 +372,7 @@
  * do_basic_setup() and the timing is such that free_initmem() has
  * been called already.
  */
-extern int kernel_thread (int (*fn)(void *), void *arg, unsigned long flags);
+extern int arch_kernel_thread (int (*fn)(void *), void *arg, unsigned long flags);
 
 /* Copy and release all segment info associated with a VM */
 #define copy_segments(tsk, mm)			do { } while (0)
diff -urN linux-2.4.21-pre5/include/asm-m68k/amigahw.h linux-2.4.21-pre6/include/asm-m68k/amigahw.h
--- linux-2.4.21-pre5/include/asm-m68k/amigahw.h	Mon Jun 11 19:15:27 2001
+++ linux-2.4.21-pre6/include/asm-m68k/amigahw.h	Wed Mar 26 16:11:40 2003
@@ -324,7 +324,7 @@
 };
 #define TOD3000_CNTRL1_HOLD	0
 #define TOD3000_CNTRL1_FREE	9
-#define TOD_3000 ((struct tod3000 *)(zTwoBase+0xDC0000))
+#define tod_3000 ((*(volatile struct tod3000 *)(zTwoBase+0xDC0000)))
 
 struct tod2000 {
   unsigned int  :28, second2:4;	/* lower digit */
@@ -349,6 +349,6 @@
 #define TOD2000_CNTRL1_BUSY	(1<<1)
 #define TOD2000_CNTRL3_24HMODE	(1<<2)
 #define TOD2000_HOUR1_PM	(1<<2)
-#define TOD_2000 ((struct tod2000 *)(zTwoBase+0xDC0000))
+#define tod_2000 ((*(volatile struct tod2000 *)(zTwoBase+0xDC0000)))
 
 #endif /* _M68K_AMIGAHW_H */
diff -urN linux-2.4.21-pre5/include/asm-m68k/apollohw.h linux-2.4.21-pre6/include/asm-m68k/apollohw.h
--- linux-2.4.21-pre5/include/asm-m68k/apollohw.h	Wed Jan 26 12:44:21 2000
+++ linux-2.4.21-pre6/include/asm-m68k/apollohw.h	Wed Mar 26 16:11:40 2003
@@ -101,9 +101,4 @@
 
 #define isaIO2mem(x) (((((x) & 0x3f8)  << 7) | (((x) & 0xfc00) >> 6) | ((x) & 0x7)) + 0x40000 + IO_BASE)
 
-#define inb(addr) (*((volatile unsigned char *)(addr)))
-#define outb(val,addr) (*((volatile unsigned char *)(addr)) = (val))
-#define inw(addr) (*((volatile unsigned short *)(addr)))
-#define outw(val,addr) (*((volatile unsigned short *)(addr)) = (val))
-
 #endif
diff -urN linux-2.4.21-pre5/include/asm-m68k/dvma.h linux-2.4.21-pre6/include/asm-m68k/dvma.h
--- linux-2.4.21-pre5/include/asm-m68k/dvma.h	Mon Jun 11 19:15:27 2001
+++ linux-2.4.21-pre6/include/asm-m68k/dvma.h	Wed Mar 26 16:11:40 2003
@@ -22,7 +22,8 @@
 
 #define dvma_malloc(x) dvma_malloc_align(x, 0)
 #define dvma_map(x, y) dvma_map_align(x, y, 0)
-
+#define dvma_map_vme(x, y) (dvma_map(x, y) & 0xfffff)
+#define dvma_map_align_vme(x, y, z) (dvma_map_align (x, y, z) & 0xfffff)
 extern unsigned long dvma_map_align(unsigned long kaddr, int len, 
 			    int align);
 extern void *dvma_malloc_align(unsigned long len, unsigned long align);
@@ -52,6 +53,8 @@
 /* virt <-> phys conversions */
 #define dvma_vtop(x) ((unsigned long)(x) & 0xffffff)
 #define dvma_ptov(x) ((unsigned long)(x) | 0xf000000)
+#define dvma_vtovme(x) ((unsigned long)(x) & 0x00fffff)
+#define dvma_vmetov(x) ((unsigned long)(x) | 0xff00000)
 #define dvma_vtob(x) dvma_vtop(x)
 #define dvma_btov(x) dvma_ptov(x)
 
diff -urN linux-2.4.21-pre5/include/asm-m68k/floppy.h linux-2.4.21-pre6/include/asm-m68k/floppy.h
--- linux-2.4.21-pre5/include/asm-m68k/floppy.h	Thu Oct 25 13:53:55 2001
+++ linux-2.4.21-pre6/include/asm-m68k/floppy.h	Wed Mar 26 16:11:40 2003
@@ -9,7 +9,7 @@
  *
  * Copyright (C) 1999, 2000, 2001
  *
- * Sun3x support added 2/4/2000 Sam Creasey (sammy@oh.verio.com)
+ * Sun3x support added 2/4/2000 Sam Creasey (sammy@sammy.net)
  *
  */
 
diff -urN linux-2.4.21-pre5/include/asm-m68k/io.h linux-2.4.21-pre6/include/asm-m68k/io.h
--- linux-2.4.21-pre5/include/asm-m68k/io.h	Thu Nov 28 15:53:15 2002
+++ linux-2.4.21-pre6/include/asm-m68k/io.h	Wed Mar 26 16:11:40 2003
@@ -162,7 +162,9 @@
 #ifdef CONFIG_GG2
     case GG2_ISA: return GG2_ISA_MEM_B(addr);
 #endif
-      /* FIXME: any ISA mem mapping for PCMCIA? */
+#ifdef CONFIG_AMIGA_PCMCIA
+    case AG_ISA: return addr;
+#endif
     default: return 0; /* avoid warnings, just in case */
     }
 }
@@ -176,6 +178,9 @@
 #ifdef CONFIG_GG2
     case GG2_ISA: return GG2_ISA_MEM_W(addr);
 #endif
+#ifdef CONFIG_AMIGA_PCMCIA
+    case AG_ISA: return addr;
+#endif
     default: return 0; /* avoid warnings, just in case */
     }
 }
@@ -187,9 +192,9 @@
 #define isa_outw(val,port) (ISA_SEX ? out_be16(isa_itw(port),(val)) : out_le16(isa_itw(port),(val)))
 
 #define isa_readb(p)       in_8(isa_mtb(p))
-#define isa_readw(p)       in_le16(isa_mtw(p))
+#define isa_readw(p)       (ISA_SEX ? in_be16(isa_mtw(p)) : in_le16(isa_mtw(p)))
 #define isa_writeb(val,p)  out_8(isa_mtb(p),(val))
-#define isa_writew(val,p)  out_le16(isa_mtw(p),(val))
+#define isa_writew(val,p)  (ISA_SEX ? out_be16(isa_mtw(p),(val)) : out_le16(isa_mtw(p),(val)))
 
 static inline void isa_delay(void)
 {
@@ -280,23 +285,6 @@
 #endif /* CONFIG_PCI */
 
 
-/* Values for nocacheflag and cmode */
-#define IOMAP_FULL_CACHING		0
-#define IOMAP_NOCACHE_SER		1
-#define IOMAP_NOCACHE_NONSER		2
-#define IOMAP_WRITETHROUGH		3
-
-/*
- * Change "struct page" to physical address.
- */
-#define page_to_phys(page)	((page - mem_map) << PAGE_SHIFT)
-
-extern void iounmap(void *addr);
-
-extern void *__ioremap(unsigned long physaddr, unsigned long size,
-		       int cacheflag);
-extern void __iounmap(void *addr, unsigned long size);
-
 extern inline void *ioremap(unsigned long physaddr, unsigned long size)
 {
 	return __ioremap(physaddr, size, IOMAP_NOCACHE_SER);
diff -urN linux-2.4.21-pre5/include/asm-m68k/nubus.h linux-2.4.21-pre6/include/asm-m68k/nubus.h
--- linux-2.4.21-pre5/include/asm-m68k/nubus.h	Thu Nov 28 15:53:15 2002
+++ linux-2.4.21-pre6/include/asm-m68k/nubus.h	Wed Mar 26 16:11:40 2003
@@ -1,7 +1,7 @@
 #ifndef _ASM_M68K_NUBUS_H
 #define _ASM_M68K_NUBUS_H
 
-#include <asm/io.h>
+#include <asm/raw_io.h>
 
 #define nubus_readb raw_inb
 #define nubus_readw raw_inw
diff -urN linux-2.4.21-pre5/include/asm-m68k/page.h linux-2.4.21-pre6/include/asm-m68k/page.h
--- linux-2.4.21-pre5/include/asm-m68k/page.h	Fri Aug  2 17:39:45 2002
+++ linux-2.4.21-pre6/include/asm-m68k/page.h	Wed Mar 26 16:11:40 2003
@@ -6,10 +6,13 @@
 /* PAGE_SHIFT determines the page size */
 #ifndef CONFIG_SUN3
 #define PAGE_SHIFT	(12)
-#define PAGE_SIZE	(4096)
 #else
 #define PAGE_SHIFT	(13)
-#define PAGE_SIZE	(8192)
+#endif
+#ifdef __ASSEMBLY__
+#define PAGE_SIZE	(1 << PAGE_SHIFT)
+#else
+#define PAGE_SIZE	(1UL << PAGE_SHIFT)
 #endif
 #define PAGE_MASK	(~(PAGE_SIZE-1))
 
@@ -141,7 +144,7 @@
 {
      if(x == 0)
 	  return 0;
-     if(x > PAGE_OFFSET)
+     if(x >= PAGE_OFFSET)
         return (x-PAGE_OFFSET);
      else
         return (x+0x2000000);
diff -urN linux-2.4.21-pre5/include/asm-m68k/processor.h linux-2.4.21-pre6/include/asm-m68k/processor.h
--- linux-2.4.21-pre5/include/asm-m68k/processor.h	Fri Oct  5 12:11:05 2001
+++ linux-2.4.21-pre6/include/asm-m68k/processor.h	Wed Mar 26 16:11:40 2003
@@ -105,7 +105,7 @@
 {
 }
 
-extern int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags);
+extern int arch_kernel_thread(int (*fn)(void *), void * arg, unsigned long flags);
 
 #define copy_segments(tsk, mm)		do { } while (0)
 #define release_segments(mm)		do { } while (0)
diff -urN linux-2.4.21-pre5/include/asm-m68k/raw_io.h linux-2.4.21-pre6/include/asm-m68k/raw_io.h
--- linux-2.4.21-pre5/include/asm-m68k/raw_io.h	Thu Nov 28 15:53:15 2002
+++ linux-2.4.21-pre6/include/asm-m68k/raw_io.h	Wed Mar 26 16:11:40 2003
@@ -11,6 +11,19 @@
 #ifdef __KERNEL__
 
 
+/* Values for nocacheflag and cmode */
+#define IOMAP_FULL_CACHING		0
+#define IOMAP_NOCACHE_SER		1
+#define IOMAP_NOCACHE_NONSER		2
+#define IOMAP_WRITETHROUGH		3
+
+extern void iounmap(void *addr);
+
+extern void *__ioremap(unsigned long physaddr, unsigned long size,
+		       int cacheflag);
+extern void __iounmap(void *addr, unsigned long size);
+
+
 /* ++roman: The assignments to temp. vars avoid that gcc sometimes generates
  * two accesses to memory, which may be undesirable for some devices.
  */
diff -urN linux-2.4.21-pre5/include/asm-m68k/sbus.h linux-2.4.21-pre6/include/asm-m68k/sbus.h
--- linux-2.4.21-pre5/include/asm-m68k/sbus.h	Mon Jun 11 19:15:27 2001
+++ linux-2.4.21-pre6/include/asm-m68k/sbus.h	Wed Mar 26 16:11:40 2003
@@ -5,7 +5,7 @@
 #ifndef __M68K_SBUS_H
 #define __M68K_SBUS_H
 
-struct linux_sbus_device {
+struct sbus_dev {
 	struct {
 		unsigned int which_io;
 		unsigned int phys_addr;
diff -urN linux-2.4.21-pre5/include/asm-m68k/sun3-head.h linux-2.4.21-pre6/include/asm-m68k/sun3-head.h
--- linux-2.4.21-pre5/include/asm-m68k/sun3-head.h	Sat Sep  4 13:06:41 1999
+++ linux-2.4.21-pre6/include/asm-m68k/sun3-head.h	Wed Mar 26 16:11:40 2003
@@ -9,4 +9,4 @@
 #define FC_SUPERD    5
 #define FC_CPU      7
 
-#endif __SUN3_HEAD_H
+#endif /* __SUN3_HEAD_H */
diff -urN linux-2.4.21-pre5/include/asm-m68k/sun3ints.h linux-2.4.21-pre6/include/asm-m68k/sun3ints.h
--- linux-2.4.21-pre5/include/asm-m68k/sun3ints.h	Mon Jun 11 19:15:27 2001
+++ linux-2.4.21-pre6/include/asm-m68k/sun3ints.h	Wed Mar 26 16:11:40 2003
@@ -20,6 +20,8 @@
 #include <asm/intersil.h>
 #include <asm/oplib.h>
 
+#define SUN3_INT_VECS 192
+
 void sun3_enable_irq(unsigned int irq);
 void sun3_disable_irq(unsigned int irq);
 int sun3_request_irq(unsigned int irq,
@@ -36,5 +38,11 @@
 extern void sun3_process_int(int, struct pt_regs *);
 extern volatile unsigned char* sun3_intreg;
 
+/* master list of VME vectors -- don't fuck with this */
+#define SUN3_VEC_FLOPPY 0x40
+#define SUN3_VEC_VMESCSI0 0x40
+#define SUN3_VEC_VMESCSI1 0x41
+#define SUN3_VEC_CG 0xA8
+
 
 #endif /* SUN3INTS_H */
diff -urN linux-2.4.21-pre5/include/asm-m68k/sun3mmu.h linux-2.4.21-pre6/include/asm-m68k/sun3mmu.h
--- linux-2.4.21-pre5/include/asm-m68k/sun3mmu.h	Sat Sep  4 13:06:41 1999
+++ linux-2.4.21-pre6/include/asm-m68k/sun3mmu.h	Wed Mar 26 16:11:40 2003
@@ -161,6 +161,10 @@
 	return;
 }
 
+extern void *sun3_ioremap(unsigned long phys, unsigned long size,
+			  unsigned long type);
+
+extern int sun3_map_test(unsigned long addr, char *val);
 
 #endif	/* !__ASSEMBLY__ */
 
diff -urN linux-2.4.21-pre5/include/asm-m68k/sun3xflop.h linux-2.4.21-pre6/include/asm-m68k/sun3xflop.h
--- linux-2.4.21-pre5/include/asm-m68k/sun3xflop.h	Thu Oct 25 13:53:55 2001
+++ linux-2.4.21-pre6/include/asm-m68k/sun3xflop.h	Wed Mar 26 16:11:40 2003
@@ -3,7 +3,7 @@
  * Derived partially from asm-sparc/floppy.h, which is:
  *     Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
  *
- * Sun3x version 2/4/2000 Sam Creasey (sammy@oh.verio.com)
+ * Sun3x version 2/4/2000 Sam Creasey (sammy@sammy.net)
  */
 
 #ifndef __ASM_SUN3X_FLOPPY_H
diff -urN linux-2.4.21-pre5/include/asm-m68k/virtconvert.h linux-2.4.21-pre6/include/asm-m68k/virtconvert.h
--- linux-2.4.21-pre5/include/asm-m68k/virtconvert.h	Wed Jan 26 12:44:21 2000
+++ linux-2.4.21-pre6/include/asm-m68k/virtconvert.h	Wed Mar 26 16:11:40 2003
@@ -85,5 +85,12 @@
 #define bus_to_virt phys_to_virt
 #endif
 
+/*
+ * Change "struct page" to physical address.
+ */
+
+#define __page_address(page)      (PAGE_OFFSET + (((page) - mem_map) << PAGE_SHIFT))
+#define page_to_phys(page)       virt_to_phys((void *)__page_address(page))
+
 #endif
 #endif
diff -urN linux-2.4.21-pre5/include/asm-m68k/zorro.h linux-2.4.21-pre6/include/asm-m68k/zorro.h
--- linux-2.4.21-pre5/include/asm-m68k/zorro.h	Thu Oct 25 13:53:55 2001
+++ linux-2.4.21-pre6/include/asm-m68k/zorro.h	Wed Mar 26 16:11:40 2003
@@ -1,5 +1,6 @@
 #ifndef _ASM_M68K_ZORRO_H
 #define _ASM_M68K_ZORRO_H
+
 #include <asm/raw_io.h>
 
 #define z_readb raw_inb
@@ -14,22 +15,6 @@
 #define z_memcpy_fromio(a,b,c)	memcpy((a),(void *)(b),(c))
 #define z_memcpy_toio(a,b,c)	memcpy((void *)(a),(b),(c))
 
-
-/* Values for nocacheflag and cmode */
-#ifndef IOMAP_FULL_CACHING		
-#define IOMAP_FULL_CACHING		0
-#define IOMAP_NOCACHE_SER		1
-#define IOMAP_NOCACHE_NONSER		2
-#define IOMAP_WRITETHROUGH		3
-#endif
-
-extern void iounmap(void *addr);
-
-extern void *__ioremap(unsigned long physaddr, unsigned long size,
-		       int cacheflag);
-extern void __iounmap(void *addr, unsigned long size);
-
-
 extern inline void *z_remap_nocache_ser(unsigned long physaddr, 
 					  unsigned long size)
 {
diff -urN linux-2.4.21-pre5/include/asm-mips/processor.h linux-2.4.21-pre6/include/asm-mips/processor.h
--- linux-2.4.21-pre5/include/asm-mips/processor.h	Thu Nov 28 15:53:15 2002
+++ linux-2.4.21-pre6/include/asm-mips/processor.h	Wed Mar 26 16:11:40 2003
@@ -188,7 +188,7 @@
 /* Free all resources held by a thread. */
 #define release_thread(thread) do { } while(0)
 
-extern int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags);
+extern int arch_kernel_thread(int (*fn)(void *), void * arg, unsigned long flags);
 
 /* Copy and release all segment info associated with a VM */
 #define copy_segments(p, mm) do { } while(0)
diff -urN linux-2.4.21-pre5/include/asm-mips64/processor.h linux-2.4.21-pre6/include/asm-mips64/processor.h
--- linux-2.4.21-pre5/include/asm-mips64/processor.h	Thu Nov 28 15:53:15 2002
+++ linux-2.4.21-pre6/include/asm-mips64/processor.h	Wed Mar 26 16:11:40 2003
@@ -231,7 +231,7 @@
 /* Free all resources held by a thread. */
 #define release_thread(thread) do { } while(0)
 
-extern int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags);
+extern int arch_kernel_thread(int (*fn)(void *), void * arg, unsigned long flags);
 
 /* Copy and release all segment info associated with a VM */
 #define copy_segments(p, mm) do { } while(0)
diff -urN linux-2.4.21-pre5/include/asm-parisc/processor.h linux-2.4.21-pre6/include/asm-parisc/processor.h
--- linux-2.4.21-pre5/include/asm-parisc/processor.h	Wed Mar 26 16:11:29 2003
+++ linux-2.4.21-pre6/include/asm-parisc/processor.h	Wed Mar 26 16:11:40 2003
@@ -292,7 +292,7 @@
 
 /* Free all resources held by a thread. */
 extern void release_thread(struct task_struct *);
-extern int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags);
+extern int arch_kernel_thread(int (*fn)(void *), void * arg, unsigned long flags);
 
 extern void map_hpux_gateway_page(struct task_struct *tsk, struct mm_struct *mm);
 
diff -urN linux-2.4.21-pre5/include/asm-parisc/rtc.h linux-2.4.21-pre6/include/asm-parisc/rtc.h
--- linux-2.4.21-pre5/include/asm-parisc/rtc.h	Wed Dec 31 16:00:00 1969
+++ linux-2.4.21-pre6/include/asm-parisc/rtc.h	Wed Mar 26 16:11:40 2003
@@ -0,0 +1,132 @@
+/* include/asm-parisc/rtc.h */
+
+#ifndef _ASM_RTC_H
+#define _ASM_RTC_H
+
+#ifdef __KERNEL__
+
+#include <linux/rtc.h>
+#include <asm/errno.h>
+
+#define RTC_PIE 0x40		/* periodic interrupt enable */
+#define RTC_AIE 0x20		/* alarm interrupt enable */
+#define RTC_UIE 0x10		/* update-finished interrupt enable */
+
+#define RTC_BATT_BAD 0x100	/* battery bad */
+
+/* some dummy definitions */
+#define RTC_SQWE 0x08		/* enable square-wave output */
+#define RTC_DM_BINARY 0x04	/* all time/date values are BCD if clear */
+#define RTC_24H 0x02		/* 24 hour mode - else hours bit 7 means pm */
+#define RTC_DST_EN 0x01	        /* auto switch DST - works f. USA only */
+
+
+/* constants for calculation */
+#define SECS_PER_HOUR   (60 * 60)
+#define SECS_PER_DAY    (SECS_PER_HOUR * 24)
+
+#define __isleap(year) \
+  ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
+
+
+/* How many days come before each month (0-12).  */
+static const unsigned short int __mon_yday[2][13] =
+{
+	/* Normal years.  */
+	{ 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
+	/* Leap years.  */
+	{ 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
+};
+
+static inline unsigned int get_rtc_time(struct rtc_time *wtime)
+{
+	/*
+	 * Only the values that we read from the RTC are set. We leave
+	 * tm_wday, tm_yday and tm_isdst untouched. Even though the
+	 * RTC has RTC_DAY_OF_WEEK, we ignore it, as it is only updated
+	 * by the RTC when initially set to a non-zero value.
+	 */
+	
+	struct pdc_tod tod_data;
+	long int days, rem, y;
+	const unsigned short int *ip;
+
+	if (pdc_tod_read(&tod_data) < 0)
+		return (RTC_24H | RTC_BATT_BAD);
+
+	
+	// most of the remainder of this function is:
+	//	Copyright (C) 1991, 1993, 1997, 1998 Free Software Foundation, Inc.
+	//	This was originally a part of the GNU C Library.
+	//      It is distributed under the GPL, and was swiped from offtime.c
+
+
+	days = tod_data.tod_sec / SECS_PER_DAY;
+	rem = tod_data.tod_sec % SECS_PER_DAY;
+
+	wtime->tm_hour = rem / SECS_PER_HOUR;
+	rem %= SECS_PER_HOUR;
+	wtime->tm_min = rem / 60;
+	wtime->tm_sec = rem % 60;
+
+	y = 1970;
+	
+#define DIV(a, b) ((a) / (b) - ((a) % (b) < 0))
+#define LEAPS_THRU_END_OF(y) (DIV (y, 4) - DIV (y, 100) + DIV (y, 400))
+
+	while (days < 0 || days >= (__isleap (y) ? 366 : 365))
+	{
+		/* Guess a corrected year, assuming 365 days per year.  */
+		long int yg = y + days / 365 - (days % 365 < 0);
+
+		/* Adjust DAYS and Y to match the guessed year.  */
+		days -= ((yg - y) * 365
+			 + LEAPS_THRU_END_OF (yg - 1)
+			 - LEAPS_THRU_END_OF (y - 1));
+		y = yg;
+	}
+	wtime->tm_year = y - 1900;
+#undef DIV
+#undef LEAPS_THRU_END_OF
+
+	ip = __mon_yday[__isleap(y)];
+	for (y = 11; days < (long int) ip[y]; --y)
+		continue;
+	days -= ip[y];
+	wtime->tm_mon = y;
+	wtime->tm_mday = days + 1;
+	
+	return (RTC_24H);
+}
+
+static inline int set_rtc_time(struct rtc_time *wtime)
+{
+	u_int32_t secs;
+
+	secs = mktime(wtime->tm_year + 1900, wtime->tm_mon + 1, wtime->tm_mday, 
+		      wtime->tm_hour, wtime->tm_min, wtime->tm_sec);
+
+	if (pdc_tod_set(secs, 0) < 0)
+		return -EINVAL;
+	else
+		return 0;
+}
+
+static inline unsigned int get_rtc_ss(void)
+{
+	return -EINVAL;
+}
+
+static inline int get_rtc_pll(struct rtc_pll_info *pll)
+{
+	return -EINVAL;
+}
+static inline int set_rtc_pll(struct rtc_pll_info *pll)
+{
+	return -EINVAL;
+}
+
+#endif /* __KERNEL__ */
+
+#endif /* _ASM__RTC_H */
+
diff -urN linux-2.4.21-pre5/include/asm-ppc/bootinfo.h linux-2.4.21-pre6/include/asm-ppc/bootinfo.h
--- linux-2.4.21-pre5/include/asm-ppc/bootinfo.h	Wed Mar 26 16:11:29 2003
+++ linux-2.4.21-pre6/include/asm-ppc/bootinfo.h	Wed Mar 26 16:11:40 2003
@@ -29,6 +29,7 @@
 #define BI_SYSMAP		0x1015
 #define BI_MACHTYPE		0x1016
 #define BI_MEMSIZE		0x1017
+#define BI_BOARD_INFO		0x1018
 
 extern struct bi_record *find_bootinfo(void);
 extern void parse_bootinfo(struct bi_record *rec);
diff -urN linux-2.4.21-pre5/include/asm-ppc/cache.h linux-2.4.21-pre6/include/asm-ppc/cache.h
--- linux-2.4.21-pre5/include/asm-ppc/cache.h	Wed Mar 26 16:11:29 2003
+++ linux-2.4.21-pre6/include/asm-ppc/cache.h	Wed Mar 26 16:11:40 2003
@@ -39,6 +39,8 @@
 
 #ifndef __ASSEMBLY__
 extern void flush_dcache_range(unsigned long start, unsigned long stop);
+extern void invalidate_dcache_range(unsigned long start, unsigned long stop);
+extern void clean_dcache_range(unsigned long start, unsigned long stop);
 #endif /* __ASSEMBLY__ */
 
 /* prep registers for L2 */
diff -urN linux-2.4.21-pre5/include/asm-ppc/div64.h linux-2.4.21-pre6/include/asm-ppc/div64.h
--- linux-2.4.21-pre5/include/asm-ppc/div64.h	Wed Mar 26 16:11:29 2003
+++ linux-2.4.21-pre6/include/asm-ppc/div64.h	Wed Mar 26 16:11:40 2003
@@ -1,10 +1,23 @@
 #ifndef __PPC_DIV64
 #define __PPC_DIV64
 
-#define do_div(n,base) ({ \
-int __res; \
-__res = ((unsigned long) n) % (unsigned) base; \
-n = ((unsigned long) n) / (unsigned) base; \
-__res; })
+#include <linux/types.h>
+
+extern u32 __div64_32(u64 *dividend, u32 div);
+
+#define do_div(n, div)	({			\
+	u64 __n = (n);				\
+	u32 __d = (div);			\
+	u32 __q, __r;				\
+	if ((__n >> 32) == 0) {			\
+		__q = (u32)__n / __d;		\
+		__r = (u32)__n - __q * __d;	\
+		(n) = __q;			\
+	} else {				\
+		__r = __div64_32(&__n, __d);	\
+		(n) = __n;			\
+	}					\
+	__r;					\
+})
 
 #endif
diff -urN linux-2.4.21-pre5/include/asm-ppc/highmem.h linux-2.4.21-pre6/include/asm-ppc/highmem.h
--- linux-2.4.21-pre5/include/asm-ppc/highmem.h	Wed Mar 26 16:11:29 2003
+++ linux-2.4.21-pre6/include/asm-ppc/highmem.h	Wed Mar 26 16:11:40 2003
@@ -49,16 +49,19 @@
 
 #define KMAP_FIX_BEGIN	(0xfe400000UL)
 
-extern void *kmap_high(struct page *page);
+extern void *kmap_high(struct page *page, int nonblock);
 extern void kunmap_high(struct page *page);
 
-static inline void *kmap(struct page *page)
+#define kmap(page)		__kmap(page, 0)
+#define kmap_nonblock(page)	__kmap(page, 1)
+
+static inline void *__kmap(struct page *page, int nonblock)
 {
 	if (in_interrupt())
 		BUG();
 	if (page < highmem_start_page)
 		return page_address(page);
-	return kmap_high(page);
+	return kmap_high(page, nonblock);
 }
 
 static inline void kunmap(struct page *page)
diff -urN linux-2.4.21-pre5/include/asm-ppc/machdep.h linux-2.4.21-pre6/include/asm-ppc/machdep.h
--- linux-2.4.21-pre5/include/asm-ppc/machdep.h	Wed Mar 26 16:11:29 2003
+++ linux-2.4.21-pre6/include/asm-ppc/machdep.h	Wed Mar 26 16:11:40 2003
@@ -104,6 +104,9 @@
 	 */
 	int (*feature_call)(unsigned int feature, ...);
 
+	/* Hook for board-specific info passed by the bootloader */
+	void (*board_info)(void *bdinfo, int bdinfo_size);
+
 #ifdef CONFIG_SMP
 	/* functions for dealing with other cpus */
 	struct smp_ops_t *smp_ops;
diff -urN linux-2.4.21-pre5/include/asm-ppc/processor.h linux-2.4.21-pre6/include/asm-ppc/processor.h
--- linux-2.4.21-pre5/include/asm-ppc/processor.h	Wed Mar 26 16:11:29 2003
+++ linux-2.4.21-pre6/include/asm-ppc/processor.h	Wed Mar 26 16:11:40 2003
@@ -622,7 +622,7 @@
 /*
  * Create a new kernel thread.
  */
-extern long kernel_thread(int (*fn)(void *), void *arg, unsigned long flags);
+extern long arch_kernel_thread(int (*fn)(void *), void *arg, unsigned long flags);
 
 /*
  * Bus types
diff -urN linux-2.4.21-pre5/include/asm-ppc/uaccess.h linux-2.4.21-pre6/include/asm-ppc/uaccess.h
--- linux-2.4.21-pre5/include/asm-ppc/uaccess.h	Wed Mar 26 16:11:29 2003
+++ linux-2.4.21-pre6/include/asm-ppc/uaccess.h	Wed Mar 26 16:11:40 2003
@@ -185,7 +185,7 @@
 	  case 1: __get_user_asm(x,ptr,retval,"lbz"); break;	\
 	  case 2: __get_user_asm(x,ptr,retval,"lhz"); break;	\
 	  case 4: __get_user_asm(x,ptr,retval,"lwz"); break;	\
-	  case 8: __get_user_asm2(x, ptr, retval);		\
+	  case 8: __get_user_asm2(x, ptr, retval); break;	\
 	  default: (x) = __get_user_bad();			\
 	}							\
 } while (0)
diff -urN linux-2.4.21-pre5/include/asm-ppc64/delay.h linux-2.4.21-pre6/include/asm-ppc64/delay.h
--- linux-2.4.21-pre5/include/asm-ppc64/delay.h	Wed Mar 26 16:11:29 2003
+++ linux-2.4.21-pre6/include/asm-ppc64/delay.h	Wed Mar 26 16:11:40 2003
@@ -45,6 +45,13 @@
 
 	__delay(loops);
 }
+
+static inline void ndelay(unsigned long nsecs)
+{
+	unsigned long loops = (tb_ticks_per_usec * nsecs) / 1000;
+
+	__delay(loops);
+}
 #endif /* !__ASSEMBLY__ */
 
 #endif /* _PPC64_DELAY_H */
diff -urN linux-2.4.21-pre5/include/asm-ppc64/processor.h linux-2.4.21-pre6/include/asm-ppc64/processor.h
--- linux-2.4.21-pre5/include/asm-ppc64/processor.h	Wed Mar 26 16:11:29 2003
+++ linux-2.4.21-pre6/include/asm-ppc64/processor.h	Wed Mar 26 16:11:40 2003
@@ -602,7 +602,7 @@
 /*
  * Create a new kernel thread.
  */
-extern long kernel_thread(int (*fn)(void *), void *arg, unsigned long flags);
+extern long arch_kernel_thread(int (*fn)(void *), void *arg, unsigned long flags);
 
 /*
  * Bus types
diff -urN linux-2.4.21-pre5/include/asm-s390/processor.h linux-2.4.21-pre6/include/asm-s390/processor.h
--- linux-2.4.21-pre5/include/asm-s390/processor.h	Fri Aug  2 17:39:45 2002
+++ linux-2.4.21-pre6/include/asm-s390/processor.h	Wed Mar 26 16:11:40 2003
@@ -113,7 +113,7 @@
 
 /* Free all resources held by a thread. */
 extern void release_thread(struct task_struct *);
-extern int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags);
+extern int arch_kernel_thread(int (*fn)(void *), void * arg, unsigned long flags);
 
 /* Copy and release all segment info associated with a VM */
 #define copy_segments(nr, mm)           do { } while (0)
diff -urN linux-2.4.21-pre5/include/asm-s390x/processor.h linux-2.4.21-pre6/include/asm-s390x/processor.h
--- linux-2.4.21-pre5/include/asm-s390x/processor.h	Thu Nov 28 15:53:15 2002
+++ linux-2.4.21-pre6/include/asm-s390x/processor.h	Wed Mar 26 16:11:40 2003
@@ -128,7 +128,7 @@
 
 /* Free all resources held by a thread. */
 extern void release_thread(struct task_struct *);
-extern int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags);
+extern int arch_kernel_thread(int (*fn)(void *), void * arg, unsigned long flags);
 
 /* Copy and release all segment info associated with a VM */
 #define copy_segments(nr, mm)           do { } while (0)
diff -urN linux-2.4.21-pre5/include/asm-sh/processor.h linux-2.4.21-pre6/include/asm-sh/processor.h
--- linux-2.4.21-pre5/include/asm-sh/processor.h	Fri Oct  5 12:11:05 2001
+++ linux-2.4.21-pre6/include/asm-sh/processor.h	Wed Mar 26 16:11:40 2003
@@ -137,7 +137,7 @@
 /*
  * create a kernel thread without removing it from tasklists
  */
-extern int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags);
+extern int arch_kernel_thread(int (*fn)(void *), void * arg, unsigned long flags);
 
 /*
  * Bus types
diff -urN linux-2.4.21-pre5/include/asm-sparc/highmem.h linux-2.4.21-pre6/include/asm-sparc/highmem.h
--- linux-2.4.21-pre5/include/asm-sparc/highmem.h	Thu Nov 28 15:53:15 2002
+++ linux-2.4.21-pre6/include/asm-sparc/highmem.h	Wed Mar 26 16:11:40 2003
@@ -27,7 +27,7 @@
 #define HIGHMEM_DEBUG 1
 
 /* in mm/highmem.c */
-extern void *kmap_high(struct page *page);
+extern void *kmap_high(struct page *page, int nonblocking);
 extern void kunmap_high(struct page *page);
 
 /* declarations for highmem.c */
@@ -60,13 +60,16 @@
 void *kmap_atomic(struct page *page, enum km_type type);
 void kunmap_atomic(void *kvaddr, enum km_type type);
 
-static inline void *kmap(struct page *page)
+#define kmap(page) __kmap(page, 0)
+#define kmap_nonblock(page) __kmap(page, 1)
+
+static inline void *__kmap(struct page *page, int nonblocking)
 {
 	if (in_interrupt())
 		BUG();
 	if (page < highmem_start_page)
 		return page_address(page);
-	return kmap_high(page);
+	return kmap_high(page, nonblocking);
 }
 
 static inline void kunmap(struct page *page)
diff -urN linux-2.4.21-pre5/include/asm-sparc/ioctl.h linux-2.4.21-pre6/include/asm-sparc/ioctl.h
--- linux-2.4.21-pre5/include/asm-sparc/ioctl.h	Mon Dec 20 22:05:52 1999
+++ linux-2.4.21-pre6/include/asm-sparc/ioctl.h	Wed Mar 26 16:11:40 2003
@@ -2,23 +2,35 @@
 #ifndef _SPARC_IOCTL_H
 #define _SPARC_IOCTL_H
 
+/*
+ * Our DIR and SIZE overlap in order to simulteneously provide
+ * a non-zero _IOC_NONE (for binary compatibility) and
+ * 14 bits of size as on i386. Here's the layout:
+ *
+ *   0xE0000000   DIR
+ *   0x80000000     DIR = WRITE
+ *   0x40000000     DIR = READ
+ *   0x20000000     DIR = NONE
+ *   0x3FFF0000   SIZE (overlaps NONE bit)
+ *   0x0000FF00   TYPE
+ *   0x000000FF   NR (CMD)
+ */
+
 #define _IOC_NRBITS      8
 #define _IOC_TYPEBITS    8
-#define _IOC_SIZEBITS    8
-#define _IOC_RESVBITS    5
+#define _IOC_SIZEBITS   13	/* Actually 14, see below. */
 #define _IOC_DIRBITS     3
 
 #define _IOC_NRMASK      ((1 << _IOC_NRBITS)-1)
 #define _IOC_TYPEMASK    ((1 << _IOC_TYPEBITS)-1)
-#define _IOC_RESVMASK    ((1 << _IOC_RESVBITS)-1)
 #define _IOC_SIZEMASK    ((1 << _IOC_SIZEBITS)-1)
+#define _IOC_XSIZEMASK   ((1 << (_IOC_SIZEBITS+1))-1)
 #define _IOC_DIRMASK     ((1 << _IOC_DIRBITS)-1)
 
 #define _IOC_NRSHIFT     0
 #define _IOC_TYPESHIFT   (_IOC_NRSHIFT + _IOC_NRBITS)
 #define _IOC_SIZESHIFT   (_IOC_TYPESHIFT + _IOC_TYPEBITS)
-#define _IOC_RESVSHIFT   (_IOC_SIZESHIFT + _IOC_SIZEBITS)
-#define _IOC_DIRSHIFT    (_IOC_RESVSHIFT + _IOC_RESVBITS)
+#define _IOC_DIRSHIFT    (_IOC_SIZESHIFT + _IOC_SIZEBITS)
 
 #define _IOC_NONE        1U
 #define _IOC_READ        2U
@@ -35,17 +47,20 @@
 #define _IOW(type,nr,size)  _IOC(_IOC_WRITE,(type),(nr),sizeof(size))
 #define _IOWR(type,nr,size) _IOC(_IOC_READ|_IOC_WRITE,(type),(nr),sizeof(size))
 
-#define _IOC_DIR(nr)        (((nr) >> _IOC_DIRSHIFT) & _IOC_DIRMASK)
+/* Used to decode ioctl numbers in drivers despite the leading underscore... */
+#define _IOC_DIR(nr)    \
+ ( (((((nr) >> _IOC_DIRSHIFT) & _IOC_DIRMASK) & (_IOC_WRITE|_IOC_READ)) != 0)?   \
+                            (((nr) >> _IOC_DIRSHIFT) & (_IOC_WRITE|_IOC_READ)):  \
+                            (((nr) >> _IOC_DIRSHIFT) & _IOC_DIRMASK) )
 #define _IOC_TYPE(nr)       (((nr) >> _IOC_TYPESHIFT) & _IOC_TYPEMASK)
 #define _IOC_NR(nr)         (((nr) >> _IOC_NRSHIFT) & _IOC_NRMASK)
-#define _IOC_SIZE(nr)       (((nr) >> _IOC_SIZESHIFT) & _IOC_SIZEMASK)
-
-/* ...and for the PCMCIA... */
+#define _IOC_SIZE(nr)       (((nr) >> _IOC_SIZESHIFT) & _IOC_XSIZEMASK)
 
+/* ...and for the PCMCIA and sound. */
 #define IOC_IN          (_IOC_WRITE << _IOC_DIRSHIFT)
 #define IOC_OUT         (_IOC_READ << _IOC_DIRSHIFT)
 #define IOC_INOUT       ((_IOC_WRITE|_IOC_READ) << _IOC_DIRSHIFT)
-#define IOCSIZE_MASK    (_IOC_SIZEMASK << _IOC_SIZESHIFT)
+#define IOCSIZE_MASK    (_IOC_XSIZEMASK << _IOC_SIZESHIFT)
 #define IOCSIZE_SHIFT   (_IOC_SIZESHIFT)
 
 #endif /* !(_SPARC_IOCTL_H) */
diff -urN linux-2.4.21-pre5/include/asm-sparc/processor.h linux-2.4.21-pre6/include/asm-sparc/processor.h
--- linux-2.4.21-pre5/include/asm-sparc/processor.h	Wed Oct 10 23:42:47 2001
+++ linux-2.4.21-pre6/include/asm-sparc/processor.h	Wed Mar 26 16:11:40 2003
@@ -146,7 +146,7 @@
 
 /* Free all resources held by a thread. */
 #define release_thread(tsk)		do { } while(0)
-extern pid_t kernel_thread(int (*fn)(void *), void * arg, unsigned long flags);
+extern pid_t arch_kernel_thread(int (*fn)(void *), void * arg, unsigned long flags);
 
 
 #define copy_segments(tsk, mm)		do { } while (0)
diff -urN linux-2.4.21-pre5/include/asm-sparc/unistd.h linux-2.4.21-pre6/include/asm-sparc/unistd.h
--- linux-2.4.21-pre5/include/asm-sparc/unistd.h	Fri Aug  2 17:39:45 2002
+++ linux-2.4.21-pre6/include/asm-sparc/unistd.h	Wed Mar 26 16:11:40 2003
@@ -184,24 +184,24 @@
 /* #define __NR_exportfs        166    SunOS Specific                              */
 #define __NR_mount              167 /* Common                                      */
 #define __NR_ustat              168 /* Common                                      */
-/* #define __NR_semsys          169    SunOS Specific                              */
-/* #define __NR_msgsys          170    SunOS Specific                              */
-/* #define __NR_shmsys          171    SunOS Specific                              */
-/* #define __NR_auditsys        172    SunOS Specific                              */
-/* #define __NR_rfssys          173    SunOS Specific                              */
+#define __NR_setxattr           169 /* SunOS: semsys                               */
+#define __NR_lsetxattr          170 /* SunOS: msgsys                               */
+#define __NR_fsetxattr          171 /* SunOS: shmsys                               */
+#define __NR_getxattr           172 /* SunOS: auditsys                             */
+#define __NR_lgetxattr          173 /* SunOS: rfssys                               */
 #define __NR_getdents           174 /* Common                                      */
 #define __NR_setsid             175 /* Common                                      */
 #define __NR_fchdir             176 /* Common                                      */
-/* #define __NR_fchroot         177    SunOS Specific                              */
-/* #define __NR_vpixsys         178    SunOS Specific                              */
-/* #define __NR_aioread         179    SunOS Specific                              */
-/* #define __NR_aiowrite        180    SunOS Specific                              */
-/* #define __NR_aiowait         181    SunOS Specific                              */
-/* #define __NR_aiocancel       182    SunOS Specific                              */
+#define __NR_fgetxattr          177 /* SunOS: fchroot                              */
+#define __NR_listxattr          178 /* SunOS: vpixsys                              */
+#define __NR_llistxattr         179 /* SunOS: aioread                              */
+#define __NR_flistxattr         180 /* SunOS: aiowrite                             */
+#define __NR_removexattr        181 /* SunOS: aiowait                              */
+#define __NR_lremovexattr       182 /* SunOS: aiocancel                            */
 #define __NR_sigpending         183 /* Common                                      */
 #define __NR_query_module	184 /* Linux Specific				   */
 #define __NR_setpgid            185 /* Common                                      */
-/* #define __NR_pathconf        186    SunOS Specific                              */
+#define __NR_fremovexattr       186 /* SunOS: pathconf                             */
 #define __NR_tkill              187 /* SunOS: fpathconf                            */
 /* #define __NR_sysconf         188    SunOS Specific                              */
 #define __NR_uname              189 /* Linux Specific                              */
diff -urN linux-2.4.21-pre5/include/asm-sparc64/ioctl.h linux-2.4.21-pre6/include/asm-sparc64/ioctl.h
--- linux-2.4.21-pre5/include/asm-sparc64/ioctl.h	Tue Oct 27 09:52:21 1998
+++ linux-2.4.21-pre6/include/asm-sparc64/ioctl.h	Wed Mar 26 16:11:40 2003
@@ -2,23 +2,35 @@
 #ifndef _SPARC64_IOCTL_H
 #define _SPARC64_IOCTL_H
 
+/*
+ * Our DIR and SIZE overlap in order to simulteneously provide
+ * a non-zero _IOC_NONE (for binary compatibility) and
+ * 14 bits of size as on i386. Here's the layout:
+ *
+ *   0xE0000000   DIR
+ *   0x80000000     DIR = WRITE
+ *   0x40000000     DIR = READ
+ *   0x20000000     DIR = NONE
+ *   0x3FFF0000   SIZE (overlaps NONE bit)
+ *   0x0000FF00   TYPE
+ *   0x000000FF   NR (CMD)
+ */
+
 #define _IOC_NRBITS      8
 #define _IOC_TYPEBITS    8
-#define _IOC_SIZEBITS    8
-#define _IOC_RESVBITS    5
+#define _IOC_SIZEBITS   13	/* Actually 14, see below. */
 #define _IOC_DIRBITS     3
 
 #define _IOC_NRMASK      ((1 << _IOC_NRBITS)-1)
 #define _IOC_TYPEMASK    ((1 << _IOC_TYPEBITS)-1)
-#define _IOC_RESVMASK    ((1 << _IOC_RESVBITS)-1)
 #define _IOC_SIZEMASK    ((1 << _IOC_SIZEBITS)-1)
+#define _IOC_XSIZEMASK   ((1 << (_IOC_SIZEBITS+1))-1)
 #define _IOC_DIRMASK     ((1 << _IOC_DIRBITS)-1)
 
 #define _IOC_NRSHIFT     0
 #define _IOC_TYPESHIFT   (_IOC_NRSHIFT + _IOC_NRBITS)
 #define _IOC_SIZESHIFT   (_IOC_TYPESHIFT + _IOC_TYPEBITS)
-#define _IOC_RESVSHIFT   (_IOC_SIZESHIFT + _IOC_SIZEBITS)
-#define _IOC_DIRSHIFT    (_IOC_RESVSHIFT + _IOC_RESVBITS)
+#define _IOC_DIRSHIFT    (_IOC_SIZESHIFT + _IOC_SIZEBITS)
 
 #define _IOC_NONE        1U
 #define _IOC_READ        2U
@@ -35,17 +47,20 @@
 #define _IOW(type,nr,size)  _IOC(_IOC_WRITE,(type),(nr),sizeof(size))
 #define _IOWR(type,nr,size) _IOC(_IOC_READ|_IOC_WRITE,(type),(nr),sizeof(size))
 
-#define _IOC_DIR(nr)        (((nr) >> _IOC_DIRSHIFT) & _IOC_DIRMASK)
+/* Used to decode ioctl numbers in drivers despite the leading underscore... */
+#define _IOC_DIR(nr)    \
+ ( (((((nr) >> _IOC_DIRSHIFT) & _IOC_DIRMASK) & (_IOC_WRITE|_IOC_READ)) != 0)?   \
+                            (((nr) >> _IOC_DIRSHIFT) & (_IOC_WRITE|_IOC_READ)):  \
+                            (((nr) >> _IOC_DIRSHIFT) & _IOC_DIRMASK) )
 #define _IOC_TYPE(nr)       (((nr) >> _IOC_TYPESHIFT) & _IOC_TYPEMASK)
 #define _IOC_NR(nr)         (((nr) >> _IOC_NRSHIFT) & _IOC_NRMASK)
-#define _IOC_SIZE(nr)       (((nr) >> _IOC_SIZESHIFT) & _IOC_SIZEMASK)
-
-/* ...and for the PCMCIA... */
+#define _IOC_SIZE(nr)       (((nr) >> _IOC_SIZESHIFT) & _IOC_XSIZEMASK)
 
+/* ...and for the PCMCIA and sound. */
 #define IOC_IN          (_IOC_WRITE << _IOC_DIRSHIFT)
 #define IOC_OUT         (_IOC_READ << _IOC_DIRSHIFT)
 #define IOC_INOUT       ((_IOC_WRITE|_IOC_READ) << _IOC_DIRSHIFT)
-#define IOCSIZE_MASK    (_IOC_SIZEMASK << _IOC_SIZESHIFT)
+#define IOCSIZE_MASK    (_IOC_XSIZEMASK << _IOC_SIZESHIFT)
 #define IOCSIZE_SHIFT   (_IOC_SIZESHIFT)
 
 #endif /* !(_SPARC64_IOCTL_H) */
diff -urN linux-2.4.21-pre5/include/asm-sparc64/irq.h linux-2.4.21-pre6/include/asm-sparc64/irq.h
--- linux-2.4.21-pre5/include/asm-sparc64/irq.h	Wed Mar 26 16:11:29 2003
+++ linux-2.4.21-pre6/include/asm-sparc64/irq.h	Wed Mar 26 16:11:40 2003
@@ -114,8 +114,6 @@
 extern void disable_irq(unsigned int);
 #define disable_irq_nosync disable_irq
 extern void enable_irq(unsigned int);
-extern void init_timers(void (*lvl10_irq)(int, void *, struct pt_regs *),
-			unsigned long *);
 extern unsigned int build_irq(int pil, int inofixup, unsigned long iclr, unsigned long imap);
 extern unsigned int sbus_build_irq(void *sbus, unsigned int ino);
 extern unsigned int psycho_build_irq(void *psycho, int imap_off, int ino, int need_dma_sync);
diff -urN linux-2.4.21-pre5/include/asm-sparc64/processor.h linux-2.4.21-pre6/include/asm-sparc64/processor.h
--- linux-2.4.21-pre5/include/asm-sparc64/processor.h	Fri Aug  2 17:39:45 2002
+++ linux-2.4.21-pre6/include/asm-sparc64/processor.h	Wed Mar 26 16:11:40 2003
@@ -270,7 +270,7 @@
 /* Free all resources held by a thread. */
 #define release_thread(tsk)		do { } while(0)
 
-extern pid_t kernel_thread(int (*fn)(void *), void * arg, unsigned long flags);
+extern pid_t arch_kernel_thread(int (*fn)(void *), void * arg, unsigned long flags);
 
 #define copy_segments(tsk, mm)		do { } while (0)
 #define release_segments(mm)		do { } while (0)
diff -urN linux-2.4.21-pre5/include/asm-sparc64/spitfire.h linux-2.4.21-pre6/include/asm-sparc64/spitfire.h
--- linux-2.4.21-pre5/include/asm-sparc64/spitfire.h	Thu Nov 28 15:53:15 2002
+++ linux-2.4.21-pre6/include/asm-sparc64/spitfire.h	Wed Mar 26 16:11:40 2003
@@ -45,8 +45,6 @@
 
 extern enum ultra_tlb_layout tlb_type;
 
-#define SPARC64_USE_STICK	(tlb_type != spitfire)
-
 #define CHEETAH_HIGHEST_LOCKED_TLBENT	(16 - 1)
 
 #define L1DCACHE_SIZE		0x4000
diff -urN linux-2.4.21-pre5/include/asm-sparc64/timer.h linux-2.4.21-pre6/include/asm-sparc64/timer.h
--- linux-2.4.21-pre5/include/asm-sparc64/timer.h	Mon May 22 09:50:55 2000
+++ linux-2.4.21-pre6/include/asm-sparc64/timer.h	Wed Mar 26 16:11:40 2003
@@ -50,6 +50,17 @@
  */
 #define SUN5_HZ_TO_LIMIT(__hz)  (1000000/(__hz))
 
+struct sparc64_tick_ops {
+	void (*init_tick)(unsigned long);
+	unsigned long (*get_tick)(void);
+	unsigned long (*get_compare)(void);
+	unsigned long (*add_tick)(unsigned long, unsigned long);
+	unsigned long (*add_compare)(unsigned long);
+	unsigned long softint_mask;
+};
+
+extern struct sparc64_tick_ops *tick_ops;
+
 #ifdef CONFIG_SMP
 extern unsigned long timer_tick_offset;
 extern void timer_tick_interrupt(struct pt_regs *);
diff -urN linux-2.4.21-pre5/include/asm-sparc64/unistd.h linux-2.4.21-pre6/include/asm-sparc64/unistd.h
--- linux-2.4.21-pre5/include/asm-sparc64/unistd.h	Fri Aug  2 17:39:45 2002
+++ linux-2.4.21-pre6/include/asm-sparc64/unistd.h	Wed Mar 26 16:11:40 2003
@@ -184,24 +184,24 @@
 /* #define __NR_exportfs        166    SunOS Specific                              */
 #define __NR_mount              167 /* Common                                      */
 #define __NR_ustat              168 /* Common                                      */
-/* #define __NR_semsys          169    SunOS Specific                              */
-/* #define __NR_msgsys          170    SunOS Specific                              */
-/* #define __NR_shmsys          171    SunOS Specific                              */
-/* #define __NR_auditsys        172    SunOS Specific                              */
-/* #define __NR_rfssys          173    SunOS Specific                              */
+#define __NR_setxattr           169 /* SunOS: semsys                               */
+#define __NR_lsetxattr          170 /* SunOS: msgsys                               */
+#define __NR_fsetxattr          171 /* SunOS: shmsys                               */
+#define __NR_getxattr           172 /* SunOS: auditsys                             */
+#define __NR_lgetxattr          173 /* SunOS: rfssys                               */
 #define __NR_getdents           174 /* Common                                      */
 #define __NR_setsid             175 /* Common                                      */
 #define __NR_fchdir             176 /* Common                                      */
-/* #define __NR_fchroot         177    SunOS Specific                              */
-/* #define __NR_vpixsys         178    SunOS Specific                              */
-/* #define __NR_aioread         179    SunOS Specific                              */
-/* #define __NR_aiowrite        180    SunOS Specific                              */
-/* #define __NR_aiowait         181    SunOS Specific                              */
-/* #define __NR_aiocancel       182    SunOS Specific                              */
+#define __NR_fgetxattr          177 /* SunOS: fchroot                              */
+#define __NR_listxattr          178 /* SunOS: vpixsys                              */
+#define __NR_llistxattr         179 /* SunOS: aioread                              */
+#define __NR_flistxattr         180 /* SunOS: aiowrite                             */
+#define __NR_removexattr        181 /* SunOS: aiowait                              */
+#define __NR_lremovexattr       182 /* SunOS: aiocancel                            */
 #define __NR_sigpending         183 /* Common                                      */
 #define __NR_query_module	184 /* Linux Specific				   */
 #define __NR_setpgid            185 /* Common                                      */
-/* #define __NR_pathconf        186    SunOS Specific                              */
+#define __NR_fremovexattr       186 /* SunOS: pathconf                             */
 #define __NR_tkill              187 /* SunOS: fpathconf                            */
 /* #define __NR_sysconf         188    SunOS Specific                              */
 #define __NR_uname              189 /* Linux Specific                              */
diff -urN linux-2.4.21-pre5/include/asm-x86_64/apic.h linux-2.4.21-pre6/include/asm-x86_64/apic.h
--- linux-2.4.21-pre5/include/asm-x86_64/apic.h	Thu Nov 28 15:53:15 2002
+++ linux-2.4.21-pre6/include/asm-x86_64/apic.h	Wed Mar 26 16:11:40 2003
@@ -78,7 +78,7 @@
 extern void smp_local_timer_interrupt (struct pt_regs * regs);
 extern void setup_APIC_clocks (void);
 extern void setup_apic_nmi_watchdog (void);
-extern inline void nmi_watchdog_tick (struct pt_regs * regs);
+extern inline void nmi_watchdog_tick (struct pt_regs * regs, unsigned reason);
 extern int APIC_init_uniprocessor (void);
 extern void disable_APIC_timer(void);
 extern void enable_APIC_timer(void);
diff -urN linux-2.4.21-pre5/include/asm-x86_64/checksum.h linux-2.4.21-pre6/include/asm-x86_64/checksum.h
--- linux-2.4.21-pre5/include/asm-x86_64/checksum.h	Thu Nov 28 15:53:15 2002
+++ linux-2.4.21-pre6/include/asm-x86_64/checksum.h	Wed Mar 26 16:11:40 2003
@@ -125,7 +125,7 @@
  * Before filling it in it needs to be csum_fold()'ed.
  * buff should be aligned to a 64bit boundary if possible.
  */ 
-extern unsigned int csum_partial(const unsigned char *buff, int len, unsigned int sum);
+extern unsigned int csum_partial(const unsigned char *buff, unsigned len, unsigned int sum);
 
 #define  _HAVE_ARCH_COPY_AND_CSUM_FROM_USER 1
 #define HAVE_CSUM_COPY_USER 1
@@ -179,4 +179,14 @@
 csum_ipv6_magic(struct in6_addr *saddr, struct in6_addr *daddr,
 		__u32 len, unsigned short proto, unsigned int sum);
 
+static inline unsigned add32_with_carry(unsigned a, unsigned b)
+{
+	asm("addl %2,%0\n\t"
+	    "adcl $0,%0" 
+	    : "=r" (a) 
+	    : "0" (a), "r" (b));
+	return a;
+}
+
 #endif
+
diff -urN linux-2.4.21-pre5/include/asm-x86_64/debugreg.h linux-2.4.21-pre6/include/asm-x86_64/debugreg.h
--- linux-2.4.21-pre5/include/asm-x86_64/debugreg.h	Thu Nov 28 15:53:15 2002
+++ linux-2.4.21-pre6/include/asm-x86_64/debugreg.h	Wed Mar 26 16:11:40 2003
@@ -58,7 +58,7 @@
    We can slow the instruction pipeline for instructions coming via the
    gdt or the ldt if we want to.  I am not sure why this is an advantage */
 
-#define DR_CONTROL_RESERVED (0xFC00) /* Reserved by Intel */
+#define DR_CONTROL_RESERVED (0xFFFFFFFF0000FC00UL) /* Reserved */
 #define DR_LOCAL_SLOWDOWN (0x100)   /* Local slow the pipeline */
 #define DR_GLOBAL_SLOWDOWN (0x200)  /* Global slow the pipeline */
 
diff -urN linux-2.4.21-pre5/include/asm-x86_64/delay.h linux-2.4.21-pre6/include/asm-x86_64/delay.h
--- linux-2.4.21-pre5/include/asm-x86_64/delay.h	Thu Nov 28 15:53:15 2002
+++ linux-2.4.21-pre6/include/asm-x86_64/delay.h	Wed Mar 26 16:11:40 2003
@@ -8,8 +8,10 @@
  */
  
 extern void __bad_udelay(void);
+extern void __bad_ndelay(void);
 
 extern void __udelay(unsigned long usecs);
+extern void __ndelay(unsigned long usecs);
 extern void __const_udelay(unsigned long usecs);
 extern void __delay(unsigned long loops);
 
@@ -17,4 +19,8 @@
 	((n) > 20000 ? __bad_udelay() : __const_udelay((n) * 0x10c6ul)) : \
 	__udelay(n))
 
+#define ndelay(n) (__builtin_constant_p(n) ? \
+       ((n) > 20000 ? __bad_ndelay() : __const_udelay((n) * 5ul)) : \
+       __ndelay(n))
+
 #endif /* defined(_X8664_DELAY_H) */
diff -urN linux-2.4.21-pre5/include/asm-x86_64/floppy.h linux-2.4.21-pre6/include/asm-x86_64/floppy.h
--- linux-2.4.21-pre5/include/asm-x86_64/floppy.h	Thu Nov 28 15:53:15 2002
+++ linux-2.4.21-pre6/include/asm-x86_64/floppy.h	Wed Mar 26 16:11:40 2003
@@ -22,7 +22,7 @@
  * floppy accesses go through the track buffer.
  */
 #define _CROSS_64KB(a,s,vdma) \
-(!vdma && ((unsigned long)(a)/K_64 != ((unsigned long)(a) + (s) - 1) / K_64))
+(!(vdma) && ((unsigned long)(a)/K_64 != ((unsigned long)(a) + (s) - 1) / K_64))
 
 #define CROSS_64KB(a,s) _CROSS_64KB(a,s,use_virtual_dma & 1)
 
diff -urN linux-2.4.21-pre5/include/asm-x86_64/hw_irq.h linux-2.4.21-pre6/include/asm-x86_64/hw_irq.h
--- linux-2.4.21-pre5/include/asm-x86_64/hw_irq.h	Wed Mar 26 16:11:29 2003
+++ linux-2.4.21-pre6/include/asm-x86_64/hw_irq.h	Wed Mar 26 16:11:40 2003
@@ -14,7 +14,7 @@
  *
  *	hacked by Andi Kleen for x86-64.
  * 
- *  $Id: hw_irq.h,v 1.30 2002/10/02 15:44:22 ak Exp $
+ *  $Id: hw_irq.h,v 1.31 2003/02/18 18:35:55 ak Exp $
  */
 
 #include <linux/config.h>
@@ -31,7 +31,6 @@
 #define FIRST_EXTERNAL_VECTOR	0x20
 
 #define IA32_SYSCALL_VECTOR	0x80
-#define KDBENTER_VECTOR		0x81
 
 
 /*
@@ -51,9 +50,8 @@
 #define ERROR_APIC_VECTOR	0xfe
 #define INVALIDATE_TLB_VECTOR	0xfd
 #define RESCHEDULE_VECTOR	0xfc
-#define KDB_VECTOR		0xfa
+/* 0xfa free */
 #define CALL_FUNCTION_VECTOR	0xfb
-#define KDB_VECTOR              0xfa
 
 /*
  * Local APIC timer IRQ vector is on a different priority level,
diff -urN linux-2.4.21-pre5/include/asm-x86_64/i387.h linux-2.4.21-pre6/include/asm-x86_64/i387.h
--- linux-2.4.21-pre5/include/asm-x86_64/i387.h	Thu Nov 28 15:53:15 2002
+++ linux-2.4.21-pre6/include/asm-x86_64/i387.h	Wed Mar 26 16:11:40 2003
@@ -17,8 +17,8 @@
 #include <asm/sigcontext.h>
 #include <asm/user.h>
 
-extern void init_fpu(void);
-int save_i387( struct _fpstate *buf);
+extern void init_fpu(struct task_struct *child);
+extern int save_i387(struct _fpstate *buf);
 
 /*
  * FPU lazy state save handling...
@@ -110,7 +110,7 @@
 {
 	struct task_struct *tsk = current;
 	if (tsk->flags & PF_USEDFPU) {
-		asm volatile("fxsave %0 ; fnclex"
+		asm volatile("rex64 ; fxsave %0 ; fnclex"
 			      : "=m" (tsk->thread.i387.fxsave));
 		tsk->flags &= ~PF_USEDFPU;
 		return;
@@ -134,18 +134,4 @@
 	return restore_fpu_checking((struct i387_fxsave_struct *)buf);
 }
 
-
-static inline void empty_fpu(struct task_struct *child)
-{
-	if (!child->used_math) {
-		/* Simulate an empty FPU. */
-		memset(&child->thread.i387.fxsave,0,sizeof(struct i387_fxsave_struct));
-		child->thread.i387.fxsave.cwd = 0x037f; 
-		child->thread.i387.fxsave.swd = 0;
-		child->thread.i387.fxsave.twd = 0; 
-		child->thread.i387.fxsave.mxcsr = 0x1f80;
-	}
-	child->used_math = 1; 
-}		
-
 #endif /* __ASM_X86_64_I387_H */
diff -urN linux-2.4.21-pre5/include/asm-x86_64/kdebug.h linux-2.4.21-pre6/include/asm-x86_64/kdebug.h
--- linux-2.4.21-pre5/include/asm-x86_64/kdebug.h	Thu Nov 28 15:53:15 2002
+++ linux-2.4.21-pre6/include/asm-x86_64/kdebug.h	Wed Mar 26 16:11:40 2003
@@ -9,8 +9,12 @@
 	struct pt_regs *regs;
 	const char *str;
 	long err; 
+	int trapnr;
+	int signr;
 }; 
 
+/* Note - you should never unregister because that can race with NMIs.
+   If you really want to do it use RCU infrastructure. */
 extern struct notifier_block *die_chain;
 
 /* Grossly misnamed. */
@@ -21,18 +25,21 @@
 	DIE_PANIC,
 	DIE_NMI,
 	DIE_DIE,
+	DIE_NMIWATCHDOG,
+	DIE_KERNELDEBUG,
+	DIE_TRAP,
+	DIE_GPF,
 	DIE_CALL,
-	DIE_CPUINIT,	/* not really a die, but .. */
-	DIE_TRAPINIT,	/* not really a die, but .. */
-	DIE_STOP, 
 }; 
 
-static inline int notify_die(enum die_val val,char *str,struct pt_regs *regs,long err)
+static inline int notify_die(enum die_val val,char *str,struct pt_regs *regs,long err,int trap,int sig)
 { 
-	struct die_args args = { regs: regs, str: str, err: err }; 
+	struct die_args args = { regs: regs, str: str, err: err, trapnr: trap,
+	signr: sig}; 
 	return notifier_call_chain(&die_chain, val, &args); 
 } 
 
+
 extern int printk_address(unsigned long address);
 extern void die(const char *,struct pt_regs *,long);
 extern void show_stack(unsigned long* esp);
diff -urN linux-2.4.21-pre5/include/asm-x86_64/pgtable.h linux-2.4.21-pre6/include/asm-x86_64/pgtable.h
--- linux-2.4.21-pre5/include/asm-x86_64/pgtable.h	Wed Mar 26 16:11:29 2003
+++ linux-2.4.21-pre6/include/asm-x86_64/pgtable.h	Wed Mar 26 16:11:40 2003
@@ -405,7 +405,7 @@
 #define pml4_offset_k(address) ((pml4_t *)read_pda(level4_pgt) + pml4_index(address))
 #define level3_offset_k(dir, address) ((pgd_t *) pml4_page(*(dir)) + pgd_index(address))
 #define mk_kernel_pml4(address,prot) ((pml4_t){(address) | pgprot_val(prot)})
-
+#define pml4_present(pml4) (pml4_val(pml4) & _PAGE_PRESENT)
 
 /*
  * x86 doesn't have any external MMU info: the kernel page
diff -urN linux-2.4.21-pre5/include/asm-x86_64/processor.h linux-2.4.21-pre6/include/asm-x86_64/processor.h
--- linux-2.4.21-pre5/include/asm-x86_64/processor.h	Wed Mar 26 16:11:29 2003
+++ linux-2.4.21-pre6/include/asm-x86_64/processor.h	Wed Mar 26 16:11:40 2003
@@ -51,6 +51,8 @@
 				    call  */
 	int	x86_clflush_size;
 	int	x86_tlbsize;	/* number of 4K pages in DTLB/ITLB combined(in pages)*/
+        __u8    x86_virt_bits, x86_phys_bits;
+        __u32   x86_power; 
 	unsigned long loops_per_jiffy;
 } ____cacheline_aligned;
 
@@ -302,7 +304,7 @@
 	u16 reserved5;
 	u16 io_map_base;
 	u32 io_bitmap[IO_BITMAP_SIZE];
-} __attribute__((packed));
+} __attribute__((packed)) ____cacheline_aligned;
 
 struct thread_struct {
 	unsigned long	rsp0;
diff -urN linux-2.4.21-pre5/include/asm-x86_64/proto.h linux-2.4.21-pre6/include/asm-x86_64/proto.h
--- linux-2.4.21-pre5/include/asm-x86_64/proto.h	Wed Mar 26 16:11:29 2003
+++ linux-2.4.21-pre6/include/asm-x86_64/proto.h	Wed Mar 26 16:11:40 2003
@@ -37,6 +37,7 @@
 extern unsigned long start_pfn, end_pfn, end_pfn_map; 
 
 extern void show_stack(unsigned long * rsp);
+extern void show_trace(unsigned long *stack);
 
 #define round_up(x,y) (((x) + (y) - 1) & ~((y)-1))
 #define round_down(x,y) ((x) & ~((y)-1))
diff -urN linux-2.4.21-pre5/include/asm-x86_64/system.h linux-2.4.21-pre6/include/asm-x86_64/system.h
--- linux-2.4.21-pre5/include/asm-x86_64/system.h	Wed Mar 26 16:11:29 2003
+++ linux-2.4.21-pre6/include/asm-x86_64/system.h	Wed Mar 26 16:11:40 2003
@@ -21,6 +21,23 @@
 #define __PUSH(x) "pushq %%" __STR(x) "\n\t"
 #define __POP(x)  "popq  %%" __STR(x) "\n\t"
 
+struct save_context_frame { 
+        unsigned long rbp; 
+        unsigned long rbx;
+	unsigned long r11;
+	unsigned long r10;
+	unsigned long r9;
+	unsigned long r8;
+	unsigned long rcx;
+	unsigned long rdx;
+	unsigned long r15;
+	unsigned long r14;
+	unsigned long r13;
+	unsigned long r12;
+	unsigned long rdi;
+	unsigned long rsi;
+};
+
 /* frame pointer must be last for get_wchan */
 #define SAVE_CONTEXT \
 	__PUSH(rsi) __PUSH(rdi) \
@@ -37,11 +54,12 @@
 	asm volatile(SAVE_CONTEXT					\
 		     "movq %%rsp,%0\n\t"	/* save RSP */		\
 		     "movq %3,%%rsp\n\t"	/* restore RSP */	\
-		     "leaq 1f(%%rip),%%rax\n\t"				\
+		     "leaq thread_return(%%rip),%%rax\n\t"		\
 		     "movq %%rax,%1\n\t"	/* save RIP */		\
 		     "pushq %4\n\t"		/* setup new RIP */	\
 		     "jmp __switch_to\n\t"				\
-		     "1:\n\t"						\
+		     ".globl thread_return\n"				\
+		     "thread_return:\n\t"				\
 		     RESTORE_CONTEXT					\
 		     :"=m" (prev->thread.rsp),"=m" (prev->thread.rip), "=a" (l) \
 		     :"m" (next->thread.rsp),"m" (next->thread.rip),	\
diff -urN linux-2.4.21-pre5/include/asm-x86_64/uaccess.h linux-2.4.21-pre6/include/asm-x86_64/uaccess.h
--- linux-2.4.21-pre5/include/asm-x86_64/uaccess.h	Thu Nov 28 15:53:15 2002
+++ linux-2.4.21-pre6/include/asm-x86_64/uaccess.h	Wed Mar 26 16:11:40 2003
@@ -71,9 +71,6 @@
 	unsigned long insn, fixup;
 };
 
-/* Returns 0 if exception not found and fixup otherwise.  */
-extern unsigned long search_exception_table(unsigned long);
-
 
 /*
  * These are the main single-value transfer routines.  They automatically
@@ -82,7 +79,7 @@
  * This gets kind of ugly. We want to return _two_ values in "get_user()"
  * and yet we don't want to do any pointers, because that is too much
  * of a performance impact. Thus we have a few rather ugly macros here,
- * and hide all the uglyness from the user.
+ * and hide all the ugliness from the user.
  *
  * The "__xxx" versions of the user access functions are versions that
  * do not verify the address space, that must have been done previously
@@ -103,7 +100,8 @@
 
 /* Careful: we have to cast the result to the type of the pointer for sign reasons */
 #define get_user(x,ptr)							\
-({	long __ret_gu,__val_gu;						\
+({	long __val_gu;							\
+	int __ret_gu; 							\
 	switch(sizeof (*(ptr))) {					\
 	case 1:  __get_user_x(1,__ret_gu,__val_gu,ptr); break;		\
 	case 2:  __get_user_x(2,__ret_gu,__val_gu,ptr); break;		\
@@ -138,7 +136,7 @@
 
 #define __put_user_nocheck(x,ptr,size)			\
 ({							\
-	long __pu_err;					\
+	int __pu_err;					\
 	__put_user_size((x),(ptr),(size),__pu_err);	\
 	__pu_err;					\
 })
@@ -146,7 +144,7 @@
 
 #define __put_user_check(x,ptr,size)			\
 ({							\
-	long __pu_err = -EFAULT;			\
+	int __pu_err = -EFAULT;				\
 	__typeof__(*(ptr)) *__pu_addr = (ptr);		\
 	if (access_ok(VERIFY_WRITE,__pu_addr,size))	\
 		__put_user_size((x),__pu_addr,(size),__pu_err);	\
@@ -157,10 +155,10 @@
 do {									\
 	retval = 0;							\
 	switch (size) {							\
-	  case 1: __put_user_asm(x,ptr,retval,"b","b","iq"); break;	\
-	  case 2: __put_user_asm(x,ptr,retval,"w","w","ir"); break;	\
-	  case 4: __put_user_asm(x,ptr,retval,"l","k","ir"); break;	\
-	  case 8: __put_user_asm(x,ptr,retval,"q","","ir"); break;	\
+	  case 1: __put_user_asm(x,ptr,retval,"b","b","iq",-EFAULT); break;\
+	  case 2: __put_user_asm(x,ptr,retval,"w","w","ir",-EFAULT); break;\
+	  case 4: __put_user_asm(x,ptr,retval,"l","k","ir",-EFAULT); break;\
+	  case 8: __put_user_asm(x,ptr,retval,"q","","ir",-EFAULT); break;\
 	  default: __put_user_bad();					\
 	}								\
 } while (0)
@@ -174,12 +172,12 @@
  * we do not write to any memory gcc knows about, so there are no
  * aliasing issues.
  */
-#define __put_user_asm(x, addr, err, itype, rtype, ltype)	\
+#define __put_user_asm(x, addr, err, itype, rtype, ltype, errno)	\
 	__asm__ __volatile__(				\
 		"1:	mov"itype" %"rtype"1,%2\n"	\
 		"2:\n"							\
 		".section .fixup,\"ax\"\n"		\
-		"3:	movq %3,%0\n"				\
+		"3:	mov %3,%0\n"				\
 		"	jmp 2b\n"					\
 		".previous\n"					\
 		".section __ex_table,\"a\"\n"	\
@@ -187,32 +185,33 @@
 		"	.quad 1b,3b\n"				\
 		".previous"						\
 		: "=r"(err)						\
-		: ltype (x), "m"(__m(addr)), "i"(-EFAULT), "0"(err))
+		: ltype (x), "m"(__m(addr)), "i"(errno), "0"(err))
 
 
 #define __get_user_nocheck(x,ptr,size)				\
 ({								\
-	long __gu_err, __gu_val;				\
+	int __gu_err;						\
+	long __gu_val;						\
 	__get_user_size(__gu_val,(ptr),(size),__gu_err);	\
 	(x) = (__typeof__(*(ptr)))__gu_val;			\
 	__gu_err;						\
 })
 
-extern long __get_user_bad(void);
+extern int __get_user_bad(void);
 
 #define __get_user_size(x,ptr,size,retval)				\
 do {									\
 	retval = 0;							\
 	switch (size) {							\
-	  case 1: __get_user_asm(x,ptr,retval,"b","b","=q"); break;	\
-	  case 2: __get_user_asm(x,ptr,retval,"w","w","=r"); break;	\
-	  case 4: __get_user_asm(x,ptr,retval,"l","k","=r"); break;	\
-	  case 8: __get_user_asm(x,ptr,retval,"q","","=r"); break;	\
+	  case 1: __get_user_asm(x,ptr,retval,"b","b","=q",-EFAULT); break;\
+	  case 2: __get_user_asm(x,ptr,retval,"w","w","=r",-EFAULT); break;\
+	  case 4: __get_user_asm(x,ptr,retval,"l","k","=r",-EFAULT); break;\
+	  case 8: __get_user_asm(x,ptr,retval,"q","","=r",-EFAULT); break;\
 	  default: (x) = __get_user_bad();				\
 	}								\
 } while (0)
 
-#define __get_user_asm(x, addr, err, itype, rtype, ltype)	\
+#define __get_user_asm(x, addr, err, itype, rtype, ltype, errno)	\
 	__asm__ __volatile__(					\
 		"1:	mov"itype" %2,%"rtype"1\n"		\
 		"2:\n"						\
@@ -226,29 +225,85 @@
 		"	.quad 1b,3b\n"				\
 		".previous"					\
 		: "=r"(err), ltype (x)				\
-		: "m"(__m(addr)), "i"(-EFAULT), "0"(err))
+		: "m"(__m(addr)), "i"(errno), "0"(err))
 
 /*
  * Copy To/From Userspace
- * 
- * This relies on an optimized common worker function.
- * 
- * Could do special inline versions for small constant copies, but avoid this
- * for now. It's not clear it is worth it. 
  */
 
+/* Handles exceptions in both to and from, but doesn't do access_ok */
 extern unsigned long copy_user_generic(void *to, const void *from, unsigned len); 
 
 extern unsigned long copy_to_user(void *to, const void *from, unsigned len); 
 extern unsigned long copy_from_user(void *to, const void *from, unsigned len); 
-#define __copy_to_user copy_user_generic
-#define __copy_from_user copy_user_generic
+
+static inline int __copy_from_user(void *dst, const void *src, unsigned size) 
+{ 
+	if (!__builtin_constant_p(size))
+		return copy_user_generic(dst,src,size);
+	int ret = 0; 
+	switch (size) { 
+	case 1:__get_user_asm(*(u8*)dst,(u8 *)src,ret,"b","b","=q",1); 
+		return ret;
+	case 2:__get_user_asm(*(u16*)dst,(u16*)src,ret,"w","w","=r",2);
+		return ret;
+	case 4:__get_user_asm(*(u32*)dst,(u32*)src,ret,"l","k","=r",4);
+		return ret;
+	case 8:__get_user_asm(*(u64*)dst,(u64*)src,ret,"q","","=r",8);
+		return ret; 
+	case 10:
+	       	__get_user_asm(*(u64*)dst,(u64*)src,ret,"q","","=r",16);
+		if (ret) return ret;
+		__get_user_asm(*(u16*)(8+dst),(u16*)(8+src),ret,"w","w","=r",2);
+		return ret; 
+	case 16:
+		__get_user_asm(*(u64*)dst,(u64*)src,ret,"q","","=r",16);
+		if (ret) return ret;
+		__get_user_asm(*(u64*)(8+dst),(u64*)(8+src),ret,"q","","=r",8);
+		return ret; 
+	default:
+		return copy_user_generic(dst,src,size); 
+	}
+}	
+
+static inline int __copy_to_user(void *dst, const void *src, unsigned size) 
+{ 
+	if (!__builtin_constant_p(size))
+		return copy_user_generic(dst,src,size);
+	int ret = 0; 
+	switch (size) { 
+	case 1:__put_user_asm(*(u8*)src,(u8 *)dst,ret,"b","b","iq",1); 
+		return ret;
+	case 2:__put_user_asm(*(u16*)src,(u16*)dst,ret,"w","w","ir",2);
+		return ret;
+	case 4:__put_user_asm(*(u32*)src,(u32*)dst,ret,"l","k","ir",4);
+		return ret;
+	case 8:__put_user_asm(*(u64*)src,(u64*)dst,ret,"q","","ir",8);
+		return ret; 
+	case 10:
+		__put_user_asm(*(u64*)src,(u64*)dst,ret,"q","","ir",10);
+		if (ret) return ret;
+		asm("":::"memory");
+		__put_user_asm(4[(u16*)src],4+(u16*)dst,ret,"w","w","ir",2);
+		return ret; 
+	case 16:
+		__put_user_asm(*(u64*)src,(u64*)dst,ret,"q","","ir",16);
+		if (ret) return ret;
+		asm("":::"memory");
+		__put_user_asm(1[(u64*)src],1+(u64*)dst,ret,"q","","ir",8);
+		return ret; 
+	default:
+		return copy_user_generic(dst,src,size); 
+	}
+}	
 
 long strncpy_from_user(char *dst, const char *src, long count);
 long __strncpy_from_user(char *dst, const char *src, long count);
-#define strlen_user(str) strnlen_user(str, ~0UL >> 1)
 long strnlen_user(const char *str, long n);
+long strlen_user(const char *str);
 unsigned long clear_user(void *mem, unsigned long len);
 unsigned long __clear_user(void *mem, unsigned long len);
 
+extern unsigned long search_exception_table(unsigned long);
+
 #endif /* __X86_64_UACCESS_H */
diff -urN linux-2.4.21-pre5/include/linux/atmdev.h linux-2.4.21-pre6/include/linux/atmdev.h
--- linux-2.4.21-pre5/include/linux/atmdev.h	Fri Aug  2 17:39:45 2002
+++ linux-2.4.21-pre6/include/linux/atmdev.h	Wed Mar 26 16:11:40 2003
@@ -302,7 +302,6 @@
 	int (*send)(struct atm_vcc *vcc,struct sk_buff *skb);
 	void		*dev_data;	/* per-device data */
 	void		*proto_data;	/* per-protocol data */
-	struct timeval	timestamp;	/* AAL timestamps */
 	struct sk_buff_head recvq;	/* receive queue */
 	struct k_atm_aal_stats *stats;	/* pointer to AAL stats group */
 	wait_queue_head_t sleep;	/* if socket is busy */
diff -urN linux-2.4.21-pre5/include/linux/ext2_fs.h linux-2.4.21-pre6/include/linux/ext2_fs.h
--- linux-2.4.21-pre5/include/linux/ext2_fs.h	Thu Nov 22 11:46:52 2001
+++ linux-2.4.21-pre6/include/linux/ext2_fs.h	Wed Mar 26 16:11:40 2003
@@ -587,6 +587,7 @@
 extern int ext2_sync_inode (struct inode *);
 extern void ext2_discard_prealloc (struct inode *);
 extern void ext2_truncate (struct inode *);
+extern void ext2_set_inode_flags(struct inode *inode);
 
 /* ioctl.c */
 extern int ext2_ioctl (struct inode *, struct file *, unsigned int,
diff -urN linux-2.4.21-pre5/include/linux/ext3_fs.h linux-2.4.21-pre6/include/linux/ext3_fs.h
--- linux-2.4.21-pre5/include/linux/ext3_fs.h	Wed Mar 26 16:11:29 2003
+++ linux-2.4.21-pre6/include/linux/ext3_fs.h	Wed Mar 26 16:11:40 2003
@@ -642,6 +642,7 @@
 extern void ext3_dirty_inode(struct inode *);
 extern int ext3_change_inode_journal_flag(struct inode *, int);
 extern void ext3_truncate (struct inode *);
+extern void ext3_set_inode_flags(struct inode *);
 
 /* ioctl.c */
 extern int ext3_ioctl (struct inode *, struct file *, unsigned int,
diff -urN linux-2.4.21-pre5/include/linux/netfilter_ipv4/ip_conntrack.h linux-2.4.21-pre6/include/linux/netfilter_ipv4/ip_conntrack.h
--- linux-2.4.21-pre5/include/linux/netfilter_ipv4/ip_conntrack.h	Thu Nov 28 15:53:15 2002
+++ linux-2.4.21-pre6/include/linux/netfilter_ipv4/ip_conntrack.h	Wed Mar 26 16:11:40 2003
@@ -6,6 +6,7 @@
 
 #include <linux/config.h>
 #include <linux/netfilter_ipv4/ip_conntrack_tuple.h>
+#include <linux/bitops.h>
 #include <asm/atomic.h>
 
 enum ip_conntrack_info
@@ -41,6 +42,10 @@
 	/* Conntrack should never be early-expired. */
 	IPS_ASSURED_BIT = 2,
 	IPS_ASSURED = (1 << IPS_ASSURED_BIT),
+
+	/* Connection is confirmed: originating packet has left box */
+	IPS_CONFIRMED_BIT = 3,
+	IPS_CONFIRMED = (1 << IPS_CONFIRMED_BIT),
 };
 
 #include <linux/netfilter_ipv4/ip_conntrack_tcp.h>
@@ -159,7 +164,7 @@
 	struct ip_conntrack_tuple_hash tuplehash[IP_CT_DIR_MAX];
 
 	/* Have we seen traffic both ways yet? (bitset) */
-	volatile unsigned long status;
+	unsigned long status;
 
 	/* Timer function; drops refcnt when it goes off. */
 	struct timer_list timeout;
@@ -254,7 +259,7 @@
 /* It's confirmed if it is, or has been in the hash table. */
 static inline int is_confirmed(struct ip_conntrack *ct)
 {
-	return ct->tuplehash[IP_CT_DIR_ORIGINAL].list.next != NULL;
+	return test_bit(IPS_CONFIRMED_BIT, &ct->status);
 }
 
 extern unsigned int ip_conntrack_htable_size;
diff -urN linux-2.4.21-pre5/include/linux/netfilter_ipv6/ip6t_ah.h linux-2.4.21-pre6/include/linux/netfilter_ipv6/ip6t_ah.h
--- linux-2.4.21-pre5/include/linux/netfilter_ipv6/ip6t_ah.h	Wed Dec 31 16:00:00 1969
+++ linux-2.4.21-pre6/include/linux/netfilter_ipv6/ip6t_ah.h	Wed Mar 26 16:11:40 2003
@@ -0,0 +1,30 @@
+#ifndef _IP6T_AH_H
+#define _IP6T_AH_H
+
+struct ip6t_ah
+{
+	u_int32_t spis[2];			/* Security Parameter Index */
+	u_int32_t hdrlen;			/* Header Length */
+	u_int8_t  hdrres;			/* Test of the Reserved Filed */
+	u_int8_t  invflags;			/* Inverse flags */
+};
+
+#define IP6T_AH_SPI 0x01
+#define IP6T_AH_LEN 0x02
+#define IP6T_AH_RES 0x04
+
+/* Values for "invflags" field in struct ip6t_ah. */
+#define IP6T_AH_INV_SPI		0x01	/* Invert the sense of spi. */
+#define IP6T_AH_INV_LEN		0x02	/* Invert the sense of length. */
+#define IP6T_AH_INV_MASK	0x03	/* All possible flags. */
+
+#define MASK_HOPOPTS    128
+#define MASK_DSTOPTS    64
+#define MASK_ROUTING    32
+#define MASK_FRAGMENT   16
+#define MASK_AH         8
+#define MASK_ESP        4
+#define MASK_NONE       2
+#define MASK_PROTO      1
+
+#endif /*_IP6T_AH_H*/
diff -urN linux-2.4.21-pre5/include/linux/netfilter_ipv6/ip6t_esp.h linux-2.4.21-pre6/include/linux/netfilter_ipv6/ip6t_esp.h
--- linux-2.4.21-pre5/include/linux/netfilter_ipv6/ip6t_esp.h	Wed Dec 31 16:00:00 1969
+++ linux-2.4.21-pre6/include/linux/netfilter_ipv6/ip6t_esp.h	Wed Mar 26 16:11:40 2003
@@ -0,0 +1,23 @@
+#ifndef _IP6T_ESP_H
+#define _IP6T_ESP_H
+
+struct ip6t_esp
+{
+	u_int32_t spis[2];			/* Security Parameter Index */
+	u_int8_t  invflags;			/* Inverse flags */
+};
+
+#define MASK_HOPOPTS    128
+#define MASK_DSTOPTS    64
+#define MASK_ROUTING    32
+#define MASK_FRAGMENT   16
+#define MASK_AH         8
+#define MASK_ESP        4
+#define MASK_NONE       2
+#define MASK_PROTO      1
+
+/* Values for "invflags" field in struct ip6t_esp. */
+#define IP6T_ESP_INV_SPI		0x01	/* Invert the sense of spi. */
+#define IP6T_ESP_INV_MASK	0x01	/* All possible flags. */
+
+#endif /*_IP6T_ESP_H*/
diff -urN linux-2.4.21-pre5/include/linux/netfilter_ipv6/ip6t_frag.h linux-2.4.21-pre6/include/linux/netfilter_ipv6/ip6t_frag.h
--- linux-2.4.21-pre5/include/linux/netfilter_ipv6/ip6t_frag.h	Wed Dec 31 16:00:00 1969
+++ linux-2.4.21-pre6/include/linux/netfilter_ipv6/ip6t_frag.h	Wed Mar 26 16:11:40 2003
@@ -0,0 +1,33 @@
+#ifndef _IP6T_FRAG_H
+#define _IP6T_FRAG_H
+
+struct ip6t_frag
+{
+	u_int32_t ids[2];			/* Security Parameter Index */
+	u_int32_t hdrlen;			/* Header Length */
+	u_int8_t  flags;			/*  */
+	u_int8_t  invflags;			/* Inverse flags */
+};
+
+#define IP6T_FRAG_IDS 		0x01
+#define IP6T_FRAG_LEN 		0x02
+#define IP6T_FRAG_RES 		0x04
+#define IP6T_FRAG_FST 		0x08
+#define IP6T_FRAG_MF  		0x10
+#define IP6T_FRAG_NMF  		0x20
+
+/* Values for "invflags" field in struct ip6t_frag. */
+#define IP6T_FRAG_INV_IDS	0x01	/* Invert the sense of ids. */
+#define IP6T_FRAG_INV_LEN	0x02	/* Invert the sense of length. */
+#define IP6T_FRAG_INV_MASK	0x03	/* All possible flags. */
+
+#define MASK_HOPOPTS    128
+#define MASK_DSTOPTS    64
+#define MASK_ROUTING    32
+#define MASK_FRAGMENT   16
+#define MASK_AH         8
+#define MASK_ESP        4
+#define MASK_NONE       2
+#define MASK_PROTO      1
+
+#endif /*_IP6T_FRAG_H*/
diff -urN linux-2.4.21-pre5/include/linux/netfilter_ipv6/ip6t_hl.h linux-2.4.21-pre6/include/linux/netfilter_ipv6/ip6t_hl.h
--- linux-2.4.21-pre5/include/linux/netfilter_ipv6/ip6t_hl.h	Wed Dec 31 16:00:00 1969
+++ linux-2.4.21-pre6/include/linux/netfilter_ipv6/ip6t_hl.h	Wed Mar 26 16:11:40 2003
@@ -0,0 +1,22 @@
+/* ip6tables module for matching the Hop Limit value
+ * Maciej Soltysiak <solt@dns.toxicfilms.tv>
+ * Based on HW's ttl module */
+
+#ifndef _IP6T_HL_H
+#define _IP6T_HL_H
+
+enum {
+	IP6T_HL_EQ = 0,		/* equals */
+	IP6T_HL_NE,		/* not equals */
+	IP6T_HL_LT,		/* less than */
+	IP6T_HL_GT,		/* greater than */
+};
+
+
+struct ip6t_hl_info {
+	u_int8_t	mode;
+	u_int8_t	hop_limit;
+};
+
+
+#endif
diff -urN linux-2.4.21-pre5/include/linux/netfilter_ipv6/ip6t_ipv6header.h linux-2.4.21-pre6/include/linux/netfilter_ipv6/ip6t_ipv6header.h
--- linux-2.4.21-pre5/include/linux/netfilter_ipv6/ip6t_ipv6header.h	Wed Dec 31 16:00:00 1969
+++ linux-2.4.21-pre6/include/linux/netfilter_ipv6/ip6t_ipv6header.h	Wed Mar 26 16:11:40 2003
@@ -0,0 +1,27 @@
+/* ipv6header match - matches IPv6 packets based
+on whether they contain certain headers */
+
+/* Original idea: Brad Chapman 
+ * Rewritten by: Andras Kis-Szabo <kisza@sch.bme.hu> */
+
+
+#ifndef __IPV6HEADER_H
+#define __IPV6HEADER_H
+
+struct ip6t_ipv6header_info
+{
+	u_int8_t matchflags;
+	u_int8_t invflags;
+	u_int8_t modeflag;
+};
+
+#define MASK_HOPOPTS    128
+#define MASK_DSTOPTS    64
+#define MASK_ROUTING    32
+#define MASK_FRAGMENT   16
+#define MASK_AH         8
+#define MASK_ESP        4
+#define MASK_NONE       2
+#define MASK_PROTO      1
+
+#endif /* __IPV6HEADER_H */
diff -urN linux-2.4.21-pre5/include/linux/netfilter_ipv6/ip6t_opts.h linux-2.4.21-pre6/include/linux/netfilter_ipv6/ip6t_opts.h
--- linux-2.4.21-pre5/include/linux/netfilter_ipv6/ip6t_opts.h	Wed Dec 31 16:00:00 1969
+++ linux-2.4.21-pre6/include/linux/netfilter_ipv6/ip6t_opts.h	Wed Mar 26 16:11:40 2003
@@ -0,0 +1,32 @@
+#ifndef _IP6T_OPTS_H
+#define _IP6T_OPTS_H
+
+#define IP6T_OPTS_OPTSNR 16
+
+struct ip6t_opts
+{
+	u_int32_t hdrlen;			/* Header Length */
+	u_int8_t flags;				/*  */
+	u_int8_t invflags;			/* Inverse flags */
+	u_int16_t opts[IP6T_OPTS_OPTSNR];	/* opts */
+	u_int8_t optsnr;			/* Nr of OPts */
+};
+
+#define IP6T_OPTS_LEN 		0x01
+#define IP6T_OPTS_OPTS 		0x02
+#define IP6T_OPTS_NSTRICT	0x04
+
+/* Values for "invflags" field in struct ip6t_rt. */
+#define IP6T_OPTS_INV_LEN	0x01	/* Invert the sense of length. */
+#define IP6T_OPTS_INV_MASK	0x01	/* All possible flags. */
+
+#define MASK_HOPOPTS    128
+#define MASK_DSTOPTS    64
+#define MASK_ROUTING    32
+#define MASK_FRAGMENT   16
+#define MASK_AH         8
+#define MASK_ESP        4
+#define MASK_NONE       2
+#define MASK_PROTO      1
+
+#endif /*_IP6T_OPTS_H*/
diff -urN linux-2.4.21-pre5/include/linux/netfilter_ipv6/ip6t_rt.h linux-2.4.21-pre6/include/linux/netfilter_ipv6/ip6t_rt.h
--- linux-2.4.21-pre5/include/linux/netfilter_ipv6/ip6t_rt.h	Wed Dec 31 16:00:00 1969
+++ linux-2.4.21-pre6/include/linux/netfilter_ipv6/ip6t_rt.h	Wed Mar 26 16:11:40 2003
@@ -0,0 +1,42 @@
+#ifndef _IP6T_RT_H
+#define _IP6T_RT_H
+
+/*#include <linux/in6.h>*/
+
+#define IP6T_RT_HOPS 16
+
+struct ip6t_rt
+{
+	u_int32_t rt_type;			/* Routing Type */
+	u_int32_t segsleft[2];			/* Segments Left */
+	u_int32_t hdrlen;			/* Header Length */
+	u_int8_t  flags;			/*  */
+	u_int8_t  invflags;			/* Inverse flags */
+	struct in6_addr addrs[IP6T_RT_HOPS];	/* Hops */
+	u_int8_t addrnr;			/* Nr of Addresses */
+};
+
+#define IP6T_RT_TYP 		0x01
+#define IP6T_RT_SGS 		0x02
+#define IP6T_RT_LEN 		0x04
+#define IP6T_RT_RES 		0x08
+#define IP6T_RT_FST_MASK	0x30
+#define IP6T_RT_FST 		0x10
+#define IP6T_RT_FST_NSTRICT	0x20
+
+/* Values for "invflags" field in struct ip6t_rt. */
+#define IP6T_RT_INV_TYP		0x01	/* Invert the sense of type. */
+#define IP6T_RT_INV_SGS		0x02	/* Invert the sense of Segments. */
+#define IP6T_RT_INV_LEN		0x04	/* Invert the sense of length. */
+#define IP6T_RT_INV_MASK	0x07	/* All possible flags. */
+
+#define MASK_HOPOPTS    128
+#define MASK_DSTOPTS    64
+#define MASK_ROUTING    32
+#define MASK_FRAGMENT   16
+#define MASK_AH         8
+#define MASK_ESP        4
+#define MASK_NONE       2
+#define MASK_PROTO      1
+
+#endif /*_IP6T_RT_H*/
diff -urN linux-2.4.21-pre5/include/linux/raid/md.h linux-2.4.21-pre6/include/linux/raid/md.h
--- linux-2.4.21-pre5/include/linux/raid/md.h	Thu Nov 22 11:48:07 2001
+++ linux-2.4.21-pre6/include/linux/raid/md.h	Wed Mar 26 16:11:40 2003
@@ -29,6 +29,7 @@
 #include <linux/module.h>
 #include <linux/hdreg.h>
 #include <linux/proc_fs.h>
+#include <linux/seq_file.h>
 #include <linux/smp_lock.h>
 #include <linux/delay.h>
 #include <net/checksum.h>
diff -urN linux-2.4.21-pre5/include/linux/raid/md_k.h linux-2.4.21-pre6/include/linux/raid/md_k.h
--- linux-2.4.21-pre5/include/linux/raid/md_k.h	Wed Mar 26 16:11:29 2003
+++ linux-2.4.21-pre6/include/linux/raid/md_k.h	Wed Mar 26 16:11:40 2003
@@ -224,7 +224,7 @@
 	int (*make_request)(mddev_t *mddev, int rw, struct buffer_head * bh);
 	int (*run)(mddev_t *mddev);
 	int (*stop)(mddev_t *mddev);
-	int (*status)(char *page, mddev_t *mddev);
+	void (*status)(struct seq_file *seq, mddev_t *mddev);
 	int (*error_handler)(mddev_t *mddev, kdev_t dev);
 
 /*
diff -urN linux-2.4.21-pre5/include/linux/sched.h linux-2.4.21-pre6/include/linux/sched.h
--- linux-2.4.21-pre5/include/linux/sched.h	Wed Mar 26 16:11:29 2003
+++ linux-2.4.21-pre6/include/linux/sched.h	Wed Mar 26 16:11:40 2003
@@ -345,6 +345,7 @@
 	/* ??? */
 	unsigned long personality;
 	int did_exec:1;
+	unsigned task_dumpable:1;
 	pid_t pid;
 	pid_t pgrp;
 	pid_t tty_old_pgrp;
@@ -454,6 +455,8 @@
 #define PT_TRACESYSGOOD	0x00000008
 #define PT_PTRACE_CAP	0x00000010	/* ptracer can follow suid-exec */
 
+#define is_dumpable(tsk)	((tsk)->task_dumpable && (tsk)->mm->dumpable)
+
 /*
  * Limit the stack by to some sane default: root can always
  * increase this limit if needed..  8MB seems reasonable.
@@ -809,6 +812,8 @@
 extern void FASTCALL(add_wait_queue_exclusive(wait_queue_head_t *q, wait_queue_t * wait));
 extern void FASTCALL(remove_wait_queue(wait_queue_head_t *q, wait_queue_t * wait));
 
+extern long kernel_thread(int (*fn)(void *), void * arg, unsigned long flags);
+
 #define __wait_event(wq, condition) 					\
 do {									\
 	wait_queue_t __wait;						\
diff -urN linux-2.4.21-pre5/include/linux/skbuff.h linux-2.4.21-pre6/include/linux/skbuff.h
--- linux-2.4.21-pre5/include/linux/skbuff.h	Wed Mar 26 16:11:29 2003
+++ linux-2.4.21-pre6/include/linux/skbuff.h	Wed Mar 26 16:11:40 2003
@@ -752,7 +752,7 @@
 	return skb->data_len;
 }
 
-static inline int skb_headlen(const struct sk_buff *skb)
+static inline unsigned int skb_headlen(const struct sk_buff *skb)
 {
 	return skb->len - skb->data_len;
 }
@@ -1096,7 +1096,7 @@
  
 static inline struct sk_buff *skb_padto(struct sk_buff *skb, unsigned int len)
 {
-	unsigned int size = skb->len + skb->data_len;
+	unsigned int size = skb->len;
 	if(likely(size >= len))
 		return skb;
 	return skb_pad(skb, len-size);
diff -urN linux-2.4.21-pre5/include/linux/types.h linux-2.4.21-pre6/include/linux/types.h
--- linux-2.4.21-pre5/include/linux/types.h	Fri Aug  2 17:39:46 2002
+++ linux-2.4.21-pre6/include/linux/types.h	Wed Mar 26 16:11:40 2003
@@ -3,6 +3,13 @@
 
 #ifdef	__KERNEL__
 #include <linux/config.h>
+
+#define BITS_TO_LONGS(bits) \
+	(((bits)+BITS_PER_LONG-1)/BITS_PER_LONG)
+#define DECLARE_BITMAP(name,bits) \
+	unsigned long name[BITS_TO_LONGS(bits)]
+#define CLEAR_BITMAP(name,bits) \
+	memset(name, 0, BITS_TO_LONGS(bits)*sizeof(unsigned long))
 #endif
 
 #include <linux/posix_types.h>
diff -urN linux-2.4.21-pre5/include/linux/umem.h linux-2.4.21-pre6/include/linux/umem.h
--- linux-2.4.21-pre5/include/linux/umem.h	Fri Aug  2 17:39:46 2002
+++ linux-2.4.21-pre6/include/linux/umem.h	Wed Mar 26 16:11:40 2003
@@ -127,4 +127,5 @@
 
 #define PCI_VENDOR_ID_MICRO_MEMORY		0x1332
 #define PCI_DEVICE_ID_MICRO_MEMORY_5415CN	0x5415
+#define PCI_DEVICE_ID_MICRO_MEMORY_5425CN	0x5425
 #endif
diff -urN linux-2.4.21-pre5/include/net/af_unix.h linux-2.4.21-pre6/include/net/af_unix.h
--- linux-2.4.21-pre5/include/net/af_unix.h	Mon Apr 24 13:43:04 2000
+++ linux-2.4.21-pre6/include/net/af_unix.h	Wed Mar 26 16:11:40 2003
@@ -13,9 +13,30 @@
 
 extern atomic_t unix_tot_inflight;
 
+static inline unix_socket *first_unix_socket(int *i)
+{
+	for (*i = 0; *i <= UNIX_HASH_SIZE; (*i)++) {
+		if (unix_socket_table[*i])
+			return unix_socket_table[*i];
+	}
+	return NULL;
+}
+
+static inline unix_socket *next_unix_socket(int *i, unix_socket *s)
+{
+	/* More in this chain? */
+	if (s->next)
+		return s->next;
+	/* Look for next non-empty chain. */
+	for ((*i)++; *i <= UNIX_HASH_SIZE; (*i)++) {
+		if (unix_socket_table[*i])
+			return unix_socket_table[*i];
+	}
+	return NULL;
+}
 
-#define forall_unix_sockets(i, s) for (i=0; i<=UNIX_HASH_SIZE; i++) \
-                                    for (s=unix_socket_table[i]; s; s=s->next)
+#define forall_unix_sockets(i, s) \
+	for (s = first_unix_socket(&(i)); s; s = next_unix_socket(&(i),(s)))
 
 struct unix_address
 {
diff -urN linux-2.4.21-pre5/include/net/ip6_route.h linux-2.4.21-pre6/include/net/ip6_route.h
--- linux-2.4.21-pre5/include/net/ip6_route.h	Mon Dec 11 13:30:48 2000
+++ linux-2.4.21-pre6/include/net/ip6_route.h	Wed Mar 26 16:11:40 2003
@@ -31,6 +31,8 @@
 extern struct dst_entry *	ip6_route_output(struct sock *sk,
 						 struct flowi *fl);
 
+extern int			ip6_route_me_harder(struct sk_buff *skb);
+
 extern void			ip6_route_init(void);
 extern void			ip6_route_cleanup(void);
 
diff -urN linux-2.4.21-pre5/include/net/sock.h linux-2.4.21-pre6/include/net/sock.h
--- linux-2.4.21-pre5/include/net/sock.h	Wed Mar 26 16:11:29 2003
+++ linux-2.4.21-pre6/include/net/sock.h	Wed Mar 26 16:11:40 2003
@@ -902,27 +902,41 @@
 
 /**
  *	sk_filter - run a packet through a socket filter
+ *	@sk: sock associated with &sk_buff
  *	@skb: buffer to filter
- *	@filter: filter to apply
+ *	@needlock: set to 1 if the sock is not locked by caller.
  *
  * Run the filter code and then cut skb->data to correct size returned by
  * sk_run_filter. If pkt_len is 0 we toss packet. If skb->len is smaller
  * than pkt_len we keep whole skb->data. This is the socket level
  * wrapper to sk_run_filter. It returns 0 if the packet should
- * be accepted or 1 if the packet should be tossed.
+ * be accepted or -EPERM if the packet should be tossed.
  */
- 
-static inline int sk_filter(struct sk_buff *skb, struct sk_filter *filter)
+
+static inline int sk_filter(struct sock *sk, struct sk_buff *skb, int needlock)
 {
-	int pkt_len;
+	int err = 0;
 
-        pkt_len = sk_run_filter(skb, filter->insns, filter->len);
-        if(!pkt_len)
-                return 1;	/* Toss Packet */
-        else
-                skb_trim(skb, pkt_len);
+	if (sk->filter) {
+		struct sk_filter *filter;
+		
+		if (needlock)
+			bh_lock_sock(sk);
+		
+		filter = sk->filter;
+		if (filter) {
+			int pkt_len = sk_run_filter(skb, filter->insns,
+						    filter->len);
+			if (!pkt_len)
+				err = -EPERM;
+			else
+				skb_trim(skb, pkt_len);
+		}
 
-	return 0;
+		if (needlock)
+			bh_unlock_sock(sk);
+	}
+	return err;
 }
 
 /**
@@ -949,6 +963,13 @@
 	atomic_add(sk_filter_len(fp), &sk->omem_alloc);
 }
 
+#else
+
+static inline int sk_filter(struct sock *sk, struct sk_buff *skb, int needlock)
+{
+	return 0;
+}
+
 #endif /* CONFIG_FILTER */
 
 /*
@@ -1155,36 +1176,31 @@
 
 static inline int sock_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
 {
+	int err = 0;
+
 	/* Cast skb->rcvbuf to unsigned... It's pointless, but reduces
 	   number of warnings when compiling with -W --ANK
 	 */
-	if (atomic_read(&sk->rmem_alloc) + skb->truesize >= (unsigned)sk->rcvbuf)
-                return -ENOMEM;
-
-#ifdef CONFIG_FILTER
-	if (sk->filter) {
-		int err = 0;
-		struct sk_filter *filter;
-
-		/* It would be deadlock, if sock_queue_rcv_skb is used
-		   with socket lock! We assume that users of this
-		   function are lock free.
-		 */
-		bh_lock_sock(sk);
-		if ((filter = sk->filter) != NULL && sk_filter(skb, filter))
-			err = -EPERM;
-		bh_unlock_sock(sk);
-		if (err)
-			return err;	/* Toss packet */
+	if (atomic_read(&sk->rmem_alloc) + skb->truesize >= (unsigned)sk->rcvbuf) {
+		err = -ENOMEM;
+		goto out;
 	}
-#endif /* CONFIG_FILTER */
+
+	/* It would be deadlock, if sock_queue_rcv_skb is used
+	   with socket lock! We assume that users of this
+	   function are lock free.
+	*/
+	err = sk_filter(sk, skb, 1);
+	if (err)
+		goto out;
 
 	skb->dev = NULL;
 	skb_set_owner_r(skb, sk);
 	skb_queue_tail(&sk->receive_queue, skb);
 	if (!sk->dead)
 		sk->data_ready(sk,skb->len);
-	return 0;
+out:
+	return err;
 }
 
 static inline int sock_queue_err_skb(struct sock *sk, struct sk_buff *skb)
diff -urN linux-2.4.21-pre5/init/do_mounts.c linux-2.4.21-pre6/init/do_mounts.c
--- linux-2.4.21-pre5/init/do_mounts.c	Wed Mar 26 16:11:29 2003
+++ linux-2.4.21-pre6/init/do_mounts.c	Wed Mar 26 16:11:40 2003
@@ -813,6 +813,8 @@
 	sys_fchdir(root_fd);
 	sys_chroot(".");
 	sys_umount("/old/dev", 0);
+	close(old_fd);
+	close(root_fd);
 
 	if (real_root_dev == ram0) {
 		sys_chdir("/old");
diff -urN linux-2.4.21-pre5/kernel/fork.c linux-2.4.21-pre6/kernel/fork.c
--- linux-2.4.21-pre5/kernel/fork.c	Thu Nov 28 15:53:15 2002
+++ linux-2.4.21-pre6/kernel/fork.c	Wed Mar 26 16:11:40 2003
@@ -27,6 +27,7 @@
 #include <asm/pgalloc.h>
 #include <asm/uaccess.h>
 #include <asm/mmu_context.h>
+#include <asm/processor.h>
 
 /* The idle threads do not count.. */
 int nr_threads;
@@ -565,6 +566,31 @@
 	p->flags = new_flags;
 }
 
+long kernel_thread(int (*fn)(void *), void * arg, unsigned long flags)
+{
+	struct task_struct *task = current;
+	unsigned old_task_dumpable;
+	long ret;
+
+	/* lock out any potential ptracer */
+	task_lock(task);
+	if (task->ptrace) {
+		task_unlock(task);
+		return -EPERM;
+	}
+
+	old_task_dumpable = task->task_dumpable;
+	task->task_dumpable = 0;
+	task_unlock(task);
+
+	ret = arch_kernel_thread(fn, arg, flags);
+
+	/* never reached in child process, only in parent */
+	current->task_dumpable = old_task_dumpable;
+
+	return ret;
+}
+
 /*
  *  Ok, this is the main fork-routine. It copies the system process
  * information (task[nr]) and sets up the necessary registers. It also
diff -urN linux-2.4.21-pre5/kernel/ptrace.c linux-2.4.21-pre6/kernel/ptrace.c
--- linux-2.4.21-pre5/kernel/ptrace.c	Fri Aug  2 17:39:46 2002
+++ linux-2.4.21-pre6/kernel/ptrace.c	Wed Mar 26 16:11:40 2003
@@ -21,6 +21,10 @@
  */
 int ptrace_check_attach(struct task_struct *child, int kill)
 {
+	mb();
+	if (!is_dumpable(child))
+		return -EPERM;
+
 	if (!(child->ptrace & PT_PTRACED))
 		return -ESRCH;
 
@@ -70,7 +74,7 @@
  	    (current->gid != task->gid)) && !capable(CAP_SYS_PTRACE))
 		goto bad;
 	rmb();
-	if (!task->mm->dumpable && !capable(CAP_SYS_PTRACE))
+	if (!is_dumpable(task) && !capable(CAP_SYS_PTRACE))
 		goto bad;
 	/* the same process cannot be attached many times */
 	if (task->ptrace & PT_PTRACED)
@@ -136,6 +140,8 @@
 	/* Worry about races with exit() */
 	task_lock(tsk);
 	mm = tsk->mm;
+	if (!is_dumpable(tsk) || (&init_mm == mm))
+		mm = NULL;
 	if (mm)
 		atomic_inc(&mm->mm_users);
 	task_unlock(tsk);
diff -urN linux-2.4.21-pre5/kernel/sys.c linux-2.4.21-pre6/kernel/sys.c
--- linux-2.4.21-pre5/kernel/sys.c	Wed Mar 26 16:11:29 2003
+++ linux-2.4.21-pre6/kernel/sys.c	Wed Mar 26 16:11:40 2003
@@ -1238,7 +1238,7 @@
 			error = put_user(current->pdeath_signal, (int *)arg2);
 			break;
 		case PR_GET_DUMPABLE:
-			if (current->mm->dumpable)
+			if (is_dumpable(current))
 				error = 1;
 			break;
 		case PR_SET_DUMPABLE:
@@ -1246,7 +1246,8 @@
 				error = -EINVAL;
 				break;
 			}
-			current->mm->dumpable = arg2;
+			if (is_dumpable(current))
+				current->mm->dumpable = arg2;
 			break;
 
 	        case PR_SET_UNALIGN:
diff -urN linux-2.4.21-pre5/mm/filemap.c linux-2.4.21-pre6/mm/filemap.c
--- linux-2.4.21-pre5/mm/filemap.c	Wed Mar 26 16:11:29 2003
+++ linux-2.4.21-pre6/mm/filemap.c	Wed Mar 26 16:11:40 2003
@@ -1743,7 +1743,7 @@
 	return written;
 }
 
-asmlinkage ssize_t sys_sendfile(int out_fd, int in_fd, off_t *offset, size_t count)
+static ssize_t common_sendfile(int out_fd, int in_fd, loff_t *offset, size_t count)
 {
 	ssize_t retval;
 	struct file * in_file, * out_file;
@@ -1788,27 +1788,19 @@
 	retval = 0;
 	if (count) {
 		read_descriptor_t desc;
-		loff_t pos = 0, *ppos;
-
-		retval = -EFAULT;
-		ppos = &in_file->f_pos;
-		if (offset) {
-			if (get_user(pos, offset))
-				goto fput_out;
-			ppos = &pos;
-		}
+		
+		if (!offset)
+			offset = &in_file->f_pos;
 
 		desc.written = 0;
 		desc.count = count;
 		desc.buf = (char *) out_file;
 		desc.error = 0;
-		do_generic_file_read(in_file, ppos, &desc, file_send_actor);
+		do_generic_file_read(in_file, offset, &desc, file_send_actor);
 
 		retval = desc.written;
 		if (!retval)
 			retval = desc.error;
-		if (offset)
-			put_user(pos, offset);
 	}
 
 fput_out:
@@ -1819,6 +1811,38 @@
 	return retval;
 }
 
+asmlinkage ssize_t sys_sendfile(int out_fd, int in_fd, off_t *offset, size_t count)
+{
+	loff_t pos, *ppos = NULL;
+	ssize_t ret;
+	if (offset) {
+		off_t off;
+		if (unlikely(get_user(off, offset)))
+			return -EFAULT;
+		pos = off;
+		ppos = &pos;
+	}
+	ret = common_sendfile(out_fd, in_fd, ppos, count);
+	if (offset)
+		put_user((off_t)pos, offset);
+	return ret;
+}
+
+asmlinkage ssize_t sys_sendfile64(int out_fd, int in_fd, loff_t *offset, size_t count)
+{
+	loff_t pos, *ppos = NULL;
+	ssize_t ret;
+	if (offset) {
+		if (unlikely(copy_from_user(&pos, offset, sizeof(loff_t))))
+			return -EFAULT;
+		ppos = &pos;
+	}
+	ret = common_sendfile(out_fd, in_fd, ppos, count);
+	if (offset)
+		put_user(pos, offset);
+	return ret;
+}
+
 static ssize_t do_readahead(struct file *file, unsigned long index, unsigned long nr)
 {
 	struct address_space *mapping = file->f_dentry->d_inode->i_mapping;
diff -urN linux-2.4.21-pre5/net/atm/common.c linux-2.4.21-pre6/net/atm/common.c
--- linux-2.4.21-pre5/net/atm/common.c	Fri Aug  2 17:39:46 2002
+++ linux-2.4.21-pre6/net/atm/common.c	Wed Mar 26 16:11:40 2003
@@ -128,7 +128,6 @@
 	vcc->push_oam = NULL;
 	vcc->vpi = vcc->vci = 0; /* no VCI/VPI yet */
 	vcc->atm_options = vcc->aal_options = 0;
-	vcc->timestamp.tv_sec = vcc->timestamp.tv_usec = 0;
 	init_waitqueue_head(&vcc->sleep);
 	skb_queue_head_init(&vcc->recvq);
 	skb_queue_head_init(&vcc->listenq);
@@ -387,7 +386,7 @@
 	set_current_state(TASK_RUNNING);
 	remove_wait_queue(&vcc->sleep,&wait);
 	if (error <= 0) return error;
-	vcc->timestamp = skb->stamp;
+	sock_recv_timestamp(m, vcc->sk, skb);
 	eff_len = skb->len > size ? size : skb->len;
 	if (skb->len > size) /* Not fit ?  Report it... */
 		m->msg_flags |= MSG_TRUNC;
@@ -619,13 +618,11 @@
 			kfree(tmp_buf);
 			goto done;
 		case SIOCGSTAMP: /* borrowed from IP */
-			if (!vcc->timestamp.tv_sec) {
+			if (!vcc->sk->stamp.tv_sec) {
 				ret_val = -ENOENT;
 				goto done;
 			}
-			vcc->timestamp.tv_sec += vcc->timestamp.tv_usec/1000000;
-			vcc->timestamp.tv_usec %= 1000000;
-			ret_val = copy_to_user((void *) arg,&vcc->timestamp,
+			ret_val = copy_to_user((void *) arg, &vcc->sk->stamp,
 			    sizeof(struct timeval)) ? -EFAULT : 0;
 			goto done;
 		case ATM_SETSC:
diff -urN linux-2.4.21-pre5/net/atm/lec.c linux-2.4.21-pre6/net/atm/lec.c
--- linux-2.4.21-pre5/net/atm/lec.c	Sun Sep 30 12:26:08 2001
+++ linux-2.4.21-pre6/net/atm/lec.c	Wed Mar 26 16:11:40 2003
@@ -204,7 +204,8 @@
         struct lecdatahdr_8023 *lec_h;
         struct atm_vcc *send_vcc;
 	struct lec_arp_table *entry;
-        unsigned char *nb, *dst;
+        unsigned char *dst;
+	int min_frame_size;
 #ifdef CONFIG_TR
         unsigned char rdesc[ETH_ALEN]; /* Token Ring route descriptor */
 #endif
@@ -275,26 +276,24 @@
 #endif /* DUMP_PACKETS > 0 */
 
         /* Minimum ethernet-frame size */
-        if (skb->len <62) {
-                if (skb->truesize < 62) {
-                        printk("%s:data packet %d / %d\n",
-                               dev->name,
-                               skb->len,skb->truesize);
-                        nb=(unsigned char*)kmalloc(64, GFP_ATOMIC);
-                        if (nb == NULL) {
+#ifdef CONFIG_TR
+        if (priv->is_trdev)
+                min_frame_size = LEC_MINIMUM_8025_SIZE;
+	else
+#endif
+        min_frame_size = LEC_MINIMUM_8023_SIZE;
+        if (skb->len < min_frame_size) {
+                if (skb->truesize < min_frame_size) {
+                        skb2 = skb_copy_expand(skb, 0,
+                            min_frame_size - skb->truesize, GFP_ATOMIC);
                                 dev_kfree_skb(skb);
+                        if (skb2 == NULL) {
+                                priv->stats.tx_dropped++;
                                 return 0;
                         }
-                        memcpy(nb,skb->data,skb->len);
-                        kfree(skb->head);
-                        skb->head = skb->data = nb;
-                        skb->tail = nb+62;
-                        skb->end = nb+64;
-                        skb->len=62;
-                        skb->truesize = 64;
-                } else {
-                        skb->len = 62;
+                        skb = skb2;
                 }
+		skb_put(skb, min_frame_size - skb->len);
         }
         
         /* Send to right vcc */
@@ -623,6 +622,14 @@
         return 0;
 }
 
+static void lec_set_multicast_list(struct net_device *dev)
+{
+	/* by default, all multicast frames arrive over the bus.
+         * eventually support selective multicast service
+         */
+        return;
+}
+
 static void 
 lec_init(struct net_device *dev)
 {
@@ -632,7 +639,7 @@
         dev->hard_start_xmit = lec_send_packet;
 
         dev->get_stats = lec_get_stats;
-        dev->set_multicast_list = NULL;
+        dev->set_multicast_list = lec_set_multicast_list;
         dev->do_ioctl  = NULL;
         printk("%s: Initialized!\n",dev->name);
         return;
@@ -711,7 +718,7 @@
                         lec_arp_check_empties(priv, vcc, skb);
                 }
                 skb->dev = dev;
-                skb->data += 2; /* skip lec_id */
+                skb_pull(skb, 2); /* skip lec_id */
 #ifdef CONFIG_TR
                 if (priv->is_trdev) skb->protocol = tr_type_trans(skb, dev);
                 else
diff -urN linux-2.4.21-pre5/net/atm/lec.h linux-2.4.21-pre6/net/atm/lec.h
--- linux-2.4.21-pre5/net/atm/lec.h	Fri Feb  9 11:34:13 2001
+++ linux-2.4.21-pre6/net/atm/lec.h	Wed Mar 26 16:11:40 2003
@@ -38,6 +38,9 @@
   unsigned char h_source[ETH_ALEN];
 };
 
+#define LEC_MINIMUM_8023_SIZE   62
+#define LEC_MINIMUM_8025_SIZE   16
+
 /*
  * Operations that LANE2 capable device can do. Two first functions
  * are used to make the device do things. See spec 3.1.3 and 3.1.4.
diff -urN linux-2.4.21-pre5/net/bridge/br_if.c linux-2.4.21-pre6/net/bridge/br_if.c
--- linux-2.4.21-pre5/net/bridge/br_if.c	Wed Mar 26 16:11:29 2003
+++ linux-2.4.21-pre6/net/bridge/br_if.c	Wed Mar 26 16:11:40 2003
@@ -153,8 +153,6 @@
 	p->path_cost = br_initial_port_cost(dev);
 	p->priority = 0x80;
 
-	dev->br_port = p;
-
 	for (i=1;i<255;i++)
 		if (br_get_port(br, i) == NULL)
 			break;
@@ -164,6 +162,8 @@
 		return NULL;
 	}
 
+	dev->br_port = p;
+
 	p->port_no = i;
 	br_init_port(p);
 	p->state = BR_STATE_DISABLED;
diff -urN linux-2.4.21-pre5/net/core/sock.c linux-2.4.21-pre6/net/core/sock.c
--- linux-2.4.21-pre5/net/core/sock.c	Fri Aug  2 17:39:46 2002
+++ linux-2.4.21-pre6/net/core/sock.c	Wed Mar 26 16:11:40 2003
@@ -793,6 +793,7 @@
 					page = alloc_pages(sk->allocation, 0);
 					if (!page) {
 						err = -ENOBUFS;
+						skb_shinfo(skb)->nr_frags = i;
 						kfree_skb(skb);
 						goto failure;
 					}
diff -urN linux-2.4.21-pre5/net/decnet/dn_nsp_in.c linux-2.4.21-pre6/net/decnet/dn_nsp_in.c
--- linux-2.4.21-pre5/net/decnet/dn_nsp_in.c	Fri Nov  9 14:12:54 2001
+++ linux-2.4.21-pre6/net/decnet/dn_nsp_in.c	Wed Mar 26 16:11:40 2003
@@ -565,26 +565,19 @@
  */
 static __inline__ int dn_queue_skb(struct sock *sk, struct sk_buff *skb, int sig, struct sk_buff_head *queue)
 {
-#ifdef CONFIG_FILTER
-	struct sk_filter *filter;
-#endif
+	int err;
 
         /* Cast skb->rcvbuf to unsigned... It's pointless, but reduces
            number of warnings when compiling with -W --ANK
          */
-        if (atomic_read(&sk->rmem_alloc) + skb->truesize >= (unsigned)sk->rcvbuf
-)
-                return -ENOMEM;
-
-#ifdef CONFIG_FILTER
-        if (sk->filter) {
-		int err = 0;
-                if ((filter = sk->filter) != NULL && sk_filter(skb, sk->filter))
-                        err = -EPERM;  /* Toss packet */
-		if (err)
-			return err;
+        if (atomic_read(&sk->rmem_alloc) + skb->truesize >= (unsigned)sk->rcvbuf) {
+        	err = -ENOMEM;
+        	goto out;
         }
-#endif /* CONFIG_FILTER */
+
+	err = sk_filter(sk, skb, 0);
+	if (err)
+		goto out;
 
         skb_set_owner_r(skb, sk);
         skb_queue_tail(queue, skb);
@@ -602,8 +595,8 @@
 				    (sig == SIGURG) ? POLL_PRI : POLL_IN);
 	}
 	read_unlock(&sk->callback_lock);
-
-        return 0;
+out:
+        return err;
 }
 
 static void dn_nsp_otherdata(struct sock *sk, struct sk_buff *skb)
diff -urN linux-2.4.21-pre5/net/econet/af_econet.c linux-2.4.21-pre6/net/econet/af_econet.c
--- linux-2.4.21-pre5/net/econet/af_econet.c	Fri Aug  2 17:39:46 2002
+++ linux-2.4.21-pre6/net/econet/af_econet.c	Wed Mar 26 16:11:40 2003
@@ -306,6 +306,7 @@
 		
 		eb = (struct ec_cb *)&skb->cb;
 		
+		/* BUG: saddr may be NULL */
 		eb->cookie = saddr->cookie;
 		eb->sec = *saddr;
 		eb->sent = ec_tx_done;
diff -urN linux-2.4.21-pre5/net/ipv4/af_inet.c linux-2.4.21-pre6/net/ipv4/af_inet.c
--- linux-2.4.21-pre5/net/ipv4/af_inet.c	Fri Aug  2 17:39:46 2002
+++ linux-2.4.21-pre6/net/ipv4/af_inet.c	Wed Mar 26 16:11:40 2003
@@ -1038,6 +1038,7 @@
 	struct list_head *lh;
 	struct inet_protosw *answer;
 	int protocol = p->protocol;
+	struct list_head *last_perm;
 
 	br_write_lock_bh(BR_NETPROTO_LOCK);
 
@@ -1046,24 +1047,29 @@
 
 	/* If we are trying to override a permanent protocol, bail. */
 	answer = NULL;
+	last_perm = &inetsw[p->type];
 	list_for_each(lh, &inetsw[p->type]) {
 		answer = list_entry(lh, struct inet_protosw, list);
 
 		/* Check only the non-wild match. */
-		if (protocol == answer->protocol &&
-		    (INET_PROTOSW_PERMANENT & answer->flags))
-			break;
+		if (INET_PROTOSW_PERMANENT & answer->flags) {
+			if (protocol == answer->protocol)
+				break;
+			last_perm = lh;
+		}
 
 		answer = NULL;
 	}
 	if (answer)
 		goto out_permanent;
 
-	/* Add to the BEGINNING so that we override any existing
-	 * entry.  This means that when we remove this entry, the
+	/* Add the new entry after the last permanent entry if any, so that
+	 * the new entry does not override a permanent entry when matched with
+	 * a wild-card protocol. But it is allowed to override any existing
+	 * non-permanent entry.  This means that when we remove this entry, the 
 	 * system automatically returns to the old behavior.
 	 */
-	list_add(&p->list, &inetsw[p->type]);
+	list_add(&p->list, last_perm);
 out:
 	br_write_unlock_bh(BR_NETPROTO_LOCK);
 	return;
diff -urN linux-2.4.21-pre5/net/ipv4/icmp.c linux-2.4.21-pre6/net/ipv4/icmp.c
--- linux-2.4.21-pre5/net/ipv4/icmp.c	Fri Aug  2 17:39:46 2002
+++ linux-2.4.21-pre6/net/ipv4/icmp.c	Wed Mar 26 16:11:40 2003
@@ -178,55 +178,27 @@
 static struct icmp_control icmp_pointers[NR_ICMP_TYPES+1];
 
 /*
- *	The ICMP socket. This is the most convenient way to flow control
+ *	The ICMP socket(s). This is the most convenient way to flow control
  *	our ICMP output as well as maintain a clean interface throughout
  *	all layers. All Socketless IP sends will soon be gone.
  */
 	
-struct inode icmp_inode;
-struct socket *icmp_socket = &icmp_inode.u.socket_i;
+static struct inode __icmp_inode[NR_CPUS];
+#define icmp_socket (&__icmp_inode[smp_processor_id()].u.socket_i)
+#define icmp_socket_cpu(X) (&__icmp_inode[(X)].u.socket_i)
 
-/* ICMPv4 socket is only a bit non-reenterable (unlike ICMPv6,
-   which is strongly non-reenterable). A bit later it will be made
-   reenterable and the lock may be removed then.
- */
-
-static int icmp_xmit_holder = -1;
-
-static int icmp_xmit_lock_bh(void)
-{
-	if (!spin_trylock(&icmp_socket->sk->lock.slock)) {
-		if (icmp_xmit_holder == smp_processor_id())
-			return -EAGAIN;
-		spin_lock(&icmp_socket->sk->lock.slock);
-	}
-	icmp_xmit_holder = smp_processor_id();
-	return 0;
-}
-
-static __inline__ int icmp_xmit_lock(void)
+static void icmp_xmit_lock(void)
 {
-	int ret;
 	local_bh_disable();
-	ret = icmp_xmit_lock_bh();
-	if (ret)
-		local_bh_enable();
-	return ret;
-}
-
-static void icmp_xmit_unlock_bh(void)
-{
-	icmp_xmit_holder = -1;
-	spin_unlock(&icmp_socket->sk->lock.slock);
+	if (unlikely(!spin_trylock(&icmp_socket->sk->lock.slock)))
+		BUG();
 }
 
-static __inline__ void icmp_xmit_unlock(void)
+static void icmp_xmit_unlock(void)
 {
-	icmp_xmit_unlock_bh();
-	local_bh_enable();
+	spin_unlock_bh(&icmp_socket->sk->lock.slock);
 }
 
-
 /*
  *	Send an ICMP frame.
  */
@@ -348,8 +320,7 @@
 	if (ip_options_echo(&icmp_param->replyopts, skb))
 		return;
 
-	if (icmp_xmit_lock_bh())
-		return;
+	icmp_xmit_lock();
 
 	icmp_param->data.icmph.checksum=0;
 	icmp_param->csum=0;
@@ -374,7 +345,7 @@
 	}
 	ip_rt_put(rt);
 out:
-	icmp_xmit_unlock_bh();
+	icmp_xmit_unlock();
 }
 
 
@@ -456,8 +427,7 @@
 		}
 	}
 
-	if (icmp_xmit_lock())
-		return;
+	icmp_xmit_lock();
 
 	/*
 	 *	Construct source address and options.
@@ -985,29 +955,32 @@
 
 void __init icmp_init(struct net_proto_family *ops)
 {
-	int err;
+	int err, i;
 
-	icmp_inode.i_mode = S_IFSOCK;
-	icmp_inode.i_sock = 1;
-	icmp_inode.i_uid = 0;
-	icmp_inode.i_gid = 0;
-	init_waitqueue_head(&icmp_inode.i_wait);
-	init_waitqueue_head(&icmp_inode.u.socket_i.wait);
-
-	icmp_socket->inode = &icmp_inode;
-	icmp_socket->state = SS_UNCONNECTED;
-	icmp_socket->type=SOCK_RAW;
-
-	if ((err=ops->create(icmp_socket, IPPROTO_ICMP))<0)
-		panic("Failed to create the ICMP control socket.\n");
-	icmp_socket->sk->allocation=GFP_ATOMIC;
-	icmp_socket->sk->sndbuf = SK_WMEM_MAX*2;
-	icmp_socket->sk->protinfo.af_inet.ttl = MAXTTL;
-	icmp_socket->sk->protinfo.af_inet.pmtudisc = IP_PMTUDISC_DONT;
-
-	/* Unhash it so that IP input processing does not even
-	 * see it, we do not wish this socket to see incoming
-	 * packets.
-	 */
-	icmp_socket->sk->prot->unhash(icmp_socket->sk);
+	for (i = 0; i < NR_CPUS; i++) {
+		__icmp_inode[i].i_mode = S_IFSOCK;
+		__icmp_inode[i].i_sock = 1;
+		__icmp_inode[i].i_uid = 0;
+		__icmp_inode[i].i_gid = 0;
+		init_waitqueue_head(&__icmp_inode[i].i_wait);
+		init_waitqueue_head(&__icmp_inode[i].u.socket_i.wait);
+
+		icmp_socket_cpu(i)->inode = &__icmp_inode[i];
+		icmp_socket_cpu(i)->state = SS_UNCONNECTED;
+		icmp_socket_cpu(i)->type = SOCK_RAW;
+
+		if ((err=ops->create(icmp_socket_cpu(i), IPPROTO_ICMP)) < 0)
+			panic("Failed to create the ICMP control socket.\n");
+
+		icmp_socket_cpu(i)->sk->allocation=GFP_ATOMIC;
+		icmp_socket_cpu(i)->sk->sndbuf = SK_WMEM_MAX*2;
+		icmp_socket_cpu(i)->sk->protinfo.af_inet.ttl = MAXTTL;
+		icmp_socket_cpu(i)->sk->protinfo.af_inet.pmtudisc = IP_PMTUDISC_DONT;
+
+		/* Unhash it so that IP input processing does not even
+		 * see it, we do not wish this socket to see incoming
+		 * packets.
+		 */
+		icmp_socket_cpu(i)->sk->prot->unhash(icmp_socket_cpu(i)->sk);
+	}
 }
diff -urN linux-2.4.21-pre5/net/ipv4/ip_fragment.c linux-2.4.21-pre6/net/ipv4/ip_fragment.c
--- linux-2.4.21-pre5/net/ipv4/ip_fragment.c	Mon Feb 25 11:38:14 2002
+++ linux-2.4.21-pre6/net/ipv4/ip_fragment.c	Wed Mar 26 16:11:40 2003
@@ -19,6 +19,7 @@
  *		Bill Hawes	:	Frag accounting and evictor fixes.
  *		John McDonald	:	0 length frag bug.
  *		Alexey Kuznetsov:	SMP races, threading, cleanup.
+ *		Patrick McHardy :	LRU queue of frag heads for evictor.
  */
 
 #include <linux/config.h>
@@ -26,6 +27,7 @@
 #include <linux/mm.h>
 #include <linux/sched.h>
 #include <linux/skbuff.h>
+#include <linux/list.h>
 #include <linux/ip.h>
 #include <linux/icmp.h>
 #include <linux/netdevice.h>
@@ -67,6 +69,7 @@
 /* Describe an entry in the "incomplete datagrams" queue. */
 struct ipq {
 	struct ipq	*next;		/* linked list pointers			*/
+	struct list_head lru_list;	/* lru list member 			*/
 	u32		saddr;
 	u32		daddr;
 	u16		id;
@@ -94,6 +97,7 @@
 /* Per-bucket lock is easy to add now. */
 static struct ipq *ipq_hash[IPQ_HASHSZ];
 static rwlock_t ipfrag_lock = RW_LOCK_UNLOCKED;
+static LIST_HEAD(ipq_lru_list);
 int ip_frag_nqueues = 0;
 
 static __inline__ void __ipq_unlink(struct ipq *qp)
@@ -101,6 +105,7 @@
 	if(qp->next)
 		qp->next->pprev = qp->pprev;
 	*qp->pprev = qp->next;
+	list_del(&qp->lru_list);
 	ip_frag_nqueues--;
 }
 
@@ -202,39 +207,30 @@
  */
 static void ip_evictor(void)
 {
-	int i, progress;
+	struct ipq *qp;
+	struct list_head *tmp;
 
-	do {
+	for(;;) {
 		if (atomic_read(&ip_frag_mem) <= sysctl_ipfrag_low_thresh)
 			return;
-		progress = 0;
-		/* FIXME: Make LRU queue of frag heads. -DaveM */
-		for (i = 0; i < IPQ_HASHSZ; i++) {
-			struct ipq *qp;
-			if (ipq_hash[i] == NULL)
-				continue;
-
-			read_lock(&ipfrag_lock);
-			if ((qp = ipq_hash[i]) != NULL) {
-				/* find the oldest queue for this hash bucket */
-				while (qp->next)
-					qp = qp->next;
-				atomic_inc(&qp->refcnt);
-				read_unlock(&ipfrag_lock);
-
-				spin_lock(&qp->lock);
-				if (!(qp->last_in&COMPLETE))
-					ipq_kill(qp);
-				spin_unlock(&qp->lock);
-
-				ipq_put(qp);
-				IP_INC_STATS_BH(IpReasmFails);
-				progress = 1;
-				continue;
-			}
+		read_lock(&ipfrag_lock);
+		if (list_empty(&ipq_lru_list)) {
 			read_unlock(&ipfrag_lock);
+			return;
 		}
-	} while (progress);
+		tmp = ipq_lru_list.next;
+		qp = list_entry(tmp, struct ipq, lru_list);
+		atomic_inc(&qp->refcnt);
+		read_unlock(&ipfrag_lock);
+
+		spin_lock(&qp->lock);
+		if (!(qp->last_in&COMPLETE))
+			ipq_kill(qp);
+		spin_unlock(&qp->lock);
+
+		ipq_put(qp);
+		IP_INC_STATS_BH(IpReasmFails);
+	}
 }
 
 /*
@@ -302,6 +298,8 @@
 		qp->next->pprev = &qp->next;
 	ipq_hash[hash] = qp;
 	qp->pprev = &ipq_hash[hash];
+	INIT_LIST_HEAD(&qp->lru_list);
+	list_add_tail(&qp->lru_list, &ipq_lru_list);
 	ip_frag_nqueues++;
 	write_unlock(&ipfrag_lock);
 	return qp;
@@ -496,6 +494,10 @@
 	if (offset == 0)
 		qp->last_in |= FIRST_IN;
 
+	write_lock(&ipfrag_lock);
+	list_move_tail(&qp->lru_list, &ipq_lru_list);
+	write_unlock(&ipfrag_lock);
+
 	return;
 
 err:
diff -urN linux-2.4.21-pre5/net/ipv4/netfilter/ip_conntrack_core.c linux-2.4.21-pre6/net/ipv4/netfilter/ip_conntrack_core.c
--- linux-2.4.21-pre5/net/ipv4/netfilter/ip_conntrack_core.c	Wed Mar 26 16:11:29 2003
+++ linux-2.4.21-pre6/net/ipv4/netfilter/ip_conntrack_core.c	Wed Mar 26 16:11:40 2003
@@ -13,9 +13,6 @@
  *	- export ip_conntrack[_expect]_{find_get,put} functions
  * */
 
-#ifdef MODULE
-#define __NO_VERSION__
-#endif
 #include <linux/version.h>
 #include <linux/config.h>
 #include <linux/types.h>
@@ -292,9 +289,6 @@
 {
 	DEBUGP("clean_from_lists(%p)\n", ct);
 	MUST_BE_WRITE_LOCKED(&ip_conntrack_lock);
-	/* Remove from both hash lists: must not NULL out next ptrs,
-           otherwise we'll look unconfirmed.  Fortunately, LIST_DELETE
-           doesn't do this. --RR */
 	LIST_DELETE(&ip_conntrack_hash
 		    [hash_conntrack(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple)],
 		    &ct->tuplehash[IP_CT_DIR_ORIGINAL]);
@@ -467,6 +461,7 @@
 		ct->timeout.expires += jiffies;
 		add_timer(&ct->timeout);
 		atomic_inc(&ct->ct_general.use);
+		set_bit(IPS_CONFIRMED_BIT, &ct->status);
 		WRITE_UNLOCK(&ip_conntrack_lock);
 		return NF_ACCEPT;
 	}
@@ -585,7 +580,7 @@
    connection.  Too bad: we're in trouble anyway. */
 static inline int unreplied(const struct ip_conntrack_tuple_hash *i)
 {
-	return !(i->ctrack->status & IPS_ASSURED);
+	return !(test_bit(IPS_ASSURED_BIT, &i->ctrack->status));
 }
 
 static int early_drop(struct list_head *chain)
@@ -720,7 +715,7 @@
 			conntrack, expected);
 		/* Welcome, Mr. Bond.  We've been expecting you... */
 		IP_NF_ASSERT(master_ct(conntrack));
-		conntrack->status = IPS_EXPECTED;
+		__set_bit(IPS_EXPECTED_BIT, &conntrack->status);
 		conntrack->master = expected;
 		expected->sibling = conntrack;
 		LIST_DELETE(&ip_conntrack_expect_list, expected);
@@ -768,11 +763,11 @@
 		*set_reply = 1;
 	} else {
 		/* Once we've had two way comms, always ESTABLISHED. */
-		if (h->ctrack->status & IPS_SEEN_REPLY) {
+		if (test_bit(IPS_SEEN_REPLY_BIT, &h->ctrack->status)) {
 			DEBUGP("ip_conntrack_in: normal packet for %p\n",
 			       h->ctrack);
 		        *ctinfo = IP_CT_ESTABLISHED;
-		} else if (h->ctrack->status & IPS_EXPECTED) {
+		} else if (test_bit(IPS_EXPECTED_BIT, &h->ctrack->status)) {
 			DEBUGP("ip_conntrack_in: related packet for %p\n",
 			       h->ctrack);
 			*ctinfo = IP_CT_RELATED;
diff -urN linux-2.4.21-pre5/net/ipv4/netfilter/ip_conntrack_ftp.c linux-2.4.21-pre6/net/ipv4/netfilter/ip_conntrack_ftp.c
--- linux-2.4.21-pre5/net/ipv4/netfilter/ip_conntrack_ftp.c	Wed Mar 26 16:11:29 2003
+++ linux-2.4.21-pre6/net/ipv4/netfilter/ip_conntrack_ftp.c	Wed Mar 26 16:11:40 2003
@@ -32,7 +32,7 @@
 
 static int try_rfc959(const char *, size_t, u_int32_t [], char);
 static int try_eprt(const char *, size_t, u_int32_t [], char);
-static int try_espv_response(const char *, size_t, u_int32_t [], char);
+static int try_epsv_response(const char *, size_t, u_int32_t [], char);
 
 static struct ftp_search {
 	enum ip_conntrack_dir dir;
@@ -65,7 +65,7 @@
 		IP_CT_DIR_REPLY,
 		"229 ", sizeof("229 ") - 1, '(', ')',
 		IP_CT_FTP_EPSV,
-		try_espv_response,
+		try_epsv_response,
 	},
 };
 
@@ -157,7 +157,7 @@
 }
 
 /* Returns 0, or length of numbers: |||6446| */
-static int try_espv_response(const char *data, size_t dlen, u_int32_t array[6],
+static int try_epsv_response(const char *data, size_t dlen, u_int32_t array[6],
 			     char term)
 {
 	char delim;
diff -urN linux-2.4.21-pre5/net/ipv4/netfilter/ip_conntrack_proto_tcp.c linux-2.4.21-pre6/net/ipv4/netfilter/ip_conntrack_proto_tcp.c
--- linux-2.4.21-pre5/net/ipv4/netfilter/ip_conntrack_proto_tcp.c	Wed Mar 26 16:11:29 2003
+++ linux-2.4.21-pre6/net/ipv4/netfilter/ip_conntrack_proto_tcp.c	Wed Mar 26 16:11:40 2003
@@ -1,4 +1,3 @@
-#define __NO_VERSION__
 #include <linux/types.h>
 #include <linux/sched.h>
 #include <linux/timer.h>
@@ -191,7 +190,7 @@
 	   have an established connection: this is a fairly common
 	   problem case, so we can delete the conntrack
 	   immediately.  --RR */
-	if (!(conntrack->status & IPS_SEEN_REPLY) && tcph->rst) {
+	if (!test_bit(IPS_SEEN_REPLY_BIT, &conntrack->status) && tcph->rst) {
 		WRITE_UNLOCK(&tcp_lock);
 		if (del_timer(&conntrack->timeout))
 			conntrack->timeout.function((unsigned long)conntrack);
diff -urN linux-2.4.21-pre5/net/ipv4/netfilter/ip_conntrack_proto_udp.c linux-2.4.21-pre6/net/ipv4/netfilter/ip_conntrack_proto_udp.c
--- linux-2.4.21-pre5/net/ipv4/netfilter/ip_conntrack_proto_udp.c	Thu Nov 28 15:53:15 2002
+++ linux-2.4.21-pre6/net/ipv4/netfilter/ip_conntrack_proto_udp.c	Wed Mar 26 16:11:40 2003
@@ -51,7 +51,7 @@
 {
 	/* If we've seen traffic both ways, this is some kind of UDP
 	   stream.  Extend timeout. */
-	if (conntrack->status & IPS_SEEN_REPLY) {
+	if (test_bit(IPS_SEEN_REPLY_BIT, &conntrack->status)) {
 		ip_ct_refresh(conntrack, UDP_STREAM_TIMEOUT);
 		/* Also, more likely to be important, and not a probe */
 		set_bit(IPS_ASSURED_BIT, &conntrack->status);
diff -urN linux-2.4.21-pre5/net/ipv4/netfilter/ip_conntrack_standalone.c linux-2.4.21-pre6/net/ipv4/netfilter/ip_conntrack_standalone.c
--- linux-2.4.21-pre5/net/ipv4/netfilter/ip_conntrack_standalone.c	Thu Nov 28 15:53:15 2002
+++ linux-2.4.21-pre6/net/ipv4/netfilter/ip_conntrack_standalone.c	Wed Mar 26 16:11:40 2003
@@ -77,7 +77,7 @@
 }
 
 static unsigned int
-print_conntrack(char *buffer, const struct ip_conntrack *conntrack)
+print_conntrack(char *buffer, struct ip_conntrack *conntrack)
 {
 	unsigned int len;
 	struct ip_conntrack_protocol *proto
@@ -95,12 +95,12 @@
 	len += print_tuple(buffer + len,
 			   &conntrack->tuplehash[IP_CT_DIR_ORIGINAL].tuple,
 			   proto);
-	if (!(conntrack->status & IPS_SEEN_REPLY))
+	if (!(test_bit(IPS_SEEN_REPLY_BIT, &conntrack->status)))
 		len += sprintf(buffer + len, "[UNREPLIED] ");
 	len += print_tuple(buffer + len,
 			   &conntrack->tuplehash[IP_CT_DIR_REPLY].tuple,
 			   proto);
-	if (conntrack->status & IPS_ASSURED)
+	if (test_bit(IPS_ASSURED_BIT, &conntrack->status))
 		len += sprintf(buffer + len, "[ASSURED] ");
 	len += sprintf(buffer + len, "use=%u ",
 		       atomic_read(&conntrack->ct_general.use));
diff -urN linux-2.4.21-pre5/net/ipv4/netfilter/ip_nat_core.c linux-2.4.21-pre6/net/ipv4/netfilter/ip_nat_core.c
--- linux-2.4.21-pre5/net/ipv4/netfilter/ip_nat_core.c	Thu Nov 28 15:53:15 2002
+++ linux-2.4.21-pre6/net/ipv4/netfilter/ip_nat_core.c	Wed Mar 26 16:11:40 2003
@@ -2,9 +2,6 @@
 
 /* (c) 1999 Paul `Rusty' Russell.  Licenced under the GNU General
    Public Licence. */
-#ifdef MODULE
-#define __NO_VERSION__
-#endif
 #include <linux/version.h>
 #include <linux/module.h>
 #include <linux/types.h>
@@ -915,7 +912,7 @@
 		   packet, except it was never src/dst reversed, so
 		   where we would normally apply a dst manip, we apply
 		   a src, and vice versa. */
-		if (info->manips[i].hooknum == opposite_hook[hooknum]) {
+		if (info->manips[i].hooknum == hooknum) {
 			DEBUGP("icmp_reply: inner %s -> %u.%u.%u.%u %u\n",
 			       info->manips[i].maniptype == IP_NAT_MANIP_SRC
 			       ? "DST" : "SRC",
@@ -926,9 +923,9 @@
 				  &info->manips[i].manip,
 				  !info->manips[i].maniptype,
 				  &skb->nfcache);
-		/* Outer packet needs to have IP header NATed like
-                   it's a reply. */
-		} else if (info->manips[i].hooknum == hooknum) {
+			/* Outer packet needs to have IP header NATed like
+	                   it's a reply. */
+
 			/* Use mapping to map outer packet: 0 give no
                            per-proto mapping */
 			DEBUGP("icmp_reply: outer %s -> %u.%u.%u.%u\n",
diff -urN linux-2.4.21-pre5/net/ipv4/netfilter/ip_nat_rule.c linux-2.4.21-pre6/net/ipv4/netfilter/ip_nat_rule.c
--- linux-2.4.21-pre5/net/ipv4/netfilter/ip_nat_rule.c	Thu Nov 28 15:53:15 2002
+++ linux-2.4.21-pre6/net/ipv4/netfilter/ip_nat_rule.c	Wed Mar 26 16:11:40 2003
@@ -1,5 +1,4 @@
 /* Everything about the rules for NAT. */
-#define __NO_VERSION__
 #include <linux/types.h>
 #include <linux/ip.h>
 #include <linux/netfilter.h>
diff -urN linux-2.4.21-pre5/net/ipv4/netfilter/ip_tables.c linux-2.4.21-pre6/net/ipv4/netfilter/ip_tables.c
--- linux-2.4.21-pre5/net/ipv4/netfilter/ip_tables.c	Wed Mar 26 16:11:29 2003
+++ linux-2.4.21-pre6/net/ipv4/netfilter/ip_tables.c	Wed Mar 26 16:11:40 2003
@@ -1629,7 +1629,7 @@
 		     u_int8_t type, u_int8_t code,
 		     int invert)
 {
-	return (type == test_type && code >= min_code && code <= max_code)
+	return ((test_type == 0xFF) || (type == test_type && code >= min_code && code <= max_code))
 		^ invert;
 }
 
diff -urN linux-2.4.21-pre5/net/ipv4/route.c linux-2.4.21-pre6/net/ipv4/route.c
--- linux-2.4.21-pre5/net/ipv4/route.c	Thu Nov 28 15:53:15 2002
+++ linux-2.4.21-pre6/net/ipv4/route.c	Wed Mar 26 16:11:40 2003
@@ -2156,7 +2156,7 @@
 		struct net_device *dev = __dev_get_by_index(iif);
 		err = -ENODEV;
 		if (!dev)
-			goto out;
+			goto out_free;
 		skb->protocol	= htons(ETH_P_IP);
 		skb->dev	= dev;
 		local_bh_disable();
@@ -2171,10 +2171,8 @@
 			memcpy(&oif, RTA_DATA(rta[RTA_OIF - 1]), sizeof(int));
 		err = ip_route_output(&rt, dst, src, rtm->rtm_tos, oif);
 	}
-	if (err) {
-		kfree_skb(skb);
-		goto out;
-	}
+	if (err)
+		goto out_free;
 
 	skb->dst = &rt->u.dst;
 	if (rtm->rtm_flags & RTM_F_NOTIFY)
@@ -2185,16 +2183,20 @@
 	err = rt_fill_info(skb, NETLINK_CB(in_skb).pid, nlh->nlmsg_seq,
 				RTM_NEWROUTE, 0);
 	if (!err)
-		goto out;
+		goto out_free;
 	if (err < 0) {
 		err = -EMSGSIZE;
-		goto out;
+		goto out_free;
 	}
 
 	err = netlink_unicast(rtnl, skb, NETLINK_CB(in_skb).pid, MSG_DONTWAIT);
 	if (err > 0)
 		err = 0;
 out:	return err;
+
+out_free:
+	kfree_skb(skb);
+	goto out;
 }
 
 int ip_rt_dump(struct sk_buff *skb,  struct netlink_callback *cb)
diff -urN linux-2.4.21-pre5/net/ipv4/tcp_ipv4.c linux-2.4.21-pre6/net/ipv4/tcp_ipv4.c
--- linux-2.4.21-pre5/net/ipv4/tcp_ipv4.c	Wed Mar 26 16:11:29 2003
+++ linux-2.4.21-pre6/net/ipv4/tcp_ipv4.c	Wed Mar 26 16:11:40 2003
@@ -1663,12 +1663,6 @@
  */
 int tcp_v4_do_rcv(struct sock *sk, struct sk_buff *skb)
 {
-#ifdef CONFIG_FILTER
-	struct sk_filter *filter = sk->filter;
-	if (filter && sk_filter(skb, filter))
-		goto discard;
-#endif /* CONFIG_FILTER */
-
   	IP_INC_STATS_BH(IpInDelivers);
 
 	if (sk->state == TCP_ESTABLISHED) { /* Fast path */
@@ -1772,6 +1766,9 @@
 	if (sk->state == TCP_TIME_WAIT)
 		goto do_time_wait;
 
+	if (sk_filter(sk, skb, 0))
+		goto discard_and_relse;
+
 	skb->dev = NULL;
 
 	bh_lock_sock(sk);
diff -urN linux-2.4.21-pre5/net/ipv4/tcp_output.c linux-2.4.21-pre6/net/ipv4/tcp_output.c
--- linux-2.4.21-pre5/net/ipv4/tcp_output.c	Wed Mar 26 16:11:29 2003
+++ linux-2.4.21-pre6/net/ipv4/tcp_output.c	Wed Mar 26 16:11:40 2003
@@ -1431,7 +1431,8 @@
 	}
 
 	if (err <= 0) {
-		tp->backoff++;
+		if (tp->backoff < sysctl_tcp_retries2)
+			tp->backoff++;
 		tp->probes_out++;
 		tcp_reset_xmit_timer (sk, TCP_TIME_PROBE0, 
 				      min(tp->rto << tp->backoff, TCP_RTO_MAX));
diff -urN linux-2.4.21-pre5/net/ipv6/af_inet6.c linux-2.4.21-pre6/net/ipv6/af_inet6.c
--- linux-2.4.21-pre5/net/ipv6/af_inet6.c	Wed Mar 26 16:11:29 2003
+++ linux-2.4.21-pre6/net/ipv6/af_inet6.c	Wed Mar 26 16:11:40 2003
@@ -542,6 +542,7 @@
 	struct list_head *lh;
 	struct inet_protosw *answer;
 	int protocol = p->protocol;
+	struct list_head *last_perm;
 
 	br_write_lock_bh(BR_NETPROTO_LOCK);
 
@@ -550,24 +551,29 @@
 
 	/* If we are trying to override a permanent protocol, bail. */
 	answer = NULL;
+	last_perm = &inetsw6[p->type];
 	list_for_each(lh, &inetsw6[p->type]) {
 		answer = list_entry(lh, struct inet_protosw, list);
 
 		/* Check only the non-wild match. */
-		if (protocol == answer->protocol &&
-		    (INET_PROTOSW_PERMANENT & answer->flags))
-			break;
+		if (INET_PROTOSW_PERMANENT & answer->flags) {
+			if (protocol == answer->protocol)
+				break;
+			last_perm = lh;
+		}
 
 		answer = NULL;
 	}
 	if (answer)
 		goto out_permanent;
 
-	/* Add to the BEGINNING so that we override any existing
-	 * entry.  This means that when we remove this entry, the
+	/* Add the new entry after the last permanent entry if any, so that
+	 * the new entry does not override a permanent entry when matched with
+	 * a wild-card protocol. But it is allowed to override any existing
+	 * non-permanent entry.  This means that when we remove this entry, the 
 	 * system automatically returns to the old behavior.
 	 */
-	list_add(&p->list, &inetsw6[p->type]);
+	list_add(&p->list, last_perm);
 out:
 	br_write_unlock_bh(BR_NETPROTO_LOCK);
 	return;
diff -urN linux-2.4.21-pre5/net/ipv6/icmp.c linux-2.4.21-pre6/net/ipv6/icmp.c
--- linux-2.4.21-pre5/net/ipv6/icmp.c	Wed Mar 26 16:11:29 2003
+++ linux-2.4.21-pre6/net/ipv6/icmp.c	Wed Mar 26 16:11:40 2003
@@ -28,7 +28,6 @@
  *	YOSHIFUJI Hideaki @USAGI:	added sysctl for icmp rate limit.
  */
 
-#define __NO_VERSION__
 #include <linux/module.h>
 #include <linux/errno.h>
 #include <linux/types.h>
@@ -68,10 +67,12 @@
 struct icmpv6_mib icmpv6_statistics[NR_CPUS*2];
 
 /*
- *	ICMP socket for flow control.
+ *	ICMP socket(s) for flow control.
  */
 
-struct socket *icmpv6_socket;
+static struct socket *__icmpv6_socket[NR_CPUS];
+#define icmpv6_socket	__icmpv6_socket[smp_processor_id()]
+#define icmpv6_socket_cpu(X) __icmpv6_socket[(X)]
 
 int icmpv6_rcv(struct sk_buff *skb);
 
@@ -96,43 +97,18 @@
 };
 
 
-static int icmpv6_xmit_holder = -1;
-
-static int icmpv6_xmit_lock_bh(void)
-{
-	if (!spin_trylock(&icmpv6_socket->sk->lock.slock)) {
-		if (icmpv6_xmit_holder == smp_processor_id())
-			return -EAGAIN;
-		spin_lock(&icmpv6_socket->sk->lock.slock);
-	}
-	icmpv6_xmit_holder = smp_processor_id();
-	return 0;
-}
-
-static __inline__ int icmpv6_xmit_lock(void)
+static void icmpv6_xmit_lock(void)
 {
-	int ret;
 	local_bh_disable();
-	ret = icmpv6_xmit_lock_bh();
-	if (ret)
-		local_bh_enable();
-	return ret;
+	if (unlikely(!spin_trylock(&icmpv6_socket->sk->lock.slock)))
+		BUG();
 }
 
-static void icmpv6_xmit_unlock_bh(void)
+static void icmpv6_xmit_unlock(void)
 {
-	icmpv6_xmit_holder = -1;
-	spin_unlock(&icmpv6_socket->sk->lock.slock);
+	spin_unlock_bh(&icmpv6_socket->sk->lock.slock);
 }
 
-static __inline__ void icmpv6_xmit_unlock(void)
-{
-	icmpv6_xmit_unlock_bh();
-	local_bh_enable();
-}
-
-
-
 /*
  *	getfrag callback
  */
@@ -349,8 +325,7 @@
 	fl.uli_u.icmpt.type = type;
 	fl.uli_u.icmpt.code = code;
 
-	if (icmpv6_xmit_lock())
-		return;
+	icmpv6_xmit_lock();
 
 	if (!icmpv6_xrlim_allow(sk, type, &fl))
 		goto out;
@@ -423,15 +398,14 @@
 	fl.uli_u.icmpt.type = ICMPV6_ECHO_REPLY;
 	fl.uli_u.icmpt.code = 0;
 
-	if (icmpv6_xmit_lock_bh())
-		return;
+	icmpv6_xmit_lock();
 
 	ip6_build_xmit(sk, icmpv6_getfrag, &msg, &fl, msg.len, NULL, -1,
 		       MSG_DONTWAIT);
 	ICMP6_INC_STATS_BH(Icmp6OutEchoReplies);
 	ICMP6_INC_STATS_BH(Icmp6OutMsgs);
 
-	icmpv6_xmit_unlock_bh();
+	icmpv6_xmit_unlock();
 }
 
 static void icmpv6_notify(struct sk_buff *skb, int type, int code, u32 info)
@@ -640,41 +614,53 @@
 int __init icmpv6_init(struct net_proto_family *ops)
 {
 	struct sock *sk;
-	int err;
+	int err, i, j;
+
+	for (i = 0; i < NR_CPUS; i++) {
+		icmpv6_socket_cpu(i) = sock_alloc();
+		if (icmpv6_socket_cpu(i) == NULL) {
+			printk(KERN_ERR
+			       "Failed to create the ICMP6 control socket.\n");
+			err = -1;
+			goto fail;
+		}
+		icmpv6_socket_cpu(i)->inode->i_uid = 0;
+		icmpv6_socket_cpu(i)->inode->i_gid = 0;
+		icmpv6_socket_cpu(i)->type = SOCK_RAW;
+
+		if ((err = ops->create(icmpv6_socket_cpu(i), IPPROTO_ICMPV6)) < 0) {
+			printk(KERN_ERR
+			       "Failed to initialize the ICMP6 control socket "
+			       "(err %d).\n",
+			       err);
+			goto fail;
+		}
 
-	icmpv6_socket = sock_alloc();
-	if (icmpv6_socket == NULL) {
-		printk(KERN_ERR
-		       "Failed to create the ICMP6 control socket.\n");
-		return -1;
-	}
-	icmpv6_socket->inode->i_uid = 0;
-	icmpv6_socket->inode->i_gid = 0;
-	icmpv6_socket->type = SOCK_RAW;
-
-	if ((err = ops->create(icmpv6_socket, IPPROTO_ICMPV6)) < 0) {
-		printk(KERN_ERR
-		       "Failed to initialize the ICMP6 control socket (err %d).\n",
-		       err);
-		sock_release(icmpv6_socket);
-		icmpv6_socket = NULL; /* for safety */
-		return err;
-	}
-
-	sk = icmpv6_socket->sk;
-	sk->allocation = GFP_ATOMIC;
-	sk->sndbuf = SK_WMEM_MAX*2;
-	sk->prot->unhash(sk);
+		sk = icmpv6_socket_cpu(i)->sk;
+		sk->allocation = GFP_ATOMIC;
+		sk->sndbuf = SK_WMEM_MAX*2;
+		sk->prot->unhash(sk);
+	}
 
 	inet6_add_protocol(&icmpv6_protocol);
 
 	return 0;
+fail:
+	for (j = 0; j < i; j++) {
+		sock_release(icmpv6_socket_cpu(j));
+		icmpv6_socket_cpu(j) = NULL;
+	}
+	return err;
 }
 
 void icmpv6_cleanup(void)
 {
-	sock_release(icmpv6_socket);
-	icmpv6_socket = NULL; /* For safety. */
+	int i;
+
+	for (i = 0; i < NR_CPUS; i++) {
+		sock_release(icmpv6_socket_cpu(i));
+		icmpv6_socket_cpu(i) = NULL;
+	}
 	inet6_del_protocol(&icmpv6_protocol);
 }
 
diff -urN linux-2.4.21-pre5/net/ipv6/ip6_output.c linux-2.4.21-pre6/net/ipv6/ip6_output.c
--- linux-2.4.21-pre5/net/ipv6/ip6_output.c	Wed Mar 26 16:11:29 2003
+++ linux-2.4.21-pre6/net/ipv6/ip6_output.c	Wed Mar 26 16:11:40 2003
@@ -132,7 +132,7 @@
 
 
 #ifdef CONFIG_NETFILTER
-static int route6_me_harder(struct sk_buff *skb)
+int ip6_route_me_harder(struct sk_buff *skb)
 {
 	struct ipv6hdr *iph = skb->nh.ipv6h;
 	struct dst_entry *dst;
@@ -150,7 +150,7 @@
 
 	if (dst->error) {
 		if (net_ratelimit())
-			printk(KERN_DEBUG "route6_me_harder: No more route.\n");
+			printk(KERN_DEBUG "ip6_route_me_harder: No more route.\n");
 		return -EINVAL;
 	}
 
@@ -166,7 +166,7 @@
 {
 #ifdef CONFIG_NETFILTER
 	if (skb->nfcache & NFC_ALTERED){
-		if (route6_me_harder(skb) != 0){
+		if (ip6_route_me_harder(skb) != 0){
 			kfree_skb(skb);
 			return -EINVAL;
 		}
diff -urN linux-2.4.21-pre5/net/ipv6/ipv6_sockglue.c linux-2.4.21-pre6/net/ipv6/ipv6_sockglue.c
--- linux-2.4.21-pre5/net/ipv6/ipv6_sockglue.c	Wed Mar 26 16:11:29 2003
+++ linux-2.4.21-pre6/net/ipv6/ipv6_sockglue.c	Wed Mar 26 16:11:40 2003
@@ -21,7 +21,6 @@
  *	o	Return an optlen of the truncated length if need be
  */
 
-#define __NO_VERSION__
 #include <linux/module.h>
 #include <linux/config.h>
 #include <linux/errno.h>
diff -urN linux-2.4.21-pre5/net/ipv6/mcast.c linux-2.4.21-pre6/net/ipv6/mcast.c
--- linux-2.4.21-pre5/net/ipv6/mcast.c	Wed Mar 26 16:11:29 2003
+++ linux-2.4.21-pre6/net/ipv6/mcast.c	Wed Mar 26 16:11:40 2003
@@ -26,7 +26,6 @@
  *		- MLD for link-local addresses.
  */
 
-#define __NO_VERSION__
 #include <linux/config.h>
 #include <linux/module.h>
 #include <linux/errno.h>
diff -urN linux-2.4.21-pre5/net/ipv6/ndisc.c linux-2.4.21-pre6/net/ipv6/ndisc.c
--- linux-2.4.21-pre5/net/ipv6/ndisc.c	Wed Mar 26 16:11:29 2003
+++ linux-2.4.21-pre6/net/ipv6/ndisc.c	Wed Mar 26 16:11:40 2003
@@ -42,7 +42,6 @@
 #define ND_PRINTK2 ND_PRINTK
 #endif
 
-#define __NO_VERSION__
 #include <linux/module.h>
 #include <linux/config.h>
 #include <linux/errno.h>
diff -urN linux-2.4.21-pre5/net/ipv6/netfilter/Config.in linux-2.4.21-pre6/net/ipv6/netfilter/Config.in
--- linux-2.4.21-pre5/net/ipv6/netfilter/Config.in	Thu Nov 28 15:53:15 2002
+++ linux-2.4.21-pre6/net/ipv6/netfilter/Config.in	Wed Mar 26 16:11:40 2003
@@ -18,12 +18,28 @@
 # The simple matches.
   dep_tristate '  limit match support' CONFIG_IP6_NF_MATCH_LIMIT $CONFIG_IP6_NF_IPTABLES
   dep_tristate '  MAC address match support' CONFIG_IP6_NF_MATCH_MAC $CONFIG_IP6_NF_IPTABLES
+  if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+    dep_tristate '  Routing header match support (EXPERIMENTAL)' CONFIG_IP6_NF_MATCH_RT $CONFIG_IP6_NF_IPTABLES
+  fi
+  if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+    dep_tristate '  Hop-by-Hop and Dst opts header match (EXPERIMENTAL)' CONFIG_IP6_NF_MATCH_OPTS $CONFIG_IP6_NF_IPTABLES
+  fi
+  if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+    dep_tristate '  Fragmentation header match support (EXPERIMENTAL)' CONFIG_IP6_NF_MATCH_FRAG $CONFIG_IP6_NF_IPTABLES
+  fi
+  dep_tristate '  HL match support' CONFIG_IP6_NF_MATCH_HL $CONFIG_IP6_NF_IPTABLES
   dep_tristate '  Multiple port match support' CONFIG_IP6_NF_MATCH_MULTIPORT $CONFIG_IP6_NF_IPTABLES
   if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
     dep_tristate '  Owner match support (EXPERIMENTAL)' CONFIG_IP6_NF_MATCH_OWNER $CONFIG_IP6_NF_IPTABLES
   fi
 #  dep_tristate '  MAC address match support' CONFIG_IP6_NF_MATCH_MAC $CONFIG_IP6_NF_IPTABLES
   dep_tristate '  netfilter MARK match support' CONFIG_IP6_NF_MATCH_MARK $CONFIG_IP6_NF_IPTABLES
+  if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+    dep_tristate '  IPv6 Extension Headers Match (EXPERIMENTAL)' CONFIG_IP6_NF_MATCH_IPV6HEADER $CONFIG_IP6_NF_IPTABLES
+  fi
+  if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+    dep_tristate '  AH/ESP match support (EXPERIMENTAL)' CONFIG_IP6_NF_MATCH_AHESP $CONFIG_IP6_NF_IPTABLES
+  fi
   dep_tristate '  Packet Length match support' CONFIG_IP6_NF_MATCH_LENGTH $CONFIG_IP6_NF_IPTABLES
   if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
     dep_tristate '  EUI64 address check (EXPERIMENTAL)' CONFIG_IP6_NF_MATCH_EUI64 $CONFIG_IP6_NF_IPTABLES
diff -urN linux-2.4.21-pre5/net/ipv6/netfilter/Makefile linux-2.4.21-pre6/net/ipv6/netfilter/Makefile
--- linux-2.4.21-pre5/net/ipv6/netfilter/Makefile	Thu Nov 28 15:53:15 2002
+++ linux-2.4.21-pre6/net/ipv6/netfilter/Makefile	Wed Mar 26 16:11:40 2003
@@ -17,6 +17,11 @@
 obj-$(CONFIG_IP6_NF_MATCH_MARK) += ip6t_mark.o
 obj-$(CONFIG_IP6_NF_MATCH_LENGTH) += ip6t_length.o
 obj-$(CONFIG_IP6_NF_MATCH_MAC) += ip6t_mac.o
+obj-$(CONFIG_IP6_NF_MATCH_RT) += ip6t_rt.o
+obj-$(CONFIG_IP6_NF_MATCH_OPTS) += ip6t_hbh.o ip6t_dst.o
+obj-$(CONFIG_IP6_NF_MATCH_IPV6HEADER) += ip6t_ipv6header.o
+obj-$(CONFIG_IP6_NF_MATCH_FRAG) += ip6t_frag.o
+obj-$(CONFIG_IP6_NF_MATCH_AHESP) += ip6t_esp.o ip6t_ah.o
 obj-$(CONFIG_IP6_NF_MATCH_EUI64) += ip6t_eui64.o
 obj-$(CONFIG_IP6_NF_MATCH_MULTIPORT) += ip6t_multiport.o
 obj-$(CONFIG_IP6_NF_MATCH_OWNER) += ip6t_owner.o
@@ -25,5 +30,6 @@
 obj-$(CONFIG_IP6_NF_TARGET_MARK) += ip6t_MARK.o
 obj-$(CONFIG_IP6_NF_QUEUE) += ip6_queue.o
 obj-$(CONFIG_IP6_NF_TARGET_LOG) += ip6t_LOG.o
+obj-$(CONFIG_IP_NF_MATCH_HL) += ip6t_hl.o
 
 include $(TOPDIR)/Rules.make
diff -urN linux-2.4.21-pre5/net/ipv6/netfilter/ip6_queue.c linux-2.4.21-pre6/net/ipv6/netfilter/ip6_queue.c
--- linux-2.4.21-pre5/net/ipv6/netfilter/ip6_queue.c	Thu Nov 28 15:53:15 2002
+++ linux-2.4.21-pre6/net/ipv6/netfilter/ip6_queue.c	Wed Mar 26 16:11:40 2003
@@ -326,45 +326,6 @@
 	return status;
 }
 
-/*
- * Taken from net/ipv6/ip6_output.c
- *
- * We should use the one there, but is defined static
- * so we put this just here and let the things as
- * they are now.
- *
- * If that one is modified, this one should be modified too.
- */
-static int
-route6_me_harder(struct sk_buff *skb)
-{
-	struct ipv6hdr *iph = skb->nh.ipv6h;
-	struct dst_entry *dst;
-	struct flowi fl;
-
-	fl.proto = iph->nexthdr;
-	fl.fl6_dst = &iph->daddr;
-	fl.fl6_src = &iph->saddr;
-	fl.oif = skb->sk ? skb->sk->bound_dev_if : 0;
-	fl.fl6_flowlabel = 0;
-	fl.uli_u.ports.dport = 0;
-	fl.uli_u.ports.sport = 0;
-
-	dst = ip6_route_output(skb->sk, &fl);
-
-	if (dst->error) {
-		if (net_ratelimit())
-			printk(KERN_DEBUG "route6_me_harder: No more route.\n");
-		return -EINVAL;
-	}
-
-	/* Drop old route. */
-	dst_release(skb->dst);
-
-	skb->dst = dst;
-	return 0;
-}
-
 static int
 ipq_mangle_ipv6(ipq_verdict_msg_t *v, struct ipq_queue_entry *e)
 {
@@ -410,7 +371,7 @@
 		struct ipv6hdr *iph = e->skb->nh.ipv6h;
 		if (ipv6_addr_cmp(&iph->daddr, &e->rt_info.daddr) ||
 		    ipv6_addr_cmp(&iph->saddr, &e->rt_info.saddr))
-			return route6_me_harder(e->skb);
+			return ip6_route_me_harder(e->skb);
 	}
 	return 0;
 }
diff -urN linux-2.4.21-pre5/net/ipv6/netfilter/ip6t_ah.c linux-2.4.21-pre6/net/ipv6/netfilter/ip6t_ah.c
--- linux-2.4.21-pre5/net/ipv6/netfilter/ip6t_ah.c	Wed Dec 31 16:00:00 1969
+++ linux-2.4.21-pre6/net/ipv6/netfilter/ip6t_ah.c	Wed Mar 26 16:11:40 2003
@@ -0,0 +1,218 @@
+/* Kernel module to match AH parameters. */
+#include <linux/module.h>
+#include <linux/skbuff.h>
+#include <linux/ipv6.h>
+#include <linux/types.h>
+#include <net/checksum.h>
+#include <net/ipv6.h>
+
+#include <linux/netfilter_ipv6/ip6_tables.h>
+#include <linux/netfilter_ipv6/ip6t_ah.h>
+
+EXPORT_NO_SYMBOLS;
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("IPv6 AH match");
+MODULE_AUTHOR("Andras Kis-Szabo <kisza@sch.bme.hu>");
+
+#if 0
+#define DEBUGP printk
+#else
+#define DEBUGP(format, args...)
+#endif
+
+struct ahhdr {
+       __u8    nexthdr;
+       __u8    hdrlen;
+       __u16   reserved;
+       __u32   spi;
+};
+
+int ipv6_ext_hdr(u8 nexthdr)
+{
+        return ( (nexthdr == NEXTHDR_HOP)       ||
+                 (nexthdr == NEXTHDR_ROUTING)   ||
+                 (nexthdr == NEXTHDR_FRAGMENT)  ||
+                 (nexthdr == NEXTHDR_AUTH)      ||
+                 (nexthdr == NEXTHDR_ESP)       ||
+                 (nexthdr == NEXTHDR_NONE)      ||
+                 (nexthdr == NEXTHDR_DEST) );
+}
+
+/* Returns 1 if the spi is matched by the range, 0 otherwise */
+static inline int
+spi_match(u_int32_t min, u_int32_t max, u_int32_t spi, int invert)
+{
+       int r=0;
+       DEBUGP("ah spi_match:%c 0x%x <= 0x%x <= 0x%x",invert? '!':' ',
+              min,spi,max);
+       r=(spi >= min && spi <= max) ^ invert;
+       DEBUGP(" result %s\n",r? "PASS\n" : "FAILED\n");
+       return r;
+}
+
+static int
+match(const struct sk_buff *skb,
+      const struct net_device *in,
+      const struct net_device *out,
+      const void *matchinfo,
+      int offset,
+      const void *protohdr,
+      u_int16_t datalen,
+      int *hotdrop)
+{
+       struct ahhdr *ah = NULL;
+       const struct ip6t_ah *ahinfo = matchinfo;
+       unsigned int temp;
+       int len;
+       u8 nexthdr;
+       unsigned int ptr;
+       unsigned int hdrlen = 0;
+
+       /*DEBUGP("IPv6 AH entered\n");*/
+       /* if (opt->auth == 0) return 0;
+       * It does not filled on output */
+
+       /* type of the 1st exthdr */
+       nexthdr = skb->nh.ipv6h->nexthdr;
+       /* pointer to the 1st exthdr */
+       ptr = sizeof(struct ipv6hdr);
+       /* available length */
+       len = skb->len - ptr;
+       temp = 0;
+
+        while (ipv6_ext_hdr(nexthdr)) {
+               struct ipv6_opt_hdr *hdr;
+
+              DEBUGP("ipv6_ah header iteration \n");
+
+              /* Is there enough space for the next ext header? */
+                if (len < (int)sizeof(struct ipv6_opt_hdr))
+                        return 0;
+              /* No more exthdr -> evaluate */
+                if (nexthdr == NEXTHDR_NONE) {
+                     break;
+              }
+              /* ESP -> evaluate */
+                if (nexthdr == NEXTHDR_ESP) {
+                     break;
+              }
+
+              hdr=(struct ipv6_opt_hdr *)skb->data+ptr;
+
+              /* Calculate the header length */
+                if (nexthdr == NEXTHDR_FRAGMENT) {
+                        hdrlen = 8;
+                } else if (nexthdr == NEXTHDR_AUTH)
+                        hdrlen = (hdr->hdrlen+2)<<2;
+                else
+                        hdrlen = ipv6_optlen(hdr);
+
+              /* AH -> evaluate */
+                if (nexthdr == NEXTHDR_AUTH) {
+                     temp |= MASK_AH;
+                     break;
+              }
+
+
+              /* set the flag */
+              switch (nexthdr){
+                     case NEXTHDR_HOP:
+                     case NEXTHDR_ROUTING:
+                     case NEXTHDR_FRAGMENT:
+                     case NEXTHDR_AUTH:
+                     case NEXTHDR_DEST:
+                            break;
+                     default:
+                            DEBUGP("ipv6_ah match: unknown nextheader %u\n",nexthdr);
+                            return 0;
+                            break;
+              }
+
+                nexthdr = hdr->nexthdr;
+                len -= hdrlen;
+                ptr += hdrlen;
+		if ( ptr > skb->len ) {
+			DEBUGP("ipv6_ah: new pointer too large! \n");
+			break;
+		}
+        }
+
+       /* AH header not found */
+       if ( temp != MASK_AH ) return 0;
+
+       if (len < (int)sizeof(struct ahhdr)){
+	       *hotdrop = 1;
+       		return 0;
+       }
+
+       ah=skb->data+ptr;
+
+       DEBUGP("IPv6 AH LEN %u %u ", hdrlen, ah->hdrlen);
+       DEBUGP("RES %04X ", ah->reserved);
+       DEBUGP("SPI %u %08X\n", ntohl(ah->spi), ntohl(ah->spi));
+
+       DEBUGP("IPv6 AH spi %02X ",
+       		(spi_match(ahinfo->spis[0], ahinfo->spis[1],
+                           ntohl(ah->spi),
+                           !!(ahinfo->invflags & IP6T_AH_INV_SPI))));
+       DEBUGP("len %02X %04X %02X ",
+       		ahinfo->hdrlen, hdrlen,
+       		(!ahinfo->hdrlen ||
+                           (ahinfo->hdrlen == hdrlen) ^
+                           !!(ahinfo->invflags & IP6T_AH_INV_LEN)));
+       DEBUGP("res %02X %04X %02X\n", 
+       		ahinfo->hdrres, ah->reserved,
+       		!(ahinfo->hdrres && ah->reserved));
+
+       return (ah != NULL)
+              &&
+              (spi_match(ahinfo->spis[0], ahinfo->spis[1],
+                           ntohl(ah->spi),
+                           !!(ahinfo->invflags & IP6T_AH_INV_SPI)))
+              &&
+              (!ahinfo->hdrlen ||
+                           (ahinfo->hdrlen == hdrlen) ^
+                           !!(ahinfo->invflags & IP6T_AH_INV_LEN))
+              &&
+              !(ahinfo->hdrres && ah->reserved);
+}
+
+/* Called when user tries to insert an entry of this type. */
+static int
+checkentry(const char *tablename,
+          const struct ip6t_ip6 *ip,
+          void *matchinfo,
+          unsigned int matchinfosize,
+          unsigned int hook_mask)
+{
+       const struct ip6t_ah *ahinfo = matchinfo;
+
+       if (matchinfosize != IP6T_ALIGN(sizeof(struct ip6t_ah))) {
+              DEBUGP("ip6t_ah: matchsize %u != %u\n",
+                      matchinfosize, IP6T_ALIGN(sizeof(struct ip6t_ah)));
+              return 0;
+       }
+       if (ahinfo->invflags & ~IP6T_AH_INV_MASK) {
+              DEBUGP("ip6t_ah: unknown flags %X\n",
+                      ahinfo->invflags);
+              return 0;
+       }
+
+       return 1;
+}
+
+static struct ip6t_match ah_match
+= { { NULL, NULL }, "ah", &match, &checkentry, NULL, THIS_MODULE };
+
+static int __init init(void)
+{
+       return ip6t_register_match(&ah_match);
+}
+
+static void __exit cleanup(void)
+{
+       ip6t_unregister_match(&ah_match);
+}
+
+module_init(init);
+module_exit(cleanup);
diff -urN linux-2.4.21-pre5/net/ipv6/netfilter/ip6t_dst.c linux-2.4.21-pre6/net/ipv6/netfilter/ip6t_dst.c
--- linux-2.4.21-pre5/net/ipv6/netfilter/ip6t_dst.c	Wed Dec 31 16:00:00 1969
+++ linux-2.4.21-pre6/net/ipv6/netfilter/ip6t_dst.c	Wed Mar 26 16:11:40 2003
@@ -0,0 +1,287 @@
+/* Kernel module to match Hop-by-Hop and Destination parameters. */
+#include <linux/module.h>
+#include <linux/skbuff.h>
+#include <linux/ipv6.h>
+#include <linux/types.h>
+#include <net/checksum.h>
+#include <net/ipv6.h>
+
+#include <asm/byteorder.h>
+
+#include <linux/netfilter_ipv6/ip6_tables.h>
+#include <linux/netfilter_ipv6/ip6t_opts.h>
+
+#define LOW(n)		(n & 0x00FF)
+
+#define HOPBYHOP	0
+
+EXPORT_NO_SYMBOLS;
+MODULE_LICENSE("GPL");
+#if HOPBYHOP
+MODULE_DESCRIPTION("IPv6 HbH match");
+#else
+MODULE_DESCRIPTION("IPv6 DST match");
+#endif
+MODULE_AUTHOR("Andras Kis-Szabo <kisza@sch.bme.hu>");
+
+#if 0
+#define DEBUGP printk
+#else
+#define DEBUGP(format, args...)
+#endif
+
+int ipv6_ext_hdr(u8 nexthdr)
+{
+        return ( (nexthdr == NEXTHDR_HOP)       ||
+                 (nexthdr == NEXTHDR_ROUTING)   ||
+                 (nexthdr == NEXTHDR_FRAGMENT)  ||
+                 (nexthdr == NEXTHDR_AUTH)      ||
+                 (nexthdr == NEXTHDR_ESP)       ||
+                 (nexthdr == NEXTHDR_NONE)      ||
+                 (nexthdr == NEXTHDR_DEST) );
+}
+
+/*
+ * (Type & 0xC0) >> 6
+ * 	0	-> ignorable
+ * 	1	-> must drop the packet
+ * 	2	-> send ICMP PARM PROB regardless and drop packet
+ * 	3	-> Send ICMP if not a multicast address and drop packet
+ *  (Type & 0x20) >> 5
+ *  	0	-> invariant
+ *  	1	-> can change the routing
+ *  (Type & 0x1F) Type
+ *      0	-> PAD0 (only 1 byte!)
+ *      1	-> PAD1 LENGTH info (total length = length + 2)
+ *      C0 | 2	-> JUMBO 4 x x x x ( xxxx > 64k )
+ *      5	-> RTALERT 2 x x
+ */
+
+static int
+match(const struct sk_buff *skb,
+      const struct net_device *in,
+      const struct net_device *out,
+      const void *matchinfo,
+      int offset,
+      const void *protohdr,
+      u_int16_t datalen,
+      int *hotdrop)
+{
+       struct ipv6_opt_hdr *optsh = NULL;
+       const struct ip6t_opts *optinfo = matchinfo;
+       unsigned int temp;
+       unsigned int len;
+       u8 nexthdr;
+       unsigned int ptr;
+       unsigned int hdrlen = 0;
+       unsigned int ret = 0;
+       u_int16_t *optdesc = NULL;
+       
+       /* type of the 1st exthdr */
+       nexthdr = skb->nh.ipv6h->nexthdr;
+       /* pointer to the 1st exthdr */
+       ptr = sizeof(struct ipv6hdr);
+       /* available length */
+       len = skb->len - ptr;
+       temp = 0;
+
+        while (ipv6_ext_hdr(nexthdr)) {
+               struct ipv6_opt_hdr *hdr;
+
+              DEBUGP("ipv6_opts header iteration \n");
+
+              /* Is there enough space for the next ext header? */
+                if (len < (int)sizeof(struct ipv6_opt_hdr))
+                        return 0;
+              /* No more exthdr -> evaluate */
+                if (nexthdr == NEXTHDR_NONE) {
+                     break;
+              }
+              /* ESP -> evaluate */
+                if (nexthdr == NEXTHDR_ESP) {
+                     break;
+              }
+
+              hdr=(void *)(skb->data)+ptr;
+
+              /* Calculate the header length */
+                if (nexthdr == NEXTHDR_FRAGMENT) {
+                        hdrlen = 8;
+                } else if (nexthdr == NEXTHDR_AUTH)
+                        hdrlen = (hdr->hdrlen+2)<<2;
+                else
+                        hdrlen = ipv6_optlen(hdr);
+
+              /* OPTS -> evaluate */
+#if HOPBYHOP
+                if (nexthdr == NEXTHDR_HOP) {
+                     temp |= MASK_HOPOPTS;
+#else
+                if (nexthdr == NEXTHDR_DEST) {
+                     temp |= MASK_DSTOPTS;
+#endif
+                     break;
+              }
+
+
+              /* set the flag */
+              switch (nexthdr){
+                     case NEXTHDR_HOP:
+                     case NEXTHDR_ROUTING:
+                     case NEXTHDR_FRAGMENT:
+                     case NEXTHDR_AUTH:
+                     case NEXTHDR_DEST:
+                            break;
+                     default:
+                            DEBUGP("ipv6_opts match: unknown nextheader %u\n",nexthdr);
+                            return 0;
+                            break;
+              }
+
+                nexthdr = hdr->nexthdr;
+                len -= hdrlen;
+                ptr += hdrlen;
+		if ( ptr > skb->len ) {
+			DEBUGP("ipv6_opts: new pointer is too large! \n");
+			break;
+		}
+        }
+
+       /* OPTIONS header not found */
+#if HOPBYHOP
+       if ( temp != MASK_HOPOPTS ) return 0;
+#else
+       if ( temp != MASK_DSTOPTS ) return 0;
+#endif
+
+       if (len < (int)sizeof(struct ipv6_opt_hdr)){
+	       *hotdrop = 1;
+       		return 0;
+       }
+
+       if (len < hdrlen){
+	       /* Packet smaller than it's length field */
+       		return 0;
+       }
+
+       optsh=(void *)(skb->data)+ptr;
+
+       DEBUGP("IPv6 OPTS LEN %u %u ", hdrlen, optsh->hdrlen);
+
+       DEBUGP("len %02X %04X %02X ",
+       		optinfo->hdrlen, hdrlen,
+       		(!(optinfo->flags & IP6T_OPTS_LEN) ||
+                           ((optinfo->hdrlen == hdrlen) ^
+                           !!(optinfo->invflags & IP6T_OPTS_INV_LEN))));
+
+       ret = (optsh != NULL)
+       		&&
+	      	(!(optinfo->flags & IP6T_OPTS_LEN) ||
+                           ((optinfo->hdrlen == hdrlen) ^
+                           !!(optinfo->invflags & IP6T_OPTS_INV_LEN)));
+
+       temp = len = 0;
+       ptr += 2;
+       hdrlen -= 2;
+       if ( !(optinfo->flags & IP6T_OPTS_OPTS) ){
+	       return ret;
+	} else if (optinfo->flags & IP6T_OPTS_NSTRICT) {
+		DEBUGP("Not strict - not implemented");
+	} else {
+		DEBUGP("Strict ");
+		DEBUGP("#%d ",optinfo->optsnr);
+		for(temp=0; temp<optinfo->optsnr; temp++){
+			optdesc = (void *)(skb->data)+ptr;
+			/* Type check */
+			if ( (unsigned char)*optdesc != 
+				(optinfo->opts[temp] & 0xFF00)>>8 ){
+				DEBUGP("Tbad %02X %02X\n",
+						(unsigned char)*optdesc,
+						(optinfo->opts[temp] &
+						 0xFF00)>>8);
+				return 0;
+			} else {
+				DEBUGP("Tok ");
+			}
+			/* Length check */
+			if (((optinfo->opts[temp] & 0x00FF) != 0xFF) &&
+				(unsigned char)*optdesc != 0){
+				if ( ntohs((u16)*optdesc) != 
+						optinfo->opts[temp] ){
+					DEBUGP("Lbad %02X %04X %04X\n",
+							(unsigned char)*optdesc,
+							ntohs((u16)*optdesc),
+							optinfo->opts[temp]);
+					return 0;
+				} else {
+					DEBUGP("Lok ");
+				}
+			}
+			/* Step to the next */
+			if ((unsigned char)*optdesc == 0){
+				DEBUGP("PAD0 \n");
+				ptr++;
+				hdrlen--;
+			} else {
+				ptr += LOW(ntohs(*optdesc));
+				hdrlen -= LOW(ntohs(*optdesc));
+				DEBUGP("len%04X \n", 
+					LOW(ntohs(*optdesc)));
+			}
+			if (ptr > skb->len || ( !hdrlen && 
+				(temp != optinfo->optsnr - 1))) {
+				DEBUGP("new pointer is too large! \n");
+				break;
+			}
+		}
+		if (temp == optinfo->optsnr)
+			return ret;
+		else return 0;
+	}
+
+	return 0;
+}
+
+/* Called when user tries to insert an entry of this type. */
+static int
+checkentry(const char *tablename,
+          const struct ip6t_ip6 *ip,
+          void *matchinfo,
+          unsigned int matchinfosize,
+          unsigned int hook_mask)
+{
+       const struct ip6t_opts *optsinfo = matchinfo;
+
+       if (matchinfosize != IP6T_ALIGN(sizeof(struct ip6t_opts))) {
+              DEBUGP("ip6t_opts: matchsize %u != %u\n",
+                      matchinfosize, IP6T_ALIGN(sizeof(struct ip6t_opts)));
+              return 0;
+       }
+       if (optsinfo->invflags & ~IP6T_OPTS_INV_MASK) {
+              DEBUGP("ip6t_opts: unknown flags %X\n",
+                      optsinfo->invflags);
+              return 0;
+       }
+
+       return 1;
+}
+
+static struct ip6t_match opts_match
+#if HOPBYHOP
+= { { NULL, NULL }, "hbh", &match, &checkentry, NULL, THIS_MODULE };
+#else
+= { { NULL, NULL }, "dst", &match, &checkentry, NULL, THIS_MODULE };
+#endif
+
+static int __init init(void)
+{
+       return ip6t_register_match(&opts_match);
+}
+
+static void __exit cleanup(void)
+{
+       ip6t_unregister_match(&opts_match);
+}
+
+module_init(init);
+module_exit(cleanup);
diff -urN linux-2.4.21-pre5/net/ipv6/netfilter/ip6t_esp.c linux-2.4.21-pre6/net/ipv6/netfilter/ip6t_esp.c
--- linux-2.4.21-pre5/net/ipv6/netfilter/ip6t_esp.c	Wed Dec 31 16:00:00 1969
+++ linux-2.4.21-pre6/net/ipv6/netfilter/ip6t_esp.c	Wed Mar 26 16:11:40 2003
@@ -0,0 +1,186 @@
+/* Kernel module to match ESP parameters. */
+#include <linux/module.h>
+#include <linux/skbuff.h>
+#include <linux/ipv6.h>
+#include <linux/types.h>
+#include <net/checksum.h>
+#include <net/ipv6.h>
+
+#include <linux/netfilter_ipv6/ip6_tables.h>
+#include <linux/netfilter_ipv6/ip6t_esp.h>
+
+EXPORT_NO_SYMBOLS;
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("IPv6 ESP match");
+MODULE_AUTHOR("Andras Kis-Szabo <kisza@sch.bme.hu>");
+
+#if 0
+#define DEBUGP printk
+#else
+#define DEBUGP(format, args...)
+#endif
+
+struct esphdr {
+	__u32   spi;
+};
+
+int ipv6_ext_hdr(u8 nexthdr)
+{
+        return ( (nexthdr == NEXTHDR_HOP)       ||
+                 (nexthdr == NEXTHDR_ROUTING)   ||
+                 (nexthdr == NEXTHDR_FRAGMENT)  ||
+                 (nexthdr == NEXTHDR_AUTH)      ||
+                 (nexthdr == NEXTHDR_ESP)       ||
+                 (nexthdr == NEXTHDR_NONE)      ||
+                 (nexthdr == NEXTHDR_DEST) );
+}
+
+/* Returns 1 if the spi is matched by the range, 0 otherwise */
+static inline int
+spi_match(u_int32_t min, u_int32_t max, u_int32_t spi, int invert)
+{
+	int r=0;
+        DEBUGP("esp spi_match:%c 0x%x <= 0x%x <= 0x%x",invert? '!':' ',
+        	min,spi,max);
+	r=(spi >= min && spi <= max) ^ invert;
+	DEBUGP(" result %s\n",r? "PASS\n" : "FAILED\n");
+	return r;
+}
+
+static int
+match(const struct sk_buff *skb,
+      const struct net_device *in,
+      const struct net_device *out,
+      const void *matchinfo,
+      int offset,
+      const void *protohdr,
+      u_int16_t datalen,
+      int *hotdrop)
+{
+	struct esphdr *esp = NULL;
+	const struct ip6t_esp *espinfo = matchinfo;
+	unsigned int temp;
+	int len;
+	u8 nexthdr;
+	unsigned int ptr;
+
+	/* Make sure this isn't an evil packet */
+	/*DEBUGP("ipv6_esp entered \n");*/
+
+	/* type of the 1st exthdr */
+	nexthdr = skb->nh.ipv6h->nexthdr;
+	/* pointer to the 1st exthdr */
+	ptr = sizeof(struct ipv6hdr);
+	/* available length */
+	len = skb->len - ptr;
+	temp = 0;
+
+        while (ipv6_ext_hdr(nexthdr)) {
+        	struct ipv6_opt_hdr *hdr;
+        	int hdrlen;
+
+		DEBUGP("ipv6_esp header iteration \n");
+
+		/* Is there enough space for the next ext header? */
+                if (len < (int)sizeof(struct ipv6_opt_hdr))
+                        return 0;
+		/* No more exthdr -> evaluate */
+                if (nexthdr == NEXTHDR_NONE) {
+			break;
+		}
+		/* ESP -> evaluate */
+                if (nexthdr == NEXTHDR_ESP) {
+			temp |= MASK_ESP;
+			break;
+		}
+
+		hdr=(struct ipv6_opt_hdr *)skb->data+ptr;
+
+		/* Calculate the header length */
+                if (nexthdr == NEXTHDR_FRAGMENT) {
+                        hdrlen = 8;
+                } else if (nexthdr == NEXTHDR_AUTH)
+                        hdrlen = (hdr->hdrlen+2)<<2;
+                else
+                        hdrlen = ipv6_optlen(hdr);
+
+		/* set the flag */
+		switch (nexthdr){
+			case NEXTHDR_HOP:
+			case NEXTHDR_ROUTING:
+			case NEXTHDR_FRAGMENT:
+			case NEXTHDR_AUTH:
+			case NEXTHDR_DEST:
+				break;
+			default:
+				DEBUGP("ipv6_esp match: unknown nextheader %u\n",nexthdr);
+				return 0;
+				break;
+		}
+
+                nexthdr = hdr->nexthdr;
+                len -= hdrlen;
+                ptr += hdrlen;
+		if ( ptr > skb->len ) {
+			DEBUGP("ipv6_esp: new pointer too large! \n");
+			break;
+		}
+        }
+
+	/* ESP header not found */
+	if ( temp != MASK_ESP ) return 0;
+
+       if (len < (int)sizeof(struct esphdr)){
+	       *hotdrop = 1;
+       		return 0;
+       }
+
+	esp=skb->data+ptr;
+
+	DEBUGP("IPv6 ESP SPI %u %08X\n", ntohl(esp->spi), ntohl(esp->spi));
+
+	return (esp != NULL)
+		&& spi_match(espinfo->spis[0], espinfo->spis[1],
+			      ntohl(esp->spi),
+			      !!(espinfo->invflags & IP6T_ESP_INV_SPI));
+}
+
+/* Called when user tries to insert an entry of this type. */
+static int
+checkentry(const char *tablename,
+	   const struct ip6t_ip6 *ip,
+	   void *matchinfo,
+	   unsigned int matchinfosize,
+	   unsigned int hook_mask)
+{
+	const struct ip6t_esp *espinfo = matchinfo;
+
+	if (matchinfosize != IP6T_ALIGN(sizeof(struct ip6t_esp))) {
+		DEBUGP("ip6t_esp: matchsize %u != %u\n",
+			 matchinfosize, IP6T_ALIGN(sizeof(struct ip6t_esp)));
+		return 0;
+	}
+	if (espinfo->invflags & ~IP6T_ESP_INV_MASK) {
+		DEBUGP("ip6t_esp: unknown flags %X\n",
+			 espinfo->invflags);
+		return 0;
+	}
+
+	return 1;
+}
+
+static struct ip6t_match esp_match
+= { { NULL, NULL }, "esp", &match, &checkentry, NULL, THIS_MODULE };
+
+static int __init init(void)
+{
+	return ip6t_register_match(&esp_match);
+}
+
+static void __exit cleanup(void)
+{
+	ip6t_unregister_match(&esp_match);
+}
+
+module_init(init);
+module_exit(cleanup);
diff -urN linux-2.4.21-pre5/net/ipv6/netfilter/ip6t_frag.c linux-2.4.21-pre6/net/ipv6/netfilter/ip6t_frag.c
--- linux-2.4.21-pre5/net/ipv6/netfilter/ip6t_frag.c	Wed Dec 31 16:00:00 1969
+++ linux-2.4.21-pre6/net/ipv6/netfilter/ip6t_frag.c	Wed Mar 26 16:11:40 2003
@@ -0,0 +1,250 @@
+/* Kernel module to match FRAG parameters. */
+#include <linux/module.h>
+#include <linux/skbuff.h>
+#include <linux/ipv6.h>
+#include <linux/types.h>
+#include <net/checksum.h>
+#include <net/ipv6.h>
+
+#include <asm/byteorder.h>
+
+#include <linux/netfilter_ipv6/ip6_tables.h>
+#include <linux/netfilter_ipv6/ip6t_frag.h>
+
+EXPORT_NO_SYMBOLS;
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("IPv6 FRAG match");
+MODULE_AUTHOR("Andras Kis-Szabo <kisza@sch.bme.hu>");
+
+#if 0
+#define DEBUGP printk
+#else
+#define DEBUGP(format, args...)
+#endif
+
+#if 0
+#if     BYTE_ORDER == BIG_ENDIAN
+#define IP6F_OFF_MASK       0xfff8  /* mask out offset from _offlg */
+#define IP6F_RESERVED_MASK  0x0006  /* reserved bits in ip6f_offlg */
+#define IP6F_MORE_FRAG      0x0001  /* more-fragments flag */
+#else   /* BYTE_ORDER == LITTLE_ENDIAN */
+#define IP6F_OFF_MASK       0xf8ff  /* mask out offset from _offlg */
+#define IP6F_RESERVED_MASK  0x0600  /* reserved bits in ip6f_offlg */
+#define IP6F_MORE_FRAG      0x0100  /* more-fragments flag */
+#endif
+#endif
+
+#define IP6F_OFF_MASK       0xf8ff  /* mask out offset from _offlg */
+#define IP6F_RESERVED_MASK  0x0600  /* reserved bits in ip6f_offlg */
+#define IP6F_MORE_FRAG      0x0100  /* more-fragments flag */
+
+struct fraghdr {
+       __u8    nexthdr;
+       __u8    hdrlen;
+       __u16   info;
+       __u32   id;
+};
+
+int ipv6_ext_hdr(u8 nexthdr)
+{
+        return ( (nexthdr == NEXTHDR_HOP)       ||
+                 (nexthdr == NEXTHDR_ROUTING)   ||
+                 (nexthdr == NEXTHDR_FRAGMENT)  ||
+                 (nexthdr == NEXTHDR_AUTH)      ||
+                 (nexthdr == NEXTHDR_ESP)       ||
+                 (nexthdr == NEXTHDR_NONE)      ||
+                 (nexthdr == NEXTHDR_DEST) );
+}
+
+/* Returns 1 if the id is matched by the range, 0 otherwise */
+static inline int
+id_match(u_int32_t min, u_int32_t max, u_int32_t id, int invert)
+{
+       int r=0;
+       DEBUGP("frag id_match:%c 0x%x <= 0x%x <= 0x%x",invert? '!':' ',
+              min,id,max);
+       r=(id >= min && id <= max) ^ invert;
+       DEBUGP(" result %s\n",r? "PASS" : "FAILED");
+       return r;
+}
+
+static int
+match(const struct sk_buff *skb,
+      const struct net_device *in,
+      const struct net_device *out,
+      const void *matchinfo,
+      int offset,
+      const void *protohdr,
+      u_int16_t datalen,
+      int *hotdrop)
+{
+       struct fraghdr *frag = NULL;
+       const struct ip6t_frag *fraginfo = matchinfo;
+       unsigned int temp;
+       int len;
+       u8 nexthdr;
+       unsigned int ptr;
+       unsigned int hdrlen = 0;
+
+       /* type of the 1st exthdr */
+       nexthdr = skb->nh.ipv6h->nexthdr;
+       /* pointer to the 1st exthdr */
+       ptr = sizeof(struct ipv6hdr);
+       /* available length */
+       len = skb->len - ptr;
+       temp = 0;
+
+        while (ipv6_ext_hdr(nexthdr)) {
+               struct ipv6_opt_hdr *hdr;
+
+              DEBUGP("ipv6_frag header iteration \n");
+
+              /* Is there enough space for the next ext header? */
+                if (len < (int)sizeof(struct ipv6_opt_hdr))
+                        return 0;
+              /* No more exthdr -> evaluate */
+                if (nexthdr == NEXTHDR_NONE) {
+                     break;
+              }
+              /* ESP -> evaluate */
+                if (nexthdr == NEXTHDR_ESP) {
+                     break;
+              }
+
+              hdr=(struct ipv6_opt_hdr *)skb->data+ptr;
+
+              /* Calculate the header length */
+                if (nexthdr == NEXTHDR_FRAGMENT) {
+                        hdrlen = 8;
+                } else if (nexthdr == NEXTHDR_AUTH)
+                        hdrlen = (hdr->hdrlen+2)<<2;
+                else
+                        hdrlen = ipv6_optlen(hdr);
+
+              /* FRAG -> evaluate */
+                if (nexthdr == NEXTHDR_FRAGMENT) {
+                     temp |= MASK_FRAGMENT;
+                     break;
+              }
+
+
+              /* set the flag */
+              switch (nexthdr){
+                     case NEXTHDR_HOP:
+                     case NEXTHDR_ROUTING:
+                     case NEXTHDR_FRAGMENT:
+                     case NEXTHDR_AUTH:
+                     case NEXTHDR_DEST:
+                            break;
+                     default:
+                            DEBUGP("ipv6_frag match: unknown nextheader %u\n",nexthdr);
+                            return 0;
+                            break;
+              }
+
+                nexthdr = hdr->nexthdr;
+                len -= hdrlen;
+                ptr += hdrlen;
+		if ( ptr > skb->len ) {
+			DEBUGP("ipv6_frag: new pointer too large! \n");
+			break;
+		}
+        }
+
+       /* FRAG header not found */
+       if ( temp != MASK_FRAGMENT ) return 0;
+
+       if (len < (int)sizeof(struct fraghdr)){
+	       *hotdrop = 1;
+       		return 0;
+       }
+
+       frag=skb->data+ptr;
+
+       DEBUGP("IPv6 FRAG LEN %u %u ", hdrlen, frag->hdrlen);
+       DEBUGP("INFO %04X ", frag->info);
+       DEBUGP("OFFSET %04X ", frag->info & IP6F_OFF_MASK);
+       DEBUGP("RES %04X ", frag->info & IP6F_RESERVED_MASK);
+       DEBUGP("MF %04X ", frag->info & IP6F_MORE_FRAG);
+       DEBUGP("ID %u %08X\n", ntohl(frag->id), ntohl(frag->id));
+
+       DEBUGP("IPv6 FRAG id %02X ",
+       		(id_match(fraginfo->ids[0], fraginfo->ids[1],
+                           ntohl(frag->id),
+                           !!(fraginfo->invflags & IP6T_FRAG_INV_IDS))));
+       DEBUGP("len %02X %04X %02X ",
+       		fraginfo->hdrlen, hdrlen,
+       		(!fraginfo->hdrlen ||
+                           (fraginfo->hdrlen == hdrlen) ^
+                           !!(fraginfo->invflags & IP6T_FRAG_INV_LEN)));
+       DEBUGP("res %02X %02X %02X ", 
+       		(fraginfo->flags & IP6T_FRAG_RES), frag->info & IP6F_RESERVED_MASK,
+       		!((fraginfo->flags & IP6T_FRAG_RES) && (frag->info & IP6F_RESERVED_MASK)));
+       DEBUGP("first %02X %02X %02X ", 
+       		(fraginfo->flags & IP6T_FRAG_FST), frag->info & IP6F_OFF_MASK,
+       		!((fraginfo->flags & IP6T_FRAG_FST) && (frag->info & IP6F_OFF_MASK)));
+       DEBUGP("mf %02X %02X %02X ", 
+       		(fraginfo->flags & IP6T_FRAG_MF), frag->info & IP6F_MORE_FRAG,
+       		!((fraginfo->flags & IP6T_FRAG_MF) && !((frag->info & IP6F_MORE_FRAG))));
+       DEBUGP("last %02X %02X %02X\n", 
+       		(fraginfo->flags & IP6T_FRAG_NMF), frag->info & IP6F_MORE_FRAG,
+       		!((fraginfo->flags & IP6T_FRAG_NMF) && (frag->info & IP6F_MORE_FRAG)));
+
+       return (frag != NULL)
+       		&&
+       		(id_match(fraginfo->ids[0], fraginfo->ids[1],
+                           ntohl(frag->id),
+                           !!(fraginfo->invflags & IP6T_FRAG_INV_IDS)))
+		&&
+	      	(!fraginfo->hdrlen ||
+                           (fraginfo->hdrlen == hdrlen) ^
+                           !!(fraginfo->invflags & IP6T_FRAG_INV_LEN))
+		&&
+		!((fraginfo->flags & IP6T_FRAG_RES) && (frag->info & IP6F_RESERVED_MASK))
+		&&
+		!((fraginfo->flags & IP6T_FRAG_FST) && (frag->info & IP6F_OFF_MASK))
+		&&
+		!((fraginfo->flags & IP6T_FRAG_MF) && !((frag->info & IP6F_MORE_FRAG)))
+		&&
+		!((fraginfo->flags & IP6T_FRAG_NMF) && (frag->info & IP6F_MORE_FRAG));
+}
+
+/* Called when user tries to insert an entry of this type. */
+static int
+checkentry(const char *tablename,
+          const struct ip6t_ip6 *ip,
+          void *matchinfo,
+          unsigned int matchinfosize,
+          unsigned int hook_mask)
+{
+       const struct ip6t_frag *fraginfo = matchinfo;
+
+       if (matchinfosize != IP6T_ALIGN(sizeof(struct ip6t_frag))) {
+              DEBUGP("ip6t_frag: matchsize %u != %u\n",
+                      matchinfosize, IP6T_ALIGN(sizeof(struct ip6t_frag)));
+              return 0;
+       }
+       if (fraginfo->invflags & ~IP6T_FRAG_INV_MASK) {
+              DEBUGP("ip6t_frag: unknown flags %X\n",
+                      fraginfo->invflags);
+              return 0;
+       }
+
+       return 1;
+}
+
+static struct ip6t_match frag_match
+= { { NULL, NULL }, "frag", &match, &checkentry, NULL, THIS_MODULE };
+
+static int __init init(void)
+{
+       return ip6t_register_match(&frag_match);
+}
+
+static void __exit cleanup(void)
+{
+       ip6t_unregister_match(&frag_match);
+}
+
+module_init(init);
+module_exit(cleanup);
diff -urN linux-2.4.21-pre5/net/ipv6/netfilter/ip6t_hbh.c linux-2.4.21-pre6/net/ipv6/netfilter/ip6t_hbh.c
--- linux-2.4.21-pre5/net/ipv6/netfilter/ip6t_hbh.c	Wed Dec 31 16:00:00 1969
+++ linux-2.4.21-pre6/net/ipv6/netfilter/ip6t_hbh.c	Wed Mar 26 16:11:40 2003
@@ -0,0 +1,287 @@
+/* Kernel module to match Hop-by-Hop and Destination parameters. */
+#include <linux/module.h>
+#include <linux/skbuff.h>
+#include <linux/ipv6.h>
+#include <linux/types.h>
+#include <net/checksum.h>
+#include <net/ipv6.h>
+
+#include <asm/byteorder.h>
+
+#include <linux/netfilter_ipv6/ip6_tables.h>
+#include <linux/netfilter_ipv6/ip6t_opts.h>
+
+#define LOW(n)		(n & 0x00FF)
+
+#define HOPBYHOP	1
+
+EXPORT_NO_SYMBOLS;
+MODULE_LICENSE("GPL");
+#if HOPBYHOP
+MODULE_DESCRIPTION("IPv6 HbH match");
+#else
+MODULE_DESCRIPTION("IPv6 DST match");
+#endif
+MODULE_AUTHOR("Andras Kis-Szabo <kisza@sch.bme.hu>");
+
+#if 0
+#define DEBUGP printk
+#else
+#define DEBUGP(format, args...)
+#endif
+
+int ipv6_ext_hdr(u8 nexthdr)
+{
+        return ( (nexthdr == NEXTHDR_HOP)       ||
+                 (nexthdr == NEXTHDR_ROUTING)   ||
+                 (nexthdr == NEXTHDR_FRAGMENT)  ||
+                 (nexthdr == NEXTHDR_AUTH)      ||
+                 (nexthdr == NEXTHDR_ESP)       ||
+                 (nexthdr == NEXTHDR_NONE)      ||
+                 (nexthdr == NEXTHDR_DEST) );
+}
+
+/*
+ * (Type & 0xC0) >> 6
+ * 	0	-> ignorable
+ * 	1	-> must drop the packet
+ * 	2	-> send ICMP PARM PROB regardless and drop packet
+ * 	3	-> Send ICMP if not a multicast address and drop packet
+ *  (Type & 0x20) >> 5
+ *  	0	-> invariant
+ *  	1	-> can change the routing
+ *  (Type & 0x1F) Type
+ *      0	-> PAD0 (only 1 byte!)
+ *      1	-> PAD1 LENGTH info (total length = length + 2)
+ *      C0 | 2	-> JUMBO 4 x x x x ( xxxx > 64k )
+ *      5	-> RTALERT 2 x x
+ */
+
+static int
+match(const struct sk_buff *skb,
+      const struct net_device *in,
+      const struct net_device *out,
+      const void *matchinfo,
+      int offset,
+      const void *protohdr,
+      u_int16_t datalen,
+      int *hotdrop)
+{
+       struct ipv6_opt_hdr *optsh = NULL;
+       const struct ip6t_opts *optinfo = matchinfo;
+       unsigned int temp;
+       unsigned int len;
+       u8 nexthdr;
+       unsigned int ptr;
+       unsigned int hdrlen = 0;
+       unsigned int ret = 0;
+       u_int16_t *optdesc = NULL;
+       
+       /* type of the 1st exthdr */
+       nexthdr = skb->nh.ipv6h->nexthdr;
+       /* pointer to the 1st exthdr */
+       ptr = sizeof(struct ipv6hdr);
+       /* available length */
+       len = skb->len - ptr;
+       temp = 0;
+
+        while (ipv6_ext_hdr(nexthdr)) {
+               struct ipv6_opt_hdr *hdr;
+
+              DEBUGP("ipv6_opts header iteration \n");
+
+              /* Is there enough space for the next ext header? */
+                if (len < (int)sizeof(struct ipv6_opt_hdr))
+                        return 0;
+              /* No more exthdr -> evaluate */
+                if (nexthdr == NEXTHDR_NONE) {
+                     break;
+              }
+              /* ESP -> evaluate */
+                if (nexthdr == NEXTHDR_ESP) {
+                     break;
+              }
+
+              hdr=(void *)(skb->data)+ptr;
+
+              /* Calculate the header length */
+                if (nexthdr == NEXTHDR_FRAGMENT) {
+                        hdrlen = 8;
+                } else if (nexthdr == NEXTHDR_AUTH)
+                        hdrlen = (hdr->hdrlen+2)<<2;
+                else
+                        hdrlen = ipv6_optlen(hdr);
+
+              /* OPTS -> evaluate */
+#if HOPBYHOP
+                if (nexthdr == NEXTHDR_HOP) {
+                     temp |= MASK_HOPOPTS;
+#else
+                if (nexthdr == NEXTHDR_DEST) {
+                     temp |= MASK_DSTOPTS;
+#endif
+                     break;
+              }
+
+
+              /* set the flag */
+              switch (nexthdr){
+                     case NEXTHDR_HOP:
+                     case NEXTHDR_ROUTING:
+                     case NEXTHDR_FRAGMENT:
+                     case NEXTHDR_AUTH:
+                     case NEXTHDR_DEST:
+                            break;
+                     default:
+                            DEBUGP("ipv6_opts match: unknown nextheader %u\n",nexthdr);
+                            return 0;
+                            break;
+              }
+
+                nexthdr = hdr->nexthdr;
+                len -= hdrlen;
+                ptr += hdrlen;
+		if ( ptr > skb->len ) {
+			DEBUGP("ipv6_opts: new pointer is too large! \n");
+			break;
+		}
+        }
+
+       /* OPTIONS header not found */
+#if HOPBYHOP
+       if ( temp != MASK_HOPOPTS ) return 0;
+#else
+       if ( temp != MASK_DSTOPTS ) return 0;
+#endif
+
+       if (len < (int)sizeof(struct ipv6_opt_hdr)){
+	       *hotdrop = 1;
+       		return 0;
+       }
+
+       if (len < hdrlen){
+	       /* Packet smaller than it's length field */
+       		return 0;
+       }
+
+       optsh=(void *)(skb->data)+ptr;
+
+       DEBUGP("IPv6 OPTS LEN %u %u ", hdrlen, optsh->hdrlen);
+
+       DEBUGP("len %02X %04X %02X ",
+       		optinfo->hdrlen, hdrlen,
+       		(!(optinfo->flags & IP6T_OPTS_LEN) ||
+                           ((optinfo->hdrlen == hdrlen) ^
+                           !!(optinfo->invflags & IP6T_OPTS_INV_LEN))));
+
+       ret = (optsh != NULL)
+       		&&
+	      	(!(optinfo->flags & IP6T_OPTS_LEN) ||
+                           ((optinfo->hdrlen == hdrlen) ^
+                           !!(optinfo->invflags & IP6T_OPTS_INV_LEN)));
+
+       temp = len = 0;
+       ptr += 2;
+       hdrlen -= 2;
+       if ( !(optinfo->flags & IP6T_OPTS_OPTS) ){
+	       return ret;
+	} else if (optinfo->flags & IP6T_OPTS_NSTRICT) {
+		DEBUGP("Not strict - not implemented");
+	} else {
+		DEBUGP("Strict ");
+		DEBUGP("#%d ",optinfo->optsnr);
+		for(temp=0; temp<optinfo->optsnr; temp++){
+			optdesc = (void *)(skb->data)+ptr;
+			/* Type check */
+			if ( (unsigned char)*optdesc != 
+				(optinfo->opts[temp] & 0xFF00)>>8 ){
+				DEBUGP("Tbad %02X %02X\n",
+						(unsigned char)*optdesc,
+						(optinfo->opts[temp] &
+						 0xFF00)>>8);
+				return 0;
+			} else {
+				DEBUGP("Tok ");
+			}
+			/* Length check */
+			if (((optinfo->opts[temp] & 0x00FF) != 0xFF) &&
+				(unsigned char)*optdesc != 0){
+				if ( ntohs((u16)*optdesc) != 
+						optinfo->opts[temp] ){
+					DEBUGP("Lbad %02X %04X %04X\n",
+							(unsigned char)*optdesc,
+							ntohs((u16)*optdesc),
+							optinfo->opts[temp]);
+					return 0;
+				} else {
+					DEBUGP("Lok ");
+				}
+			}
+			/* Step to the next */
+			if ((unsigned char)*optdesc == 0){
+				DEBUGP("PAD0 \n");
+				ptr++;
+				hdrlen--;
+			} else {
+				ptr += LOW(ntohs(*optdesc));
+				hdrlen -= LOW(ntohs(*optdesc));
+				DEBUGP("len%04X \n", 
+					LOW(ntohs(*optdesc)));
+			}
+			if (ptr > skb->len || ( !hdrlen && 
+				(temp != optinfo->optsnr - 1))) {
+				DEBUGP("new pointer is too large! \n");
+				break;
+			}
+		}
+		if (temp == optinfo->optsnr)
+			return ret;
+		else return 0;
+	}
+
+	return 0;
+}
+
+/* Called when user tries to insert an entry of this type. */
+static int
+checkentry(const char *tablename,
+          const struct ip6t_ip6 *ip,
+          void *matchinfo,
+          unsigned int matchinfosize,
+          unsigned int hook_mask)
+{
+       const struct ip6t_opts *optsinfo = matchinfo;
+
+       if (matchinfosize != IP6T_ALIGN(sizeof(struct ip6t_opts))) {
+              DEBUGP("ip6t_opts: matchsize %u != %u\n",
+                      matchinfosize, IP6T_ALIGN(sizeof(struct ip6t_opts)));
+              return 0;
+       }
+       if (optsinfo->invflags & ~IP6T_OPTS_INV_MASK) {
+              DEBUGP("ip6t_opts: unknown flags %X\n",
+                      optsinfo->invflags);
+              return 0;
+       }
+
+       return 1;
+}
+
+static struct ip6t_match opts_match
+#if HOPBYHOP
+= { { NULL, NULL }, "hbh", &match, &checkentry, NULL, THIS_MODULE };
+#else
+= { { NULL, NULL }, "dst", &match, &checkentry, NULL, THIS_MODULE };
+#endif
+
+static int __init init(void)
+{
+       return ip6t_register_match(&opts_match);
+}
+
+static void __exit cleanup(void)
+{
+       ip6t_unregister_match(&opts_match);
+}
+
+module_init(init);
+module_exit(cleanup);
diff -urN linux-2.4.21-pre5/net/ipv6/netfilter/ip6t_hl.c linux-2.4.21-pre6/net/ipv6/netfilter/ip6t_hl.c
--- linux-2.4.21-pre5/net/ipv6/netfilter/ip6t_hl.c	Wed Dec 31 16:00:00 1969
+++ linux-2.4.21-pre6/net/ipv6/netfilter/ip6t_hl.c	Wed Mar 26 16:11:40 2003
@@ -0,0 +1,74 @@
+/*
+ * Hop Limit matching module
+ * Maciej Soltysiak <solt@dns.toxicfilms.tv>
+ * Based on HW's ttl module
+ *
+ * This software is distributed under the terms  GNU GPL
+ */
+
+#include <linux/module.h>
+#include <linux/skbuff.h>
+
+#include <linux/netfilter_ipv6/ip6t_hl.h>
+#include <linux/netfilter_ipv6/ip6_tables.h>
+
+MODULE_AUTHOR("Maciej Soltysiak <solt@dns.toxicfilms.tv>");
+MODULE_DESCRIPTION("IP tables Hop Limit matching module");
+MODULE_LICENSE("GPL");
+
+static int match(const struct sk_buff *skb, const struct net_device *in,
+		 const struct net_device *out, const void *matchinfo,
+		 int offset, const void *hdr, u_int16_t datalen,
+		 int *hotdrop)
+{
+	const struct ip6t_hl_info *info = matchinfo;
+	const struct ipv6hdr *ip6h = skb->nh.ipv6h;
+
+	switch (info->mode) {
+		case IP6T_HL_EQ:
+			return (ip6h->hop_limit == info->hop_limit);
+			break;
+		case IP6T_HL_NE:
+			return (!(ip6h->hop_limit == info->hop_limit));
+			break;
+		case IP6T_HL_LT:
+			return (ip6h->hop_limit < info->hop_limit);
+			break;
+		case IP6T_HL_GT:
+			return (ip6h->hop_limit > info->hop_limit);
+			break;
+		default:
+			printk(KERN_WARNING "ip6t_hl: unknown mode %d\n", 
+				info->mode);
+			return 0;
+	}
+
+	return 0;
+}
+
+static int checkentry(const char *tablename, const struct ip6t_ip6 *ip,
+		      void *matchinfo, unsigned int matchsize,
+		      unsigned int hook_mask)
+{
+	if (matchsize != IP6T_ALIGN(sizeof(struct ip6t_hl_info)))
+		return 0;
+
+	return 1;
+}
+
+static struct ip6t_match hl_match = { { NULL, NULL }, "hl", &match,
+		&checkentry, NULL, THIS_MODULE };
+
+static int __init init(void)
+{
+	return ip6t_register_match(&hl_match);
+}
+
+static void __exit fini(void)
+{
+	ip6t_unregister_match(&hl_match);
+
+}
+
+module_init(init);
+module_exit(fini);
diff -urN linux-2.4.21-pre5/net/ipv6/netfilter/ip6t_ipv6header.c linux-2.4.21-pre6/net/ipv6/netfilter/ip6t_ipv6header.c
--- linux-2.4.21-pre5/net/ipv6/netfilter/ip6t_ipv6header.c	Wed Dec 31 16:00:00 1969
+++ linux-2.4.21-pre6/net/ipv6/netfilter/ip6t_ipv6header.c	Wed Mar 26 16:11:40 2003
@@ -0,0 +1,222 @@
+/* ipv6header match - matches IPv6 packets based
+on whether they contain certain headers */
+
+/* Original idea: Brad Chapman 
+ * Rewritten by: Andras Kis-Szabo <kisza@sch.bme.hu> */
+
+#include <linux/module.h>
+#include <linux/skbuff.h>
+#include <linux/ipv6.h>
+#include <linux/types.h>
+#include <net/checksum.h>
+#include <net/ipv6.h>
+
+#include <linux/netfilter_ipv6/ip6_tables.h>
+#include <linux/netfilter_ipv6/ip6t_ipv6header.h>
+
+EXPORT_NO_SYMBOLS;
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("IPv6 headers match");
+MODULE_AUTHOR("Andras Kis-Szabo <kisza@sch.bme.hu>");
+
+#if 0
+#define DEBUGP printk
+#else
+#define DEBUGP(format, args...)
+#endif
+
+int ipv6_ext_hdr(u8 nexthdr)
+{
+        return ( (nexthdr == NEXTHDR_HOP)       ||
+                 (nexthdr == NEXTHDR_ROUTING)   ||
+                 (nexthdr == NEXTHDR_FRAGMENT)  ||
+                 (nexthdr == NEXTHDR_AUTH)      ||
+                 (nexthdr == NEXTHDR_ESP)       ||
+                 (nexthdr == NEXTHDR_NONE)      ||
+                 (nexthdr == NEXTHDR_DEST) );
+}
+
+static int
+ipv6header_match(const struct sk_buff *skb,
+		 const struct net_device *in,
+		 const struct net_device *out,
+		 const void *matchinfo,
+		 int offset,
+		 const void *protohdr,
+		 u_int16_t datalen,
+		 int *hotdrop)
+{
+	const struct ip6t_ipv6header_info *info = matchinfo;
+	unsigned int temp;
+	int len;
+	u8 nexthdr;
+	unsigned int ptr;
+        struct inet6_skb_parm *opt = (struct inet6_skb_parm *)skb->cb;
+
+	/* Make sure this isn't an evil packet */
+	DEBUGP("ipv6_header entered \n");
+
+	/* type of the 1st exthdr */
+	nexthdr = skb->nh.ipv6h->nexthdr;
+	/* pointer to the 1st exthdr */
+	ptr = sizeof(struct ipv6hdr);
+	/* available length */
+	len = skb->len - ptr;
+	temp = 0;
+
+	DEBUGP("ipv6_header nexthdr %02X \n",nexthdr);
+	DEBUGP("ipv6_header ptr %08X \n",ptr);
+	DEBUGP("ipv6_header skblen %04X \n",skb->len);
+	DEBUGP("ipv6_header skbdatalen %04X \n",skb->data_len);
+	DEBUGP("ipv6_header len %04X \n",len);
+#if 0
+	for (temp=0;temp<skb->len;temp++){
+		if (!(temp % 16 )) DEBUGP("\nipv6_header data ");
+		DEBUGP("%02X ",skb->data[temp]);
+	}
+#endif
+	DEBUGP("\nipv6_header h.raw %02X %02X %02X %02X \n",
+			skb->h.raw[0],
+			skb->h.raw[1],
+			skb->h.raw[2],
+			skb->h.raw[3]);
+	DEBUGP("ipv6_header nh.raw %02X %02X %02X %02X \n",
+			skb->nh.raw[0],
+			skb->nh.raw[1],
+			skb->nh.raw[2],
+			skb->nh.raw[3]);
+	DEBUGP("ipv6_header CB %02X %02X %02X %02X %02X %02X %02X \n",
+			opt->iif,
+			opt->ra,
+			opt->hop,
+			opt->auth,
+			opt->dst0,
+			opt->srcrt,
+			opt->dst1);
+
+	temp = 0;
+
+        while (ipv6_ext_hdr(nexthdr)) {
+        	struct ipv6_opt_hdr *hdr;
+        	int hdrlen;
+
+		DEBUGP("ipv6_header header iteration \n");
+
+		/* Is there enough space for the next ext header? */
+                if (len < (int)sizeof(struct ipv6_opt_hdr))
+                        return 0;
+		/* No more exthdr -> evaluate */
+                if (nexthdr == NEXTHDR_NONE) {
+			temp |= MASK_NONE;
+			break;
+		}
+		/* ESP -> evaluate */
+                if (nexthdr == NEXTHDR_ESP) {
+			temp |= MASK_ESP;
+			break;
+		}
+
+		hdr=(struct ipv6_opt_hdr *)skb->data+ptr;
+
+		/* Calculate the header length */
+                if (nexthdr == NEXTHDR_FRAGMENT) {
+                        hdrlen = 8;
+                } else if (nexthdr == NEXTHDR_AUTH)
+                        hdrlen = (hdr->hdrlen+2)<<2;
+                else
+                        hdrlen = ipv6_optlen(hdr);
+
+		DEBUGP("ipv6_header hdrlen %04X \n",hdrlen);
+
+		/* set the flag */
+		switch (nexthdr){
+			case NEXTHDR_HOP:
+				temp |= MASK_HOPOPTS;
+				break;
+			case NEXTHDR_ROUTING:
+				temp |= MASK_ROUTING;
+				break;
+			case NEXTHDR_FRAGMENT:
+				temp |= MASK_FRAGMENT;
+				break;
+			case NEXTHDR_AUTH:
+				temp |= MASK_AH;
+				break;
+			case NEXTHDR_DEST:
+				temp |= MASK_DSTOPTS;
+				break;
+			default:
+				DEBUGP("IPV6HEADER match: unknown nextheader %u\n",nexthdr);
+				return 0;
+				break;
+		}
+
+                nexthdr = hdr->nexthdr;
+                len -= hdrlen;
+                ptr += hdrlen;
+		if ( ptr > skb->len ) {
+			DEBUGP("ipv6_header new ptr %04X \n",ptr);
+			break;
+		}
+        }
+
+	if ( (nexthdr != NEXTHDR_NONE ) && (nexthdr != NEXTHDR_ESP) )
+		temp |= MASK_PROTO;
+
+	DEBUGP ("ipv6header: %02X %02X \n", temp, info->matchflags);
+
+	if (info->modeflag)
+		return (!( (temp & info->matchflags)
+			^ info->matchflags) ^ info->invflags);
+	else
+		return (!( temp ^ info->matchflags) ^ info->invflags);
+}
+
+static int
+ipv6header_checkentry(const char *tablename,
+		      const struct ip6t_ip6 *ip,
+		      void *matchinfo,
+		      unsigned int matchsize,
+		      unsigned int hook_mask)
+{
+	/* Check for obvious errors */
+	/* This match is valid in all hooks! */
+	if (matchsize != IP6T_ALIGN(sizeof(struct ip6t_ipv6header_info))) {
+		DEBUGP("ip6t_ipv6header: matchsize != %u\n",
+			 IP6T_ALIGN(sizeof(struct ip6t_ipv6header_info)));
+		return 0;
+	}
+
+	return 1;
+}
+
+static void
+ipv6header_destroy(void *matchinfo,
+		   unsigned int matchinfosize)
+{
+	return;
+}
+
+static struct ip6t_match
+ip6t_ipv6header_match = {
+	{ NULL, NULL },
+	"ipv6header",
+	&ipv6header_match,
+	&ipv6header_checkentry,
+	&ipv6header_destroy,
+	THIS_MODULE
+};
+
+static int  __init ipv6header_init(void)
+{
+	return ip6t_register_match(&ip6t_ipv6header_match);
+}
+
+static void __exit ipv6header_exit(void)
+{
+	ip6t_unregister_match(&ip6t_ipv6header_match);
+}
+
+module_init(ipv6header_init);
+module_exit(ipv6header_exit);
+
diff -urN linux-2.4.21-pre5/net/ipv6/netfilter/ip6t_rt.c linux-2.4.21-pre6/net/ipv6/netfilter/ip6t_rt.c
--- linux-2.4.21-pre5/net/ipv6/netfilter/ip6t_rt.c	Wed Dec 31 16:00:00 1969
+++ linux-2.4.21-pre6/net/ipv6/netfilter/ip6t_rt.c	Wed Mar 26 16:11:40 2003
@@ -0,0 +1,305 @@
+/* Kernel module to match ROUTING parameters. */
+#include <linux/module.h>
+#include <linux/skbuff.h>
+#include <linux/ipv6.h>
+#include <linux/types.h>
+#include <net/checksum.h>
+#include <net/ipv6.h>
+
+#include <asm/byteorder.h>
+
+#include <linux/netfilter_ipv6/ip6_tables.h>
+#include <linux/netfilter_ipv6/ip6t_rt.h>
+
+EXPORT_NO_SYMBOLS;
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("IPv6 RT match");
+MODULE_AUTHOR("Andras Kis-Szabo <kisza@sch.bme.hu>");
+
+#if 0
+#define DEBUGP printk
+#else
+#define DEBUGP(format, args...)
+#endif
+
+int ipv6_ext_hdr(u8 nexthdr)
+{
+        return ( (nexthdr == NEXTHDR_HOP)       ||
+                 (nexthdr == NEXTHDR_ROUTING)   ||
+                 (nexthdr == NEXTHDR_FRAGMENT)  ||
+                 (nexthdr == NEXTHDR_AUTH)      ||
+                 (nexthdr == NEXTHDR_ESP)       ||
+                 (nexthdr == NEXTHDR_NONE)      ||
+                 (nexthdr == NEXTHDR_DEST) );
+}
+
+/* Returns 1 if the id is matched by the range, 0 otherwise */
+static inline int
+segsleft_match(u_int32_t min, u_int32_t max, u_int32_t id, int invert)
+{
+       int r=0;
+       DEBUGP("rt segsleft_match:%c 0x%x <= 0x%x <= 0x%x",invert? '!':' ',
+              min,id,max);
+       r=(id >= min && id <= max) ^ invert;
+       DEBUGP(" result %s\n",r? "PASS" : "FAILED");
+       return r;
+}
+
+static int
+match(const struct sk_buff *skb,
+      const struct net_device *in,
+      const struct net_device *out,
+      const void *matchinfo,
+      int offset,
+      const void *protohdr,
+      u_int16_t datalen,
+      int *hotdrop)
+{
+       struct ipv6_rt_hdr *route = NULL;
+       const struct ip6t_rt *rtinfo = matchinfo;
+       unsigned int temp;
+       unsigned int len;
+       u8 nexthdr;
+       unsigned int ptr;
+       unsigned int hdrlen = 0;
+       unsigned int ret = 0;
+
+       /* type of the 1st exthdr */
+       nexthdr = skb->nh.ipv6h->nexthdr;
+       /* pointer to the 1st exthdr */
+       ptr = sizeof(struct ipv6hdr);
+       /* available length */
+       len = skb->len - ptr;
+       temp = 0;
+
+        while (ipv6_ext_hdr(nexthdr)) {
+               struct ipv6_opt_hdr *hdr;
+
+              DEBUGP("ipv6_rt header iteration \n");
+
+              /* Is there enough space for the next ext header? */
+                if (len < (int)sizeof(struct ipv6_opt_hdr))
+                        return 0;
+              /* No more exthdr -> evaluate */
+                if (nexthdr == NEXTHDR_NONE) {
+                     break;
+              }
+              /* ESP -> evaluate */
+                if (nexthdr == NEXTHDR_ESP) {
+                     break;
+              }
+
+              hdr=(struct ipv6_opt_hdr *)skb->data+ptr;
+
+              /* Calculate the header length */
+                if (nexthdr == NEXTHDR_FRAGMENT) {
+                        hdrlen = 8;
+                } else if (nexthdr == NEXTHDR_AUTH)
+                        hdrlen = (hdr->hdrlen+2)<<2;
+                else
+                        hdrlen = ipv6_optlen(hdr);
+
+              /* ROUTING -> evaluate */
+                if (nexthdr == NEXTHDR_ROUTING) {
+                     temp |= MASK_ROUTING;
+                     break;
+              }
+
+
+              /* set the flag */
+              switch (nexthdr){
+                     case NEXTHDR_HOP:
+                     case NEXTHDR_ROUTING:
+                     case NEXTHDR_FRAGMENT:
+                     case NEXTHDR_AUTH:
+                     case NEXTHDR_DEST:
+                            break;
+                     default:
+                            DEBUGP("ipv6_rt match: unknown nextheader %u\n",nexthdr);
+                            return 0;
+                            break;
+              }
+
+                nexthdr = hdr->nexthdr;
+                len -= hdrlen;
+                ptr += hdrlen;
+		if ( ptr > skb->len ) {
+			DEBUGP("ipv6_rt: new pointer is too large! \n");
+			break;
+		}
+        }
+
+       /* ROUTING header not found */
+       if ( temp != MASK_ROUTING ) return 0;
+
+       if (len < (int)sizeof(struct ipv6_rt_hdr)){
+	       *hotdrop = 1;
+       		return 0;
+       }
+
+       if (len < hdrlen){
+	       /* Pcket smaller than its length field */
+       		return 0;
+       }
+
+       route=skb->data+ptr;
+
+       DEBUGP("IPv6 RT LEN %u %u ", hdrlen, route->hdrlen);
+       DEBUGP("TYPE %04X ", route->type);
+       DEBUGP("SGS_LEFT %u %08X\n", ntohl(route->segments_left), ntohl(route->segments_left));
+
+       DEBUGP("IPv6 RT segsleft %02X ",
+       		(segsleft_match(rtinfo->segsleft[0], rtinfo->segsleft[1],
+                           ntohl(route->segments_left),
+                           !!(rtinfo->invflags & IP6T_RT_INV_SGS))));
+       DEBUGP("type %02X %02X %02X ",
+       		rtinfo->rt_type, route->type, 
+       		(!(rtinfo->flags & IP6T_RT_TYP) ||
+                           ((rtinfo->rt_type == route->type) ^
+                           !!(rtinfo->invflags & IP6T_RT_INV_TYP))));
+       DEBUGP("len %02X %04X %02X ",
+       		rtinfo->hdrlen, hdrlen,
+       		(!(rtinfo->flags & IP6T_RT_LEN) ||
+                           ((rtinfo->hdrlen == hdrlen) ^
+                           !!(rtinfo->invflags & IP6T_RT_INV_LEN))));
+       DEBUGP("res %02X %02X %02X ", 
+       		(rtinfo->flags & IP6T_RT_RES), ((struct rt0_hdr *)route)->bitmap,
+       		!((rtinfo->flags & IP6T_RT_RES) && (((struct rt0_hdr *)route)->bitmap)));
+
+       ret = (route != NULL)
+       		&&
+       		(segsleft_match(rtinfo->segsleft[0], rtinfo->segsleft[1],
+                           ntohl(route->segments_left),
+                           !!(rtinfo->invflags & IP6T_RT_INV_SGS)))
+		&&
+	      	(!(rtinfo->flags & IP6T_RT_LEN) ||
+                           ((rtinfo->hdrlen == hdrlen) ^
+                           !!(rtinfo->invflags & IP6T_RT_INV_LEN)))
+		&&
+       		(!(rtinfo->flags & IP6T_RT_TYP) ||
+                           ((rtinfo->rt_type == route->type) ^
+                           !!(rtinfo->invflags & IP6T_RT_INV_TYP)))
+		&&
+       		!((rtinfo->flags & IP6T_RT_RES) && (((struct rt0_hdr *)route)->bitmap));
+
+	DEBUGP("#%d ",rtinfo->addrnr);
+       temp = len = ptr = 0;
+       if ( !(rtinfo->flags & IP6T_RT_FST) ){
+	       return ret;
+	} else if (rtinfo->flags & IP6T_RT_FST_NSTRICT) {
+		DEBUGP("Not strict ");
+		if ( rtinfo->addrnr > (unsigned int)((hdrlen-8)/16) ){
+			DEBUGP("There isn't enough space\n");
+			return 0;
+		} else {
+			DEBUGP("#%d ",rtinfo->addrnr);
+			ptr = 0;
+			for(temp=0; temp<(unsigned int)((hdrlen-8)/16); temp++){
+				len = 0;
+				while ((u8)(((struct rt0_hdr *)route)->
+						addr[temp].s6_addr[len]) ==
+					(u8)(rtinfo->addrs[ptr].s6_addr[len])){
+					DEBUGP("%02X?%02X ",
+		(u8)(((struct rt0_hdr *)route)->addr[temp].s6_addr[len]),
+					(u8)(rtinfo->addrs[ptr].s6_addr[len]));
+					len++;
+					if ( len == 16 ) break;
+				}
+				if (len==16) {
+					DEBUGP("ptr=%d temp=%d;\n",ptr,temp);
+					ptr++;
+				} else {
+					DEBUGP("%02X?%02X ",
+		(u8)(((struct rt0_hdr *)route)->addr[temp].s6_addr[len]),
+					(u8)(rtinfo->addrs[ptr].s6_addr[len]));
+					DEBUGP("!ptr=%d temp=%d;\n",ptr,temp);
+				}
+				if (ptr==rtinfo->addrnr) break;
+			}
+			DEBUGP("ptr=%d len=%d #%d\n",ptr,len, rtinfo->addrnr);
+			if ( (len == 16) && (ptr == rtinfo->addrnr))
+				return ret;
+			else return 0;
+		}
+	} else {
+		DEBUGP("Strict ");
+		if ( rtinfo->addrnr > (unsigned int)((hdrlen-8)/16) ){
+			DEBUGP("There isn't enough space\n");
+			return 0;
+		} else {
+			DEBUGP("#%d ",rtinfo->addrnr);
+			for(temp=0; temp<rtinfo->addrnr; temp++){
+				len = 0;
+				while ((u8)(((struct rt0_hdr *)route)->
+						addr[temp].s6_addr[len]) ==
+					(u8)(rtinfo->addrs[temp].s6_addr[len])){
+					DEBUGP("%02X?%02X ",
+		(u8)(((struct rt0_hdr *)route)->addr[temp].s6_addr[len]),
+					(u8)(rtinfo->addrs[temp].s6_addr[len]));
+					len++;
+					if ( len == 16 ) break;
+				}
+				if (len!=16) {
+					DEBUGP("%02X?%02X ",
+		(u8)(((struct rt0_hdr *)route)->addr[temp].s6_addr[len]),
+					(u8)(rtinfo->addrs[temp].s6_addr[len]));
+					DEBUGP("!len=%d temp=%d;\n",len,temp);
+					break;
+				}
+			}
+			DEBUGP("temp=%d len=%d #%d\n",temp,len,rtinfo->addrnr);
+			if ( (len == 16) && (temp == rtinfo->addrnr) && (temp == (unsigned int)((hdrlen-8)/16)))
+				return ret;
+			else return 0;
+		}
+	}
+
+	return 0;
+}
+
+/* Called when user tries to insert an entry of this type. */
+static int
+checkentry(const char *tablename,
+          const struct ip6t_ip6 *ip,
+          void *matchinfo,
+          unsigned int matchinfosize,
+          unsigned int hook_mask)
+{
+       const struct ip6t_rt *rtinfo = matchinfo;
+
+       if (matchinfosize != IP6T_ALIGN(sizeof(struct ip6t_rt))) {
+              DEBUGP("ip6t_rt: matchsize %u != %u\n",
+                      matchinfosize, IP6T_ALIGN(sizeof(struct ip6t_rt)));
+              return 0;
+       }
+       if (rtinfo->invflags & ~IP6T_RT_INV_MASK) {
+              DEBUGP("ip6t_rt: unknown flags %X\n",
+                      rtinfo->invflags);
+              return 0;
+       }
+       if ( (rtinfo->flags & (IP6T_RT_RES|IP6T_RT_FST_MASK)) && 
+		       (!(rtinfo->flags & IP6T_RT_TYP) || 
+		       (rtinfo->rt_type != 0) || 
+		       (rtinfo->invflags & IP6T_RT_INV_TYP)) ) {
+	      DEBUGP("`--rt-type 0' required before `--rt-0-*'");
+              return 0;
+       }
+
+       return 1;
+}
+
+static struct ip6t_match rt_match
+= { { NULL, NULL }, "rt", &match, &checkentry, NULL, THIS_MODULE };
+
+static int __init init(void)
+{
+       return ip6t_register_match(&rt_match);
+}
+
+static void __exit cleanup(void)
+{
+       ip6t_unregister_match(&rt_match);
+}
+
+module_init(init);
+module_exit(cleanup);
diff -urN linux-2.4.21-pre5/net/ipv6/reassembly.c linux-2.4.21-pre6/net/ipv6/reassembly.c
--- linux-2.4.21-pre5/net/ipv6/reassembly.c	Thu Nov 28 15:53:15 2002
+++ linux-2.4.21-pre6/net/ipv6/reassembly.c	Wed Mar 26 16:11:40 2003
@@ -22,6 +22,7 @@
  *
  *      Horst von Brand Add missing #include <linux/string.h>
  *	Alexey Kuznetsov	SMP races, threading, cleanup.
+ *	Patrick McHardy		LRU queue of frag heads for evictor.
  */
 #include <linux/config.h>
 #include <linux/errno.h>
@@ -30,6 +31,7 @@
 #include <linux/socket.h>
 #include <linux/sockios.h>
 #include <linux/sched.h>
+#include <linux/list.h>
 #include <linux/net.h>
 #include <linux/netdevice.h>
 #include <linux/in6.h>
@@ -67,6 +69,7 @@
 struct frag_queue
 {
 	struct frag_queue	*next;
+	struct list_head lru_list;		/* lru list member	*/
 
 	__u32			id;		/* fragment id		*/
 	struct in6_addr		saddr;
@@ -95,6 +98,7 @@
 
 static struct frag_queue *ip6_frag_hash[IP6Q_HASHSZ];
 static rwlock_t ip6_frag_lock = RW_LOCK_UNLOCKED;
+static LIST_HEAD(ip6_frag_lru_list);
 int ip6_frag_nqueues = 0;
 
 static __inline__ void __fq_unlink(struct frag_queue *fq)
@@ -102,6 +106,7 @@
 	if(fq->next)
 		fq->next->pprev = fq->pprev;
 	*fq->pprev = fq->next;
+	list_del(&fq->lru_list);
 	ip6_frag_nqueues--;
 }
 
@@ -193,38 +198,30 @@
 
 static void ip6_evictor(void)
 {
-	int i, progress;
+	struct frag_queue *fq;
+	struct list_head *tmp;
 
-	do {
+	for(;;) {
 		if (atomic_read(&ip6_frag_mem) <= sysctl_ip6frag_low_thresh)
 			return;
-		progress = 0;
-		for (i = 0; i < IP6Q_HASHSZ; i++) {
-			struct frag_queue *fq;
-			if (ip6_frag_hash[i] == NULL)
-				continue;
-
-			read_lock(&ip6_frag_lock);
-			if ((fq = ip6_frag_hash[i]) != NULL) {
-				/* find the oldest queue for this hash bucket */
-				while (fq->next)
-					fq = fq->next;
-				atomic_inc(&fq->refcnt);
-				read_unlock(&ip6_frag_lock);
-
-				spin_lock(&fq->lock);
-				if (!(fq->last_in&COMPLETE))
-					fq_kill(fq);
-				spin_unlock(&fq->lock);
-
-				fq_put(fq);
-				IP6_INC_STATS_BH(Ip6ReasmFails);
-				progress = 1;
-				continue;
-			}
+		read_lock(&ip6_frag_lock);
+		if (list_empty(&ip6_frag_lru_list)) {
 			read_unlock(&ip6_frag_lock);
+			return;
 		}
-	} while (progress);
+		tmp = ip6_frag_lru_list.next;
+		fq = list_entry(tmp, struct frag_queue, lru_list);
+		atomic_inc(&fq->refcnt);
+		read_unlock(&ip6_frag_lock);
+
+		spin_lock(&fq->lock);
+		if (!(fq->last_in&COMPLETE))
+			fq_kill(fq);
+		spin_unlock(&fq->lock);
+
+		fq_put(fq);
+		IP6_INC_STATS_BH(Ip6ReasmFails);
+	}
 }
 
 static void ip6_frag_expire(unsigned long data)
@@ -294,6 +291,8 @@
 		fq->next->pprev = &fq->next;
 	ip6_frag_hash[hash] = fq;
 	fq->pprev = &ip6_frag_hash[hash];
+	INIT_LIST_HEAD(&fq->lru_list);
+	list_add_tail(&fq->lru_list, &ip6_frag_lru_list);
 	ip6_frag_nqueues++;
 	write_unlock(&ip6_frag_lock);
 	return fq;
@@ -501,6 +500,9 @@
 		fq->nhoffset = nhoff;
 		fq->last_in |= FIRST_IN;
 	}
+	write_lock(&ip6_frag_lock);
+	list_move_tail(&fq->lru_list, &ip6_frag_lru_list);
+	write_unlock(&ip6_frag_lock);
 	return;
 
 err:
diff -urN linux-2.4.21-pre5/net/ipv6/route.c linux-2.4.21-pre6/net/ipv6/route.c
--- linux-2.4.21-pre5/net/ipv6/route.c	Wed Mar 26 16:11:29 2003
+++ linux-2.4.21-pre6/net/ipv6/route.c	Wed Mar 26 16:11:40 2003
@@ -605,15 +605,17 @@
    Remove it only when all the things will work!
  */
 
-static void ipv6_wash_prefix(struct in6_addr *pfx, int plen)
+static void ipv6_addr_prefix(struct in6_addr *pfx,
+			     const struct in6_addr *addr, int plen)
 {
 	int b = plen&0x7;
-	int o = (plen + 7)>>3;
+	int o = plen>>3;
 
+	memcpy(pfx->s6_addr, addr, o);
 	if (o < 16)
 		memset(pfx->s6_addr + o, 0, 16 - o);
 	if (b != 0)
-		pfx->s6_addr[plen>>3] &= (0xFF<<(8-b));
+		pfx->s6_addr[o] = addr->s6_addr[o]&(0xff00 >> b);
 }
 
 static int ipv6_get_mtu(struct net_device *dev)
@@ -686,16 +688,16 @@
 			goto out;
 	}
 
-	ipv6_addr_copy(&rt->rt6i_dst.addr, &rtmsg->rtmsg_dst);
+	ipv6_addr_prefix(&rt->rt6i_dst.addr, 
+			 &rtmsg->rtmsg_dst, rtmsg->rtmsg_dst_len);
 	rt->rt6i_dst.plen = rtmsg->rtmsg_dst_len;
 	if (rt->rt6i_dst.plen == 128)
 	       rt->u.dst.flags = DST_HOST;
-	ipv6_wash_prefix(&rt->rt6i_dst.addr, rt->rt6i_dst.plen);
 
 #ifdef CONFIG_IPV6_SUBTREES
-	ipv6_addr_copy(&rt->rt6i_src.addr, &rtmsg->rtmsg_src);
+	ipv6_addr_prefix(&rt->rt6i_src.addr, 
+			 &rtmsg->rtmsg_src, rtmsg->rtmsg_src_len);
 	rt->rt6i_src.plen = rtmsg->rtmsg_src_len;
-	ipv6_wash_prefix(&rt->rt6i_src.addr, rt->rt6i_src.plen);
 #endif
 
 	rt->rt6i_metric = rtmsg->rtmsg_metric;
@@ -1692,14 +1694,14 @@
 {
 	struct rtattr **rta = arg;
 	int iif = 0;
-	int err;
+	int err = -ENOBUFS;
 	struct sk_buff *skb;
 	struct flowi fl;
 	struct rt6_info *rt;
 
 	skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
 	if (skb == NULL)
-		return -ENOBUFS;
+		goto out;
 
 	/* Reserve room for dummy headers, this skb can pass
 	   through good chunk of routing engine.
@@ -1723,8 +1725,10 @@
 	if (iif) {
 		struct net_device *dev;
 		dev = __dev_get_by_index(iif);
-		if (!dev)
-			return -ENODEV;
+		if (!dev) {
+			err = -ENODEV;
+			goto out_free;
+		}
 	}
 
 	fl.oif = 0;
@@ -1741,13 +1745,19 @@
 			    fl.nl_u.ip6_u.saddr,
 			    iif,
 			    RTM_NEWROUTE, NETLINK_CB(in_skb).pid, nlh->nlmsg_seq);
-	if (err < 0)
-		return -EMSGSIZE;
+	if (err < 0) {
+		err = -EMSGSIZE;
+		goto out_free;
+	}
 
 	err = netlink_unicast(rtnl, skb, NETLINK_CB(in_skb).pid, MSG_DONTWAIT);
-	if (err < 0)
-		return err;
-	return 0;
+	if (err > 0)
+		err = 0;
+out:
+	return err;
+out_free:
+	kfree_skb(skb);
+	goto out;	
 }
 
 void inet6_rt_notify(int event, struct rt6_info *rt)
diff -urN linux-2.4.21-pre5/net/ipv6/sit.c linux-2.4.21-pre6/net/ipv6/sit.c
--- linux-2.4.21-pre5/net/ipv6/sit.c	Thu Nov 28 15:53:15 2002
+++ linux-2.4.21-pre6/net/ipv6/sit.c	Wed Mar 26 16:11:40 2003
@@ -18,7 +18,6 @@
  * Nate Thompson <nate@thebog.net>:		6to4 support
  */
 
-#define __NO_VERSION__
 #include <linux/config.h>
 #include <linux/module.h>
 #include <linux/errno.h>
diff -urN linux-2.4.21-pre5/net/ipv6/tcp_ipv6.c linux-2.4.21-pre6/net/ipv6/tcp_ipv6.c
--- linux-2.4.21-pre5/net/ipv6/tcp_ipv6.c	Wed Mar 26 16:11:29 2003
+++ linux-2.4.21-pre6/net/ipv6/tcp_ipv6.c	Wed Mar 26 16:11:40 2003
@@ -24,7 +24,6 @@
  *      2 of the License, or (at your option) any later version.
  */
 
-#define __NO_VERSION__
 #include <linux/module.h>
 #include <linux/config.h>
 #include <linux/errno.h>
@@ -1427,9 +1426,6 @@
  */
 static int tcp_v6_do_rcv(struct sock *sk, struct sk_buff *skb)
 {
-#ifdef CONFIG_FILTER
-	struct sk_filter *filter;
-#endif
 	struct sk_buff *opt_skb = NULL;
 
 	/* Imagine: socket is IPv6. IPv4 packet arrives,
@@ -1443,12 +1439,6 @@
 	if (skb->protocol == htons(ETH_P_IP))
 		return tcp_v4_do_rcv(sk, skb);
 
-#ifdef CONFIG_FILTER
-	filter = sk->filter;
-	if (filter && sk_filter(skb, filter))
-		goto discard;
-#endif /* CONFIG_FILTER */
-
 	/*
 	 *	socket locking is here for SMP purposes as backlog rcv
 	 *	is currently called with bh processing disabled.
@@ -1601,6 +1591,9 @@
 	if(sk->state == TCP_TIME_WAIT)
 		goto do_time_wait;
 
+	if (sk_filter(sk, skb, 0))
+		goto discard_and_relse;
+		
 	skb->dev = NULL;
 
 	bh_lock_sock(sk);
diff -urN linux-2.4.21-pre5/net/netlink/af_netlink.c linux-2.4.21-pre6/net/netlink/af_netlink.c
--- linux-2.4.21-pre5/net/netlink/af_netlink.c	Thu Nov 28 15:53:16 2002
+++ linux-2.4.21-pre6/net/netlink/af_netlink.c	Wed Mar 26 16:11:40 2003
@@ -562,6 +562,14 @@
 	read_unlock(&nl_table_lock);
 }
 
+static inline void netlink_rcv_wake(struct sock *sk)
+{
+	if (skb_queue_len(&sk->receive_queue) == 0)
+		clear_bit(0, &sk->protinfo.af_netlink->state);
+	if (!test_bit(0, &sk->protinfo.af_netlink->state))
+		wake_up_interruptible(&sk->protinfo.af_netlink->wait);
+}
+
 static int netlink_sendmsg(struct socket *sock, struct msghdr *msg, int len,
 			   struct scm_cookie *scm)
 {
@@ -675,12 +683,7 @@
 		netlink_dump(sk);
 
 out:
-	if (skb_queue_len(&sk->receive_queue) <= sk->rcvbuf/2) {
-		if (skb_queue_len(&sk->receive_queue) == 0)
-			clear_bit(0, &sk->protinfo.af_netlink->state);
-		if (!test_bit(0, &sk->protinfo.af_netlink->state))
-			wake_up_interruptible(&sk->protinfo.af_netlink->wait);
-	}
+	netlink_rcv_wake(sk);
 	return err ? : copied;
 }
 
@@ -688,13 +691,7 @@
 {
 	if (sk->protinfo.af_netlink->data_ready)
 		sk->protinfo.af_netlink->data_ready(sk, len);
-
-	if (skb_queue_len(&sk->receive_queue) <= sk->rcvbuf/2) {
-		if (skb_queue_len(&sk->receive_queue) == 0)
-			clear_bit(0, &sk->protinfo.af_netlink->state);
-		if (!test_bit(0, &sk->protinfo.af_netlink->state))
-			wake_up_interruptible(&sk->protinfo.af_netlink->wait);
-	}
+	netlink_rcv_wake(sk);
 }
 
 /*
diff -urN linux-2.4.21-pre5/net/netrom/nr_dev.c linux-2.4.21-pre6/net/netrom/nr_dev.c
--- linux-2.4.21-pre5/net/netrom/nr_dev.c	Wed Jun 27 17:10:55 2001
+++ linux-2.4.21-pre6/net/netrom/nr_dev.c	Wed Mar 26 16:11:40 2003
@@ -19,7 +19,6 @@
  */
 
 #include <linux/config.h>
-#define __NO_VERSION__
 #include <linux/module.h>
 #include <linux/proc_fs.h>
 #include <linux/kernel.h>
diff -urN linux-2.4.21-pre5/net/netsyms.c linux-2.4.21-pre6/net/netsyms.c
--- linux-2.4.21-pre5/net/netsyms.c	Wed Mar 26 16:11:29 2003
+++ linux-2.4.21-pre6/net/netsyms.c	Wed Mar 26 16:11:40 2003
@@ -295,6 +295,9 @@
 EXPORT_SYMBOL(unregister_inet6addr_notifier);
 #include <net/ip6_route.h>
 EXPORT_SYMBOL(ip6_route_output);
+#ifdef CONFIG_NETFILTER
+EXPORT_SYMBOL(ip6_route_me_harder);
+#endif
 #endif
 #if defined (CONFIG_IPV6_MODULE) || defined (CONFIG_KHTTPD) || defined (CONFIG_KHTTPD_MODULE)
 /* inet functions common to v4 and v6 */
diff -urN linux-2.4.21-pre5/net/rose/rose_dev.c linux-2.4.21-pre6/net/rose/rose_dev.c
--- linux-2.4.21-pre5/net/rose/rose_dev.c	Wed Jun 27 17:10:55 2001
+++ linux-2.4.21-pre6/net/rose/rose_dev.c	Wed Mar 26 16:11:40 2003
@@ -15,7 +15,6 @@
  */
 
 #include <linux/config.h>
-#define __NO_VERSION__
 #include <linux/module.h>
 #include <linux/proc_fs.h>
 #include <linux/kernel.h>
diff -urN linux-2.4.21-pre5/net/sched/sch_gred.c linux-2.4.21-pre6/net/sched/sch_gred.c
--- linux-2.4.21-pre5/net/sched/sch_gred.c	Wed Mar 26 16:11:29 2003
+++ linux-2.4.21-pre6/net/sched/sch_gred.c	Wed Mar 26 16:11:40 2003
@@ -116,7 +116,7 @@
 	}
 
 
-	if ( ((skb->tc_index&0xf) > t->DPs) || !(q=t->tab[skb->tc_index&0xf])) {
+	if ( ((skb->tc_index&0xf) > (t->DPs -1)) || !(q=t->tab[skb->tc_index&0xf])) {
 		printk("GRED: setting to default (%d)\n ",t->def);
 		if (!(q=t->tab[t->def])) {
 			DPRINTK("GRED: setting to default FAILED! dropping!! "
diff -urN linux-2.4.21-pre5/net/sunrpc/sched.c linux-2.4.21-pre6/net/sunrpc/sched.c
--- linux-2.4.21-pre5/net/sunrpc/sched.c	Thu Nov 28 15:53:16 2002
+++ linux-2.4.21-pre6/net/sunrpc/sched.c	Wed Mar 26 16:11:40 2003
@@ -169,10 +169,8 @@
 static inline void
 rpc_delete_timer(struct rpc_task *task)
 {
-	if (timer_pending(&task->tk_timer)) {
-		dprintk("RPC: %4d deleting timer\n", task->tk_pid);
-		del_timer_sync(&task->tk_timer);
-	}
+	dprintk("RPC: %4d deleting timer\n", task->tk_pid);
+	del_timer_sync(&task->tk_timer);
 }
 
 /*
diff -urN linux-2.4.21-pre5/net/sunrpc/stats.c linux-2.4.21-pre6/net/sunrpc/stats.c
--- linux-2.4.21-pre5/net/sunrpc/stats.c	Thu Nov 28 15:53:16 2002
+++ linux-2.4.21-pre6/net/sunrpc/stats.c	Wed Mar 26 16:11:40 2003
@@ -12,7 +12,6 @@
  * Copyright (C) 1995, 1996, 1997 Olaf Kirch <okir@monad.swb.de>
  */
 
-#define __NO_VERSION__
 #include <linux/module.h>
 
 #include <linux/init.h>
diff -urN linux-2.4.21-pre5/net/sunrpc/sunrpc_syms.c linux-2.4.21-pre6/net/sunrpc/sunrpc_syms.c
--- linux-2.4.21-pre5/net/sunrpc/sunrpc_syms.c	Thu Nov 28 15:53:16 2002
+++ linux-2.4.21-pre6/net/sunrpc/sunrpc_syms.c	Wed Mar 26 16:11:40 2003
@@ -6,7 +6,6 @@
  * Copyright (C) 1997 Olaf Kirch <okir@monad.swb.de>
  */
 
-#define __NO_VERSION__
 #include <linux/config.h>
 #include <linux/module.h>
 
diff -urN linux-2.4.21-pre5/net/sunrpc/sysctl.c linux-2.4.21-pre6/net/sunrpc/sysctl.c
--- linux-2.4.21-pre5/net/sunrpc/sysctl.c	Fri Apr 13 20:26:07 2001
+++ linux-2.4.21-pre6/net/sunrpc/sysctl.c	Wed Mar 26 16:11:40 2003
@@ -14,7 +14,6 @@
 #include <linux/ctype.h>
 #include <linux/fs.h>
 #include <linux/sysctl.h>
-#define __NO_VERSION__
 #include <linux/module.h>
 
 #include <asm/uaccess.h>
diff -urN linux-2.4.21-pre5/net/x25/x25_link.c linux-2.4.21-pre6/net/x25/x25_link.c
--- linux-2.4.21-pre5/net/x25/x25_link.c	Fri Jun 29 19:38:26 2001
+++ linux-2.4.21-pre6/net/x25/x25_link.c	Wed Mar 26 16:11:40 2003
@@ -219,7 +219,7 @@
 
 	dptr = skb_put(skb, X25_STD_MIN_LEN + 2);
 
-	*dptr++ = ((lci >> 8) & 0x0F) | (neigh->extended) ? X25_GFI_EXTSEQ : X25_GFI_STDSEQ;
+	*dptr++ = ((lci >> 8) & 0x0F) | (neigh->extended ? X25_GFI_EXTSEQ : X25_GFI_STDSEQ);
 	*dptr++ = ((lci >> 0) & 0xFF);
 	*dptr++ = X25_CLEAR_REQUEST;
 	*dptr++ = cause;
