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

diff -urN linux-2.4.22-pre3/Documentation/Configure.help linux-2.4.22-pre4/Documentation/Configure.help
--- linux-2.4.22-pre3/Documentation/Configure.help	2003-07-09 16:56:23.000000000 -0700
+++ linux-2.4.22-pre4/Documentation/Configure.help	2003-07-09 16:56:27.000000000 -0700
@@ -12,8 +12,8 @@
 #     <http://nevod.perm.su/service/linux/doc/kernel/Configure.help>
 #   - French, by Pierre Tane <mailto:tanep@bigfoot.com>, at
 #     <http://www.traduc.org/kernelfr/>
-#   - Polish, by Dominik Mierzejewski <mailto:dmierzej@elka.pw.edu.pl>, at
-#     <http://home.elka.pw.edu.pl/~dmierzej/linux/kernel/>
+#   - Polish, by Dominik Mierzejewski <mailto:dominik@piorunek.pl>, at
+#     <http://www.piorunek.pl/~dominik/linux/kernel/>
 #   - German, by SuSE, at <http://www.suse.de/~ke/kernel/>. This patch
 #     also includes infrastructure to support different languages.
 #   - Catalan, by Antoni Bella <mailto:bella5@teleline.es>, at
@@ -12886,10 +12886,18 @@
   If you say Y here, you will be able to set per user limits for disk
   usage (also called disk quotas). Currently, it works only for the
   ext2 file system. You need additional software in order to use quota
-  support; for details, read the Quota mini-HOWTO, available from
+  support (you can download sources from
+  <http://www.sf.net/projects/linuxquota/>). For further details, read
+  the Quota mini-HOWTO, available from
   <http://www.tldp.org/docs.html#howto>. Probably the quota
   support is only useful for multi user systems. If unsure, say N.
 
+VFS v0 quota format support
+CONFIG_QFMT_V2
+  This quota format allows using quotas with 32-bit UIDs/GIDs. If you
+  need this functionality say Y here. Note that you will need latest
+  quota utilities for new quota format with this kernel.
+
 Memory Technology Device (MTD) support
 CONFIG_MTD
   Memory Technology Devices are flash, RAM and similar chips, often
diff -urN linux-2.4.22-pre3/Documentation/arm/SA1100/CERF linux-2.4.22-pre4/Documentation/arm/SA1100/CERF
--- linux-2.4.22-pre3/Documentation/arm/SA1100/CERF	2000-08-13 09:58:24.000000000 -0700
+++ linux-2.4.22-pre4/Documentation/arm/SA1100/CERF	2003-07-09 16:56:27.000000000 -0700
@@ -1,23 +1,28 @@
-The Intrinsyc CerfBoard is a StrongARM 1110-based computer on a board that measures 
-approximately 2" square. It includes an Ethernet controller, an RS232-compatible serial port, a
-USB function port, and one CompactFlash+ slot on the back. Pictures can be found at the
+The Intrinsyc CerfBoard is a StrongARM 1110-based computer on a board
+that measures approximately 2" square. It includes an Ethernet
+controller, an RS232-compatible serial port, a USB function port, and
+one CompactFlash+ slot on the back. Pictures can be found at the
 Intrinsyc website, http://www.intrinsyc.com.
 
-This document describes the support in the Linux kernel for the Intrinsyc CerfBoard as of
-version 2.4.0-test4-np1.
+This document describes the support in the Linux kernel for the
+Intrinsyc CerfBoard as of version 2.4.0-test4-np1.
 
 Supported in this version:
-   - CompactFlash+ slot (select PCMCIA in General Setup and any options that may be required)
-   - Onboard Crystal CS8900 Ethernet controller (Cerf CS8900A support in Network Devices)
+   - CompactFlash+ slot (select PCMCIA in General Setup and any options
+     that may be required)
+   - Onboard Crystal CS8900 Ethernet controller (Cerf CS8900A support in
+     Network Devices)
    - Serial ports with a serial console (hardcoded to 38400 8N1)
 
 Not supported in this version (yet):
    - LCD driver/touchscreen interface
-   - UDC (a driver exists right now, but is unstable and slow and only works with the Linux USB)
+   - UDC (a driver exists right now, but is unstable and slow and only
+     works with the Linux USB)
 
-In order to get this kernel onto your Cerf, you need a server that runs both BOOTP and
-TFTP. Detailed instructions should have come with your evaluation kit on how to use the
-bootloader. This series of commands will suffice:
+In order to get this kernel onto your Cerf, you need a server that runs
+both BOOTP and TFTP. Detailed instructions should have come with your
+evaluation kit on how to use the bootloader. This series of commands
+will suffice:
 
    make cerf_config
    make xconfig
@@ -25,10 +30,9 @@
    make zImage
    cp arch/arm/boot/zImage <TFTP directory>
 
-The default config uses a 4MB RAM disk located at 0xc0500000 as root. Setting the board to
-mount root from a NFS partition works, too.
+The default config uses a 4MB RAM disk located at 0xc0500000 as root.
+Setting the board to mount root from a NFS partition works, too.
 
 
 I-Gene Leong, Intrinsyc Software Inc.
 ileong@intrinsyc.com
-
diff -urN linux-2.4.22-pre3/Documentation/filesystems/befs.txt linux-2.4.22-pre4/Documentation/filesystems/befs.txt
--- linux-2.4.22-pre3/Documentation/filesystems/befs.txt	2002-11-28 15:53:08.000000000 -0800
+++ linux-2.4.22-pre4/Documentation/filesystems/befs.txt	2003-07-09 16:56:27.000000000 -0700
@@ -18,32 +18,35 @@
 AUTHOR
 =====
 Current maintainer: Will Dyson <will_dyson@pobox.com>
-Has been working on the code since Aug 13, 2001. See the changelog for details.
+Has been working on the code since Aug 13, 2001. See the changelog for
+details.
 
 Original Author: Makoto Kato <m_kato@ga2.so-net.ne.jp>
-His orriginal code can still be found at: <http://hp.vector.co.jp/authors/VA008030/bfs/>
-Does anyone know of a more current email address for Makoto? He doesn't respond
-to the address given above...
+His orriginal code can still be found at:
+<http://hp.vector.co.jp/authors/VA008030/bfs/>
+Does anyone know of a more current email address for Makoto? He doesn't
+respond to the address given above...
 
 WHAT IS THIS DRIVER?
 ==================
 This module implements the native filesystem of BeOS <http://www.be.com/>
-for the linux 2.4.1 and later kernels. Currently it is a read-only implementation.
+for the linux 2.4.1 and later kernels. Currently it is a read-only
+implementation.
 
 Which is it, BFS or BEFS?
 ================
 Be, Inc said, "BeOS Filesystem is officially called BFS, not BeFS". 
-But Unixware Boot Filesystem is called bfs, too. And they are already in the
-kernel.
-Because of this nameing conflict, on Linux the BeOS filesystem is called befs.
+But Unixware Boot Filesystem is called bfs, too. And they are already in
+the kernel. Because of this nameing conflict, on Linux the BeOS
+filesystem is called befs.
 
 HOW TO INSTALL
 ==============
 step 1.  Install the BeFS  patch into the source code tree of linux.
 
 Apply the patchfile to your kernel source tree.
-Assuming that your kernel source is in /foo/bar/linux and the patchfile is called
-patch-befs-xxx, you would do the following:
+Assuming that your kernel source is in /foo/bar/linux and the patchfile
+is called patch-befs-xxx, you would do the following:
 
 	cd /foo/bar/linux
 	patch -p1 < /path/to/patch-befs-xxx
@@ -66,8 +69,9 @@
 The BeFS module is not a standard part of the linux kernel, so you must first
 enable support for experimental code under the "Code maturity level" menu.
 
-Then, under the "Filesystems" menu will be an option called "BeFS filesystem (experimental)",
-or something like that. Enable that option (it is fine to make it a module).
+Then, under the "Filesystems" menu will be an option called "BeFS
+filesystem (experimental)", or something like that. Enable that option
+(it is fine to make it a module).
 
 Save your kernel configuration and then build your kernel.
 
diff -urN linux-2.4.22-pre3/Documentation/ia64/efirtc.txt linux-2.4.22-pre4/Documentation/ia64/efirtc.txt
--- linux-2.4.22-pre3/Documentation/ia64/efirtc.txt	2001-02-08 16:32:44.000000000 -0800
+++ linux-2.4.22-pre4/Documentation/ia64/efirtc.txt	2003-07-09 16:56:27.000000000 -0700
@@ -12,13 +12,13 @@
 to get access to the Time Service offered by EFI version 0.92.
 
 EFI provides 4 calls one can make once the OS is booted: GetTime(),
-SetTime(), GetWakeupTime(), SetWakeupTime() which are all supported by this driver.
-We describe those calls as well the design of the driver in the following
-sections.
+SetTime(), GetWakeupTime(), SetWakeupTime() which are all supported by this
+driver. We describe those calls as well the design of the driver in the
+following sections.
 
 II/ Design Decisions
 
-The original ideas was to provide a very simple  driver to get access to, 
+The original ideas was to provide a very simple driver to get access to, 
 at first, the time of day service. This is required in order to access, in a 
 portable way, the CMOS clock. A program like /sbin/hwclock uses such a clock 
 to initialize the system view of the time during boot.
@@ -39,8 +39,8 @@
 without necessarily impacting any of the user applications. The decoupling
 enables flexibility and permits writing wrapper code is ncase things change.
 
-The driver exposes two interfaces, one via the device file and a set of ioctl()s.
-The other is read-only via the /proc filesystem. 
+The driver exposes two interfaces, one via the device file and a set of
+ioctl()s. The other is read-only via the /proc filesystem. 
 
 As of today we don't offer a /proc/sys interface.
 
diff -urN linux-2.4.22-pre3/Documentation/kernel-parameters.txt linux-2.4.22-pre4/Documentation/kernel-parameters.txt
--- linux-2.4.22-pre3/Documentation/kernel-parameters.txt	2003-07-09 16:56:23.000000000 -0700
+++ linux-2.4.22-pre4/Documentation/kernel-parameters.txt	2003-07-09 16:56:27.000000000 -0700
@@ -503,7 +503,7 @@
 	plip=		[PPT,NET] Parallel port network link.
 
 	profile=	[KNL] enable kernel profiling via /proc/profile
-			(param:log level).
+			(param: profile step/bucket size as a power of 2)
 
 	prompt_ramdisk=	[RAM] List of RAM disks to prompt for floppy disk
 			before loading.
diff -urN linux-2.4.22-pre3/Documentation/s390/c7000.txt linux-2.4.22-pre4/Documentation/s390/c7000.txt
--- linux-2.4.22-pre3/Documentation/s390/c7000.txt	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.4.22-pre4/Documentation/s390/c7000.txt	2003-07-09 16:56:27.000000000 -0700
@@ -0,0 +1,92 @@
+Cisco 7000 (CLAW) support
+
+The c7000 module provides support for a channel attached Cisco 7xxx
+family router on Linux/390.  The parameters for the module are as follows:
+
+	base0=0xYYYY	This parameter defines the base unit address of the
+			channel attached router.
+
+	lhost0=s1    	This parameter defines the local host name and
+			must match the claw directive "host-name" field
+			(first string).  The default value is "UTS".
+
+	uhost0=s2	This parameter defines the unit's name and must
+			match the claw directive "device-name" field
+			(second string).  The default value is "C7011".
+
+	lappl0=s3	This parameter defines the local application name
+			and must match the claw directive "host-app" field
+			(third string).  The default value is "TCPIP".
+
+	uappl0=s4	This parameter defines the unit application name and
+			must match the claw directive "device-app" field
+			(fourth string).  The default value is "TCPIP".
+
+	dbg=x		This parameter defines the message level.  Higher
+			numbers will result in additional diagnostic messages.
+			The default value is 0.
+
+	noauto=z	This parameter controls the automatic detection of
+			the unit base address (base0).  When set to a non
+			zero value, automatic detection of unit base addresses
+			is not done.  The default value is 0.
+
+Note that the values coded in strings s1 - s4 are case sensitive.
+
+For example, assume that the following claw directive has been coded in
+the Cisco router:
+
+claw 0100 6C 129.212.61.101 UTS C7011 TCPIP TCPIP
+
+The module can be loaded using the following command:
+
+insmod c7000 base0=0x336c lhost0="UTS" uhost0="C7011" lappl0="TCPIP" \
+uappl0="TCPIP" dbg=0 noauto=1
+
+Additional interfaces can be defined via parameters base1 - base3,
+lhost1 - lhost3, lappl1 - lappl3, uhost1 - uhost3, uappl1 - uappl3.
+The interfaces are named "ci0" - "ci3".  After loading the module, the
+ifconfig command is used to configure the interface.  For example:
+
+ifconfig ci0 129.212.61.101
+ifconfig ci0 netmask 255.255.255.0 broadcast 129.212.61.0
+ifconfig ci0
+
+The route command is used to specify the router as the default route:
+
+route add default gw 129.212.61.200
+
+The interface can be automatically activated at boot time by following this
+procedure:
+
+1) Add the following two lines to file "/etc/conf.modules":
+
+alias ci0 c7000
+options c7000 base0=0xYYYY lhost0=s1 uhost0=s2 lappl0=s3 uappl0=s4
+
+2) Edit file "/etc/sysconfig/network" as follows:
+
+NETWORKING=yes
+FORWARD_IPV4=no
+HOSTNAME=your-hostname
+GATEWAYDEV=ci0
+GATEWAY=your-gateway-ip-address
+
+Substitute your own host name and gateway IP address.
+
+3) Create a file in directory "/etc/sysconfig/network-scripts" called
+"ifcfg-ci0".  The contents are as follows:
+
+DEVICE=ci0
+USERCTL=no
+ONBOOT=yes
+BOOTPROTO=none
+BROADCAST=your-broadcast-ip-address
+NETWORK=your-network-address
+NETMASK=your-netmask
+IPADDR=your-ip-address
+
+Substitute your IP address, broadcast IP address, network address and
+network mask.
+
+4) chmod +x ifcfg-ci0
diff -urN linux-2.4.22-pre3/Documentation/sound/Introduction linux-2.4.22-pre4/Documentation/sound/Introduction
--- linux-2.4.22-pre3/Documentation/sound/Introduction	2001-02-16 15:53:08.000000000 -0800
+++ linux-2.4.22-pre4/Documentation/sound/Introduction	2003-07-09 16:56:27.000000000 -0700
@@ -80,7 +80,7 @@
     additional features.
 
 2.  The commercial OSS driver may be obtained from the site:
-    http://www/opensound.com.  This may be used for cards that
+    http://www.opensound.com.  This may be used for cards that
     are unsupported by the kernel driver, or may be used
     by other operating systems.  
 
diff -urN linux-2.4.22-pre3/Documentation/sound/OPL3-SA linux-2.4.22-pre4/Documentation/sound/OPL3-SA
--- linux-2.4.22-pre3/Documentation/sound/OPL3-SA	2000-03-08 11:37:03.000000000 -0800
+++ linux-2.4.22-pre4/Documentation/sound/OPL3-SA	2003-07-09 16:56:27.000000000 -0700
@@ -34,7 +34,7 @@
 	mpu_irq: This is the MPU401's IRQ.
 
 If you'd like to use the OPL3 FM Synthesizer, make sure you enable
-CONFIG_YM3812 (in 'make config'). That'll build the opl3.o module.
+CONFIG_SOUND_YM3812 (in 'make config'). That'll build the opl3.o module.
 
 Then a simple 'insmod opl3 io=0x388', and you now have FM Synth.
 
diff -urN linux-2.4.22-pre3/Documentation/sound/VIBRA16 linux-2.4.22-pre4/Documentation/sound/VIBRA16
--- linux-2.4.22-pre3/Documentation/sound/VIBRA16	1999-03-07 15:22:06.000000000 -0800
+++ linux-2.4.22-pre4/Documentation/sound/VIBRA16	2003-07-09 16:56:27.000000000 -0700
@@ -62,7 +62,7 @@
 
 	Or, take the hard way:
 
-insmod souncore
+insmod soundcore
 insmod sound
 insmod uart401
 insmod sb io=0x220 irq=5 dma=1 dma16=3
diff -urN linux-2.4.22-pre3/Makefile linux-2.4.22-pre4/Makefile
--- linux-2.4.22-pre3/Makefile	2003-07-09 16:56:23.000000000 -0700
+++ linux-2.4.22-pre4/Makefile	2003-07-09 16:56:27.000000000 -0700
@@ -1,7 +1,7 @@
 VERSION = 2
 PATCHLEVEL = 4
 SUBLEVEL = 22
-EXTRAVERSION = -pre3
+EXTRAVERSION = -pre4
 
 KERNELRELEASE=$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)
 
diff -urN linux-2.4.22-pre3/arch/cris/Makefile linux-2.4.22-pre4/arch/cris/Makefile
--- linux-2.4.22-pre3/arch/cris/Makefile	2002-08-02 17:39:42.000000000 -0700
+++ linux-2.4.22-pre4/arch/cris/Makefile	2003-07-09 16:56:27.000000000 -0700
@@ -1,4 +1,4 @@
-# $Id: Makefile,v 1.27 2002/03/08 13:08:50 bjornw Exp $
+#
 # cris/Makefile
 #
 # This file is included by the global makefile so that you can add your own
@@ -56,6 +56,10 @@
 # each others config options
 SUBDIRS += arch/cris/boot/rescue
 endif
+
+# Note that the order of .o files below is important is some cases
+# (because initcalls are executed in the order they are linked)
+# Never add any driver before mm.o!
 CORE_FILES += arch/cris/kernel/kernel.o arch/cris/mm/mm.o
 DRIVERS += arch/cris/drivers/drivers.o
 LIBGCC = $(shell $(CC) $(CFLAGS) -print-file-name=libgcc.a)
diff -urN linux-2.4.22-pre3/arch/cris/boot/compressed/README linux-2.4.22-pre4/arch/cris/boot/compressed/README
--- linux-2.4.22-pre3/arch/cris/boot/compressed/README	2001-02-08 16:32:44.000000000 -0800
+++ linux-2.4.22-pre4/arch/cris/boot/compressed/README	2003-07-09 16:56:27.000000000 -0700
@@ -1,6 +1,5 @@
 Creation of the self-extracting compressed kernel image (vmlinuz)
 -----------------------------------------------------------------
-$Id: README,v 1.1 2000/11/22 17:20:46 bjornw Exp $
 
 This can be slightly confusing because it's a process with many steps.
 
diff -urN linux-2.4.22-pre3/arch/cris/boot/compressed/misc.c linux-2.4.22-pre4/arch/cris/boot/compressed/misc.c
--- linux-2.4.22-pre3/arch/cris/boot/compressed/misc.c	2001-05-01 16:04:56.000000000 -0700
+++ linux-2.4.22-pre4/arch/cris/boot/compressed/misc.c	2003-07-09 16:56:27.000000000 -0700
@@ -1,19 +1,17 @@
 /*
  * misc.c
  *
- * $Id: misc.c,v 1.6 2001/04/09 10:00:21 starvik Exp $
- * 
- * This is a collection of several routines from gzip-1.0.3 
+ * This is a collection of several routines from gzip-1.0.3
  * adapted for Linux.
  *
  * malloc by Hannu Savolainen 1993 and Matthias Urlichs 1994
  * puts by Nick Holloway 1993, better puts by Martin Mares 1995
  * adoptation for Linux/CRIS Axis Communications AB, 1999
- * 
+ *
  */
 
 /* where the piggybacked kernel image expects itself to live.
- * it is the same adress we use when we network load an uncompressed
+ * it is the same address we use when we network load an uncompressed
  * image into DRAM, and it is the address the kernel is linked to live
  * at by etrax100.ld.
  */
@@ -65,8 +63,8 @@
 #define ENCRYPTED    0x20 /* bit 5 set: file is encrypted */
 #define RESERVED     0xC0 /* bit 6,7:   reserved */
 
-#define get_byte() inbuf[inptr++]	
-	
+#define get_byte() inbuf[inptr++]
+
 /* Diagnostic functions */
 #ifdef DEBUG
 #  define Assert(cond,msg) {if(!(cond)) error(msg);}
@@ -95,20 +93,20 @@
 static long bytes_out = 0;
 static uch *output_data;
 static unsigned long output_ptr = 0;
- 
+
 static void *malloc(int size);
 static void free(void *where);
 static void error(char *m);
 static void gzip_mark(void **);
 static void gzip_release(void **);
- 
+
 static void puts(const char *);
 
 /* the "heap" is put directly after the BSS ends, at end */
-  
+
 extern int end;
 static long free_mem_ptr = (long)&end;
- 
+
 #include "../../../../lib/inflate.c"
 
 static void *malloc(int size)
@@ -196,9 +194,9 @@
     ulg c = crc;         /* temporary variable */
     unsigned n;
     uch *in, *out, ch;
-    
+
     in = window;
-    out = &output_data[output_ptr]; 
+    out = &output_data[output_ptr];
     for (n = 0; n < outcnt; n++) {
 	    ch = *out++ = *in++;
 	    c = crc_32_tab[((int)c ^ ch) & 0xff] ^ (c >> 8);
@@ -229,7 +227,7 @@
 decompress_kernel()
 {
 	char revision;
-	
+
 	/* input_data is set in head.S */
 	inbuf = input_data;
 
diff -urN linux-2.4.22-pre3/arch/cris/boot/rescue/head.S linux-2.4.22-pre4/arch/cris/boot/rescue/head.S
--- linux-2.4.22-pre3/arch/cris/boot/rescue/head.S	2002-02-25 11:37:52.000000000 -0800
+++ linux-2.4.22-pre4/arch/cris/boot/rescue/head.S	2003-07-09 16:56:27.000000000 -0700
@@ -1,5 +1,4 @@
-/* $Id: head.S,v 1.10 2001/11/08 15:10:16 starvik Exp $
- * 
+/*
  * Rescue code, made to reside at the beginning of the
  * flash-memory. when it starts, it checks a partition
  * table at the first sector after the rescue sector.
@@ -52,7 +51,7 @@
  * 
  * Bit 0 in flags signifies RW or RO. The rescue code only bothers
  * to check the checksum for RO partitions, since the others will
- * change its data without updating the checksums. A 1 in bit 0
+ * change their data without updating the checksums. A 1 in bit 0
  * means RO, 0 means RW. That way, it is possible to set a partition
  * in RO mode initially, and later mark it as RW, since you can always
  * write 0's to the flash.
@@ -60,7 +59,7 @@
  * During the wait for serial input, the status LED will flash so the
  * user knows something went wrong.
  * 
- * Copyright (C) 1999,2001 Axis Communications AB
+ * Copyright (C) 1999, 2000, 2001, 2002, 2003 Axis Communications AB
  */
 
 #include <linux/config.h>
@@ -130,7 +129,7 @@
 
 	;; first put a jump test to give a possibility of upgrading the rescue code
 	;; without erasing/reflashing the sector. we put a longword of -1 here and if
-	;; its not -1, we jump using the value as jump target. since we can always
+	;; it is not -1, we jump using the value as jump target. since we can always
 	;; change 1's to 0's without erasing the sector, it is possible to add new
 	;; code after this and altering the jumptarget in an upgrade.
 
@@ -150,7 +149,20 @@
 	move.d 0x3, $r0    ; enable = on, phy = mii_clk
 	move.d $r0, [R_NETWORK_GEN_CONFIG]
 #endif
+
+	;; Setup port PA and PB default initial directions and data
+	;; (so we can flash LEDs, and so that DTR and others are set)
+	
+	move.b	CONFIG_ETRAX_DEF_R_PORT_PA_DIR, $r0
+	move.b	$r0, [R_PORT_PA_DIR]
+	move.b	CONFIG_ETRAX_DEF_R_PORT_PA_DATA, $r0
+	move.b	$r0, [R_PORT_PA_DATA]
 	
+	move.b	CONFIG_ETRAX_DEF_R_PORT_PB_DIR, $r0
+	move.b	$r0, [R_PORT_PB_DIR]
+	move.b	CONFIG_ETRAX_DEF_R_PORT_PB_DATA, $r0
+	move.b	$r0, [R_PORT_PB_DATA]
+
 	;; We need to setup the bus registers before we start using the DRAM
 #include "../../lib/dram_init.S"
 
@@ -192,7 +204,7 @@
 	moveq	-1, $r7
 	
 ploop:	move.d	[$r3+], $r1	; partition offset (from ptable start)
-	bne	notfirst	; check if its the partition containing ptable
+	bne	notfirst	; check if it is the partition containing ptable
 	nop			; yes..
 	move.d	$r8, $r1	; for its checksum check, skip the ptable
 	move.d	[$r3+], $r2	; partition length
@@ -226,18 +238,6 @@
 	;; otherwise fall through to the rescue code.
 	
 do_rescue:
-	;; setup port PA and PB default initial directions and data
-	;; (so we can flash LEDs, and so that DTR and others are set)
-	
-	move.b	CONFIG_ETRAX_DEF_R_PORT_PA_DIR, $r0
-	move.b	$r0, [R_PORT_PA_DIR]
-	move.b	CONFIG_ETRAX_DEF_R_PORT_PA_DATA, $r0
-	move.b	$r0, [R_PORT_PA_DATA]
-	
-	move.b	CONFIG_ETRAX_DEF_R_PORT_PB_DIR, $r0
-	move.b	$r0, [R_PORT_PB_DIR]
-	move.b	CONFIG_ETRAX_DEF_R_PORT_PB_DATA, $r0
-	move.b	$r0, [R_PORT_PB_DATA]
 
 	;; setup the serial port at 115200 baud
 	
diff -urN linux-2.4.22-pre3/arch/cris/boot/rescue/kimagerescue.S linux-2.4.22-pre4/arch/cris/boot/rescue/kimagerescue.S
--- linux-2.4.22-pre3/arch/cris/boot/rescue/kimagerescue.S	2001-10-08 11:43:54.000000000 -0700
+++ linux-2.4.22-pre4/arch/cris/boot/rescue/kimagerescue.S	2003-07-09 16:56:27.000000000 -0700
@@ -1,5 +1,4 @@
-/* $Id: kimagerescue.S,v 1.5 2001/10/03 17:15:15 bjornw Exp $
- * 
+/*
  * Rescue code to be prepended on a kimage and copied to the
  * rescue serial port.
  * This is called from the rescue code, it will copy received data to
diff -urN linux-2.4.22-pre3/arch/cris/boot/rescue/testrescue.S linux-2.4.22-pre4/arch/cris/boot/rescue/testrescue.S
--- linux-2.4.22-pre3/arch/cris/boot/rescue/testrescue.S	2001-10-08 11:43:54.000000000 -0700
+++ linux-2.4.22-pre4/arch/cris/boot/rescue/testrescue.S	2003-07-09 16:56:27.000000000 -0700
@@ -1,9 +1,8 @@
-/* $Id: testrescue.S,v 1.3 2001/10/03 17:15:15 bjornw Exp $
- *
+/*
  * Simple testcode to download by the rescue block.
  * Just lits some LEDs to show it was downloaded correctly.
- * 
- * Copyright (C) 1999 Axis Communications AB
+ *
+ * Copyright (C) 1999-2003 Axis Communications AB
  */
 
 #define ASSEMBLER_MACROS_ONLY
@@ -16,11 +15,9 @@
 	moveq	-1, $r2
 	move.b	$r2, [R_PORT_PA_DIR]
 	moveq	0, $r2
-	move.b	$r2, [R_PORT_PA_DATA]	
+	move.b	$r2, [R_PORT_PA_DATA]
 
 endless:
 	nop
 	ba	endless
 	nop
-
-	
diff -urN linux-2.4.22-pre3/arch/cris/config.in linux-2.4.22-pre4/arch/cris/config.in
--- linux-2.4.22-pre3/arch/cris/config.in	2003-07-09 16:56:23.000000000 -0700
+++ linux-2.4.22-pre4/arch/cris/config.in	2003-07-09 16:56:27.000000000 -0700
@@ -32,14 +32,27 @@
 
 tristate 'Kernel support for ELF binaries' CONFIG_BINFMT_ELF
 
+string 'Kernel command line' CONFIG_ETRAX_CMDLINE "root=/dev/mtdblock3"
+
 bool 'Use kernel gdb debugger' CONFIG_ETRAX_KGDB
 
-bool 'Enable Etrax100 watchdog' CONFIG_ETRAX_WATCHDOG
+bool 'Use Artpec gdb debugger' CONFIG_ETRAX_AGDB
+
+bool 'Enable ETRAX watchdog' CONFIG_ETRAX_WATCHDOG
 if [ "$CONFIG_ETRAX_WATCHDOG" = "y" ]; then
-	bool 'Disable watchdog during Oops printouts' CONFIG_ETRAX_WATCHDOG_NICE_DOGGY
+	bool '  Disable watchdog during Oops printouts' CONFIG_ETRAX_WATCHDOG_NICE_DOGGY
 fi
 
+bool 'Enable ETRAX interrupt debugging' CONFIG_ETRAX_DEBUG_INTERRUPT
+
 bool 'Enable ETRAX fast timer API' CONFIG_ETRAX_FAST_TIMER
+if [ "$CONFIG_ETRAX_FAST_TIMER" = "y" ]; then
+	bool '  Enable fast timer logging' CONFIG_ETRAX_FAST_TIMER_LOGGING
+	bool '  Enable fast timer debug' CONFIG_ETRAX_FAST_TIMER_DEBUG
+	bool '  Enable fast timer sanity checks' CONFIG_ETRAX_FAST_TIMER_SANITY_CHECKS
+fi
+
+bool 'Enable reboot at out of memory' CONFIG_OOM_REBOOT
 
 endmenu
 
@@ -51,7 +64,7 @@
 	 Etrax-100-LX-v2	CONFIG_ETRAX100LX_V2	\
 	 Etrax-100-LX-for-xsim-simulator   CONFIG_SVINTO_SIM" Etrax-100-LX-v1
 
-# Etrax100 LX v1 has a MMU "feature" requiring a low mapping
+# ETRAX 100LX v1 has a MMU "feature" requiring a low mapping
 
 if [ "$CONFIG_ETRAX100LX" = "y" ]; then
 	define_bool CONFIG_CRIS_LOW_MAP y
@@ -64,8 +77,6 @@
 
 int 'Buswidth of flash in bytes' CONFIG_ETRAX_FLASH_BUSWIDTH 2
 
-string 'Root device name' CONFIG_ETRAX_ROOT_DEVICE "/dev/mtdblock3"
-
 choice 'Product LED port' \
 	"Port-PA-LEDs	CONFIG_ETRAX_PA_LEDS	\
 	 Port-PB-LEDs	CONFIG_ETRAX_PB_LEDS	\
@@ -73,36 +84,36 @@
 	 None	CONFIG_ETRAX_NO_LEDS" Port-PA-LEDs
 
 if [ "$CONFIG_ETRAX_NO_LEDS" != "y" ]; then
-  int '  First green LED bit' CONFIG_ETRAX_LED1G 2
-  int '  First red LED bit' CONFIG_ETRAX_LED1R 3
-  int '  Second green LED bit' CONFIG_ETRAX_LED2G 4
-  int '  Second red LED bit' CONFIG_ETRAX_LED2R 5
-  int '  Third green LED bit' CONFIG_ETRAX_LED3G 2
-  int '  Third red LED bit' CONFIG_ETRAX_LED3R 2
+	int '  First green LED bit' CONFIG_ETRAX_LED1G 2
+	int '  First red LED bit' CONFIG_ETRAX_LED1R 3
+	int '  Second green LED bit' CONFIG_ETRAX_LED2G 4
+	int '  Second red LED bit' CONFIG_ETRAX_LED2R 5
+	int '  Third green LED bit' CONFIG_ETRAX_LED3G 2
+	int '  Third red LED bit' CONFIG_ETRAX_LED3R 2
 fi
 
 if [ "$CONFIG_ETRAX_CSP0_LEDS" = "y" ]; then
-  int '  Fourth red LED bit' CONFIG_ETRAX_LED4R 2
-  int '  Fourth green LED bit' CONFIG_ETRAX_LED4G 2
-  int '  Fifth red LED bit' CONFIG_ETRAX_LED5R 2
-  int '  Fifth green LED bit' CONFIG_ETRAX_LED5G 2
-  int '  Sixth red LED bit' CONFIG_ETRAX_LED6R 2
-  int '  Sixth green LED bit' CONFIG_ETRAX_LED6G 2
-  int '  Seventh red LED bit' CONFIG_ETRAX_LED7R 2
-  int '  Seventh green LED bit' CONFIG_ETRAX_LED7G 2
-  int '  Eigth yellow LED bit' CONFIG_ETRAX_LED8Y 2
-  int '  Ninth yellow LED bit' CONFIG_ETRAX_LED9Y 2
-  int '  Tenth yellow LED bit' CONFIG_ETRAX_LED10Y 2
-  int '  Eleventh yellow LED bit' CONFIG_ETRAX_LED11Y 2
-  int '  Twelfth red LED bit' CONFIG_ETRAX_LED12R 2
-fi  
+	int '  Fourth red LED bit' CONFIG_ETRAX_LED4R 2
+	int '  Fourth green LED bit' CONFIG_ETRAX_LED4G 2
+	int '  Fifth red LED bit' CONFIG_ETRAX_LED5R 2
+	int '  Fifth green LED bit' CONFIG_ETRAX_LED5G 2
+	int '  Sixth red LED bit' CONFIG_ETRAX_LED6R 2
+	int '  Sixth green LED bit' CONFIG_ETRAX_LED6G 2
+	int '  Seventh red LED bit' CONFIG_ETRAX_LED7R 2
+	int '  Seventh green LED bit' CONFIG_ETRAX_LED7G 2
+	int '  Eigth yellow LED bit' CONFIG_ETRAX_LED8Y 2
+	int '  Ninth yellow LED bit' CONFIG_ETRAX_LED9Y 2
+	int '  Tenth yellow LED bit' CONFIG_ETRAX_LED10Y 2
+	int '  Eleventh yellow LED bit' CONFIG_ETRAX_LED11Y 2
+	int '  Twelfth red LED bit' CONFIG_ETRAX_LED12R 2
+fi
 
 choice 'Product debug-port' \
 	"Serial-0	CONFIG_ETRAX_DEBUG_PORT0	\
 	 Serial-1	CONFIG_ETRAX_DEBUG_PORT1	\
 	 Serial-2	CONFIG_ETRAX_DEBUG_PORT2	\
 	 Serial-3	CONFIG_ETRAX_DEBUG_PORT3	\
-	 disabled	CONFIG_ETRAX_DEBUG_PORT_NULL" Serial-0 
+	 disabled	CONFIG_ETRAX_DEBUG_PORT_NULL" Serial-0
 
 choice 'Product rescue-port' \
 	"Serial-0	CONFIG_ETRAX_RESCUE_SER0	\
@@ -115,13 +126,13 @@
 
 bool 'SDRAM support' CONFIG_ETRAX_SDRAM
 if [ "$CONFIG_ETRAX_SDRAM" = "n" ]; then
-	hex 'R_DRAM_CONFIG' CONFIG_ETRAX_DEF_R_DRAM_CONFIG 1a200040
-	hex 'R_DRAM_TIMING' CONFIG_ETRAX_DEF_R_DRAM_TIMING 5611
+	hex '  R_DRAM_CONFIG' CONFIG_ETRAX_DEF_R_DRAM_CONFIG 1a200040
+	hex '  R_DRAM_TIMING' CONFIG_ETRAX_DEF_R_DRAM_TIMING 5611
 fi
 
 if [ "$CONFIG_ETRAX_SDRAM" = "y" ]; then
-	hex 'R_SDRAM_CONFIG' CONFIG_ETRAX_DEF_R_SDRAM_CONFIG d2fa7878
-	hex 'R_SDRAM_TIMING' CONFIG_ETRAX_DEF_R_SDRAM_TIMING 80004801
+	hex '  R_SDRAM_CONFIG' CONFIG_ETRAX_DEF_R_SDRAM_CONFIG d2fa7878
+	hex '  R_SDRAM_TIMING' CONFIG_ETRAX_DEF_R_SDRAM_TIMING 80004801
 fi
 
 hex 'R_PORT_PA_DIR' CONFIG_ETRAX_DEF_R_PORT_PA_DIR 1c
@@ -132,13 +143,13 @@
 
 bool 'Software Shutdown Support' CONFIG_ETRAX_SOFT_SHUTDOWN
 if [ "$CONFIG_ETRAX_SOFT_SHUTDOWN" = "y" ]; then
-   int 'Shutdown bit on port CSP0' CONFIG_ETRAX_SHUTDOWN_BIT 12
-   int 'Power button bit on port G' CONFIG_ETRAX_POWERBUTTON_BIT 25
+   int '  Shutdown bit on port CSP0' CONFIG_ETRAX_SHUTDOWN_BIT 12
+   int '  Power button bit on port G' CONFIG_ETRAX_POWERBUTTON_BIT 25
 fi
 
 endmenu
 
-# bring in Etrax built-in drivers
+# bring in ETRAX built-in drivers
 
 source arch/cris/drivers/Config.in
 
@@ -258,7 +269,7 @@
 #bool 'Debug kmalloc/kfree' CONFIG_DEBUG_MALLOC
 bool 'Kernel profiling support' CONFIG_PROFILE
 if [ "$CONFIG_PROFILE" = "y" ]; then
-  int ' Profile shift count' CONFIG_PROFILE_SHIFT 2
+  int '  Profile shift count' CONFIG_PROFILE_SHIFT 2
 fi
 
 source crypto/Config.in
diff -urN linux-2.4.22-pre3/arch/cris/defconfig linux-2.4.22-pre4/arch/cris/defconfig
--- linux-2.4.22-pre3/arch/cris/defconfig	2002-08-02 17:39:42.000000000 -0700
+++ linux-2.4.22-pre4/arch/cris/defconfig	2003-07-09 16:56:27.000000000 -0700
@@ -85,8 +85,12 @@
 # CONFIG_ETRAX_ETHERNET_LPSLAVE is not set
 CONFIG_ETRAX_SERIAL=y
 CONFIG_ETRAX_SERIAL_PORT0=y
+CONFIG_ETRAX_SERIAL_PORT0_DMA6_OUT=y
+CONFIG_ETRAX_SERIAL_PORT0_DMA7_IN=y
 # CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_ON_PB is not set
 CONFIG_ETRAX_SERIAL_PORT1=y
+CONFIG_ETRAX_SERIAL_PORT1_DMA8_OUT=y
+CONFIG_ETRAX_SERIAL_PORT1_DMA9_IN=y
 # CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_ON_PB is not set
 # CONFIG_ETRAX_SERIAL_PORT2 is not set
 # CONFIG_ETRAX_SERIAL_PORT3 is not set
diff -urN linux-2.4.22-pre3/arch/cris/drivers/Config.in linux-2.4.22-pre4/arch/cris/drivers/Config.in
--- linux-2.4.22-pre3/arch/cris/drivers/Config.in	2003-06-13 07:51:29.000000000 -0700
+++ linux-2.4.22-pre4/arch/cris/drivers/Config.in	2003-07-09 16:56:27.000000000 -0700
@@ -23,15 +23,21 @@
 
 bool 'Serial-port support' CONFIG_ETRAX_SERIAL
 if [ "$CONFIG_ETRAX_SERIAL" = "y" ]; then
-  bool '  Use fast timers for serial DMA flush (experimental)' CONFIG_ETRAX_SERIAL_FAST_TIMER
+  bool '  Use fast timers for serial DMA flush' CONFIG_ETRAX_SERIAL_FAST_TIMER
   if [ "$CONFIG_ETRAX_SERIAL_FAST_TIMER" = "n" ]; then
     bool '  Fast serial port DMA flush' CONFIG_ETRAX_SERIAL_FLUSH_DMA_FAST
     if [ "$CONFIG_ETRAX_SERIAL_FLUSH_DMA_FAST" = "n" ]; then
       int '  Receive flush timeout (ticks) ' CONFIG_ETRAX_SERIAL_RX_TIMEOUT_TICKS 5
     fi
   fi
+  bool '  Enable external clock on PB6' CONFIG_ETRAX_EXTERN_PB6CLK_ENABLED
+  if [ "$CONFIG_ETRAX_EXTERN_PB6CLK_ENABLED" = "y" ]; then
+    int '  Extern clock frequency (baudrate=clk/8) (Hz)' CONFIG_ETRAX_EXTERN_PB6CLK_FREQ
+  fi
   bool '  Serial port 0 enabled' CONFIG_ETRAX_SERIAL_PORT0
   if [ "$CONFIG_ETRAX_SERIAL_PORT0" = "y" ]; then
+  bool '  Serial port 0 uses DMA6 out' CONFIG_ETRAX_SERIAL_PORT0_DMA6_OUT
+  bool '  Serial port 0 uses DMA7 in' CONFIG_ETRAX_SERIAL_PORT0_DMA7_IN
   choice 'Ser0 DTR, RI, DSR and CD assignment' \
   "No_DTR_RI_DSR_CD      CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_ON_NONE \
    DTR_RI_DSR_CD_on_PA   CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_ON_PA \
@@ -67,6 +73,8 @@
 
   bool '  Serial port 1 enabled' CONFIG_ETRAX_SERIAL_PORT1
   if [ "$CONFIG_ETRAX_SERIAL_PORT1" = "y" ]; then
+  bool '  Serial port 1 uses DMA8 out' CONFIG_ETRAX_SERIAL_PORT1_DMA8_OUT
+  bool '  Serial port 1 uses DMA9 in' CONFIG_ETRAX_SERIAL_PORT1_DMA9_IN
   choice 'Ser1 DTR, RI, DSR and CD assignment' \
   "No_DTR_RI_DSR_CD      CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_ON_NONE \
    DTR_RI_DSR_CD_on_PA   CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_ON_PA \
@@ -106,6 +114,8 @@
   fi
   bool '  Serial port 2 enabled' CONFIG_ETRAX_SERIAL_PORT2
   if [ "$CONFIG_ETRAX_SERIAL_PORT2" = "y" ]; then
+  bool '  Serial port 2 uses DMA2 out' CONFIG_ETRAX_SERIAL_PORT2_DMA2_OUT
+  bool '  Serial port 2 uses DMA3 in' CONFIG_ETRAX_SERIAL_PORT2_DMA3_IN
   choice 'Ser2 DTR, RI, DSR and CD assignment' \
   "No_DTR_RI_DSR_CD      CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_ON_NONE \
    DTR_RI_DSR_CD_on_PA   CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_ON_PA \
@@ -139,6 +149,8 @@
   fi
   bool '  Serial port 3 enabled' CONFIG_ETRAX_SERIAL_PORT3
   if [ "$CONFIG_ETRAX_SERIAL_PORT3" = "y" ]; then
+  bool '  Serial port 3 uses DMA4 out' CONFIG_ETRAX_SERIAL_PORT3_DMA4_OUT
+  bool '  Serial port 3 uses DMA5 in' CONFIG_ETRAX_SERIAL_PORT3_DMA5_IN
   choice 'Ser3 DTR, RI, DSR and CD assignment' \
   "No_DTR_RI_DSR_CD      CONFIG_ETRAX_SER3_DTR_RI_DSR_CD_ON_NONE \
    DTR_RI_DSR_CD_on_PA   CONFIG_ETRAX_SER3_DTR_RI_DSR_CD_ON_PA \
@@ -303,6 +315,7 @@
 
 bool 'Real Time Clock support' CONFIG_ETRAX_RTC
 if [ "$CONFIG_ETRAX_RTC" = "y" ]; then
+  bool '  Enable RTC read only mode' CONFIG_ETRAX_RTC_READONLY	 
   choice '  RTC chip' \
     "DS1302 CONFIG_ETRAX_DS1302 \
      PCF8563  CONFIG_ETRAX_PCF8563" DS1302
diff -urN linux-2.4.22-pre3/arch/cris/drivers/axisflashmap.c linux-2.4.22-pre4/arch/cris/drivers/axisflashmap.c
--- linux-2.4.22-pre3/arch/cris/drivers/axisflashmap.c	2003-06-13 07:51:29.000000000 -0700
+++ linux-2.4.22-pre4/arch/cris/drivers/axisflashmap.c	2003-07-09 16:56:27.000000000 -0700
@@ -11,6 +11,9 @@
  * partition split defined below.
  *
  * $Log: axisflashmap.c,v $
+ * Revision 1.29  2003/04/01 14:12:06  starvik
+ * Added loglevel for lots of printks
+ *
  * Revision 1.28  2002/10/01 08:08:43  jonashg
  * The first partition ends at the start of the partition table.
  *
@@ -300,7 +303,8 @@
 {
 	struct mtd_info *mtd_cs = NULL;
 
-	printk("%s: Probing a 0x%08lx bytes large window at 0x%08lx.\n",
+	printk(KERN_INFO 
+	       "%s: Probing a 0x%08lx bytes large window at 0x%08lx.\n",
 	       map_cs->name, map_cs->size, map_cs->map_priv_1);
 
 #ifdef CONFIG_MTD_AMDSTD
@@ -387,7 +391,7 @@
 	struct partitiontable_head *ptable_head;
 	struct partitiontable_entry *ptable;
 	int use_default_ptable = 1; /* Until proven otherwise. */
-	const char *pmsg = "  /dev/flash%d at 0x%08x, size 0x%08x\n";
+	const char *pmsg = KERN_INFO "  /dev/flash%d at 0x%08x, size 0x%08x\n";
 
 	if (!(mymtd = flash_probe())) {
 		/* There's no reason to use this module if no flash chip can
@@ -396,7 +400,7 @@
 		panic("axisflashmap found no flash chip!\n");
 	}
 
-	printk("%s: 0x%08x bytes of flash memory.\n",
+	printk(KERN_INFO "%s: 0x%08x bytes of flash memory.\n",
 	       mymtd->name, mymtd->size);
 
 	mymtd->module = THIS_MODULE;
@@ -440,7 +444,7 @@
 		ptable_ok = (csum == ptable_head->checksum);
 
 		/* Read the entries and use/show the info.  */
-		printk(" Found a%s partition table at 0x%p-0x%p.\n",
+		printk(KERN_INFO " Found a%s partition table at 0x%p-0x%p.\n",
 		       (ptable_ok ? " valid" : "n invalid"), ptable_head,
 		       max_addr);
 
@@ -472,14 +476,14 @@
 		axis_partitions[pidx].offset = romfs_start - FLASH_CACHED_ADDR;
 		axis_partitions[pidx].mask_flags |= MTD_WRITEABLE;
 
-		printk(" Adding readonly flash partition for romfs image:\n");
+		printk(KERN_INFO " Adding readonly flash partition for romfs image:\n");
 		printk(pmsg, pidx, axis_partitions[pidx].offset,
 		       axis_partitions[pidx].size);
 		pidx++;
 	}
 
 	if (use_default_ptable) {
-		printk(" Using default partition table.\n");
+		printk(KERN_INFO " Using default partition table.\n");
 		err = add_mtd_partitions(mymtd, axis_default_partitions,
 		                         NUM_DEFAULT_PARTITIONS);
 	} else {
@@ -508,7 +512,7 @@
 			      "mtd_info!\n");
 		}
 
-		printk(" Adding RAM partition for romfs image:\n");
+		printk(KERN_INFO " Adding RAM partition for romfs image:\n");
 		printk(pmsg, pidx, romfs_start, romfs_length);
 
 		err = mtdram_init_device(mtd_ram, (void*)romfs_start, 
diff -urN linux-2.4.22-pre3/arch/cris/drivers/ds1302.c linux-2.4.22-pre4/arch/cris/drivers/ds1302.c
--- linux-2.4.22-pre3/arch/cris/drivers/ds1302.c	2003-06-13 07:51:29.000000000 -0700
+++ linux-2.4.22-pre4/arch/cris/drivers/ds1302.c	2003-07-09 16:56:27.000000000 -0700
@@ -4,9 +4,22 @@
 *!
 *! DESCRIPTION: Implements an interface for the DS1302 RTC through Etrax I/O
 *!
-*! Functions exported: ds1302_readreg, ds1302_writereg, ds1302_init, get_rtc_status
+*! Functions exported: ds1302_readreg, ds1302_writereg, ds1302_init
 *!
 *! $Log: ds1302.c,v $
+*! Revision 1.19  2003/06/12 08:02:05  johana
+*! Removed faulty comma from printk.
+*! Fixed warning () -> (void)
+*!
+*! Revision 1.18  2003/04/16 09:02:28  oskarp
+*! * Merged change_branch--rtc_readonly to main.
+*!
+*! Revision 1.17  2003/04/01 14:12:06  starvik
+*! Added loglevel for lots of printks
+*!
+*! Revision 1.16  2003/02/19 15:51:16  cedric
+*! Start the clock from ds1302_init()
+*!
 *! Revision 1.15  2002/10/11 16:14:33  johana
 *! Added CONFIG_ETRAX_DS1302_TRICKLE_CHARGE and initial setting of the
 *! trcklecharge register.
@@ -95,7 +108,7 @@
 *!
 *! (C) Copyright 1999, 2000, 2001  Axis Communications AB, LUND, SWEDEN
 *!
-*! $Id: ds1302.c,v 1.15 2002/10/11 16:14:33 johana Exp $
+*! $Id: ds1302.c,v 1.19 2003/06/12 08:02:05 johana Exp $
 *!
 *!***************************************************************************/
 
@@ -263,12 +276,23 @@
 void
 ds1302_writereg(int reg, unsigned char val) 
 {
-	ds1302_wenable();
-	start();
-	out_byte(0x80 | (reg << 1)); /* write register */
-	out_byte(val);
-	stop();
-	ds1302_wdisable();
+#ifndef CONFIG_ETRAX_RTC_READONLY
+	int do_writereg = 1;
+#else
+	int do_writereg = 0;
+	
+	if (reg == RTC_TRICKLECHARGER)
+		do_writereg = 1;
+#endif
+	
+	if (do_writereg) {
+		ds1302_wenable();
+		start();
+		out_byte(0x80 | (reg << 1)); /* write register */
+		out_byte(val);
+		stop();
+		ds1302_wdisable();
+	}
 }
 
 void
@@ -405,20 +429,33 @@
 			tcs_val = RTC_TCR_PATTERN | (tcs_val & 0x0F);
 			ds1302_writereg(RTC_TRICKLECHARGER, tcs_val);
 			return 0;
-		}                
+		}
+		case RTC_VLOW_RD:
+		{
+			/* TODO: 
+			 * Implement voltage low detection support 
+			 */
+			printk(KERN_WARNING "DS1302: RTC Voltage Low detection"
+			       " is not supported\n");
+			return 0;
+		}
+		case RTC_VLOW_SET:
+		{
+			/* TODO:
+			 * Nothing to do since Voltage Low detection is not supported
+			 */
+			return 0;
+		}
 		default:
 			return -ENOIOCTLCMD;
 	}
 }
 
-int
-get_rtc_status(char *buf) 
+static void
+print_rtc_status(void) 
 {
-	char *p;
 	struct rtc_time tm;
 
-	p = buf;
-
 	get_rtc_time(&tm);
 
 	/*
@@ -426,13 +463,10 @@
 	 * time or for Universal Standard Time (GMT). Probably local though.
 	 */
 
-	p += sprintf(p,
-		"rtc_time\t: %02d:%02d:%02d\n"
-		"rtc_date\t: %04d-%02d-%02d\n",
-		tm.tm_hour, tm.tm_min, tm.tm_sec,
-		tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
-
-	return  p - buf;
+	printk(KERN_INFO "rtc_time\t: %02d:%02d:%02d\n",
+	       tm.tm_hour, tm.tm_min, tm.tm_sec);
+	printk(KERN_INFO "rtc_date\t: %04d-%02d-%02d\n",
+	       tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
 }
 
 
@@ -466,11 +500,10 @@
 	out_byte(0xc1); /* read RAM byte 0 */
 
 	if((res = in_byte()) == MAGIC_PATTERN) {
-		char buf[100];
 		stop();
 		ds1302_wdisable();
-		printk("%s: RTC found.\n", ds1302_name);
-		printk("%s: SDA, SCL, RST on PB%i, PB%i, %s%i\n",
+		printk(KERN_INFO "%s: RTC found.\n", ds1302_name);
+		printk(KERN_INFO "%s: SDA, SCL, RST on PB%i, PB%i, %s%i\n",
 		       ds1302_name,
 		       CONFIG_ETRAX_DS1302_SDABIT,
 		       CONFIG_ETRAX_DS1302_SCLBIT,
@@ -480,12 +513,10 @@
 		       "PB",
 #endif
 		       CONFIG_ETRAX_DS1302_RSTBIT);
-                get_rtc_status(buf);
-                printk(buf);
+                print_rtc_status();
 		retval = 1;
 	} else {
 		stop();
-		printk("%s: RTC not found.\n", ds1302_name);
 		retval = 0;
 	}
 
@@ -518,9 +549,12 @@
 				   (IO_STATE(R_GEN_CONFIG, g0dir, out)));    
     		*R_GEN_CONFIG = genconfig_shadow;
 #endif
-		if (!ds1302_probe())
+		if (!ds1302_probe()) {
+			printk(KERN_WARNING "%s: RTC not found.\n", ds1302_name);
       			return -1;
+		}
 #else
+		printk(KERN_WARNING "%s: RTC not found.\n", ds1302_name);
     		return -1;
 #endif
   	}
@@ -533,5 +567,7 @@
 	/* Initialise trickle charger */
 	ds1302_writereg(RTC_TRICKLECHARGER,
 			RTC_TCR_PATTERN |(CONFIG_ETRAX_DS1302_TRICKLE_CHARGE & 0x0F));
+	// Start clock by resetting CLOCK_HALT
+	ds1302_writereg(RTC_SECONDS, (ds1302_readreg(RTC_SECONDS) & 0x7F));
 	return 0;
 }
diff -urN linux-2.4.22-pre3/arch/cris/drivers/eeprom.c linux-2.4.22-pre4/arch/cris/drivers/eeprom.c
--- linux-2.4.22-pre3/arch/cris/drivers/eeprom.c	2003-06-13 07:51:29.000000000 -0700
+++ linux-2.4.22-pre4/arch/cris/drivers/eeprom.c	2003-07-09 16:56:27.000000000 -0700
@@ -1,4 +1,4 @@
-/*!*****************************************************************************
+/******************************************************************************
 *!
 *!  Implements an interface for i2c compatible eeproms to run under linux.
 *!  Supports 2k, 8k(?) and 16k. Uses adaptive timing adjustents by
@@ -20,6 +20,18 @@
 *!                                  in the spin-lock.
 *!
 *!  $Log: eeprom.c,v $
+*!  Revision 1.12  2003/04/09 08:31:14  pkj
+*!  Typo correction (taken from Linux 2.5).
+*!
+*!  Revision 1.11  2003/02/12 20:43:46  johana
+*!  Previous checkin removed beginning of comment.
+*!
+*!  Revision 1.10  2003/02/10 07:18:20  starvik
+*!  Removed misplaced ;
+*!
+*!  Revision 1.9  2003/01/22 06:54:46  starvik
+*!  Fixed warnings issued by GCC 3.2.1
+*!
 *!  Revision 1.8  2001/06/15 13:24:29  jonashg
 *!  * Added verification of pointers from userspace in read and write.
 *!  * Made busy counter volatile.
@@ -203,7 +215,7 @@
      * it will mirror the address space:
      * 1. We read two locations (that are mirrored), 
      *    if the content differs * it's a 16kB EEPROM.
-     * 2. if it doesn't differ - write diferent value to one of the locations,
+     * 2. if it doesn't differ - write different value to one of the locations,
      *    check the other - if content still is the same it's a 2k EEPROM,
      *    restore original data.
      */
@@ -493,7 +505,7 @@
 
 static ssize_t eeprom_read(struct file * file, char * buf, size_t count, loff_t *off)
 {
-  int i, read=0;
+  int read=0;
   unsigned long p = file->f_pos;
 
   unsigned char page;
@@ -519,7 +531,7 @@
   if(!eeprom_address(p))
   {
     printk(KERN_INFO "%s: Read failed to address the eeprom: "
-           "0x%08X (%i) page: %i\n", eeprom_name, p, p, page);
+           "0x%08lX (%li) page: %i\n", eeprom_name, p, p, page);
     i2c_stop();
     
     /* don't forget to wake them up */
@@ -602,7 +614,7 @@
       if(!eeprom_address(p))
       {
         printk(KERN_INFO "%s: Write failed to address the eeprom: "
-               "0x%08X (%i) \n", eeprom_name, p, p);
+               "0x%08lX (%li) \n", eeprom_name, p, p);
         i2c_stop();
         
         /* don't forget to wake them up */
@@ -740,7 +752,7 @@
 
 static int eeprom_address(unsigned long addr)
 {
-  int i, j;
+  int i;
   unsigned char page, offset;
 
   page   = (unsigned char) (addr >> 8);
@@ -795,7 +807,7 @@
   return 1;
 }
 
-/* Reads from current adress. */
+/* Reads from current address. */
 
 static int read_from_eeprom(char * buf, int count)
 {
diff -urN linux-2.4.22-pre3/arch/cris/drivers/ethernet.c linux-2.4.22-pre4/arch/cris/drivers/ethernet.c
--- linux-2.4.22-pre3/arch/cris/drivers/ethernet.c	2003-06-13 07:51:29.000000000 -0700
+++ linux-2.4.22-pre4/arch/cris/drivers/ethernet.c	2003-07-09 16:56:27.000000000 -0700
@@ -1,4 +1,4 @@
-/* $Id: ethernet.c,v 1.34 2002/12/13 07:17:44 starvik Exp $
+/* $Id: ethernet.c,v 1.44 2003/07/01 10:55:07 starvik Exp $
  *
  * e100net.c: A network driver for the ETRAX 100LX network controller.
  *
@@ -7,9 +7,52 @@
  * The outline of this driver comes from skeleton.c.
  *
  * $Log: ethernet.c,v $
- * Revision 1.34  2002/12/13 07:17:44  starvik
- * Basic ethtool and MII ioctls
- * Handle out of memory when allocating new buffers
+ * Revision 1.44  2003/07/01 10:55:07  starvik
+ * Never bring down link to make stupid POE equipment happy
+ *
+ * Revision 1.43  2003/04/24 08:26:50  starvik
+ * New LED behaviour: LED off when no link
+ *
+ * Revision 1.42  2003/04/10 07:14:58  starvik
+ * Merge of changes from todo list
+ *
+ * Revision 1.41  2003/04/09 08:31:14  pkj
+ * Typo correction (taken from Linux 2.5).
+ *
+ * Revision 1.40  2003/04/01 14:12:06  starvik
+ * Added loglevel for lots of printks
+ *
+ * Revision 1.39.2.3  2003/04/01 07:51:14  starvik
+ * Default Ethernet Stations Address can be specified with command line option.
+ *
+ * Revision 1.39.2.2  2003/03/31 15:41:07  starvik
+ * Only communicate with transciever when ETRAX is properly configured.
+ *
+ * Revision 1.39.2.1  2003/03/31 14:12:46  starvik
+ * Transmit interrupt always enabled. This has two side effects:
+ *   1. UDP (and possibly other protocols) works on quiet networks
+ *   2. Slightly lower transmit performance
+ *
+ * Revision 1.39  2003/03/06 15:45:39  henriken
+ * Off by one error in group address register setting.
+ *
+ * Revision 1.38  2003/02/27 17:23:07  starvik
+ * Corrected Rev to Revision
+ *
+ * Revision 1.37  2003/02/27 10:52:59  magnusmn
+ * More generic transceiver support.
+ *
+ * Revision 1.33.2.4  2003/02/24 16:51:26  magnusmn
+ * TDK specific registers to check speed/duplex
+ *
+ * Revision 1.33.2.3  2003/02/24 09:16:31  magnusmn
+ * ethernet.c
+ *
+ * Revision 1.33.2.2  2003/02/21 11:57:27  magnusmn
+ * Merging differences between 1.36 and 1.33 into ethernet.c (needed ethtool ioctl)
+ *
+ * Revision 1.33.2.1  2002/12/04 07:54:49  starvik
+ * First attempt to get more generic transceiver support
  *
  * Revision 1.33  2002/10/02 20:16:17  hp
  * SETF, SETS: Use underscored IO_x_ macros rather than incorrect token concatenation
@@ -220,6 +263,16 @@
 	struct sk_buff* skb;
 } etrax_eth_descr;
 
+/* Some transceivers requires special handling */
+struct transceiver_ops
+{
+	unsigned int oui;
+	void (*check_speed)(void);
+	void (*check_duplex)(void);
+};
+
+struct transceiver_ops* transceiver;
+
 /* Duplex settings */
 enum duplex
 {
@@ -240,10 +293,17 @@
 */
 #define MDIO_BASE_STATUS_REG                0x1
 #define MDIO_BASE_CONTROL_REG               0x0
+#define MDIO_PHY_ID_HIGH_REG                0x2
+#define MDIO_PHY_ID_LOW_REG                 0x3
 #define MDIO_BC_NEGOTIATE                0x0200
 #define MDIO_BC_FULL_DUPLEX_MASK         0x0100
 #define MDIO_BC_AUTO_NEG_MASK            0x1000
 #define MDIO_BC_SPEED_SELECT_MASK        0x2000
+#define MDIO_STATUS_100_FD               0x4000
+#define MDIO_STATUS_100_HD               0x2000
+#define MDIO_STATUS_10_FD                0x1000
+#define MDIO_STATUS_10_HD                0x0800
+#define MDIO_STATUS_SPEED_DUPLEX_MASK	 0x7800
 #define MDIO_ADVERTISMENT_REG               0x4
 #define MDIO_ADVERT_100_FD                0x100
 #define MDIO_ADVERT_100_HD                0x080
@@ -257,9 +317,13 @@
 
 /* Broadcom specific */
 #define MDIO_AUX_CTRL_STATUS_REG           0x18
-#define MDIO_FULL_DUPLEX_IND                0x1
-#define MDIO_SPEED                          0x2
-#define MDIO_PHYS_ADDR                      0x0
+#define MDIO_BC_FULL_DUPLEX_IND             0x1
+#define MDIO_BC_SPEED                       0x2
+
+/* TDK specific */
+#define MDIO_TDK_DIAGNOSTIC_REG              18
+#define MDIO_TDK_DIAGNOSTIC_RATE          0x400
+#define MDIO_TDK_DIAGNOSTIC_DPLX          0x800
 
 /* Network flash constants */
 #define NET_FLASH_TIME                  (HZ/50) /* 20 ms */
@@ -303,11 +367,12 @@
 static etrax_eth_descr TxDescList[NBR_OF_TX_DESC] __attribute__ ((aligned(32)));
 
 static unsigned int network_rec_config_shadow = 0;
+static unsigned int mdio_phy_addr; /* Transciever address */
 
 /* Network speed indication. */
 static struct timer_list speed_timer;
 static struct timer_list clear_led_timer;
-static int current_speed; /* Speed read from tranceiver */
+static int current_speed; /* Speed read from transceiver */
 static int current_speed_selection; /* Speed selected by user */
 static int led_next_time;
 static int led_active;
@@ -337,6 +402,7 @@
 static void e100_hardware_send_packet(char *buf, int length);
 static void update_rx_stats(struct net_device_stats *);
 static void update_tx_stats(struct net_device_stats *);
+static int e100_probe_transceiver(void);
 
 static void e100_check_speed(unsigned long dummy);
 static void e100_set_speed(unsigned long speed);
@@ -349,11 +415,25 @@
 static void e100_send_mdio_cmd(unsigned short cmd, int write_cmd);
 static void e100_send_mdio_bit(unsigned char bit);
 static unsigned char e100_receive_mdio_bit(void);
-static void e100_reset_tranceiver(void);
+static void e100_reset_transceiver(void);
 
 static void e100_clear_network_leds(unsigned long dummy);
 static void e100_set_network_leds(int active);
 
+static void broadcom_check_speed(void);
+static void broadcom_check_duplex(void);
+static void tdk_check_speed(void);
+static void tdk_check_duplex(void);
+static void generic_check_speed(void);
+static void generic_check_duplex(void);
+
+struct transceiver_ops transceivers[] = 
+{
+	{0x1018, broadcom_check_speed, broadcom_check_duplex},  /* Broadcom */
+	{0xC039, tdk_check_speed, tdk_check_duplex},            /* TDK */
+	{0x0000, generic_check_speed, generic_check_duplex}     /* Generic, must be last */
+};
+
 #define tx_done(dev) (*R_DMA_CH0_CMD == 0)
 
 /*
@@ -369,11 +449,12 @@
 {
 	int i;
 
-	printk("ETRAX 100LX 10/100MBit ethernet v2.0 (c) 2000-2001 Axis Communications AB\n");
+	printk(KERN_INFO
+	       "ETRAX 100LX 10/100MBit ethernet v2.0 (c) 2000-2001 Axis Communications AB\n");
 
 	dev->base_addr = (unsigned int)R_NETWORK_SA_0; /* just to have something to show */
 
-	printk("%s initialized\n", dev->name);
+	printk(KERN_INFO "%s initialized\n", dev->name);
 
 	/* make Linux aware of the new hardware  */
 
@@ -468,7 +549,6 @@
 	current_speed_selection = 0; /* Auto */
 	speed_timer.expires = jiffies + NET_LINK_UP_CHECK_INTERVAL;
 	speed_timer.function = e100_check_speed;
-	add_timer(&speed_timer);
         
 	clear_led_timer.function = e100_clear_network_leds;
         
@@ -476,8 +556,11 @@
 	current_duplex = autoneg;
 	duplex_timer.expires = jiffies + NET_DUPLEX_CHECK_INTERVAL;		
 	duplex_timer.function = e100_check_duplex;
-	add_timer(&duplex_timer);
 
+        /* Initialize group address registers to make sure that no */
+        /* unwanted addresses are matched */
+	*R_NETWORK_GA_0 = 0x00000000;
+	*R_NETWORK_GA_1 = 0x00000000;
 	return 0;
 }
 
@@ -508,8 +591,7 @@
 
 	/* show it in the log as well */
 
-	printk("%s: changed MAC to ", dev->name);
-
+	printk(KERN_INFO "%s: changed MAC to ", dev->name);
 	for (i = 0; i < 5; i++)
 		printk("%02X:", dev->dev_addr[i]);
 
@@ -532,12 +614,6 @@
 {
 	unsigned long flags;
 
-	/* disable the ethernet interface while we configure it */
-
-	*R_NETWORK_GEN_CONFIG =
-		IO_STATE(R_NETWORK_GEN_CONFIG, phy,    mii_clk) |
-		IO_STATE(R_NETWORK_GEN_CONFIG, enable, off);
-
 	/* enable the MDIO output pin */
 
 	*R_NETWORK_MGM_CTRL = IO_STATE(R_NETWORK_MGM_CTRL, mdoe, enable);
@@ -636,7 +712,8 @@
 	/* enable the irq's for ethernet DMA */
 
 	*R_IRQ_MASK2_SET =
-		IO_STATE(R_IRQ_MASK2_SET, dma1_eop, set); 
+		IO_STATE(R_IRQ_MASK2_SET, dma0_eop, set) |
+		IO_STATE(R_IRQ_MASK2_SET, dma1_eop, set);
 
 	*R_IRQ_MASK0_SET =
 		IO_STATE(R_IRQ_MASK0_SET, overrun,       set) |
@@ -665,6 +742,14 @@
 
 	restore_flags(flags);
 	
+	/* Probe for transceiver */
+	if (e100_probe_transceiver())
+		goto grace_exit4;
+
+	/* Start duplex/speed timers */
+	add_timer(&speed_timer);
+	add_timer(&duplex_timer);
+
 	/* We are now ready to accept transmit requeusts from
 	 * the queueing layer of the networking.
 	 */
@@ -684,6 +769,33 @@
 	return -EAGAIN;
 }
 
+static void
+generic_check_speed(void)
+{
+	unsigned long data;
+	data = e100_get_mdio_reg(MDIO_ADVERTISMENT_REG);
+	if ((data & MDIO_ADVERT_100_FD) ||
+	    (data & MDIO_ADVERT_100_HD))
+		current_speed = 100;
+	else
+		current_speed = 10;
+}
+
+static void
+tdk_check_speed(void)
+{
+	unsigned long data;
+	data = e100_get_mdio_reg(MDIO_TDK_DIAGNOSTIC_REG);
+	current_speed = (data & MDIO_TDK_DIAGNOSTIC_RATE ? 100 : 10);
+}
+
+static void
+broadcom_check_speed(void)
+{
+	unsigned long data;
+	data = e100_get_mdio_reg(MDIO_AUX_CTRL_STATUS_REG);
+	current_speed = (data & MDIO_BC_SPEED ? 100 : 10);
+}
 
 static void
 e100_check_speed(unsigned long dummy)
@@ -695,8 +807,7 @@
 	if (!(data & MDIO_LINK_UP_MASK)) {
 		current_speed = 0;
 	} else {
-		data = e100_get_mdio_reg(MDIO_AUX_CTRL_STATUS_REG);
-		current_speed = (data & MDIO_SPEED ? 100 : 10);
+		transceiver->check_speed();
 	}
 	
 	if (old_speed != current_speed)
@@ -761,29 +872,21 @@
 static void
 e100_set_speed(unsigned long speed)
 {
-	current_speed_selection = speed;
-	e100_negotiate();
+	if (speed != current_speed_selection) {
+		current_speed_selection = speed;
+		e100_negotiate();
+	}
 }
 
 static void
 e100_check_duplex(unsigned long dummy)
 {
-	unsigned long data;
-
-	data = e100_get_mdio_reg(MDIO_AUX_CTRL_STATUS_REG);
-        
-	if (data & MDIO_FULL_DUPLEX_IND) {
-		if (!full_duplex) { /* Duplex changed to full? */
-			full_duplex = 1;
-			SETF(network_rec_config_shadow, R_NETWORK_REC_CONFIG, duplex, full_duplex);
-			*R_NETWORK_REC_CONFIG = network_rec_config_shadow;
-		}
-	} else { /* half */
-		if (full_duplex) { /* Duplex changed to half? */
-			full_duplex = 0;
-			SETF(network_rec_config_shadow, R_NETWORK_REC_CONFIG, duplex, full_duplex);
-			*R_NETWORK_REC_CONFIG = network_rec_config_shadow;
-		}
+	int old_duplex = full_duplex;
+	transceiver->check_duplex();
+	if (old_duplex != full_duplex) { 
+		/* Duplex changed */
+		SETF(network_rec_config_shadow, R_NETWORK_REC_CONFIG, duplex, full_duplex);
+		*R_NETWORK_REC_CONFIG = network_rec_config_shadow;
 	}
 
 	/* Reinitialize the timer. */
@@ -791,13 +894,71 @@
 	add_timer(&duplex_timer);
 }
 
+static void
+generic_check_duplex(void)
+{
+	unsigned long data;
+	data = e100_get_mdio_reg(MDIO_ADVERTISMENT_REG);
+	if ((data & MDIO_ADVERT_100_FD) ||
+	    (data & MDIO_ADVERT_10_FD))
+		full_duplex = 1;
+	else
+		full_duplex = 0;
+}
+
+static void
+tdk_check_duplex(void)
+{
+	unsigned long data;
+	data = e100_get_mdio_reg(MDIO_TDK_DIAGNOSTIC_REG);
+	full_duplex = (data & MDIO_TDK_DIAGNOSTIC_DPLX) ? 1 : 0;
+}
+
+static void
+broadcom_check_duplex(void)
+{
+	unsigned long data;
+	data = e100_get_mdio_reg(MDIO_AUX_CTRL_STATUS_REG);        
+	full_duplex = (data & MDIO_BC_FULL_DUPLEX_IND) ? 1 : 0;
+}
+
 static void 
 e100_set_duplex(enum duplex new_duplex)
 {
-	current_duplex = new_duplex;
-	e100_negotiate();
+	if (new_duplex != current_duplex) {
+		current_duplex = new_duplex;
+		e100_negotiate();
+	}
 }
 
+static int 
+e100_probe_transceiver(void)
+{
+	unsigned int phyid_high;
+	unsigned int phyid_low;
+	unsigned int oui;
+	struct transceiver_ops* ops = NULL;
+
+	/* Probe MDIO physical address */
+	for (mdio_phy_addr = 0; mdio_phy_addr <= 31; mdio_phy_addr++) {
+		if (e100_get_mdio_reg(MDIO_BASE_STATUS_REG) != 0xffff)
+			break;
+	}
+	if (mdio_phy_addr == 32)
+		 return -ENODEV;
+
+	/* Get manufacturer */
+	phyid_high = e100_get_mdio_reg(MDIO_PHY_ID_HIGH_REG);
+	phyid_low = e100_get_mdio_reg(MDIO_PHY_ID_LOW_REG);
+	oui = (phyid_high << 6) | (phyid_low >> 10);
+	
+	for (ops = &transceivers[0]; ops->oui; ops++) {
+		if (ops->oui == oui)
+			break;
+	}
+	transceiver = ops;
+	return 0;
+}
 
 static unsigned short
 e100_get_mdio_reg(unsigned char reg_num)
@@ -807,7 +968,7 @@
 	int bitCounter;
 	
 	/* Start of frame, OP Code, Physical Address, Register Address */
-	cmd = (MDIO_START << 14) | (MDIO_READ << 12) | (MDIO_PHYS_ADDR << 7) |
+	cmd = (MDIO_START << 14) | (MDIO_READ << 12) | (mdio_phy_addr << 7) |
 		(reg_num << 2);
 	
 	e100_send_mdio_cmd(cmd, 0);
@@ -828,7 +989,7 @@
 	int bitCounter;
 	unsigned short cmd;
 
-	cmd = (MDIO_START << 14) | (MDIO_WRITE << 12) | (MDIO_PHYS_ADDR << 7) |
+	cmd = (MDIO_START << 14) | (MDIO_WRITE << 12) | (mdio_phy_addr << 7) |
 	      (reg << 2);
 
 	e100_send_mdio_cmd(cmd, 1);
@@ -888,7 +1049,7 @@
 }
 
 static void 
-e100_reset_tranceiver(void)
+e100_reset_transceiver(void)
 {
 	unsigned short cmd;
 	unsigned short data;
@@ -896,7 +1057,7 @@
 
 	data = e100_get_mdio_reg(MDIO_BASE_CONTROL_REG);
 
-	cmd = (MDIO_START << 14) | (MDIO_WRITE << 12) | (MDIO_PHYS_ADDR << 7) | (MDIO_BASE_CONTROL_REG << 2);
+	cmd = (MDIO_START << 14) | (MDIO_WRITE << 12) | (mdio_phy_addr << 7) | (MDIO_BASE_CONTROL_REG << 2);
 
 	e100_send_mdio_cmd(cmd, 1);
 	
@@ -928,9 +1089,9 @@
 	RESET_DMA(NETWORK_TX_DMA_NBR);
 	WAIT_DMA(NETWORK_TX_DMA_NBR);
 	
-	/* Reset the tranceiver. */
+	/* Reset the transceiver. */
 	
-	e100_reset_tranceiver();
+	e100_reset_transceiver();
 	
 	/* and get rid of the packets that never got an interrupt */
 	while (myFirstTxDesc != myNextTxDesc)
@@ -978,9 +1139,6 @@
 
 	/* Stop queue if full */
 	if (myNextTxDesc == myFirstTxDesc) {
-		/* Enable transmit interrupt to wake up queue */
-		*R_DMA_CH0_CLR_INTR = IO_STATE(R_DMA_CH0_CLR_INTR, clr_eop, do);		
-		*R_IRQ_MASK2_SET = IO_STATE(R_IRQ_MASK2_SET, dma0_eop, set);
 		netif_stop_queue(dev);
 	}
 
@@ -1001,6 +1159,11 @@
 	struct net_local *np = (struct net_local *)dev->priv;
 	unsigned long irqbits = *R_IRQ_MASK2_RD;
  
+	/* Disable RX/TX IRQs to avoid reentrancy */
+	*R_IRQ_MASK2_CLR = 
+	  IO_STATE(R_IRQ_MASK2_CLR, dma0_eop, clr) |
+	  IO_STATE(R_IRQ_MASK2_CLR, dma1_eop, clr);
+        
 	/* Handle received packets */
 	if (irqbits & IO_STATE(R_IRQ_MASK2_RD, dma1_eop, active)) {
 		/* acknowledge the eop interrupt */
@@ -1044,9 +1207,13 @@
 	if (irqbits & IO_STATE(R_IRQ_MASK2_RD, dma0_eop, active)) {
 		/* acknowledge the eop interrupt and wake up queue */
 		*R_DMA_CH0_CLR_INTR = IO_STATE(R_DMA_CH0_CLR_INTR, clr_eop, do);
-		*R_IRQ_MASK2_CLR = IO_STATE(R_IRQ_MASK2_CLR, dma0_eop, clr);		
 		netif_wake_queue(dev);
 	}
+
+	/* Enable RX/TX IRQs again */
+	*R_IRQ_MASK2_SET = 
+	  IO_STATE(R_IRQ_MASK2_SET, dma0_eop, set) |
+	  IO_STATE(R_IRQ_MASK2_SET, dma1_eop, set);
 }
 
 static void
@@ -1183,14 +1350,10 @@
 {
 	struct net_local *np = (struct net_local *)dev->priv;
 
-	printk("Closing %s.\n", dev->name);
+	printk(KERN_INFO "Closing %s.\n", dev->name);
 
 	netif_stop_queue(dev);
 
-	*R_NETWORK_GEN_CONFIG =
-		IO_STATE(R_NETWORK_GEN_CONFIG, phy,    mii_clk) |
-		IO_STATE(R_NETWORK_GEN_CONFIG, enable, off);
-	
 	*R_IRQ_MASK0_CLR =
 		IO_STATE(R_IRQ_MASK0_CLR, overrun, clr) |
 		IO_STATE(R_IRQ_MASK0_CLR, underrun, clr) |
@@ -1221,6 +1384,9 @@
 	update_rx_stats(&np->stats);
 	update_tx_stats(&np->stats);
 
+	/* Stop speed/duplex timers */
+	del_timer(&speed_timer);
+	del_timer(&duplex_timer);
 	return 0;
 }
 
@@ -1233,7 +1399,7 @@
 		case SIOCETHTOOL:
 			return e100_ethtool_ioctl(dev,ifr);
 		case SIOCGMIIPHY: /* Get PHY address */
-			data->phy_id = MDIO_PHYS_ADDR;
+			data->phy_id = mdio_phy_addr;
 			break;
 		case SIOCGMIIREG: /* Read MII register */
 			data->val_out = e100_get_mdio_reg(data->reg_num);
@@ -1252,7 +1418,7 @@
 		case SET_ETH_SPEED_AUTO:              /* Auto negotiate speed */
 			e100_set_speed(0);
 			break;
-		case SET_ETH_DUPLEX_HALF:              /* Hhalf duplex. */
+		case SET_ETH_DUPLEX_HALF:              /* Half duplex. */
 			e100_set_duplex(half);
 			break;
 		case SET_ETH_DUPLEX_FULL:              /* Full duplex. */
@@ -1285,7 +1451,7 @@
 			  SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full;
 			ecmd.port = PORT_TP;
 			ecmd.transceiver = XCVR_EXTERNAL;
-			ecmd.phy_address = MDIO_PHYS_ADDR;
+			ecmd.phy_address = mdio_phy_addr;
 			ecmd.speed = current_speed;
 			ecmd.duplex = full_duplex ? DUPLEX_FULL : DUPLEX_HALF;
 			ecmd.advertising = ADVERTISED_TP;
@@ -1328,7 +1494,7 @@
 			struct ethtool_drvinfo info;
 			memset((void *) &info, 0, sizeof (info));
 			strncpy(info.driver, "ETRAX 100LX", sizeof(info.driver) - 1);
-			strncpy(info.version, "$Rev$", sizeof(info.version) - 1);
+			strncpy(info.version, "$Revision: 1.44 $", sizeof(info.version) - 1);
 			strncpy(info.fw_version, "N/A", sizeof(info.fw_version) - 1);
 			strncpy(info.bus_info, "N/A", sizeof(info.bus_info) - 1);
 			info.regdump_len = 0;
@@ -1457,7 +1623,7 @@
 			
 			hash_ix &= 0x3f;
 			
-			if (hash_ix > 32) {
+			if (hash_ix >= 32) {
 				hi_bits |= (1 << (hash_ix-32));
 			}
 			else {
@@ -1526,7 +1692,7 @@
 
 	if (!current_speed) {
 		/* Make LED red, link is down */
-		LED_NETWORK_SET(LED_RED);
+		LED_NETWORK_SET(LED_OFF);
 	}
 	else if (light_leds) {
 		if (current_speed == 10) {
@@ -1555,4 +1721,26 @@
 		return -ENODEV;
 }
 
+static int __init
+e100_boot_setup(char* str)
+{
+	struct sockaddr sa = {0};
+	int i;
+
+	/* Parse the colon separated Ethernet station address */
+	for (i = 0; i <  ETH_ALEN; i++) {
+		unsigned int tmp;
+		if (sscanf(str + 3*i, "%2x", &tmp) != 1) {
+			printk(KERN_WARNING "Malformed station address");
+			return 0;
+		}
+		sa.sa_data[i] = (char)tmp;
+	}	
+
+	default_mac = sa;
+	return 1;
+}
+
+__setup("etrax100_eth=", e100_boot_setup);
+
 module_init(etrax_init_module);
diff -urN linux-2.4.22-pre3/arch/cris/drivers/examples/kiobuftest.c linux-2.4.22-pre4/arch/cris/drivers/examples/kiobuftest.c
--- linux-2.4.22-pre3/arch/cris/drivers/examples/kiobuftest.c	2001-04-06 10:42:55.000000000 -0700
+++ linux-2.4.22-pre4/arch/cris/drivers/examples/kiobuftest.c	2003-07-09 16:56:27.000000000 -0700
@@ -4,18 +4,12 @@
  *
  * It is necessary because the pages the virtual pointers reference, might
  * not exist in memory (could be mapped to the zero-page, filemapped etc)
- * and DMA cannot trigger the MMU to force them in (and would have time 
+ * and DMA cannot trigger the MMU to force them in (and would have time
  * contraints making it impossible to wait for it anyway).
  *
- * Author:  Bjorn Wesen
- *
- * $Log: kiobuftest.c,v $
- * Revision 1.2  2001/02/27 13:52:50  bjornw
- * malloc.h -> slab.h
- *
- * Revision 1.1  2001/01/19 15:57:49  bjornw
- * Example of how to do direct HW -> user-mode DMA
+ * Copyright (c) 2001, 2002, 2003  Axis Communications AB
  *
+ * Author:  Bjorn Wesen
  *
  */
 
@@ -32,55 +26,55 @@
 
 #define KIOBUFTEST_MAJOR 124  /* in the local range, experimental */
 
-
 static ssize_t
 kiobuf_read(struct file *filp, char *buf, size_t len, loff_t *ppos)
 {
+	struct kiobuf *iobuf;
+	int res, i;
 
-     struct kiobuf *iobuf;
-     int res, i;
- 
-     /* Make a kiobuf that maps the entire length the reader has given
-      * us
-      */
-
-     res = alloc_kiovec(1, &iobuf);
-     if (res)
-	     return res;
-     
-     if((res = map_user_kiobuf(READ, iobuf, (unsigned long)buf, len))) {
-	     printk("map_user_kiobuf failed, return %d\n", res);
-	     return res;
-     }
-
-     /* At this point, the virtual area buf[0] -> buf[len-1] will
-      * have corresponding pages mapped in physical memory and locked
-      * until we unmap the kiobuf. They cannot be swapped out or moved
-      * around.
-      */
-
-     printk("nr_pages == %d\noffset == %d\nlength == %d\n",
-	    iobuf->nr_pages, iobuf->offset, iobuf->length);
-
-     for(i = 0; i < iobuf->nr_pages; i++) {
-	     printk("page_add(maplist[%d]) == 0x%x\n", i,
-		    page_address(iobuf->maplist[i]));
-     }
-
-     /* This is the place to create the necessary scatter-gather vector
-      * for the DMA using the iobuf->maplist array and page_address
-      * (don't forget __pa if the DMA needs the actual physical DRAM address)
-      * and run it.
-      */
+	/*
+	 * Make a kiobuf that maps the entire length the reader has given us.
+	 */
 
+	res = alloc_kiovec(1, &iobuf);
+	if (res)
+		return res;
+
+	if ((res = map_user_kiobuf(READ, iobuf, (unsigned long)buf, len))) {
+		printk("map_user_kiobuf failed, return %d\n", res);
+		free_kiovec(1, &iobuf);
+		return res;
+	}
+
+	/*
+	 * At this point, the virtual area buf[0] -> buf[len-1] will have
+	 * corresponding pages mapped in physical memory and locked until
+	 * we unmap the kiobuf. They cannot be swapped out or moved around.
+	 */
+
+	printk("nr_pages == %d\noffset == %d\nlength == %d\n",
+	       iobuf->nr_pages, iobuf->offset, iobuf->length);
+
+	for (i = 0; i < iobuf->nr_pages; i++) {
+		printk("page_add(maplist[%d]) == 0x%x\n", i,
+		       page_address(iobuf->maplist[i]));
+	}
+
+	/*
+	 * This is the place to create the necessary scatter-gather vector
+	 * for the DMA using the iobuf->maplist array and page_address (don't
+	 * forget __pa if the DMA needs the actual physical DRAM address)
+	 * and run it.
+	 */
 
 
 
-     /* Release the mapping and exit */
+	/* Release the mapping and exit */
 
-     unmap_kiobuf(iobuf); /* The unlock_kiobuf is implicit here */
+	unmap_kiobuf(iobuf); /* The unlock_kiobuf is implicit here */
+	free_kiovec(1, &iobuf);
 
-     return len;
+	return len;
 }
 
 
@@ -97,7 +91,7 @@
 	/* register char device */
 
 	res = register_chrdev(KIOBUFTEST_MAJOR, "kiobuftest", &kiobuf_fops);
-	if(res < 0) {
+	if (res < 0) {
 		printk(KERN_ERR "kiobuftest: couldn't get a major number.\n");
 		return res;
 	}
@@ -105,4 +99,11 @@
 	printk("Initializing kiobuf-test device\n");
 }
 
+static void __exit
+kiobuftest_exit(void)
+{
+	unregister_chrdev(KIOBUFTEST_MAJOR, "kiobuftest");
+}
+
 module_init(kiobuftest_init);
+module_exit(kiobuftest_exit);
diff -urN linux-2.4.22-pre3/arch/cris/drivers/gpio.c linux-2.4.22-pre4/arch/cris/drivers/gpio.c
--- linux-2.4.22-pre3/arch/cris/drivers/gpio.c	2003-06-13 07:51:29.000000000 -0700
+++ linux-2.4.22-pre4/arch/cris/drivers/gpio.c	2003-07-09 16:56:27.000000000 -0700
@@ -1,4 +1,4 @@
-/* $Id: gpio.c,v 1.21 2002/12/02 08:11:41 starvik Exp $
+/* $Id: gpio.c,v 1.25 2003/06/24 07:24:31 johana Exp $
  *
  * Etrax general port I/O device
  *
@@ -9,6 +9,22 @@
  *             Johan Adolfsson  (read/set directions, write, port G)
  *
  * $Log: gpio.c,v $
+ * Revision 1.25  2003/06/24 07:24:31  johana
+ * Added gpio_pa_irq_enabled_mask to keep track of what interrupts we have
+ * enabled so we anly clear those that we have enabled and let bits/pins
+ * used by other drivers alone.
+ * Update gpio_some_alarms in IO_CLRALARM ioctl().
+ *
+ * Revision 1.24  2003/06/19 08:58:25  johana
+ * Turn off interrupts while fiddling with genconfig_shadow.
+ * Put some printk within D() macro.
+ *
+ * Revision 1.23  2003/04/01 14:12:06  starvik
+ * Added loglevel for lots of printks
+ *
+ * Revision 1.22  2003/01/09 18:03:47  starvik
+ * init_ioremap is now called by kernel before device drivers are initialized
+ *
  * Revision 1.21  2002/12/02 08:11:41  starvik
  * Merge of Linux 2.4.20
  *
@@ -169,6 +185,7 @@
 static struct gpio_private *alarmlist = 0;
 
 static int gpio_some_alarms = 0; /* Set if someone uses alarm */
+static unsigned long gpio_pa_irq_enabled_mask = 0;
 
 /* Port A and B use 8 bit access, but Port G is 32 bit */
 #define NUM_PORTS (GPIO_MINOR_B+1)
@@ -238,13 +255,19 @@
 	unsigned long data;
 	poll_wait(file, &priv->alarm_wq, wait);
 	if (priv->minor == GPIO_MINOR_A) {
+		unsigned long flags;
 		unsigned long tmp;
 		data = *R_PORT_PA_DATA;
 		/* PA has support for high level interrupt -
 		 * lets activate for those low and with highalarm set
 		 */
 		tmp = ~data & priv->highalarm & 0xFF;
-		*R_IRQ_MASK1_SET = (tmp << R_IRQ_MASK1_SET__pa0__BITNR);
+		tmp = (tmp << R_IRQ_MASK1_SET__pa0__BITNR);
+		save_flags(flags); cli();
+		gpio_pa_irq_enabled_mask |= tmp;
+		*R_IRQ_MASK1_SET = tmp;
+		restore_flags(flags);
+		
 	} else if (priv->minor == GPIO_MINOR_B)
 		data = *R_PORT_PB_DATA;
 	else if (priv->minor == GPIO_MINOR_G)
@@ -293,12 +316,15 @@
 {
 	unsigned long tmp;
 	/* Find what PA interrupts are active */
-	tmp = (*R_IRQ_READ1 >> R_IRQ_READ1__pa0__BITNR) & 0xFF;
+	tmp = (*R_IRQ_READ1);
+
+	/* Find those that we have enabled */
+	tmp &= gpio_pa_irq_enabled_mask;
+
 	/* Clear them.. */
-	/* NOTE: Maybe we need to be more careful here if some other
-	 * driver uses PA interrupt as well?
-	 */
-	*R_IRQ_MASK1_CLR = (tmp << R_IRQ_MASK1_CLR__pa0__BITNR);
+	*R_IRQ_MASK1_CLR = tmp;
+	gpio_pa_irq_enabled_mask &= ~tmp;
+	
 	if (gpio_some_alarms) {
 		etrax_gpio_wake_up_check();
 	}
@@ -437,7 +463,7 @@
 		p = p->next;
 	}
 	gpio_some_alarms = 0;
-		
+	
 	return 0;
 }
 
@@ -459,6 +485,7 @@
 		return ~(*priv->dir_shadow) & 0xFF; /* Only 8 bits */
 	} else if (priv->minor == GPIO_MINOR_G) {
 		/* We must fiddle with R_GEN_CONFIG to change dir */
+		save_flags(flags); cli();
 		if (((arg & dir_g_in_bits) != arg) && 
 		    (arg & changeable_dir_g)) {
 			arg &= changeable_dir_g;
@@ -483,16 +510,17 @@
 				dir_g_in_bits |= (1<<24);
 				dir_g_out_bits &= ~(1<<24);
 			}
-			printk("gpio: SETINPUT on port G set "
-				"genconfig to 0x%08lX "
-				"in_bits: 0x%08lX "
-				"out_bits: 0x%08lX\n", 
-			       (unsigned long)genconfig_shadow, 
-			       dir_g_in_bits, dir_g_out_bits);
+			D(printk(KERN_INFO "gpio: SETINPUT on port G set "
+				 "genconfig to 0x%08lX "
+				 "in_bits: 0x%08lX "
+				 "out_bits: 0x%08lX\n", 
+				 (unsigned long)genconfig_shadow, 
+				 dir_g_in_bits, dir_g_out_bits));
 			*R_GEN_CONFIG = genconfig_shadow;
 			/* Must be a >120 ns delay before writing this again */
 				
 		}
+		restore_flags(flags);
 		return dir_g_in_bits;
 	}
 	return 0;
@@ -508,7 +536,8 @@
 		restore_flags(flags);
 		return *priv->dir_shadow;
 	} else if (priv->minor == GPIO_MINOR_G) {
-		/* We must fiddle with R_GEN_CONFIG to change dir */			
+		/* We must fiddle with R_GEN_CONFIG to change dir */
+		save_flags(flags); cli();
 		if (((arg & dir_g_out_bits) != arg) &&
 		    (arg & changeable_dir_g)) {
 			/* Set bits in genconfig to set to output */
@@ -532,15 +561,16 @@
 				dir_g_out_bits |= (1<<24);
 				dir_g_in_bits &= ~(1<<24);
 			}
-			printk("gpio: SETOUTPUT on port G set "
-				"genconfig to 0x%08lX "
-				"in_bits: 0x%08lX "
-				"out_bits: 0x%08lX\n", 
-			       (unsigned long)genconfig_shadow, 
-			       dir_g_in_bits, dir_g_out_bits);
+			D(printk(KERN_INFO "gpio: SETOUTPUT on port G set "
+				 "genconfig to 0x%08lX "
+				 "in_bits: 0x%08lX "
+				 "out_bits: 0x%08lX\n", 
+				 (unsigned long)genconfig_shadow, 
+				 dir_g_in_bits, dir_g_out_bits));
 			*R_GEN_CONFIG = genconfig_shadow;
 			/* Must be a >120 ns delay before writing this again */
 		}
+		restore_flags(flags);
 		return dir_g_out_bits & 0x7FFFFFFF;
 	}
 	return 0;
@@ -605,6 +635,20 @@
 		// clear alarm for bits with 1 in arg
 		priv->highalarm &= ~arg;
 		priv->lowalarm  &= ~arg;
+		{
+			/* Must update gpio_some_alarms */
+			struct gpio_private *p = alarmlist;
+			int some_alarms;
+			some_alarms = 0;
+			while (p) {
+				if (p->highalarm | p->lowalarm) {
+					some_alarms = 1;
+					break;
+				}
+				p = p->next;
+			}
+			gpio_some_alarms = some_alarms;
+		}		
 		break;
 	case IO_READDIR: /* Use IO_SETGET_INPUT/OUTPUT instead! */
 		/* Read direction 0=input 1=output */
@@ -824,9 +868,11 @@
 	dir_g_in_bits |= (~dir_g_shadow & changeable_dir_g);
 
 
-	printk("GPIO port G: in_bits: 0x%08lX out_bits: 0x%08lX val: %08lX\n",
+	printk(KERN_INFO
+	       "GPIO port G: in_bits: 0x%08lX out_bits: 0x%08lX val: %08lX\n",
 	       dir_g_in_bits, dir_g_out_bits, (unsigned long)*R_PORT_G_DATA);
-	printk("GPIO port G: dir: %08lX changeable: %08lX\n", 
+	printk(KERN_INFO
+	       "GPIO port G: dir: %08lX changeable: %08lX\n", 
 	       dir_g_shadow, changeable_dir_g);
 }
 
@@ -835,7 +881,6 @@
 static __init int
 gpio_init(void)
 {
-	extern void init_ioremap(void);
 	int res;
 #if defined (CONFIG_ETRAX_CSP0_LEDS)
 	int i;
@@ -850,8 +895,7 @@
 	}
 
 	/* Clear all leds */
-#if defined (CONFIG_ETRAX_CSP0_LEDS) ||  defined (CONFIG_ETRAX_PA_LEDS) || defined (CONFIG_ETRAX_PB_LEDS) 
-	init_ioremap();
+#if defined (CONFIG_ETRAX_CSP0_LEDS) ||  defined (CONFIG_ETRAX_PA_LEDS) || defined (CONFIG_ETRAX_PB_LEDS)
 	LED_NETWORK_SET(0);
 	LED_ACTIVE_SET(0);
 	LED_DISK_READ(0);
@@ -865,7 +909,8 @@
 
 #endif
 	gpio_init_port_g();
-	printk("ETRAX 100LX GPIO driver v2.5, (c) 2001, 2002 Axis Communications AB\n");
+	printk(KERN_INFO
+	       "ETRAX 100LX GPIO driver v2.5, (c) 2001, 2002 Axis Communications AB\n");
 	/* We call etrax_gpio_wake_up_check() from timer interrupt and
 	 * from cpu_idle() in kernel/process.c
 	 * The check in cpu_idle() reduces latency from ~15 ms to ~6 ms
@@ -873,11 +918,11 @@
 	 */  
 	if (request_irq(TIMER0_IRQ_NBR, gpio_poll_timer_interrupt,
 			SA_SHIRQ | SA_INTERRUPT,"gpio poll", NULL)) {
-		printk("err: timer0 irq for gpio\n");
+		printk(KERN_CRIT "err: timer0 irq for gpio\n");
 	}
 	if (request_irq(PA_IRQ_NBR, gpio_pa_interrupt,
 			SA_SHIRQ | SA_INTERRUPT,"gpio PA", NULL)) {
-		printk("err: PA irq for gpio\n");
+		printk(KERN_CRIT "err: PA irq for gpio\n");
 	}
 	
 	return res;
diff -urN linux-2.4.22-pre3/arch/cris/drivers/i2c.c linux-2.4.22-pre4/arch/cris/drivers/i2c.c
--- linux-2.4.22-pre3/arch/cris/drivers/i2c.c	2003-06-13 07:51:29.000000000 -0700
+++ linux-2.4.22-pre4/arch/cris/drivers/i2c.c	2003-07-09 16:56:27.000000000 -0700
@@ -11,7 +11,26 @@
 *! Jan 14 2000  Johan Adolfsson    Fixed PB shadow register stuff - 
 *!                                 don't use PB_I2C if DS1302 uses same bits,
 *!                                 use PB.
+*| June 23 2003 Pieter Grimmerink  Added 'i2c_sendnack'. i2c_readreg now
+*|                                 generates nack on last received byte, 
+*|                                 instead of ack.
+*|                                 i2c_getack changed data level while clock
+*|                                 was high, causing DS75 to see  a stop condition
+*|
 *! $Log: i2c.c,v $
+*! Revision 1.12  2003/06/23 14:43:47  oskarp
+*! * i2c_sendnack is added.
+*!   - i2c_readreg now generates nack on last received byte, instead of ack.
+*!     i2c_getack changed data level while clock was high, causing DS75 to
+*!     see a stop condition.
+*!
+*! Revision 1.11  2003/05/23 09:37:23  oskarp
+*! * Made i2c_init() visible.
+*!   - e.g. pcf856.c uses i2c_init().
+*!
+*! Revision 1.10  2003/04/01 14:12:06  starvik
+*! Added loglevel for lots of printks
+*!
 *! Revision 1.9  2002/10/31 15:32:26  starvik
 *! Update Port B register and shadow even when running with hardware support
 *!   to avoid glitches when reading bits
@@ -53,7 +72,7 @@
 *! (C) Copyright 1999-2002 Axis Communications AB, LUND, SWEDEN
 *!
 *!***************************************************************************/
-/* $Id: i2c.c,v 1.9 2002/10/31 15:32:26 starvik Exp $ */
+/* $Id: i2c.c,v 1.12 2003/06/23 14:43:47 oskarp Exp $ */
 /****************** INCLUDE FILES SECTION ***********************************/
 
 #include <linux/module.h>
@@ -133,7 +152,7 @@
 
 #define i2c_getbit() (((*R_PORT_PB_READ & (1 << SDABIT))) >> SDABIT)
 
-#else
+#else /* !CONFIG_ETRAX_I2C_USES_PB_NOT_PB_I2C */
 /* enable or disable the i2c interface */
 
 #define i2c_enable() *R_PORT_PB_I2C = (port_pb_i2c_shadow |= IO_MASK(R_PORT_PB_I2C, i2c_en))
@@ -163,7 +182,8 @@
 /* read a bit from the i2c interface */
 
 #define i2c_getbit() (*R_PORT_PB_READ & 0x1)
-#endif
+
+#endif /* CONFIG_ETRAX_I2C_USES_PB_NOT_PB_I2C */
 
 /* use the kernels delay routine */
 
@@ -299,6 +319,12 @@
 	}
 	i2c_clk(I2C_CLOCK_HIGH);
 	i2c_delay(CLOCK_HIGH_TIME);
+	
+	/*
+	 * we leave the clock low, getbyte is usually followed
+	 * by sendack/nack, they assume the clock to be low
+	 */
+	i2c_clk(I2C_CLOCK_LOW);
 	return aBitByte;
 }
 
@@ -360,6 +386,13 @@
 		i2c_delay(CLOCK_HIGH_TIME/2);
 	}
 
+   /*
+    * our clock is high now, make sure data is low
+    * before we enable our output. If we keep data high
+    * and enable output, we would generate a stop condition.
+    */
+   i2c_data(I2C_DATA_LOW);
+   
 	/*
 	 * end clock pulse
 	 */
@@ -417,6 +450,37 @@
 
 /*#---------------------------------------------------------------------------
 *#
+*# FUNCTION NAME: i2c_sendnack
+*#
+*# DESCRIPTION  : Sends NACK on received data
+*#
+*#--------------------------------------------------------------------------*/
+void
+i2c_sendnack(void)
+{
+	/*
+	 * enable output
+	 */
+	i2c_delay(CLOCK_LOW_TIME);
+	i2c_dir_out();
+	/*
+	 * set data high
+	 */
+	i2c_data(I2C_DATA_HIGH);
+	/*
+	 * generate clock pulse
+	 */
+	i2c_delay(CLOCK_HIGH_TIME/6);
+	i2c_clk(I2C_CLOCK_HIGH);
+	i2c_delay(CLOCK_HIGH_TIME);
+	i2c_clk(I2C_CLOCK_LOW);
+	i2c_delay(CLOCK_LOW_TIME);
+	
+	i2c_dir_in();
+}
+
+/*#---------------------------------------------------------------------------
+*#
 *# FUNCTION NAME: i2c_writereg
 *#
 *# DESCRIPTION  : Writes a value to an I2C device
@@ -546,9 +610,10 @@
 		 */
 		b = i2c_inbyte();
 		/*
-		 * send Ack
+		 * last received byte needs to be nacked
+		 * instead of acked
 		 */
-		i2c_sendack();
+		i2c_sendnack();
 		/*
 		 * end sequence
 		 */
@@ -623,7 +688,7 @@
 	release:  i2c_release,
 };
 
-static int __init
+int __init
 i2c_init(void)
 {
 	int res;
@@ -653,7 +718,7 @@
 		return res;
 	}
 
-	printk("I2C driver v2.2, (c) 1999-2001 Axis Communications AB\n");
+	printk(KERN_INFO "I2C driver v2.2, (c) 1999-2001 Axis Communications AB\n");
 	
 	return 0;
 }
diff -urN linux-2.4.22-pre3/arch/cris/drivers/i2c.h linux-2.4.22-pre4/arch/cris/drivers/i2c.h
--- linux-2.4.22-pre3/arch/cris/drivers/i2c.h	2001-05-19 17:43:05.000000000 -0700
+++ linux-2.4.22-pre4/arch/cris/drivers/i2c.h	2003-07-09 16:56:27.000000000 -0700
@@ -1,6 +1,8 @@
-/* $Id: i2c.h,v 1.3 2001/03/19 12:43:01 markusl Exp $ */
+
+#include <linux/init.h>
 
 /* High level I2C actions */
+int __init i2c_init(void);
 int i2c_writereg(unsigned char theSlave, unsigned char theReg, unsigned char theValue);
 unsigned char i2c_readreg(unsigned char theSlave, unsigned char theReg);
 
@@ -11,6 +13,3 @@
 unsigned char i2c_inbyte(void);
 int i2c_getack(void);
 void i2c_sendack(void);
-
-
-
diff -urN linux-2.4.22-pre3/arch/cris/drivers/ide.c linux-2.4.22-pre4/arch/cris/drivers/ide.c
--- linux-2.4.22-pre3/arch/cris/drivers/ide.c	2003-06-13 07:51:29.000000000 -0700
+++ linux-2.4.22-pre4/arch/cris/drivers/ide.c	2003-07-09 16:56:27.000000000 -0700
@@ -1,4 +1,4 @@
-/* $Id: ide.c,v 1.26 2002/09/17 12:16:59 bjornw Exp $
+/* $Id: ide.c,v 1.30 2003/07/08 07:24:47 pkj Exp $
  *
  * Etrax specific IDE functions, like init and PIO-mode setting etc.
  * Almost the entire ide.c is used for the rest of the Etrax ATA driver.
@@ -8,6 +8,19 @@
  *             Mikael Starvik     (pio setup stuff)
  *
  * $Log: ide.c,v $
+ * Revision 1.30  2003/07/08 07:24:47  pkj
+ * Corrected spelling mistakes originally found in 2.5.x
+ *
+ * Revision 1.29  2003/06/17 13:57:49  starvik
+ * Merge of Linux 2.4.21
+ *
+ * Revision 1.28  2003/01/22 12:41:12  starvik
+ * Added LBA48 support
+ * Fixed typo in e100_ideproc
+ *
+ * Revision 1.27  2003/01/09 18:03:47  starvik
+ * init_ioremap is now called by kernel before device drivers are initialized
+ *
  * Revision 1.26  2002/09/17 12:16:59  bjornw
  * Removed unnecessary cli/sti pair
  *
@@ -151,6 +164,8 @@
 static volatile unsigned long *reset_addr;
 #endif
 
+static int e100_read_command = 0;
+
 #define LOWDB(x)
 #define D(x) 
 
@@ -186,14 +201,9 @@
 			    IO_MASK(R_ATA_STATUS_DATA, tr_rdy)))
 		timeleft--;
 }
-unsigned char 
-IN_BYTE(ide_ioreg_t reg) {
-        /* data was in the lower 16 bits in the status reg */
-        return (unsigned char)(IN_WORD(reg));
-}
 
 unsigned short 
-IN_WORD(ide_ioreg_t reg) {
+IN_BYTE(ide_ioreg_t reg) {
 	int status;
 	int timeleft;
 
@@ -237,7 +247,7 @@
 
 	LOWDB(printk("inb: 0x%x from reg 0x%x\n", status & 0xff, reg));
 
-        return (unsigned short)status;
+        return (unsigned char)status;
 }
 
 /* PIO timing (in R_ATA_CONFIG)
@@ -292,9 +302,15 @@
 #define ATA_PIO0_STROBE 19
 #define ATA_PIO0_HOLD    4
 
-static int e100_dmaproc (ide_dma_action_t func, ide_drive_t *drive);
-static void e100_ideproc (ide_ide_action_t func, ide_drive_t *drive,
-			  void *buffer, unsigned int length);
+static int e100_dma_check (ide_drive_t *drive);
+static int e100_dma_begin (ide_drive_t *drive);
+static int e100_dma_end (ide_drive_t *drive);
+static int e100_dma_read (ide_drive_t *drive);
+static int e100_dma_write (ide_drive_t *drive);
+static void e100_ide_input_data (ide_drive_t *drive, void *, unsigned int);
+static void e100_ide_output_data (ide_drive_t *drive, void *, unsigned int);
+static void e100_atapi_input_bytes(ide_drive_t *drive, void *, unsigned int);
+static void e100_atapi_output_bytes(ide_drive_t *drive, void *, unsigned int);
 
 /*
  * good_dma_drives() lists the model names (from "hdparm -i")
@@ -309,8 +325,6 @@
 
 static void tune_e100_ide(ide_drive_t *drive, byte pio)
 {
-	unsigned long flags;
-	
 	pio = 4;
 	/* pio = ide_get_best_pio_mode(drive, pio, 4, NULL); */
 	
@@ -374,8 +388,15 @@
 		ide_hwif_t *hwif = &ide_hwifs[h];
 		hwif->chipset = ide_etrax100;
 		hwif->tuneproc = &tune_e100_ide;
-		hwif->dmaproc = &e100_dmaproc;
-		hwif->ideproc = &e100_ideproc;
+                hwif->ata_input_data = &e100_ide_input_data;
+                hwif->ata_output_data = &e100_ide_output_data;
+                hwif->atapi_input_bytes = &e100_atapi_input_bytes;
+                hwif->atapi_output_bytes = &e100_atapi_output_bytes;
+                hwif->ide_dma_check = &e100_dma_check;
+                hwif->ide_dma_end = &e100_dma_end;
+		hwif->ide_dma_write = &e100_dma_write;
+		hwif->ide_dma_read = &e100_dma_read;
+		hwif->ide_dma_begin = &e100_dma_begin;
 	}
 
 	/* actually reset and configure the etrax100 ide/ata interface */
@@ -400,11 +421,9 @@
         REG_SHADOW_SET(R_PORT_G_DATA, port_g_data_shadow, 27, 0);
 #endif 
 #ifdef CONFIG_ETRAX_IDE_CSE1_16_RESET
-        init_ioremap();
         REG_SHADOW_SET(port_cse1_addr, port_cse1_shadow, 16, 0);
 #endif
 #ifdef CONFIG_ETRAX_IDE_CSP0_8_RESET
-        init_ioremap();
         REG_SHADOW_SET(port_csp0_addr, port_csp0_shadow, 8, 0);
 #endif
 #ifdef CONFIG_ETRAX_IDE_PB7_RESET  
@@ -618,7 +637,7 @@
         LED_DISK_WRITE(1);
         
         /* Etrax will set busy = 1 until the multi pio transfer has finished
-         * and tr_rdy = 1 after each succesful word transfer. 
+         * and tr_rdy = 1 after each successful word transfer.
          * When the last byte has been transferred Etrax will first set tr_tdy = 1 
          * and then busy = 0 (not in the same cycle). If we read busy before it
          * has been set to 0 we will think that we should transfer more bytes 
@@ -655,32 +674,6 @@
 	e100_atapi_output_bytes(drive, buffer, wcount << 2);
 }
 
-/*
- * The multiplexor for ide_xxxput_data and atapi calls
- */
-static void 
-e100_ideproc (ide_ide_action_t func, ide_drive_t *drive,
-	      void *buffer, unsigned int length)
-{
-	switch (func) {
-		case ideproc_ide_input_data:
-			e100_ide_input_data(drive, buffer, length);
-			break;
-		case ideproc_ide_output_data:
-			e100_ide_input_data(drive, buffer, length);
-			break;
-		case ideproc_atapi_input_bytes:
-			e100_atapi_input_bytes(drive, buffer, length);
-			break;
-		case ideproc_atapi_output_bytes:
-			e100_atapi_output_bytes(drive, buffer, length);
-			break;
-		default:
-			printk("e100_ideproc: unsupported func %d!\n", func);
-			break;
-	}
-}
-
 /* we only have one DMA channel on the chip for ATA, so we can keep these statically */
 static etrax_dma_descr ata_descrs[MAX_DMA_DESCRS];
 static unsigned int ata_tot_size;
@@ -815,8 +808,8 @@
 	LED_DISK_READ(0);
 	LED_DISK_WRITE(0);
 
-	dma_stat = HWIF(drive)->dmaproc(ide_dma_end, drive);
-	stat = GET_STAT();			/* get drive status */
+	dma_stat = HWIF(drive)->ide_dma_end(drive);
+	stat = HWIF(drive)->INB(IDE_STATUS_REG);		/* get drive status */
 	if (OK_STAT(stat,DRIVE_READY,drive->bad_wstat|DRQ_STAT)) {
 		if (!dma_stat) {
 			struct request *rq;
@@ -833,7 +826,7 @@
 }
 
 /*
- * e100_dmaproc() initiates/aborts DMA read/write operations on a drive.
+ * Functions below initiates/aborts DMA read/write operations on a drive.
  *
  * The caller is assumed to have selected the drive and programmed the drive's
  * sector address using CHS or LBA.  All that remains is to prepare for DMA
@@ -848,59 +841,19 @@
  * the caller should revert to PIO for the current request.
  */
 
-static int e100_dmaproc (ide_dma_action_t func, ide_drive_t *drive)
+static int e100_dma_check(ide_drive_t *drive)
 {
-        static unsigned int reading; /* static to support ide_dma_begin semantics */
-	int atapi = 0;
-
-	D(printk("e100_dmaproc func %d\n", func));
-
-        switch (func) {
-		case ide_dma_verbose:
-			return 0;
-                case ide_dma_check:
-                        return config_drive_for_dma (drive);
-		case ide_dma_off:
-		case ide_dma_off_quietly:
-			/* ok.. we don't really need to do anything I think. */
-			return 0;
-                case ide_dma_write:
-                        reading = 0;
-			break;
-                case ide_dma_read:
-			reading = 1;
-                        break;
-                case ide_dma_begin:
-			/* begin DMA, used by ATAPI devices which want to issue the 
-			 * appropriate IDE command themselves.
-			 *
-			 * they have already called ide_dma_read/write to set the
-			 * static reading flag, now they call ide_dma_begin to do
-			 * the real stuff. we tell our code below not to issue
-			 * any IDE commands itself and jump into it. 
-			 */
-			atapi++;
-			goto dma_begin;
-		case ide_dma_end: /* returns 1 on error, 0 otherwise */
-			/* TODO: check if something went wrong with the DMA */
-			return 0;
-
-                default:
-                        printk("e100_dmaproc: unsupported func %d\n", func);
-                        return 1;
-        }
-
-	/* ATAPI-devices (not disks) first call ide_dma_read/write to set the direction
-	 * then they call ide_dma_begin after they have issued the appropriate drive command
-	 * themselves to actually start the chipset DMA. so we just return here if we're
-	 * not a diskdrive.
-	 */
-
-        if (drive->media != ide_disk)
-                return 0;
+	return config_drive_for_dma (drive);
+}
 
- dma_begin:
+static int e100_dma_end(ide_drive_t *drive)
+{
+	/* TODO: check if something went wrong with the DMA */
+	return 0;
+}
 
+static int e100_start_dma(ide_drive_t *drive, int atapi, int reading)
+{
 	if(reading) {
 
 		RESET_DMA(ATA_RX_DMA_NBR); /* sometimes the DMA channel get stuck so we need to do this */
@@ -917,8 +870,15 @@
 			ide_set_handler(drive, &etrax_dma_intr, WAIT_CMD, NULL);
 			
 			/* issue cmd to drive */
-			
-			OUT_BYTE(WIN_READDMA, IDE_COMMAND_REG);
+                        if ((HWGROUP(drive)->rq->cmd == IDE_DRIVE_TASKFILE) &&
+			    (drive->addressing == 1)) {
+				ide_task_t *args = HWGROUP(drive)->rq->special;
+				OUT_BYTE(args->tfRegister[IDE_COMMAND_OFFSET], IDE_COMMAND_REG);
+			} else if (drive->addressing) {
+				OUT_BYTE(WIN_READDMA_EXT, IDE_COMMAND_REG);
+			} else {
+				OUT_BYTE(WIN_READDMA, IDE_COMMAND_REG);
+			}
 		}
 
 		/* begin DMA */
@@ -968,8 +928,15 @@
 			ide_set_handler(drive, &etrax_dma_intr, WAIT_CMD, NULL);
 			
 			/* issue cmd to drive */
-			
-			OUT_BYTE(WIN_WRITEDMA, IDE_COMMAND_REG);
+			if ((HWGROUP(drive)->rq->cmd == IDE_DRIVE_TASKFILE) &&
+			    (drive->addressing == 1)) {
+				ide_task_t *args = HWGROUP(drive)->rq->special;
+				OUT_BYTE(args->tfRegister[IDE_COMMAND_OFFSET], IDE_COMMAND_REG);
+			} else if (drive->addressing) {
+				OUT_BYTE(WIN_WRITEDMA_EXT, IDE_COMMAND_REG);
+			} else {
+				OUT_BYTE(WIN_WRITEDMA, IDE_COMMAND_REG);
+			}
 		}
 
 		/* begin DMA */
@@ -994,14 +961,44 @@
 
 		D(printk("dma write of %d bytes.\n", ata_tot_size));
 	}
-
-	/* DMA started successfully */
 	return 0;
 }
 
-/* ide.c calls this, but we don't need to do anything particular */
+static int e100_dma_write(ide_drive_t *drive)
+{
+	e100_read_command = 0;
+	/* ATAPI-devices (not disks) first call ide_dma_read/write to set the direction
+	 * then they call ide_dma_begin after they have issued the appropriate drive command
+	 * themselves to actually start the chipset DMA. so we just return here if we're
+	 * not a diskdrive.
+	 */
+	if (drive->media != ide_disk)
+                return 0;
+	return e100_start_dma(drive, 0, 0);
+}
+
+static int e100_dma_read(ide_drive_t *drive)
+{
+	e100_read_command = 1;
+	/* ATAPI-devices (not disks) first call ide_dma_read/write to set the direction
+	 * then they call ide_dma_begin after they have issued the appropriate drive command
+	 * themselves to actually start the chipset DMA. so we just return here if we're
+	 * not a diskdrive.
+	 */
+	if (drive->media != ide_disk)
+                return 0;
+	return e100_start_dma(drive, 0, 1);
+}
 
-int ide_release_dma (ide_hwif_t *hwif)
+static int e100_dma_begin(ide_drive_t *drive)
 {
-	return 1;
+	/* begin DMA, used by ATAPI devices which want to issue the 
+	 * appropriate IDE command themselves.
+	 *
+	 * they have already called ide_dma_read/write to set the
+	 * static reading flag, now they call ide_dma_begin to do
+	 * the real stuff. we tell our code below not to issue
+	 * any IDE commands itself and jump into it. 
+	 */
+	 return e100_start_dma(drive, 1, e100_read_command);
 }
diff -urN linux-2.4.22-pre3/arch/cris/drivers/lpslave/bintocarr.pl linux-2.4.22-pre4/arch/cris/drivers/lpslave/bintocarr.pl
--- linux-2.4.22-pre3/arch/cris/drivers/lpslave/bintocarr.pl	2001-10-08 11:43:54.000000000 -0700
+++ linux-2.4.22-pre4/arch/cris/drivers/lpslave/bintocarr.pl	2003-07-09 16:56:27.000000000 -0700
@@ -1,5 +1,5 @@
 #!/usr/bin/perl -w
-# $Id: bintocarr.pl,v 1.4 2001/08/08 08:18:13 bjarne Exp $
+
 # Copy of mkjulbin.pl made by Olof
 # convert a binary stdin to a C-file containing a char array of the input
 # first argument is the symbol name
@@ -27,4 +27,3 @@
 $lensymb = ("_" . ($symbol . "_length"));
 
 print "__asm__(\"\\t.globl $lensymb\\n$lensymb = $bcount\\n\");\n";
-  
diff -urN linux-2.4.22-pre3/arch/cris/drivers/lpslave/e100lpslave.S linux-2.4.22-pre4/arch/cris/drivers/lpslave/e100lpslave.S
--- linux-2.4.22-pre3/arch/cris/drivers/lpslave/e100lpslave.S	2001-07-26 15:10:06.000000000 -0700
+++ linux-2.4.22-pre4/arch/cris/drivers/lpslave/e100lpslave.S	2003-07-09 16:56:27.000000000 -0700
@@ -1,12 +1,11 @@
-	;; $Id: e100lpslave.S,v 1.3 2001/06/21 16:55:26 olof Exp $
-	;;
-	;; Etrax100 slave network<->parport forwarder
-	;;
-	;; Copyright (c) 1999 Bjorn Wesen, Axis Communications AB
-	;;
-	;; We got 784 bytes (par loader size) to do DMA forwarding
-	;; between DMA0/1 (ethernet) and DMA3/4 (par port 0 RX/1 TX)
-	;;
+;;
+;; Etrax100 slave network<->parport forwarder
+;;
+;; Copyright (c) 1999, 2000, 2001, 2002, 2003 Axis Communications AB
+;;
+;; We got 784 bytes (par loader size) to do DMA forwarding
+;; between DMA0/1 (ethernet) and DMA3/4 (par port 0 RX/1 TX)
+;;
 
 #include <linux/config.h>
 #if 0
diff -urN linux-2.4.22-pre3/arch/cris/drivers/parport.c linux-2.4.22-pre4/arch/cris/drivers/parport.c
--- linux-2.4.22-pre3/arch/cris/drivers/parport.c	2003-06-13 07:51:29.000000000 -0700
+++ linux-2.4.22-pre4/arch/cris/drivers/parport.c	2003-07-09 16:56:27.000000000 -0700
@@ -1,13 +1,13 @@
-/* $Id: parport.c,v 1.10 2002/10/03 05:54:24 starvik Exp $
- * 
- * Elinux parallel port driver
+/*
+ * Parallel port driver for ETRAX.
+ *
  * NOTE!
  *   Since par0 shares DMA with ser2 and par 1 shares DMA with ser3
  *   this should be handled if both are enabled at the same time.
  *   THIS IS NOT HANDLED YET!
  *
- * Copyright (c) 2001 Axis Communications AB
- * 
+ * Copyright (c) 2001, 2002, 2003 Axis Communications AB
+ *
  * Author: Fredrik Hugosson
  *
  */
@@ -406,7 +406,7 @@
 static void __init
 parport_etrax_show_parallel_version(void)
 {
-	printk("ETRAX 100LX parallel port driver v1.0, (c) 2001 Axis Communications AB\n");
+	printk("ETRAX 100LX parallel port driver v1.0, (c) 2001-2003 Axis Communications AB\n");
 }
 
 #ifdef CONFIG_ETRAX_PAR0_DMA
diff -urN linux-2.4.22-pre3/arch/cris/drivers/pcf8563.c linux-2.4.22-pre4/arch/cris/drivers/pcf8563.c
--- linux-2.4.22-pre3/arch/cris/drivers/pcf8563.c	2003-06-13 07:51:29.000000000 -0700
+++ linux-2.4.22-pre4/arch/cris/drivers/pcf8563.c	2003-07-09 16:56:27.000000000 -0700
@@ -8,14 +8,13 @@
  * low detector are also provided. All address and data are transferred
  * serially via two-line bidirectional I2C-bus. Maximum bus speed is
  * 400 kbits/s. The built-in word address register is incremented
- * automatically after each written or read bute.
+ * automatically after each written or read byte.
  *
- * Copyright (c) 2002, Axis Communications AB
+ * Copyright (c) 2002-2003, Axis Communications AB
  * All rights reserved.
  *
  * Author: Tobias Anderberg <tobiasa@axis.com>.
  *
- * $Id: pcf8563.c,v 1.4 2002/10/15 09:22:42 tobiasa Exp $
  */
 
 #include <linux/config.h>
@@ -36,19 +35,15 @@
 #include <asm/rtc.h>
 #include "i2c.h"
 
-#define PCF8563_MAJOR 121		/* Local major number. */
-#define DEVICE_NAME "rtc"		/* Name which is registered in /proc/devices. */
-#define PCF8563_NAME "PCF8563"
-#define DRIVER_VERSION "$Revision: 1.4 $"
-
-/* I2C bus slave registers. */
-#define RTC_I2C_READ		0xa3
-#define RTC_I2C_WRITE		0xa2
+#define PCF8563_MAJOR	121	/* Local major number. */
+#define DEVICE_NAME	"rtc"	/* Name which is registered in /proc/devices. */
+#define PCF8563_NAME	"PCF8563"
+#define DRIVER_VERSION	"$Revision: 1.15 $"
 
 /* Two simple wrapper macros, saves a few keystrokes. */
 #define rtc_read(x) i2c_readreg(RTC_I2C_READ, x)
 #define rtc_write(x,y) i2c_writereg(RTC_I2C_WRITE, x, y)
-	
+
 static const unsigned char days_in_month[] =
 	{ 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
 
@@ -64,53 +59,73 @@
 };
 
 unsigned char
-pcf8563_readreg(int reg) 
+pcf8563_readreg(int reg)
 {
-	unsigned char res = i2c_readreg(RTC_I2C_READ, reg);
+	unsigned char res = rtc_read(reg);
 
 	/* The PCF8563 does not return 0 for unimplemented bits */
-	switch(reg)
-	{
+	switch (reg) {
 		case RTC_SECONDS:
 		case RTC_MINUTES:
-		     res &= 0x7f;
-		     break;
+			res &= 0x7F;
+			break;
 		case RTC_HOURS:
 		case RTC_DAY_OF_MONTH:
-		     res &= 0x3f;
-		     break;
+			res &= 0x3F;
+			break;
+		case RTC_WEEKDAY:
+			res &= 0x07;
+			break;
 		case RTC_MONTH:
-		     res = (res & 0x1f) - 1;  /* PCF8563 returns month in range 1-12 */
-		     break;
+			res &= 0x1F;
+			break;
+		case RTC_CONTROL1:
+			res &= 0xA8;
+			break;
+		case RTC_CONTROL2:
+			res &= 0x1F;
+			break;
+		case RTC_CLOCKOUT_FREQ:
+		case RTC_TIMER_CONTROL:
+			res &= 0x83;
+			break;
 	}
 	return res;
 }
 
 void
-pcf8563_writereg(int reg, unsigned char val) 
+pcf8563_writereg(int reg, unsigned char val)
 {
-	i2c_writereg(RTC_I2C_WRITE,reg,val);
+#ifdef CONFIG_ETRAX_RTC_READONLY
+	if (reg == RTC_CONTROL1 || (reg >= RTC_SECONDS && reg <= RTC_YEAR))
+		return;
+#endif
+
+	rtc_write(reg, val);
 }
 
 void
 get_rtc_time(struct rtc_time *tm)
 {
-	tm->tm_sec = rtc_read(RTC_SECONDS);
-	tm->tm_min = rtc_read(RTC_MINUTES);
+	tm->tm_sec  = rtc_read(RTC_SECONDS);
+	tm->tm_min  = rtc_read(RTC_MINUTES);
 	tm->tm_hour = rtc_read(RTC_HOURS);
 	tm->tm_mday = rtc_read(RTC_DAY_OF_MONTH);
-	tm->tm_mon = rtc_read(RTC_MONTH);
+	tm->tm_wday = rtc_read(RTC_WEEKDAY);
+	tm->tm_mon  = rtc_read(RTC_MONTH);
 	tm->tm_year = rtc_read(RTC_YEAR);
 
 	if (tm->tm_sec & 0x80)
-		printk(KERN_WARNING "%s: RTC Low Voltage - date/time is not reliable!\n", PCF8563_NAME);
+		printk(KERN_WARNING "%s: RTC Voltage Low - reliable date/time "
+		       "information is no longer guaranteed!\n", PCF8563_NAME);
 
-	tm->tm_year = BCD_TO_BIN(tm->tm_year) + ((tm->tm_mon & 0x80) ? 100 : 0);
-	tm->tm_sec &= 0x7f;
-	tm->tm_min &= 0x7f;
-	tm->tm_hour &= 0x3f;
-	tm->tm_mday &= 0x3f;
-	tm->tm_mon &= 0x1f;
+	tm->tm_year  = BCD_TO_BIN(tm->tm_year) + ((tm->tm_mon & 0x80) ? 100 : 0);
+	tm->tm_sec  &= 0x7F;
+	tm->tm_min  &= 0x7F;
+	tm->tm_hour &= 0x3F;
+	tm->tm_mday &= 0x3F;
+	tm->tm_wday &= 0x07; /* Not coded in BCD. */
+	tm->tm_mon  &= 0x1F;
 
 	BCD_TO_BIN(tm->tm_sec);
 	BCD_TO_BIN(tm->tm_min);
@@ -124,10 +139,14 @@
 pcf8563_init(void)
 {
 	unsigned char ret;
+
+	/* Initiate the i2c protocol. */
+	i2c_init();
+
 	/*
 	 * First of all we need to reset the chip. This is done by
-	 * clearing control1, control2 and clk freq, clear the 
-	 * Voltage Low bit, and resetting all alarms.
+	 * clearing control1, control2 and clk freq and resetting
+	 * all alarms.
 	 */
 	if (rtc_write(RTC_CONTROL1, 0x00) < 0)
 		goto err;
@@ -138,37 +157,35 @@
 	if (rtc_write(RTC_CLOCKOUT_FREQ, 0x00) < 0)
 		goto err;
 
-	/* Clear the VL bit in the seconds register. */
-	ret = rtc_read(RTC_SECONDS);
-	
-	if (rtc_write(RTC_SECONDS, (ret & 0x7f)) < 0)
+	if (rtc_write(RTC_TIMER_CONTROL, 0x03) < 0)
 		goto err;
-		
+
 	/* Reset the alarms. */
-	if (rtc_write(RTC_MINUTE_ALARM, 0x00) < 0)
+	if (rtc_write(RTC_MINUTE_ALARM, 0x80) < 0)
 		goto err;
-	
-	if (rtc_write(RTC_HOUR_ALARM, 0x00) < 0)
+
+	if (rtc_write(RTC_HOUR_ALARM, 0x80) < 0)
 		goto err;
-	
-	if (rtc_write(RTC_DAY_ALARM, 0x00) < 0)
+
+	if (rtc_write(RTC_DAY_ALARM, 0x80) < 0)
 		goto err;
-	
-	if (rtc_write(RTC_WEEKDAY_ALARM, 0x00) < 0)
+
+	if (rtc_write(RTC_WEEKDAY_ALARM, 0x80) < 0)
 		goto err;
 
 	if (register_chrdev(PCF8563_MAJOR, DEVICE_NAME, &pcf8563_fops) < 0) {
-		printk(KERN_INFO "%s: Unable to get major numer %d for RTC device.\n", 
+		printk(KERN_INFO "%s: Unable to get major numer %d for RTC device.\n",
 		       PCF8563_NAME, PCF8563_MAJOR);
 		return -1;
 	}
 
 	printk(KERN_INFO "%s Real-Time Clock Driver, %s\n", PCF8563_NAME, DRIVER_VERSION);
-        
+
 	/* Check for low voltage, and warn about it.. */
 	if (rtc_read(RTC_SECONDS) & 0x80)
-		printk(KERN_WARNING "%s: RTC Low Voltage - date/time is not reliable!\n", PCF8563_NAME);
-	
+		printk(KERN_WARNING "%s: RTC Voltage Low - reliable date/time "
+		       "information is no longer guaranteed!\n", PCF8563_NAME);
+
 	return 0;
 
 err:
@@ -200,75 +217,113 @@
 
 	switch (cmd) {
 		case RTC_RD_TIME:
-			{
-				struct rtc_time tm;
+		{
+			struct rtc_time tm;
 
-				get_rtc_time(&tm);
+			get_rtc_time(&tm);
 
-				if (copy_to_user((struct rtc_time *) arg, &tm, sizeof(struct rtc_time))) {
-					return -EFAULT;
-				}
-
-				return 0;
+			if (copy_to_user((struct rtc_time *) arg, &tm, sizeof tm)) {
+				return -EFAULT;
 			}
-			break;
-		case RTC_SET_TIME:
-			{
-				int leap;
-				int century;
-				unsigned long flags;
-				struct rtc_time tm;
-
-				if (!capable(CAP_SYS_TIME))
-					return -EPERM;
-
-				if (copy_from_user(&tm, (struct rtc_time *) arg, sizeof(struct rtc_time)))
-					return -EFAULT;
-
-				/* Convert from struct tm to struct rtc_time. */
-				tm.tm_year += 1900;
-				tm.tm_mon += 1;
-				
-				leap = ((tm.tm_mon == 2) && ((tm.tm_year % 4) == 0)) ? 1 : 0;
-
-				/* Perform some sanity checks. */
-				if ((tm.tm_year < 1970) ||
-				    (tm.tm_mon > 12) ||
-				    (tm.tm_mday == 0) ||
-				    (tm.tm_mday > days_in_month[tm.tm_mon] + leap) ||
-				    (tm.tm_hour >= 24) ||
-				    (tm.tm_min >= 60) ||
-				    (tm.tm_sec >= 60))
-					return -EINVAL;
-
-				century = (tm.tm_year >= 2000) ? 0x80 : 0;
-				tm.tm_year = tm.tm_year % 100;
-
-				BIN_TO_BCD(tm.tm_year);
-				BIN_TO_BCD(tm.tm_mday);
-				BIN_TO_BCD(tm.tm_hour);
-				BIN_TO_BCD(tm.tm_min);
-				BIN_TO_BCD(tm.tm_sec);
-				tm.tm_mon |= century;
-				
-				rtc_write(RTC_YEAR, tm.tm_year);
-				rtc_write(RTC_MONTH, tm.tm_mon);
-				rtc_write(RTC_DAY_OF_MONTH, tm.tm_mday);
-				rtc_write(RTC_HOURS, tm.tm_hour);
-				rtc_write(RTC_MINUTES, tm.tm_min);
-				rtc_write(RTC_SECONDS, tm.tm_sec);
 
-				return 0;
+			return 0;
+		}
+
+		case RTC_SET_TIME:
+		{
+#ifdef CONFIG_ETRAX_RTC_READONLY
+			return -EPERM;
+#else
+			int leap;
+			int year;
+			int century;
+			struct rtc_time tm;
+
+			if (!capable(CAP_SYS_TIME))
+				return -EPERM;
+
+			if (copy_from_user(&tm, (struct rtc_time *) arg, sizeof tm))
+				return -EFAULT;
+
+			/* Convert from struct tm to struct rtc_time. */
+			tm.tm_year += 1900;
+			tm.tm_mon += 1;
+
+			/*
+			 * Check if tm.tm_year is a leap year. A year is a leap
+			 * year if it is divisible by 4 but not 100, except
+			 * that years divisible by 400 _are_ leap years.
+			 */
+			year = tm.tm_year;
+			leap = (tm.tm_mon == 2) && ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0);
+
+			/* Perform some sanity checks. */
+			if ((tm.tm_year < 1970) ||
+			    (tm.tm_mon > 12) ||
+			    (tm.tm_mday == 0) ||
+			    (tm.tm_mday > days_in_month[tm.tm_mon] + leap) ||
+			    (tm.tm_wday >= 7) ||
+			    (tm.tm_hour >= 24) ||
+			    (tm.tm_min >= 60) ||
+			    (tm.tm_sec >= 60))
+				return -EINVAL;
+
+			century = (tm.tm_year >= 2000) ? 0x80 : 0;
+			tm.tm_year = tm.tm_year % 100;
+
+			BIN_TO_BCD(tm.tm_year);
+			BIN_TO_BCD(tm.tm_mday);
+			BIN_TO_BCD(tm.tm_hour);
+			BIN_TO_BCD(tm.tm_min);
+			BIN_TO_BCD(tm.tm_sec);
+			tm.tm_mon |= century;
+
+			rtc_write(RTC_YEAR, tm.tm_year);
+			rtc_write(RTC_MONTH, tm.tm_mon);
+			rtc_write(RTC_WEEKDAY, tm.tm_wday); /* Not coded in BCD. */
+			rtc_write(RTC_DAY_OF_MONTH, tm.tm_mday);
+			rtc_write(RTC_HOURS, tm.tm_hour);
+			rtc_write(RTC_MINUTES, tm.tm_min);
+			rtc_write(RTC_SECONDS, tm.tm_sec);
+
+			return 0;
+#endif /* !CONFIG_ETRAX_RTC_READONLY */
+		}
+
+		case RTC_VLOW_RD:
+		{
+			int vl_bit = 0;
+
+			if (rtc_read(RTC_SECONDS) & 0x80) {
+				vl_bit = 1;
+				printk(KERN_WARNING "%s: RTC Voltage Low - reliable "
+				       "date/time information is no longer guaranteed!\n",
+				       PCF8563_NAME);
 			}
-			break;
+			if (copy_to_user((int *) arg, &vl_bit, sizeof(int)))
+				return -EFAULT;
+
+			return 0;
+		}
+
+		case RTC_VLOW_SET:
+		{
+			/* Clear the VL bit in the seconds register */
+			int ret = rtc_read(RTC_SECONDS);
+
+			rtc_write(RTC_SECONDS, (ret & 0x7F));
+
+			return 0;
+		}
+
 		default:
-				return -ENOTTY;
+			return -ENOTTY;
 	}
 
 	return 0;
 }
 
-int 
+int
 pcf8563_open(struct inode *inode, struct file *filp)
 {
 	MOD_INC_USE_COUNT;
diff -urN linux-2.4.22-pre3/arch/cris/drivers/serial.c linux-2.4.22-pre4/arch/cris/drivers/serial.c
--- linux-2.4.22-pre3/arch/cris/drivers/serial.c	2003-06-13 07:51:29.000000000 -0700
+++ linux-2.4.22-pre4/arch/cris/drivers/serial.c	2003-07-09 16:56:27.000000000 -0700
@@ -1,12 +1,92 @@
-/* $Id: serial.c,v 1.44 2002/11/21 04:28:06 hp Exp $
+/* $Id: serial.c,v 1.54 2003/07/08 12:42:19 johana Exp $
  *
  * Serial port driver for the ETRAX 100LX chip
  *
- *      Copyright (C) 1998, 1999, 2000, 2001, 2002  Axis Communications AB
+ *    Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003  Axis Communications AB
  *
- *      Many, many authors. Based once upon a time on serial.c for 16x50.
+ *    Many, many authors. Based once upon a time on serial.c for 16x50.
  *
  * $Log: serial.c,v $
+ * Revision 1.54  2003/07/08 12:42:19  johana
+ * Removed some test defines within #if 0.
+ * Moved a comment to correct place.
+ *
+ * Revision 1.53  2003/07/08 09:48:28  starvik
+ * Removed test code
+ *
+ * Revision 1.52  2003/07/08 07:24:47  pkj
+ * Corrected spelling mistakes originally found in 2.5.x
+ *
+ * Revision 1.51  2003/07/07 09:17:12  johana
+ * Config.in:
+ * Don't consider ETRAX_FAST_TIMER experimental.
+ * Add CONFIG_ETRAX_EXTERN_PB6CLK_ENABLED
+ * Add CONFIG_ETRAX_EXTERN_PB6CLK_FREQ
+ * Add CONFIG_ETRAX_SERIAL_PORTx_DMAy_OUT/IN
+ *
+ * serial.h:
+ * Support for with and without DMA.
+ * Added baud_base and custom_divisor for flexible baudrate.
+ * Added flush_time_usec.
+ * Removed unused DMA register pointers.
+ *
+ * serial.c:
+ * Use C99 style initialisation of rs_table, some fields added, some removed.
+ * Always disable interrupts when fiddling with shadow variables.
+ * Support for flexible baudrate using prescaler or external clock.
+ * Only disable the DMA channel if we own it.
+ * Support for either using DMA or char-by-char interrupt.
+ *
+ * DMA changes:
+ * o When transmitting with DMA, don't put all in one descriptor, split
+ *   it so that we can wake up the application before everything is sent.
+ * o When starting a fast timer to flip later, use a longer, fixed time.
+ * o Use flush_time_usec and have a minimum value to prevent excessive load
+ *   on high baudrates.
+ * o Do force_eop_if_needed() after processing to reduce load.
+ * o All DMA interrupts uses SA_SHIRQ | SA_INTERRUPT
+ *
+ * Only have interrupts disabled while processing rx interrupts,
+ * unmask just tx interrupts and unblock serial interrupt and keep
+ * global interrupts enabled while processing tx interrupts to make
+ * char-by-char interrupts usable.
+ *
+ * Flow control:
+ * o Use normal tx interrupt when sending x_char.
+ * o Use throttling within the driver when using DMA.
+ *
+ * Improved debugging with DEBUG_LOG macro.
+ *
+ * Revision 1.50  2003/06/13 10:04:12  johana
+ * Help the user to avoid trouble by:
+ * Preventing FAST_TIMER and FAST_DMA_FLUSH at the same time.
+ * Forcing mixed mode for status/control lines if not all pins are used.
+ *
+ * Som minor changes to reduce diff to os/lx25 version.
+ *
+ * Revision 1.49  2003/05/30 11:31:54  johana
+ * Merged in change-branch--serial9bit that adds CMSPAR support for sticky
+ * parity (mark/space)
+ *
+ * Revision 1.48  2003/05/30 11:03:57  johana
+ * Implemented rs_send_xchar() by disabling the DMA and writing manually.
+ * Added e100_disable_txdma_channel() and e100_enable_txdma_channel().
+ * Fixed rs_throttle() and rs_unthrottle() to properly call rs_send_xchar
+ * instead of setting info->x_char and check the CRTSCTS flag before
+ * controlling the rts pin.
+ *
+ * Revision 1.47  2003/04/09 08:31:14  pkj
+ * Typo correction (taken from Linux 2.5).
+ *
+ * Revision 1.46  2003/04/01 14:12:06  starvik
+ * Added loglevel for lots of printks
+ *
+ * Revision 1.45  2003/01/22 06:54:46  starvik
+ * Fixed warnings issued by GCC 3.2.1
+ *
+ * Revision 1.44.4.1  2003/01/08 06:47:12  starvik
+ * Added mark/space parity (for 9-bit serial)
+ *
  * Revision 1.44  2002/11/21 04:28:06  hp
  * Change static inline to extern inline where otherwise outlined with gcc-3.2
  *
@@ -206,7 +286,7 @@
  *   Items worth noticing:
  *
  *      No Etrax100 port 1 workarounds (does only compile on 2.4 anyway now)
- *      RS485 is not ported (why cant it be done in userspace as on x86 ?)
+ *      RS485 is not ported (why can't it be done in userspace as on x86 ?)
  *      Statistics done through async_icount - if any more stats are needed,
  *      that's the place to put them or in an arch-dep version of it.
  *      timeout_interrupt and the other fast timeout stuff not ported yet
@@ -357,7 +437,7 @@
  *
  */
 
-static char *serial_version = "$Revision: 1.44 $";
+static char *serial_version = "$Revision: 1.54 $";
 
 #include <linux/config.h>
 #include <linux/version.h>
@@ -392,7 +472,7 @@
 
 #include <asm/svinto.h>
 
-/* non-arch dependant serial structures are in linux/serial.h */
+/* non-arch dependent serial structures are in linux/serial.h */
 #include <linux/serial.h>
 /* while we keep our own stuff (struct e100_serial) in a local .h file */
 #include "serial.h"
@@ -406,6 +486,11 @@
 #error "Enable FAST_TIMER to use SERIAL_FAST_TIMER"
 #endif
 #endif
+
+#if defined(CONFIG_ETRAX_FAST_TIMER) && defined(CONFIG_ETRAX_SERIAL_FLUSH_DMA_FAST)
+#error "Disable either ETRAX_SERIAL_FLUSH_DMA_FAST or ETRAX_FAST_TIMER"
+#endif
+
 /*
  * All of the compatibilty code so we can compile serial.c against
  * older kernels is hidden in serial_compat.h
@@ -414,8 +499,11 @@
 #include "serial_compat.h"
 #endif
 
+#if 1 /* Set to 0 to avoid inlining - easier to see how size of parts */
 #define _INLINE_ inline
-
+#else
+#define _INLINE_
+#endif
 static DECLARE_TASK_QUEUE(tq_serial);
 
 struct tty_driver serial_driver, callout_driver;
@@ -436,7 +524,7 @@
 //#define SERIAL_DEBUG_DATA
 //#define SERIAL_DEBUG_THROTTLE
 //#define SERIAL_DEBUG_IO  /* Debug for Extra control and status pins */
-#define SERIAL_DEBUG_LINE 0 /* What serport we want to debug */
+#define SERIAL_DEBUG_LINE 3 /* What serport we want to debug */
 
 /* Enable this to use serial interrupts to handle when you
    expect the first received event on the serial port to
@@ -444,14 +532,78 @@
    from eLinux */
 #define SERIAL_HANDLE_EARLY_ERRORS
 
-#define TTY_THROTTLE_LIMIT (TTY_FLIPBUF_SIZE/10)
+/* Defined and used in n_tty.c, but we need it here as well */
+#define TTY_THRESHOLD_THROTTLE 128
 
+/* Due to buffersizes and threshold values, our SERIAL_DESCR_BUF_SIZE
+ * must not be to high or flow control won't work if we leave it to the tty
+ * layer so we have our own throttling in flush_to_flip
+ * TTY_FLIPBUF_SIZE=512,
+ * TTY_THRESHOLD_THROTTLE/UNTHROTTLE=128
+ * BUF_SIZE can't be > 128
+ */
+/* Currently 16 descriptors x 128 bytes = 2048 bytes */
 #define SERIAL_DESCR_BUF_SIZE 256
 
+#define SERIAL_PRESCALE_BASE 3125000 /* 3.125MHz */
+#define DEF_BAUD_BASE SERIAL_PRESCALE_BASE
+
+/* We don't want to load the system with massive fast timer interrupt
+ * on high baudrates so limit it to 250 us (4kHz) */
+#define MIN_FLUSH_TIME_USEC 250
+
 /* Add an x here to log a lot of timer stuff */
 #define TIMERD(x)
+/* Debug details of interrupt handling */
+#define DINTR1(x)  /* irq on/off, errors */
+#define DINTR2(x)    /* tx and rx */
+/* Debug flip buffer stuff */
+#define DFLIP(x)
+/* Debug flow control and overview of data flow */
+#define DFLOW(x)
+#define DBAUD(x)
+#ifdef CONFIG_ETRAX_DEBUG_INTERRUPT
+#define DLOG_INT_TRIG(x) 
+#else
+#define DLOG_INT_TRIG(x)
+#endif
 
+//#define DEBUG_LOG_INCLUDED
+#ifndef DEBUG_LOG_INCLUDED
 #define DEBUG_LOG(line, string, value)
+#else
+struct debug_log_info 
+{
+	unsigned long time;
+	unsigned long timer_data;
+//  int line;
+	const char *string;
+	int value;
+};
+#define DEBUG_LOG_SIZE 4096
+
+struct debug_log_info debug_log[DEBUG_LOG_SIZE];
+int debug_log_pos = 0;
+  
+#define DEBUG_LOG(_line, _string, _value) do { \
+  if ((_line) == SERIAL_DEBUG_LINE) {\
+    debug_log_func(_line, _string, _value); \
+  }\
+}while(0)
+
+void debug_log_func(int line, const char *string, int value)
+{
+	if (debug_log_pos < DEBUG_LOG_SIZE) { 
+		debug_log[debug_log_pos].time = jiffies;
+		debug_log[debug_log_pos].timer_data = *R_TIMER_DATA;
+//    debug_log[debug_log_pos].line = line;
+		debug_log[debug_log_pos].string = string;
+		debug_log[debug_log_pos].value = value;
+		debug_log_pos++; 
+	}
+	/*printk(string, value);*/
+} 
+#endif
 
 #ifndef CONFIG_ETRAX_SERIAL_RX_TIMEOUT_TICKS
 /* Default number of timer ticks before flushing rx fifo 
@@ -461,21 +613,23 @@
 #define CONFIG_ETRAX_SERIAL_RX_TIMEOUT_TICKS 5 
 #endif
 
+unsigned long timer_data_to_ns(unsigned long timer_data);
+
 static void change_speed(struct e100_serial *info);
+static void rs_throttle(struct tty_struct * tty);
 static void rs_wait_until_sent(struct tty_struct *tty, int timeout);
 static int rs_write(struct tty_struct * tty, int from_user,
                     const unsigned char *buf, int count);
-extern inline int raw_write(struct tty_struct * tty, int from_user,
+extern _INLINE_ int rs_raw_write(struct tty_struct * tty, int from_user,
                             const unsigned char *buf, int count);
 #ifdef CONFIG_ETRAX_RS485
 static int e100_write_rs485(struct tty_struct * tty, int from_user,
                             const unsigned char *buf, int count);
 #endif
-static int 
-get_lsr_info(struct e100_serial * info, unsigned int *value);
+static int get_lsr_info(struct e100_serial * info, unsigned int *value);
 
 
-#define DEF_BAUD 0x99   /* 115.2 kbit/s */
+#define DEF_BAUD 115200   /* 115.2 kbit/s */
 #define STD_FLAGS (ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST)
 #define DEF_RX 0x20  /* or SERIAL_CTRL_W >> 8 */
 /* Default value of tx_ctrl register: has txd(bit 7)=1 (idle) as default */
@@ -484,6 +638,7 @@
 /* offsets from R_SERIALx_CTRL */
 
 #define REG_DATA 0
+#define REG_DATA_STATUS32 0 /* this is the 32 bit register R_SERIALx_READ */
 #define REG_TR_DATA 0
 #define REG_STATUS 1
 #define REG_TR_CTRL 1
@@ -519,60 +674,162 @@
  */
 
 
+/* Mask for the irqs possibly enabled in R_IRQ_MASK1_RD etc. */
+static const unsigned long e100_ser_int_mask = 0
+#ifdef CONFIG_ETRAX_SERIAL_PORT0
+| IO_MASK(R_IRQ_MASK1_RD, ser0_data) | IO_MASK(R_IRQ_MASK1_RD, ser0_ready)
+#endif
+#ifdef CONFIG_ETRAX_SERIAL_PORT1
+| IO_MASK(R_IRQ_MASK1_RD, ser1_data) | IO_MASK(R_IRQ_MASK1_RD, ser1_ready)
+#endif
+#ifdef CONFIG_ETRAX_SERIAL_PORT2
+| IO_MASK(R_IRQ_MASK1_RD, ser2_data) | IO_MASK(R_IRQ_MASK1_RD, ser2_ready)
+#endif
+#ifdef CONFIG_ETRAX_SERIAL_PORT3
+| IO_MASK(R_IRQ_MASK1_RD, ser3_data) | IO_MASK(R_IRQ_MASK1_RD, ser3_ready)
+#endif	
+; 
+unsigned long r_alt_ser_baudrate_shadow = 0;
+
 /* this is the data for the four serial ports in the etrax100 */
 /*  DMA2(ser2), DMA4(ser3), DMA6(ser0) or DMA8(ser1) */
 /* R_DMA_CHx_CLR_INTR, R_DMA_CHx_FIRST, R_DMA_CHx_CMD */
 
 static struct e100_serial rs_table[] = {
-	{ DEF_BAUD, (unsigned char *)R_SERIAL0_CTRL, 1U << 12, /* uses DMA 6 and 7 */
-	  R_DMA_CH6_CLR_INTR, R_DMA_CH6_FIRST, R_DMA_CH6_CMD,
-	  R_DMA_CH6_STATUS, R_DMA_CH6_HWSW, R_DMA_CH6_DESCR,
-	  R_DMA_CH7_CLR_INTR, R_DMA_CH7_FIRST, R_DMA_CH7_CMD,
-	  R_DMA_CH7_STATUS, R_DMA_CH7_HWSW, R_DMA_CH7_DESCR,
-	  STD_FLAGS, DEF_RX, DEF_TX, 2,
+	{ .baud        = DEF_BAUD,
+	  .port        = (unsigned char *)R_SERIAL0_CTRL,
+	  .irq         = 1U << 12, /* uses DMA 6 and 7 */
+	  .oclrintradr = R_DMA_CH6_CLR_INTR,
+	  .ofirstadr   = R_DMA_CH6_FIRST,
+	  .ocmdadr     = R_DMA_CH6_CMD,
+	  .ostatusadr  = R_DMA_CH6_STATUS,
+	  .iclrintradr = R_DMA_CH7_CLR_INTR,
+	  .ifirstadr   = R_DMA_CH7_FIRST,
+	  .icmdadr     = R_DMA_CH7_CMD,
+	  .idescradr   = R_DMA_CH7_DESCR,
+	  .flags       = STD_FLAGS,
+	  .rx_ctrl     = DEF_RX,
+	  .tx_ctrl     = DEF_TX,
+	  .iseteop     = 2,
 #ifdef CONFIG_ETRAX_SERIAL_PORT0
-          1
+          .enabled  = 1,
+#ifdef CONFIG_ETRAX_SERIAL_PORT0_DMA6_OUT
+	  .dma_out_enabled = 1,
+#else
+	  .dma_out_enabled = 0,
+#endif
+#ifdef CONFIG_ETRAX_SERIAL_PORT0_DMA7_IN
+	  .dma_in_enabled = 1,
+#else
+	  .dma_in_enabled = 0
+#endif
 #else
-          0
+          .enabled  = 0,
+	  .dma_out_enabled = 0,
+	  .dma_in_enabled = 0	  
 #endif
+
 },  /* ttyS0 */
 #ifndef CONFIG_SVINTO_SIM
-	{ DEF_BAUD, (unsigned char *)R_SERIAL1_CTRL, 1U << 16, /* uses DMA 8 and 9 */
-	  R_DMA_CH8_CLR_INTR, R_DMA_CH8_FIRST, R_DMA_CH8_CMD,
-	  R_DMA_CH8_STATUS, R_DMA_CH8_HWSW, R_DMA_CH8_DESCR,
-	  R_DMA_CH9_CLR_INTR, R_DMA_CH9_FIRST, R_DMA_CH9_CMD,
-	  R_DMA_CH9_STATUS, R_DMA_CH9_HWSW, R_DMA_CH9_DESCR,
-	  STD_FLAGS, DEF_RX, DEF_TX, 3 ,
+	{ .baud        = DEF_BAUD,
+	  .port        = (unsigned char *)R_SERIAL1_CTRL,
+	  .irq         = 1U << 16, /* uses DMA 8 and 9 */
+	  .oclrintradr = R_DMA_CH8_CLR_INTR,
+	  .ofirstadr   = R_DMA_CH8_FIRST,
+	  .ocmdadr     = R_DMA_CH8_CMD,
+	  .ostatusadr  = R_DMA_CH8_STATUS,
+	  .iclrintradr = R_DMA_CH9_CLR_INTR,
+	  .ifirstadr   = R_DMA_CH9_FIRST,
+	  .icmdadr     = R_DMA_CH9_CMD,
+	  .idescradr   = R_DMA_CH9_DESCR,
+	  .flags       = STD_FLAGS,
+	  .rx_ctrl     = DEF_RX,
+	  .tx_ctrl     = DEF_TX,
+	  .iseteop     = 3,
 #ifdef CONFIG_ETRAX_SERIAL_PORT1
-          1
+          .enabled  = 1,
+#ifdef CONFIG_ETRAX_SERIAL_PORT1_DMA8_OUT
+	  .dma_out_enabled = 1,
+#else
+	  .dma_out_enabled = 0,
+#endif
+#ifdef CONFIG_ETRAX_SERIAL_PORT1_DMA9_IN
+	  .dma_in_enabled = 1,
 #else
-          0
+	  .dma_in_enabled = 0
+#endif
+#else
+          .enabled  = 0,
+	  .dma_out_enabled = 0,
+	  .dma_in_enabled = 0
 #endif
 },  /* ttyS1 */
 
-	{ DEF_BAUD, (unsigned char *)R_SERIAL2_CTRL, 1U << 4,  /* uses DMA 2 and 3 */
-	  R_DMA_CH2_CLR_INTR, R_DMA_CH2_FIRST, R_DMA_CH2_CMD,
-	  R_DMA_CH2_STATUS, R_DMA_CH2_HWSW, R_DMA_CH2_DESCR,
-	  R_DMA_CH3_CLR_INTR, R_DMA_CH3_FIRST, R_DMA_CH3_CMD,
-	  R_DMA_CH3_STATUS, R_DMA_CH3_HWSW, R_DMA_CH3_DESCR,
-	  STD_FLAGS, DEF_RX, DEF_TX, 0,
+	{ .baud        = DEF_BAUD,
+	  .port        = (unsigned char *)R_SERIAL2_CTRL,
+	  .irq         = 1U << 4,  /* uses DMA 2 and 3 */
+	  .oclrintradr = R_DMA_CH2_CLR_INTR,
+	  .ofirstadr   = R_DMA_CH2_FIRST,
+	  .ocmdadr     = R_DMA_CH2_CMD,
+	  .ostatusadr  = R_DMA_CH2_STATUS,
+	  .iclrintradr = R_DMA_CH3_CLR_INTR,
+	  .ifirstadr   = R_DMA_CH3_FIRST,
+	  .icmdadr     = R_DMA_CH3_CMD,
+	  .idescradr   = R_DMA_CH3_DESCR,
+	  .flags       = STD_FLAGS,
+	  .rx_ctrl     = DEF_RX,
+	  .tx_ctrl     = DEF_TX,
+	  .iseteop     = 0,
 #ifdef CONFIG_ETRAX_SERIAL_PORT2
-          1
+          .enabled  = 1,
+#ifdef CONFIG_ETRAX_SERIAL_PORT2_DMA2_OUT
+	  .dma_out_enabled = 1,
+#else
+	  .dma_out_enabled = 0,
+#endif
+#ifdef CONFIG_ETRAX_SERIAL_PORT2_DMA3_IN
+	  .dma_in_enabled = 1,
 #else
-          0
+	  .dma_in_enabled = 0
+#endif
+#else
+          .enabled  = 0,
+	  .dma_out_enabled = 0,
+	  .dma_in_enabled = 0
 #endif
  },  /* ttyS2 */
 
-	{ DEF_BAUD, (unsigned char *)R_SERIAL3_CTRL, 1U << 8,  /* uses DMA 4 and 5 */
-	  R_DMA_CH4_CLR_INTR, R_DMA_CH4_FIRST, R_DMA_CH4_CMD,
-	  R_DMA_CH4_STATUS, R_DMA_CH4_HWSW, R_DMA_CH4_DESCR,
-	  R_DMA_CH5_CLR_INTR, R_DMA_CH5_FIRST, R_DMA_CH5_CMD,
-	  R_DMA_CH5_STATUS, R_DMA_CH5_HWSW, R_DMA_CH5_DESCR,
-	  STD_FLAGS, DEF_RX, DEF_TX, 1,
+	{ .baud        = DEF_BAUD,
+	  .port        = (unsigned char *)R_SERIAL3_CTRL,
+	  .irq         = 1U << 8,  /* uses DMA 4 and 5 */
+	  .oclrintradr = R_DMA_CH4_CLR_INTR,
+	  .ofirstadr   = R_DMA_CH4_FIRST,
+	  .ocmdadr     = R_DMA_CH4_CMD,
+	  .ostatusadr  = R_DMA_CH4_STATUS,
+	  .iclrintradr = R_DMA_CH5_CLR_INTR,
+	  .ifirstadr   = R_DMA_CH5_FIRST,
+	  .icmdadr     = R_DMA_CH5_CMD,
+	  .idescradr   = R_DMA_CH5_DESCR,
+	  .flags       = STD_FLAGS,
+	  .rx_ctrl     = DEF_RX,
+	  .tx_ctrl     = DEF_TX,
+	  .iseteop     = 1,
 #ifdef CONFIG_ETRAX_SERIAL_PORT3
-          1
+          .enabled  = 1,
+#ifdef CONFIG_ETRAX_SERIAL_PORT3_DMA4_OUT
+	  .dma_out_enabled = 1,
+#else
+	  .dma_out_enabled = 0,
+#endif
+#ifdef CONFIG_ETRAX_SERIAL_PORT3_DMA5_IN
+	  .dma_in_enabled = 1,
 #else
-          0
+	  .dma_in_enabled = 0
+#endif
+#else
+          .enabled  = 0,
+	  .dma_out_enabled = 0,
+	  .dma_in_enabled = 0  
 #endif
  }   /* ttyS3 */
 #endif
@@ -627,7 +884,7 @@
  ((CONFIG_ETRAX_SER##line##_##pinname##_ON_PA_BIT >= 0)? \
 		(R_PORT_PA_DATA): ( \
  (CONFIG_ETRAX_SER##line##_##pinname##_ON_PB_BIT >= 0)? \
-		(R_PORT_PB_DATA):&dummy_ser[##line##]))
+		(R_PORT_PB_DATA):&dummy_ser[line]))
 
 #define E100_STRUCT_SHADOW(line, pinname) \
  ((CONFIG_ETRAX_SER##line##_##pinname##_ON_PA_BIT >= 0)? \
@@ -646,6 +903,225 @@
 #define DUMMY_CD_MASK  8
 static unsigned char dummy_ser[NR_PORTS] = {0xFF, 0xFF, 0xFF,0xFF};
 
+/* If not all status pins are used or disabled, use mixed mode */
+#ifdef CONFIG_ETRAX_SERIAL_PORT0
+
+#define SER0_PA_BITSUM (CONFIG_ETRAX_SER0_DTR_ON_PA_BIT+CONFIG_ETRAX_SER0_RI_ON_PA_BIT+CONFIG_ETRAX_SER0_DSR_ON_PA_BIT+CONFIG_ETRAX_SER0_CD_ON_PA_BIT)
+
+#if SER0_PA_BITSUM != -4
+#  if CONFIG_ETRAX_SER0_DTR_ON_PA_BIT == -1
+#    ifndef CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_MIXED
+#      define CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_MIXED 1
+#    endif
+#   endif
+# if CONFIG_ETRAX_SER0_RI_ON_PA_BIT == -1
+#   ifndef CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_MIXED
+#     define CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_MIXED 1
+#   endif
+#  endif
+#  if CONFIG_ETRAX_SER0_DSR_ON_PA_BIT == -1
+#    ifndef CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_MIXED
+#      define CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_MIXED 1
+#    endif
+#  endif
+#  if CONFIG_ETRAX_SER0_CD_ON_PA_BIT == -1
+#    ifndef CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_MIXED
+#      define CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_MIXED 1
+#    endif
+#  endif
+#endif
+
+#define SER0_PB_BITSUM (CONFIG_ETRAX_SER0_DTR_ON_PB_BIT+CONFIG_ETRAX_SER0_RI_ON_PB_BIT+CONFIG_ETRAX_SER0_DSR_ON_PB_BIT+CONFIG_ETRAX_SER0_CD_ON_PB_BIT)
+
+#if SER0_PB_BITSUM != -4
+#  if CONFIG_ETRAX_SER0_DTR_ON_PB_BIT == -1
+#    ifndef CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_MIXED
+#      define CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_MIXED 1
+#    endif
+#   endif
+# if CONFIG_ETRAX_SER0_RI_ON_PB_BIT == -1
+#   ifndef CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_MIXED
+#     define CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_MIXED 1
+#   endif
+#  endif
+#  if CONFIG_ETRAX_SER0_DSR_ON_PB_BIT == -1
+#    ifndef CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_MIXED
+#      define CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_MIXED 1
+#    endif
+#  endif
+#  if CONFIG_ETRAX_SER0_CD_ON_PB_BIT == -1
+#    ifndef CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_MIXED
+#      define CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_MIXED 1
+#    endif
+#  endif
+#endif
+
+#endif /* PORT0 */
+
+
+#ifdef CONFIG_ETRAX_SERIAL_PORT1
+
+#define SER1_PA_BITSUM (CONFIG_ETRAX_SER1_DTR_ON_PA_BIT+CONFIG_ETRAX_SER1_RI_ON_PA_BIT+CONFIG_ETRAX_SER1_DSR_ON_PA_BIT+CONFIG_ETRAX_SER1_CD_ON_PA_BIT)
+
+#if SER1_PA_BITSUM != -4
+#  if CONFIG_ETRAX_SER1_DTR_ON_PA_BIT == -1
+#    ifndef CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_MIXED
+#      define CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_MIXED 1
+#    endif
+#   endif
+# if CONFIG_ETRAX_SER1_RI_ON_PA_BIT == -1
+#   ifndef CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_MIXED
+#     define CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_MIXED 1
+#   endif
+#  endif
+#  if CONFIG_ETRAX_SER1_DSR_ON_PA_BIT == -1
+#    ifndef CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_MIXED
+#      define CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_MIXED 1
+#    endif
+#  endif
+#  if CONFIG_ETRAX_SER1_CD_ON_PA_BIT == -1
+#    ifndef CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_MIXED
+#      define CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_MIXED 1
+#    endif
+#  endif
+#endif
+
+#define SER1_PB_BITSUM (CONFIG_ETRAX_SER1_DTR_ON_PB_BIT+CONFIG_ETRAX_SER1_RI_ON_PB_BIT+CONFIG_ETRAX_SER1_DSR_ON_PB_BIT+CONFIG_ETRAX_SER1_CD_ON_PB_BIT)
+
+#if SER1_PB_BITSUM != -4
+#  if CONFIG_ETRAX_SER1_DTR_ON_PB_BIT == -1
+#    ifndef CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_MIXED
+#      define CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_MIXED 1
+#    endif
+#   endif
+# if CONFIG_ETRAX_SER1_RI_ON_PB_BIT == -1
+#   ifndef CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_MIXED
+#     define CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_MIXED 1
+#   endif
+#  endif
+#  if CONFIG_ETRAX_SER1_DSR_ON_PB_BIT == -1
+#    ifndef CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_MIXED
+#      define CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_MIXED 1
+#    endif
+#  endif
+#  if CONFIG_ETRAX_SER1_CD_ON_PB_BIT == -1
+#    ifndef CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_MIXED
+#      define CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_MIXED 1
+#    endif
+#  endif
+#endif
+
+#endif /* PORT1 */
+
+#ifdef CONFIG_ETRAX_SERIAL_PORT2
+
+#define SER2_PA_BITSUM (CONFIG_ETRAX_SER2_DTR_ON_PA_BIT+CONFIG_ETRAX_SER2_RI_ON_PA_BIT+CONFIG_ETRAX_SER2_DSR_ON_PA_BIT+CONFIG_ETRAX_SER2_CD_ON_PA_BIT)
+
+#if SER2_PA_BITSUM != -4
+#  if CONFIG_ETRAX_SER2_DTR_ON_PA_BIT == -1
+#    ifndef CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_MIXED
+#      define CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_MIXED 1
+#    endif
+#   endif
+# if CONFIG_ETRAX_SER2_RI_ON_PA_BIT == -1
+#   ifndef CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_MIXED
+#     define CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_MIXED 1
+#   endif
+#  endif
+#  if CONFIG_ETRAX_SER2_DSR_ON_PA_BIT == -1
+#    ifndef CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_MIXED
+#      define CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_MIXED 1
+#    endif
+#  endif
+#  if CONFIG_ETRAX_SER2_CD_ON_PA_BIT == -1
+#    ifndef CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_MIXED
+#      define CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_MIXED 1
+#    endif
+#  endif
+#endif
+
+#define SER2_PB_BITSUM (CONFIG_ETRAX_SER2_DTR_ON_PB_BIT+CONFIG_ETRAX_SER2_RI_ON_PB_BIT+CONFIG_ETRAX_SER2_DSR_ON_PB_BIT+CONFIG_ETRAX_SER2_CD_ON_PB_BIT)
+
+#if SER2_PB_BITSUM != -4
+#  if CONFIG_ETRAX_SER2_DTR_ON_PB_BIT == -1
+#    ifndef CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_MIXED
+#      define CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_MIXED 1
+#    endif
+#   endif
+# if CONFIG_ETRAX_SER2_RI_ON_PB_BIT == -1
+#   ifndef CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_MIXED
+#     define CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_MIXED 1
+#   endif
+#  endif
+#  if CONFIG_ETRAX_SER2_DSR_ON_PB_BIT == -1
+#    ifndef CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_MIXED
+#      define CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_MIXED 1
+#    endif
+#  endif
+#  if CONFIG_ETRAX_SER2_CD_ON_PB_BIT == -1
+#    ifndef CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_MIXED
+#      define CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_MIXED 1
+#    endif
+#  endif
+#endif
+
+#endif /* PORT2 */
+
+#ifdef CONFIG_ETRAX_SERIAL_PORT3
+
+#define SER3_PA_BITSUM (CONFIG_ETRAX_SER3_DTR_ON_PA_BIT+CONFIG_ETRAX_SER3_RI_ON_PA_BIT+CONFIG_ETRAX_SER3_DSR_ON_PA_BIT+CONFIG_ETRAX_SER3_CD_ON_PA_BIT)
+
+#if SER3_PA_BITSUM != -4
+#  if CONFIG_ETRAX_SER3_DTR_ON_PA_BIT == -1
+#    ifndef CONFIG_ETRAX_SER3_DTR_RI_DSR_CD_MIXED
+#      define CONFIG_ETRAX_SER3_DTR_RI_DSR_CD_MIXED 1
+#    endif
+#   endif
+# if CONFIG_ETRAX_SER3_RI_ON_PA_BIT == -1
+#   ifndef CONFIG_ETRAX_SER3_DTR_RI_DSR_CD_MIXED
+#     define CONFIG_ETRAX_SER3_DTR_RI_DSR_CD_MIXED 1
+#   endif
+#  endif
+#  if CONFIG_ETRAX_SER3_DSR_ON_PA_BIT == -1
+#    ifndef CONFIG_ETRAX_SER3_DTR_RI_DSR_CD_MIXED
+#      define CONFIG_ETRAX_SER3_DTR_RI_DSR_CD_MIXED 1
+#    endif
+#  endif
+#  if CONFIG_ETRAX_SER3_CD_ON_PA_BIT == -1
+#    ifndef CONFIG_ETRAX_SER3_DTR_RI_DSR_CD_MIXED
+#      define CONFIG_ETRAX_SER3_DTR_RI_DSR_CD_MIXED 1
+#    endif
+#  endif
+#endif
+
+#define SER3_PB_BITSUM (CONFIG_ETRAX_SER3_DTR_ON_PB_BIT+CONFIG_ETRAX_SER3_RI_ON_PB_BIT+CONFIG_ETRAX_SER3_DSR_ON_PB_BIT+CONFIG_ETRAX_SER3_CD_ON_PB_BIT)
+
+#if SER3_PB_BITSUM != -4
+#  if CONFIG_ETRAX_SER3_DTR_ON_PB_BIT == -1
+#    ifndef CONFIG_ETRAX_SER3_DTR_RI_DSR_CD_MIXED
+#      define CONFIG_ETRAX_SER3_DTR_RI_DSR_CD_MIXED 1
+#    endif
+#   endif
+# if CONFIG_ETRAX_SER3_RI_ON_PB_BIT == -1
+#   ifndef CONFIG_ETRAX_SER3_DTR_RI_DSR_CD_MIXED
+#     define CONFIG_ETRAX_SER3_DTR_RI_DSR_CD_MIXED 1
+#   endif
+#  endif
+#  if CONFIG_ETRAX_SER3_DSR_ON_PB_BIT == -1
+#    ifndef CONFIG_ETRAX_SER3_DTR_RI_DSR_CD_MIXED
+#      define CONFIG_ETRAX_SER3_DTR_RI_DSR_CD_MIXED 1
+#    endif
+#  endif
+#  if CONFIG_ETRAX_SER3_CD_ON_PB_BIT == -1
+#    ifndef CONFIG_ETRAX_SER3_DTR_RI_DSR_CD_MIXED
+#      define CONFIG_ETRAX_SER3_DTR_RI_DSR_CD_MIXED 1
+#    endif
+#  endif
+#endif
+
+#endif /* PORT3 */
+
+
 #if defined(CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_MIXED) || \
     defined(CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_MIXED) || \
     defined(CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_MIXED) || \
@@ -653,6 +1129,7 @@
 #define CONFIG_ETRAX_SERX_DTR_RI_DSR_CD_MIXED
 #endif
 
+
 #ifdef CONFIG_ETRAX_SERX_DTR_RI_DSR_CD_MIXED
 /* The pins can be mixed on PA and PB */
 #define CONTROL_PINS_PORT_NOT_USED(line) \
@@ -838,7 +1315,6 @@
 #define E100_RTS_MASK 0x20
 #define E100_CTS_MASK 0x40
 
-
 /* All serial port signals are active low:
  * active   = 0 -> 3.3V to RS-232 driver -> -12V on RS-232 level
  * inactive = 1 -> 0V   to RS-232 driver -> +12V on RS-232 level
@@ -937,6 +1413,9 @@
 
 	/* calc timeout */
 	info->char_time_usec = ((bits * 1000000) / info->baud) + 1;
+	info->flush_time_usec = 4*info->char_time_usec;
+	if (info->flush_time_usec < MIN_FLUSH_TIME_USEC)
+		info->flush_time_usec = MIN_FLUSH_TIME_USEC;
 }
 
 /*
@@ -980,7 +1459,7 @@
 		retval = baud_table[cflag & CBAUD];
 
 	if (retval < 0) {
-		printk("serdriver tried setting invalid baud rate, flags %x.\n", cflag);
+		printk(KERN_WARNING "serdriver tried setting invalid baud rate, flags %x.\n", cflag);
 		retval = 5; /* choose default 9600 instead */
 	}
 
@@ -1036,9 +1515,13 @@
 e100_rts(struct e100_serial *info, int set)
 {
 #ifndef CONFIG_SVINTO_SIM
+	unsigned long flags;
+	save_flags(flags);
+	cli();
 	info->rx_ctrl &= ~E100_RTS_MASK;
 	info->rx_ctrl |= (set ? 0 : E100_RTS_MASK);  /* RTS is active low */
 	info->port[REG_REC_CTRL] = info->rx_ctrl;
+	restore_flags(flags);
 #ifdef SERIAL_DEBUG_IO  
 	printk("ser%i rts %i\n", info->line, set);
 #endif
@@ -1112,6 +1595,7 @@
 #ifdef SERIAL_DEBUG_INTR
 	printk("rxdma_irq(%d): 0\n",info->line);
 #endif
+	DINTR1(DEBUG_LOG(info->line,"IRQ disable_rxdma_irq %i\n", info->line));
 	*R_IRQ_MASK2_CLR = (info->irq << 2) | (info->irq << 3);
 }
 
@@ -1121,6 +1605,7 @@
 #ifdef SERIAL_DEBUG_INTR
 	printk("rxdma_irq(%d): 1\n",info->line);
 #endif
+	DINTR1(DEBUG_LOG(info->line,"IRQ enable_rxdma_irq %i\n", info->line));
 	*R_IRQ_MASK2_SET = (info->irq << 2) | (info->irq << 3);
 }
 
@@ -1132,6 +1617,7 @@
 #ifdef SERIAL_DEBUG_INTR
 	printk("txdma_irq(%d): 0\n",info->line);
 #endif
+	DINTR1(DEBUG_LOG(info->line,"IRQ disable_txdma_irq %i\n", info->line));
 	*R_IRQ_MASK2_CLR = info->irq;
 }
 
@@ -1141,9 +1627,143 @@
 #ifdef SERIAL_DEBUG_INTR
 	printk("txdma_irq(%d): 1\n",info->line);
 #endif
+	DINTR1(DEBUG_LOG(info->line,"IRQ enable_txdma_irq %i\n", info->line));
 	*R_IRQ_MASK2_SET = info->irq;
 }
 
+static _INLINE_ void
+e100_disable_txdma_channel(struct e100_serial *info)
+{
+	unsigned long flags;
+  
+	/* Disable output DMA channel for the serial port in question
+	 * ( set to something other then serialX)
+	 */
+	save_flags(flags);
+	cli();
+	DFLOW(DEBUG_LOG(info->line, "disable_txdma_channel %i\n", info->line));
+	if (info->line == 0) {
+		if ((genconfig_shadow & IO_MASK(R_GEN_CONFIG, dma6)) ==
+		    IO_STATE(R_GEN_CONFIG, dma6, serial0)) {
+			genconfig_shadow &=  ~IO_MASK(R_GEN_CONFIG, dma6);
+			genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma6, unused);
+		}
+	} else if (info->line == 1) {
+		if ((genconfig_shadow & IO_MASK(R_GEN_CONFIG, dma8)) ==
+		    IO_STATE(R_GEN_CONFIG, dma8, serial1)) {
+			genconfig_shadow &=  ~IO_MASK(R_GEN_CONFIG, dma8);
+			genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma8, usb);
+		}
+	} else if (info->line == 2) {
+		if ((genconfig_shadow & IO_MASK(R_GEN_CONFIG, dma2)) ==
+		    IO_STATE(R_GEN_CONFIG, dma2, serial2)) {
+			genconfig_shadow &=  ~IO_MASK(R_GEN_CONFIG, dma2);
+			genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma2, par0);
+		}
+	} else if (info->line == 3) {
+		if ((genconfig_shadow & IO_MASK(R_GEN_CONFIG, dma4)) ==
+		    IO_STATE(R_GEN_CONFIG, dma4, serial3)) {
+			genconfig_shadow &=  ~IO_MASK(R_GEN_CONFIG, dma4);
+			genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma4, par1);
+		}
+	}
+	*R_GEN_CONFIG = genconfig_shadow;
+	restore_flags(flags);
+}
+
+
+static _INLINE_ void
+e100_enable_txdma_channel(struct e100_serial *info)
+{
+	unsigned long flags;
+  
+	save_flags(flags);
+	cli();
+	DFLOW(DEBUG_LOG(info->line, "enable_txdma_channel %i\n", info->line));
+	/* Enable output DMA channel for the serial port in question */
+	if (info->line == 0) {
+		genconfig_shadow &=  ~IO_MASK(R_GEN_CONFIG, dma6);
+		genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma6, serial0);
+	} else if (info->line == 1) {
+		genconfig_shadow &=  ~IO_MASK(R_GEN_CONFIG, dma8);
+		genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma8, serial1);
+	} else if (info->line == 2) {
+		genconfig_shadow &=  ~IO_MASK(R_GEN_CONFIG, dma2);
+		genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma2, serial2);
+	} else if (info->line == 3) {
+		genconfig_shadow &=  ~IO_MASK(R_GEN_CONFIG, dma4);
+		genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma4, serial3);
+	}
+	*R_GEN_CONFIG = genconfig_shadow;
+	restore_flags(flags);
+}
+
+static _INLINE_ void
+e100_disable_rxdma_channel(struct e100_serial *info)
+{
+	unsigned long flags;
+  
+	/* Disable input DMA channel for the serial port in question
+	 * ( set to something other then serialX)
+	 */
+	save_flags(flags);
+	cli();  
+	if (info->line == 0) {
+		if ((genconfig_shadow & IO_MASK(R_GEN_CONFIG, dma7)) ==
+		    IO_STATE(R_GEN_CONFIG, dma7, serial0)) {
+			genconfig_shadow &=  ~IO_MASK(R_GEN_CONFIG, dma7);
+			genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma7, unused);
+		}
+	} else if (info->line == 1) {
+		if ((genconfig_shadow & IO_MASK(R_GEN_CONFIG, dma9)) ==
+		    IO_STATE(R_GEN_CONFIG, dma9, serial1)) {
+			genconfig_shadow &=  ~IO_MASK(R_GEN_CONFIG, dma9);
+			genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma9, usb);
+		}
+	} else if (info->line == 2) {
+		if ((genconfig_shadow & IO_MASK(R_GEN_CONFIG, dma3)) ==
+		    IO_STATE(R_GEN_CONFIG, dma3, serial2)) {
+			genconfig_shadow &=  ~IO_MASK(R_GEN_CONFIG, dma3);
+			genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma3, par0);
+		}
+	} else if (info->line == 3) {
+		if ((genconfig_shadow & IO_MASK(R_GEN_CONFIG, dma5)) ==
+		    IO_STATE(R_GEN_CONFIG, dma5, serial3)) {
+			genconfig_shadow &=  ~IO_MASK(R_GEN_CONFIG, dma5);
+			genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma5, par1);
+		}
+	}
+	*R_GEN_CONFIG = genconfig_shadow;
+	restore_flags(flags);
+}
+
+
+static _INLINE_ void
+e100_enable_rxdma_channel(struct e100_serial *info)
+{
+	unsigned long flags;
+  
+	save_flags(flags);
+	cli();  
+	/* Enable input DMA channel for the serial port in question */
+	if (info->line == 0) {
+		genconfig_shadow &=  ~IO_MASK(R_GEN_CONFIG, dma7);
+		genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma7, serial0);
+	} else if (info->line == 1) {
+		genconfig_shadow &=  ~IO_MASK(R_GEN_CONFIG, dma9);
+		genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma9, serial1);
+	} else if (info->line == 2) {
+		genconfig_shadow &=  ~IO_MASK(R_GEN_CONFIG, dma3);
+		genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma3, serial2);
+	} else if (info->line == 3) {
+		genconfig_shadow &=  ~IO_MASK(R_GEN_CONFIG, dma5);
+		genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma5, serial3);
+	}
+	*R_GEN_CONFIG = genconfig_shadow;
+	restore_flags(flags);
+}
+
+
 #ifdef SERIAL_HANDLE_EARLY_ERRORS
 /* in order to detect and fix errors on the first byte
    we have to use the serial interrupts as well. */
@@ -1154,6 +1774,7 @@
 #ifdef SERIAL_DEBUG_INTR
 	printk("ser_irq(%d): 0\n",info->line);
 #endif
+	DINTR1(DEBUG_LOG(info->line,"IRQ disable data_irq %i\n", info->line));
 	*R_IRQ_MASK1_CLR = (1U << (8+2*info->line));
 }
 
@@ -1166,10 +1787,50 @@
 	       (8+2*info->line),
 	       (1U << (8+2*info->line)));
 #endif
+	DINTR1(DEBUG_LOG(info->line,"IRQ enable data_irq %i\n", info->line));
 	*R_IRQ_MASK1_SET = (1U << (8+2*info->line));
 }
 #endif
 
+static inline void
+e100_disable_serial_tx_ready_irq(struct e100_serial *info) 
+{
+#ifdef SERIAL_DEBUG_INTR
+	printk("ser_tx_irq(%d): 0\n",info->line);
+#endif
+	DINTR1(DEBUG_LOG(info->line,"IRQ disable ready_irq %i\n", info->line));
+	*R_IRQ_MASK1_CLR = (1U << (8+1+2*info->line));
+}
+
+static inline void
+e100_enable_serial_tx_ready_irq(struct e100_serial *info) 
+{
+#ifdef SERIAL_DEBUG_INTR
+	printk("ser_tx_irq(%d): 1\n",info->line);
+	printk("**** %d = %d\n",
+	       (8+1+2*info->line),
+	       (1U << (8+1+2*info->line)));
+#endif
+	DINTR2(DEBUG_LOG(info->line,"IRQ enable ready_irq %i\n", info->line));
+	*R_IRQ_MASK1_SET = (1U << (8+1+2*info->line));
+}
+
+static inline void e100_enable_rx_irq(struct e100_serial *info)
+{
+	if (info->uses_dma_in)
+		e100_enable_rxdma_irq(info);
+	else
+		e100_enable_serial_data_irq(info);
+}
+static inline void e100_disable_rx_irq(struct e100_serial *info)
+{
+	if (info->uses_dma_in)
+		e100_disable_rxdma_irq(info);
+	else
+		e100_disable_serial_data_irq(info);
+}
+
+
 #if defined(CONFIG_ETRAX_RS485)
 /* Enable RS-485 mode on selected port. This is UGLY. */
 static int
@@ -1183,7 +1844,10 @@
 
 	info->rs485.rts_on_send = 0x01 & r->rts_on_send;
 	info->rs485.rts_after_sent = 0x01 & r->rts_after_sent;
-	info->rs485.delay_rts_before_send = r->delay_rts_before_send;
+	if (r->delay_rts_before_send >= 1000)
+		info->rs485.delay_rts_before_send = 1000;
+	else
+		info->rs485.delay_rts_before_send = r->delay_rts_before_send;
 	info->rs485.enabled = r->enabled;
 /*	printk("rts: on send = %i, after = %i, enabled = %i",
 		    info->rs485.rts_on_send,
@@ -1223,7 +1887,7 @@
 	e100_rts(info, info->rs485.rts_after_sent);
 #if defined(CONFIG_ETRAX_RS485_DISABLE_RECEIVER)
 	e100_enable_rx(info);
-	e100_enable_rxdma_irq(info);
+	e100_enable_rx_irq(info);	
 #endif
 }
 #endif
@@ -1245,8 +1909,12 @@
 	if (info) {
 		unsigned long flags;
 		unsigned long xoff;
-		
+
 		save_flags(flags); cli();
+		DFLOW(DEBUG_LOG(info->line, "XOFF rs_stop xmit %i\n",
+				CIRC_CNT(info->xmit.head,
+					 info->xmit.tail,SERIAL_XMIT_SIZE)));
+		
 		xoff = IO_FIELD(R_SERIAL0_XOFF, xoff_char, STOP_CHAR(info->tty));
 		xoff |= IO_STATE(R_SERIAL0_XOFF, tx_stop, stop);
 		if (tty->termios->c_iflag & IXON ) {
@@ -1267,6 +1935,9 @@
 		unsigned long xoff;
 
 		save_flags(flags); cli();
+		DFLOW(DEBUG_LOG(info->line, "XOFF rs_start xmit %i\n",
+				CIRC_CNT(info->xmit.head,
+					 info->xmit.tail,SERIAL_XMIT_SIZE)));
 		xoff = IO_FIELD(R_SERIAL0_XOFF, xoff_char, STOP_CHAR(tty));
 		xoff |= IO_STATE(R_SERIAL0_XOFF, tx_stop, enable);
 		if (tty->termios->c_iflag & IXON ) {
@@ -1274,6 +1945,9 @@
 		}
 	
 		*((unsigned long *)&info->port[REG_XOFF]) = xoff;
+		if (!info->uses_dma_out &&
+		    info->xmit.head != info->xmit.tail && info->xmit.buf)
+			e100_enable_serial_tx_ready_irq(info);
 		
 		restore_flags(flags);
 	}
@@ -1308,6 +1982,8 @@
 rs_sched_event(struct e100_serial *info,
 				    int event)
 {
+	if (info->event & (1 << event))
+		return;
 	info->event |= 1 << event;
 	queue_task(&info->tqueue, &tq_serial);
 	mark_bh(SERIAL_BH);
@@ -1325,7 +2001,7 @@
  */
 
 static void 
-transmit_chars(struct e100_serial *info)
+transmit_chars_dma(struct e100_serial *info)
 {
 	unsigned int c, sentl;
 	struct etrax_dma_descr *descr;
@@ -1333,11 +2009,11 @@
 #ifdef CONFIG_SVINTO_SIM
 	/* This will output too little if tail is not 0 always since
 	 * we don't reloop to send the other part. Anyway this SHOULD be a
-	 * no-op - transmit_chars would never really be called during sim
+	 * no-op - transmit_chars_dma would never really be called during sim
 	 * since rs_write does not write into the xmit buffer then.
 	 */
 	if (info->xmit.tail)
-		printk("Error in serial.c:transmit_chars(), tail!=0\n");
+		printk("Error in serial.c:transmit_chars_dma(), tail!=0\n");
 	if (info->xmit.head != info->xmit.tail) {
 		SIMCOUT(info->xmit.buf + info->xmit.tail,
 			CIRC_CNT(info->xmit.head,
@@ -1359,7 +2035,7 @@
 #endif
 	if (!info->tr_running) {
 		/* weirdo... we shouldn't get here! */
-		printk("Achtung: transmit_chars with !tr_running\n");
+		printk(KERN_WARNING "Achtung: transmit_chars_dma with !tr_running\n");
 		return;
 	}
 
@@ -1375,6 +2051,8 @@
 		/* otherwise we find the amount of data sent here */
 		sentl = descr->hw_len;
 
+	DFLOW(DEBUG_LOG(info->line, "TX %i done\n", sentl));
+		
 	/* update stats */
 	info->icount.tx += sentl;
 
@@ -1392,6 +2070,13 @@
 
 	c = CIRC_CNT_TO_END(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE);
 
+	/* Don't send all in one DMA transfer - divide it so we wake up
+	 * application before all is sent
+	 */
+
+	if (c >= 4*WAKEUP_CHARS)
+		c = c/2;
+
 	if (c <= 0) {
 		/* our job here is done, don't schedule any new DMA transfer */
 		info->tr_running = 0;
@@ -1411,17 +2096,17 @@
 
 	/* ok we can schedule a dma send of c chars starting at info->xmit.tail */
 	/* set up the descriptor correctly for output */
-
+	DFLOW(DEBUG_LOG(info->line, "TX %i\n", c));
 	descr->ctrl = d_int | d_eol | d_wait; /* Wait needed for tty_wait_until_sent() */
 	descr->sw_len = c;
 	descr->buf = virt_to_phys(info->xmit.buf + info->xmit.tail);
 	descr->status = 0;
 
 	*info->ofirstadr = virt_to_phys(descr); /* write to R_DMAx_FIRST */
-	*info->ocmdadr = 1;       /* dma command start -> R_DMAx_CMD */
+	*info->ocmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, start);       /* dma command start -> R_DMAx_CMD */
 	
 	/* DMA is now running (hopefully) */
-} /* transmit_chars */
+} /* transmit_chars_dma */
 
 static void 
 start_transmit(struct e100_serial *info)
@@ -1435,15 +2120,17 @@
 	info->tr_descr.hw_len = 0;
 	info->tr_descr.status = 0;
 	info->tr_running = 1;
-
-	transmit_chars(info);
+	if (info->uses_dma_out)
+		transmit_chars_dma(info);
+	else
+		e100_enable_serial_tx_ready_irq(info);
 } /* start_transmit */
 
 #ifdef CONFIG_ETRAX_SERIAL_FAST_TIMER
 static int serial_fast_timer_started = 0;
 static int serial_fast_timer_expired = 0;
 static void flush_timeout_function(unsigned long data);
-#define START_FLUSH_FAST_TIMER(info, string) {\
+#define START_FLUSH_FAST_TIMER_TIME(info, string, usec) {\
   unsigned long timer_flags; \
   save_flags(timer_flags); \
   cli(); \
@@ -1454,7 +2141,7 @@
     start_one_shot_timer(&fast_timers[info->line], \
                          flush_timeout_function, \
                          (unsigned long)info, \
-                         info->char_time_usec*4, \
+                         (usec), \
                          string); \
   } \
   else { \
@@ -1462,8 +2149,10 @@
   } \
   restore_flags(timer_flags); \
 }
+#define START_FLUSH_FAST_TIMER(info, string) START_FLUSH_FAST_TIMER_TIME(info, string, info->flush_time_usec)
 
 #else
+#define START_FLUSH_FAST_TIMER_TIME(info, string, usec)
 #define START_FLUSH_FAST_TIMER(info, string)
 #endif
 
@@ -1508,18 +2197,27 @@
 add_char_and_flag(struct e100_serial *info, unsigned char data, unsigned char flag)
 {
 	struct etrax_recv_buffer *buffer;
+	if (info->uses_dma_in) {
+		if (!(buffer = alloc_recv_buffer(4)))
+			return 0;
 
-	if (!(buffer = alloc_recv_buffer(4)))
-		return 0;
-
-	buffer->length = 1;
-	buffer->error = flag;
-	buffer->buffer[0] = data;
+		buffer->length = 1;
+		buffer->error = flag;
+		buffer->buffer[0] = data;
 	
-	append_recv_buffer(info, buffer);
-
-	info->icount.rx++;
+		append_recv_buffer(info, buffer);
 
+		info->icount.rx++;
+	} else {
+		struct tty_struct *tty = info->tty;		
+		*tty->flip.char_buf_ptr = data;
+		*tty->flip.flag_buf_ptr = flag;
+		tty->flip.flag_buf_ptr++;
+		tty->flip.char_buf_ptr++;
+		tty->flip.count++;
+		info->icount.rx++;
+	}
+	
 	return 1;
 }
 
@@ -1529,7 +2227,8 @@
 	struct etrax_recv_buffer *buffer = phys_to_virt(descr->buf) - sizeof *buffer;
 
 	if (info->recv_cnt + recvl > 65536) {
-		printk(__FUNCTION__ ": Too much pending incoming serial data! Dropping %u bytes.\n", recvl);
+		printk(KERN_CRIT
+		       "%s: Too much pending incoming serial data! Dropping %u bytes.\n", __FUNCTION__, recvl);
 		return 0;
 	}
 
@@ -1542,7 +2241,7 @@
 	append_recv_buffer(info, buffer);
 
 	if (!(buffer = alloc_recv_buffer(SERIAL_DESCR_BUF_SIZE)))
-		panic(__FUNCTION__ ": Failed to allocate memory for receive buffer!\n");
+		panic("%s: Failed to allocate memory for receive buffer!\n", __FUNCTION__);
 
 	descr->buf = virt_to_phys(buffer->buffer);
 	
@@ -1579,8 +2278,16 @@
 		/* Reset the status information */
 		descr->status = 0;
 
-		DEBUG_LOG(info->line, "recvl %lu\n", recvl);
-
+		DFLOW(  DEBUG_LOG(info->line, "RX %lu\n", recvl);
+			if (info->tty->stopped) {
+				unsigned char *buf = phys_to_virt(descr->buf);
+				DEBUG_LOG(info->line, "rx 0x%02X\n", buf[0]);
+				DEBUG_LOG(info->line, "rx 0x%02X\n", buf[1]);
+				DEBUG_LOG(info->line, "rx 0x%02X\n", buf[2]);
+			}
+			);
+		
+		
 		/* update stats */
 		info->icount.rx += recvl;
 
@@ -1591,7 +2298,7 @@
 }
 
 static _INLINE_ void 
-receive_chars(struct e100_serial *info)
+receive_chars_dma(struct e100_serial *info)
 {
 	struct tty_struct *tty;
 	unsigned char rstat;
@@ -1623,7 +2330,10 @@
 
 	/* Read the status register to detect errors */
 	rstat = info->port[REG_STATUS];
-
+	if (rstat & IO_MASK(R_SERIAL0_STATUS, xoff_detect) ) {
+		DFLOW(DEBUG_LOG(info->line, "XOFF detect stat %x\n", rstat));
+	}
+		
 	if (rstat & SER_ERROR_MASK) {
 		/* If we got an error, we must reset it by reading the
 		 * data_in field
@@ -1658,7 +2368,7 @@
 	/* Set up the receiving descriptors */
 	for (i = 0; i < SERIAL_RECV_DESCRIPTORS; i++) {
 		if (!(buffer = alloc_recv_buffer(SERIAL_DESCR_BUF_SIZE)))
-			panic(__FUNCTION__ ": Failed to allocate memory for receive buffer!\n");
+			panic("%s: Failed to allocate memory for receive buffer!\n", __FUNCTION__);
 
 		descr[i].ctrl = d_int;
 		descr[i].buf = virt_to_phys(buffer->buffer);
@@ -1691,20 +2401,20 @@
 	 */
 	return;
 #endif
+	info->tty->flip.count = 0;	
+	if (info->uses_dma_in) {
+		/* reset the input dma channel to be sure it works */
+
+		*info->icmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, reset);
+		while (IO_EXTRACT(R_DMA_CH6_CMD, cmd, *info->icmdadr) ==
+		       IO_STATE_VALUE(R_DMA_CH6_CMD, cmd, reset));
 
-	/* reset the input dma channel to be sure it works */
-
-	*info->icmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, reset);
-	while (IO_EXTRACT(R_DMA_CH6_CMD, cmd, *info->icmdadr) ==
-	       IO_STATE_VALUE(R_DMA_CH6_CMD, cmd, reset));
-
-	info->tty->flip.count = 0;
-
-	start_recv_dma(info);
+		start_recv_dma(info);
 	
 #ifdef CONFIG_ETRAX_SERIAL_FLUSH_DMA_FAST
-	start_flush_timer();
+		start_flush_timer();
 #endif
+	}
 }
 
 
@@ -1749,25 +2459,25 @@
 	
 	for (i = 0; i < NR_PORTS; i++) {
 		info = rs_table + i;
-		if (!info->enabled || !info->uses_dma) 
+		if (!info->enabled || !info->uses_dma_out) 
 			continue; 
 		/* check for dma_descr (don't need to check for dma_eop in output dma for serial */
 		if (ireg & info->irq) {  
 			/* we can send a new dma bunch. make it so. */
-			DEBUG_LOG(info->line, "tr_interrupt %i\n", i);
+			DINTR2(DEBUG_LOG(info->line, "tr_interrupt %i\n", i));
 			/* Read jiffies_usec first, 
 			 * we want this time to be as late as possible
 			 */
  			PROCSTAT(ser_stat[info->line].tx_dma_ints++);
 			info->last_tx_active_usec = GET_JIFFIES_USEC();
 			info->last_tx_active = jiffies;
-			transmit_chars(info);
+			transmit_chars_dma(info);
 		}
 		
 		/* FIXME: here we should really check for a change in the
 		   status lines and if so call status_handle(info) */
 	}
-}
+} /* tr_interrupt */
 
 /* dma input channel interrupt handler */
 
@@ -1795,18 +2505,18 @@
 	
 	for (i = 0; i < NR_PORTS; i++) {
 		info = rs_table + i;
-		if (!info->enabled || !info->uses_dma) 
+		if (!info->enabled || !info->uses_dma_in) 
 			continue; 
 		/* check for both dma_eop and dma_descr for the input dma channel */
 		if (ireg & ((info->irq << 2) | (info->irq << 3))) {
 			/* we have received something */
-			receive_chars(info);
+			receive_chars_dma(info);
 		}
 		
 		/* FIXME: here we should really check for a change in the
 		   status lines and if so call status_handle(info) */
 	}
-}
+} /* rec_interrupt */
 
 static _INLINE_ int
 force_eop_if_needed(struct e100_serial *info)
@@ -1846,7 +2556,7 @@
 	if (!info->forced_eop) {
 		info->forced_eop = 1;
 		PROCSTAT(ser_stat[info->line].timeout_flush_cnt++);
-		DEBUG_LOG(info->line, "timeout EOP %i\n", info->line);
+		TIMERD(DEBUG_LOG(info->line, "timeout EOP %i\n", info->line));
 		FORCE_EOP(info);
 	}
 
@@ -1860,7 +2570,8 @@
 	struct etrax_recv_buffer *buffer;
 	unsigned int length;
 	unsigned long flags;
-
+	int max_flip_size;
+	
 	if (!info->first_recv_buffer)
 		return;
 
@@ -1873,12 +2584,46 @@
 	}
 
 	length = tty->flip.count;
+	/* Don't flip more than the ldisc has room for.
+	 * The return value from ldisc.receive_room(tty) - might not be up to
+	 * date, the previous flip of up to TTY_FLIPBUF_SIZE might be on the
+	 * processed and not accounted for yet.
+	 * Since we use DMA, 1 SERIAL_DESCR_BUF_SIZE could be on the way.
+	 * Lets buffer data here and let flow control take care of it.
+	 * Since we normally flip large chunks, the ldisc don't react
+	 * with throttle until too late if we flip to much.
+	 */
+	max_flip_size = tty->ldisc.receive_room(tty); 
+	if (max_flip_size < 0)
+		max_flip_size = 0;
+	if (max_flip_size <= (TTY_FLIPBUF_SIZE +         /* Maybe not accounted for */
+			      length + info->recv_cnt +  /* We have this queued */
+			      2*SERIAL_DESCR_BUF_SIZE +    /* This could be on the way */
+			      TTY_THRESHOLD_THROTTLE)) { /* Some slack */
+		/* check TTY_THROTTLED first so it indicates our state */
+		if (!test_and_set_bit(TTY_THROTTLED, &tty->flags)) {
+			DFLOW(DEBUG_LOG(info->line,"flush_to_flip throttles room %lu\n", max_flip_size));
+			rs_throttle(tty);
+		}
+#if 0		
+		else if (max_flip_size <= (TTY_FLIPBUF_SIZE +         /* Maybe not accounted for */
+					   length + info->recv_cnt +  /* We have this queued */
+					   SERIAL_DESCR_BUF_SIZE +    /* This could be on the way */
+					   TTY_THRESHOLD_THROTTLE)) { /* Some slack */
+			DFLOW(DEBUG_LOG(info->line,"flush_to_flip throttles again! %lu\n", max_flip_size));
+			rs_throttle(tty);
+		}
+#endif		
+	}
 
-	while ((buffer = info->first_recv_buffer) && length < TTY_FLIPBUF_SIZE) {
+	if (max_flip_size > TTY_FLIPBUF_SIZE)
+		max_flip_size = TTY_FLIPBUF_SIZE;
+
+	while ((buffer = info->first_recv_buffer) && length < max_flip_size) {
 		unsigned int count = buffer->length;
 
-		if (length + count > TTY_FLIPBUF_SIZE)
-			count = TTY_FLIPBUF_SIZE - length;
+		if (length + count > max_flip_size)
+			count = max_flip_size - length;
 
 		memcpy(tty->flip.char_buf_ptr + length, buffer->buffer, count);
 		memset(tty->flip.flag_buf_ptr + length, TTY_NORMAL, count);
@@ -1886,7 +2631,8 @@
 
 		length += count;
 		info->recv_cnt -= count;
-
+		DFLIP(DEBUG_LOG(info->line,"flip: %i\n", length));
+		
 		if (count == buffer->length) {
 			info->first_recv_buffer = buffer->next;
 			kfree(buffer);
@@ -1901,10 +2647,31 @@
 		info->last_recv_buffer = NULL;
 
 	tty->flip.count = length;
-
+	DFLIP(if (tty->ldisc.chars_in_buffer(tty) > 3500) {
+		DEBUG_LOG(info->line, "ldisc %lu\n",
+			  tty->ldisc.chars_in_buffer(tty));
+		DEBUG_LOG(info->line, "flip.count %lu\n",
+			  tty->flip.count);
+	      }
+	      );
 	restore_flags(flags);
 
 #if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,66)
+	DFLIP(
+	  if (1) {
+	  
+		  if (test_bit(TTY_DONT_FLIP, &tty->flags)) {
+			  DEBUG_LOG(info->line, "*** TTY_DONT_FLIP set flip.count %i ***\n", tty->flip.count);
+			  DEBUG_LOG(info->line, "*** recv_cnt %i\n", info->recv_cnt);
+		  } else {
+		  }
+		  DEBUG_LOG(info->line, "*** rxtot %i\n", info->icount.rx);
+		  DEBUG_LOG(info->line, "ldisc %lu\n", tty->ldisc.chars_in_buffer(tty));
+		  DEBUG_LOG(info->line, "room  %lu\n", tty->ldisc.receive_room(tty));
+	  }
+	  
+	);
+	  
 	/* this includes a check for low-latency */
 	tty_flip_buffer_push(tty);
 #else
@@ -1915,12 +2682,19 @@
 static _INLINE_ void
 check_flush_timeout(struct e100_serial *info)
 {
-	force_eop_if_needed(info);
-
+	/* Flip what we've got (if we can) */
 	flush_to_flip_buffer(info);
 
-	if (info->first_recv_buffer)
-		START_FLUSH_FAST_TIMER(info, "flip");
+	/* We might need to flip later, but not to fast
+	 * since the system is busy processing input... */
+	if (info->first_recv_buffer) 
+		START_FLUSH_FAST_TIMER_TIME(info, "flip", 2000);
+
+	/* Force eop last, since data might have come while we're processing
+	 * and if we started the slow timer above, we won't start a fast
+	 * below.
+	 */
+	force_eop_if_needed(info);	
 }
 
 #ifdef CONFIG_ETRAX_SERIAL_FAST_TIMER
@@ -1960,7 +2734,7 @@
 	
 	for (i = 0; i < NR_PORTS; i++) {
 		info = rs_table + i;
-		if (info->enabled && info->uses_dma) 
+		if (info->enabled && info->uses_dma_in) 
 			check_flush_timeout(info);
 	}
 } /* timeout_interrupt */
@@ -1988,7 +2762,7 @@
 	
 	for (i = 0; i < NR_PORTS; i++) {
 		info = rs_table + i;
-		if (info->uses_dma) 
+		if (info->uses_dma_in) 
 			check_flush_timeout(info);
 	}
 
@@ -2017,7 +2791,7 @@
 
 B= Break character (0x00) with framing error.
 E= Error byte with parity error received after B characters.
-F= "Faked" valid byte received immediatly after B characters.
+F= "Faked" valid byte received immediately after B characters.
 V= Valid byte
 
 1.
@@ -2054,7 +2828,7 @@
 
 To distinguish a V byte in 1. from an F byte in 2. we keep a timestamp
 of the last faulty char (B) and compares it with the current time:
-If the time elapsed time is less then 2*char_time_usec we will assume
+If the time elapsed time is less than 2*char_time_usec we will assume
 it's a faked F char and not a Valid char and set 
 info->errorcode = ERRCODE_SET_BREAK. 
 
@@ -2066,15 +2840,159 @@
 TODO: The break will be delayed until an F or V character is received.
 
 */
+	
+extern _INLINE_
+struct e100_serial * handle_ser_rx_interrupt_no_dma(struct e100_serial *info)
+{
+	unsigned long data_read;
+	struct tty_struct *tty = info->tty;	  
+
+	if (!tty) {
+		printk("!NO TTY!\n");
+		return info;
+	}
+	if (tty->flip.count >= TTY_FLIPBUF_SIZE - TTY_THRESHOLD_THROTTLE) {
+		/* check TTY_THROTTLED first so it indicates our state */
+		if (!test_and_set_bit(TTY_THROTTLED, &tty->flags)) {
+			DFLOW(DEBUG_LOG(info->line, "rs_throttle flip.count: %i\n", tty->flip.count));
+			rs_throttle(tty);
+		}
+	}
+	if (tty->flip.count >= TTY_FLIPBUF_SIZE) {
+		DEBUG_LOG(info->line, "force FLIP! %i\n", tty->flip.count);
+		tty->flip.tqueue.routine((void *) tty);
+		if (tty->flip.count >= TTY_FLIPBUF_SIZE) {
+			DEBUG_LOG(info->line, "FLIP FULL! %i\n", tty->flip.count);
+			return info;		/* if TTY_DONT_FLIP is set */
+		}
+	}
+	/* Read data and status at the same time */
+	data_read = *((unsigned long *)&info->port[REG_DATA_STATUS32]);
+more_data:
+	if (data_read & IO_MASK(R_SERIAL0_READ, xoff_detect) ) {
+		DFLOW(DEBUG_LOG(info->line, "XOFF detect\n", 0));
+	}
+	DINTR2(DEBUG_LOG(info->line, "ser_rx   %c\n", IO_EXTRACT(R_SERIAL0_READ, data_in, data_read)));
+
+	if (data_read & ( IO_MASK(R_SERIAL0_READ, framing_err) |
+			  IO_MASK(R_SERIAL0_READ, par_err) |
+			  IO_MASK(R_SERIAL0_READ, overrun) )) {
+		/* An error */
+		info->last_rx_active_usec = GET_JIFFIES_USEC();
+		info->last_rx_active = jiffies;
+		DINTR1(DEBUG_LOG(info->line, "ser_rx err stat_data %04X\n", data_read));
+		DLOG_INT_TRIG(
+		if (!log_int_trig1_pos) {
+			log_int_trig1_pos = log_int_pos;
+			log_int(rdpc(), 0, 0);
+		}
+		);
+		
+		
+		if ( ((data_read & IO_MASK(R_SERIAL0_READ, data_in)) == 0) &&
+		     (data_read & IO_MASK(R_SERIAL0_READ, framing_err)) ) {
+			/* Most likely a break, but we get interrupts over and
+			 * over again.
+			 */
 
-extern void _INLINE_ handle_ser_interrupt(struct e100_serial *info)
+			if (!info->break_detected_cnt) {
+				DEBUG_LOG(info->line, "#BRK start\n", 0);
+			}
+			if (data_read & IO_MASK(R_SERIAL0_READ, rxd)) {
+				/* The RX pin is high now, so the break
+				 * must be over, but....
+				 * we can't really know if we will get another
+				 * last byte ending the break or not. 
+				 * And we don't know if the byte (if any) will 
+				 * have an error or look valid.
+				 */
+				DEBUG_LOG(info->line, "# BL BRK\n", 0);
+				info->errorcode = ERRCODE_INSERT_BREAK;
+			}
+			info->break_detected_cnt++;
+		} else {
+			/* The error does not look like a break, but could be
+			 * the end of one
+			 */
+			if (info->break_detected_cnt) {
+				DEBUG_LOG(info->line, "EBRK %i\n", info->break_detected_cnt);
+				info->errorcode = ERRCODE_INSERT_BREAK;
+			} else {
+				if (info->errorcode == ERRCODE_INSERT_BREAK) {
+					info->icount.brk++;
+					*tty->flip.char_buf_ptr = 0;
+					*tty->flip.flag_buf_ptr = TTY_BREAK;
+					tty->flip.flag_buf_ptr++;
+					tty->flip.char_buf_ptr++;
+					tty->flip.count++;
+					info->icount.rx++;
+				}
+				*tty->flip.char_buf_ptr = IO_EXTRACT(R_SERIAL0_READ, data_in, data_read);
+
+				if (data_read & IO_MASK(R_SERIAL0_READ, par_err)) {
+					info->icount.parity++;
+					*tty->flip.flag_buf_ptr = TTY_PARITY;
+				} else if (data_read & IO_MASK(R_SERIAL0_READ, overrun)) {
+					info->icount.overrun++;
+					*tty->flip.flag_buf_ptr = TTY_OVERRUN;
+				} else if (data_read & IO_MASK(R_SERIAL0_READ, framing_err)) {
+					info->icount.frame++;
+					*tty->flip.flag_buf_ptr = TTY_FRAME;
+				}
+				info->errorcode = 0;
+			}
+			info->break_detected_cnt = 0;
+		}
+	} else if (data_read & IO_MASK(R_SERIAL0_READ, data_avail)) {
+		/* No error */
+		DLOG_INT_TRIG(
+		if (!log_int_trig1_pos) {
+			if (log_int_pos >= log_int_size) {
+				log_int_pos = 0;
+			}
+			log_int_trig0_pos = log_int_pos;
+			log_int(rdpc(), 0, 0);
+		}
+		);
+		*tty->flip.char_buf_ptr = IO_EXTRACT(R_SERIAL0_READ, data_in, data_read);
+		*tty->flip.flag_buf_ptr = 0;
+	} else {
+		DEBUG_LOG(info->line, "ser_rx int but no data_avail  %08lX\n", data_read);
+	}
+	
+	
+	tty->flip.flag_buf_ptr++;
+	tty->flip.char_buf_ptr++;
+	tty->flip.count++;
+	info->icount.rx++;
+	data_read = *((unsigned long *)&info->port[REG_DATA_STATUS32]);
+	if (data_read & IO_MASK(R_SERIAL0_READ, data_avail)) {
+		DEBUG_LOG(info->line, "ser_rx   %c in loop\n", IO_EXTRACT(R_SERIAL0_READ, data_in, data_read));
+		goto more_data;
+	}
+	
+	tty_flip_buffer_push(info->tty);
+	return info;
+}
+
+extern _INLINE_ 
+struct e100_serial* handle_ser_rx_interrupt(struct e100_serial *info)
 {
-	unsigned char rstat = info->port[REG_STATUS];
+	unsigned char rstat;
 
 #ifdef SERIAL_DEBUG_INTR
 	printk("Interrupt from serport %d\n", i);
 #endif
 /*	DEBUG_LOG(info->line, "ser_interrupt stat %03X\n", rstat | (i << 8)); */
+	if (!info->uses_dma_in) {
+		return handle_ser_rx_interrupt_no_dma(info);
+	}
+	/* DMA is used */
+	rstat = info->port[REG_STATUS];
+	if (rstat & IO_MASK(R_SERIAL0_STATUS, xoff_detect) ) {
+		DFLOW(DEBUG_LOG(info->line, "XOFF detect\n", 0));
+	}
+
 	if (rstat & SER_ERROR_MASK) {
 		unsigned char data;
 
@@ -2084,7 +3002,8 @@
 		 * data_in field
 		 */
 		data = info->port[REG_DATA];
-
+		DINTR1(DEBUG_LOG(info->line, "ser_rx!  %c\n", data));
+		DINTR1(DEBUG_LOG(info->line, "ser_rx err stat %02X\n", rstat));
 		if (!data && (rstat & SER_FRAMING_ERR_MASK)) {
 			/* Most likely a break, but we get interrupts over and
 			 * over again.
@@ -2113,15 +3032,22 @@
 				DEBUG_LOG(info->line, "EBRK %i\n", info->break_detected_cnt);
 				info->errorcode = ERRCODE_INSERT_BREAK;
 			} else {
-				if (info->errorcode == ERRCODE_INSERT_BREAK)
+				if (info->errorcode == ERRCODE_INSERT_BREAK) {
+					info->icount.brk++;
 					add_char_and_flag(info, '\0', TTY_BREAK);
+				}
 
-				if (rstat & SER_PAR_ERR_MASK)
+				if (rstat & SER_PAR_ERR_MASK) {
+					info->icount.parity++;
 					add_char_and_flag(info, data, TTY_PARITY);
-				else if (rstat & SER_OVERRUN_MASK)
+				} else if (rstat & SER_OVERRUN_MASK) {
+					info->icount.overrun++;
 					add_char_and_flag(info, data, TTY_OVERRUN);
-				else if (rstat & SER_FRAMING_ERR_MASK)
+				} else if (rstat & SER_FRAMING_ERR_MASK) {
+					info->icount.frame++;
 					add_char_and_flag(info, data, TTY_FRAME);
+				}
+				
 				info->errorcode = 0;
 			}
 			info->break_detected_cnt = 0;
@@ -2145,7 +3071,7 @@
 			if (elapsed_usec < 2*info->char_time_usec) {
 				DEBUG_LOG(info->line, "FBRK %i\n", info->line);
 				/* Report as BREAK (error) and let
-				 * receive_chars() handle it
+				 * receive_chars_dma() handle it
 				 */
 				info->errorcode = ERRCODE_SET_BREAK;
 			} else {
@@ -2155,38 +3081,195 @@
 		}
 
 #ifdef SERIAL_DEBUG_INTR
-		printk("** OK, disabling ser_interupts\n");
+		printk("** OK, disabling ser_interrupts\n");
 #endif
 		e100_disable_serial_data_irq(info);
-
+		DINTR2(DEBUG_LOG(info->line, "ser_rx OK %d\n", info->line));			
 		info->break_detected_cnt = 0;
 
 		PROCSTAT(ser_stat[info->line].ser_ints_ok_cnt++);
-		DEBUG_LOG(info->line, "ser_int OK %d\n", info->line);
 	}
-
 	/* Restarting the DMA never hurts */
 	*info->icmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, restart);
 	START_FLUSH_FAST_TIMER(info, "ser_int");
-} /* handle_ser_interrupt */
+	return info;
+} /* handle_ser_rx_interrupt */
+
+extern _INLINE_ void handle_ser_tx_interrupt(struct e100_serial *info)
+{
+	unsigned long flags;
+	
+	if (info->x_char) {
+		unsigned char rstat;
+		DFLOW(DEBUG_LOG(info->line, "tx_int: xchar 0x%02X\n", info->x_char));
+		save_flags(flags); cli();
+		rstat = info->port[REG_STATUS];
+		DFLOW(DEBUG_LOG(info->line, "stat %x\n", rstat));
+				
+		info->port[REG_TR_DATA] = info->x_char;
+		info->icount.tx++;
+		info->x_char = 0;
+		/* We must enable since it is disabled in ser_interrupt */
+		e100_enable_serial_tx_ready_irq(info);
+		restore_flags(flags);
+		return;
+	} 
+	if (info->uses_dma_out) {
+		unsigned char rstat;
+		int i;
+		/* We only use normal tx interrupt when sending x_char */
+		DFLOW(DEBUG_LOG(info->line, "tx_int: xchar sent\n", 0));
+		save_flags(flags); cli();
+		rstat = info->port[REG_STATUS];
+		DFLOW(DEBUG_LOG(info->line, "stat %x\n", rstat));
+		e100_disable_serial_tx_ready_irq(info);
+		if (info->tty->stopped)
+			rs_stop(info->tty);
+		/* Enable the DMA channel and tell it to continue */
+		e100_enable_txdma_channel(info);
+		/* Wait 12 cycles before doing the DMA command */
+		for(i = 6;  i > 0; i--)
+			nop();
+		
+		*info->ocmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, continue); 
+		restore_flags(flags);
+		return;
+	}
+	/* Normal char-by-char interrupt */
+	if (info->xmit.head == info->xmit.tail
+	    || info->tty->stopped
+	    || info->tty->hw_stopped) {
+		DFLOW(DEBUG_LOG(info->line, "tx_int: stopped %i\n", info->tty->stopped));
+		e100_disable_serial_tx_ready_irq(info);
+		info->tr_running = 0;
+		return;
+	}
+	DINTR2(DEBUG_LOG(info->line, "tx_int %c\n", info->xmit.buf[info->xmit.tail]));
+	/* Send a byte, rs485 timing is critical so turn of ints */
+	save_flags(flags); cli();
+	info->port[REG_TR_DATA] = info->xmit.buf[info->xmit.tail];
+	info->xmit.tail = (info->xmit.tail + 1) & (SERIAL_XMIT_SIZE-1);
+	info->icount.tx++;
+	if (info->xmit.head == info->xmit.tail) {
+#if defined(CONFIG_ETRAX_RS485) && defined(CONFIG_ETRAX_FAST_TIMER)
+		if (info->rs485.enabled) {
+			/* Set a short timer to toggle RTS */
+			start_one_shot_timer(&fast_timers_rs485[info->line],
+			                     rs485_toggle_rts_timer_function,
+			                     (unsigned long)info,
+			                     info->char_time_usec*2,
+			                     "RS-485");
+		}
+#endif /* RS485 */
+		info->last_tx_active_usec = GET_JIFFIES_USEC();
+		info->last_tx_active = jiffies;
+		e100_disable_serial_tx_ready_irq(info);
+		DFLOW(DEBUG_LOG(info->line, "tx_int: stop2\n", 0));
+	} else {
+		/* We must enable since it is disabled in ser_interrupt */
+		e100_enable_serial_tx_ready_irq(info);
+	}
+	restore_flags(flags);
+	
+	if (CIRC_CNT(info->xmit.head,
+		     info->xmit.tail,
+		     SERIAL_XMIT_SIZE) < WAKEUP_CHARS)
+		rs_sched_event(info, RS_EVENT_WRITE_WAKEUP);
 
+} /* handle_ser_tx_interrupt */
+
+/* result of time measurements:
+ * RX duration 54-60 us when doing something, otherwise 6-9 us
+ * ser_int duration: just sending: 8-15 us normally, up to 73 us
+ */
 static void 
 ser_interrupt(int irq, void *dev_id, struct pt_regs *regs)
 {
+	static volatile int tx_started = 0;
 	struct e100_serial *info;
 	int i;
-
+	unsigned long flags;
+	unsigned long irq_mask1_rd;
+	unsigned long data_mask = (1 << (8+2*0)); /* ser0 data_avail */
+	static volatile unsigned long reentered_ready_mask = 0;
+	
+	save_flags(flags); cli();
+	irq_mask1_rd = *R_IRQ_MASK1_RD;
+	/* First handle all rx interrupts with ints disabled */
+	info = rs_table;
+	irq_mask1_rd &= e100_ser_int_mask;
 	for (i = 0; i < NR_PORTS; i++) {
-		info = rs_table + i;
-
-		if (!info->enabled || !info->uses_dma) 
-			continue; 
-
-		/* Which line caused the irq? */
-		if (*R_IRQ_MASK1_RD & (1U << (8+2*info->line))) { 
-			handle_ser_interrupt(info);
+		/* Which line caused the data irq? */
+		if (irq_mask1_rd & data_mask) { 
+			handle_ser_rx_interrupt(info);
 		}
+		info += 1;
+		data_mask <<= 2;
 	}
+	/* Handle tx interrupts with interrupts enabled so we
+	 * can take care of new data interrupts while transmitting
+	 * We protect the tx part with the tx_started flag.
+	 * We disable the tr_ready interrupts we are about to handle and
+	 * unblock the serial interrupt so new serial interrupts may come.
+	 *
+	 * If we get a new interrupt:
+	 *  - it migth be due to synchronous serial ports.
+	 *  - serial irq will be blocked by general irq handler.
+	 *  - async data will be handled above (sync will be ignored).
+	 *  - tx_started flag will prevent us from trying to send again and
+	 *    we will exit fast - no need to unblock serial irq.
+	 *  - Next (sync) serial interrupt handler will be runned with
+	 *    disabled interrupt due to restore_flags() at end of function,
+	 *    so sync handler will not be preempted or reentered.
+	 */
+	if (!tx_started) {
+		unsigned long ready_mask;
+		unsigned long 
+		tx_started = 1;
+		/* Only the tr_ready interrupts left */
+		irq_mask1_rd &= (IO_MASK(R_IRQ_MASK1_RD, ser0_ready) |
+				 IO_MASK(R_IRQ_MASK1_RD, ser1_ready) |
+				 IO_MASK(R_IRQ_MASK1_RD, ser2_ready) |
+				 IO_MASK(R_IRQ_MASK1_RD, ser3_ready));
+		while (irq_mask1_rd) {
+			/* Disable those we are about to handle */
+			*R_IRQ_MASK1_CLR = irq_mask1_rd;
+			/* Unblock the serial interrupt */
+			*R_VECT_MASK_SET = IO_STATE(R_VECT_MASK_SET, serial, set);
+			
+			sti();
+			ready_mask = (1 << (8+1+2*0)); /* ser0 tr_ready */
+			info = rs_table;
+			for (i = 0; i < NR_PORTS; i++) {
+				/* Which line caused the ready irq? */
+				if (irq_mask1_rd & ready_mask) { 
+					handle_ser_tx_interrupt(info);
+				}
+				info += 1;
+				ready_mask <<= 2;
+			}
+			/* handle_ser_tx_interrupt enables tr_ready interrupts */
+			cli();
+			/* Handle reentered TX interrupt */
+			irq_mask1_rd = reentered_ready_mask;
+		}
+		cli();	
+		tx_started = 0;
+	} else {
+		unsigned long ready_mask;
+		ready_mask = irq_mask1_rd & (IO_MASK(R_IRQ_MASK1_RD, ser0_ready) |
+					     IO_MASK(R_IRQ_MASK1_RD, ser1_ready) |
+					     IO_MASK(R_IRQ_MASK1_RD, ser2_ready) |
+					     IO_MASK(R_IRQ_MASK1_RD, ser3_ready));
+		if (ready_mask) {
+			reentered_ready_mask |= ready_mask;
+			/* Disable those we are about to handle */
+			*R_IRQ_MASK1_CLR = ready_mask;
+			DFLOW(DEBUG_LOG(SERIAL_DEBUG_LINE, "ser_int reentered with TX %X\n", ready_mask));
+		}
+	}
+	
+	restore_flags(flags);
 } /* ser_interrupt */
 #endif
 
@@ -2288,25 +3371,40 @@
 	 * Reset the DMA channels and make sure their interrupts are cleared
 	 */
 
-	info->uses_dma = 1;
-	*info->icmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, reset);
-	*info->ocmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, reset);
-
-	/* Wait until reset cycle is complete */
-	while (IO_EXTRACT(R_DMA_CH6_CMD, cmd, *info->icmdadr) ==
-	       IO_STATE_VALUE(R_DMA_CH6_CMD, cmd, reset));
-
-	while (IO_EXTRACT(R_DMA_CH6_CMD, cmd, *info->ocmdadr) ==
-	       IO_STATE_VALUE(R_DMA_CH6_CMD, cmd, reset));
+	if (info->dma_in_enabled) {
+		info->uses_dma_in = 1;
+		e100_enable_rxdma_channel(info);
+		
+		*info->icmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, reset);
 
-	/* Make sure the irqs are cleared */
-	*info->iclrintradr =
-		IO_STATE(R_DMA_CH6_CLR_INTR, clr_descr, do) |
-		IO_STATE(R_DMA_CH6_CLR_INTR, clr_eop, do);
-	*info->oclrintradr =
-		IO_STATE(R_DMA_CH6_CLR_INTR, clr_descr, do) |
-		IO_STATE(R_DMA_CH6_CLR_INTR, clr_eop, do);
+		/* Wait until reset cycle is complete */
+		while (IO_EXTRACT(R_DMA_CH6_CMD, cmd, *info->icmdadr) ==
+		       IO_STATE_VALUE(R_DMA_CH6_CMD, cmd, reset));
+
+		/* Make sure the irqs are cleared */
+		*info->iclrintradr =
+			IO_STATE(R_DMA_CH6_CLR_INTR, clr_descr, do) |
+			IO_STATE(R_DMA_CH6_CLR_INTR, clr_eop, do);
+	} else {
+		e100_disable_rxdma_channel(info);
+	}
+	
+	if (info->dma_out_enabled) {
+		info->uses_dma_out = 1;
+		e100_enable_txdma_channel(info);
+		*info->ocmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, reset);
+		
+		while (IO_EXTRACT(R_DMA_CH6_CMD, cmd, *info->ocmdadr) ==
+		       IO_STATE_VALUE(R_DMA_CH6_CMD, cmd, reset));
 
+		/* Make sure the irqs are cleared */
+		*info->oclrintradr =
+			IO_STATE(R_DMA_CH6_CLR_INTR, clr_descr, do) |
+			IO_STATE(R_DMA_CH6_CLR_INTR, clr_eop, do);
+	} else {
+		e100_disable_txdma_channel(info);
+	}
+	
 	if (info->tty)
 		clear_bit(TTY_IO_ERROR, &info->tty->flags);
 
@@ -2321,8 +3419,10 @@
 	 * and set the speed and other flags of the serial port
 	 * this will start the rx/tx as well
 	 */
+	
 #ifdef SERIAL_HANDLE_EARLY_ERRORS
-	e100_enable_serial_data_irq(info);
+	if (info->uses_dma_in)
+		e100_enable_serial_data_irq(info);
 #endif	
 	change_speed(info);
 
@@ -2331,9 +3431,10 @@
 	(void)info->port[REG_DATA];
 
 	/* enable the interrupts */
+	if (info->uses_dma_out)
+		e100_enable_txdma_irq(info);
 
-	e100_enable_txdma_irq(info);
-	e100_enable_rxdma_irq(info);
+	e100_enable_rx_irq(info);
 
 	info->tr_running = 0; /* to be sure we don't lock up the transmitter */
 
@@ -2342,10 +3443,10 @@
 	start_receive(info);
 	
 	/* for safety, make sure the descriptors last result is 0 bytes written */
-	
 	info->tr_descr.sw_len = 0;
 	info->tr_descr.hw_len = 0;
 	info->tr_descr.status = 0;
+	
 
 	/* enable RTS/DTR last */
 
@@ -2374,20 +3475,28 @@
 
 #ifndef CONFIG_SVINTO_SIM	
 	/* shut down the transmitter and receiver */
-
+	DFLOW(DEBUG_LOG(info->line, "shutdown %i\n", info->line));
 	e100_disable_rx(info);
 	info->port[REG_TR_CTRL] = (info->tx_ctrl &= ~0x40);
 
-	e100_disable_rxdma_irq(info);
-	e100_disable_txdma_irq(info);
-
-	info->tr_running = 0;
-
-	/* reset both dma channels */
-
-	*info->icmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, reset);
-	*info->ocmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, reset);
-	info->uses_dma = 0;
+	/* disable interrupts, reset dma channels */
+	if (info->uses_dma_in) {
+		e100_disable_rxdma_irq(info);
+		*info->icmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, reset);
+		info->uses_dma_in = 0;
+	} else {
+		e100_disable_serial_data_irq(info);
+	}
+	
+	if (info->uses_dma_out) {
+		e100_disable_txdma_irq(info);
+		info->tr_running = 0;
+		*info->ocmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, reset);
+		info->uses_dma_out = 0;
+	} else {
+		e100_disable_serial_tx_ready_irq(info);
+		info->tr_running = 0;		
+	}
 
 #endif /* CONFIG_SVINTO_SIM */
 
@@ -2435,7 +3544,7 @@
 {
 	unsigned int cflag;
 	unsigned long xoff;
-
+	unsigned long flags;
 	/* first some safety checks */
 	
 	if (!info->tty || !info->tty->termios)
@@ -2444,17 +3553,80 @@
 		return;
 	
 	cflag = info->tty->termios->c_cflag;
-	
+
 	/* possibly, the tx/rx should be disabled first to do this safely */
 	
 	/* change baud-rate and write it to the hardware */
-	
-	info->baud = cflag_to_baud(cflag);
+	if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST) {
+		/* Special baudrate */
+		u32 mask = 0xFF << (info->line*8); /* Each port has 8 bits */
+		unsigned long alt_source = 
+				IO_STATE(R_ALT_SER_BAUDRATE, ser0_rec, normal) |
+				IO_STATE(R_ALT_SER_BAUDRATE, ser0_tr, normal);
+		/* R_ALT_SER_BAUDRATE selects the source */
+		DBAUD(printk("Custom baudrate: baud_base/divisor %lu/%i\n",
+		       (unsigned long)info->baud_base, info->custom_divisor));
+		if (info->baud_base == SERIAL_PRESCALE_BASE) {
+			/* 0, 2-65535 (0=65536) */
+			u16 divisor = info->custom_divisor;
+			/* R_SERIAL_PRESCALE (upper 16 bits of R_CLOCK_PRESCALE) */
+			/* baudrate is 3.125MHz/custom_divisor */
+			alt_source = 
+				IO_STATE(R_ALT_SER_BAUDRATE, ser0_rec, prescale) |
+				IO_STATE(R_ALT_SER_BAUDRATE, ser0_tr, prescale);
+			alt_source = 0x11;
+			DBAUD(printk("Writing SERIAL_PRESCALE: divisor %i\n", divisor));
+			*R_SERIAL_PRESCALE = divisor;
+			info->baud = SERIAL_PRESCALE_BASE/divisor;
+		}
+#ifdef CONFIG_ETRAX_EXTERN_PB6CLK_ENABLED
+		else if ((info->baud_base==CONFIG_ETRAX_EXTERN_PB6CLK_FREQ/8 &&
+			  info->custom_divisor == 1) ||
+			 (info->baud_base==CONFIG_ETRAX_EXTERN_PB6CLK_FREQ &&
+			  info->custom_divisor == 8)) {
+				/* ext_clk selected */
+				alt_source = 
+					IO_STATE(R_ALT_SER_BAUDRATE, ser0_rec, extern) |
+					IO_STATE(R_ALT_SER_BAUDRATE, ser0_tr, extern);
+				DBAUD(printk("using external baudrate: %lu\n", CONFIG_ETRAX_EXTERN_PB6CLK_FREQ/8));
+				info->baud = CONFIG_ETRAX_EXTERN_PB6CLK_FREQ/8;
+			}
+		}
+#endif
+		else
+		{
+			/* Bad baudbase, we don't support using timer0
+			 * for baudrate.
+			 */
+			printk(KERN_WARNING "Bad baud_base/custom_divisor: %lu/%i\n",
+			       (unsigned long)info->baud_base, info->custom_divisor);
+		}
+		r_alt_ser_baudrate_shadow &= ~mask;
+		r_alt_ser_baudrate_shadow |= (alt_source << (info->line*8));
+		*R_ALT_SER_BAUDRATE = r_alt_ser_baudrate_shadow;
+	} else {
+		/* Normal baudrate */
+		/* Make sure we use normal baudrate */
+		u32 mask = 0xFF << (info->line*8); /* Each port has 8 bits */
+		unsigned long alt_source = 
+			IO_STATE(R_ALT_SER_BAUDRATE, ser0_rec, normal) |
+			IO_STATE(R_ALT_SER_BAUDRATE, ser0_tr, normal);
+		r_alt_ser_baudrate_shadow &= ~mask;
+		r_alt_ser_baudrate_shadow |= (alt_source << (info->line*8));
+#ifndef CONFIG_SVINTO_SIM
+		*R_ALT_SER_BAUDRATE = r_alt_ser_baudrate_shadow;
+#endif /* CONFIG_SVINTO_SIM */ 
+
+		info->baud = cflag_to_baud(cflag);
+#ifndef CONFIG_SVINTO_SIM
+		info->port[REG_BAUD] = cflag_to_etrax_baud(cflag);
+#endif /* CONFIG_SVINTO_SIM */
+	}
 	
 #ifndef CONFIG_SVINTO_SIM
-	info->port[REG_BAUD] = cflag_to_etrax_baud(cflag);
 	/* start with default settings and then fill in changes */
-
+	save_flags(flags);
+	cli();
 	/* 8 bit, no/even parity */
 	info->rx_ctrl &= ~(IO_MASK(R_SERIAL0_REC_CTRL, rec_bitnr) |
 			   IO_MASK(R_SERIAL0_REC_CTRL, rec_par_en) |
@@ -2484,14 +3656,26 @@
 		info->rx_ctrl |= IO_STATE(R_SERIAL0_REC_CTRL, rec_par_en, enable);
 	}
 	
-	if (cflag & PARODD) {
-		/* set odd parity */
-		info->tx_ctrl |= IO_STATE(R_SERIAL0_TR_CTRL, tr_par, odd);
-		info->rx_ctrl |= IO_STATE(R_SERIAL0_REC_CTRL, rec_par, odd);
+	if (cflag & CMSPAR) {
+		/* enable stick parity */
+		info->tx_ctrl |= IO_STATE(R_SERIAL0_TR_CTRL, tr_stick_par, stick);
+		info->rx_ctrl |= IO_STATE(R_SERIAL0_REC_CTRL, rec_stick_par, stick);
+		if (!(cflag & PARODD)) {
+			/* set mark parity */
+			info->tx_ctrl |= IO_STATE(R_SERIAL0_TR_CTRL, tr_par, odd);
+			info->rx_ctrl |= IO_STATE(R_SERIAL0_REC_CTRL, rec_par, odd);
+		}
+	} else {
+		if (cflag & PARODD) {
+			/* set odd parity */
+			info->tx_ctrl |= IO_STATE(R_SERIAL0_TR_CTRL, tr_par, odd);
+			info->rx_ctrl |= IO_STATE(R_SERIAL0_REC_CTRL, rec_par, odd);
+		}
 	}
 	
 	if (cflag & CRTSCTS) {
 		/* enable automatic CTS handling */
+		DFLOW(DEBUG_LOG(info->line, "FLOW auto_cts enabled\n", 0));
 		info->tx_ctrl |= IO_STATE(R_SERIAL0_TR_CTRL, auto_cts, active);
 	}
 	
@@ -2507,10 +3691,12 @@
 	xoff = IO_FIELD(R_SERIAL0_XOFF, xoff_char, STOP_CHAR(info->tty));
 	xoff |= IO_STATE(R_SERIAL0_XOFF, tx_stop, enable);
 	if (info->tty->termios->c_iflag & IXON ) {
+		DFLOW(DEBUG_LOG(info->line, "FLOW XOFF enabled 0x%02X\n", STOP_CHAR(info->tty)));
 		xoff |= IO_STATE(R_SERIAL0_XOFF, auto_xoff, enable);
 	}
 	
 	*((unsigned long *)&info->port[REG_XOFF]) = xoff;
+	restore_flags(flags);
 #endif /* !CONFIG_SVINTO_SIM */
 
 	update_char_time(info);
@@ -2543,8 +3729,8 @@
 	restore_flags(flags);
 }
 
-extern inline int 
-raw_write(struct tty_struct * tty, int from_user,
+extern _INLINE_ int 
+rs_raw_write(struct tty_struct * tty, int from_user,
 	  const unsigned char *buf, int count)
 {
 	int	c, ret = 0;
@@ -2558,7 +3744,7 @@
 	
 #ifdef SERIAL_DEBUG_DATA
 	if (info->line == SERIAL_DEBUG_LINE)
-		printk("raw_write (%d), status %d\n", 
+		printk("rs_raw_write (%d), status %d\n", 
 		       count, info->port[REG_STATUS]);
 #endif
 
@@ -2568,6 +3754,9 @@
 	return count;
 #endif
 	save_flags(flags);
+
+	DFLOW(DEBUG_LOG(info->line, "write count %i ", count));
+	DFLOW(DEBUG_LOG(info->line, "ldisc %i\n", tty->ldisc.chars_in_buffer(tty)));
 	
 	/* the cli/restore_flags pairs below are needed because the
 	 * DMA interrupt handler moves the info->xmit values. the memcpy
@@ -2635,6 +3824,7 @@
 	 * this does not need IRQ protection since if tr_running == 0
 	 * the IRQ's are not running anyway for this port.
 	 */
+	DFLOW(DEBUG_LOG(info->line, "write ret %i\n", ret));
 	
 	if (info->xmit.head != info->xmit.tail &&
 	    !tty->stopped &&
@@ -2644,7 +3834,7 @@
 	}
  	
 	return ret;
-} /* raw_write() */
+} /* rs_raw_write() */
 
 static int 
 rs_write(struct tty_struct * tty, int from_user,
@@ -2666,7 +3856,7 @@
 		e100_rts(info, info->rs485.rts_on_send);
 #if defined(CONFIG_ETRAX_RS485_DISABLE_RECEIVER)
 		e100_disable_rx(info);
-		e100_disable_rxdma_irq(info);
+		e100_disable_rx_irq(info);
 #endif
 
 		if (info->rs485.delay_rts_before_send > 0) {
@@ -2676,7 +3866,7 @@
 	}
 #endif /* CONFIG_ETRAX_RS485 */
 
-	count = raw_write(tty, from_user, buf, count);
+	count = rs_raw_write(tty, from_user, buf, count);
 
 #if defined(CONFIG_ETRAX_RS485)
 	if (info->rs485.enabled)
@@ -2704,7 +3894,7 @@
 	
 #if defined(CONFIG_ETRAX_RS485_DISABLE_RECEIVER)
 		e100_enable_rx(info);
-		e100_enable_rxdma_irq(info);
+		e100_enable_rx_irq(info);
 #endif
 	}
 #endif /* CONFIG_ETRAX_RS485 */
@@ -2760,20 +3950,33 @@
  * This function is used to send a high-priority XON/XOFF character to
  * the device
  *
- * Since we don't bother to check for info->x_char in transmit_chars yet,
- * we don't really implement this function yet.
+ * Since we use DMA we don't check for info->x_char in transmit_chars_dma(),
+ * but we do it in handle_ser_tx_interrupt().
+ * We disable DMA channel and enable tx ready interrupt and write the
+ * character when possible.
  */
 static void rs_send_xchar(struct tty_struct *tty, char ch)
 {
 	struct e100_serial *info = (struct e100_serial *)tty->driver_data;
+	unsigned long flags;
+	save_flags(flags); cli();
+	if (info->uses_dma_out) {
+		/* Put the DMA on hold and disable the channel */
+		*info->ocmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, hold); 
+		while (IO_EXTRACT(R_DMA_CH6_CMD, cmd, *info->ocmdadr) !=
+		       IO_STATE_VALUE(R_DMA_CH6_CMD, cmd, hold));
+		e100_disable_txdma_channel(info);
+	}
+	
+	/* Must make sure transmitter is not stopped before we can transmit */
+	if (tty->stopped)
+		rs_start(tty);
 
-	printk("serial.c:rs_send_xchar not implemented!\n");
-
+	/* Enable manual transmit interrupt and send from there */
+	DFLOW(DEBUG_LOG(info->line, "rs_send_xchar 0x%02X\n", ch));
 	info->x_char = ch;
-	if (ch) {
-		/* Make sure transmit interrupts are on */
-		/* TODO. */
-	}
+	e100_enable_serial_tx_ready_irq(info);
+	restore_flags(flags);
 }
 
 /*
@@ -2788,50 +3991,49 @@
 rs_throttle(struct tty_struct * tty)
 {
 	struct e100_serial *info = (struct e100_serial *)tty->driver_data;
-	unsigned long flags;
 #ifdef SERIAL_DEBUG_THROTTLE
 	char	buf[64];
 
-	printk("throttle %s: %d....\n", _tty_name(tty, buf),
-	       tty->ldisc.chars_in_buffer(tty));
+	printk("throttle %s: %lu....\n", tty_name(tty, buf),
+	       (unsigned long)tty->ldisc.chars_in_buffer(tty));
 #endif
-
+	DFLOW(DEBUG_LOG(info->line,"rs_throttle %lu\n", tty->ldisc.chars_in_buffer(tty)));
+	
+	/* Do RTS before XOFF since XOFF might take some time */
+	if (tty->termios->c_cflag & CRTSCTS) {
+		/* Turn off RTS line */
+		e100_rts(info, 0);
+	}
 	if (I_IXOFF(tty))
-		info->x_char = STOP_CHAR(tty);
+		rs_send_xchar(tty, STOP_CHAR(tty));
 
-	/* Turn off RTS line (do this atomic) should here be an else ?? */
-
-	save_flags(flags); 
-	cli();
-	e100_rts(info, 0);
-	restore_flags(flags);
 }
 
 static void 
 rs_unthrottle(struct tty_struct * tty)
 {
 	struct e100_serial *info = (struct e100_serial *)tty->driver_data;
-	unsigned long flags;
 #ifdef SERIAL_DEBUG_THROTTLE
 	char	buf[64];
 
-	printk("unthrottle %s: %d....\n", _tty_name(tty, buf),
-	       tty->ldisc.chars_in_buffer(tty));
+	printk("unthrottle %s: %lu....\n", tty_name(tty, buf),
+	       (unsigned long)tty->ldisc.chars_in_buffer(tty));
 #endif
+	DFLOW(DEBUG_LOG(info->line,"rs_unthrottle ldisc %d\n", tty->ldisc.chars_in_buffer(tty)));
+	DFLOW(DEBUG_LOG(info->line,"rs_unthrottle flip.count: %i\n", tty->flip.count));	
+	/* Do RTS before XOFF since XOFF might take some time */
+	if (tty->termios->c_cflag & CRTSCTS) {
+		/* Assert RTS line  */
+		e100_rts(info, 1);
+	}
 
 	if (I_IXOFF(tty)) {
 		if (info->x_char)
 			info->x_char = 0;
 		else
-			info->x_char = START_CHAR(tty);
+			rs_send_xchar(tty, START_CHAR(tty));
 	}
 
-	/* Assert RTS line (do this atomic) */
-
-	save_flags(flags); 
-	cli();
-	e100_rts(info, 1);
-	restore_flags(flags);
 }
 
 /*
@@ -2859,8 +4061,10 @@
 	tmp.port = (int)info->port;
 	tmp.irq = info->irq;
 	tmp.flags = info->flags;
+	tmp.baud_base = info->baud_base;
 	tmp.close_delay = info->close_delay;
 	tmp.closing_wait = info->closing_wait;
+	tmp.custom_divisor = info->custom_divisor;
 	if (copy_to_user(retinfo, &tmp, sizeof(*retinfo)))
 		return -EFAULT;
 	return 0;
@@ -2897,9 +4101,11 @@
 	 * OK, past this point, all the error checking has been done.
 	 * At this point, we start making changes.....
 	 */
-	
+
+	info->baud_base = new_serial.baud_base;	
 	info->flags = ((info->flags & ~ASYNC_FLAGS) |
 		       (new_serial.flags & ASYNC_FLAGS));
+	info->custom_divisor = new_serial.custom_divisor;
 	info->type = new_serial.type;
 	info->close_delay = new_serial.close_delay;
 	info->closing_wait = new_serial.closing_wait;
@@ -3098,18 +4304,20 @@
 
 	info->tx_ctrl &= 0x3F; /* Clear bit 7 (txd) and 6 (tr_enable) */
 	info->port[REG_TR_CTRL] = info->tx_ctrl;
-
+	restore_flags(flags);
 	/* wait for "duration" jiffies */
 
 	schedule();
 
+	cli();
 	info->tx_ctrl |= (0x80 | 0x40); /* Set bit 7 (txd) and 6 (tr_enable) */
 	info->port[REG_TR_CTRL] = info->tx_ctrl;
 
 	/* the DMA gets awfully confused if we toggle the tranceiver like this 
 	 * so we need to reset it 
 	 */
-	*info->ocmdadr = 4;
+	if (info->uses_dma_out)
+		*info->ocmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, reset);
 
 	restore_flags(flags);
 }
@@ -3252,6 +4460,7 @@
 
 	change_speed(info);
 
+	/* Handle turning off CRTSCTS */
 	if ((old_termios->c_cflag & CRTSCTS) &&
 	    !(tty->termios->c_cflag & CRTSCTS)) {
 		tty->hw_stopped = 0;
@@ -3301,12 +4510,13 @@
 		 * one, we've got real problems, since it means the
 		 * serial port won't be shutdown.
 		 */
-		printk("rs_close: bad serial port count; tty->count is 1, "
+		printk(KERN_CRIT
+		       "rs_close: bad serial port count; tty->count is 1, "
 		       "info->count is %d\n", info->count);
 		info->count = 1;
 	}
 	if (--info->count < 0) {
-		printk("rs_close: bad serial port count for ttyS%d: %d\n",
+		printk(KERN_CRIT "rs_close: bad serial port count for ttyS%d: %d\n",
 		       info->line, info->count);
 		info->count = 0;
 	}
@@ -3340,7 +4550,7 @@
 
 #ifndef CONFIG_SVINTO_SIM
 	e100_disable_rx(info);
-	e100_disable_rxdma_irq(info);
+	e100_disable_rx_irq(info);
 
 	if (info->flags & ASYNC_INITIALIZED) {
 		/*
@@ -3676,6 +4886,12 @@
 #ifdef SERIAL_DEBUG_OPEN
 	printk("rs_open ttyS%d successful...\n", info->line);
 #endif
+	DLOG_INT_TRIG( log_int_pos = 0);
+
+	DFLIP(	if (info->line == SERIAL_DEBUG_LINE) {
+			info->icount.rx = 0;
+		} );
+	
 	return 0;
 }
 
@@ -3683,11 +4899,12 @@
  * /proc fs routines....
  */
 
-extern inline int line_info(char *buf, struct e100_serial *info)
+extern _INLINE_ int line_info(char *buf, struct e100_serial *info)
 {
 	char	stat_buf[30];
 	int	ret;
-
+	unsigned long tmp;
+	
 	ret = sprintf(buf, "%d: uart:E100 port:%lX irq:%d",
 		      info->line, (unsigned long)info->port, info->irq);
 
@@ -3716,11 +4933,39 @@
 	ret += sprintf(buf+ret, " tx:%lu rx:%lu",
 		       (unsigned long)info->icount.tx,
 		       (unsigned long)info->icount.rx);
-
+	tmp = CIRC_CNT(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE);
+	if (tmp) {
+		ret += sprintf(buf+ret, " tx_pend:%lu/%lu",
+			       (unsigned long)tmp,
+			       (unsigned long)SERIAL_XMIT_SIZE);
+	}
+	
 	ret += sprintf(buf+ret, " rx_pend:%lu/%lu",
 		       (unsigned long)info->recv_cnt,
 		       (unsigned long)info->max_recv_cnt);
 
+#if 1
+	if (info->tty) {
+		
+		if (info->tty->stopped)
+			ret += sprintf(buf+ret, " stopped:%i",
+				       (int)info->tty->stopped);
+		if (info->tty->hw_stopped)
+			ret += sprintf(buf+ret, " hw_stopped:%i",
+				       (int)info->tty->hw_stopped);
+	}
+	
+	{
+		unsigned char rstat = info->port[REG_STATUS];
+		if (rstat & IO_MASK(R_SERIAL0_STATUS, xoff_detect) )
+			ret += sprintf(buf+ret, " xoff_detect:1");
+	}
+	
+#endif
+	
+
+
+	
 	if (info->icount.frame)
 		ret += sprintf(buf+ret, " fe:%lu",
 			       (unsigned long)info->icount.frame);
@@ -3744,12 +4989,14 @@
 	return ret;
 }
 
+
+
 int rs_read_proc(char *page, char **start, off_t off, int count,
 		 int *eof, void *data)
 {
 	int i, len = 0, l;
 	off_t	begin = 0;
-
+	
 	len += sprintf(page, "serinfo:1.0 driver:%s\n",
 		       serial_version);
 	for (i = 0; i < NR_PORTS && len < 4000; i++) {
@@ -3764,6 +5011,22 @@
 			len = 0;
 		}
 	}
+#ifdef DEBUG_LOG_INCLUDED
+	for (i = 0; i < debug_log_pos; i++) {
+		len += sprintf(page + len, "%-4i %lu.%lu ", i, debug_log[i].time, timer_data_to_ns(debug_log[i].timer_data));
+		len += sprintf(page + len, debug_log[i].string, debug_log[i].value);
+		if (len+begin > off+count)
+			goto done;
+		if (len+begin < off) {
+			begin += len;
+			len = 0;
+		}
+	}
+	len += sprintf(page + len, "debug_log %i/%i  %li bytes\n",
+		       i, DEBUG_LOG_SIZE, begin+len);
+	debug_log_pos = 0;
+#endif
+
 	*eof = 1;
 done:
 	if (off >= len+begin)
@@ -3777,7 +5040,8 @@
 static void 
 show_serial_version(void)
 {
-	printk("ETRAX 100LX serial-driver %s, (c) 2000-2002 Axis Communications AB\r\n",
+	printk(KERN_INFO
+	       "ETRAX 100LX serial-driver %s, (c) 2000-2003 Axis Communications AB\r\n",
 	       &serial_version[11]); /* "$Revision: x.yy" */
 }
 
@@ -3794,7 +5058,7 @@
 	init_bh(SERIAL_BH, do_serial_bh);
 
 	/* Setup the timed flush handler system */
-
+	
 #if !defined(CONFIG_ETRAX_SERIAL_FAST_TIMER) && !defined(CONFIG_ETRAX_SERIAL_FLUSH_DMA_FAST)
 	init_timer(&flush_timer);
 	flush_timer.function = timed_flush_handler;
@@ -3868,12 +5132,15 @@
 	/* do some initializing for the separate ports */
   
 	for (i = 0, info = rs_table; i < NR_PORTS; i++,info++) {
-		info->uses_dma = 0;   
+		info->uses_dma_in = 0;
+		info->uses_dma_out = 0;
 		info->line = i;
 		info->tty = 0;
 		info->type = PORT_ETRAX;
 		info->tr_running = 0;
 		info->forced_eop = 0;
+		info->baud_base = DEF_BAUD_BASE;
+		info->custom_divisor = 0;		
 		info->flags = 0;
 		info->close_delay = 5*HZ/10;
 		info->closing_wait = 30*HZ;
@@ -3920,41 +5187,58 @@
 #ifndef CONFIG_SVINTO_SIM
 	/* Not needed in simulator.  May only complicate stuff. */
 	/* hook the irq's for DMA channel 6 and 7, serial output and input, and some more... */
+
+	if (request_irq(SERIAL_IRQ_NBR, ser_interrupt, SA_SHIRQ | SA_INTERRUPT, "serial ", NULL))
+		panic("irq8");
+	
 #ifdef CONFIG_ETRAX_SERIAL_PORT0
+#ifdef CONFIG_ETRAX_SERIAL_PORT0_DMA6_OUT
 	if (request_irq(SER0_DMA_TX_IRQ_NBR, tr_interrupt, SA_INTERRUPT, "serial 0 dma tr", NULL))
 		panic("irq22");
+#endif
+#ifdef CONFIG_ETRAX_SERIAL_PORT0_DMA7_IN
 	if (request_irq(SER0_DMA_RX_IRQ_NBR, rec_interrupt, SA_INTERRUPT, "serial 0 dma rec", NULL))
 		panic("irq23");
 #endif
-#ifdef SERIAL_HANDLE_EARLY_ERRORS
-	if (request_irq(SERIAL_IRQ_NBR, ser_interrupt, SA_SHIRQ | SA_INTERRUPT, "serial ", NULL))
-		panic("irq8");
 #endif
+
 #ifdef CONFIG_ETRAX_SERIAL_PORT1
+#ifdef CONFIG_ETRAX_SERIAL_PORT1_DMA8_OUT
 	if (request_irq(SER1_DMA_TX_IRQ_NBR, tr_interrupt, SA_INTERRUPT, "serial 1 dma tr", NULL))
 		panic("irq24");
+#endif
+#ifdef CONFIG_ETRAX_SERIAL_PORT1_DMA9_IN
 	if (request_irq(SER1_DMA_RX_IRQ_NBR, rec_interrupt, SA_INTERRUPT, "serial 1 dma rec", NULL))
 		panic("irq25");
 #endif
+#endif
 #ifdef CONFIG_ETRAX_SERIAL_PORT2
 	/* DMA Shared with par0 (and SCSI0 and ATA) */
-	if (request_irq(SER2_DMA_TX_IRQ_NBR, tr_interrupt, SA_SHIRQ, "serial 2 dma tr", NULL))
+#ifdef CONFIG_ETRAX_SERIAL_PORT2_DMA2_OUT
+	if (request_irq(SER2_DMA_TX_IRQ_NBR, tr_interrupt, SA_SHIRQ | SA_INTERRUPT, "serial 2 dma tr", NULL))
 		panic("irq18");
-	if (request_irq(SER2_DMA_RX_IRQ_NBR, rec_interrupt, SA_SHIRQ, "serial 2 dma rec", NULL))
+#endif
+#ifdef CONFIG_ETRAX_SERIAL_PORT2_DMA3_IN
+	if (request_irq(SER2_DMA_RX_IRQ_NBR, rec_interrupt, SA_SHIRQ | SA_INTERRUPT, "serial 2 dma rec", NULL))
 		panic("irq19");
 #endif
+#endif
 #ifdef CONFIG_ETRAX_SERIAL_PORT3
 	/* DMA Shared with par1 (and SCSI1 and Extern DMA 0) */
-	if (request_irq(SER3_DMA_TX_IRQ_NBR, tr_interrupt, SA_SHIRQ, "serial 3 dma tr", NULL))
+#ifdef CONFIG_ETRAX_SERIAL_PORT3_DMA4_OUT
+	if (request_irq(SER3_DMA_TX_IRQ_NBR, tr_interrupt, SA_SHIRQ | SA_INTERRUPT, "serial 3 dma tr", NULL))
 		panic("irq20");
-	if (request_irq(SER3_DMA_RX_IRQ_NBR, rec_interrupt, SA_SHIRQ, "serial 3 dma rec", NULL))
+#endif
+#ifdef CONFIG_ETRAX_SERIAL_PORT3_DMA5_IN
+	if (request_irq(SER3_DMA_RX_IRQ_NBR, rec_interrupt, SA_SHIRQ | SA_INTERRUPT, "serial 3 dma rec", NULL))
 		panic("irq21");
 #endif
+#endif
 
 #ifdef CONFIG_ETRAX_SERIAL_FLUSH_DMA_FAST
 	if (request_irq(TIMER1_IRQ_NBR, timeout_interrupt, SA_SHIRQ | SA_INTERRUPT,
 		       "fast serial dma timeout", NULL)) {
-		printk("err: timer1 irq\n");
+		printk(KERN_CRIT "err: timer1 irq\n");
 	}
 #endif
 #endif /* CONFIG_SVINTO_SIM */
diff -urN linux-2.4.22-pre3/arch/cris/drivers/serial.h linux-2.4.22-pre4/arch/cris/drivers/serial.h
--- linux-2.4.22-pre3/arch/cris/drivers/serial.h	2002-08-02 17:39:42.000000000 -0700
+++ linux-2.4.22-pre4/arch/cris/drivers/serial.h	2003-07-09 16:56:27.000000000 -0700
@@ -23,7 +23,7 @@
  * For definitions of the flags field, see tty.h
  */
 
-#define SERIAL_RECV_DESCRIPTORS 8
+#define SERIAL_RECV_DESCRIPTORS 16
 
 struct etrax_recv_buffer {
 	struct etrax_recv_buffer *next;
@@ -44,30 +44,29 @@
 	volatile u32		*ofirstadr;   /* adr to R_DMA_CHx_FIRST */
 	volatile u8		*ocmdadr;     /* adr to R_DMA_CHx_CMD */
 	const volatile u8	*ostatusadr;  /* adr to R_DMA_CHx_STATUS */
-	volatile u32		*ohwswadr;    /* adr to R_DMA_CHx_HWSW */
-	volatile u32		*odescradr;   /* adr to R_DMA_CHx_DESCR */
 
 	/* Input registers */
 	volatile u8		*iclrintradr; /* adr to R_DMA_CHx_CLR_INTR */
 	volatile u32		*ifirstadr;   /* adr to R_DMA_CHx_FIRST */
 	volatile u8		*icmdadr;     /* adr to R_DMA_CHx_CMD */
-	const volatile u8	*istatusadr;  /* adr to R_DMA_CHx_STATUS */
-	volatile u32		*ihwswadr;    /* adr to R_DMA_CHx_HWSW */
 	volatile u32		*idescradr;   /* adr to R_DMA_CHx_DESCR */
 
 	int			flags;	/* defined in tty.h */
 
-	u8			rx_ctrl; /* shadow for R_SERIALx_REC_CTRL */
-	u8			tx_ctrl; /* shadow for R_SERIALx_TR_CTRL */
-	u8			iseteop; /* bit number for R_SET_EOP for the input dma */
-
-	int			enabled; /* Set to 1 if the port is enabled in HW config */
-  
+	u8		rx_ctrl; /* shadow for R_SERIALx_REC_CTRL */
+	u8		tx_ctrl; /* shadow for R_SERIALx_TR_CTRL */
+	u8		iseteop; /* bit number for R_SET_EOP for the input dma */
+	u8		enabled; /* Set to 1 if the port is enabled in HW config */
+	
+	u8		dma_out_enabled:1; /* Set to 1 if DMA should be used */
+	u8		dma_in_enabled:1;  /* Set to 1 if DMA should be used */
+	
 	/* end of fields defined in rs_table[] in .c-file */
-
-	int			uses_dma; /* Set to 1 if DMA should be used */
-	unsigned char           forced_eop; /* a fifo eop has been forced */
-
+	u8		uses_dma_in;  /* Set to 1 if DMA is used */
+	u8		uses_dma_out; /* Set to 1 if DMA is used */
+	u8		forced_eop;   /* a fifo eop has been forced */
+	int			baud_base;     /* For special baudrates */
+	int			custom_divisor; /* For special baudrates */
 	struct etrax_dma_descr	tr_descr;
 	struct etrax_dma_descr	rec_descr[SERIAL_RECV_DESCRIPTORS];
 	int			cur_rec_descr;
@@ -108,6 +107,7 @@
 #endif  
 
 	unsigned long		char_time_usec;       /* The time for 1 char, in usecs */
+	unsigned long		flush_time_usec;      /* How often we should flush */
 	unsigned long		last_tx_active_usec;  /* Last tx usec in the jiffies */
 	unsigned long		last_tx_active;       /* Last tx time in jiffies */
 	unsigned long		last_rx_active_usec;  /* Last rx usec in the jiffies */
diff -urN linux-2.4.22-pre3/arch/cris/drivers/sync_serial.c linux-2.4.22-pre4/arch/cris/drivers/sync_serial.c
--- linux-2.4.22-pre3/arch/cris/drivers/sync_serial.c	2003-06-13 07:51:29.000000000 -0700
+++ linux-2.4.22-pre4/arch/cris/drivers/sync_serial.c	2003-07-09 16:56:27.000000000 -0700
@@ -1,7 +1,7 @@
 /*  
  * Simple synchronous serial port driver for ETRAX 100LX.
  *
- * Synchronous serial ports are used for continous streamed data like audio.
+ * Synchronous serial ports are used for continuous streamed data like audio.
  * The default setting for this driver is compatible with the STA 013 MP3
  * decoder. The driver can easily be tuned to fit other audio encoder/decoders
  * and SPI
@@ -29,7 +29,7 @@
 #include <asm/system.h>
 #include <asm/sync_serial.h>
 
-/* The receiver is a bit tricky beacuse of the continous stream of data. */
+/* The receiver is a bit tricky beacuse of the continuous stream of data.*/
 /*                                                                       */
 /* Two DMA descriptors are linked together. Each DMA descriptor is       */
 /* responsible for one half of a common buffer.                          */
@@ -256,7 +256,7 @@
 	initialize_port(1);
 	if (ports[0].use_dma) /* Port 0 uses dma, we must manual allocate IRQ */
 	{
-		if (request_irq(8, manual_interrupt, SA_SHIRQ | SA_INTERRUPT, "synchronous serial manual irq", &ports[1]))
+		if (request_irq(8, manual_interrupt, SA_SHIRQ | SA_INTERRUPT, "synchronous serial manual irq", &ports[1]))
 			panic("Can't allocate sync serial manual irq");
 	}
 #endif
diff -urN linux-2.4.22-pre3/arch/cris/drivers/usb-host.c linux-2.4.22-pre4/arch/cris/drivers/usb-host.c
--- linux-2.4.22-pre3/arch/cris/drivers/usb-host.c	2002-11-28 15:53:09.000000000 -0800
+++ linux-2.4.22-pre4/arch/cris/drivers/usb-host.c	2003-07-09 16:56:27.000000000 -0700
@@ -1,10 +1,7 @@
 /*
  * usb-host.c: ETRAX 100LX USB Host Controller Driver (HCD)
  *
- * Copyright (c) 2001 Axis Communications AB.
- *
- * $Id: usb-host.c,v 1.13 2001/11/13 12:06:17 pkj Exp $
- *
+ * Copyright (c) 2002, 2003 Axis Communications AB.
  */
 
 #include <linux/config.h>
@@ -19,6 +16,7 @@
 #include <linux/init.h>
 #include <linux/version.h>
 #include <linux/list.h>
+#include <linux/spinlock.h>
 
 #include <asm/uaccess.h>
 #include <asm/io.h>
@@ -28,24 +26,37 @@
 #include <asm/svinto.h>
 
 #include <linux/usb.h>
+/* Ugly include because we don't live with the other host drivers. */
+#include <../drivers/usb/hcd.h>
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION (2, 4, 20)
+typedef struct urb urb_t, *purb_t;
+typedef struct iso_packet_descriptor iso_packet_descriptor_t;
+typedef struct usb_ctrlrequest devrequest;
+#endif
+
 #include "usb-host.h"
 
 #define ETRAX_USB_HC_IRQ USB_HC_IRQ_NBR
 #define ETRAX_USB_RX_IRQ USB_DMA_RX_IRQ_NBR
 #define ETRAX_USB_TX_IRQ USB_DMA_TX_IRQ_NBR
 
-static const char *usb_hcd_version = "$Revision: 1.13 $";
+static const char *usb_hcd_version = "$Revision: 1.18 $";
 
 #undef KERN_DEBUG
 #define KERN_DEBUG ""
 
+
 #undef USB_DEBUG_RH
-#undef USB_DEBUG_EP
+#undef USB_DEBUG_EPID
+#undef USB_DEBUG_SB
 #undef USB_DEBUG_DESC
+#undef USB_DEBUG_URB
 #undef USB_DEBUG_TRACE
-#undef USB_DEBUG_CTRL
 #undef USB_DEBUG_BULK
+#undef USB_DEBUG_CTRL
 #undef USB_DEBUG_INTR
+#undef USB_DEBUG_ISOC
 
 #ifdef USB_DEBUG_RH
 #define dbg_rh(format, arg...) printk(KERN_DEBUG __FILE__ ": (RH) " format "\n" , ## arg)
@@ -53,10 +64,16 @@
 #define dbg_rh(format, arg...) do {} while (0)
 #endif
 
-#ifdef USB_DEBUG_EP
-#define dbg_ep(format, arg...) printk(KERN_DEBUG __FILE__ ": (EP) " format "\n" , ## arg)
+#ifdef USB_DEBUG_EPID
+#define dbg_epid(format, arg...) printk(KERN_DEBUG __FILE__ ": (EPID) " format "\n" , ## arg)
 #else
-#define dbg_ep(format, arg...) do {} while (0)
+#define dbg_epid(format, arg...) do {} while (0)
+#endif
+
+#ifdef USB_DEBUG_SB
+#define dbg_sb(format, arg...) printk(KERN_DEBUG __FILE__ ": (SB) " format "\n" , ## arg)
+#else
+#define dbg_sb(format, arg...) do {} while (0)
 #endif
 
 #ifdef USB_DEBUG_CTRL
@@ -77,6 +94,12 @@
 #define dbg_intr(format, arg...) do {} while (0)
 #endif
 
+#ifdef USB_DEBUG_ISOC
+#define dbg_isoc(format, arg...) printk(KERN_DEBUG __FILE__ ": (ISOC) " format "\n" , ## arg)
+#else
+#define dbg_isoc(format, arg...) do {} while (0)
+#endif
+
 #ifdef USB_DEBUG_TRACE
 #define DBFENTER (printk(KERN_DEBUG __FILE__ ": Entering: " __FUNCTION__ "\n"))
 #define DBFEXIT  (printk(KERN_DEBUG __FILE__ ": Exiting:  " __FUNCTION__ "\n"))
@@ -85,6 +108,7 @@
 #define DBFEXIT  do {} while (0)
 #endif
 
+
 /*-------------------------------------------------------------------
  Virtual Root Hub
  -------------------------------------------------------------------*/
@@ -158,78 +182,262 @@
 	0xff   /*  __u8  PortPwrCtrlMask; *** 7 ports max *** */
 };
 
+static struct timer_list bulk_start_timer;
+static struct timer_list bulk_eot_timer;
+/* We want the start timer to expire before the eot timer, because the former might start
+   traffic, thus making it unnecessary for the latter to time out. */
+#define BULK_START_TIMER_INTERVAL (HZ/10) /* 100 ms */
+#define BULK_EOT_TIMER_INTERVAL (HZ/10+2) /* 120 ms */
 
 #define OK(x) len = (x); dbg_rh("OK(%d): line: %d", x, __LINE__); break
 #define CHECK_ALIGN(x) if (((__u32)(x)) & 0x00000003) \
 {panic("Alignment check (DWORD) failed at %s:%s:%d\n", __FILE__, __FUNCTION__, __LINE__);}
 
-static unsigned long submit_urb_count = 0;
+#define SLAB_FLAG     (in_interrupt() ? SLAB_ATOMIC : SLAB_KERNEL)
+#define KMALLOC_FLAG  (in_interrupt() ? GFP_ATOMIC : GFP_KERNEL)
+
+/* Most helpful debugging aid */
+#define assert(expr) ((void) ((expr) ? 0 : (err("assert failed at line %d",__LINE__))))
 
-//#define ETRAX_USB_INTR_IRQ
-//#define ETRAX_USB_INTR_ERROR_FATAL
+/* Alternative assert define which stops after a failed assert. */
+/*
+#define assert(expr)                                      \
+{                                                         \
+        if (!(expr)) {                                    \
+                err("assert failed at line %d",__LINE__); \
+                while (1);                                \
+        }                                                 \
+}
+*/
 
-#define RX_BUF_SIZE        32768
-#define RX_DESC_BUF_SIZE   64
-#define NBR_OF_RX_DESC     (RX_BUF_SIZE / RX_DESC_BUF_SIZE)
 
-#define NBR_OF_EP_DESC     32
+/* FIXME: Should RX_BUF_SIZE be a config option, or maybe we should adjust it dynamically?
+   To adjust it dynamically we would have to get an interrupt when we reach the end
+   of the rx descriptor list, or when we get close to the end, and then allocate more
+   descriptors. */
+
+#define NBR_OF_RX_DESC     512
+#define RX_DESC_BUF_SIZE   1024
+#define RX_BUF_SIZE        (NBR_OF_RX_DESC * RX_DESC_BUF_SIZE)
+
+/* The number of epids is, among other things, used for pre-allocating
+   ctrl, bulk and isoc EP descriptors (one for each epid).
+   Assumed to be > 1 when initiating the DMA lists. */
+#define NBR_OF_EPIDS       32
 
+/* Support interrupt traffic intervals up to 128 ms. */
 #define MAX_INTR_INTERVAL 128
 
-static __u32 ep_usage_bitmask;
-static __u32 ep_really_active;
-static __u32 ep_out_traffic;
-
-static unsigned char RxBuf[RX_BUF_SIZE];
-static USB_IN_Desc_t RxDescList[NBR_OF_RX_DESC] __attribute__ ((aligned (4)));
+/* If periodic traffic (intr or isoc) is to be used, then one entry in the EP table
+   must be "invalid". By this we mean that we shouldn't care about epid attentions
+   for this epid, or at least handle them differently from epid attentions for "valid"
+   epids. This define determines which one to use (don't change it). */   
+#define INVALID_EPID     31
+/* A special epid for the bulk dummys. */
+#define DUMMY_EPID       30
+
+/* This is just a software cache for the valid entries in R_USB_EPT_DATA. */
+static __u32 epid_usage_bitmask;
+
+/* A bitfield to keep information on in/out traffic is needed to uniquely identify
+   an endpoint on a device, since the most significant bit which indicates traffic
+   direction is lacking in the ep_id field (ETRAX epids can handle both in and
+   out traffic on endpoints that are otherwise identical). The USB framework, however,
+   relies on them to be handled separately.  For example, bulk IN and OUT urbs cannot
+   be queued in the same list, since they would block each other. */
+static __u32 epid_out_traffic;
+
+/* DMA IN cache bug. Align the DMA IN buffers to 32 bytes, i.e. a cache line.
+   Since RX_DESC_BUF_SIZE is 1024 is a multiple of 32, all rx buffers will be cache aligned. */
+static volatile unsigned char RxBuf[RX_BUF_SIZE] __attribute__ ((aligned (32)));
+static volatile USB_IN_Desc_t RxDescList[NBR_OF_RX_DESC] __attribute__ ((aligned (4)));
 
+/* Pointers into RxDescList. */
 static volatile USB_IN_Desc_t *myNextRxDesc;
 static volatile USB_IN_Desc_t *myLastRxDesc;
 static volatile USB_IN_Desc_t *myPrevRxDesc;
 
-static USB_EP_Desc_t TxCtrlEPList[NBR_OF_EP_DESC] __attribute__ ((aligned (4)));
-static USB_EP_Desc_t TxBulkEPList[NBR_OF_EP_DESC] __attribute__ ((aligned (4)));
+/* EP descriptors must be 32-bit aligned. */
+static volatile USB_EP_Desc_t TxCtrlEPList[NBR_OF_EPIDS] __attribute__ ((aligned (4)));
+static volatile USB_EP_Desc_t TxBulkEPList[NBR_OF_EPIDS] __attribute__ ((aligned (4)));
+/* After each enabled bulk EP (IN or OUT) we put two disabled EP descriptors with the eol flag set,
+   causing the DMA to stop the DMA channel. The first of these two has the intr flag set, which
+   gives us a dma8_sub0_descr interrupt. When we receive this, we advance the DMA one step in the
+   EP list and then restart the bulk channel, thus forcing a switch between bulk EP descriptors
+   in each frame. */
+static volatile USB_EP_Desc_t TxBulkDummyEPList[NBR_OF_EPIDS][2] __attribute__ ((aligned (4)));
+
+static volatile USB_EP_Desc_t TxIsocEPList[NBR_OF_EPIDS] __attribute__ ((aligned (4)));
+static volatile USB_SB_Desc_t TxIsocSB_zout __attribute__ ((aligned (4)));
+
+static volatile USB_EP_Desc_t TxIntrEPList[MAX_INTR_INTERVAL] __attribute__ ((aligned (4)));
+static volatile USB_SB_Desc_t TxIntrSB_zout __attribute__ ((aligned (4)));
+
+/* A zout transfer makes a memory access at the address of its buf pointer, which means that setting
+   this buf pointer to 0 will cause an access to the flash. In addition to this, setting sw_len to 0
+   results in a 16/32 bytes (depending on DMA burst size) transfer. Instead, we set it to 1, and point
+   it to this buffer. */
+static int zout_buffer[4] __attribute__ ((aligned (4)));
 
-static USB_EP_Desc_t TxIntrEPList[MAX_INTR_INTERVAL] __attribute__ ((aligned (4)));
-static USB_SB_Desc_t TxIntrSB_zout __attribute__ ((aligned (4)));
-
-static struct urb *URB_List[NBR_OF_EP_DESC];
+/* Cache for allocating new EP and SB descriptors. */
 static kmem_cache_t *usb_desc_cache;
+
+/* Cache for the registers allocated in the top half. */
+static kmem_cache_t *top_half_reg_cache;
+
 static struct usb_bus *etrax_usb_bus;
 
-#ifdef USB_DEBUG_DESC
-static void dump_urb (struct urb *urb);
-#endif
+/* This is a circular (double-linked) list of the active urbs for each epid.
+   The head is never removed, and new urbs are linked onto the list as 
+   urb_entry_t elements. Don't reference urb_list directly; use the wrapper
+   functions instead. Note that working with these lists might require spinlock
+   protection. */
+static struct list_head urb_list[NBR_OF_EPIDS];
+
+/* Read about the need and usage of this lock in submit_ctrl_urb. */
+static spinlock_t urb_list_lock;
+
+/* Used when unlinking asynchronously. */
+static struct list_head urb_unlink_list;
+
+/* Wrappers around the list functions (include/linux/list.h). */
+
+static inline int urb_list_empty(int epid)
+{
+        return list_empty(&urb_list[epid]);
+}
+
+/* Returns first urb for this epid, or NULL if list is empty. */
+static inline urb_t *urb_list_first(int epid)
+{
+        urb_t *first_urb = 0;
+
+        if (!urb_list_empty(epid)) {
+                /* Get the first urb (i.e. head->next). */
+                urb_entry_t *urb_entry = list_entry((&urb_list[epid])->next, urb_entry_t, list);
+                first_urb = urb_entry->urb;
+        }
+        return first_urb;
+}
+
+/* Adds an urb_entry last in the list for this epid. */
+static inline void urb_list_add(urb_t *urb, int epid)
+{
+        urb_entry_t *urb_entry = (urb_entry_t *)kmalloc(sizeof(urb_entry_t), KMALLOC_FLAG);
+        assert(urb_entry);
+
+        urb_entry->urb = urb;
+        list_add_tail(&urb_entry->list, &urb_list[epid]);
+}
+
+/* Search through the list for an element that contains this urb. (The list
+   is expected to be short and the one we are about to delete will often be
+   the first in the list.) */
+static inline urb_entry_t *__urb_list_entry(urb_t *urb, int epid)
+{
+        struct list_head *entry;
+        struct list_head *tmp;
+        urb_entry_t *urb_entry;
+
+        list_for_each_safe(entry, tmp, &urb_list[epid]) {
+                urb_entry = list_entry(entry, urb_entry_t, list);
+                assert(urb_entry);
+                assert(urb_entry->urb);
+                
+                if (urb_entry->urb == urb) {
+                        return urb_entry;
+                }
+        }
+        return 0;
+}
+
+/* Delete an urb from the list. */
+static inline void urb_list_del(urb_t *urb, int epid)
+{
+        urb_entry_t *urb_entry = __urb_list_entry(urb, epid);
+        assert(urb_entry);
+        
+        /* Delete entry and free. */
+        list_del(&urb_entry->list);
+        kfree(urb_entry);
+}
+
+/* Move an urb to the end of the list. */
+static inline void urb_list_move_last(urb_t *urb, int epid)
+{
+        urb_entry_t *urb_entry = __urb_list_entry(urb, epid);
+        assert(urb_entry);
+
+        list_del(&urb_entry->list);
+        list_add_tail(&urb_entry->list, &urb_list[epid]);
+}
+
+/* For debug purposes only. */
+static inline void urb_list_dump(int epid)
+{
+        struct list_head *entry;
+        struct list_head *tmp;
+        urb_entry_t *urb_entry;
+        int i = 0;
+
+        info("Dumping urb list for epid %d", epid);
+        
+        list_for_each_safe(entry, tmp, &urb_list[epid]) {
+                urb_entry = list_entry(entry, urb_entry_t, list);
+                info("   entry %d, urb = 0x%lx", i, (unsigned long)urb_entry->urb);
+        }
+}
+
 static void init_rx_buffers(void);
-static int etrax_rh_unlink_urb (struct urb *urb);
-static void etrax_rh_send_irq(struct urb *urb);
-static void etrax_rh_init_int_timer(struct urb *urb);
+static int etrax_rh_unlink_urb(urb_t *urb);
+static void etrax_rh_send_irq(urb_t *urb);
+static void etrax_rh_init_int_timer(urb_t *urb);
 static void etrax_rh_int_timer_do(unsigned long ptr);
 
-static void etrax_usb_setup_epid(int epid, char devnum, char endpoint,
-				 char packsize, char slow, char out_traffic);
-static int etrax_usb_lookup_epid(unsigned char devnum, char endpoint,
-				 char slow, int maxp, char out_traffic);
+static int etrax_usb_setup_epid(urb_t *urb);
+static int etrax_usb_lookup_epid(urb_t *urb);
 static int etrax_usb_allocate_epid(void);
 static void etrax_usb_free_epid(int epid);
-static void cleanup_sb(USB_SB_Desc_t *sb);
 
-static void etrax_usb_do_ctrl_hw_add(struct urb *urb, int epid, char maxlen);
-static void etrax_usb_do_bulk_hw_add(struct urb *urb, int epid, char maxlen);
+static int etrax_remove_from_sb_list(urb_t *urb);
 
-static int etrax_usb_submit_ctrl_urb(struct urb *urb);
+static void etrax_usb_add_to_bulk_sb_list(urb_t *urb, int epid);
+static void etrax_usb_add_to_ctrl_sb_list(urb_t *urb, int epid);
+static void etrax_usb_add_to_intr_sb_list(urb_t *urb, int epid);
+static void etrax_usb_add_to_isoc_sb_list(urb_t *urb, int epid); 
+
+static int etrax_usb_submit_bulk_urb(urb_t *urb);
+static int etrax_usb_submit_ctrl_urb(urb_t *urb);
+static int etrax_usb_submit_intr_urb(urb_t *urb);
+static int etrax_usb_submit_isoc_urb(urb_t *urb);
 
-static int etrax_usb_submit_urb(struct urb *urb);
-static int etrax_usb_unlink_urb(struct urb *urb);
+static int etrax_usb_submit_urb(urb_t *urb);
+static int etrax_usb_unlink_urb(urb_t *urb);
 static int etrax_usb_get_frame_number(struct usb_device *usb_dev);
 static int etrax_usb_allocate_dev(struct usb_device *usb_dev);
 static int etrax_usb_deallocate_dev(struct usb_device *usb_dev);
 
 static void etrax_usb_tx_interrupt(int irq, void *vhc, struct pt_regs *regs);
 static void etrax_usb_rx_interrupt(int irq, void *vhc, struct pt_regs *regs);
-static void etrax_usb_hc_intr_top_half(int irq, void *vhc, struct pt_regs *regs);
+static void etrax_usb_hc_interrupt_top_half(int irq, void *vhc, struct pt_regs *regs);
+static void etrax_usb_hc_interrupt_bottom_half(void *data);
 
-static int etrax_rh_submit_urb (struct urb *urb);
+/* The following is a list of interrupt handlers for the host controller interrupts we use.
+   They are called from etrax_usb_hc_interrupt_bottom_half. */
+static void etrax_usb_hc_isoc_eof_interrupt(void);
+static void etrax_usb_hc_bulk_eot_interrupt(int timer_induced);
+static void etrax_usb_hc_epid_attn_interrupt(usb_interrupt_registers_t *reg);
+static void etrax_usb_hc_port_status_interrupt(usb_interrupt_registers_t *reg);
+static void etrax_usb_hc_ctl_status_interrupt(usb_interrupt_registers_t *reg);
+
+static int etrax_rh_submit_urb (urb_t *urb);
+
+/* Forward declaration needed because they are used in the rx interrupt routine. */
+static void etrax_usb_complete_urb(urb_t *urb, int status);
+static void etrax_usb_complete_bulk_urb(urb_t *urb, int status);
+static void etrax_usb_complete_ctrl_urb(urb_t *urb, int status);
+static void etrax_usb_complete_intr_urb(urb_t *urb, int status);
+static void etrax_usb_complete_isoc_urb(urb_t *urb, int status);
 
 static int etrax_usb_hc_init(void);
 static void etrax_usb_hc_cleanup(void);
@@ -243,65 +451,193 @@
 	etrax_usb_unlink_urb
 };
 
-#ifdef USB_DEBUG_DESC
-static void dump_urb(struct urb *urb)
-{
-	printk("\nurb                   :0x%08X\n", urb);
-	printk("next                  :0x%08X\n", urb->next);
-	printk("dev                   :0x%08X\n", urb->dev);
-	printk("pipe                  :0x%08X\n", urb->pipe);
-	printk("status                :%d\n", urb->status);
-	printk("transfer_flags        :0x%08X\n", urb->transfer_flags);
-	printk("transfer_buffer       :0x%08X\n", urb->transfer_buffer);
-	printk("transfer_buffer_length:%d\n", urb->transfer_buffer_length);
-	printk("actual_length         :%d\n", urb->actual_length);
-	printk("setup_packet          :0x%08X\n", urb->setup_packet);
-	printk("start_frame           :%d\n", urb->start_frame);
-	printk("number_of_packets     :%d\n", urb->number_of_packets);
-	printk("interval              :%d\n", urb->interval);
-	printk("error_count           :%d\n", urb->error_count);
-	printk("context               :0x%08X\n", urb->context);
-	printk("complete              :0x%08X\n\n", urb->complete);
-}
-
-static void dump_in_desc(USB_IN_Desc_t *in)
-{
-	printk("\nUSB_IN_Desc at 0x%08X\n", in);
-	printk("  sw_len  : 0x%04X (%d)\n", in->sw_len, in->sw_len);
-	printk("  command : 0x%04X\n", in->command);
-	printk("  next    : 0x%08X\n", in->next);
-	printk("  buf     : 0x%08X\n", in->buf);
-	printk("  hw_len  : 0x%04X (%d)\n", in->hw_len, in->hw_len);
-	printk("  status  : 0x%04X\n\n", in->status);
-}
-
-static void dump_sb_desc(USB_SB_Desc_t *sb)
-{
-	printk("\nUSB_SB_Desc at 0x%08X\n", sb);
-	printk("  sw_len  : 0x%04X (%d)\n", sb->sw_len, sb->sw_len);
-	printk("  command : 0x%04X\n", sb->command);
-	printk("  next    : 0x%08X\n", sb->next);
-	printk("  buf     : 0x%08X\n\n", sb->buf);
+/* Note that these functions are always available in their "__" variants, for use in
+   error situations. The "__" missing variants are controlled by the USB_DEBUG_DESC/
+   USB_DEBUG_URB macros. */
+static void __dump_urb(purb_t purb)
+{
+	printk("\nurb                  :0x%08lx\n", (unsigned long)purb);
+	printk("next                  :0x%08lx\n", (unsigned long)purb->next);
+	printk("dev                   :0x%08lx\n", (unsigned long)purb->dev);
+	printk("pipe                  :0x%08x\n", purb->pipe);
+	printk("status                :%d\n", purb->status);
+	printk("transfer_flags        :0x%08x\n", purb->transfer_flags);
+	printk("transfer_buffer       :0x%08lx\n", (unsigned long)purb->transfer_buffer);
+	printk("transfer_buffer_length:%d\n", purb->transfer_buffer_length);
+	printk("actual_length         :%d\n", purb->actual_length);
+	printk("setup_packet          :0x%08lx\n", (unsigned long)purb->setup_packet);
+	printk("start_frame           :%d\n", purb->start_frame);
+	printk("number_of_packets     :%d\n", purb->number_of_packets);
+	printk("interval              :%d\n", purb->interval);
+	printk("error_count           :%d\n", purb->error_count);
+	printk("context               :0x%08lx\n", (unsigned long)purb->context);
+        printk("complete              :0x%08lx\n\n", (unsigned long)purb->complete);
+}
+
+static void __dump_in_desc(volatile USB_IN_Desc_t *in)
+{
+	printk("\nUSB_IN_Desc at 0x%08lx\n", (unsigned long)in);
+	printk("  sw_len  : 0x%04x (%d)\n", in->sw_len, in->sw_len);
+	printk("  command : 0x%04x\n", in->command);
+	printk("  next    : 0x%08lx\n", in->next);
+	printk("  buf     : 0x%08lx\n", in->buf);
+	printk("  hw_len  : 0x%04x (%d)\n", in->hw_len, in->hw_len);
+        printk("  status  : 0x%04x\n\n", in->status);
+}
+
+static void __dump_sb_desc(volatile USB_SB_Desc_t *sb)
+{
+	char tt = (sb->command & 0x30) >> 4;
+	char *tt_string;
+
+	switch (tt) {
+	case 0:
+		tt_string = "zout";
+		break;
+	case 1:
+		tt_string = "in";
+		break;
+	case 2:
+		tt_string = "out";
+		break;
+	case 3:
+		tt_string = "setup";
+		break;
+	default:
+		tt_string = "unknown (weird)";
+	}
+		
+	printk("\n   USB_SB_Desc at 0x%08lx\n", (unsigned long)sb); 
+        printk("     command : 0x%04x\n", sb->command);
+	printk("        rem     : %d\n", (sb->command & 0x3f00) >> 8);
+	printk("        full    : %d\n", (sb->command & 0x40) >> 6);
+	printk("        tt      : %d (%s)\n", tt, tt_string);
+	printk("        intr    : %d\n", (sb->command & 0x8) >> 3);
+	printk("        eot     : %d\n", (sb->command & 0x2) >> 1);
+	printk("        eol     : %d\n", sb->command & 0x1);
+        printk("     sw_len  : 0x%04x (%d)\n", sb->sw_len, sb->sw_len);
+	printk("     next    : 0x%08lx\n", sb->next);
+        printk("     buf     : 0x%08lx\n\n", sb->buf);
 }
 
 
-static void dump_ep_desc(USB_EP_Desc_t *ep)
-{
-	printk("\nUSB_EP_Desc at 0x%08X\n", ep);
-	printk("  hw_len  : 0x%04X (%d)\n", ep->hw_len, ep->hw_len);
-	printk("  command : 0x%08X\n", ep->command);
-	printk("  sub     : 0x%08X\n", ep->sub);
-	printk("  nep     : 0x%08X\n\n", ep->nep);
+static void __dump_ep_desc(volatile USB_EP_Desc_t *ep)
+{
+	printk("\nUSB_EP_Desc at 0x%08lx\n", (unsigned long)ep);
+        printk("  command : 0x%04x\n", ep->command);
+	printk("     ep_id   : %d\n", (ep->command & 0x1f00) >> 8);
+	printk("     enable  : %d\n", (ep->command & 0x10) >> 4);
+	printk("     intr    : %d\n", (ep->command & 0x8) >> 3);
+	printk("     eof     : %d\n", (ep->command & 0x2) >> 1);
+	printk("     eol     : %d\n", ep->command & 0x1);
+	printk("  hw_len  : 0x%04x (%d)\n", ep->hw_len, ep->hw_len);
+        printk("  next    : 0x%08lx\n", ep->next);
+	printk("  sub     : 0x%08lx\n\n", ep->sub);
+}
+
+static inline void __dump_ep_list(int pipe_type)
+{
+        volatile USB_EP_Desc_t *ep;
+        volatile USB_EP_Desc_t *first_ep;
+        volatile USB_SB_Desc_t *sb;
+
+        switch (pipe_type)
+        {
+        case PIPE_BULK:
+                first_ep = &TxBulkEPList[0];
+                break;
+        case PIPE_CONTROL:
+                first_ep = &TxCtrlEPList[0];
+                break;
+        case PIPE_INTERRUPT:
+                first_ep = &TxIntrEPList[0];
+                break;
+        case PIPE_ISOCHRONOUS:
+                first_ep = &TxIsocEPList[0];
+                break;
+        default:
+		warn("Cannot dump unknown traffic type");
+		return;
+        }
+        ep = first_ep;
+
+        printk("\n\nDumping EP list...\n\n");
+
+        do {
+		__dump_ep_desc(ep);
+		/* Cannot phys_to_virt on 0 as it turns into 80000000, which is != 0. */
+		sb = ep->sub ? phys_to_virt(ep->sub) : 0;                
+		while (sb) {
+			__dump_sb_desc(sb);
+			sb = sb->next ? phys_to_virt(sb->next) : 0;
+		}
+                ep = (volatile USB_EP_Desc_t *)(phys_to_virt(ep->next));
+                
+        } while (ep != first_ep);
+}
+
+static inline void __dump_ept_data(int epid)
+{
+	unsigned long flags;
+	__u32 r_usb_ept_data;
+
+	if (epid < 0 || epid > 31) {
+		printk("Cannot dump ept data for invalid epid %d\n", epid);
+		return;
+	}
+
+	save_flags(flags);
+	cli();
+	*R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid); 
+	nop();
+	r_usb_ept_data = *R_USB_EPT_DATA;
+	restore_flags(flags);
+	
+	printk("\nR_USB_EPT_DATA = 0x%x for epid %d :\n", r_usb_ept_data, epid);
+	if (r_usb_ept_data == 0) {
+		/* No need for more detailed printing. */
+		return;
+	}
+	printk("  valid           : %d\n", (r_usb_ept_data & 0x80000000) >> 31);
+	printk("  hold            : %d\n", (r_usb_ept_data & 0x40000000) >> 30);
+	printk("  error_count_in  : %d\n", (r_usb_ept_data & 0x30000000) >> 28);
+	printk("  t_in            : %d\n", (r_usb_ept_data & 0x08000000) >> 27);
+	printk("  low_speed       : %d\n", (r_usb_ept_data & 0x04000000) >> 26);
+	printk("  port            : %d\n", (r_usb_ept_data & 0x03000000) >> 24);
+	printk("  error_code      : %d\n", (r_usb_ept_data & 0x00c00000) >> 22);
+	printk("  t_out           : %d\n", (r_usb_ept_data & 0x00200000) >> 21);
+	printk("  error_count_out : %d\n", (r_usb_ept_data & 0x00180000) >> 19);
+	printk("  max_len         : %d\n", (r_usb_ept_data & 0x0003f800) >> 11);
+	printk("  ep              : %d\n", (r_usb_ept_data & 0x00000780) >> 7);
+	printk("  dev             : %d\n", (r_usb_ept_data & 0x0000003f)); 
 }
 
+static inline void __dump_ept_data_list(void)
+{
+	int i;
 
+	printk("Dumping the whole R_USB_EPT_DATA list\n");
+
+	for (i = 0; i < 32; i++) {
+		__dump_ept_data(i);
+	}
+}
+#ifdef USB_DEBUG_DESC
+#define dump_in_desc(...) __dump_in_desc(...)
+#define dump_sb_desc(...) __dump_sb_desc(...)
+#define dump_ep_desc(...) __dump_ep_desc(...)
 #else
-#define dump_urb(...)     do {} while (0)
 #define dump_in_desc(...) do {} while (0)
 #define dump_sb_desc(...) do {} while (0)
 #define dump_ep_desc(...) do {} while (0)
 #endif
 
+#ifdef USB_DEBUG_URB
+#define dump_urb(x)     __dump_urb(x)
+#else
+#define dump_urb(x)     do {} while (0)
+#endif
+
 static void init_rx_buffers(void)
 {
 	int i;
@@ -315,6 +651,11 @@
 		RxDescList[i].buf = virt_to_phys(RxBuf + (i * RX_DESC_BUF_SIZE));
 		RxDescList[i].hw_len = 0;
 		RxDescList[i].status = 0;
+
+                /* DMA IN cache bug. (struct etrax_dma_descr has the same layout as USB_IN_Desc
+		   for the relevant fields.) */
+                prepare_rx_descriptor((struct etrax_dma_descr*)&RxDescList[i]);
+
 	}
 	
 	RxDescList[i].sw_len = RX_DESC_BUF_SIZE;
@@ -334,367 +675,254 @@
 	DBFEXIT;
 }
 
-static void init_tx_ctrl_ep(void)
+static void init_tx_bulk_ep(void)
 {
 	int i;
 	
 	DBFENTER;
 	
-	for (i = 0; i < (NBR_OF_EP_DESC - 1); i++) {
-		TxCtrlEPList[i].hw_len = 0;
-		TxCtrlEPList[i].command = IO_FIELD(USB_EP_command, epid, i);
-		TxCtrlEPList[i].sub = 0;
-		TxCtrlEPList[i].nep = virt_to_phys(&TxCtrlEPList[i + 1]);
+	for (i = 0; i < (NBR_OF_EPIDS - 1); i++) {
+                CHECK_ALIGN(&TxBulkEPList[i]);
+		TxBulkEPList[i].hw_len = 0;
+		TxBulkEPList[i].command = IO_FIELD(USB_EP_command, epid, i);
+		TxBulkEPList[i].sub = 0;
+		TxBulkEPList[i].next = virt_to_phys(&TxBulkEPList[i + 1]);
+
+		/* Initiate two EPs, disabled and with the eol flag set. No need for any
+		   preserved epid. */
+
+		/* The first one has the intr flag set so we get an interrupt when the DMA
+		   channel is about to become disabled. */
+		CHECK_ALIGN(&TxBulkDummyEPList[i][0]);
+		TxBulkDummyEPList[i][0].hw_len = 0;
+		TxBulkDummyEPList[i][0].command = (IO_FIELD(USB_EP_command, epid, DUMMY_EPID) |
+						   IO_STATE(USB_EP_command, eol, yes) |
+						   IO_STATE(USB_EP_command, intr, yes));
+		TxBulkDummyEPList[i][0].sub = 0;
+		TxBulkDummyEPList[i][0].next = virt_to_phys(&TxBulkDummyEPList[i][1]);
+
+		/* The second one. */
+		CHECK_ALIGN(&TxBulkDummyEPList[i][1]);
+		TxBulkDummyEPList[i][1].hw_len = 0;
+		TxBulkDummyEPList[i][1].command = (IO_FIELD(USB_EP_command, epid, DUMMY_EPID) |
+						   IO_STATE(USB_EP_command, eol, yes));
+		TxBulkDummyEPList[i][1].sub = 0;
+		/* The last dummy's next pointer is the same as the current EP's next pointer. */
+		TxBulkDummyEPList[i][1].next = virt_to_phys(&TxBulkEPList[i + 1]);
 	}
-	
-	TxCtrlEPList[i].hw_len = 0;
-	TxCtrlEPList[i].command = IO_STATE(USB_EP_command, eol, yes) |
-		IO_FIELD(USB_EP_command, epid, i);
 
-	TxCtrlEPList[i].sub = 0;
-	TxCtrlEPList[i].nep = virt_to_phys(&TxCtrlEPList[0]);
-	
-	*R_DMA_CH8_SUB1_EP = virt_to_phys(&TxCtrlEPList[0]);
-	*R_DMA_CH8_SUB1_CMD = IO_STATE(R_DMA_CH8_SUB1_CMD, cmd, start);
-	
+	/* Configure the last one. */
+        CHECK_ALIGN(&TxBulkEPList[i]);
+	TxBulkEPList[i].hw_len = 0;
+	TxBulkEPList[i].command = (IO_STATE(USB_EP_command, eol, yes) |
+                                   IO_FIELD(USB_EP_command, epid, i));
+	TxBulkEPList[i].sub = 0;
+	TxBulkEPList[i].next = virt_to_phys(&TxBulkEPList[0]);
+
+	/* No need configuring dummy EPs for the last one as it will never be used for
+	   bulk traffic (i == INVALD_EPID at this point). */
+
+	/* Set up to start on the last EP so we will enable it when inserting traffic
+	   for the first time (imitating the situation where the DMA has stopped
+	   because there was no more traffic). */
+	*R_DMA_CH8_SUB0_EP = virt_to_phys(&TxBulkEPList[i]);
+	/* No point in starting the bulk channel yet.
+	 *R_DMA_CH8_SUB0_CMD = IO_STATE(R_DMA_CH8_SUB0_CMD, cmd, start); */
 	DBFEXIT;
 }
 
-static void init_tx_bulk_ep(void)
+static void init_tx_ctrl_ep(void)
 {
 	int i;
 	
 	DBFENTER;
 	
-	for (i = 0; i < (NBR_OF_EP_DESC - 1); i++) {
-		TxBulkEPList[i].hw_len = 0;
-		TxBulkEPList[i].command = IO_FIELD(USB_EP_command, epid, i);
-		TxBulkEPList[i].sub = 0;
-		TxBulkEPList[i].nep = virt_to_phys(&TxBulkEPList[i + 1]);
+	for (i = 0; i < (NBR_OF_EPIDS - 1); i++) {
+                CHECK_ALIGN(&TxCtrlEPList[i]);
+		TxCtrlEPList[i].hw_len = 0;
+		TxCtrlEPList[i].command = IO_FIELD(USB_EP_command, epid, i);
+		TxCtrlEPList[i].sub = 0;
+		TxCtrlEPList[i].next = virt_to_phys(&TxCtrlEPList[i + 1]);
 	}
 	
-	TxBulkEPList[i].hw_len = 0;
-	TxBulkEPList[i].command = IO_STATE(USB_EP_command, eol, yes) |
-		IO_FIELD(USB_EP_command, epid, i);
+        CHECK_ALIGN(&TxCtrlEPList[i]);
+	TxCtrlEPList[i].hw_len = 0;
+	TxCtrlEPList[i].command = (IO_STATE(USB_EP_command, eol, yes) |
+                                   IO_FIELD(USB_EP_command, epid, i));
 
-	TxBulkEPList[i].sub = 0;
-	TxBulkEPList[i].nep = virt_to_phys(&TxBulkEPList[0]);
+	TxCtrlEPList[i].sub = 0;
+	TxCtrlEPList[i].next = virt_to_phys(&TxCtrlEPList[0]);
 	
-	*R_DMA_CH8_SUB0_EP = virt_to_phys(&TxBulkEPList[0]);
-	*R_DMA_CH8_SUB0_CMD = IO_STATE(R_DMA_CH8_SUB0_CMD, cmd, start);
+	*R_DMA_CH8_SUB1_EP = virt_to_phys(&TxCtrlEPList[0]);
+	*R_DMA_CH8_SUB1_CMD = IO_STATE(R_DMA_CH8_SUB1_CMD, cmd, start);
 	
 	DBFEXIT;
 }
 
+
 static void init_tx_intr_ep(void)
 {
 	int i;
 
 	DBFENTER;
 
-	TxIntrSB_zout.sw_len = 0;
+        /* Read comment at zout_buffer declaration for an explanation to this. */
+	TxIntrSB_zout.sw_len = 1;
 	TxIntrSB_zout.next = 0;
-	TxIntrSB_zout.buf = 0;
-	TxIntrSB_zout.command = IO_FIELD(USB_SB_command, rem, 0) |
-		IO_STATE(USB_SB_command, tt, zout) |
-		IO_STATE(USB_SB_command, full, yes) |
-		IO_STATE(USB_SB_command, eot, yes) |
-		IO_STATE(USB_SB_command, eol, yes);
+	TxIntrSB_zout.buf = virt_to_phys(&zout_buffer[0]);
+	TxIntrSB_zout.command = (IO_FIELD(USB_SB_command, rem, 0) |
+                                 IO_STATE(USB_SB_command, tt, zout) |
+                                 IO_STATE(USB_SB_command, full, yes) |
+                                 IO_STATE(USB_SB_command, eot, yes) |
+                                 IO_STATE(USB_SB_command, eol, yes));
 
 	for (i = 0; i < (MAX_INTR_INTERVAL - 1); i++) {
+                CHECK_ALIGN(&TxIntrEPList[i]);
 		TxIntrEPList[i].hw_len = 0;
-		TxIntrEPList[i].command = IO_STATE(USB_EP_command, eof, yes) |
-			IO_STATE(USB_EP_command, enable, yes) |
-			IO_FIELD(USB_EP_command, epid, 0);
+		TxIntrEPList[i].command =
+			(IO_STATE(USB_EP_command, eof, yes) |
+                         IO_STATE(USB_EP_command, enable, yes) |
+                         IO_FIELD(USB_EP_command, epid, INVALID_EPID));
 		TxIntrEPList[i].sub = virt_to_phys(&TxIntrSB_zout);
-		TxIntrEPList[i].nep = virt_to_phys(&TxIntrEPList[i + 1]);
+		TxIntrEPList[i].next = virt_to_phys(&TxIntrEPList[i + 1]);
 	}
 	
+	CHECK_ALIGN(&TxIntrEPList[i]);
 	TxIntrEPList[i].hw_len = 0;
 	TxIntrEPList[i].command =
-		IO_STATE(USB_EP_command, eof, yes) |
-		IO_STATE(USB_EP_command, enable, yes) |
-		IO_FIELD(USB_EP_command, epid, 0);
+		(IO_STATE(USB_EP_command, eof, yes) |
+		 IO_STATE(USB_EP_command, eol, yes) |
+		 IO_STATE(USB_EP_command, enable, yes) |
+		 IO_FIELD(USB_EP_command, epid, INVALID_EPID));
 	TxIntrEPList[i].sub = virt_to_phys(&TxIntrSB_zout);
-	TxIntrEPList[i].nep = virt_to_phys(&TxIntrEPList[0]);
+	TxIntrEPList[i].next = virt_to_phys(&TxIntrEPList[0]);
 
 	*R_DMA_CH8_SUB2_EP = virt_to_phys(&TxIntrEPList[0]);
 	*R_DMA_CH8_SUB2_CMD = IO_STATE(R_DMA_CH8_SUB2_CMD, cmd, start);
-	
 	DBFEXIT;
 }
 
-
-static int etrax_usb_unlink_intr_urb(struct urb *urb)
+static void init_tx_isoc_ep(void)
 {
-	USB_EP_Desc_t *tmp_ep;
-	USB_EP_Desc_t *first_ep;
-	
-	USB_EP_Desc_t *ep_desc;
-	
-	int epid;
-	char devnum;
-	char endpoint;
-	char slow;
-	int maxlen;
-	char out_traffic;
 	int i;
 	
 	DBFENTER;
-
-	devnum = usb_pipedevice(urb->pipe);
-	endpoint = usb_pipeendpoint(urb->pipe);
-	slow = usb_pipeslow(urb->pipe);
-	maxlen = usb_maxpacket(urb->dev, urb->pipe,
-			       usb_pipeout(urb->pipe));
-	out_traffic = usb_pipeout(urb->pipe);
-
-	epid = etrax_usb_lookup_epid(devnum, endpoint, slow, maxlen, out_traffic);
-	if (epid == -1) {
-		err("Trying to unlink urb that is not in traffic queue!!");
-		return -1;  /* fix this */
-	}
-
-	*R_DMA_CH8_SUB2_CMD = IO_STATE(R_DMA_CH8_SUB2_CMD, cmd, stop);
-	/* Somehow wait for the DMA to finish current activities */
-	i = jiffies + 100;
-	while (jiffies < i);	
 	
-	first_ep = &TxIntrEPList[0];
-	tmp_ep = first_ep;
+        /* Read comment at zout_buffer declaration for an explanation to this. */
+	TxIsocSB_zout.sw_len = 1;
+	TxIsocSB_zout.next = 0;
+	TxIsocSB_zout.buf = virt_to_phys(&zout_buffer[0]);
+	TxIsocSB_zout.command = (IO_FIELD(USB_SB_command, rem, 0) |
+                                 IO_STATE(USB_SB_command, tt, zout) |
+                                 IO_STATE(USB_SB_command, full, yes) |
+                                 IO_STATE(USB_SB_command, eot, yes) |
+                                 IO_STATE(USB_SB_command, eol, yes));
 	
-	do {
-		if (IO_EXTRACT(USB_EP_command, epid, ((USB_EP_Desc_t *)phys_to_virt(tmp_ep->nep))->command)
-		    == epid) {
-			/* Unlink it !!! */
-			dbg_intr("Found urb to unlink for epid %d", epid);
-			
-			ep_desc = phys_to_virt(tmp_ep->nep);
-			tmp_ep->nep = ep_desc->nep;
-			kmem_cache_free(usb_desc_cache, phys_to_virt(ep_desc->sub));
-			kmem_cache_free(usb_desc_cache, ep_desc);
-		}
+	/* The last isochronous EP descriptor is a dummy. */
+	
+	for (i = 0; i < (NBR_OF_EPIDS - 1); i++) {
+                CHECK_ALIGN(&TxIsocEPList[i]);
+		TxIsocEPList[i].hw_len = 0;
+		TxIsocEPList[i].command = IO_FIELD(USB_EP_command, epid, i);
+		TxIsocEPList[i].sub = 0;
+		TxIsocEPList[i].next = virt_to_phys(&TxIsocEPList[i + 1]);
+	}
 
-		tmp_ep = phys_to_virt(tmp_ep->nep);
-		
-	} while (tmp_ep != first_ep);
+        CHECK_ALIGN(&TxIsocEPList[i]);
+	TxIsocEPList[i].hw_len = 0;
 
-	/* We should really try to move the EP register to an EP that is not removed
-	   instead of restarting, but this will work too */
-	*R_DMA_CH8_SUB2_EP = virt_to_phys(&TxIntrEPList[0]);
-	*R_DMA_CH8_SUB2_CMD = IO_STATE(R_DMA_CH8_SUB2_CMD, cmd, start);
+	/* Must enable the last EP descr to get eof interrupt. */
+	TxIsocEPList[i].command = (IO_STATE(USB_EP_command, enable, yes) |
+                                   IO_STATE(USB_EP_command, eof, yes) |
+                                   IO_STATE(USB_EP_command, eol, yes) |
+                                   IO_FIELD(USB_EP_command, epid, INVALID_EPID));
+	TxIsocEPList[i].sub = virt_to_phys(&TxIsocSB_zout);
+	TxIsocEPList[i].next = virt_to_phys(&TxIsocEPList[0]);
 
-	clear_bit(epid, (void *)&ep_really_active);
-	URB_List[epid] = NULL;
-	etrax_usb_free_epid(epid);
+	*R_DMA_CH8_SUB3_EP = virt_to_phys(&TxIsocEPList[0]);
+	*R_DMA_CH8_SUB3_CMD = IO_STATE(R_DMA_CH8_SUB3_CMD, cmd, start);
 	
 	DBFEXIT;
-
-	return 0;
 }
 
-void etrax_usb_do_intr_recover(int epid)
+static void etrax_usb_unlink_intr_urb(urb_t *urb)
 {
-	USB_EP_Desc_t *first_ep, *tmp_ep;
-	
-	first_ep = (USB_EP_Desc_t *)phys_to_virt(*R_DMA_CH8_SUB2_EP);
-	tmp_ep = first_ep;
-
-	do {
-		if (IO_EXTRACT(USB_EP_command, epid, tmp_ep->command) == epid &&
-		    !(tmp_ep->command & IO_MASK(USB_EP_command, enable))) {
-			tmp_ep->command |= IO_STATE(USB_EP_command, enable, yes);
-		}
-		
-		tmp_ep = (USB_EP_Desc_t *)phys_to_virt(tmp_ep->nep);
-		
-	} while (tmp_ep != first_ep);
-}
+	volatile USB_EP_Desc_t *first_ep;  /* First EP in the list. */
+	volatile USB_EP_Desc_t *curr_ep;   /* Current EP, the iterator. */
+	volatile USB_EP_Desc_t *next_ep;   /* The EP after current. */
+	volatile USB_EP_Desc_t *unlink_ep; /* The one we should remove from the list. */
 
-static int etrax_usb_submit_intr_urb(struct urb *urb)
-{
-	USB_EP_Desc_t *tmp_ep;
-	USB_EP_Desc_t *first_ep;
-	
 	int epid;
-	char devnum;
-	char endpoint;
-	char maxlen;
-	char out_traffic;
-	char slow;
-	int interval;
-	int i;
-	
-	etrax_urb_priv_t *urb_priv;
-	
-	DBFENTER;
-
-	devnum = usb_pipedevice(urb->pipe);
-	endpoint = usb_pipeendpoint(urb->pipe);
-	maxlen = usb_maxpacket(urb->dev, urb->pipe,
-			       usb_pipeout(urb->pipe));
-	out_traffic = usb_pipeout(urb->pipe);
 
-	slow = usb_pipeslow(urb->pipe);
-	interval = urb->interval;
+        /* Read 8.8.4 in Designer's Reference, "Removing an EP Descriptor from the List". */
 
-	dbg_intr("Intr traffic for dev %d, endpoint %d, maxlen %d, slow %d",
-		 devnum, endpoint, maxlen, slow);
-	
-	epid = etrax_usb_lookup_epid(devnum, endpoint, slow, maxlen, out_traffic);
-	if (epid == -1) {
-		epid = etrax_usb_allocate_epid();
-		if (epid == -1) {
-			/* We're out of endpoints, return some error */
-			err("We're out of endpoints");
-			return -ENOMEM;
-		}
-		/* Now we have to fill in this ep */
-		etrax_usb_setup_epid(epid, devnum, endpoint, maxlen, slow, out_traffic);
-	}
-	/* Ok, now we got valid endpoint, lets insert some traffic */
+	DBFENTER;
 
-	urb_priv = (etrax_urb_priv_t *)kmalloc(sizeof(etrax_urb_priv_t), GFP_KERNEL);
-	urb_priv->first_sb = 0;
-	urb_priv->rx_offset = 0;
-	urb_priv->eot = 0;
-	INIT_LIST_HEAD(&urb_priv->ep_in_list);
-	urb->hcpriv = urb_priv;
+	epid = ((etrax_urb_priv_t *)urb->hcpriv)->epid;
 
-	/* This is safe since there cannot be any other URB's for this epid */
-	URB_List[epid] = urb;
-#if 0
-	first_ep = (USB_EP_Desc_t *)phys_to_virt(*R_DMA_CH8_SUB2_EP);
-#else
 	first_ep = &TxIntrEPList[0];
-#endif
+	curr_ep = first_ep;
+	
 
-	/* Round of the interval to 2^n, it is obvious that this code favours
-	   smaller numbers, but that is actually a good thing */
-	for (i = 0; interval; i++) {
-		interval = interval >> 1;
-	}
+        /* Note that this loop removes all EP descriptors with this epid. This assumes
+           that all EP descriptors belong to the one and only urb for this epid. */
 
-	urb->interval = interval = 1 << (i - 1);
+	do {
+                next_ep = (USB_EP_Desc_t *)phys_to_virt(curr_ep->next);
 
-	dbg_intr("Interval rounded to %d", interval);
+		if (IO_EXTRACT(USB_EP_command, epid, next_ep->command) == epid) {
 
-	tmp_ep = first_ep;
-	i = 0;
-	do {
-		if (tmp_ep->command & IO_MASK(USB_EP_command, eof)) {
-			if ((i % interval) == 0) {
-				/* Insert the traffic ep after tmp_ep */
-				USB_EP_Desc_t *traffic_ep;
-				USB_SB_Desc_t *traffic_sb;
+			dbg_intr("Found EP to unlink for epid %d", epid);
+                        
+                        /* This is the one we should unlink. */
+			unlink_ep = next_ep;
 
-				traffic_ep = (USB_EP_Desc_t *)
-					kmem_cache_alloc(usb_desc_cache, GFP_KERNEL);
-				traffic_sb = (USB_SB_Desc_t *)
-					kmem_cache_alloc(usb_desc_cache, GFP_KERNEL);
-
-				traffic_ep->hw_len = 0;
-				traffic_ep->command = IO_FIELD(USB_EP_command, epid, epid) |
-					IO_STATE(USB_EP_command, enable, yes);
-				traffic_ep->sub = virt_to_phys(traffic_sb);
-
-				if (usb_pipein(urb->pipe)) {
-					traffic_sb->sw_len = urb->transfer_buffer_length ?
-						(urb->transfer_buffer_length - 1) / maxlen + 1 : 0;
-					traffic_sb->next = 0;
-					traffic_sb->buf = 0;
-					traffic_sb->command = IO_FIELD(USB_SB_command, rem,
-								       urb->transfer_buffer_length % maxlen) |
-						IO_STATE(USB_SB_command, tt, in) |
-						IO_STATE(USB_SB_command, eot, yes) |
-						IO_STATE(USB_SB_command, eol, yes);
-					
-				} else if (usb_pipeout(urb->pipe)) {
-					traffic_sb->sw_len = urb->transfer_buffer_length;
-					traffic_sb->next = 0;
-					traffic_sb->buf = virt_to_phys(urb->transfer_buffer);
-					traffic_sb->command = IO_FIELD(USB_SB_command, rem, 0) |
-						IO_STATE(USB_SB_command, tt, out) |
-						IO_STATE(USB_SB_command, eot, yes) |
-						IO_STATE(USB_SB_command, eol, yes) |
-						IO_STATE(USB_SB_command, full, yes);
-				}
+                        /* Actually unlink the EP from the DMA list. */
+			curr_ep->next = unlink_ep->next;
 
-				traffic_ep->nep = tmp_ep->nep;
-				tmp_ep->nep = virt_to_phys(traffic_ep);
-				dbg_intr("One ep sucessfully inserted");
-			}
-			i++;
+                        /* Wait until the DMA is no longer at this descriptor. */
+                        while (*R_DMA_CH8_SUB2_EP == virt_to_phys(unlink_ep));
+
+                        /* Now we are free to remove it and its SB descriptor.
+                           Note that it is assumed here that there is only one sb in the
+                           sb list for this ep. */
+			kmem_cache_free(usb_desc_cache, phys_to_virt(unlink_ep->sub));
+			kmem_cache_free(usb_desc_cache, (USB_EP_Desc_t *)unlink_ep);
 		}
-		tmp_ep = (USB_EP_Desc_t *)phys_to_virt(tmp_ep->nep);
-	} while (tmp_ep != first_ep);
 
-	set_bit(epid, (void *)&ep_really_active);
-	
-	*R_DMA_CH8_SUB2_CMD = IO_STATE(R_DMA_CH8_SUB2_CMD, cmd, start);
+		curr_ep = phys_to_virt(curr_ep->next);
+		
+	} while (curr_ep != first_ep);
 
-	DBFEXIT;
-	
-	return 0;
 }
 
-
-static void handle_intr_transfer_attn(int epid, int status)
+void etrax_usb_do_intr_recover(int epid)
 {
-	struct urb *old_urb;
+	USB_EP_Desc_t *first_ep, *tmp_ep;
 
 	DBFENTER;
+        
+	first_ep = (USB_EP_Desc_t *)phys_to_virt(*R_DMA_CH8_SUB2_EP);
+	tmp_ep = first_ep;
 
-	old_urb = URB_List[epid];
-	
-	/* if (status == 0 && IN) find data and copy to urb */
-	if (status == 0 && usb_pipein(old_urb->pipe)) {
-		unsigned long flags;
-		etrax_urb_priv_t *urb_priv;
-		struct list_head *entry;
-		struct in_chunk *in;
-
-		urb_priv = (etrax_urb_priv_t *)old_urb->hcpriv;
+        /* What this does is simply to walk the list of interrupt
+           ep descriptors and enable those that are disabled. */
+ 
+	do {
+		if (IO_EXTRACT(USB_EP_command, epid, tmp_ep->command) == epid &&
+		    !(tmp_ep->command & IO_MASK(USB_EP_command, enable))) {
+			tmp_ep->command |= IO_STATE(USB_EP_command, enable, yes);
+		}
 		
-		save_flags(flags);
-		cli();
+		tmp_ep = (USB_EP_Desc_t *)phys_to_virt(tmp_ep->next);
+		
+	} while (tmp_ep != first_ep);
 
-		list_for_each(entry, &urb_priv->ep_in_list) {
-			in = list_entry(entry, struct in_chunk, list);
-			memcpy(old_urb->transfer_buffer, in->data, in->length);
-			old_urb->actual_length = in->length;
-			old_urb->status = status;
-			
-			if (old_urb->complete) {
-				old_urb->complete(old_urb);
-			}
-			
-			list_del(entry);
-			kfree(in->data);
-			kfree(in);
-		}		
-		
-		restore_flags(flags);
-
-	} else if (status != 0) {
-		warn("Some sort of error for INTR EP !!!!");
-#ifdef ETRAX_USB_INTR_ERROR_FATAL
-		/* This means that an INTR error is fatal for that endpoint */
-		etrax_usb_unlink_intr_urb(old_urb);
-		old_urb->status = status;
-		if (old_urb->complete) {
-			old_urb->complete(old_urb);
-		}
-#else
-		/* In this case we reenable the disabled endpoint(s) */
-		etrax_usb_do_intr_recover(epid);
-#endif	
-	}
-	
-	DBFEXIT;
+
+        DBFEXIT;
 }
 
-static int etrax_rh_unlink_urb (struct urb *urb)
+static int etrax_rh_unlink_urb (urb_t *urb)
 {
 	etrax_hc_t *hc;
 	
@@ -711,16 +939,12 @@
 	return 0;
 }
 
-static void etrax_rh_send_irq(struct urb *urb)
+static void etrax_rh_send_irq(urb_t *urb)
 {
 	__u16 data = 0;
 	etrax_hc_t *hc = urb->dev->bus->hcpriv;
-//	static prev_wPortStatus_1 = 0;
-//	static prev_wPortStatus_2 = 0;
-	
 /*	DBFENTER; */
-	
-	
+
 /*
   dbg_rh("R_USB_FM_NUMBER   : 0x%08X", *R_USB_FM_NUMBER);
   dbg_rh("R_USB_FM_REMAINING: 0x%08X", *R_USB_FM_REMAINING);
@@ -730,6 +954,8 @@
 	data |= (hc->rh.wPortChange_2) ? (1 << 2) : 0;
 
 	*((__u16 *)urb->transfer_buffer) = cpu_to_le16(data);
+        /* FIXME: Why is actual_length set to 1 when data is 2 bytes?
+           Since only 1 byte is used, why not declare data as __u8? */
 	urb->actual_length = 1;
 	urb->status = 0;
 
@@ -744,7 +970,7 @@
 /*	DBFEXIT; */
 }
 
-static void etrax_rh_init_int_timer(struct urb *urb)
+static void etrax_rh_init_int_timer(urb_t *urb)
 {
 	etrax_hc_t *hc;
 	
@@ -755,6 +981,8 @@
 	init_timer(&hc->rh.rh_int_timer);
 	hc->rh.rh_int_timer.function = etrax_rh_int_timer_do;
 	hc->rh.rh_int_timer.data = (unsigned long)urb;
+        /* FIXME: Is the jiffies resolution enough? All intervals < 10 ms will be mapped
+           to 0, and the rest to the nearest lower 10 ms. */
 	hc->rh.rh_int_timer.expires = jiffies + ((HZ * hc->rh.interval) / 1000);
 	add_timer(&hc->rh.rh_int_timer);
 	
@@ -763,12 +991,12 @@
 
 static void etrax_rh_int_timer_do(unsigned long ptr)
 {
-	struct urb *urb;
+	urb_t *urb;
 	etrax_hc_t *hc;
 	
 /*	DBFENTER; */
 	
-	urb = (struct urb*)ptr;
+	urb = (urb_t*)ptr;
 	hc = urb->dev->bus->hcpriv;
 	
 	if (hc->rh.send) {
@@ -780,44 +1008,81 @@
 /*	DBFEXIT; */
 }
 
-static void etrax_usb_setup_epid(int epid, char devnum, char endpoint, char packsize, char slow, char out_traffic)
+static int etrax_usb_setup_epid(urb_t *urb)
 {
+	int epid;
+	char devnum, endpoint, out_traffic, slow;
+	int maxlen;
 	unsigned long flags;
-	
-	DBFENTER;
 
-	save_flags(flags);
-	cli();
+	DBFENTER;
 	
-	if (test_bit(epid, (void *)&ep_usage_bitmask)) {
-		restore_flags(flags);
-
-		warn("Trying to setup used epid %d", epid);
+	epid = etrax_usb_lookup_epid(urb);
+	if ((epid != -1)){
+		/* An epid that fits this urb has been found. */
 		DBFEXIT;
-		return;
+		return epid;
 	}
 	
-	set_bit(epid, (void *)&ep_usage_bitmask);
+	/* We must find and initiate a new epid for this urb. */
+	epid = etrax_usb_allocate_epid();
+
+	if (epid == -1) {
+		/* Failed to allocate a new epid. */
+		DBFEXIT;
+		return epid;
+	}
+
+	/* We now have a new epid to use. Initiate it. */
+	set_bit(epid, (void *)&epid_usage_bitmask);
 	
+	devnum = usb_pipedevice(urb->pipe);
+	endpoint = usb_pipeendpoint(urb->pipe);
+	slow = usb_pipeslow(urb->pipe);
+	maxlen = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe));
+        if (usb_pipetype(urb->pipe) == PIPE_CONTROL) {
+                /* We want both IN and OUT control traffic to be put on the same EP/SB list. */
+                out_traffic = 1;
+        } else {
+                out_traffic = usb_pipeout(urb->pipe);
+        }
+
+	save_flags(flags);
+	cli();
+
 	*R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid);
 	nop();
-	*R_USB_EPT_DATA = IO_STATE(R_USB_EPT_DATA, valid, yes) |
-		IO_FIELD(R_USB_EPT_DATA, ep, endpoint) |
-		IO_FIELD(R_USB_EPT_DATA, dev, devnum) |
-		IO_FIELD(R_USB_EPT_DATA, max_len, packsize) |
-		IO_FIELD(R_USB_EPT_DATA, low_speed, slow);
-
-	if (out_traffic)
-		set_bit(epid, (void *)&ep_out_traffic);
-	else
-		clear_bit(epid, (void *)&ep_out_traffic);
 
+	if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
+		*R_USB_EPT_DATA_ISO = IO_STATE(R_USB_EPT_DATA_ISO, valid, yes) |
+			/* FIXME: Change any to the actual port? */
+			IO_STATE(R_USB_EPT_DATA_ISO, port, any) |
+			IO_FIELD(R_USB_EPT_DATA_ISO, max_len, maxlen) |
+			IO_FIELD(R_USB_EPT_DATA_ISO, ep, endpoint) |
+			IO_FIELD(R_USB_EPT_DATA_ISO, dev, devnum);
+	} else {
+		*R_USB_EPT_DATA = IO_STATE(R_USB_EPT_DATA, valid, yes) |
+			IO_FIELD(R_USB_EPT_DATA, low_speed, slow) |
+			/* FIXME: Change any to the actual port? */
+			IO_STATE(R_USB_EPT_DATA, port, any) |
+			IO_FIELD(R_USB_EPT_DATA, max_len, maxlen) |
+			IO_FIELD(R_USB_EPT_DATA, ep, endpoint) |
+			IO_FIELD(R_USB_EPT_DATA, dev, devnum);
+	}	
+	
 	restore_flags(flags);
 
-	dbg_ep("Setting up ep_id %d with devnum %d, endpoint %d and max_len %d (%s)",
-	       epid, devnum, endpoint, packsize, out_traffic ? "OUT" : "IN");
+        if (out_traffic) {
+                set_bit(epid, (void *)&epid_out_traffic);
+        } else {
+                clear_bit(epid, (void *)&epid_out_traffic);
+	}
+
+	dbg_epid("Setting up epid %d with devnum %d, endpoint %d and max_len %d (%s)",
+                 epid, devnum, endpoint, maxlen, out_traffic ? "OUT" : "IN");
 
 	DBFEXIT;
+	return epid;
 }
 
 static void etrax_usb_free_epid(int epid)
@@ -826,7 +1091,7 @@
 	
 	DBFENTER;
 
-	if (!test_bit(epid, (void *)&ep_usage_bitmask)) {
+	if (!test_bit(epid, (void *)&epid_usage_bitmask)) {
 		warn("Trying to free unused epid %d", epid);
 		DBFEXIT;
 		return;
@@ -837,56 +1102,81 @@
 
 	*R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid);
 	nop();
-	while (*R_USB_EPT_DATA & IO_MASK(R_USB_EPT_DATA, hold))
-		printk("+");
+	while (*R_USB_EPT_DATA & IO_MASK(R_USB_EPT_DATA, hold));
+        /* This will, among other things, set the valid field to 0. */
 	*R_USB_EPT_DATA = 0;
-	clear_bit(epid, (void *)&ep_usage_bitmask);
-
 	restore_flags(flags);
 
-	dbg_ep("epid: %d freed", epid);
+	clear_bit(epid, (void *)&epid_usage_bitmask);
+
+
+	dbg_epid("Freed epid %d", epid);
 	
 	DBFEXIT;
 }
 
-
-static int etrax_usb_lookup_epid(unsigned char devnum, char endpoint, char slow, int maxp, char out_traffic)
+static int etrax_usb_lookup_epid(urb_t *urb)
 {
 	int i;
-	unsigned long flags;
 	__u32 data;
-	
+	char devnum, endpoint, slow, out_traffic;
+	int maxlen;
+	unsigned long flags;
+
 	DBFENTER;
 
-	save_flags(flags);
-	cli();
+	devnum = usb_pipedevice(urb->pipe);
+	endpoint = usb_pipeendpoint(urb->pipe);
+	slow = usb_pipeslow(urb->pipe);
+	maxlen = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe));
+        if (usb_pipetype(urb->pipe) == PIPE_CONTROL) {
+                /* We want both IN and OUT control traffic to be put on the same EP/SB list. */
+                out_traffic = 1;
+        } else {
+                out_traffic = usb_pipeout(urb->pipe);
+        }
+        
+	/* Step through att epids. */
+	for (i = 0; i < NBR_OF_EPIDS; i++) {
+		if (test_bit(i, (void *)&epid_usage_bitmask) &&
+                    test_bit(i, (void *)&epid_out_traffic) == out_traffic) {
 
-	/* Skip first ep_id since it is reserved when intr. or iso traffic is used */
-	for (i = 0; i < NBR_OF_EP_DESC; i++) {
-		if (test_bit(i, (void *)&ep_usage_bitmask) &&
-		    test_bit(i, (void *)&ep_out_traffic) == out_traffic) {
+			save_flags(flags);
+			cli();
 			*R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, i);
 			nop();
-			data = *R_USB_EPT_DATA;
-			if ((IO_MASK(R_USB_EPT_DATA, valid) & data) &&
-			    (IO_EXTRACT(R_USB_EPT_DATA, dev, data) == devnum) &&
-			    (IO_EXTRACT(R_USB_EPT_DATA, ep, data) == endpoint) &&
-			    (IO_EXTRACT(R_USB_EPT_DATA, low_speed, data) == slow) &&
-			    (IO_EXTRACT(R_USB_EPT_DATA, max_len, data) == maxp)) {
+
+			if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
+				data = *R_USB_EPT_DATA_ISO;
 				restore_flags(flags);
-	
-				dbg_ep("Found ep_id %d for devnum %d, endpoint %d (%s)",
-				       i, devnum, endpoint, out_traffic ? "OUT" : "IN");
-				DBFEXIT;
-				return i;
+
+				if ((IO_MASK(R_USB_EPT_DATA_ISO, valid) & data) &&
+                                    (IO_EXTRACT(R_USB_EPT_DATA_ISO, dev, data) == devnum) &&
+                                    (IO_EXTRACT(R_USB_EPT_DATA_ISO, ep, data) == endpoint) &&
+                                    (IO_EXTRACT(R_USB_EPT_DATA_ISO, max_len, data) == maxlen)) {
+                                        dbg_epid("Found epid %d for devnum %d, endpoint %d (%s)",
+                                                 i, devnum, endpoint, out_traffic ? "OUT" : "IN");
+                                        DBFEXIT;
+                                        return i;
+				}
+			} else {
+				data = *R_USB_EPT_DATA;
+				restore_flags(flags);
+
+				if ((IO_MASK(R_USB_EPT_DATA, valid) & data) &&
+				    (IO_EXTRACT(R_USB_EPT_DATA, dev, data) == devnum) &&
+				    (IO_EXTRACT(R_USB_EPT_DATA, ep, data) == endpoint) &&
+				    (IO_EXTRACT(R_USB_EPT_DATA, low_speed, data) == slow) &&
+				    (IO_EXTRACT(R_USB_EPT_DATA, max_len, data) == maxlen)) {	
+					dbg_epid("Found epid %d for devnum %d, endpoint %d (%s)",
+                                                 i, devnum, endpoint, out_traffic ? "OUT" : "IN");
+					DBFEXIT;
+					return i;
+				}
 			}
 		}
 	}
 
-	restore_flags(flags);
-	
-	dbg_ep("Found no ep_id for devnum %d, endpoint %d (%s)",
-	       devnum, endpoint, out_traffic ? "OUT" : "IN");
 	DBFEXIT;
 	return -1;
 }
@@ -897,434 +1187,993 @@
 	
 	DBFENTER;
 
-	for (i = 0; i < NBR_OF_EP_DESC; i++) {
-		if (!test_bit(i, (void *)&ep_usage_bitmask)) {
-			dbg_ep("Found free ep_id at %d", i);
+	for (i = 0; i < NBR_OF_EPIDS; i++) {
+		if (!test_bit(i, (void *)&epid_usage_bitmask)) {
+			dbg_epid("Found free epid %d", i);
 			DBFEXIT;
 			return i;
 		}
 	}
 
-	dbg_ep("Found no free ep_id's");
+	dbg_epid("Found no free epids");
 	DBFEXIT;
 	return -1;
 }
 
-static int etrax_usb_submit_bulk_urb(struct urb *urb)
+static int etrax_usb_submit_urb(urb_t *urb)
 {
-	int epid;
-	char devnum;
-	char endpoint;
-	char maxlen;
-	char out_traffic;
-	char slow;
-
-	struct urb *tmp_urb;
-	
-	unsigned long flags;
+	etrax_hc_t *hc;
+	int ret = -EINVAL;
 	
 	DBFENTER;
 
-	devnum = usb_pipedevice(urb->pipe);
-	endpoint = usb_pipeendpoint(urb->pipe);
-	maxlen = usb_maxpacket(urb->dev, urb->pipe,
-			       usb_pipeout(urb->pipe));
-	out_traffic = usb_pipeout(urb->pipe);
-	slow = usb_pipeslow(urb->pipe);
+        if (!urb->dev || !urb->dev->bus) {
+                return -ENODEV;
+        }
+	if (urb->next != NULL) {
+                /* Is it possible for urb to be the head of a list of urbs (via the urb's
+                   next pointer), used for example in drivers for isochronous traffic.
+                   I haven't seen a device driver that relies on it being used for submit
+                   or unlink, so we warn about it and ignore it. */
+		warn("Urbs are linked, ignoring.");
+	}
+        if (urb->timeout) {
+                /* FIXME. */
+                warn("urb->timeout specified, ignoring.");
+        }
+        
+	hc = (etrax_hc_t*)urb->dev->bus->hcpriv;
 	
-	epid = etrax_usb_lookup_epid(devnum, endpoint, slow, maxlen, out_traffic);
-	if (epid == -1) {
-		epid = etrax_usb_allocate_epid();
-		if (epid == -1) {
-			/* We're out of endpoints, return some error */
-			err("We're out of endpoints");
-			return -ENOMEM;
+	if (usb_pipedevice(urb->pipe) == hc->rh.devnum) {
+		/* This request is for the Virtual Root Hub. */
+		ret = etrax_rh_submit_urb(urb);
+
+	} else if (usb_pipetype(urb->pipe) == PIPE_BULK) {
+
+		ret = etrax_usb_submit_bulk_urb(urb);
+
+	} else if (usb_pipetype(urb->pipe) == PIPE_CONTROL) {
+
+		ret = etrax_usb_submit_ctrl_urb(urb);
+
+	} else if (usb_pipetype(urb->pipe) == PIPE_INTERRUPT) {
+		int bustime;
+                
+		if (urb->bandwidth == 0) {
+			bustime = usb_check_bandwidth(urb->dev, urb);
+			if (bustime < 0) {
+				ret = bustime;
+			} else {
+				ret = etrax_usb_submit_intr_urb(urb);
+				if (ret == 0)
+                                        usb_claim_bandwidth(urb->dev, urb, bustime, 0);
+			}
+                } else {
+                        /* Bandwidth already set. */
+                        ret = etrax_usb_submit_intr_urb(urb);
+		}
+
+	} else if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
+                int bustime;
+        
+		if (urb->bandwidth == 0) {
+			bustime = usb_check_bandwidth(urb->dev, urb);
+			if (bustime < 0) {
+				ret = bustime;
+			} else {
+				ret = etrax_usb_submit_isoc_urb(urb);
+				if (ret == 0)
+                                        usb_claim_bandwidth(urb->dev, urb, bustime, 0);
+			}
+                } else {
+                        /* Bandwidth already set. */
+                        ret = etrax_usb_submit_isoc_urb(urb);
 		}
-		/* Now we have to fill in this ep */
-		etrax_usb_setup_epid(epid, devnum, endpoint, maxlen, slow, out_traffic);
 	}
-	/* Ok, now we got valid endpoint, lets insert some traffic */
 
-	urb->status = -EINPROGRESS;
+	DBFEXIT;
 
-	save_flags(flags);
-	cli();
-	
-	if (URB_List[epid]) {
-		/* Find end of list and add */
-		for (tmp_urb = URB_List[epid]; tmp_urb->next; tmp_urb = tmp_urb->next)
-			dump_urb(tmp_urb);
+	return ret;
+}
+
+static int etrax_usb_unlink_urb(urb_t *urb)
+{
+        etrax_hc_t *hc;
+        etrax_urb_priv_t *urb_priv;
+        int epid;
+
+	DBFENTER;
+        
+        if (!urb) {
+                return -EINVAL;
+        }        
+        if (!urb->dev || !urb->dev->bus) {
+                return -ENODEV;
+        }
+        if (!urb->hcpriv) {
+                /* This happens if a device driver calls unlink on an urb that
+                   was never submitted (lazy driver). */
+                return 0;
+        }
+        if (urb->transfer_flags & USB_ASYNC_UNLINK) {
+                /* FIXME. */
+                /* If USB_ASYNC_UNLINK is set:
+                   unlink
+                   move to a separate urb list
+                   call complete at next sof with ECONNRESET
+                   
+                   If not:
+                   wait 1 ms
+                   unlink
+                   call complete with ENOENT
+                */
+                warn("USB_ASYNC_UNLINK set, ignoring.");
+        }
+        
+        /* One might think that urb->status = -EINPROGRESS would be a requirement for unlinking,
+           but that doesn't work for interrupt and isochronous traffic since they are completed
+           repeatedly, and urb->status is set then. That may in itself be a bug though. */
+
+        hc = urb->dev->bus->hcpriv;
+        urb_priv = (etrax_urb_priv_t *)urb->hcpriv;
+        epid = urb_priv->epid;
+
+        /* Set the urb status (synchronous unlink). */
+        urb->status = -ENOENT;
+        urb_priv->urb_state = UNLINK;
+        
+	if (usb_pipedevice(urb->pipe) == hc->rh.devnum) {
+		int ret;
+		ret = etrax_rh_unlink_urb(urb);
+		DBFEXIT;
+		return ret;
 
-		tmp_urb->next = urb;
-		restore_flags(flags);
-	} else {
-		/* If this is the first URB, add the URB and do HW add */
-		URB_List[epid] = urb;
-		restore_flags(flags);
-		etrax_usb_do_bulk_hw_add(urb, epid, maxlen);
-	}
+	} else if (usb_pipetype(urb->pipe) == PIPE_BULK) {
+
+                dbg_bulk("Unlink of bulk urb (0x%lx)", (unsigned long)urb);
+
+                if (TxBulkEPList[epid].command & IO_MASK(USB_EP_command, enable)) {
+                        /* The EP was enabled, disable it and wait. */
+                        TxBulkEPList[epid].command &= ~IO_MASK(USB_EP_command, enable);
+                        
+                        /* Ah, the luxury of busy-wait. */
+                        while (*R_DMA_CH8_SUB0_EP == virt_to_phys(&TxBulkEPList[epid]));
+                }
+		/* Kicking dummy list out of the party. */
+		TxBulkEPList[epid].next = virt_to_phys(&TxBulkEPList[(epid + 1) % NBR_OF_EPIDS]);
+
+        } else if (usb_pipetype(urb->pipe) == PIPE_CONTROL) {
+
+                dbg_ctrl("Unlink of ctrl urb (0x%lx)", (unsigned long)urb);
+
+                if (TxCtrlEPList[epid].command & IO_MASK(USB_EP_command, enable)) {
+                        /* The EP was enabled, disable it and wait. */
+                        TxCtrlEPList[epid].command &= ~IO_MASK(USB_EP_command, enable);
+                        
+                        /* Ah, the luxury of busy-wait. */
+                        while (*R_DMA_CH8_SUB1_EP == virt_to_phys(&TxCtrlEPList[epid]));
+                }
+
+	} else if (usb_pipetype(urb->pipe) == PIPE_INTERRUPT) {
+
+                dbg_intr("Unlink of intr urb (0x%lx)", (unsigned long)urb);
+
+                /* Separate function because it's a tad more complicated. */
+		etrax_usb_unlink_intr_urb(urb);
+
+        } else if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
 
+                dbg_isoc("Unlink of isoc urb (0x%lx)", (unsigned long)urb);
+
+                if (TxIsocEPList[epid].command & IO_MASK(USB_EP_command, enable)) {
+                        /* The EP was enabled, disable it and wait. */
+                        TxIsocEPList[epid].command &= ~IO_MASK(USB_EP_command, enable);
+                        
+                        /* Ah, the luxury of busy-wait. */
+                        while (*R_DMA_CH8_SUB3_EP == virt_to_phys(&TxIsocEPList[epid]));
+                }
+        }
+
+        /* Note that we need to remove the urb from the urb list *before* removing its SB
+           descriptors. (This means that the isoc eof handler might get a null urb when we
+           are unlinking the last urb.) */
+
+        urb_list_del(urb, epid);
+
+        if (usb_pipetype(urb->pipe) == PIPE_BULK) {
+
+                TxBulkEPList[epid].sub = 0;
+                etrax_remove_from_sb_list(urb);
+
+        } else if (usb_pipetype(urb->pipe) == PIPE_CONTROL) {
+
+                TxCtrlEPList[epid].sub = 0;
+                etrax_remove_from_sb_list(urb);
+
+        } else if (usb_pipetype(urb->pipe) == PIPE_INTERRUPT) {
+
+                /* Sanity check (should never happen). */
+                assert(urb_list_empty(epid));
+
+                /* Release allocated bandwidth. */
+                usb_release_bandwidth(urb->dev, urb, 0);
+
+        } else if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
+
+                if (usb_pipeout(urb->pipe)) {
+                        /* FIXME: If the SB list isn't empty at this point, we need to set up 
+                           the EP descriptor again. */
+                } else {
+                        /* For in traffic there is only one SB descriptor for each EP even
+                           though there may be several urbs (all urbs point at the same SB). */
+                        if (urb_list_empty(epid)) {
+                                /* No more urbs, remove the SB. */
+                                TxIsocEPList[epid].sub = 0;
+                                etrax_remove_from_sb_list(urb);
+                        }
+                }
+                /* Release allocated bandwidth. */
+                usb_release_bandwidth(urb->dev, urb, 1);
+        } 
+        
+        /* Must be done before calling completion handler. */
+        kfree(urb_priv);
+        urb->hcpriv = 0;
+
+        if (urb->complete) {
+                urb->complete(urb);
+        }
+
+        /* Free the epid if urb list is empty. */
+        if (urb_list_empty(epid)) {
+                etrax_usb_free_epid(epid);
+        }
+
+        DBFEXIT;
+        return 0;
+}
+
+static int etrax_usb_get_frame_number(struct usb_device *usb_dev)
+{
+	DBFENTER;
 	DBFEXIT;
+	return (*R_USB_FM_NUMBER & 0x7ff);
+}
 
+static int etrax_usb_allocate_dev(struct usb_device *usb_dev)
+{  
+	DBFENTER;
+	DBFEXIT;
 	return 0;
 }
 
-static void etrax_usb_do_bulk_hw_add(struct urb *urb, int epid, char maxlen)
+static int etrax_usb_deallocate_dev(struct usb_device *usb_dev)
 {
-	USB_SB_Desc_t *sb_desc_1;
+	DBFENTER;
+	DBFEXIT;
+	return 0;
+}
+
+static void etrax_usb_tx_interrupt(int irq, void *vhc, struct pt_regs *regs)
+{
+	DBFENTER;
+
+        /* This interrupt handler could be used when unlinking EP descriptors. */
+
+	if (*R_IRQ_READ2 & IO_MASK(R_IRQ_READ2, dma8_sub0_descr)) {
+		USB_EP_Desc_t *ep;
+
+		//dbg_bulk("dma8_sub0_descr (BULK) intr.");
+
+		/* It should be safe clearing the interrupt here, since we don't expect to get a new
+		   one until we restart the bulk channel. */
+		*R_DMA_CH8_SUB0_CLR_INTR = IO_STATE(R_DMA_CH8_SUB0_CLR_INTR, clr_descr, do);
 
+		/* Wait while the DMA is running (though we don't expect it to be). */
+		while (*R_DMA_CH8_SUB0_CMD & IO_MASK(R_DMA_CH8_SUB0_CMD, cmd));
+
+		/* Advance the DMA to the next EP descriptor. */
+		ep = (USB_EP_Desc_t *)phys_to_virt(*R_DMA_CH8_SUB0_EP);
+
+		//dbg_bulk("descr intr: DMA is at 0x%lx", (unsigned long)ep);
+
+		/* ep->next is already a physical address; no need for a virt_to_phys. */
+		*R_DMA_CH8_SUB0_EP = ep->next;
+
+		/* Start the DMA bulk channel again. */
+		*R_DMA_CH8_SUB0_CMD = IO_STATE(R_DMA_CH8_SUB0_CMD, cmd, start);
+	}
+	if (*R_IRQ_READ2 & IO_MASK(R_IRQ_READ2, dma8_sub1_descr)) {
+		dbg_ctrl("dma8_sub1_descr (CTRL) intr.");
+		*R_DMA_CH8_SUB1_CLR_INTR = IO_STATE(R_DMA_CH8_SUB1_CLR_INTR, clr_descr, do);
+	}
+	if (*R_IRQ_READ2 & IO_MASK(R_IRQ_READ2, dma8_sub2_descr)) {
+		dbg_intr("dma8_sub2_descr (INTR) intr.");
+		*R_DMA_CH8_SUB2_CLR_INTR = IO_STATE(R_DMA_CH8_SUB2_CLR_INTR, clr_descr, do);
+	}
+	if (*R_IRQ_READ2 & IO_MASK(R_IRQ_READ2, dma8_sub3_descr)) {
+		dbg_isoc("dma8_sub3_descr (ISOC) intr.");
+		*R_DMA_CH8_SUB3_CLR_INTR = IO_STATE(R_DMA_CH8_SUB3_CLR_INTR, clr_descr, do);
+	}
+        
+	DBFEXIT;
+}
+
+static void etrax_usb_rx_interrupt(int irq, void *vhc, struct pt_regs *regs)
+{
+	urb_t *urb;
 	etrax_urb_priv_t *urb_priv;
+	int epid = 0;
+        unsigned long flags;
 
-	unsigned long flags;
+        /* Isoc diagnostics. */
+        static int curr_fm = 0;
+        static int prev_fm = 0;
+
+        DBFENTER;
+
+        /* Clear this interrupt. */
+	*R_DMA_CH9_CLR_INTR = IO_STATE(R_DMA_CH9_CLR_INTR, clr_eop, do);
+
+        /* Note that this while loop assumes that all packets span only
+           one rx descriptor. */
+
+        /* The reason we cli here is that we call the driver's callback functions. */
+        save_flags(flags);
+        cli();
+
+
+ 	while (myNextRxDesc->status & IO_MASK(USB_IN_status, eop)) {
+
+                epid = IO_EXTRACT(USB_IN_status, epid, myNextRxDesc->status);
+                urb = urb_list_first(epid);
+
+                //printk("eop for epid %d, first urb 0x%lx\n", epid, (unsigned long)urb);
+                
+                if (!urb) {
+                        err("No urb for epid %d in rx interrupt", epid);
+			__dump_ept_data(epid);
+                        goto skip_out;
+                }
+
+                /* Note that we cannot indescriminately assert(usb_pipein(urb->pipe)) since
+                   ctrl pipes are not. */
+                
+		if (myNextRxDesc->status & IO_MASK(USB_IN_status, error)) {
+			warn("error in rx desc->status, epid %d, first urb = 0x%lx", 
+                             epid, (unsigned long)urb);
+			__dump_in_desc(myNextRxDesc);
+                        etrax_usb_complete_urb(urb, -EPROTO);
+			goto skip_out;
+		}
+
+                urb_priv = (etrax_urb_priv_t *)urb->hcpriv;
+                assert(urb_priv);
+                
+                if ((usb_pipetype(urb->pipe) == PIPE_BULK) || 
+                    (usb_pipetype(urb->pipe) == PIPE_CONTROL) ||
+                    (usb_pipetype(urb->pipe) == PIPE_INTERRUPT)) {
+
+                        if (myNextRxDesc->status & IO_MASK(USB_IN_status, nodata)) {
+                                /* We get nodata for empty data transactions, and the rx descriptor's
+                                   hw_len field is not valid in that case. No data to copy in other 
+                                   words. */
+                        } else {
+                                /* Make sure the data fits in the buffer. */
+                                assert(urb_priv->rx_offset + myNextRxDesc->hw_len 
+                                       <= urb->transfer_buffer_length);
+
+                                memcpy(urb->transfer_buffer + urb_priv->rx_offset,
+                                       phys_to_virt(myNextRxDesc->buf), myNextRxDesc->hw_len);	
+                                urb_priv->rx_offset += myNextRxDesc->hw_len;
+                        }
+
+                        if (myNextRxDesc->status & IO_MASK(USB_IN_status, eot)) {
+                                etrax_usb_complete_urb(urb, 0);
+                        }
+
+                } else if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
+                        
+                        iso_packet_descriptor_t *packet;
+
+                        if (urb_priv->urb_state == UNLINK) {
+                                info("Ignoring rx data for urb being unlinked.");
+                                goto skip_out;
+                        } else if (urb_priv->urb_state == NOT_STARTED) {
+                                info("What? Got rx data for urb that isn't started?");
+                                goto skip_out;
+                        }
+                                
+                        packet = &urb->iso_frame_desc[urb_priv->isoc_packet_counter];
+                        packet->status = 0;
+
+                         if (myNextRxDesc->status & IO_MASK(USB_IN_status, nodata)) {
+                                /* We get nodata for empty data transactions, and the rx descriptor's
+                                   hw_len field is not valid in that case. We copy 0 bytes however to
+                                   stay in synch. */
+                                packet->actual_length = 0;                                
+                        } else {
+                                packet->actual_length = myNextRxDesc->hw_len;
+                                /* Make sure the data fits in the buffer. */
+                                assert(packet->actual_length <= packet->length);
+                                
+                                memcpy(urb->transfer_buffer + packet->offset,
+                                       phys_to_virt(myNextRxDesc->buf), packet->actual_length);
+                        }
+
+                        /* Increment the packet counter. */
+                        urb_priv->isoc_packet_counter++;	
+                        
+                        /* Note that we don't care about the eot field in the rx descriptor's status.
+                           It will always be set for isoc traffic. */
+			if (urb->number_of_packets == urb_priv->isoc_packet_counter) {
+
+                                /* Out-of-synch diagnostics. */
+                                curr_fm = (*R_USB_FM_NUMBER & 0x7ff);
+                                if (((prev_fm + urb_priv->isoc_packet_counter) % (0x7ff + 1)) != curr_fm) {
+                                        warn("Out of synch? Previous frame = %d, current frame = %d",
+                                             prev_fm, curr_fm);
+                                }
+                                prev_fm = curr_fm;
+
+				/* Complete the urb with status OK. */
+				etrax_usb_complete_isoc_urb(urb, 0);
+
+				/* Must set this to 0 since this urb is still active after
+				   completion. */
+				urb_priv->isoc_packet_counter = 0;
+			}
+                }
+                
+	skip_out:
+
+                /* DMA IN cache bug. Flush the DMA IN buffer from the cache. (struct etrax_dma_descr
+		   has the same layout as USB_IN_Desc for the relevant fields.) */
+                prepare_rx_descriptor((struct etrax_dma_descr*)myNextRxDesc);
+
+		myPrevRxDesc = myNextRxDesc;
+		myPrevRxDesc->command |= IO_MASK(USB_IN_command, eol);
+		myLastRxDesc->command &= ~IO_MASK(USB_IN_command, eol);
+		myLastRxDesc = myPrevRxDesc;
+
+		myNextRxDesc->status = 0;
+		myNextRxDesc = phys_to_virt(myNextRxDesc->next);
+	}
+
+        restore_flags(flags);
+        
+        DBFEXIT;
+        
+}
+
+
+/* This function will unlink the SB descriptors associated with this urb. */
+static int etrax_remove_from_sb_list(urb_t *urb)
+{
+	USB_SB_Desc_t *next_sb, *first_sb, *last_sb;
+	etrax_urb_priv_t *urb_priv;
+	int i = 0;
 
 	DBFENTER;
 
-	urb_priv = kmalloc(sizeof(etrax_urb_priv_t), GFP_KERNEL);
-	sb_desc_1 = (USB_SB_Desc_t*)kmem_cache_alloc(usb_desc_cache, GFP_KERNEL);
+	urb_priv = (etrax_urb_priv_t *)urb->hcpriv; 
+	assert(urb_priv);
 
-	if (usb_pipeout(urb->pipe)) {
+        /* Just a sanity check. Since we don't fiddle with the DMA list the EP descriptor
+	   doesn't really need to be disabled, it's just that we expect it to be. */
+        if (usb_pipetype(urb->pipe) == PIPE_BULK) {
+                assert(!(TxBulkEPList[urb_priv->epid].command & IO_MASK(USB_EP_command, enable)));
+        } else if (usb_pipetype(urb->pipe) == PIPE_CONTROL) {
+                assert(!(TxCtrlEPList[urb_priv->epid].command & IO_MASK(USB_EP_command, enable)));
+        } 
 
-		dbg_bulk("Bulk transfer for epid %d is OUT", epid);
-		dbg_bulk("transfer_buffer_length == %d", urb->transfer_buffer_length);
-		dbg_bulk("actual_length == %d", urb->actual_length);
+	first_sb = urb_priv->first_sb;
+	last_sb = urb_priv->last_sb;
 	
+        assert(first_sb);
+        assert(last_sb);
+        
+	while (first_sb != last_sb) {
+		next_sb = (USB_SB_Desc_t *)phys_to_virt(first_sb->next);
+		kmem_cache_free(usb_desc_cache, first_sb);
+		first_sb = next_sb;
+		i++;
+	}
+	kmem_cache_free(usb_desc_cache, last_sb);
+	i++;
+	dbg_sb("%d SB descriptors freed", i);
+	/* Compare i with urb->number_of_packets for Isoc traffic. 
+	   Should be same when calling unlink_urb */
+	
+	DBFEXIT;
+	
+	return i;
+}
+
+static int etrax_usb_submit_bulk_urb(urb_t *urb)
+{
+	int epid;
+        int empty;
+        unsigned long flags;
+        
+	DBFENTER;
+
+        /* Epid allocation, empty check and list add must be protected. 
+           Read about this in etrax_usb_submit_ctrl_urb. */
+
+        spin_lock_irqsave(&urb_list_lock, flags);
+	epid = etrax_usb_setup_epid(urb);
+	if (epid == -1) {
+		DBFEXIT;
+                spin_unlock_irqrestore(&urb_list_lock, flags);
+		return -ENOMEM;
+	}
+        empty = urb_list_empty(epid);
+        urb_list_add(urb, epid);
+        spin_unlock_irqrestore(&urb_list_lock, flags);
+
+        /* USB_QUEUE_BULK is UHCI-specific, but we warn anyway. */
+        if (!(urb->transfer_flags & USB_QUEUE_BULK) && !empty) {
+                warn("USB_QUEUE_BULK is not set and urb queue is not empty, ignoring.");
+        }
+
+        dbg_bulk("Adding bulk %s urb 0x%lx to %s list, epid %d", 
+                 usb_pipein(urb->pipe) ? "IN" : "OUT", (unsigned long)urb, empty ? "empty" : "", epid);
+
+        /* Mark the urb as being in progress. */
+	urb->status = -EINPROGRESS;
+
+        if (empty) {
+                etrax_usb_add_to_bulk_sb_list(urb, epid);
+        }
+	        
+	DBFEXIT;
+
+	return 0;
+}
+
+static void etrax_usb_add_to_bulk_sb_list(urb_t *urb, int epid)
+{
+	USB_SB_Desc_t *sb_desc;
+	etrax_urb_priv_t *urb_priv;
+	unsigned long flags;
+	char maxlen;
+
+	DBFENTER;
+
+	dbg_bulk("etrax_usb_add_to_bulk_sb_list, urb 0x%lx", (unsigned long)urb);
+
+	maxlen = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe));
+	
+	urb_priv = kmalloc(sizeof(etrax_urb_priv_t), KMALLOC_FLAG);
+	assert(urb_priv != NULL);
+        /* This sets rx_offset to 0. */
+        memset(urb_priv, 0, sizeof(etrax_urb_priv_t));
+        
+	sb_desc = (USB_SB_Desc_t*)kmem_cache_alloc(usb_desc_cache, SLAB_FLAG);
+	assert(sb_desc != NULL);
+	memset(sb_desc, 0, sizeof(USB_SB_Desc_t));
+
+
+	if (usb_pipeout(urb->pipe)) {
+                
+		dbg_bulk("Grabbing bulk OUT, urb 0x%lx, epid %d", (unsigned long)urb, epid);
+                
+                /* This is probably a sanity check of the bulk transaction length
+                   not being larger than 64 kB. */
 		if (urb->transfer_buffer_length > 0xffff) {
-			panic(__FILE__ __FUNCTION__ ":urb->transfer_buffer_length > 0xffff\n");
+			panic("urb->transfer_buffer_length > 0xffff");
 		}
 
-		sb_desc_1->sw_len = urb->transfer_buffer_length;  /* was actual_length */
-		sb_desc_1->command = IO_FIELD(USB_SB_command, rem, 0) |
-			IO_STATE(USB_SB_command, tt, out) |
+		sb_desc->sw_len = urb->transfer_buffer_length;
 
-#if 0
-			IO_STATE(USB_SB_command, full, no) |
-#else
-			IO_STATE(USB_SB_command, full, yes) |
-#endif
+                /* The rem field is don't care if it's not a full-length transfer, so setting
+                   it shouldn't hurt. Also, rem isn't used for OUT traffic. */
+		sb_desc->command = (IO_FIELD(USB_SB_command, rem, 0) |
+                                    IO_STATE(USB_SB_command, tt, out) |
+                                    IO_STATE(USB_SB_command, eot, yes) |
+                                    IO_STATE(USB_SB_command, eol, yes));
+
+                /* The full field is set to yes, even if we don't actually check that this is
+                   a full-length transfer (i.e., that transfer_buffer_length % maxlen = 0).
+                   Setting full prevents the USB controller from sending an empty packet in
+                   that case.  However, if USB_ZERO_PACKET was set we want that. */
+                if (!(urb->transfer_flags & USB_ZERO_PACKET)) {
+                        sb_desc->command |= IO_STATE(USB_SB_command, full, yes);
+                }
 
-			IO_STATE(USB_SB_command, eot, yes) |
-			IO_STATE(USB_SB_command, eol, yes);
-		
-		dbg_bulk("transfer_buffer is at 0x%08X", urb->transfer_buffer);
-		
-		sb_desc_1->buf = virt_to_phys(urb->transfer_buffer);
-		sb_desc_1->next = 0;
+		sb_desc->buf = virt_to_phys(urb->transfer_buffer);
+		sb_desc->next = 0;
 		
 	} else if (usb_pipein(urb->pipe)) {
 
-		dbg_bulk("Transfer for epid %d is IN", epid);
-		dbg_bulk("transfer_buffer_length = %d", urb->transfer_buffer_length);
-		dbg_bulk("rem is calculated to %d", urb->transfer_buffer_length % maxlen);
-		
-		sb_desc_1->sw_len = urb->transfer_buffer_length ?
+                dbg_bulk("Grabbing bulk IN, urb 0x%lx, epid %d", (unsigned long)urb, epid);
+
+		sb_desc->sw_len = urb->transfer_buffer_length ?
 			(urb->transfer_buffer_length - 1) / maxlen + 1 : 0;
-		dbg_bulk("sw_len got %d", sb_desc_1->sw_len);
-		dbg_bulk("transfer_buffer is at 0x%08X", urb->transfer_buffer);
-		
-		sb_desc_1->command =
-			IO_FIELD(USB_SB_command, rem,
-				 urb->transfer_buffer_length % maxlen) |
-			IO_STATE(USB_SB_command, tt, in) |
-			IO_STATE(USB_SB_command, eot, yes) |
-			IO_STATE(USB_SB_command, eol, yes);
 		
-		sb_desc_1->buf = 0;
-		sb_desc_1->next = 0;
+                /* The rem field is don't care if it's not a full-length transfer, so setting
+                   it shouldn't hurt. */
+		sb_desc->command =
+			(IO_FIELD(USB_SB_command, rem,
+                                  urb->transfer_buffer_length % maxlen) |
+                         IO_STATE(USB_SB_command, tt, in) |
+                         IO_STATE(USB_SB_command, eot, yes) |
+                         IO_STATE(USB_SB_command, eol, yes));
+		
+		sb_desc->buf = 0;
+		sb_desc->next = 0;
+	}
+	
+	urb_priv->first_sb = sb_desc;
+	urb_priv->last_sb = sb_desc;
+	urb_priv->epid = epid;
 
-		urb_priv->rx_offset = 0;
-		urb_priv->eot = 0;
-	}
-	
-	urb_priv->first_sb = sb_desc_1;
-	
-	urb->hcpriv = (void *)urb_priv;
+	urb->hcpriv = urb_priv;
 	
-	/* Reset toggle bits and reset error count, remeber to di and ei */
-	/* Warning: it is possible that this locking doesn't work with bottom-halves */
-
+	/* Reset toggle bits and reset error count. */
 	save_flags(flags);
 	cli();
 
-	*R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid); nop();
+	*R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid); 
+        nop();
+
+        /* FIXME: Is this a special case since the hold field is checked,
+           or should we check hold in a lot of other cases as well? */
 	if (*R_USB_EPT_DATA & IO_MASK(R_USB_EPT_DATA, hold)) {
-		panic("Hold was set in %s\n", __FUNCTION__);
+		panic("Hold was set in %s", __FUNCTION__);
 	}
 
+        /* Reset error counters (regardless of which direction this traffic is). */
 	*R_USB_EPT_DATA &=
 		~(IO_MASK(R_USB_EPT_DATA, error_count_in) |
 		  IO_MASK(R_USB_EPT_DATA, error_count_out));
 	
+        /* Software must preset the toggle bits. */
 	if (usb_pipeout(urb->pipe)) {
 		char toggle =
-		usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe));
+                        usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe));
 		*R_USB_EPT_DATA &= ~IO_MASK(R_USB_EPT_DATA, t_out);
 		*R_USB_EPT_DATA |= IO_FIELD(R_USB_EPT_DATA, t_out, toggle);
 	} else {
 		char toggle =
-		usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe));
+                        usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe));
 		*R_USB_EPT_DATA &= ~IO_MASK(R_USB_EPT_DATA, t_in);
 		*R_USB_EPT_DATA |= IO_FIELD(R_USB_EPT_DATA, t_in, toggle);
 	}
-		
-	/* Enable the EP descr. */
-
-	set_bit(epid, (void *)&ep_really_active);
-	
-	TxBulkEPList[epid].sub = virt_to_phys(sb_desc_1);
+                
+        /* Assert that the EP descriptor is disabled. */
+        assert(!(TxBulkEPList[epid].command & IO_MASK(USB_EP_command, enable)));
+
+        /* The reason we set the EP's sub pointer directly instead of
+           walking the SB list and linking it last in the list is that we only
+           have one active urb at a time (the rest are queued). */
+
+	/* Note that we cannot have interrupts running when we have set the SB descriptor
+	   but the EP is not yet enabled.  If a bulk eot happens for another EP, we will
+	   find this EP disabled and with a SB != 0, which will make us think that it's done. */
+	TxBulkEPList[epid].sub = virt_to_phys(sb_desc);
 	TxBulkEPList[epid].hw_len = 0;
-	TxBulkEPList[epid].command |= IO_STATE(USB_EP_command, enable, yes);
+        /* Note that we don't have to fill in the ep_id field since this
+           was done when we allocated the EP descriptors in init_tx_bulk_ep. */
+
+	/* Check if the dummy list is already with us (if several urbs were queued). */
+	if (TxBulkEPList[epid].next != virt_to_phys(&TxBulkDummyEPList[epid][0])) {
+
+		dbg_bulk("Inviting dummy list to the party for urb 0x%lx, epid %d", 
+			 (unsigned long)urb, epid);
+
+		/* The last EP in the dummy list already has its next pointer set to 
+		   TxBulkEPList[epid].next. */
 
+		/* We don't need to check if the DMA is at this EP or not before changing the
+		   next pointer, since we will do it in one 32-bit write (EP descriptors are
+		   32-bit aligned). */
+		TxBulkEPList[epid].next = virt_to_phys(&TxBulkDummyEPList[epid][0]);
+	}
+        /* Enable the EP descr. */
+	dbg_bulk("Enabling bulk EP for urb 0x%lx, epid %d", (unsigned long)urb, epid);
+        TxBulkEPList[epid].command |= IO_STATE(USB_EP_command, enable, yes);
+
+	/* Everything is set up, safe to enable interrupts again. */
 	restore_flags(flags);
 
+        /* If the DMA bulk channel isn't running, we need to restart it if it
+	   has stopped at the last EP descriptor (DMA stopped because there was
+	   no more traffic) or if it has stopped at a dummy EP with the intr flag
+	   set (DMA stopped because we were too slow in inserting new traffic). */
 	if (!(*R_DMA_CH8_SUB0_CMD & IO_MASK(R_DMA_CH8_SUB0_CMD, cmd))) {
-		*R_DMA_CH8_SUB0_CMD = IO_STATE(R_DMA_CH8_SUB0_CMD, cmd, start);
-		
-	}
 
+		USB_EP_Desc_t *ep;
+		ep = (USB_EP_Desc_t *)phys_to_virt(*R_DMA_CH8_SUB0_EP);
+		dbg_bulk("DMA channel not running in add");
+		dbg_bulk("DMA is at 0x%lx", (unsigned long)ep);
+
+		if (*R_DMA_CH8_SUB0_EP == virt_to_phys(&TxBulkEPList[NBR_OF_EPIDS - 1]) ||
+		    (ep->command & 0x8) >> 3) {
+			*R_DMA_CH8_SUB0_CMD = IO_STATE(R_DMA_CH8_SUB0_CMD, cmd, start);
+			/* Update/restart the bulk start timer since we just started the channel. */
+			mod_timer(&bulk_start_timer, jiffies + BULK_START_TIMER_INTERVAL);
+			/* Update/restart the bulk eot timer since we just inserted traffic. */
+			mod_timer(&bulk_eot_timer, jiffies + BULK_EOT_TIMER_INTERVAL);
+		}
+	}
+        
 	DBFEXIT;
 }
 
-static void handle_bulk_transfer_attn(int epid, int status)
+static void etrax_usb_complete_bulk_urb(urb_t *urb, int status)
 {
-	struct urb *old_urb;
-	etrax_urb_priv_t *hc_priv;
-	unsigned long flags;
-
+	etrax_urb_priv_t *urb_priv = (etrax_urb_priv_t *)urb->hcpriv;
+	int epid = urb_priv->epid;
+        unsigned long flags;
+        
 	DBFENTER;
 
-	clear_bit(epid, (void *)&ep_really_active);
-	
-	old_urb = URB_List[epid];
-	URB_List[epid] = old_urb->next;
+        if (status)
+                warn("Completing bulk urb with status %d.", status);
+
+	dbg_bulk("Completing bulk urb 0x%lx for epid %d", (unsigned long)urb, epid);
+
+	/* Update the urb list. */	
+        urb_list_del(urb, epid);
 
-	/* if (status == 0 && IN) find data and copy to urb */
-	if (status == 0 && usb_pipein(old_urb->pipe)) {
-		etrax_urb_priv_t *urb_priv;
-		
-		urb_priv = (etrax_urb_priv_t *)old_urb->hcpriv;
-		save_flags(flags);
-		cli();
-		if (urb_priv->eot == 1) {
-			old_urb->actual_length = urb_priv->rx_offset;
+        /* For an IN pipe, we always set the actual length, regardless of whether there was
+           an error or not (which means the device driver can use the data if it wants to). */
+	if (usb_pipein(urb->pipe)) {
+                urb->actual_length = urb_priv->rx_offset;
+	} else {
+		/* Set actual_length for OUT urbs also; the USB mass storage driver seems
+		   to want that. We wouldn't know of any partial writes if there was an error. */
+		if (status == 0) {
+			urb->actual_length = urb->transfer_buffer_length;
 		} else {
-			if (urb_priv->rx_offset == 0) {
-				status = 0;
-			} else {
-				status = -EPROTO;
-			}
-			
-			old_urb->actual_length = 0;
-			err("(BULK) No eot set in IN data!!! rx_offset is: %d", urb_priv->rx_offset);
+			urb->actual_length = 0;
 		}
-		
-		restore_flags(flags);
 	}
 
+        /* FIXME: Is there something of the things below we shouldn't do if there was an error?
+           Like, maybe we shouldn't toggle the toggle bits, or maybe we shouldn't insert more traffic. */
+
 	save_flags(flags);
 	cli();
 		
-	*R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid); nop();
-	if (usb_pipeout(old_urb->pipe)) {
+	*R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid); 
+        nop();
+
+        /* We need to fiddle with the toggle bits because the hardware doesn't do it for us. */
+	if (usb_pipeout(urb->pipe)) {
 		char toggle =
-		IO_EXTRACT(R_USB_EPT_DATA, t_out, *R_USB_EPT_DATA);
-		usb_settoggle(old_urb->dev, usb_pipeendpoint(old_urb->pipe),
-			      usb_pipeout(old_urb->pipe), toggle);
+                        IO_EXTRACT(R_USB_EPT_DATA, t_out, *R_USB_EPT_DATA);
+		usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe),
+			      usb_pipeout(urb->pipe), toggle);
 	} else {
 		char toggle =
-		IO_EXTRACT(R_USB_EPT_DATA, t_in, *R_USB_EPT_DATA);
-		usb_settoggle(old_urb->dev, usb_pipeendpoint(old_urb->pipe),
-			      usb_pipeout(old_urb->pipe), toggle);
+                        IO_EXTRACT(R_USB_EPT_DATA, t_in, *R_USB_EPT_DATA);
+		usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe),
+			      usb_pipeout(urb->pipe), toggle);
+	}
+        restore_flags(flags);
+
+        /* Remember to free the SBs. */
+	etrax_remove_from_sb_list(urb);
+	kfree(urb_priv);
+        urb->hcpriv = 0;
+        
+	/* If there are any more urb's in the list we'd better start sending */
+	if (!urb_list_empty(epid)) {
+
+                urb_t *new_urb;
+
+                /* Get the first urb. */
+                new_urb = urb_list_first(epid);
+		assert(new_urb);
+
+                dbg_bulk("More bulk for epid %d", epid);
+
+		etrax_usb_add_to_bulk_sb_list(new_urb, epid);
+	}
+
+        urb->status = status;
+
+        /* We let any non-zero status from the layer above have precedence. */
+        if (status == 0) {
+                /* USB_DISABLE_SPD means that short reads (shorter than the endpoint's max length)
+                   is to be treated as an error. */
+                if (urb->transfer_flags & USB_DISABLE_SPD) {
+                        if (usb_pipein(urb->pipe) && 
+                            (urb->actual_length != 
+                             usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe)))) {
+                                urb->status = -EREMOTEIO;
+                        } 
+                }
+        }
+        
+	if (urb->complete) {
+		urb->complete(urb);
 	}
 
-	restore_flags(flags);
-
-	/* If there are any more URB's in the list we'd better start sending */
-	if (URB_List[epid]) {
-		etrax_usb_do_bulk_hw_add(URB_List[epid], epid,
-					 usb_maxpacket(URB_List[epid]->dev, URB_List[epid]->pipe,
-						       usb_pipeout(URB_List[epid]->pipe)));
-	}
-#if 1
-	else {
-		/* This means that this EP is now free, deconfigure it */
+	if (urb_list_empty(epid)) {
+		/* This means that this EP is now free, deconfigure it. */
 		etrax_usb_free_epid(epid);
-	}
-#endif
-	
-	/* Remember to free the SB's */
-	hc_priv = (etrax_urb_priv_t *)old_urb->hcpriv;
-	cleanup_sb(hc_priv->first_sb);
-	kfree(hc_priv);
+		
+		/* No more traffic; time to clean up.
+		   Must set sub pointer to 0, since we look at the sub pointer when handling
+		   the bulk eot interrupt. */
+
+                dbg_bulk("No bulk for epid %d", epid);
+                
+		TxBulkEPList[epid].sub = 0;
+
+		/* Unlink the dummy list. */
 
-	old_urb->status = status;
-	if (old_urb->complete) {
-		old_urb->complete(old_urb);
+		dbg_bulk("Kicking dummy list out of party for urb 0x%lx, epid %d", 
+			 (unsigned long)urb, epid);
+
+		/* No need to wait for the DMA before changing the next pointer.
+		   The modulo NBR_OF_EPIDS isn't actually necessary, since we will never use
+		   the last one (INVALID_EPID) for actual traffic. */
+		TxBulkEPList[epid].next = 
+			virt_to_phys(&TxBulkEPList[(epid + 1) % NBR_OF_EPIDS]);
 	}
 
 	DBFEXIT;
 }
 
-/* ---------------------------------------------------------------------------- */
-
-static int etrax_usb_submit_ctrl_urb(struct urb *urb)
+static int etrax_usb_submit_ctrl_urb(urb_t *urb)
 {
 	int epid;
-	char devnum;
-	char endpoint;
-	char maxlen;
-	char out_traffic;
-	char slow;
-
-	struct urb *tmp_urb;
-	
-	unsigned long flags;
-	
+        int empty;
+        unsigned long flags;
+        
 	DBFENTER;
 
-	devnum = usb_pipedevice(urb->pipe);
-	endpoint = usb_pipeendpoint(urb->pipe);
-	maxlen = usb_maxpacket(urb->dev, urb->pipe,
-			       usb_pipeout(urb->pipe));
-	out_traffic = usb_pipeout(urb->pipe);
-	slow = usb_pipeslow(urb->pipe);
-	
-	epid = etrax_usb_lookup_epid(devnum, endpoint, slow, maxlen, out_traffic);
+        /* FIXME: Return -ENXIO if there is already a queued urb for this endpoint? */
+
+        /* Epid allocation, empty check and list add must be protected.
+
+           Epid allocation because if we find an existing epid for this endpoint an urb might be
+           completed (emptying the list) before we add the new urb to the list, causing the epid
+           to be de-allocated. We would then start the transfer with an invalid epid -> epid attn.
+
+           Empty check and add because otherwise we might conclude that the list is not empty,
+           after which it becomes empty before we add the new urb to the list, causing us not to 
+           insert the new traffic into the SB list. */
+
+        spin_lock_irqsave(&urb_list_lock, flags);
+	epid = etrax_usb_setup_epid(urb);
 	if (epid == -1) {
-		epid = etrax_usb_allocate_epid();
-		if (epid == -1) {
-			/* We're out of endpoints, return some error */
-			err("We're out of endpoints");
-			return -ENOMEM;
-		}
-		/* Now we have to fill in this ep */
-		etrax_usb_setup_epid(epid, devnum, endpoint, maxlen, slow, out_traffic);
+                spin_unlock_irqrestore(&urb_list_lock, flags);
+		DBFEXIT;
+		return -ENOMEM;
 	}
-	/* Ok, now we got valid endpoint, lets insert some traffic */
+        empty = urb_list_empty(epid);
+        urb_list_add(urb, epid);
+        spin_unlock_irqrestore(&urb_list_lock, flags);
 
+        dbg_ctrl("Adding ctrl urb 0x%lx to %s list, epid %d", 
+                 (unsigned long)urb, empty ? "empty" : "", epid);
+
+        /* Mark the urb as being in progress. */
 	urb->status = -EINPROGRESS;
 
-	save_flags(flags);
-	cli();
-	
-	if (URB_List[epid]) {
-		/* Find end of list and add */
-		for (tmp_urb = URB_List[epid]; tmp_urb->next; tmp_urb = tmp_urb->next)
-			dump_urb(tmp_urb);
-
-		tmp_urb->next = urb;
-		restore_flags(flags);
-	} else {
-		/* If this is the first URB, add the URB and do HW add */
-		URB_List[epid] = urb;
-		restore_flags(flags);
-		etrax_usb_do_ctrl_hw_add(urb, epid, maxlen);
-	}
+        if (empty) {
+                etrax_usb_add_to_ctrl_sb_list(urb, epid);
+        }
 
 	DBFEXIT;
 
 	return 0;
 }
 
-static void etrax_usb_do_ctrl_hw_add(struct urb *urb, int epid, char maxlen)
+static void etrax_usb_add_to_ctrl_sb_list(urb_t *urb, int epid)
 {
-	USB_SB_Desc_t *sb_desc_1;
-	USB_SB_Desc_t *sb_desc_2;
-	USB_SB_Desc_t *sb_desc_3;
+	USB_SB_Desc_t *sb_desc_setup;
+	USB_SB_Desc_t *sb_desc_data;
+	USB_SB_Desc_t *sb_desc_status;
 
 	etrax_urb_priv_t *urb_priv;
 
 	unsigned long flags;
-
+	char maxlen;
+	
 	DBFENTER;
 
-	urb_priv = kmalloc(sizeof(etrax_urb_priv_t), GFP_KERNEL);
-	sb_desc_1 = (USB_SB_Desc_t*)kmem_cache_alloc(usb_desc_cache, GFP_KERNEL);
-	sb_desc_2 = (USB_SB_Desc_t*)kmem_cache_alloc(usb_desc_cache, GFP_KERNEL);
-
-	if (!(sb_desc_1 && sb_desc_2)) {
-		panic("kmem_cache_alloc in ctrl_hw_add gave NULL pointers !!!\n");
-	}
-	
-	sb_desc_1->sw_len = 8;
-	sb_desc_1->command = IO_FIELD(USB_SB_command, rem, 0) |
-		IO_STATE(USB_SB_command, tt, setup) |
-		IO_STATE(USB_SB_command, full, yes) |
-		IO_STATE(USB_SB_command, eot, yes);
-	
-	sb_desc_1->buf = virt_to_phys(urb->setup_packet);
-	sb_desc_1->next = virt_to_phys(sb_desc_2);
-	dump_sb_desc(sb_desc_1);
+	maxlen = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe));
+	
+	urb_priv = kmalloc(sizeof(etrax_urb_priv_t), KMALLOC_FLAG);
+	assert(urb_priv != NULL);
+        /* This sets rx_offset to 0. */
+        memset(urb_priv, 0, sizeof(etrax_urb_priv_t));
+        
+	sb_desc_setup = (USB_SB_Desc_t*)kmem_cache_alloc(usb_desc_cache, SLAB_FLAG);
+	assert(sb_desc_setup != NULL);
+	sb_desc_status = (USB_SB_Desc_t*)kmem_cache_alloc(usb_desc_cache, SLAB_FLAG);
+	assert(sb_desc_status != NULL);
+
+	/* Initialize the mandatory setup SB descriptor (used only in control transfers) */	
+	sb_desc_setup->sw_len = 8;
+	sb_desc_setup->command = (IO_FIELD(USB_SB_command, rem, 0) |
+                                  IO_STATE(USB_SB_command, tt, setup) |
+                                  IO_STATE(USB_SB_command, full, yes) |
+                                  IO_STATE(USB_SB_command, eot, yes));
+	
+	sb_desc_setup->buf = virt_to_phys(urb->setup_packet);
 
 	if (usb_pipeout(urb->pipe)) {
-		dbg_ctrl("Transfer for epid %d is OUT", epid);
+                dbg_ctrl("Transfer for epid %d is OUT", epid);
 
 		/* If this Control OUT transfer has an optional data stage we add an OUT token
 		   before the mandatory IN (status) token, hence the reordered SB list */
 		
+		sb_desc_setup->next = virt_to_phys(sb_desc_status);
 		if (urb->transfer_buffer) {
-			dbg_ctrl("This OUT transfer has an extra data stage");
-			sb_desc_3 = (USB_SB_Desc_t*)kmem_cache_alloc(usb_desc_cache, GFP_KERNEL);
 
-			sb_desc_1->next = virt_to_phys(sb_desc_3);
+			dbg_ctrl("This OUT transfer has an extra data stage");
 			
-			sb_desc_3->sw_len = urb->transfer_buffer_length;
-			sb_desc_3->command = IO_STATE(USB_SB_command, tt, out) |
-				IO_STATE(USB_SB_command, full, yes) |
-				IO_STATE(USB_SB_command, eot, yes);
-			sb_desc_3->buf = virt_to_phys(urb->transfer_buffer);
-			sb_desc_3->next = virt_to_phys(sb_desc_2);
-		}
-		
-		sb_desc_2->sw_len = 1;
-		sb_desc_2->command = IO_FIELD(USB_SB_command, rem, 0) |
-			IO_STATE(USB_SB_command, tt, in) |
-			IO_STATE(USB_SB_command, eot, yes) |
-			IO_STATE(USB_SB_command, eol, yes);
-
-		sb_desc_2->buf = 0;
-		sb_desc_2->next = 0;
-		dump_sb_desc(sb_desc_2);
-		
+                        sb_desc_data = (USB_SB_Desc_t*)kmem_cache_alloc(usb_desc_cache, SLAB_FLAG);
+                        assert(sb_desc_data != NULL);
+                        
+                        sb_desc_setup->next = virt_to_phys(sb_desc_data);
+                        
+                        sb_desc_data->sw_len = urb->transfer_buffer_length;
+                        sb_desc_data->command = (IO_STATE(USB_SB_command, tt, out) |
+                                                 IO_STATE(USB_SB_command, full, yes) |
+                                                 IO_STATE(USB_SB_command, eot, yes));
+                        sb_desc_data->buf = virt_to_phys(urb->transfer_buffer);
+                        sb_desc_data->next = virt_to_phys(sb_desc_status);
+		}
+		
+		sb_desc_status->sw_len = 1;
+		sb_desc_status->command = (IO_FIELD(USB_SB_command, rem, 0) |
+                                           IO_STATE(USB_SB_command, tt, in) |
+                                           IO_STATE(USB_SB_command, eot, yes) |
+                                           IO_STATE(USB_SB_command, eol, yes));
+
+		sb_desc_status->buf = 0;
+		sb_desc_status->next = 0;
+                
 	} else if (usb_pipein(urb->pipe)) {
 
 		dbg_ctrl("Transfer for epid %d is IN", epid);
 		dbg_ctrl("transfer_buffer_length = %d", urb->transfer_buffer_length);
 		dbg_ctrl("rem is calculated to %d", urb->transfer_buffer_length % maxlen);
 
-		sb_desc_3 = (USB_SB_Desc_t*)kmem_cache_alloc(usb_desc_cache, GFP_KERNEL);
+		sb_desc_data = (USB_SB_Desc_t*)kmem_cache_alloc(usb_desc_cache, SLAB_FLAG);
+		assert(sb_desc_data != NULL);
 		
-		sb_desc_2->sw_len = urb->transfer_buffer_length ?
+		sb_desc_setup->next = virt_to_phys(sb_desc_data);
+
+		sb_desc_data->sw_len = urb->transfer_buffer_length ?
 			(urb->transfer_buffer_length - 1) / maxlen + 1 : 0;
-		dbg_ctrl("sw_len got %d", sb_desc_2->sw_len);
+		dbg_ctrl("sw_len got %d", sb_desc_data->sw_len);
 		
-		sb_desc_2->command =
-			IO_FIELD(USB_SB_command, rem,
-				 urb->transfer_buffer_length % maxlen) |
-			IO_STATE(USB_SB_command, tt, in) |
-			IO_STATE(USB_SB_command, eot, yes);
-		
-		sb_desc_2->buf = 0;
-		sb_desc_2->next = virt_to_phys(sb_desc_3);
-		dump_sb_desc(sb_desc_2);
-
-		sb_desc_3->sw_len = 1;
-		sb_desc_3->command = IO_FIELD(USB_SB_command, rem, 0) |
-			IO_STATE(USB_SB_command, tt, zout) |
-			IO_STATE(USB_SB_command, full, yes) |
-			IO_STATE(USB_SB_command, eot, yes) |
-			IO_STATE(USB_SB_command, eol, yes);
+		sb_desc_data->command =
+			(IO_FIELD(USB_SB_command, rem,
+                                  urb->transfer_buffer_length % maxlen) |
+                         IO_STATE(USB_SB_command, tt, in) |
+                         IO_STATE(USB_SB_command, eot, yes));
+		
+		sb_desc_data->buf = 0;
+		sb_desc_data->next = virt_to_phys(sb_desc_status);
+
+                /* Read comment at zout_buffer declaration for an explanation to this. */
+		sb_desc_status->sw_len = 1;
+		sb_desc_status->command = (IO_FIELD(USB_SB_command, rem, 0) |
+                                           IO_STATE(USB_SB_command, tt, zout) |
+                                           IO_STATE(USB_SB_command, full, yes) |
+                                           IO_STATE(USB_SB_command, eot, yes) |
+                                           IO_STATE(USB_SB_command, eol, yes));
 				
-		sb_desc_3->buf = 0;
-		sb_desc_3->next = 0;
-		dump_sb_desc(sb_desc_3);
-
-		urb_priv->rx_offset = 0;
-		urb_priv->eot = 0;
+		sb_desc_status->buf = virt_to_phys(&zout_buffer[0]);
+		sb_desc_status->next = 0;
 	}
 	
-	urb_priv->first_sb = sb_desc_1;
-	
-	urb->hcpriv = (void *)urb_priv;
+	urb_priv->first_sb = sb_desc_setup;
+	urb_priv->last_sb = sb_desc_status;
+	urb_priv->epid = epid;
+
+	urb->hcpriv = urb_priv;
 	
 	/* Reset toggle bits and reset error count, remeber to di and ei */
 	/* Warning: it is possible that this locking doesn't work with bottom-halves */
@@ -1332,661 +2181,1059 @@
 	save_flags(flags);
 	cli();
 
-	*R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid); nop();
+	*R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid); 
+        nop();
 	if (*R_USB_EPT_DATA & IO_MASK(R_USB_EPT_DATA, hold)) {
-		panic("Hold was set in %s\n", __FUNCTION__);
+		panic("Hold was set in %s", __FUNCTION__);
 	}
 	
 
+        /* FIXME: Compare with etrax_usb_add_to_bulk_sb_list where the toggle bits
+           are set to a specific value. Why the difference? Read "Transfer and Toggle Bits
+           in Designer's Reference, p. 8 - 11. */
 	*R_USB_EPT_DATA &=
 		~(IO_MASK(R_USB_EPT_DATA, error_count_in) |
 		  IO_MASK(R_USB_EPT_DATA, error_count_out) |
 		  IO_MASK(R_USB_EPT_DATA, t_in) |
 		  IO_MASK(R_USB_EPT_DATA, t_out));
 
-	/* Enable the EP descr. */
+	/* Since we use the rx interrupt to complete ctrl urbs, we can enable interrupts now
+	   (i.e. we don't check the sub pointer on an eot interrupt like we do for bulk traffic). */
+	restore_flags(flags);
 
-	set_bit(epid, (void *)&ep_really_active);
-	
-	TxCtrlEPList[epid].sub = virt_to_phys(sb_desc_1);
+        /* Assert that the EP descriptor is disabled. */
+        assert(!(TxCtrlEPList[epid].command & IO_MASK(USB_EP_command, enable)));
+
+	/* Set up and enable the EP descriptor. */
+	TxCtrlEPList[epid].sub = virt_to_phys(sb_desc_setup);
 	TxCtrlEPList[epid].hw_len = 0;
 	TxCtrlEPList[epid].command |= IO_STATE(USB_EP_command, enable, yes);
 
-	restore_flags(flags);
-
-	dump_ep_desc(&TxCtrlEPList[epid]);
-
-	if (!(*R_DMA_CH8_SUB1_CMD & IO_MASK(R_DMA_CH8_SUB1_CMD, cmd))) {
-		*R_DMA_CH8_SUB1_CMD = IO_STATE(R_DMA_CH8_SUB1_CMD, cmd, start);
-		
-	}
+        /* We start the DMA sub channel without checking if it's running or not, because:
+           1) If it's already running, issuing the start command is a nop.
+           2) We avoid a test-and-set race condition. */
+        *R_DMA_CH8_SUB1_CMD = IO_STATE(R_DMA_CH8_SUB1_CMD, cmd, start);
 	
 	DBFEXIT;
 }
 
-static int etrax_usb_submit_urb(struct urb *urb)
+static void etrax_usb_complete_ctrl_urb(urb_t *urb, int status)
 {
-	etrax_hc_t *hc;
-	int rval = -EINVAL;
-	
-	DBFENTER;
+        etrax_urb_priv_t *urb_priv = (etrax_urb_priv_t *)urb->hcpriv;
+        int epid = urb_priv->epid;
 
-	urb->next = NULL;
+	DBFENTER;
 
-	dump_urb(urb);
-	submit_urb_count++;
-	
-	hc = (etrax_hc_t*) urb->dev->bus->hcpriv;
-	
-	if (usb_pipedevice(urb->pipe) == hc->rh.devnum) {
-		/* This request if for the Virtual Root Hub */
-		rval = etrax_rh_submit_urb(urb);
-		
-	} else if (usb_pipetype(urb->pipe) == PIPE_CONTROL) {
-		rval = etrax_usb_submit_ctrl_urb(urb);
+        if (status)
+                warn("Completing ctrl urb with status %d.", status);
 
-	} else if (usb_pipetype(urb->pipe) == PIPE_BULK) {
-		rval = etrax_usb_submit_bulk_urb(urb);
+        dbg_ctrl("Completing ctrl epid %d, urb 0x%lx", epid, (unsigned long)urb);
 
-	} else if (usb_pipetype(urb->pipe) == PIPE_INTERRUPT) {
-		int bustime;
+	/* Remove this urb from the list. */
+        urb_list_del(urb, epid);
 
-		if (urb->bandwidth == 0) {
-			bustime = usb_check_bandwidth(urb->dev, urb);
-			if (bustime < 0) {
-				rval = bustime;
-			} else {
-				usb_claim_bandwidth(urb->dev, urb, bustime, 0);
-				rval = etrax_usb_submit_intr_urb(urb);
-			}
-			
-		}
-	} else if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
-		warn("Isochronous traffic is not supported !!!");
-		rval = -EINVAL;
+        /* For an IN pipe, we always set the actual length, regardless of whether there was
+           an error or not (which means the device driver can use the data if it wants to). */
+	if (usb_pipein(urb->pipe)) {
+                urb->actual_length = urb_priv->rx_offset;
 	}
 
-	DBFEXIT;
+        /* FIXME: Is there something of the things below we shouldn't do if there was an error?
+           Like, maybe we shouldn't insert more traffic. */
 
-	return rval;
-}
+	/* Remember to free the SBs. */
+	etrax_remove_from_sb_list(urb);
+	kfree(urb_priv);
+        urb->hcpriv = 0;
+        
+	/* If there are any more urbs in the list we'd better start sending. */
+	if (!urb_list_empty(epid)) {
+                urb_t *new_urb;
+                
+                /* Get the first urb. */
+                new_urb = urb_list_first(epid);
+		assert(new_urb);
 
-static int etrax_usb_unlink_urb(struct urb *urb)
-{
-	etrax_hc_t *hc = urb->dev->bus->hcpriv;
-	int epid;
-	int devnum, endpoint, slow, maxlen, out_traffic;
-	etrax_urb_priv_t *hc_priv;
-	unsigned long flags;
-	
-	DBFENTER;
-	dump_urb(urb);
-	devnum = usb_pipedevice(urb->pipe);
-	endpoint = usb_pipeendpoint(urb->pipe);
-	slow = usb_pipeslow(urb->pipe);
-	maxlen = usb_maxpacket(urb->dev, urb->pipe,
-			       usb_pipeout(urb->pipe));
-	out_traffic = usb_pipeout(urb->pipe);
-
-	epid = etrax_usb_lookup_epid(devnum, endpoint, slow, maxlen, out_traffic);
+                dbg_ctrl("More ctrl for epid %d, first urb = 0x%lx", epid, (unsigned long)new_urb);
 
-	if (epid == -1)
-		return 0;
-	
-	if (usb_pipedevice(urb->pipe) == hc->rh.devnum) {
-		int ret;
-		ret =  etrax_rh_unlink_urb(urb);
-		DBFEXIT;
-		return ret;
-	} else if (usb_pipetype(URB_List[epid]->pipe) == PIPE_INTERRUPT) {
-		int ret;
-		ret = etrax_usb_unlink_intr_urb(urb);
-		urb->status = -ENOENT;
-		if (urb->complete) {
-			urb->complete(urb);
-		}
-		DBFEXIT;
-		return ret;
+		etrax_usb_add_to_ctrl_sb_list(new_urb, epid);
 	}
-
-	info("Unlink of BULK or CTRL");
-
-	save_flags(flags);
-	cli();
 	
-	for (epid = 0; epid < 32; epid++) {
-		struct urb *u = URB_List[epid];
-		struct urb *prev = NULL;
-		int pos = 0;
-
-		for (; u; u = u->next) {
-			pos++;
-			if (u == urb) {
-				if (!prev) {
-					URB_List[epid] = u->next;
-				} else {
-					prev->next = u->next;
-				}
+	urb->status = status;
 
-				restore_flags(flags);
-
-				if (!prev) {
-					if (usb_pipetype(u->pipe) == PIPE_CONTROL) {
-						if (TxCtrlEPList[epid].command & IO_MASK(USB_EP_command, enable)) {
-							/* The EP was enabled, disable it and wait */
-							TxCtrlEPList[epid].command &= ~IO_MASK(USB_EP_command, enable);
-							while (*R_DMA_CH8_SUB1_EP == virt_to_phys(&TxCtrlEPList[epid]));
-						}
-					} else if (usb_pipetype(u->pipe) == PIPE_BULK) {
-						if (TxBulkEPList[epid].command & IO_MASK(USB_EP_command, enable)) {
-							TxBulkEPList[epid].command &= ~IO_MASK(USB_EP_command, enable);
-							while (*R_DMA_CH8_SUB0_EP == virt_to_phys(&TxBulkEPList[epid]));
-						}
-					}
-				}
-
-				info("Found urb at epid %d, pos %d", epid, pos);
-
-				u->status = -ENOENT;
-				if (u->complete) {
-					u->complete(u);
-				}
-				
-				hc_priv = (etrax_urb_priv_t *)u->hcpriv;
-				cleanup_sb(hc_priv->first_sb);
-				kfree(hc_priv);
+        /* We let any non-zero status from the layer above have precedence. */
+        if (status == 0) {
+                /* USB_DISABLE_SPD means that short reads (shorter than the endpoint's max length)
+                   is to be treated as an error. */
+                if (urb->transfer_flags & USB_DISABLE_SPD) {
+                        if (usb_pipein(urb->pipe) && 
+                            (urb->actual_length != 
+                             usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe)))) {
+                                urb->status = -EREMOTEIO;
+                        } 
+                }
+        }
+        
+	if (urb->complete) {
+		urb->complete(urb);
+	}
 
-				DBFEXIT;
-				return 0;
-			}
-			prev = u;
-		}
+	if (urb_list_empty(epid)) {
+		/* No more traffic. Time to clean up. */
+		etrax_usb_free_epid(epid);
+		/* Must set sub pointer to 0. */
+                dbg_ctrl("No ctrl for epid %d", epid);
+		TxCtrlEPList[epid].sub = 0;
 	}
 
-	restore_flags(flags);
-		
 	DBFEXIT;
-	return 0;
 }
 
-static int etrax_usb_get_frame_number(struct usb_device *usb_dev)
+static int etrax_usb_submit_intr_urb(urb_t *urb)
 {
+	
+	int epid;
+	
 	DBFENTER;
-	DBFEXIT;
-	return (*R_USB_FM_NUMBER);
-}
+        
+        if (usb_pipeout(urb->pipe)) {
+                /* Unsupported transfer type.
+                   We don't support interrupt out traffic. (If we do, we can't support
+                   intervals for neither in or out traffic, but are forced to schedule all
+                   interrupt traffic in one frame.) */
+                return -EINVAL;
+        }
 
-static int etrax_usb_allocate_dev(struct usb_device *usb_dev)
-{  
-	DBFENTER;
-	DBFEXIT;
-	return 0;
-}
+	epid = etrax_usb_setup_epid(urb);
+	if (epid == -1) {
+		DBFEXIT;
+		return -ENOMEM;
+	}
 
-static int etrax_usb_deallocate_dev(struct usb_device *usb_dev)
-{
-	DBFENTER;
-	DBFEXIT;
-	return 0;
-}
+	if (!urb_list_empty(epid)) {
+                /* There is already a queued urb for this endpoint. */
+                etrax_usb_free_epid(epid);
+                return -ENXIO;
+        }
+        
+        urb->status = -EINPROGRESS;
 
-static void etrax_usb_tx_interrupt(int irq, void *vhc, struct pt_regs *regs)
-{
-	DBFENTER;
+        dbg_intr("Add intr urb 0x%lx, to list, epid %d", (unsigned long)urb, epid);
 
-	if (*R_IRQ_READ2 & IO_MASK(R_IRQ_READ2, dma8_sub0_descr)) {
-		info("dma8_sub0_descr (BULK) intr.");
-		*R_DMA_CH8_SUB0_CLR_INTR = IO_STATE(R_DMA_CH8_SUB0_CLR_INTR, clr_descr, do);
-	}
-	if (*R_IRQ_READ2 & IO_MASK(R_IRQ_READ2, dma8_sub1_descr)) {
-		info("dma8_sub1_descr (CTRL) intr.");
-		*R_DMA_CH8_SUB1_CLR_INTR = IO_STATE(R_DMA_CH8_SUB1_CLR_INTR, clr_descr, do);
-	}
-	if (*R_IRQ_READ2 & IO_MASK(R_IRQ_READ2, dma8_sub2_descr)) {
-		info("dma8_sub2_descr (INT) intr.");
-		*R_DMA_CH8_SUB2_CLR_INTR = IO_STATE(R_DMA_CH8_SUB2_CLR_INTR, clr_descr, do);
-	}
-	if (*R_IRQ_READ2 & IO_MASK(R_IRQ_READ2, dma8_sub3_descr)) {
-		info("dma8_sub3_descr (ISO) intr.");
-		*R_DMA_CH8_SUB3_CLR_INTR = IO_STATE(R_DMA_CH8_SUB3_CLR_INTR, clr_descr, do);
-	}
+        urb_list_add(urb, epid);
+	etrax_usb_add_to_intr_sb_list(urb, epid);
+
+	return 0;
 	
 	DBFEXIT;
 }
 
-static void etrax_usb_rx_interrupt(int irq, void *vhc, struct pt_regs *regs)
+static void etrax_usb_add_to_intr_sb_list(urb_t *urb, int epid)
 {
-	int epid = 0;
-	struct urb *urb;
+
+	volatile USB_EP_Desc_t *tmp_ep;
+	volatile USB_EP_Desc_t *first_ep;
+	
+	char maxlen;
+	int interval;
+	int i;
+        
 	etrax_urb_priv_t *urb_priv;
-		
-	*R_DMA_CH9_CLR_INTR = IO_STATE(R_DMA_CH9_CLR_INTR, clr_eop, do);
+	
+	DBFENTER;
 
-	while (myNextRxDesc->status & IO_MASK(USB_IN_status, eop)) {
-		if (myNextRxDesc->status & IO_MASK(USB_IN_status, nodata)) {
+	maxlen = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe));
+	interval = urb->interval;
 
-			goto skip_out;
-		}
+	urb_priv = kmalloc(sizeof(etrax_urb_priv_t), KMALLOC_FLAG);
+	assert(urb_priv != NULL);
+        memset(urb_priv, 0, sizeof(etrax_urb_priv_t));
+	urb->hcpriv = urb_priv;
 
-		if (myNextRxDesc->status & IO_MASK(USB_IN_status, error)) {
-			
-			goto skip_out;
-		}
-		
-		epid = IO_EXTRACT(USB_IN_status, epid, myNextRxDesc->status);
+	first_ep = &TxIntrEPList[0];
 
-		urb = URB_List[epid];
+	/* Round of the interval to 2^n, it is obvious that this code favours
+	   smaller numbers, but that is actually a good thing */
+        /* FIXME: The "rounding error" for larger intervals will be quite
+           large. For in traffic this shouldn't be a problem since it will only
+           mean that we "poll" more often. */
+	for (i = 0; interval; i++) {
+		interval = interval >> 1;
+	}
+	interval = 1 << (i - 1);
 
-		if (urb && usb_pipein(urb->pipe)) {
-			urb_priv = (etrax_urb_priv_t *)urb->hcpriv;
+	dbg_intr("Interval rounded to %d", interval);
 
-			if (usb_pipetype(urb->pipe) == PIPE_INTERRUPT) {
-				struct in_chunk *in;
-				dbg_intr("Packet for epid %d in rx buffers", epid);
-				in = kmalloc(sizeof(struct in_chunk), GFP_ATOMIC);
-				in->length = myNextRxDesc->hw_len;
-				in->data = kmalloc(in->length, GFP_ATOMIC);
-				memcpy(in->data, phys_to_virt(myNextRxDesc->buf), in->length);
-				list_add_tail(&in->list, &urb_priv->ep_in_list);
-#ifndef ETRAX_USB_INTR_IRQ
-				etrax_usb_hc_intr_top_half(irq, vhc, regs);
-#endif
-				
-			} else {
-				if ((urb_priv->rx_offset + myNextRxDesc->hw_len) >
-				    urb->transfer_buffer_length) {
-					err("Packet (epid: %d) in RX buffer (%d) was bigger "
-					    "than the URB has room for (%d)!!!", epid, urb_priv->rx_offset + myNextRxDesc->hw_len, urb->transfer_buffer_length);
-					goto skip_out;
-				}
+	tmp_ep = first_ep;
+	i = 0;
+	do {
+		if (tmp_ep->command & IO_MASK(USB_EP_command, eof)) {
+			if ((i % interval) == 0) {
+				/* Insert the traffic ep after tmp_ep */
+				USB_EP_Desc_t *ep_desc;
+				USB_SB_Desc_t *sb_desc;
+
+				dbg_intr("Inserting EP for epid %d", epid);
+                                
+				ep_desc = (USB_EP_Desc_t *)
+					kmem_cache_alloc(usb_desc_cache, SLAB_FLAG);
+				sb_desc = (USB_SB_Desc_t *)
+					kmem_cache_alloc(usb_desc_cache, SLAB_FLAG);
+				assert(ep_desc != NULL);
+                                CHECK_ALIGN(ep_desc);
+				assert(sb_desc != NULL);
 				
-				memcpy(urb->transfer_buffer + urb_priv->rx_offset,
-				       phys_to_virt(myNextRxDesc->buf), myNextRxDesc->hw_len);
+				ep_desc->sub = virt_to_phys(sb_desc);
+				ep_desc->hw_len = 0;
+				ep_desc->command = (IO_FIELD(USB_EP_command, epid, epid) |
+                                                    IO_STATE(USB_EP_command, enable, yes));
+
+
+                                /* Round upwards the number of packets of size maxlen
+                                   that this SB descriptor should receive. */
+                                sb_desc->sw_len = urb->transfer_buffer_length ?
+                                        (urb->transfer_buffer_length - 1) / maxlen + 1 : 0;
+                                sb_desc->next = 0;
+                                sb_desc->buf = 0;
+                                sb_desc->command = 
+                                        (IO_FIELD(USB_SB_command, rem, urb->transfer_buffer_length % maxlen) |
+                                         IO_STATE(USB_SB_command, tt, in) |
+                                         IO_STATE(USB_SB_command, eot, yes) |
+                                         IO_STATE(USB_SB_command, eol, yes));
 				
-				urb_priv->rx_offset += myNextRxDesc->hw_len;
-			}
-			
-			if (myNextRxDesc->status & IO_MASK(USB_IN_status, eot)) {
-				urb_priv->eot = 1;
+				ep_desc->next = tmp_ep->next;
+				tmp_ep->next = virt_to_phys(ep_desc);
 			}
-			
-		} else {
-			err("This is almost fatal, inpacket for epid %d which does not exist "
-			    " or is out!!!\nURB was at 0x%08lX", epid, (unsigned long)urb);
-			
-			goto skip_out;
+			i++;
 		}
+		tmp_ep = (USB_EP_Desc_t *)phys_to_virt(tmp_ep->next);
+	} while (tmp_ep != first_ep);
 
-	skip_out:
-		myPrevRxDesc = myNextRxDesc;
-		myPrevRxDesc->command |= IO_MASK(USB_IN_command, eol);
-		myLastRxDesc->command &= ~IO_MASK(USB_IN_command, eol);
-		myLastRxDesc = myPrevRxDesc;
 
-		myNextRxDesc->status = 0;
-		myNextRxDesc = phys_to_virt(myNextRxDesc->next);
-	}
+        /* Note that first_sb/last_sb doesn't apply to interrupt traffic. */
+        urb_priv->epid = epid;
+
+        /* We start the DMA sub channel without checking if it's running or not, because:
+           1) If it's already running, issuing the start command is a nop.
+           2) We avoid a test-and-set race condition. */
+        *R_DMA_CH8_SUB2_CMD = IO_STATE(R_DMA_CH8_SUB2_CMD, cmd, start);
+        
+	DBFEXIT;
 }
 
 
 
-static void cleanup_sb(USB_SB_Desc_t *sb)
+static void etrax_usb_complete_intr_urb(urb_t *urb, int status)
 {
-	USB_SB_Desc_t *next_sb;
-	
+        etrax_urb_priv_t *urb_priv = (etrax_urb_priv_t *)urb->hcpriv;
+        int epid = urb_priv->epid;
+        
 	DBFENTER;
 
-	if (sb == NULL) {
-		err("cleanup_sb was given a NULL pointer");
-		return;
-	}
+        if (status)
+                warn("Completing intr urb with status %d.", status);
 
-	while (!(sb->command & IO_MASK(USB_SB_command, eol))) {
-		next_sb = (USB_SB_Desc_t *)phys_to_virt(sb->next);
-		kmem_cache_free(usb_desc_cache, sb);
-		sb = next_sb;
-	}
+        dbg_intr("Completing intr epid %d, urb 0x%lx", epid, (unsigned long)urb);
 
-	kmem_cache_free(usb_desc_cache, sb);
+        urb->status = status;
+        urb->actual_length = urb_priv->rx_offset;
 
-	DBFEXIT;
+        dbg_intr("interrupt urb->actual_length = %d", urb->actual_length);
 
+        /* We let any non-zero status from the layer above have precedence. */
+        if (status == 0) {
+                /* USB_DISABLE_SPD means that short reads (shorter than the endpoint's max length)
+                   is to be treated as an error. */
+                if (urb->transfer_flags & USB_DISABLE_SPD) {
+                        if (urb->actual_length != 
+                            usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe))) {
+                                urb->status = -EREMOTEIO;
+                        }
+                }
+        }
+        
+        if (urb->complete) {
+                urb->complete(urb);
+        }
+			
+        /* Device driver has taken care of the data now. */
+        urb_priv->rx_offset = 0;
+        
+	DBFEXIT;
 }
 
-static void handle_control_transfer_attn(int epid, int status)
+
+static int etrax_usb_submit_isoc_urb(urb_t *urb) 
 {
-	struct urb *old_urb;
-	etrax_urb_priv_t *hc_priv;	
+	int epid;
 
 	DBFENTER;
 
-	clear_bit(epid, (void *)&ep_really_active);
-	
-	old_urb = URB_List[epid];
-	URB_List[epid] = old_urb->next;
-	
-	/* if (status == 0 && IN) find data and copy to urb */
-	if (status == 0 && usb_pipein(old_urb->pipe)) {
-		unsigned long flags;
-		etrax_urb_priv_t *urb_priv;
-
-		urb_priv = (etrax_urb_priv_t *)old_urb->hcpriv;
-		save_flags(flags);
-		cli();
-		if (urb_priv->eot == 1) {
-			old_urb->actual_length = urb_priv->rx_offset;
-			dbg_ctrl("urb_priv->rx_offset: %d in handle_control_attn", urb_priv->rx_offset);
-		} else {
-			status = -EPROTO;
-			old_urb->actual_length = 0;
-			err("(CTRL) No eot set in IN data!!! rx_offset: %d", urb_priv->rx_offset);
-		}
-
-		restore_flags(flags);
-	}
-	
-	/* If there are any more URB's in the list we'd better start sending */
-	if (URB_List[epid]) {
-		etrax_usb_do_ctrl_hw_add(URB_List[epid], epid,
-					 usb_maxpacket(URB_List[epid]->dev, URB_List[epid]->pipe,
-						       usb_pipeout(URB_List[epid]->pipe)));
-	}
-#if 1
-	else {
-		/* This means that this EP is now free, deconfigure it */
-		etrax_usb_free_epid(epid);
+	/* Is there an active epid for this urb ? */	
+	epid = etrax_usb_setup_epid(urb);
+	if (epid == -1) {
+		DBFEXIT;
+		return -ENOMEM;
 	}
-#endif
 	
-	/* Remember to free the SB's */
-	hc_priv = (etrax_urb_priv_t *)old_urb->hcpriv;
-	cleanup_sb(hc_priv->first_sb);
-	kfree(hc_priv);
+        dbg_isoc("Submitting isoc urb = 0x%lx", (unsigned long)urb);
 
-	old_urb->status = status;
-	if (old_urb->complete) {
-		old_urb->complete(old_urb);
-	}
+	/* Ok, now we got valid endpoint, lets insert some traffic */
 
-	DBFEXIT;
-}
+	urb->status = -EINPROGRESS;
 
+	/* Find the last urb in the URB_List and add this urb after that one. 
+	   Also add the traffic, that is do an isoc_hw_add. This is important
+	   to make this in "real time" since isochronous traffic is time sensitive. */
+
+        dbg_isoc("Adding isoc urb to (possibly empty) list");	
+	urb_list_add(urb, epid);
+	etrax_usb_add_to_isoc_sb_list(urb, epid);
+		
+	DBFEXIT;
 
+	return 0;
+}
 
-static void etrax_usb_hc_intr_bottom_half(void *data)
+static void etrax_usb_add_to_isoc_sb_list(urb_t *urb, int epid) 
 {
-	struct usb_reg_context *reg = (struct usb_reg_context *)data;
+
+	int i = 0;	
 	
-	int error_code;
-	int epid;
+	etrax_urb_priv_t *urb_priv; 
+	USB_SB_Desc_t *prev_sb_desc,  *next_sb_desc, *temp_sb_desc; 
 
-	__u32 r_usb_ept_data;
+	DBFENTER;
 
-	etrax_hc_t *hc = reg->hc; 
-	__u16 r_usb_rh_port_status_1;
-	__u16 r_usb_rh_port_status_2;
+	prev_sb_desc = next_sb_desc = temp_sb_desc = NULL; 
 	
-	DBFENTER;
+	urb_priv = kmalloc(sizeof(etrax_urb_priv_t), KMALLOC_FLAG);
+	assert(urb_priv != NULL);
+        memset(urb_priv, 0, sizeof(etrax_urb_priv_t));
 
-	if (reg->r_usb_irq_mask_read & IO_MASK(R_USB_IRQ_MASK_READ, port_status)) {
+	urb->hcpriv = urb_priv;
+	urb_priv->epid = epid;
+	
+	if (usb_pipeout(urb->pipe)) {
 
-		/*
-		  The Etrax RH does not include a wPortChange register, so this has
-		  to be handled in software. See section 11.16.2.6.2 in USB 1.1 spec
-		  for details.
-		*/
+                /* Not implemented yet! */
+		dbg_isoc("Transfer for epid %d is OUT", epid);
 		
-		r_usb_rh_port_status_1 = reg->r_usb_rh_port_status_1;
-		r_usb_rh_port_status_2 = reg->r_usb_rh_port_status_2;
+		/* Create one SB descriptor for each packet and link them together. */
+		for (i = 0; i < urb->number_of_packets; i++) {	
 		
-		dbg_rh("port_status pending");
-		dbg_rh("r_usb_rh_port_status_1: 0x%04X", r_usb_rh_port_status_1);
-		dbg_rh("r_usb_rh_port_status_2: 0x%04X", r_usb_rh_port_status_2);
+			next_sb_desc = (USB_SB_Desc_t*)kmem_cache_alloc(usb_desc_cache, SLAB_FLAG);
+			assert(next_sb_desc != NULL);
+			
+			next_sb_desc->command = (IO_STATE(USB_SB_command, tt, out) |
+                                                 IO_STATE(USB_SB_command, eot, yes));
+			
+			next_sb_desc->sw_len = urb->iso_frame_desc[i].length;
+			next_sb_desc->buf = virt_to_phys(urb->transfer_buffer + urb->iso_frame_desc[i].offset);
+			
+			/* First SB descriptor that belongs to this urb */	
+			if (i == 0) 
+				urb_priv->first_sb = next_sb_desc;
+			else
+				prev_sb_desc->next = virt_to_phys(next_sb_desc);			
+						
+			prev_sb_desc = next_sb_desc;	
+		}
+		/* Check if full length transfer. */
+		if (urb->iso_frame_desc[urb->number_of_packets - 1].length ==
+		    usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe))) {
+			next_sb_desc->command |= IO_STATE(USB_SB_command, full, yes);
+		}
 
-		/* C_PORT_CONNECTION is set on any transition */
-		hc->rh.wPortChange_1 |=
-			((r_usb_rh_port_status_1 & (1 << RH_PORT_CONNECTION)) !=
-			 (hc->rh.prev_wPortStatus_1 & (1 << RH_PORT_CONNECTION))) ?
-			(1 << RH_PORT_CONNECTION) : 0;
-		
-		hc->rh.wPortChange_2 |=
-			((r_usb_rh_port_status_2 & (1 << RH_PORT_CONNECTION)) !=
-			 (hc->rh.prev_wPortStatus_2 & (1 << RH_PORT_CONNECTION))) ?
-			(1 << RH_PORT_CONNECTION) : 0;
+		next_sb_desc->command |= IO_STATE(USB_SB_command, eol, yes);
+		next_sb_desc->next = 0;
+		urb_priv->last_sb = next_sb_desc;
+	
+	} else if (usb_pipein(urb->pipe)) {
 
-		/* C_PORT_ENABLE is _only_ set on a one to zero transition */
-		hc->rh.wPortChange_1 |=
-			((hc->rh.prev_wPortStatus_1 & (1 << RH_PORT_ENABLE))
-			 && !(r_usb_rh_port_status_1 & (1 << RH_PORT_ENABLE))) ?
-			(1 << RH_PORT_ENABLE) : 0;
-		
-		hc->rh.wPortChange_2 |=
-			((hc->rh.prev_wPortStatus_2 & (1 << RH_PORT_ENABLE))
-			 && !(r_usb_rh_port_status_2 & (1 << RH_PORT_ENABLE))) ?
-			(1 << RH_PORT_ENABLE) : 0;
-		
-		/* C_PORT_SUSPEND seems difficult, lets ignore it.. (for now) */
-		
-		/* C_PORT_RESET is _only_ set on a transition from the resetting state
-		   to the enabled state */
-		hc->rh.wPortChange_1 |=
-			((hc->rh.prev_wPortStatus_1 & (1 << RH_PORT_RESET))
-			 && (r_usb_rh_port_status_1 & (1 << RH_PORT_ENABLE))) ?
-			(1 << RH_PORT_RESET) : 0;
-		
-		hc->rh.wPortChange_2 |=
-			((hc->rh.prev_wPortStatus_2 & (1 << RH_PORT_RESET))
-			 && (r_usb_rh_port_status_2 & (1 << RH_PORT_ENABLE))) ?
-			(1 << RH_PORT_RESET) : 0;
-		
-		hc->rh.prev_wPortStatus_1 = r_usb_rh_port_status_1;
-		hc->rh.prev_wPortStatus_2 = r_usb_rh_port_status_2;	
+		dbg_isoc("Transfer for epid %d is IN", epid);
+		dbg_isoc("transfer_buffer_length = %d", urb->transfer_buffer_length);
+		dbg_isoc("rem is calculated to %d", urb->iso_frame_desc[urb->number_of_packets - 1].length);
+		
+                /* Note that in descriptors for periodic traffic are not consumed. This means that
+                   the USB controller never propagates in the SB list. In other words, if there already
+                   is an SB descriptor in the list for this EP we don't have to do anything. */
+                if (TxIsocEPList[epid].sub == 0) {
+                        dbg_isoc("Isoc traffic not already running, allocating SB");
+                        
+                        next_sb_desc = (USB_SB_Desc_t*)kmem_cache_alloc(usb_desc_cache, SLAB_FLAG);
+                        assert(next_sb_desc != NULL);
+			
+                        next_sb_desc->command = (IO_STATE(USB_SB_command, tt, in) |
+                                                 IO_STATE(USB_SB_command, eot, yes) | 
+                                                 IO_STATE(USB_SB_command, eol, yes));
+			
+                        next_sb_desc->sw_len = urb->number_of_packets;
+                        next_sb_desc->buf = 0;
+
+                        /* The rem field is don't care for isoc traffic, so we don't set it. */
+
+                        /* Only one SB descriptor that belongs to this urb. */
+                        urb_priv->first_sb = next_sb_desc; 	
+                        urb_priv->last_sb = next_sb_desc; 	
+                
+                } else {
+                
+                        dbg_isoc("Isoc traffic already running, just setting first/last_sb");
+                        
+                        /* Each EP for isoc in will have only one SB descriptor, setup when submitting the
+                           already active urb. Note that even though we may have several first_sb/last_sb
+                           pointing at the same SB descriptor, they are freed only once (when the list has
+                           become empty). */
+                        urb_priv->first_sb = phys_to_virt(TxIsocEPList[epid].sub);
+                        urb_priv->last_sb = phys_to_virt(TxIsocEPList[epid].sub);
+                        return;
+                }
+                
+	}
+	
+	/* Find the spot to insert this urb and add it. */
+	if (TxIsocEPList[epid].sub == 0) {
+		/* First SB descriptor inserted in this list (in or out). */
+		dbg_isoc("Inserting SB desc first in list");
+		TxIsocEPList[epid].sub = virt_to_phys(urb_priv->first_sb);
+	} else {	
+		/* Isochronous traffic is already running, insert new traffic last (only out). */
+		dbg_isoc("Inserting SB desc last in list");
+		temp_sb_desc = phys_to_virt(TxIsocEPList[epid].sub);
+		while (!(temp_sb_desc->command & IO_MASK(USB_SB_command, eol))) {
+			temp_sb_desc = phys_to_virt(temp_sb_desc->next);
+		}
+		/* Next pointer must be set before eol is removed. */
+		temp_sb_desc->next = virt_to_phys(urb_priv->first_sb);	
+		/* Clear the previous end of list flag since there is a new in the
+		   added SB descriptor list. */
+		temp_sb_desc->command &= ~IO_MASK(USB_SB_command, eol);
+	}
+	
+        if (urb->transfer_flags & USB_ISO_ASAP) {
+                /* The isoc transfer should be started as soon as possible. The start_frame
+                   field is a return value if URB_ISO_ASAP was set. Comparing R_USB_FM_NUMBER
+                   with a USB Chief trace shows that the first isoc IN token is sent 2 frames
+                   later. I'm not sure how this affects usage of the start_frame field by the
+                   device driver, or how it affects things when USB_ISO_ASAP is not set, so
+                   therefore there's no compensation for the 2 frame "lag" here. */
+                urb->start_frame = (*R_USB_FM_NUMBER & 0x7ff);
+                TxIsocEPList[epid].command |= IO_STATE(USB_EP_command, enable, yes);
+                urb_priv->urb_state = STARTED;
+                dbg_isoc("URB_ISO_ASAP set, urb->start_frame set to %d", urb->start_frame);
+        } else {
+                /* Not started yet. */
+                urb_priv->urb_state = NOT_STARTED;
+        }
+        
+       /* We start the DMA sub channel without checking if it's running or not, because:
+          1) If it's already running, issuing the start command is a nop.
+          2) We avoid a test-and-set race condition. */
+        *R_DMA_CH8_SUB3_CMD = IO_STATE(R_DMA_CH8_SUB3_CMD, cmd, start);
+        
+	DBFEXIT;
+}
+
+static void etrax_usb_complete_isoc_urb(urb_t *urb, int status)
+{
+	etrax_urb_priv_t *urb_priv = (etrax_urb_priv_t *)urb->hcpriv; 
+        int epid = urb_priv->epid;
+	
+	DBFENTER;
+
+        if (status)
+                warn("Completing isoc urb with status %d.", status);
+        
+        if (usb_pipein(urb->pipe)) {
+		
+                /* Move this one down the list. */
+                urb_list_move_last(urb, epid);
+                
+                /* Mark the now first urb as started (may already be). */
+                ((etrax_urb_priv_t *)(urb_list_first(epid)->hcpriv))->urb_state = STARTED;
 	}
 
-	for (epid = 0; epid < 32; epid++) {
+	urb->status = status;
+	if (urb->complete) {
+		urb->complete(urb);
+	}
 
-		unsigned long flags;
+	DBFEXIT;
+}
 
-		save_flags(flags);
-		cli();
+static void etrax_usb_complete_urb(urb_t *urb, int status)
+{
+        switch (usb_pipetype(urb->pipe)) {
+        case PIPE_BULK:
+                etrax_usb_complete_bulk_urb(urb, status);
+                break;                
+        case PIPE_CONTROL:
+                etrax_usb_complete_ctrl_urb(urb, status);
+                break;
+        case PIPE_INTERRUPT:
+                etrax_usb_complete_intr_urb(urb, status);
+                break;
+        case PIPE_ISOCHRONOUS:
+                etrax_usb_complete_isoc_urb(urb, status);
+                break;
+        default:
+                err("Unknown pipetype");
+        }
+}
 
-		*R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid); nop();
-		r_usb_ept_data = *R_USB_EPT_DATA;
 
-		restore_flags(flags);
 
-		if (r_usb_ept_data & IO_MASK(R_USB_EPT_DATA, hold)) {
-			warn("Was hold for epid %d", epid);
-			continue;
-		}
+static void etrax_usb_hc_interrupt_top_half(int irq, void *vhc, struct pt_regs *regs)
+{
+	usb_interrupt_registers_t *reg;
+        unsigned long flags;
+	__u32 irq_mask;
+	__u8 status;
+        __u32 epid_attn;
+        __u16 port_status_1;
+	__u16 port_status_2;
+        __u32 fm_number;
 
-		if (!(r_usb_ept_data & IO_MASK(R_USB_EPT_DATA, valid))) {
-			continue;
-		}
-		
-		
-		if (test_bit(epid, (void *)&reg->r_usb_epid_attn)) {
+	DBFENTER;
 
-			if (URB_List[epid] == NULL) {
-				err("R_USB_EPT_DATA is 0x%08X", r_usb_ept_data);
-				err("submit urb has been called %lu times..", submit_urb_count);
-				err("EPID_ATTN for epid %d, with NULL entry in list", epid);
-				return;
-			}
-			
-			dbg_ep("r_usb_ept_data [%d] == 0x%08X", epid,
-			       r_usb_ept_data);
-			
-			error_code = IO_EXTRACT(R_USB_EPT_DATA, error_code,
-						r_usb_ept_data);
-			
-			if (error_code == IO_STATE_VALUE(R_USB_EPT_DATA, error_code, no_error)) {
-				/* no_error means that this urb was sucessfully sent or that we have
-				   some undefinde error*/
-				
-				if (IO_EXTRACT(R_USB_EPT_DATA, error_count_out, r_usb_ept_data) == 3 ||
-				    IO_EXTRACT(R_USB_EPT_DATA, error_count_in, r_usb_ept_data) == 3) {
-				/* Actually there were transmission errors */
-					warn("Undefined error for epid %d", epid);
-					if (usb_pipetype(URB_List[epid]->pipe) == PIPE_CONTROL) {
-						handle_control_transfer_attn(epid, -EPROTO);
-					} else if (usb_pipetype(URB_List[epid]->pipe) == PIPE_BULK) {
-						handle_bulk_transfer_attn(epid, -EPROTO);
-					} else if (usb_pipetype(URB_List[epid]->pipe) == PIPE_INTERRUPT) {
-						handle_intr_transfer_attn(epid, -EPROTO);
-					}
-							   
-				} else {
-
-					if (reg->r_usb_status & IO_MASK(R_USB_STATUS, perror)) {
-						if (usb_pipetype(URB_List[epid]->pipe) == PIPE_INTERRUPT) {
-							etrax_usb_do_intr_recover(epid);
-						} else {
-							panic("Epid attention for epid %d (none INTR), with no errors and no "
-							      "exessive retry r_usb_status is 0x%02X\n",
-							      epid, reg->r_usb_status);
-						}
-						
-					} else if (reg->r_usb_status & IO_MASK(R_USB_STATUS, ourun)) {
-						panic("Epid attention for epid %d, with no errors and no "
-						      "exessive retry r_usb_status is 0x%02X\n",
-						      epid, reg->r_usb_status);
-						
-					}
-					
-					warn("Epid attention for epid %d, with no errors and no "
-					     "exessive retry r_usb_status is 0x%02X",
-					     epid, reg->r_usb_status);
-					warn("OUT error count: %d", IO_EXTRACT(R_USB_EPT_DATA, error_count_out,
-									       r_usb_ept_data));
-					warn("IN  error count: %d", IO_EXTRACT(R_USB_EPT_DATA, error_count_in,
-									       r_usb_ept_data));
-					
+        /* Read critical registers into local variables, do kmalloc afterwards. */
+        save_flags(flags);
+        cli();
+        
+	irq_mask = *R_USB_IRQ_MASK_READ;
+        /* Reading R_USB_STATUS clears the ctl_status interrupt. Note that R_USB_STATUS
+           must be read before R_USB_EPID_ATTN since reading the latter clears the 
+           ourun and perror fields of R_USB_STATUS. */
+	status = *R_USB_STATUS;
+
+        /* Reading R_USB_EPID_ATTN clears the iso_eof, bulk_eot and epid_attn interrupts. */
+	epid_attn = *R_USB_EPID_ATTN;
+
+        /* Reading R_USB_RH_PORT_STATUS_1 and R_USB_RH_PORT_STATUS_2 clears the
+           port_status interrupt. */
+	port_status_1 = *R_USB_RH_PORT_STATUS_1;
+	port_status_2 = *R_USB_RH_PORT_STATUS_2;
+
+        /* Reading R_USB_FM_NUMBER clears the sof interrupt. */
+        /* Note: the lower 11 bits contain the actual frame number, sent with each sof. */
+        fm_number = *R_USB_FM_NUMBER;
 
-				}
-				
-			} else if (error_code == IO_STATE_VALUE(R_USB_EPT_DATA, error_code, stall)) {
-				warn("Stall for epid %d", epid);
-				if (usb_pipetype(URB_List[epid]->pipe) == PIPE_CONTROL) {
-					handle_control_transfer_attn(epid, -EPIPE);
-				} else if (usb_pipetype(URB_List[epid]->pipe) == PIPE_BULK) {
-					handle_bulk_transfer_attn(epid, -EPIPE);
-				} else if (usb_pipetype(URB_List[epid]->pipe) == PIPE_INTERRUPT) {
-					handle_intr_transfer_attn(epid, -EPIPE);
-				}
-				
-				
-			} else if (error_code == IO_STATE_VALUE(R_USB_EPT_DATA, error_code, bus_error)) {
-				panic("USB bus error for epid %d\n", epid);
-				
-			} else if (error_code == IO_STATE_VALUE(R_USB_EPT_DATA, error_code, buffer_error)) {
-				warn("Buffer error for epid %d", epid);
+        restore_flags(flags);
 
-				if (usb_pipetype(URB_List[epid]->pipe) == PIPE_CONTROL) {
-					handle_control_transfer_attn(epid, -EPROTO);
-				} else if (usb_pipetype(URB_List[epid]->pipe) == PIPE_BULK) {
-					handle_bulk_transfer_attn(epid, -EPROTO);
-				} else if (usb_pipetype(URB_List[epid]->pipe) == PIPE_INTERRUPT) {
-					handle_intr_transfer_attn(epid, -EPROTO);
-				}
+	reg = (usb_interrupt_registers_t *)kmem_cache_alloc(top_half_reg_cache, SLAB_ATOMIC);
 
-			}
-		} else if (test_bit(epid, (void *)&ep_really_active)) {
-			/* Should really be else if (testbit(really active)) */
+	assert(reg != NULL);
 
-			if (usb_pipetype(URB_List[epid]->pipe) == PIPE_CONTROL) {
+	reg->hc = (etrax_hc_t *)vhc;
 
-				if (!(TxCtrlEPList[epid].command & IO_MASK(USB_EP_command, enable))) {
-					/* Now we have to verify that this CTRL endpoint got disabled
-					   cause it reached end of list with no error */
-					
-					if (IO_EXTRACT(R_USB_EPT_DATA, error_code, r_usb_ept_data) ==
-					    IO_STATE_VALUE(R_USB_EPT_DATA, error_code, no_error)) {
-						/*
-						  This means that the endpoint has no error, is disabled
-						  and had inserted traffic,
-						  i.e. transfer sucessfully completed
-						*/
-						dbg_ctrl("Last SB for CTRL %d sent sucessfully", epid);
-						handle_control_transfer_attn(epid, 0);
-					}
-				}
-				
-			} else if (usb_pipetype(URB_List[epid]->pipe) == PIPE_BULK) {
-				if (!(TxBulkEPList[epid].command & IO_MASK(USB_EP_command, enable))) {
-					/* Now we have to verify that this BULK endpoint go disabled
-					   cause it reached end of list with no error */
-
-					if (IO_EXTRACT(R_USB_EPT_DATA, error_code, r_usb_ept_data) ==
-					    IO_STATE_VALUE(R_USB_EPT_DATA, error_code, no_error)) {
-						/*
-						  This means that the endpoint has no error, is disabled
-						  and had inserted traffic,
-						  i.e. transfer sucessfully completed
-						*/
-						dbg_bulk("Last SB for BULK %d sent sucessfully", epid);
-						handle_bulk_transfer_attn(epid, 0);
-					}
-				}
-			} else if (usb_pipetype(URB_List[epid]->pipe) == PIPE_INTERRUPT) {
-				handle_intr_transfer_attn(epid, 0);
-			}
-		}
-		
-	}	
+        /* Now put register values into kmalloc'd area. */
+        reg->r_usb_irq_mask_read = irq_mask;
+        reg->r_usb_status = status;
+        reg->r_usb_epid_attn = epid_attn;
+	reg->r_usb_rh_port_status_1 = port_status_1;
+	reg->r_usb_rh_port_status_2 = port_status_2;
+        reg->r_usb_fm_number = fm_number;
+        
+	reg->usb_bh.sync = 0;
+	reg->usb_bh.routine = etrax_usb_hc_interrupt_bottom_half;
+	reg->usb_bh.data = reg;
+
+	queue_task(&reg->usb_bh, &tq_immediate);
+	mark_bh(IMMEDIATE_BH);
+
+	DBFEXIT;
+}
+
+static void etrax_usb_hc_interrupt_bottom_half(void *data)
+{
+	usb_interrupt_registers_t *reg = (usb_interrupt_registers_t *)data;
+        __u32 irq_mask = reg->r_usb_irq_mask_read;
+	
+	DBFENTER;
 
-	kfree(reg);
+        /* Interrupts are handled in order of priority. */
+        if (irq_mask & IO_MASK(R_USB_IRQ_MASK_READ, epid_attn)) {
+                etrax_usb_hc_epid_attn_interrupt(reg);
+        }
+	if (irq_mask & IO_MASK(R_USB_IRQ_MASK_READ, port_status)) {
+                etrax_usb_hc_port_status_interrupt(reg);
+        }
+        if (irq_mask & IO_MASK(R_USB_IRQ_MASK_READ, ctl_status)) {
+                etrax_usb_hc_ctl_status_interrupt(reg);
+        }
+	if (irq_mask & IO_MASK(R_USB_IRQ_MASK_READ, iso_eof)) {
+                etrax_usb_hc_isoc_eof_interrupt();
+        } 
+	if (irq_mask & IO_MASK(R_USB_IRQ_MASK_READ, bulk_eot)) {
+                /* Update/restart the bulk start timer since obviously the channel is running. */
+                mod_timer(&bulk_start_timer, jiffies + BULK_START_TIMER_INTERVAL);
+                /* Update/restart the bulk eot timer since we just received an bulk eot interrupt. */
+                mod_timer(&bulk_eot_timer, jiffies + BULK_EOT_TIMER_INTERVAL);
+                
+                etrax_usb_hc_bulk_eot_interrupt(0);
+        }
+        
+	kmem_cache_free(top_half_reg_cache, reg);
 
 	DBFEXIT;
 }
 
 
-static void etrax_usb_hc_intr_top_half(int irq, void *vhc, struct pt_regs *regs)
+void etrax_usb_hc_isoc_eof_interrupt(void)
 {
-	struct usb_reg_context *reg;
+	urb_t *urb;
+	etrax_urb_priv_t *urb_priv;
+	int epid;
 
 	DBFENTER;
 
-	reg = (struct usb_reg_context *)kmalloc(sizeof(struct usb_reg_context), GFP_ATOMIC);
+	/* Do not check the invalid epid (it has a valid sub pointer). */
+	for (epid = 0; epid < NBR_OF_EPIDS - 1; epid++) {
+	
+		if (TxIsocEPList[epid].sub == 0) {
+			/* Nothing here to see. */
+			continue;
+		}
 
-	if (!(reg)) {
-		panic("kmalloc failed in top_half\n");
-	}
+                /* Get the first urb (if any). */
+		urb = urb_list_first(epid);
+                if (urb == 0) {
+                        warn("Ignoring NULL urb");
+                        continue;
+                }
+
+                /* Sanity check. */
+                assert(usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS);
+
+                urb_priv = (etrax_urb_priv_t *)urb->hcpriv;
+		assert(urb_priv);
+
+                if (urb_priv->urb_state == NOT_STARTED) {
+                        
+                        /* If ASAP is not set and urb->start_frame is the current frame,
+                           start the transfer. */
+                        if (!(urb->transfer_flags & USB_ISO_ASAP) && 
+                            (urb->start_frame == (*R_USB_FM_NUMBER & 0x7ff))) {
+
+                                dbg_isoc("Enabling isoc IN EP descr for epid %d", epid);
+                                TxIsocEPList[epid].command |= IO_STATE(USB_EP_command, enable, yes);
+
+                                /* This urb is now active. */
+                                urb_priv->urb_state = STARTED;
+                                continue;
+                        }
+                }
+        }
+	
+	DBFEXIT;
 
-	reg->hc = (etrax_hc_t *)vhc;
-	reg->r_usb_irq_mask_read    = *R_USB_IRQ_MASK_READ;
-	reg->r_usb_status           = *R_USB_STATUS;
+}
 
-#if 0
-	if (reg->r_usb_status & IO_MASK(R_USB_STATUS, perror)) {
-		panic("r_usb_status said perror\n");
-	}
-	if (reg->r_usb_status & IO_MASK(R_USB_STATUS, ourun)) {
-		panic("r_usb_status said ourun !!!\n");
-	}
-#endif
-	
-	reg->r_usb_epid_attn        = *R_USB_EPID_ATTN;
+void etrax_usb_hc_bulk_eot_interrupt(int timer_induced)
+{
+ 	int epid;
+ 
+        /* The technique is to run one urb at a time, wait for the eot interrupt at which
+           point the EP descriptor has been disabled. */
+        
+        DBFENTER;
+        dbg_bulk("bulk eot%s", timer_induced ? ", called by timer" : "");
+
+        for (epid = 0; epid < NBR_OF_EPIDS; epid++) {
+
+                if (!(TxBulkEPList[epid].command & IO_MASK(USB_EP_command, enable)) &&
+                    (TxBulkEPList[epid].sub != 0)) {
+
+                        urb_t *urb;
+                        etrax_urb_priv_t *urb_priv;
+                        unsigned long flags;
+                        __u32 r_usb_ept_data;
+
+                        /* Found a disabled EP descriptor which has a non-null sub pointer.
+                           Verify that this ctrl EP descriptor got disabled no errors.
+                           FIXME: Necessary to check error_code? */
+                        dbg_bulk("for epid %d?", epid);
+
+                        /* Get the first urb. */
+                        urb = urb_list_first(epid);
+
+                        /* FIXME: Could this happen for valid reasons? Why did it disappear? Because of
+                           wrong unlinking? */
+                        if (!urb) {
+                                warn("NULL urb for epid %d", epid);
+                                continue;
+                        }
+                        
+                        assert(urb);
+                        urb_priv = (etrax_urb_priv_t *)urb->hcpriv;
+                        assert(urb_priv);
+
+                        /* Sanity checks. */
+                        assert(usb_pipetype(urb->pipe) == PIPE_BULK);
+                        if (phys_to_virt(TxBulkEPList[epid].sub) != urb_priv->last_sb) {
+                                err("bulk endpoint got disabled before reaching last sb");
+                        }
+
+                        /* For bulk IN traffic, there seems to be a race condition between
+                           between the bulk eot and eop interrupts, or rather an uncertainty regarding
+                           the order in which they happen. Normally we expect the eop interrupt from
+                           DMA channel 9 to happen before the eot interrupt.
+
+                           Therefore, we complete the bulk IN urb in the rx interrupt handler instead. */
+                        
+                        if (usb_pipein(urb->pipe)) {
+                                dbg_bulk("in urb, continuing");
+                                continue;
+                        }
+                        
+                        save_flags(flags);
+                        cli();
+                        *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid); 
+                        nop();
+                        r_usb_ept_data = *R_USB_EPT_DATA;
+                        restore_flags(flags);
+                        
+                        if (IO_EXTRACT(R_USB_EPT_DATA, error_code, r_usb_ept_data) ==
+                            IO_STATE_VALUE(R_USB_EPT_DATA, error_code, no_error)) {
+                                /* This means that the endpoint has no error, is disabled
+                                   and had inserted traffic, i.e. transfer successfully completed. */
+                                etrax_usb_complete_bulk_urb(urb, 0);
+                        } else {
+                                /* Shouldn't happen. We expect errors to be caught by epid attention. */
+                                err("Found disabled bulk EP desc, error_code != no_error");
+                        }
+                }
+        }
+        
+        /* Normally, we should find (at least) one disabled EP descriptor with a valid sub pointer.
+           However, because of the uncertainty in the deliverance of the eop/eot interrupts, we may
+           not.  Also, we might find two disabled EPs when handling an eot interrupt, and then find
+           none the next time. */
+
+        DBFEXIT;
+
+}
+
+void etrax_usb_hc_epid_attn_interrupt(usb_interrupt_registers_t *reg)
+{
+        /* This function handles the epid attention interrupt.  There are a variety of reasons
+           for this interrupt to happen (Designer's Reference, p. 8 - 22 for the details):
+
+           invalid ep_id  - Invalid epid in an EP (EP disabled).
+           stall          - Not strictly an error condition (EP disabled).
+           3rd error      - Three successive transaction errors  (EP disabled).
+           buffer ourun   - Buffer overrun or underrun (EP disabled).
+           past eof1      - Intr or isoc transaction proceeds past EOF1.
+           near eof       - Intr or isoc transaction would not fit inside the frame.
+           zout transfer  - If zout transfer for a bulk endpoint (EP disabled).
+           setup transfer - If setup transfer for a non-ctrl endpoint (EP disabled). */
+
+        int epid;
+
+
+        DBFENTER;
+        
+        /* Note that we loop through all epids. We still want to catch errors for 
+           the invalid one, even though we might handle them differently. */
+        for (epid = 0; epid < NBR_OF_EPIDS; epid++) {
 
-	reg->r_usb_rh_port_status_1 = *R_USB_RH_PORT_STATUS_1;
-	reg->r_usb_rh_port_status_2 = *R_USB_RH_PORT_STATUS_2;
+		if (test_bit(epid, (void *)&reg->r_usb_epid_attn)) {
+                        
+                        urb_t *urb;
+                        __u32 r_usb_ept_data;
+                        unsigned long flags;
+                        int error_code;
+
+                        save_flags(flags);
+                        cli();                
+                        *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid); 
+                        nop();
+                        /* Note that although there are separate R_USB_EPT_DATA and R_USB_EPT_DATA_ISO
+                           registers, they are located at the same address and are of the same size.
+                           In other words, this read should be ok for isoc also. */
+                        r_usb_ept_data = *R_USB_EPT_DATA;
+                        restore_flags(flags);
+
+                        /* First some sanity checks. */
+                        if (epid == INVALID_EPID) {
+                                /* FIXME: What if it became disabled? Could seriously hurt interrupt
+				   traffic. (Use do_intr_recover.) */
+                                warn("Got epid_attn for INVALID_EPID (%d).", epid);
+				err("R_USB_EPT_DATA = 0x%x", r_usb_ept_data);
+				err("R_USB_STATUS = 0x%x", reg->r_usb_status);
+				continue;
+                        } else 	if (epid == DUMMY_EPID) {
+                                /* We definitely don't care about these ones. Besides, they are
+				   always disabled, so any possible disabling caused by the
+				   epid attention interrupt is irrelevant. */
+                                warn("Got epid_attn for DUMMY_EPID (%d).", epid);
+				continue;
+                        }
+
+                        /* Get the first urb in the urb list for this epid. We blatantly assume
+                           that only the first urb could have caused the epid attention.
+                           (For bulk and ctrl, only one urb is active at any one time. For intr
+                           and isoc we remove them once they are completed.) */
+                        urb = urb_list_first(epid);
+
+			if (urb == NULL) {
+                                err("Got epid_attn for epid %i with no urb.", epid);
+                                err("R_USB_EPT_DATA = 0x%x", r_usb_ept_data);
+                                err("R_USB_STATUS = 0x%x", reg->r_usb_status);
+				continue;
+			}
+
+                        switch (usb_pipetype(urb->pipe)) {
+                        case PIPE_BULK:
+                                warn("Got epid attn for bulk endpoint, epid %d", epid);
+                                break;                
+                        case PIPE_CONTROL:
+                                warn("Got epid attn for control endpoint, epid %d", epid);
+                                break;
+                        case PIPE_INTERRUPT:
+                                warn("Got epid attn for interrupt endpoint, epid %d", epid);
+                                break;
+                        case PIPE_ISOCHRONOUS:
+                                warn("Got epid attn for isochronous endpoint, epid %d", epid);
+                                break;
+                        }
+                        
+                        if (usb_pipetype(urb->pipe) != PIPE_ISOCHRONOUS) {        
+                                if (r_usb_ept_data & IO_MASK(R_USB_EPT_DATA, hold)) {
+                                        warn("Hold was set for epid %d.", epid);
+                                        continue;
+                                }
+                        }
+                        
+                        /* Even though error_code occupies bits 22 - 23 in both R_USB_EPT_DATA and 
+                           R_USB_EPT_DATA_ISOC, we separate them here so we don't forget in other places. */
+                        if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
+                                error_code = IO_EXTRACT(R_USB_EPT_DATA_ISO, error_code, r_usb_ept_data);
+                        } else {
+                                error_code = IO_EXTRACT(R_USB_EPT_DATA, error_code, r_usb_ept_data);
+                        }
+                        
+			/* Using IO_STATE_VALUE on R_USB_EPT_DATA should be ok for isoc also. */
+			if (error_code == IO_STATE_VALUE(R_USB_EPT_DATA, error_code, no_error)) {
 
-	reg->usb_bh.sync = 0;
-	reg->usb_bh.routine = etrax_usb_hc_intr_bottom_half;
-	reg->usb_bh.data = reg;
+                                /* Isoc traffic doesn't have error_count_in/error_count_out. */
+				if ((usb_pipetype(urb->pipe) != PIPE_ISOCHRONOUS) &&
+                                    (IO_EXTRACT(R_USB_EPT_DATA, error_count_in, r_usb_ept_data) == 3 ||
+                                     IO_EXTRACT(R_USB_EPT_DATA, error_count_out, r_usb_ept_data) == 3)) {
+				        /* 3rd error. */
+					warn("3rd error for epid %i", epid);
+                                        etrax_usb_complete_urb(urb, -EPROTO);
+
+                                } else if (reg->r_usb_status & IO_MASK(R_USB_STATUS, perror)) {
+                                        
+                                        warn("Perror for epid %d", epid);
+                                        
+                                        if (!(r_usb_ept_data & IO_MASK(R_USB_EPT_DATA, valid))) {
+                                                /* invalid ep_id */
+                                                panic("Perror because of invalid epid."
+                                                      " Deconfigured too early?");
+                                        } else {
+                                                /* past eof1, near eof, zout transfer, setup transfer */
+                                                
+                                                /* Dump the urb and the relevant EP descriptor list. */
+
+                                                __dump_urb(urb);
+						__dump_ept_data(epid);
+                                                __dump_ep_list(usb_pipetype(urb->pipe));
+                                                
+                                                panic("Something wrong with DMA descriptor contents."
+                                                      " Too much traffic inserted?");
+                                        }
+                                } else if (reg->r_usb_status & IO_MASK(R_USB_STATUS, ourun)) {
+                                        /* buffer ourun */
+                                        panic("Buffer overrun/underrun for epid %d. DMA too busy?", epid);
+                                }
 
-	queue_task(&reg->usb_bh, &tq_immediate);
-	mark_bh(IMMEDIATE_BH);
+			} else if (error_code == IO_STATE_VALUE(R_USB_EPT_DATA, error_code, stall)) {
+                                /* Not really a protocol error, just says that the endpoint gave
+                                   a stall response. Note that error_code cannot be stall for isoc. */
+                                if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
+                                        panic("Isoc traffic cannot stall");
+                                }
+                                
+                                warn("Stall for epid %d", epid);
+                                etrax_usb_complete_urb(urb, -EPIPE);
+				
+			} else if (error_code == IO_STATE_VALUE(R_USB_EPT_DATA, error_code, bus_error)) {
+                                /* Two devices responded to a transaction request. Must be resolved
+                                   by software. FIXME: Reset ports? */
+				panic("Bus error for epid %d."
+                                      " Two devices responded to transaction request",
+                                      epid);
+				
+			} else if (error_code == IO_STATE_VALUE(R_USB_EPT_DATA, error_code, buffer_error)) {
+                                /* DMA overrun or underrun. */
+				warn("Buffer overrun/underrun for epid %d. DMA too busy?", epid);
 
-	DBFEXIT;
+                                /* It seems that error_code = buffer_error in 
+                                   R_USB_EPT_DATA/R_USB_EPT_DATA_ISO and ourun = yes in R_USB_STATUS
+                                   are the same error. */
+                                etrax_usb_complete_urb(urb, -EPROTO);
+                        }               
+                }
+        }
+
+        DBFEXIT;
+        
+}
+
+void etrax_usb_bulk_start_timer_func(unsigned long dummy)
+{
+       
+        /* We might enable an EP descriptor behind the current DMA position when it's about
+           to decide that there are no more bulk traffic and it should stop the bulk channel.
+           Therefore we periodically check if the bulk channel is stopped and there is an
+           enabled bulk EP descriptor, in which case we start the bulk channel. */
+        dbg_bulk("bulk_start_timer timed out.");        
+
+        if (!(*R_DMA_CH8_SUB0_CMD & IO_MASK(R_DMA_CH8_SUB0_CMD, cmd))) {
+                int epid;
+                
+		dbg_bulk("Bulk DMA channel not running.");
+
+                for (epid = 0; epid < NBR_OF_EPIDS; epid++) {
+                        if (TxBulkEPList[epid].command & IO_MASK(USB_EP_command, enable)) {
+                                printk("Found enabled EP for epid %d, starting bulk channel.\n",
+				       epid);
+                                *R_DMA_CH8_SUB0_CMD = IO_STATE(R_DMA_CH8_SUB0_CMD, cmd, start);
+                                
+                                /* Restart the bulk eot timer since we just started the bulk channel. */
+                                mod_timer(&bulk_eot_timer, jiffies + BULK_EOT_TIMER_INTERVAL);
+
+                                /* No need to search any further. */
+                                break;
+                        }
+                }
+        } else {
+		dbg_bulk("Bulk DMA channel running.");
+	}
+}
+
+void etrax_usb_hc_port_status_interrupt(usb_interrupt_registers_t *reg)
+{
+        etrax_hc_t *hc = reg->hc;
+        __u16 r_usb_rh_port_status_1 = reg->r_usb_rh_port_status_1;
+	__u16 r_usb_rh_port_status_2 = reg->r_usb_rh_port_status_2;
+
+        DBFENTER;
+        
+        /* The Etrax RH does not include a wPortChange register, so this has to be handled in software
+           (by saving the old port status value for comparison when the port status interrupt happens).
+           See section 11.16.2.6.2 in the USB 1.1 spec for details. */
+        
+        dbg_rh("hc->rh.prev_wPortStatus_1 = 0x%x", hc->rh.prev_wPortStatus_1);
+        dbg_rh("hc->rh.prev_wPortStatus_2 = 0x%x", hc->rh.prev_wPortStatus_2);
+        dbg_rh("r_usb_rh_port_status_1 = 0x%x", r_usb_rh_port_status_1);
+        dbg_rh("r_usb_rh_port_status_2 = 0x%x", r_usb_rh_port_status_2);
+        
+        /* C_PORT_CONNECTION is set on any transition. */
+        hc->rh.wPortChange_1 |=
+                ((r_usb_rh_port_status_1 & (1 << RH_PORT_CONNECTION)) !=
+                 (hc->rh.prev_wPortStatus_1 & (1 << RH_PORT_CONNECTION))) ?
+                (1 << RH_PORT_CONNECTION) : 0;
+        
+        hc->rh.wPortChange_2 |=
+                ((r_usb_rh_port_status_2 & (1 << RH_PORT_CONNECTION)) !=
+                 (hc->rh.prev_wPortStatus_2 & (1 << RH_PORT_CONNECTION))) ?
+                (1 << RH_PORT_CONNECTION) : 0;
+        
+        /* C_PORT_ENABLE is _only_ set on a one to zero transition, i.e. when
+           the port is disabled, not when it's enabled. */
+        hc->rh.wPortChange_1 |=
+                ((hc->rh.prev_wPortStatus_1 & (1 << RH_PORT_ENABLE))
+                 && !(r_usb_rh_port_status_1 & (1 << RH_PORT_ENABLE))) ?
+                (1 << RH_PORT_ENABLE) : 0;
+	
+        hc->rh.wPortChange_2 |=
+                ((hc->rh.prev_wPortStatus_2 & (1 << RH_PORT_ENABLE))
+                 && !(r_usb_rh_port_status_2 & (1 << RH_PORT_ENABLE))) ?
+                (1 << RH_PORT_ENABLE) : 0;
+
+        /* C_PORT_SUSPEND is set to one when the device has transitioned out
+           of the suspended state, i.e. when suspend goes from one to zero. */
+        hc->rh.wPortChange_1 |=
+                ((hc->rh.prev_wPortStatus_1 & (1 << RH_PORT_SUSPEND))
+                 && !(r_usb_rh_port_status_1 & (1 << RH_PORT_SUSPEND))) ?
+                (1 << RH_PORT_SUSPEND) : 0;
+        
+        hc->rh.wPortChange_2 |=
+                ((hc->rh.prev_wPortStatus_2 & (1 << RH_PORT_SUSPEND))
+                 && !(r_usb_rh_port_status_2 & (1 << RH_PORT_SUSPEND))) ?
+                (1 << RH_PORT_SUSPEND) : 0;
+
+        
+        /* C_PORT_RESET is set when reset processing on this port is complete. */
+        hc->rh.wPortChange_1 |=
+                ((hc->rh.prev_wPortStatus_1 & (1 << RH_PORT_RESET))
+                 && !(r_usb_rh_port_status_1 & (1 << RH_PORT_RESET))) ?
+                (1 << RH_PORT_RESET) : 0;
+        
+        hc->rh.wPortChange_2 |=
+                ((hc->rh.prev_wPortStatus_2 & (1 << RH_PORT_RESET))
+                 && !(r_usb_rh_port_status_2 & (1 << RH_PORT_RESET))) ?
+                (1 << RH_PORT_RESET) : 0;
+
+        /* Save the new values for next port status change. */
+        hc->rh.prev_wPortStatus_1 = r_usb_rh_port_status_1;
+        hc->rh.prev_wPortStatus_2 = r_usb_rh_port_status_2;	
+
+        dbg_rh("hc->rh.wPortChange_1 set to 0x%x", hc->rh.wPortChange_1);
+        dbg_rh("hc->rh.wPortChange_2 set to 0x%x", hc->rh.wPortChange_2);
+        
+        DBFEXIT;
+
+}
+
+void etrax_usb_hc_ctl_status_interrupt(usb_interrupt_registers_t *reg)
+{
+        DBFENTER;
+
+        /* FIXME: What should we do if we get ourun or perror? Dump the EP and SB
+           list for the corresponding epid? */
+        if (reg->r_usb_status & IO_MASK(R_USB_STATUS, ourun)) {
+                panic("USB controller got ourun.");
+        } 
+        if (reg->r_usb_status & IO_MASK(R_USB_STATUS, perror)) {
+
+                /* Before, etrax_usb_do_intr_recover was called on this epid if it was
+                   an interrupt pipe. I don't see how re-enabling all EP descriptors
+                   will help if there was a programming error. */
+                panic("USB controller got perror.");
+        } 
+
+        if (reg->r_usb_status & IO_MASK(R_USB_STATUS, device_mode)) {
+                /* We should never operate in device mode. */
+                panic("USB controller in device mode.");
+        }
+
+        /* These if-statements could probably be nested. */
+        if (reg->r_usb_status & IO_MASK(R_USB_STATUS, host_mode)) {
+                //info("USB controller in host mode.");
+        }
+        if (reg->r_usb_status & IO_MASK(R_USB_STATUS, started)) {
+                //info("USB controller started.");
+        }
+        if (reg->r_usb_status & IO_MASK(R_USB_STATUS, running)) {
+                info("USB controller running.");
+        }
+
+        DBFEXIT;
+        
 }
 
-static int etrax_rh_submit_urb(struct urb *urb)
+
+static int etrax_rh_submit_urb(urb_t *urb)
 {
 	struct usb_device *usb_dev = urb->dev;
 	etrax_hc_t *hc = usb_dev->bus->hcpriv;
 	unsigned int pipe = urb->pipe;
-	struct usb_ctrlrequest *cmd = (struct usb_ctrlrequest *) urb->setup_packet;
+	devrequest *cmd = (devrequest *) urb->setup_packet;
 	void *data = urb->transfer_buffer;
 	int leni = urb->transfer_buffer_length;
 	int len = 0;
@@ -1999,10 +3246,14 @@
 
 	DBFENTER;
 
+        /* FIXME: What is this interrupt urb that is sent to the root hub? */
 	if (usb_pipetype (pipe) == PIPE_INTERRUPT) {
 		dbg_rh("Root-Hub submit IRQ: every %d ms", urb->interval);
 		hc->rh.urb = urb;
 		hc->rh.send = 1;
+                /* FIXME: We could probably remove this line since it's done
+                   in etrax_rh_init_int_timer. (Don't remove it from
+                   etrax_rh_init_int_timer though.) */
 		hc->rh.interval = urb->interval;
 		etrax_rh_init_int_timer(urb);
 		DBFEXIT;
@@ -2010,15 +3261,22 @@
 		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);
-
-	dbg_rh("bmRType_bReq : 0x%04X (%d)", bmRType_bReq, bmRType_bReq);
-	dbg_rh("wValue       : 0x%04X (%d)", wValue, wValue);
-	dbg_rh("wIndex       : 0x%04X (%d)", wIndex, wIndex);
-	dbg_rh("wLength      : 0x%04X (%d)", wLength, wLength);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION (2, 4, 20)
+	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);
+#else
+        bmRType_bReq = cmd->requesttype | (cmd->request << 8);
+        wValue = le16_to_cpu(cmd->value);
+        wIndex = le16_to_cpu(cmd->index);
+        wLength = le16_to_cpu(cmd->length);
+#endif
+									
+	dbg_rh("bmRType_bReq : 0x%04x (%d)", bmRType_bReq, bmRType_bReq);
+	dbg_rh("wValue       : 0x%04x (%d)", wValue, wValue);
+	dbg_rh("wIndex       : 0x%04x (%d)", wIndex, wIndex);
+	dbg_rh("wLength      : 0x%04x (%d)", wLength, wLength);
 	
 	switch (bmRType_bReq) {
 		
@@ -2050,13 +3308,11 @@
 		if (wIndex == 1) {
 			*((__u16*)data) = cpu_to_le16(hc->rh.prev_wPortStatus_1);
 			*((__u16*)data + 1) = cpu_to_le16(hc->rh.wPortChange_1);
-		}
-		else if (wIndex == 2) {
+		} else if (wIndex == 2) {
 			*((__u16*)data) = cpu_to_le16(hc->rh.prev_wPortStatus_2);
 			*((__u16*)data + 1) = cpu_to_le16(hc->rh.wPortChange_2);
-		}
-		else {
-			dbg_rh("RH_GET_STATUS whith invalid wIndex !!");
+		} else {
+			dbg_rh("RH_GET_STATUS whith invalid wIndex!");
 			OK(0);
 		}
 		
@@ -2081,9 +3337,10 @@
 		case (RH_PORT_ENABLE):
 			if (wIndex == 1) {
 
-				dbg_rh("trying to do disable of port 1");
+				dbg_rh("trying to do disable port 1");
 
 				*R_USB_PORT1_DISABLE = IO_STATE(R_USB_PORT1_DISABLE, disable, yes);
+
 				while (hc->rh.prev_wPortStatus_1 &
 				       IO_STATE(R_USB_RH_PORT_STATUS_1, enabled, yes));
 				*R_USB_PORT1_DISABLE = IO_STATE(R_USB_PORT1_DISABLE, disable, no);
@@ -2091,9 +3348,10 @@
 
 			} else if (wIndex == 2) {
 
-				dbg_rh("trying to do disable of port 2");
+				dbg_rh("trying to do disable port 2");
 				
 				*R_USB_PORT2_DISABLE = IO_STATE(R_USB_PORT2_DISABLE, disable, yes);
+
 				while (hc->rh.prev_wPortStatus_2 &
 				       IO_STATE(R_USB_RH_PORT_STATUS_2, enabled, yes));
 				*R_USB_PORT2_DISABLE = IO_STATE(R_USB_PORT2_DISABLE, disable, no);
@@ -2101,25 +3359,35 @@
 
 			} else {
 				dbg_rh("RH_CLEAR_FEATURE->RH_PORT_ENABLE "
-				       "with invalid wIndex == %d!!", wIndex);
+				       "with invalid wIndex == %d!", wIndex);
 			}			
 				
 			OK (0);
 		case (RH_PORT_SUSPEND):
-			/* Opposite to suspend should be resume, so well do a resume */
+			/* Opposite to suspend should be resume, so we'll do a resume. */
+                        /* FIXME: USB 1.1, 11.16.2.2 says:
+                           "Clearing the PORT_SUSPEND feature causes a host-initiated resume
+                           on the specified port. If the port is not in the Suspended state,
+                           the hub should treat this request as a functional no-operation."
+                           Shouldn't we check if the port is in a suspended state before
+                           resuming? */
+                        
+                        /* Make sure the controller isn't busy. */
+                        while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy));
+
 			if (wIndex == 1) {
 				*R_USB_COMMAND =
 					IO_STATE(R_USB_COMMAND, port_sel, port1) |
-					IO_STATE(R_USB_COMMAND, port_cmd, resume)|
+					IO_STATE(R_USB_COMMAND, port_cmd, resume) |
 					IO_STATE(R_USB_COMMAND, ctrl_cmd, nop);
 			} else if (wIndex == 2) {
 				*R_USB_COMMAND =
 					IO_STATE(R_USB_COMMAND, port_sel, port2) |
-					IO_STATE(R_USB_COMMAND, port_cmd, resume)|
+					IO_STATE(R_USB_COMMAND, port_cmd, resume) |
 					IO_STATE(R_USB_COMMAND, ctrl_cmd, nop);
 			} else {
 				dbg_rh("RH_CLEAR_FEATURE->RH_PORT_SUSPEND "
-				       "with invalid wIndex == %d!!", wIndex);
+				       "with invalid wIndex == %d!", wIndex);
 			}
 			
 			OK (0);
@@ -2129,26 +3397,22 @@
 			
 			if (wIndex == 1) {
 				hc->rh.wPortChange_1 &= ~(1 << RH_PORT_CONNECTION);
-			}
-			else if (wIndex == 2) {
+			} else if (wIndex == 2) {
 				hc->rh.wPortChange_2 &= ~(1 << RH_PORT_CONNECTION);
-			}
-			else {
+			} else {
 				dbg_rh("RH_CLEAR_FEATURE->RH_C_PORT_CONNECTION "
-				       "with invalid wIndex == %d!!", wIndex);
+				       "with invalid wIndex == %d!", wIndex);
 			}
 
 			OK (0);
 		case (RH_C_PORT_ENABLE):
 			if (wIndex == 1) {
 				hc->rh.wPortChange_1 &= ~(1 << RH_PORT_ENABLE);
-			}
-			else if (wIndex == 2) {
+			} else if (wIndex == 2) {
 				hc->rh.wPortChange_2 &= ~(1 << RH_PORT_ENABLE);
-			}
-			else {
+			} else {
 				dbg_rh("RH_CLEAR_FEATURE->RH_C_PORT_ENABLE "
-				       "with invalid wIndex == %d!!", wIndex);
+				       "with invalid wIndex == %d!", wIndex);
 			}
 			OK (0);
 		case (RH_C_PORT_SUSPEND):
@@ -2159,15 +3423,11 @@
 		case (RH_C_PORT_RESET):
 			if (wIndex == 1) {
 				hc->rh.wPortChange_1 &= ~(1 << RH_PORT_RESET);
-			}
-			else if (wIndex == 2) {
-				dbg_rh("This is wPortChange before clear: 0x%04X", hc->rh.wPortChange_2);
-				
+			} else if (wIndex == 2) {
 				hc->rh.wPortChange_2 &= ~(1 << RH_PORT_RESET);
-				dbg_rh("This is wPortChange after clear: 0x%04X", hc->rh.wPortChange_2);
 			} else {
 				dbg_rh("RH_CLEAR_FEATURE->RH_C_PORT_RESET "
-				       "with invalid index == %d!!", wIndex);
+				       "with invalid index == %d!", wIndex);
 			}
 
 			OK (0);
@@ -2178,6 +3438,10 @@
 	case RH_SET_FEATURE | RH_OTHER | RH_CLASS:
 		switch (wValue) {
 		case (RH_PORT_SUSPEND):
+
+                        /* Make sure the controller isn't busy. */
+                        while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy));
+
 			if (wIndex == 1) {
 				*R_USB_COMMAND =
 					IO_STATE(R_USB_COMMAND, port_sel, port1) |
@@ -2189,76 +3453,150 @@
 					IO_STATE(R_USB_COMMAND, port_cmd, suspend) |
 					IO_STATE(R_USB_COMMAND, ctrl_cmd, nop);
 			} else {
-				dbg_rh("RH_SET_FEATURE->RH_C_PORT_SUSPEND "
-				       "with invalid wIndex == %d!!", wIndex);
+				dbg_rh("RH_SET_FEATURE->RH_PORT_SUSPEND "
+				       "with invalid wIndex == %d!", wIndex);
 			}			
 
 			OK (0);
 		case (RH_PORT_RESET):
 			if (wIndex == 1) {
-				int port1_retry;
 				
-			port1_redo:
+			port_1_reset:
 				dbg_rh("Doing reset of port 1");
+                                
+                                /* Make sure the controller isn't busy. */
+                                while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy));
 
 				*R_USB_COMMAND =
+					IO_STATE(R_USB_COMMAND, port_sel, port1) | 
 					IO_STATE(R_USB_COMMAND, port_cmd, reset) |
-					IO_STATE(R_USB_COMMAND, port_sel, port1);
-
-				/* We must once again wait at least 10ms for the device to recover */
+					IO_STATE(R_USB_COMMAND, ctrl_cmd, nop);
 
-				port1_retry = 0;
-				while (!((*((volatile __u16 *)&hc->rh.prev_wPortStatus_1)) &
-					 IO_STATE(R_USB_RH_PORT_STATUS_1,
-						  enabled, yes))) {
-					printk(""); if (port1_retry++ >= 10000) {goto port1_redo;}
-				}
-				
-				/* This only seems to work if we use printk,
-				   not even schedule() works !!! WHY ?? */
+                                /* We must wait at least 10 ms for the device to recover.
+                                   15 ms should be enough. */
+                                udelay(15000);
+                                
+                                /* Wait for reset bit to go low (should be done by now). */
+                                while (hc->rh.prev_wPortStatus_1 &
+                                       IO_STATE(R_USB_RH_PORT_STATUS_1, reset, yes));
+                                
+                                /* If the port status is
+                                   1) connected and enabled then there is a device and everything is fine
+                                   2) neither connected nor enabled then there is no device, also fine
+                                   3) connected and not enabled then we try again
+                                   (Yes, there are other port status combinations besides these.) */
+
+                                if ((hc->rh.prev_wPortStatus_1 & 
+                                     IO_STATE(R_USB_RH_PORT_STATUS_1, connected, yes)) &&
+                                    (hc->rh.prev_wPortStatus_1 & 
+                                     IO_STATE(R_USB_RH_PORT_STATUS_1, enabled, no))) {
+                                        dbg_rh("Connected device on port 1, but port not enabled?"
+                                               " Trying reset again.");
+                                        goto port_2_reset;
+                                }
+
+                                /* Diagnostic printouts. */
+                                if ((hc->rh.prev_wPortStatus_1 & 
+                                     IO_STATE(R_USB_RH_PORT_STATUS_1, connected, no)) &&
+                                    (hc->rh.prev_wPortStatus_1 & 
+                                     IO_STATE(R_USB_RH_PORT_STATUS_1, enabled, no))) {
+                                        dbg_rh("No connected device on port 1");
+                                } else if ((hc->rh.prev_wPortStatus_1 & 
+                                            IO_STATE(R_USB_RH_PORT_STATUS_1, connected, yes)) &&
+                                           (hc->rh.prev_wPortStatus_1 & 
+                                            IO_STATE(R_USB_RH_PORT_STATUS_1, enabled, yes))) {
+                                        dbg_rh("Connected device on port 1, port 1 enabled");
+                                }
 
-				udelay(15000);
-			}
-			else if (wIndex == 2) {
-				int port2_retry;
-				
-			port2_redo:
+			} else if (wIndex == 2) {
+                                
+			port_2_reset:
 				dbg_rh("Doing reset of port 2");
 				
+                                /* Make sure the controller isn't busy. */
+                                while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy));
+
+                                /* Issue the reset command. */
 				*R_USB_COMMAND =
+					IO_STATE(R_USB_COMMAND, port_sel, port2) |
 					IO_STATE(R_USB_COMMAND, port_cmd, reset) |
-					IO_STATE(R_USB_COMMAND, port_sel, port2);
-
-				/* We must once again wait at least 10ms for the device to recover */
+					IO_STATE(R_USB_COMMAND, ctrl_cmd, nop);
 
-				port2_retry = 0;
-				while (!((*((volatile __u16 *)&hc->rh.prev_wPortStatus_2)) &
-					 IO_STATE(R_USB_RH_PORT_STATUS_2,
-						  enabled, yes))) {
-					printk(""); if (port2_retry++ >= 10000) {goto port2_redo;}
-				}
-				
-				/* This only seems to work if we use printk,
-				   not even schedule() works !!! WHY ?? */
+				/* We must wait at least 10 ms for the device to recover.
+                                   15 ms should be enough. */
+                                udelay(15000);
+                                
+                                /* Wait for reset bit to go low (should be done by now). */
+                                while (hc->rh.prev_wPortStatus_2 &
+                                       IO_STATE(R_USB_RH_PORT_STATUS_2, reset, yes));
+                                
+                                /* If the port status is
+                                   1) connected and enabled then there is a device and everything is fine
+                                   2) neither connected nor enabled then there is no device, also fine
+                                   3) connected and not enabled then we try again
+                                   (Yes, there are other port status combinations besides these.) */
+
+                                if ((hc->rh.prev_wPortStatus_2 & 
+                                     IO_STATE(R_USB_RH_PORT_STATUS_2, connected, yes)) &&
+                                    (hc->rh.prev_wPortStatus_2 & 
+                                     IO_STATE(R_USB_RH_PORT_STATUS_2, enabled, no))) {
+                                        dbg_rh("Connected device on port 2, but port not enabled?"
+                                               " Trying reset again.");
+                                        goto port_2_reset;
+                                }
+
+                                /* Diagnostic printouts. */
+                                if ((hc->rh.prev_wPortStatus_2 & 
+                                     IO_STATE(R_USB_RH_PORT_STATUS_2, connected, no)) &&
+                                    (hc->rh.prev_wPortStatus_2 & 
+                                     IO_STATE(R_USB_RH_PORT_STATUS_2, enabled, no))) {
+                                        dbg_rh("No connected device on port 2");
+                                } else if ((hc->rh.prev_wPortStatus_2 & 
+                                            IO_STATE(R_USB_RH_PORT_STATUS_2, connected, yes)) &&
+                                           (hc->rh.prev_wPortStatus_2 & 
+                                            IO_STATE(R_USB_RH_PORT_STATUS_2, enabled, yes))) {
+                                        dbg_rh("Connected device on port 2, port 2 enabled");
+                                }
 
-				udelay(15000);
-			}
+			} else {
+				dbg_rh("RH_SET_FEATURE->RH_PORT_RESET with invalid wIndex = %d", wIndex);
+                        }
+                        
+                        /* Make sure the controller isn't busy. */
+                        while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy));
+
+			/* If all enabled ports were disabled the host controller goes down into
+                           started mode, so we need to bring it back into the running state.
+                           (This is safe even if it's already in the running state.) */
+                        *R_USB_COMMAND =
+                                IO_STATE(R_USB_COMMAND, port_sel, nop) |
+                                IO_STATE(R_USB_COMMAND, port_cmd, reset) |
+                                IO_STATE(R_USB_COMMAND, ctrl_cmd, host_run);
 
-			/* Try to bring the HC into running state */
-			*R_USB_COMMAND =
-				IO_STATE(R_USB_COMMAND, ctrl_cmd, host_run);
-			
-			nop(); while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy));
-			
 			dbg_rh("...Done");
 			OK(0);
 
 		case (RH_PORT_POWER):
 			OK (0);	/* port power ** */
 		case (RH_PORT_ENABLE):
-			/* There is no rh port enable command in the Etrax USB interface!!!! */
+			/* There is no port enable command in the host controller, so if the
+                           port is already enabled, we do nothing. If not, we reset the port
+                           (with an ugly goto). */
+
+                        if (wIndex == 1) {
+                                if (hc->rh.prev_wPortStatus_1 & 
+                                    IO_STATE(R_USB_RH_PORT_STATUS_1, enabled, no)) {
+                                        goto port_1_reset;
+                                }
+                        } else if (wIndex == 2) {
+                                if (hc->rh.prev_wPortStatus_2 & 
+                                    IO_STATE(R_USB_RH_PORT_STATUS_2, enabled, no)) {
+                                        goto port_2_reset;
+                                }
+                        } else {
+                                dbg_rh("RH_SET_FEATURE->RH_GET_STATUS with invalid wIndex = %d", wIndex);
+                        }
 			OK (0);
-
 		}
 		break;
 		
@@ -2283,8 +3621,10 @@
 						   data, wLength);
 			if (len > 0) {
 				OK(min(leni, len));
-			} else 
+			} else {
 				stat = -EPIPE;
+                        }
+                        
 		}
 		break;
 		
@@ -2309,33 +3649,56 @@
 	urb->status = stat;
 	urb->dev = NULL;
 	if (urb->complete) {
-		urb->complete (urb);
+		urb->complete(urb);
 	}
 	DBFEXIT;
 	
 	return 0;
 }
 
+static void
+etrax_usb_bulk_eot_timer_func(unsigned long dummy)
+{
+        /* Because of a race condition in the top half, we might miss a bulk eot.
+           This timer "simulates" a bulk eot if we don't get one for a while, hopefully
+           correcting the situation. */
+        dbg_bulk("bulk_eot_timer timed out.");
+        etrax_usb_hc_bulk_eot_interrupt(1);
+}
+
 static int __init etrax_usb_hc_init(void)
 {
 	static etrax_hc_t *hc;
 	struct usb_bus *bus;
 	struct usb_device *usb_rh;
-	
+	int i;
+        
 	DBFENTER;
 
-	info("ETRAX 100LX USB-HCD %s (c) 2001 Axis Communications AB\n", usb_hcd_version);
-	
-	hc = kmalloc(sizeof(etrax_hc_t), GFP_KERNEL);
+	info("ETRAX 100LX USB-HCD %s (c) 2001-2003 Axis Communications AB\n", usb_hcd_version);
+
+ 	hc = kmalloc(sizeof(etrax_hc_t), GFP_KERNEL);
+	assert(hc != NULL);
 
 	/* We use kmem_cache_* to make sure that all DMA desc. are dword aligned */
-	usb_desc_cache = kmem_cache_create("usb_desc_cache", sizeof(USB_EP_Desc_t), 0, 0, 0, 0);
-	if (!usb_desc_cache) {
-		panic("USB Desc Cache allocation failed !!!\n");
-	}
-	
+        /* Note that we specify sizeof(USB_EP_Desc_t) as the size, but also allocate
+           SB descriptors from this cache. This is ok since sizeof(USB_EP_Desc_t) ==
+           sizeof(USB_SB_Desc_t). */
+
+	usb_desc_cache = kmem_cache_create("usb_desc_cache", sizeof(USB_EP_Desc_t), 0, 
+                                           SLAB_HWCACHE_ALIGN, 0, 0);
+	assert(usb_desc_cache != NULL);
+	
+	top_half_reg_cache = kmem_cache_create("top_half_reg_cache", 
+					       sizeof(usb_interrupt_registers_t), 
+					       0, SLAB_HWCACHE_ALIGN, 0, 0);
+	assert(top_half_reg_cache != NULL);
+
 	etrax_usb_bus = bus = usb_alloc_bus(&etrax_usb_device_operations);
 	hc->bus = bus;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION (2, 4, 20)
+	bus->bus_name="ETRAX 100LX";
+#endif
 	bus->hcpriv = hc;
 
 	/* Initalize RH to the default address.
@@ -2350,17 +3713,50 @@
 	hc->rh.prev_wPortStatus_2 = 0;
 
 	/* Initialize the intr-traffic flags */
+        /* FIXME: This isn't used. (Besides, the error field isn't initialized.) */
 	hc->intr.sleeping = 0;
 	hc->intr.wq = NULL;
 
-	/* Initially all ep's are free except ep 0 */
-	ep_usage_bitmask = 0;
-	set_bit(0, (void *)&ep_usage_bitmask);
-	ep_really_active = 0;
-	ep_out_traffic = 0;
-
-	memset(URB_List, 0, sizeof(URB_List));
+	epid_usage_bitmask = 0;
+	epid_out_traffic = 0;
 
+        /* Mark the invalid epid as being used. */
+	set_bit(INVALID_EPID, (void *)&epid_usage_bitmask);
+        *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, INVALID_EPID);
+        nop();
+        /* The valid bit should still be set ('invalid' is in our world; not the hardware's). */
+        *R_USB_EPT_DATA = (IO_STATE(R_USB_EPT_DATA, valid, yes) |
+			   IO_FIELD(R_USB_EPT_DATA, max_len, 1));
+
+	/* Mark the dummy epid as being used. */
+	set_bit(DUMMY_EPID, (void *)&epid_usage_bitmask);
+        *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, DUMMY_EPID);
+        nop();
+        *R_USB_EPT_DATA = (IO_STATE(R_USB_EPT_DATA, valid, no) |
+			   IO_FIELD(R_USB_EPT_DATA, max_len, 1));
+
+        /* Initialize the urb list by initiating a head for each list. */
+        for (i = 0; i < NBR_OF_EPIDS; i++) {
+                INIT_LIST_HEAD(&urb_list[i]);
+        }
+	spin_lock_init(&urb_list_lock);
+
+        INIT_LIST_HEAD(&urb_unlink_list);
+                        
+
+        /* Initiate the bulk start timer. */
+        init_timer(&bulk_start_timer);
+        bulk_start_timer.expires = jiffies + BULK_START_TIMER_INTERVAL;
+        bulk_start_timer.function = etrax_usb_bulk_start_timer_func;
+        add_timer(&bulk_start_timer);
+
+
+        /* Initiate the bulk eot timer. */
+        init_timer(&bulk_eot_timer);
+        bulk_eot_timer.expires = jiffies + BULK_EOT_TIMER_INTERVAL;
+        bulk_eot_timer.function = etrax_usb_bulk_eot_timer_func;
+        add_timer(&bulk_eot_timer);
+        
 	/* This code should really be moved */
 
 	if (request_dma(USB_TX_DMA_NBR, "ETRAX 100LX built-in USB (Tx)")) {
@@ -2375,48 +3771,41 @@
 		etrax_usb_hc_cleanup();
 		DBFEXIT;
 		return -1;
-	}	
-#if 0  /* Moved to head.S */
-	*R_GEN_CONFIG = genconfig_shadow =
-		(genconfig_shadow & ~(IO_MASK(R_GEN_CONFIG, usb1) |
-				      IO_MASK(R_GEN_CONFIG, usb2) |
-				      IO_MASK(R_GEN_CONFIG, dma8) |
-				      IO_MASK(R_GEN_CONFIG, dma9))) |
-		IO_STATE(R_GEN_CONFIG, dma8, usb) |
-		IO_STATE(R_GEN_CONFIG, dma9, usb)
-#ifdef CONFIG_ETRAX_USB_HOST_PORT1
-		| IO_STATE(R_GEN_CONFIG, usb1, select)
-#endif
-#ifdef CONFIG_ETRAX_USB_HOST_PORT2
-		| IO_STATE(R_GEN_CONFIG, usb2, select)
-#endif
-	;
-#endif
+	}
+
+        /* Set up the data structures for USB traffic. Note that this must be done before
+           any interrupt that relies on sane DMA list occurrs. */
+	init_rx_buffers();
+	init_tx_bulk_ep();
+	init_tx_ctrl_ep();
+	init_tx_intr_ep();
+	init_tx_isoc_ep();
+
 	
 	usb_register_bus(hc->bus);
 
-	/* We may have to set more bits, but these are the obvious ones */
+        /* Note that these interrupts are not used. */
 	*R_IRQ_MASK2_SET =
 		IO_STATE(R_IRQ_MASK2_SET, dma8_sub0_descr, set) |
 		IO_STATE(R_IRQ_MASK2_SET, dma8_sub1_descr, set) |
 		IO_STATE(R_IRQ_MASK2_SET, dma8_sub2_descr, set) |
 		IO_STATE(R_IRQ_MASK2_SET, dma8_sub3_descr, set);
 
+        /* Note that the dma9_descr interrupt is not used. */
 	*R_IRQ_MASK2_SET =
 		IO_STATE(R_IRQ_MASK2_SET, dma9_eop, set) |
 		IO_STATE(R_IRQ_MASK2_SET, dma9_descr, set);
 
+	/* FIXME: Enable iso_eof only when isoc traffic is running. */
 	*R_USB_IRQ_MASK_SET = 
-		IO_STATE(R_USB_IRQ_MASK_SET, ctl_status, set) |
-		IO_STATE(R_USB_IRQ_MASK_SET, ctl_eot, set) |
+                IO_STATE(R_USB_IRQ_MASK_SET, iso_eof, set) |
 		IO_STATE(R_USB_IRQ_MASK_SET, bulk_eot, set) |
-#ifdef ETRAX_USB_INTR_IRQ
-		IO_STATE(R_USB_IRQ_MASK_SET, intr_eot, set) |
-#endif
 		IO_STATE(R_USB_IRQ_MASK_SET, epid_attn, set) |
-		IO_STATE(R_USB_IRQ_MASK_SET, port_status, set);
+		IO_STATE(R_USB_IRQ_MASK_SET, port_status, set) |
+                IO_STATE(R_USB_IRQ_MASK_SET, ctl_status, set);
+
 	
-	if (request_irq(ETRAX_USB_HC_IRQ, etrax_usb_hc_intr_top_half, 0,
+	if (request_irq(ETRAX_USB_HC_IRQ, etrax_usb_hc_interrupt_top_half, 0,
 			"ETRAX 100LX built-in USB (HC)", hc)) {
 		err("Could not allocate IRQ %d for USB", ETRAX_USB_HC_IRQ);
 		etrax_usb_hc_cleanup();
@@ -2440,16 +3829,50 @@
 		return -1;
 	}
 
-	/* Reset the USB interface (configures as HC) */
+        /* R_USB_COMMAND:
+           USB commands in host mode. The fields in this register should all be
+           written to in one write. Do not read-modify-write one field at a time. A
+           write to this register will trigger events in the USB controller and an
+           incomplete command may lead to unpredictable results, and in worst case
+           even to a deadlock in the controller.
+           (Note however that the busy field is read-only, so no need to write to it.) */
+
+        /* Check the busy bit before writing to R_USB_COMMAND. */
+
+        while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy));
+ 
+	/* Reset the USB interface. */
 	*R_USB_COMMAND =
-		IO_STATE(R_USB_COMMAND, ctrl_cmd, reset) |
-		IO_STATE(R_USB_COMMAND, port_cmd, reset);
-	
-	nop(); while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy));
-#if 1
-	/* Initate PSTART to all unallocatable bit times */
-	*R_USB_FM_PSTART = IO_FIELD(R_USB_FM_PSTART, value, 10000);
-#endif
+                IO_STATE(R_USB_COMMAND, port_sel, nop) |
+		IO_STATE(R_USB_COMMAND, port_cmd, reset) |
+		IO_STATE(R_USB_COMMAND, ctrl_cmd, reset);
+	
+	/* Designer's Reference, p. 8 - 10 says we should Initate R_USB_FM_PSTART to 0x2A30 (10800),
+           to guarantee that control traffic gets 10% of the bandwidth, and periodic transfer may
+           allocate the rest (90%). This doesn't work though. Read on for a lenghty explanation.
+
+           While there is a difference between rev. 2 and rev. 3 of the ETRAX 100LX regarding the NAK
+           behaviour, it doesn't solve this problem. What happens is that a control transfer will not
+           be interrupted in its data stage when PSTART happens (the point at which periodic traffic
+           is started). Thus, if PSTART is set to 10800 and its IN or OUT token is NAKed until just before
+           PSTART happens, it will continue the IN/OUT transfer as long as it's ACKed. After it's done,
+           there may be too little time left for an isochronous transfer, causing an epid attention 
+           interrupt due to perror. The work-around for this is to let the control transfers run at the
+           end of the frame instead of at the beginning, and will be interrupted just fine if it doesn't
+           fit into the frame. However, since there will *always* be a control transfer at the beginning
+           of the frame, regardless of what we set PSTART to, that transfer might be a 64-byte transfer
+           which consumes up to 15% of the frame, leaving only 85% for periodic traffic. The solution to
+           this would be to 'dummy allocate' 5% of the frame with the usb_claim_bandwidth function to make
+           sure that the periodic transfers that are inserted will always fit in the frame.
+
+           The idea was suggested that a control transfer could be split up into several 8 byte transfers,
+           so that it would be interrupted by PSTART, but since this can't be done for an IN transfer this
+           hasn't been implemented.
+
+           The value 11960 is chosen to be just after the SOF token, with a couple of bit times extra
+           for possible bit stuffing. */
+
+        *R_USB_FM_PSTART = IO_FIELD(R_USB_FM_PSTART, value, 11960);
 
 #ifdef CONFIG_ETRAX_USB_HOST_PORT1
 	*R_USB_PORT1_DISABLE = IO_STATE(R_USB_PORT1_DISABLE, disable, no);
@@ -2459,32 +3882,28 @@
 	*R_USB_PORT2_DISABLE = IO_STATE(R_USB_PORT2_DISABLE, disable, no);
 #endif
 	
-	*R_USB_COMMAND =
-		IO_STATE(R_USB_COMMAND, ctrl_cmd, host_config) |
-		IO_STATE(R_USB_COMMAND, port_cmd, reset);
-	
-	nop(); while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy));
+        while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy));
 
+        /* Configure the USB interface as a host controller. */
 	*R_USB_COMMAND =
-		IO_STATE(R_USB_COMMAND, port_sel, port1) |
-		IO_STATE(R_USB_COMMAND, port_cmd, reset);
-
-	nop(); while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy));
+                IO_STATE(R_USB_COMMAND, port_sel, nop) |
+                IO_STATE(R_USB_COMMAND, port_cmd, reset) |
+		IO_STATE(R_USB_COMMAND, ctrl_cmd, host_config);
+
+        /* Note: Do not reset any ports here. Await the port status interrupts, to have a controlled
+           sequence of resetting the ports. If we reset both ports now, and there are devices
+           on both ports, we will get a bus error because both devices will answer the set address
+           request. */
 
-	/* Here we must wait at least 10ms so the device has time to recover */
-	udelay(15000);
+        while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy));
 
-	init_rx_buffers();
-	init_tx_bulk_ep();
-	init_tx_ctrl_ep();
-	init_tx_intr_ep();
-
-	/* This works. It seems like the host_run command only has effect when a device is connected,
-	 i.e. it has to be done when a interrup */
+        /* Start processing of USB traffic. */
 	*R_USB_COMMAND =
+		IO_STATE(R_USB_COMMAND, port_sel, nop) |
+		IO_STATE(R_USB_COMMAND, port_cmd, reset) |
 		IO_STATE(R_USB_COMMAND, ctrl_cmd, host_run);
-	
-	nop(); while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy));
+
+        while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy));
 
 	usb_rh = usb_alloc_dev(NULL, hc->bus);
 	hc->bus->root_hub = usb_rh;
@@ -2508,6 +3927,8 @@
 	free_dma(USB_RX_DMA_NBR);
 	usb_deregister_bus(etrax_usb_bus);
 
+        /* FIXME: call kmem_cache_destroy here? */
+
 	DBFEXIT;
 }
 
diff -urN linux-2.4.22-pre3/arch/cris/drivers/usb-host.h linux-2.4.22-pre4/arch/cris/drivers/usb-host.h
--- linux-2.4.22-pre3/arch/cris/drivers/usb-host.h	2001-08-15 01:22:15.000000000 -0700
+++ linux-2.4.22-pre4/arch/cris/drivers/usb-host.h	2003-07-09 16:56:27.000000000 -0700
@@ -5,27 +5,27 @@
 #include <linux/list.h>
 
 typedef struct USB_IN_Desc {
-	__u16 sw_len;
-	__u16 command;
-	unsigned long next;
-	unsigned long buf;
-	__u16 hw_len;
-	__u16 status;
+	volatile __u16 sw_len;
+	volatile __u16 command;
+	volatile unsigned long next;
+	volatile unsigned long buf;
+        volatile __u16 hw_len;
+	volatile __u16 status;
 } USB_IN_Desc_t;
 
 typedef struct USB_SB_Desc {
-	__u16 sw_len;
-	__u16 command;
-	unsigned long next;
-	unsigned long buf;
+	volatile __u16 sw_len;
+	volatile __u16 command;
+	volatile unsigned long next;
+	volatile unsigned long buf;
 	__u32 dummy;
 } USB_SB_Desc_t;
 
 typedef struct USB_EP_Desc {
-	__u16 hw_len;
-	__u16 command;
-	unsigned long sub;
-	unsigned long nep;
+	volatile __u16 hw_len;
+	volatile __u16 command;
+	volatile unsigned long sub;
+	volatile unsigned long next;
 	__u32 dummy;
 } USB_EP_Desc_t;
 
@@ -37,10 +37,10 @@
 	int interval;
 	int numports;
 	struct timer_list rh_int_timer;
-	__u16 wPortChange_1;
-	__u16 wPortChange_2;
-	__u16 prev_wPortStatus_1;
-	__u16 prev_wPortStatus_2;
+	volatile __u16 wPortChange_1;
+	volatile __u16 wPortChange_2;
+	volatile __u16 prev_wPortStatus_1;
+	volatile __u16 prev_wPortStatus_2;
 };
 
 struct etrax_usb_intr_traffic {
@@ -55,40 +55,77 @@
 	struct etrax_usb_intr_traffic intr;
 } etrax_hc_t;
 
-typedef enum {idle, eot, nodata}  etrax_usb_rx_state_t;
+typedef enum {
+        STARTED,
+        NOT_STARTED,
+        UNLINK
+} etrax_usb_urb_state_t;
+
+
 
 typedef struct etrax_usb_urb_priv {
+        /* The first_sb field is used for freeing all SB descriptors belonging
+           to an urb. The corresponding ep descriptor's sub pointer cannot be
+           used for this since the DMA advances the sub pointer as it processes
+           the sb list. */
 	USB_SB_Desc_t *first_sb;
+	/* The last_sb field referes to the last SB descriptor that belongs to
+           this urb. This is important to know so we can free the SB descriptors
+	   that ranges between first_sb and last_sb. */	
+	USB_SB_Desc_t *last_sb;
+
+        /* The rx_offset field is used in ctrl and bulk traffic to keep track
+           of the offset in the urb's transfer_buffer where incoming data should be
+           copied to. */
 	__u32 rx_offset;
-	etrax_usb_rx_state_t rx_state;
-	__u8 eot;
-	struct list_head ep_in_list;
-} etrax_urb_priv_t;
 
+	/* Counter used in isochronous transfers to keep track of the
+	   number of packets received/transmitted.  */
+        __u32 isoc_packet_counter;	
+        
+        /* This field is used to pass information about the urb's current state between
+           the various interrupt handlers (thus marked volatile). */
+	volatile etrax_usb_urb_state_t urb_state;
+
+	/* Connection between the submitted urb and ETRAX epid number */
+	__u8 epid;
+
+        /* The rx_data_list field is used for periodic traffic, to hold 
+           received data for later processing in the the complete_urb functions,
+           where the data us copied to the urb's transfer_buffer. Basically, we
+           use this intermediate storage because we don't know when it's safe to
+           reuse the transfer_buffer (FIXME?). */
+	struct list_head rx_data_list;
+} etrax_urb_priv_t;
 
-struct usb_reg_context
+/* This struct is for passing data from the top half to the bottom half. */
+typedef struct usb_interrupt_registers
 {
 	etrax_hc_t *hc;
 	__u32 r_usb_epid_attn;
 	__u8 r_usb_status;
-	__u32 r_usb_rh_port_status_1;
-	__u32 r_usb_rh_port_status_2;
+	__u16 r_usb_rh_port_status_1;
+	__u16 r_usb_rh_port_status_2;
 	__u32 r_usb_irq_mask_read;
+        __u32 r_usb_fm_number;
 	struct tq_struct usb_bh;
-#if 0
-	__u32 r_usb_ept_data[32];
-#endif
-};
+} usb_interrupt_registers_t;
 
-struct in_chunk
+/* This struct holds data we get from the rx descriptors for DMA channel 9
+   for periodic traffic (intr and isoc). */
+typedef struct rx_data
 {
 	void *data;
 	int length;
-	char epid;
 	struct list_head list;
-};
+} rx_data_t;
+
+typedef struct urb_entry
+{
+        urb_t *urb;
+	struct list_head list;
+} urb_entry_t;
 
-  
 /* --------------------------------------------------------------------------- 
    Virtual Root HUB 
    ------------------------------------------------------------------------- */
diff -urN linux-2.4.22-pre3/arch/cris/drivers/virtex.c linux-2.4.22-pre4/arch/cris/drivers/virtex.c
--- linux-2.4.22-pre3/arch/cris/drivers/virtex.c	2002-11-28 15:53:09.000000000 -0800
+++ linux-2.4.22-pre4/arch/cris/drivers/virtex.c	2003-07-09 16:56:27.000000000 -0700
@@ -7,7 +7,7 @@
 *!
 *!              The FPGA can be programmed by copying the bit-file to /dev/fpga.
 *!
-*!                cp fpga.bit > /dev/fpga
+*!                cat fpga.bit > /dev/fpga
 *!
 *!                Kernel log should look like:
 *!                  69900 bytes written
@@ -31,6 +31,9 @@
 *!  
 *! Jul 19 2002  Stefan Lundberg    Initial version.
 *! $Log: virtex.c,v $
+*! Revision 1.2  2003/02/24 07:50:30  fredriko
+*! Bugfixes and cleanups.
+*!
 *! Revision 1.1  2002/06/25 09:58:58  stefanl
 *! New FPGA driver for Platoon
 *!
@@ -40,7 +43,7 @@
 *! (C) Copyright 2002 Axis Communications AB, LUND, SWEDEN
 *!
 *!***************************************************************************/
-/* $Id: virtex.c,v 1.1 2002/06/25 09:58:58 stefanl Exp $ */
+/* $Id: virtex.c,v 1.2 2003/02/24 07:50:30 fredriko Exp $ */
 /****************** INCLUDE FILES SECTION ***********************************/
 
 #include <linux/module.h>
@@ -127,14 +130,20 @@
 
 void start_virtex_program(void)
 {
+  volatile unsigned int i=0;
   unsigned short reg_data=0;
 
   printk("Start writing to FPGA\n");
-  reg_data = SET_CS_BIT(reg_data); // FPGA unselected
+  reg_data = SET_CS_BIT(reg_data); // FPGA unselected, PROGRAM bit not set
+  WRITE_FPGA_PROG_REG(reg_data);
 
+  for(i=0;i<10;i++) { } // at least 300 ns loop
+  
   reg_data = SET_PROGRAM_BIT(reg_data);
   WRITE_FPGA_PROG_REG(reg_data);
 
+  for(i=0;i<10;i++) { } // at least 300 ns loop
+
   while(!READ_INIT); // Wait for init
   
   reg_data = SET_WRITE_BIT(reg_data);
@@ -304,6 +313,7 @@
   }
   if(copy_from_user(ptr, buf, count)) {
     printk("copy_from_user failed\n");
+    kfree(ptr);
     return -EFAULT;
   }
   
@@ -339,7 +349,7 @@
   switch (_IOC_NR(cmd)) {
     case VIRTEX_FPGA_WRITEREG:
       /* write to an FPGA register */
-      VIRTEX_DEBUG(printk("virtex wr %d %d\n", 
+      VIRTEX_DEBUG(printk("virtex wr 0x%x = 0x%x\n", 
                VIRTEX_FPGA_ARGREG(arg),
                VIRTEX_FPGA_ARGVALUE(arg)));
       
@@ -347,12 +357,12 @@
                              VIRTEX_FPGA_ARGVALUE(arg));
     case VIRTEX_FPGA_READREG:
     {
-      unsigned char val;
+      unsigned short val;
       /* read from an FPGA register */
-      VIRTEX_DEBUG(printk("virtex rd %d ", 
+      VIRTEX_DEBUG(printk("virtex rd 0x%x ", 
               VIRTEX_FPGA_ARGREG(arg)));
       val = virtex_readreg(VIRTEX_FPGA_ARGREG(arg));
-      VIRTEX_DEBUG(printk("= %d\n", val));
+      VIRTEX_DEBUG(printk("= 0x%x\n", val));
       return val;
     }					    
     default:
diff -urN linux-2.4.22-pre3/arch/cris/drivers/virtex.h linux-2.4.22-pre4/arch/cris/drivers/virtex.h
--- linux-2.4.22-pre3/arch/cris/drivers/virtex.h	2002-11-28 15:53:09.000000000 -0800
+++ linux-2.4.22-pre4/arch/cris/drivers/virtex.h	2003-07-09 16:56:27.000000000 -0700
@@ -1,7 +1,2 @@
-/* $Id: virtex.h,v 1.1 2002/06/25 09:58:58 stefanl Exp $ */
-
 int virtex_writereg(unsigned short theReg, unsigned short theValue);
 unsigned short virtex_readreg(unsigned short theReg);
-
-
-
diff -urN linux-2.4.22-pre3/arch/cris/kernel/Makefile linux-2.4.22-pre4/arch/cris/kernel/Makefile
--- linux-2.4.22-pre3/arch/cris/kernel/Makefile	2002-11-28 15:53:09.000000000 -0800
+++ linux-2.4.22-pre4/arch/cris/kernel/Makefile	2003-07-09 16:56:27.000000000 -0700
@@ -1,4 +1,3 @@
-# $Id: Makefile,v 1.10 2002/05/27 15:41:40 johana Exp $
 #
 # Makefile for the linux kernel.
 #
@@ -17,21 +16,24 @@
 
 O_TARGET := kernel.o
 
-export-objs := ksyms.o
+export-objs := crisksyms.o
 
 obj-y   := process.o signal.o entry.o traps.o irq.o \
            ptrace.o setup.o time.o sys_cris.o shadows.o \
 	   debugport.o semaphore.o
 
-obj-$(CONFIG_MODULES)    += ksyms.o
+obj-$(CONFIG_MODULES)    += crisksyms.o
 obj-$(CONFIG_ETRAX_KGDB) += kgdb.o
 obj-$(CONFIG_ETRAX_FAST_TIMER) += fasttimer.o
+obj-$(CONFIG_ETRAX_DEBUG_INTERRUPT) += debug.o
 
 # This dependency isn't caught by mkdep.  See entry.S.
 entry.o: entryoffsets.s
 
+# We don't want debug info for the dummy entryoffsets functions, and the
+# assembler gets confused when file directives are .if 0:d out.
 entryoffsets.s: entryoffsets.c
-	$(CC) $(CFLAGS) -S -c $<
+	$(CC) $(subst -g,,$(CFLAGS)) -S -c $<
 
 clean:
 
diff -urN linux-2.4.22-pre3/arch/cris/kernel/crisksyms.c linux-2.4.22-pre4/arch/cris/kernel/crisksyms.c
--- linux-2.4.22-pre3/arch/cris/kernel/crisksyms.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.4.22-pre4/arch/cris/kernel/crisksyms.c	2003-07-09 16:56:27.000000000 -0700
@@ -0,0 +1,104 @@
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/user.h>
+#include <linux/elfcore.h>
+#include <linux/sched.h>
+#include <linux/in6.h>
+#include <linux/interrupt.h>
+#include <linux/smp_lock.h>
+#include <linux/pm.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/tty.h>
+
+#include <asm/semaphore.h>
+#include <asm/processor.h>
+#include <asm/uaccess.h>
+#include <asm/checksum.h>
+#include <asm/io.h>
+#include <asm/hardirq.h>
+#include <asm/delay.h>
+#include <asm/irq.h>
+#include <asm/pgtable.h>
+#include <asm/fasttimer.h>
+
+extern void dump_thread(struct pt_regs *, struct user *);
+extern unsigned long get_cmos_time(void);
+extern void __Div(void);
+extern void __Mod(void);
+extern void __Udiv(void);
+extern void __Umod(void);
+extern void __ashrdi3(void);
+extern void iounmap(void *addr);
+
+/* Platform dependent support */
+EXPORT_SYMBOL(dump_thread);
+EXPORT_SYMBOL(enable_irq);
+EXPORT_SYMBOL(disable_irq);
+EXPORT_SYMBOL(kernel_thread);
+EXPORT_SYMBOL(get_cmos_time);
+EXPORT_SYMBOL(loops_per_usec);
+
+/* String functions */
+EXPORT_SYMBOL(memcmp);
+EXPORT_SYMBOL(memmove);
+EXPORT_SYMBOL(strtok);
+EXPORT_SYMBOL(strpbrk);
+EXPORT_SYMBOL(strstr);
+EXPORT_SYMBOL(strcpy);
+EXPORT_SYMBOL(strchr);
+EXPORT_SYMBOL(strcmp);
+EXPORT_SYMBOL(strlen);
+EXPORT_SYMBOL(strcat);
+EXPORT_SYMBOL(strncat);
+EXPORT_SYMBOL(strncmp);
+EXPORT_SYMBOL(strncpy);
+
+/* Math functions */
+EXPORT_SYMBOL_NOVERS(__Div);
+EXPORT_SYMBOL_NOVERS(__Mod);
+EXPORT_SYMBOL_NOVERS(__Udiv);
+EXPORT_SYMBOL_NOVERS(__Umod);
+EXPORT_SYMBOL_NOVERS(__ashrdi3);
+
+/* Memory functions */
+EXPORT_SYMBOL(__ioremap);
+EXPORT_SYMBOL(iounmap);
+
+/* Semaphore functions */
+EXPORT_SYMBOL(__up);
+EXPORT_SYMBOL(__down);
+EXPORT_SYMBOL(__down_interruptible);
+EXPORT_SYMBOL(__down_trylock);
+
+/* Export shadow registers for the CPU I/O pins */
+EXPORT_SYMBOL(genconfig_shadow);
+EXPORT_SYMBOL(port_pa_data_shadow);
+EXPORT_SYMBOL(port_pa_dir_shadow);
+EXPORT_SYMBOL(port_pb_data_shadow);
+EXPORT_SYMBOL(port_pb_dir_shadow);
+EXPORT_SYMBOL(port_pb_config_shadow);
+EXPORT_SYMBOL(port_g_data_shadow);
+
+/* Userspace access functions */
+EXPORT_SYMBOL(__copy_user_zeroing);
+EXPORT_SYMBOL(__copy_user);
+
+/* Cache flush functions */
+EXPORT_SYMBOL(flush_etrax_cache);
+EXPORT_SYMBOL(prepare_rx_descriptor);
+
+#undef memcpy
+#undef memset
+extern void * memset(void *, int, __kernel_size_t);
+extern void * memcpy(void *, const void *, __kernel_size_t);
+EXPORT_SYMBOL_NOVERS(memcpy);
+EXPORT_SYMBOL_NOVERS(memset);
+
+#ifdef CONFIG_ETRAX_FAST_TIMER
+/* Fast timer functions */
+EXPORT_SYMBOL(fast_timer_list);
+EXPORT_SYMBOL(start_one_shot_timer);
+EXPORT_SYMBOL(del_fast_timer);
+EXPORT_SYMBOL(schedule_usleep);
+#endif
diff -urN linux-2.4.22-pre3/arch/cris/kernel/debug.c linux-2.4.22-pre4/arch/cris/kernel/debug.c
--- linux-2.4.22-pre3/arch/cris/kernel/debug.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.4.22-pre4/arch/cris/kernel/debug.c	2003-07-09 16:56:27.000000000 -0700
@@ -0,0 +1,213 @@
+/*
+ * arch/cris/kernel/debug.c
+ * Various debug routines:
+ * o Logging of interrupt enabling/disabling. /proc/debug_interrupt
+ *   gives result and toggles if it is enabled or not.
+ *
+ * Copyright (C) 2003 Axis Communications AB
+ */
+
+#include <linux/config.h>
+#include <asm/system.h>
+#include <linux/init.h>
+#include <linux/proc_fs.h>
+#include <asm/svinto.h>
+
+#ifdef CONFIG_ETRAX_DEBUG_INTERRUPT
+#define LOG_INT_SIZE 8192
+#define DEBUG_INT_PROC_FILE "debug_interrupt"
+#define LOG_INT_SHOW_MIN_USEC 45000
+
+/* These are global and can be used to trig certain events. */
+int log_int_pos = 0;
+int log_int_size = LOG_INT_SIZE;
+int log_int_trig0_pos = 0;
+int log_int_trig1_pos = 0;
+
+int log_int_enable = 0; /* toggled every read of /proc/debug_interrupt */
+
+struct log_int_struct 
+{
+  unsigned long pc;
+  unsigned long ev_timer_data;
+};
+
+static struct log_int_struct log_ints[LOG_INT_SIZE];
+
+//static unsigned long prev_log_int_t = 0;
+static unsigned long prev_logged_ccr = 0;
+static unsigned long prev_ei_timer_data = 0;
+static unsigned long prev_di_timer_data = 0;
+
+#define CCR_EI_BIT 5
+enum {
+	INT_OLD_MASK = 0x01, INT_OLD_BIT = 0,
+	INT_NEW_MASK = 0x02, INT_NEW_BIT = 1,
+	INT_EI_CHANGE_MASK = 0x04, INT_EI_CHANGE_BIT = 2,
+	INT_ACTION_RESTORE = 0x08, INT_ACTION_RESTORE_BIT = 3,
+	INT_ACTION_MISSED = 0x10, INT_ACTION_MISSED_BIT = 4,
+	INT_ACTION_LONG = 0x20,  INT_ACTION_LONG_BIT = 5,
+
+	INT_OLD_EI = INT_OLD_MASK, INT_OLD_EI_BIT = INT_OLD_BIT,
+	INT_NEW_EI = INT_NEW_MASK, INT_NEW_EI_BIT = INT_NEW_BIT,
+	INT_EV_DI = INT_OLD_EI | INT_EI_CHANGE_MASK,
+	INT_EV_NOP_DI = 0,
+	INT_EV_EI = INT_NEW_EI | INT_EI_CHANGE_MASK,
+	INT_EV_NOP_EI = INT_OLD_EI | INT_NEW_EI, 
+	INT_EV_RESTORE_DI= INT_EV_DI | INT_EI_CHANGE_MASK | INT_ACTION_RESTORE,
+	INT_EV_RESTORE_EI= INT_EV_EI | INT_EI_CHANGE_MASK | INT_ACTION_RESTORE,
+	INT_EV_RESTORE_NOP_DI = INT_EV_NOP_DI | INT_ACTION_RESTORE,
+	INT_EV_RESTORE_NOP_EI = INT_EV_NOP_EI | INT_ACTION_RESTORE,
+};
+
+void log_int(unsigned long pc, unsigned long curr_ccr, unsigned long next_ccr)
+{
+	unsigned long t;
+	int ev;
+	static int no_change_cnt = 0;
+	
+	/* Just disable interrupts without logging here,
+	 * the caller will either do ei, di or restore
+	 */
+	__asm__ __volatile__ ("di" : : :"memory");
+	t = *R_TIMER_DATA;
+
+	if (curr_ccr & CCR_EI_MASK) {
+		prev_ei_timer_data = t;		
+		ev = INT_OLD_EI;
+	} else {
+		prev_di_timer_data = t;
+		if ( (((prev_ei_timer_data >> 8) & 0x000000FF) -
+		      ((prev_di_timer_data >> 8) & 0x000000FF)) & ~0x03)
+					    ev = INT_ACTION_LONG;
+		else
+			ev = 0;
+	}
+	if ((curr_ccr ^ prev_logged_ccr) & CCR_EI_MASK)
+		ev |= INT_ACTION_MISSED;
+	if (next_ccr & CCR_EI_MASK)
+		ev |= INT_NEW_EI;
+	if ((curr_ccr ^ next_ccr) & CCR_EI_MASK) {
+		ev |= INT_EI_CHANGE_MASK;
+		no_change_cnt = 0;
+	} else
+		no_change_cnt++;
+
+	prev_logged_ccr = next_ccr;
+	
+	if (log_int_enable &&
+	    ((ev & (INT_EI_CHANGE_MASK | INT_ACTION_MISSED | INT_ACTION_LONG))
+	     ||	(no_change_cnt < 40)) &&
+	    log_int_pos < LOG_INT_SIZE) {
+		int i;
+		i = log_int_pos;
+		log_int_pos++;
+		log_ints[i].pc = pc;
+		log_ints[i].ev_timer_data = (t & 0x00FFFFFF) |
+			((ev & 0xFF) << 24);
+
+	}
+//	__asm__ __volatile__ ("move %0,$ccr" : : "rm" (curr_ccr) : "memory");
+}
+void log_int_di(void)
+{
+  unsigned long pc;
+  unsigned long flags;
+  __asm__ __volatile__ ("move $srp,%0" : "=rm" (pc) : : "memory");
+  __asm__ __volatile__ ("move $ccr,%0" : "=rm" (flags) : : "memory");
+  log_int(pc, flags, 0);
+}
+
+void log_int_ei(void)
+{
+  unsigned long pc;
+  unsigned long flags;
+  __asm__ __volatile__ ("move $srp,%0" : "=rm" (pc) : : "memory");
+  __asm__ __volatile__ ("move $ccr,%0" : "=rm" (flags) : : "memory");
+  log_int(pc, flags, CCR_EI_MASK);
+}
+
+
+static int log_int_read_proc(char *page, char **start, off_t off, int count,
+			     int *eof, void *data)
+{
+	int i, len = 0;
+	off_t	begin = 0;	
+	int j, t0, t1, t, thigh, tlow, tdi0;
+	int di0, ei1;
+	len += sprintf(page + len, "trig0: %i trig1: %i\n", log_int_trig0_pos, log_int_trig1_pos);
+	for (i = 0; i < log_int_pos-1; i++) {
+
+		if (((log_ints[i].ev_timer_data >> 24) & INT_NEW_EI)) {
+			di0 = i;
+			while (di0 < log_int_pos-1 && (((log_ints[di0].ev_timer_data >> 24) & INT_NEW_EI)))
+				di0++;
+			ei1 = di0+1;
+			while (ei1 < log_int_pos-1 && (((log_ints[ei1].ev_timer_data >> 24) & INT_NEW_EI) == 0))
+				ei1++;
+			thigh = timer_data_to_ns(log_ints[ei1].ev_timer_data);
+			tlow = timer_data_to_ns(log_ints[di0].ev_timer_data);
+			tdi0 = timer_data_to_ns(log_ints[di0].ev_timer_data);
+			
+			t = thigh-tlow;
+			j = di0-1;		
+			if ((t > LOG_INT_SHOW_MIN_USEC) || (log_int_trig0_pos-30 < j && j < log_int_trig1_pos+30)) {
+
+			for (; j <= ei1; j++) {
+				t0 = ((log_ints[j+1].ev_timer_data & 0xFF) -
+				      (log_ints[j].ev_timer_data & 0xFF));
+				t1 = (((log_ints[j+1].ev_timer_data >> 8) & 0xFF) -
+				      ((log_ints[j].ev_timer_data >> 8) & 0xFF));
+				if (t1 == 0 || t1 == 1) {
+					if (t0 < 0)
+						t0 += 256;
+				} else {
+					if (t1 < 0)
+						t1 += 256;
+				}
+				thigh = timer_data_to_ns(log_ints[j+1].ev_timer_data);
+				tlow = timer_data_to_ns(log_ints[j].ev_timer_data);
+				t = thigh-tlow;
+				len += sprintf(page + len, "%4i PC %08lX-%08lX %08lX-%08lX %s high %i in %-6i ns %-7i to %-7i = %-5i ns, from first di %i ns %s\n",
+					       j, log_ints[j].pc, log_ints[j+1].pc,
+					       log_ints[j].ev_timer_data, log_ints[j+1].ev_timer_data,
+					       ((log_ints[j].ev_timer_data >> 24) & INT_NEW_EI)!= 0?"ei":"di", t1, t,
+					       tlow, thigh, thigh-tlow, thigh-tdi0,
+					       j==log_int_trig0_pos?"TRIG0":(j==log_int_trig1_pos?"TRIG1":""));
+				if (len+begin > off+count)
+					goto done;
+				if (len+begin < off) {
+					begin += len;
+					len = 0;
+				}
+			}
+			
+			len += sprintf(page + len,"\n");
+			i = ei1-2;
+			}
+			
+		}
+	
+	}
+	log_int_enable = 1;
+	log_int_pos = 0;
+	log_int_trig0_pos = 0;
+	log_int_trig1_pos = 0;
+	*eof = 1;
+done:
+	if (off >= len+begin)
+		return 0;
+	*start = page + (off-begin);
+	return ((count < begin+len-off) ? count : begin+len-off);	
+}
+
+static int __init
+log_int_init(void)
+{
+	create_proc_read_entry (DEBUG_INT_PROC_FILE, 0, 0, log_int_read_proc, NULL);
+	printk(KERN_INFO "/proc/" DEBUG_INT_PROC_FILE " size %i.\r\n",
+	       LOG_INT_SIZE);
+	return 0;
+}
+module_init(log_int_init);
+#endif /* CONFIG_ETRAX_DEBUG_INTERRUPT */
diff -urN linux-2.4.22-pre3/arch/cris/kernel/debugport.c linux-2.4.22-pre4/arch/cris/kernel/debugport.c
--- linux-2.4.22-pre3/arch/cris/kernel/debugport.c	2002-08-02 17:39:42.000000000 -0700
+++ linux-2.4.22-pre4/arch/cris/kernel/debugport.c	2003-07-09 16:56:27.000000000 -0700
@@ -12,6 +12,17 @@
  *    init_etrax_debug()
  *
  * $Log: debugport.c,v $
+ * Revision 1.9  2003/02/17 07:10:34  starvik
+ * Last merge was incomplete
+ *
+ * Revision 1.8  2003/02/17 06:59:00  starvik
+ * Merged printk corruption fix
+ *
+ * Revision 1.7.4.1  2003/01/27 10:21:57  starvik
+ * Solved the problem with corrupted debug output
+ *  * Wait until DMA, FIFO and pipe is empty before and after transmissions
+ *  * Buffer data until a FIFO flush can be triggered.
+ *
  * Revision 1.7  2002/04/23 15:35:50  bjornw
  * Cleaned up sercons struct and removed the waitkey ptr (2.4.19-pre)
  *
@@ -31,6 +42,7 @@
 #include <linux/console.h>
 #include <linux/init.h>
 #include <linux/major.h>
+#include <linux/delay.h>
 
 #include <asm/system.h>
 #include <asm/svinto.h>
@@ -94,6 +106,8 @@
 #define DEBUG_DMA_IRQ_CLR IO_STATE(R_IRQ_MASK2_CLR, dma4_descr, clr)
 #endif
 
+#define MIN_SIZE 32 /* Size that triggers the FIFO to flush characters to interface */
+
 /* Write a string of count length to the console (debug port) using DMA, polled
  * for completion. Interrupts are disabled during the whole process. Some
  * caution needs to be taken to not interfere with ttyS business on this port.
@@ -103,8 +117,11 @@
 console_write(struct console *co, const char *buf, unsigned int len)
 {
 	static struct etrax_dma_descr descr;
+	static struct etrax_dma_descr descr2;
+	static char tmp_buf[MIN_SIZE];
+	static int tmp_size = 0;
+
 	unsigned long flags; 
-	int in_progress;
 	
 #ifdef CONFIG_ETRAX_DEBUG_PORT_NULL
         /* no debug printout at all */
@@ -126,6 +143,37 @@
 	restore_flags(flags);
 	return;
 #endif
+	/* To make this work together with the real serial port driver
+	 * we have to make sure that everything is flushed when we leave
+	 * here. The following steps are made to assure this:
+	 * 1. Wait until DMA stops, FIFO is empty and serial port pipeline empty.
+	 * 2. Write at least half the FIFO to trigger flush to serial port.
+	 * 3. Wait until DMA stops, FIFO is empty and serial port pipeline empty.
+         */
+
+	/* Do we have enough characters to make the DMA/FIFO happy? */
+	if (tmp_size + len < MIN_SIZE)
+	{
+		int size = min((int)(MIN_SIZE - tmp_size),(int)len);
+		memcpy(&tmp_buf[tmp_size], buf, size);
+		tmp_size += size;
+		len -= size;
+        
+		/* Pad with space if complete line */
+		if (tmp_buf[tmp_size-1] == '\n')
+		{
+			memset(&tmp_buf[tmp_size-1], ' ', MIN_SIZE - tmp_size);
+			tmp_buf[MIN_SIZE - 1] = '\n';
+			tmp_size = MIN_SIZE;
+			len = 0;
+		}
+		else
+		{
+                  /* Wait for more characters */
+			restore_flags(flags);
+			return;
+		}
+	}
 
 	/* make sure the transmitter is enabled. 
 	 * NOTE: this overrides any setting done in ttySx, to 8N1, no auto-CTS.
@@ -134,37 +182,36 @@
 	 */
 
 	*DEBUG_TR_CTRL = 0x40;
+	while(*DEBUG_OCMD & 7); /* Until DMA is not running */
+	while(*DEBUG_STATUS & 0x7f); /* wait until output FIFO is empty as well */
+	udelay(200); /* Wait for last two characters to leave the serial transmitter */
 
-	/* if the tty has some ongoing business, remember it */
-
-	in_progress = *DEBUG_OCMD & 7;
-
-	if(in_progress) {
-		/* wait until the output dma channel is ready */
-		
-		while(*DEBUG_OCMD & 7) /* nothing */ ;
+	if (tmp_size)
+	{
+		descr.ctrl = len ?  0 : d_eop | d_wait | d_eol;
+		descr.sw_len = tmp_size;
+		descr.buf = virt_to_phys(tmp_buf);
+		descr.next = virt_to_phys(&descr2);
+		descr2.ctrl = d_eop | d_wait | d_eol;
+		descr2.sw_len = len;
+		descr2.buf = virt_to_phys((char*)buf);
+	}
+	else
+	{
+		descr.ctrl = d_eop | d_wait | d_eol;
+		descr.sw_len = len;
+		descr.buf = virt_to_phys((char*)buf);
 	}
 
-	descr.ctrl = d_eol;
-	descr.sw_len = len;
-	descr.buf = __pa(buf);
-
-	*DEBUG_FIRST = __pa(&descr); /* write to R_DMAx_FIRST */
+	*DEBUG_FIRST = virt_to_phys(&descr); /* write to R_DMAx_FIRST */
 	*DEBUG_OCMD = 1;       /* dma command start -> R_DMAx_CMD */
 
 	/* wait until the output dma channel is ready again */
+	while(*DEBUG_OCMD & 7);
+	while(*DEBUG_STATUS & 0x7f);
+	udelay(200);
 
-	while(*DEBUG_OCMD & 7) /* nothing */;
-
-	/* clear pending interrupts so we don't get a surprise below */
-
-	if(in_progress)
-		*DEBUG_OCLRINT = 2;  /* only clear EOP, leave DESCR for the tty */
-	else
-		*DEBUG_OCLRINT = 3;  /* clear both EOP and DESCR */
-
-	while(*DEBUG_STATUS & 0x7f); /* wait until output FIFO is empty as well */
-
+	tmp_size = 0;
 	restore_flags(flags);
 }
 
diff -urN linux-2.4.22-pre3/arch/cris/kernel/entry.S linux-2.4.22-pre4/arch/cris/kernel/entry.S
--- linux-2.4.22-pre3/arch/cris/kernel/entry.S	2003-06-13 07:51:29.000000000 -0700
+++ linux-2.4.22-pre4/arch/cris/kernel/entry.S	2003-07-09 16:56:27.000000000 -0700
@@ -1,4 +1,4 @@
-/* $Id: entry.S,v 1.40 2002/12/02 08:11:41 starvik Exp $
+/* $Id: entry.S,v 1.42 2003/07/07 09:00:50 johana Exp $
  *
  *  linux/arch/cris/entry.S
  *
@@ -7,6 +7,12 @@
  *  Authors:	Bjorn Wesen (bjornw@axis.com)
  *
  *  $Log: entry.S,v $
+ *  Revision 1.42  2003/07/07 09:00:50  johana
+ *  Added CONFIG_ETRAX_DEBUG_INTERRUPT support.
+ *
+ *  Revision 1.41  2003/06/17 13:57:50  starvik
+ *  Merge of Linux 2.4.21
+ *
  *  Revision 1.40  2002/12/02 08:11:41  starvik
  *  Merge of Linux 2.4.20
  *
@@ -358,6 +364,12 @@
 	
 _Rexit:
 	;; this epilogue MUST match the prologues in multiple_interrupt, irq.h and ptregs.h
+#ifdef CONFIG_ETRAX_DEBUG_INTERRUPT
+	move.d $pc,$r10
+	move $ccr,$r11
+	move.d	[$sp + LDCCR], $r12; regs->dccr	
+	jsr log_int
+#endif
 	pop	$r10		; frametype
 	bne	_RBFexit	; was not CRIS_FRAME_NORMAL, handle otherwise
 	addq	4, $sp		; skip orig_r10, in delayslot
@@ -449,6 +461,22 @@
 	push	$srp		         ; we keep the old/new PC on the stack 
 	add.d	$r12, $r10		 ; r10 = current tasks tss
 	move	$dccr, [$r10+LTHREAD_DCCR] ; save irq enable state
+#ifdef CONFIG_ETRAX_DEBUG_INTERRUPT
+	push $r9
+	push $r10
+	push $r11
+	push $r12
+	push $r13
+	move.d $pc,$r10
+	move $ccr,$r11
+	clear.d $r12
+	jsr log_int
+	pop $r13
+	pop $r12
+	pop $r11
+	pop $r10
+	pop $r9
+#endif
 	di
 
 	move	$usp, [$r10+LTHREAD_USP] ; save user-mode stackpointer
diff -urN linux-2.4.22-pre3/arch/cris/kernel/fasttimer.c linux-2.4.22-pre4/arch/cris/kernel/fasttimer.c
--- linux-2.4.22-pre3/arch/cris/kernel/fasttimer.c	2003-06-13 07:51:29.000000000 -0700
+++ linux-2.4.22-pre4/arch/cris/kernel/fasttimer.c	2003-07-09 16:56:27.000000000 -0700
@@ -1,10 +1,16 @@
-/* $Id: fasttimer.c,v 1.5 2002/10/15 06:21:39 starvik Exp $
+/* $Id: fasttimer.c,v 1.7 2003/04/01 14:12:07 starvik Exp $
  * linux/arch/cris/kernel/fasttimer.c
  *
  * Fast timers for ETRAX100/ETRAX100LX
  * This may be useful in other OS than Linux so use 2 space indentation...
  *
  * $Log: fasttimer.c,v $
+ * Revision 1.7  2003/04/01 14:12:07  starvik
+ * Added loglevel for lots of printks
+ *
+ * Revision 1.6  2003/02/10 17:05:44  pkj
+ * Made fast_timer_pending() public.
+ *
  * Revision 1.5  2002/10/15 06:21:39  starvik
  * Added call to init_waitqueue_head
  *
@@ -63,7 +69,7 @@
  * Revision 1.1  2000/10/26 15:49:16  johana
  * Added fasttimer, highresolution timers.
  *
- * Copyright (C) 2000,2001 2002 Axis Communications AB, Lund, Sweden
+ * Copyright (C) 2000,2001,2002,2003 Axis Communications AB, Lund, Sweden
  */
 
 #include <linux/errno.h>
@@ -118,7 +124,7 @@
 static int fast_timer_is_init = 0;
 static int fast_timer_ints = 0;
 
-static struct fast_timer *fast_timer_list = NULL;
+struct fast_timer *fast_timer_list = NULL;
 
 #ifdef DEBUG_LOG_INCLUDED
 #define DEBUG_LOG_MAX 128
@@ -313,7 +319,8 @@
     {
       if (tmp == t)
       {
-        printk("timer name: %s data: 0x%08lX already in list!\n", name, data);
+        printk(KERN_WARNING
+	       "timer name: %s data: 0x%08lX already in list!\n", name, data);
         sanity_failed++;
         return;
       }
@@ -379,11 +386,6 @@
   restore_flags(flags);
 } /* start_one_shot_timer */
 
-static inline int fast_timer_pending (const struct fast_timer * t)
-{
-  return (t->next != NULL) || (t->prev != NULL) || (t == fast_timer_list);
-}
-
 static inline int detach_fast_timer (struct fast_timer *t)
 {
   struct fast_timer *next, *prev;
@@ -770,7 +772,7 @@
       cli();
       if (t->next != nextt)
       {
-        printk("timer removed!\n");
+        printk(KERN_WARNING "timer removed!\n");
       }
       t = nextt;
     }
@@ -951,7 +953,7 @@
     int i;
 #endif
 
-    printk("fast_timer_init()\n");
+    printk(KERN_INFO "fast_timer_init()\n");
 
 #if 0 && defined(FAST_TIMER_TEST)
     for (i = 0; i <= TIMER0_DIV; i++)
@@ -971,7 +973,7 @@
     if(request_irq(TIMER1_IRQ_NBR, timer1_handler, SA_SHIRQ,
                    "fast timer int", NULL))
     {
-      printk("err: timer1 irq\n");
+      printk(KERN_CRIT "err: timer1 irq\n");
     }
     fast_timer_is_init = 1;
 #ifdef FAST_TIMER_TEST
diff -urN linux-2.4.22-pre3/arch/cris/kernel/head.S linux-2.4.22-pre4/arch/cris/kernel/head.S
--- linux-2.4.22-pre3/arch/cris/kernel/head.S	2002-08-02 17:39:42.000000000 -0700
+++ linux-2.4.22-pre4/arch/cris/kernel/head.S	2003-07-09 16:56:27.000000000 -0700
@@ -1,4 +1,4 @@
-/* $Id: head.S,v 1.44 2002/02/05 15:39:57 bjornw Exp $
+/* $Id: head.S,v 1.47 2003/07/08 09:53:35 starvik Exp $
  * 
  * Head of the kernel - alter with care
  *
@@ -7,6 +7,15 @@
  * Authors:	Bjorn Wesen (bjornw@axis.com)
  * 
  * $Log: head.S,v $
+ * Revision 1.47  2003/07/08 09:53:35  starvik
+ * Corrected last CVS log entry
+ *
+ * Revision 1.46  2003/05/15 08:07:51  gunnard
+ * Removed serial port DMA when using AGDB.
+ *
+ * Revision 1.45  2003/04/28 05:31:05  starvik
+ * Added section attributes
+ *
  * Revision 1.44  2002/02/05 15:39:57  bjornw
  * Oops.. non-CRAMFS_MAGIC should jump over the copying, not into it...
  *
@@ -311,7 +320,7 @@
 
 	;; Put this in a suitable section where we can reclaim storage
 	;; after init.
-	.section ".text.init"
+	.section ".text.init", "ax"
 _inflash:
 #ifdef CONFIG_ETRAX_ETHERNET	
 	;; Start MII clock to make sure it is running when tranceiver is reset
@@ -543,14 +552,24 @@
 #if (!defined(CONFIG_ETRAX_KGDB) || !defined(CONFIG_ETRAX_DEBUG_PORT0)) \
 	&& !defined(CONFIG_DMA_MEMCPY)
 	; DMA channels 6 and 7 to ser0, kgdb doesnt want DMA
+#if (defined(CONFIG_ETRAX_AGDB))
+	; agdb only wants DMA out, no dma in
+	or.d	  IO_STATE (R_GEN_CONFIG, dma6, serial0),$r0
+#else
 	or.d	  IO_STATE (R_GEN_CONFIG, dma7, serial0)	\
 		| IO_STATE (R_GEN_CONFIG, dma6, serial0),$r0
 #endif
+#endif
 #if !defined(CONFIG_ETRAX_KGDB) || !defined(CONFIG_ETRAX_DEBUG_PORT1)
 	; DMA channels 8 and 9 to ser1, kgdb doesnt want DMA
+#if (defined(CONFIG_ETRAX_AGDB))
+	  ; agdb only wants DMA out, no dma in
+	or.d	  IO_STATE (R_GEN_CONFIG, dma8, serial1),$r0
+#else
 	or.d	  IO_STATE (R_GEN_CONFIG, dma9, serial1)	\
 		| IO_STATE (R_GEN_CONFIG, dma8, serial1),$r0
 #endif	
+#endif	
 #ifdef CONFIG_DMA_MEMCPY
 	; 6/7 memory-memory DMA
 	or.d	  IO_STATE (R_GEN_CONFIG, dma7, intdma6)	\
@@ -561,19 +580,29 @@
 	or.w	IO_STATE (R_GEN_CONFIG, ser2, select),$r0
 #if !defined(CONFIG_ETRAX_KGDB) || !defined(CONFIG_ETRAX_DEBUG_PORT2)
 	; DMA channels 2 and 3 to ser2, kgdb doesnt want DMA
+#if (defined(CONFIG_ETRAX_AGDB))
+	; agdb only wants DMA out, no dma in
+	or.d	  IO_STATE (R_GEN_CONFIG, dma2, serial2),$r0
+#else
 	or.d	  IO_STATE (R_GEN_CONFIG, dma3, serial2)	\
 		| IO_STATE (R_GEN_CONFIG, dma2, serial2),$r0
 #endif
 #endif
+#endif
 #if defined(CONFIG_ETRAX_SERIAL_PORT3) || defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL_PORT1)
 	; Enable serial port 3
 	or.w	IO_STATE (R_GEN_CONFIG, ser3, select),$r0
 #if !defined(CONFIG_ETRAX_KGDB) || !defined(CONFIG_ETRAX_DEBUG_PORT3)
 	; DMA channels 4 and 5 to ser3, kgdb doesnt want DMA
+#if (defined(CONFIG_ETRAX_AGDB))
+	; agdb only wants DMA out, no dma in
+	or.d	  IO_STATE (R_GEN_CONFIG, dma4, serial3),$r0
+#else
 	or.d	  IO_STATE (R_GEN_CONFIG, dma5, serial3)	\
 		| IO_STATE (R_GEN_CONFIG, dma4, serial3),$r0
 #endif
 #endif
+#endif
 #if defined(CONFIG_ETRAX_PARALLEL_PORT0) || defined(CONFIG_ETRAX_ETHERNET_LPSLAVE)
 	; parport 0 enabled using DMA 2/3
 	or.w	IO_STATE (R_GEN_CONFIG, par0, select),$r0
@@ -840,5 +869,5 @@
 swapper_pg_dir = 0xc0002000
 #endif
 
-	.section ".data.init"
+	.section ".data.init", "aw"
 #include "../lib/hw_settings.S"
diff -urN linux-2.4.22-pre3/arch/cris/kernel/irq.c linux-2.4.22-pre4/arch/cris/kernel/irq.c
--- linux-2.4.22-pre3/arch/cris/kernel/irq.c	2002-02-25 11:37:52.000000000 -0800
+++ linux-2.4.22-pre4/arch/cris/kernel/irq.c	2003-07-09 16:56:27.000000000 -0700
@@ -1,8 +1,7 @@
-/* $Id: irq.c,v 1.18 2001/11/21 13:40:18 bjornw Exp $
- *
+/*
  *	linux/arch/cris/kernel/irq.c
  *
- *      Copyright (c) 2000,2001 Axis Communications AB
+ *      Copyright (c) 2000, 2001, 2002, 2003 Axis Communications AB
  *
  *      Authors: Bjorn Wesen (bjornw@axis.com)
  *
@@ -59,7 +58,7 @@
 disable_irq(unsigned int irq_nr)
 {
 	unsigned long flags;
-	
+
 	save_flags(flags);
 	cli();
 	mask_irq(irq_nr);
@@ -70,6 +69,7 @@
 enable_irq(unsigned int irq_nr)
 {
 	unsigned long flags;
+
 	save_flags(flags);
 	cli();
 	unmask_irq(irq_nr);
@@ -88,7 +88,8 @@
 	return 0;
 }
 
-irqvectptr irq_shortcuts[NR_IRQS]; /* vector of shortcut jumps after the irq prologue */
+/* vector of shortcut jumps after the irq prologue */
+irqvectptr irq_shortcuts[NR_IRQS];
 
 /* don't use set_int_vector, it bypasses the linux interrupt handlers. it is
  * global just so that the kernel gdb can use it.
@@ -104,8 +105,8 @@
 	etrax_irv->v[n + 0x20] = (irqvectptr)addr;
 }
 
-/* the breakpoint vector is obviously not made just like the normal irq handlers
- * but needs to contain _code_ to jump to addr.
+/* the breakpoint vector is obviously not made just like the normal irq
+ * handlers but needs to contain _code_ to jump to addr.
  *
  * the BREAK n instruction jumps to IBR + n * 8
  */
@@ -117,7 +118,7 @@
 	unsigned long *jaddr = (unsigned long *)(jinstr + 1);
 
 	/* if you don't know what this does, do not touch it! */
-	
+
 	*jinstr = 0x0d3f;
 	*jaddr = (unsigned long)addr;
 
@@ -163,18 +164,18 @@
 BUILD_IRQ(25, 0x2000000)
 /* IRQ 26-30 are reserved */
 BUILD_IRQ(31, 0x80000000)
- 
+
 /*
- * Pointers to the low-level handlers 
+ * Pointers to the low-level handlers
  */
 
 static void (*interrupt[NR_IRQS])(void) = {
 	NULL, NULL, IRQ2_interrupt, IRQ3_interrupt,
 	IRQ4_interrupt, IRQ5_interrupt, IRQ6_interrupt, IRQ7_interrupt,
 	IRQ8_interrupt, IRQ9_interrupt, IRQ10_interrupt, IRQ11_interrupt,
-	IRQ12_interrupt, IRQ13_interrupt, NULL, NULL,	
-	IRQ16_interrupt, IRQ17_interrupt, IRQ18_interrupt, IRQ19_interrupt,	
-	IRQ20_interrupt, IRQ21_interrupt, IRQ22_interrupt, IRQ23_interrupt,	
+	IRQ12_interrupt, IRQ13_interrupt, NULL, NULL,
+	IRQ16_interrupt, IRQ17_interrupt, IRQ18_interrupt, IRQ19_interrupt,
+	IRQ20_interrupt, IRQ21_interrupt, IRQ22_interrupt, IRQ23_interrupt,
 	IRQ24_interrupt, IRQ25_interrupt, NULL, NULL, NULL, NULL, NULL,
 	IRQ31_interrupt
 };
@@ -183,9 +184,9 @@
 	NULL, NULL, sIRQ2_interrupt, sIRQ3_interrupt,
 	sIRQ4_interrupt, sIRQ5_interrupt, sIRQ6_interrupt, sIRQ7_interrupt,
 	sIRQ8_interrupt, sIRQ9_interrupt, sIRQ10_interrupt, sIRQ11_interrupt,
-	sIRQ12_interrupt, sIRQ13_interrupt, NULL, NULL,	
-	sIRQ16_interrupt, sIRQ17_interrupt, sIRQ18_interrupt, sIRQ19_interrupt,	
-	sIRQ20_interrupt, sIRQ21_interrupt, sIRQ22_interrupt, sIRQ23_interrupt,	
+	sIRQ12_interrupt, sIRQ13_interrupt, NULL, NULL,
+	sIRQ16_interrupt, sIRQ17_interrupt, sIRQ18_interrupt, sIRQ19_interrupt,
+	sIRQ20_interrupt, sIRQ21_interrupt, sIRQ22_interrupt, sIRQ23_interrupt,
 	sIRQ24_interrupt, sIRQ25_interrupt, NULL, NULL, NULL, NULL, NULL,
 	sIRQ31_interrupt
 };
@@ -230,7 +231,7 @@
 
 	for (i = 0; i < NR_IRQS; i++) {
 		action = irq_action[i];
-		if (!action) 
+		if (!action)
 			continue;
 		len += sprintf(buf+len, "%2d: %10u %c %s",
 			i, kstat.irqs[0][i],
@@ -286,9 +287,9 @@
 }
 
 /* this function links in a handler into the chain of handlers for the
-   given irq, and if the irq has never been registred, the appropriate
-   handler is entered into the interrupt vector
-*/
+ * given irq, and if the irq has never been registred, the appropriate
+ * handler is entered into the interrupt vector
+ */
 
 int setup_etrax_irq(int irq, struct irqaction * new)
 {
@@ -322,37 +323,39 @@
 	*p = new;
 
 	if (!shared) {
-		/* if the irq wasn't registred before, enter it into the vector table
-		   and unmask it physically 
-		*/
+		/* if the irq wasn't registred before, enter it into the vector
+		 * table and unmask it physically
+		 */
 		set_int_vector(irq, interrupt[irq], sinterrupt[irq]);
 		unmask_irq(irq);
 	}
-	
+
 	restore_flags(flags);
 	return 0;
 }
 
 /* this function is called by a driver to register an irq handler
-   Valid flags:
-   SA_INTERRUPT -> it's a fast interrupt, handler called with irq disabled and
-                   no signal checking etc is performed upon exit
-   SA_SHIRQ -> the interrupt can be shared between different handlers, the handler
-                is required to check if the irq was "aimed" at it explicitely
-   SA_RANDOM -> the interrupt will add to the random generators entropy
-*/
+ * Valid flags:
+ *   SA_INTERRUPT: it's a fast interrupt, handler called with irq disabled and
+ *                 no signal checking etc is performed upon exit
+ *   SA_SHIRQ:     the interrupt can be shared between different handlers, the
+ *                 handler is required to check if the irq was "aimed" at it
+ *                 explicitely
+ *   SA_RANDOM:    the interrupt will add to the random generators entropy
+ */
 
-int request_irq(unsigned int irq, 
+int request_irq(unsigned int irq,
 		void (*handler)(int, void *, struct pt_regs *),
-		unsigned long irqflags, 
+		unsigned long irqflags,
 		const char * devname,
 		void *dev_id)
 {
 	int retval;
 	struct irqaction * action;
 
-	/* interrupts 0 and 1 are hardware breakpoint and NMI and we can't support
-	   these yet. interrupt 15 is the multiple irq, it's special. */
+	/* interrupts 0 and 1 are hardware breakpoint and NMI and we can't
+	 * support these yet. interrupt 15 is the multiple irq, it's special.
+	 */
 
 	if(irq < 2 || irq == 15 || irq >= NR_IRQS)
 		return -EINVAL;
@@ -362,7 +365,7 @@
 
 	/* allocate and fill in a handler structure and setup the irq */
 
-	action = (struct irqaction *)kmalloc(sizeof(struct irqaction), GFP_KERNEL);
+	action = kmalloc(sizeof *action, GFP_KERNEL);
 	if (!action)
 		return -ENOMEM;
 
@@ -379,7 +382,7 @@
 		kfree(action);
 	return retval;
 }
-		
+
 void free_irq(unsigned int irq, void *dev_id)
 {
 	struct irqaction * action, **p;
@@ -415,9 +418,9 @@
 	while(1);
 }
 
-/* init_IRQ() is called by start_kernel and is responsible for fixing IRQ masks and
-   setting the irq vector table to point to bad_interrupt ptrs.
-*/
+/* init_IRQ() is called by start_kernel and is responsible for fixing IRQ masks
+ * and setting the irq vector table to point to bad_interrupt ptrs.
+ */
 
 void system_call(void);  /* from entry.S */
 void do_sigtrap(void); /* from entry.S */
@@ -442,26 +445,27 @@
 
 	for(i = 0; i < NR_IRQS; i++)
 		irq_shortcuts[i] = NULL;
-        
-        for (i = 0; i < 256; i++)
-               etrax_irv->v[i] = weird_irq;
-
-        /* the entries in the break vector contain actual code to be
-           executed by the associated break handler, rather than just a jump
-           address. therefore we need to setup a default breakpoint handler
-           for all breakpoints */
+
+	for (i = 0; i < 256; i++)
+		etrax_irv->v[i] = weird_irq;
+
+	/* the entries in the break vector contain actual code to be
+	 * executed by the associated break handler, rather than just a jump
+	 * address. therefore we need to setup a default breakpoint handler
+	 * for all breakpoints
+	 */
 
 	for (i = 0; i < 16; i++)
-                set_break_vector(i, do_sigtrap);
-        
+		set_break_vector(i, do_sigtrap);
+
 	/* set all etrax irq's to the bad handlers */
 	for (i = 2; i < NR_IRQS; i++)
 		set_int_vector(i, bad_interrupt[i], 0);
-        
+
 	/* except IRQ 15 which is the multiple-IRQ handler on Etrax100 */
 
 	set_int_vector(15, multiple_interrupt, 0);
-	
+
 	/* 0 and 1 which are special breakpoint/NMI traps */
 
 	set_int_vector(0, hwbreakpoint, 0);
@@ -475,17 +479,17 @@
 
 	set_break_vector(13, system_call);
 
-        /* setup a breakpoint handler for debugging used for both user and
-           kernel mode debugging  (which is why it is not inside an ifdef
-           CONFIG_ETRAX_KGDB) */
-        set_break_vector(8, gdb_handle_breakpoint);
+	/* setup a breakpoint handler for debugging used for both user and
+	 * kernel mode debugging  (which is why it is not inside an ifdef
+	 * CONFIG_ETRAX_KGDB)
+	 */
+	set_break_vector(8, gdb_handle_breakpoint);
 
 #ifdef CONFIG_ETRAX_KGDB
 	/* setup kgdb if its enabled, and break into the debugger */
 	kgdb_init();
 	breakpoint();
 #endif
-
 }
 
 #if defined(CONFIG_PROC_FS) && defined(CONFIG_SYSCTL)
diff -urN linux-2.4.22-pre3/arch/cris/kernel/kgdb.c linux-2.4.22-pre4/arch/cris/kernel/kgdb.c
--- linux-2.4.22-pre3/arch/cris/kernel/kgdb.c	2002-11-28 15:53:09.000000000 -0800
+++ linux-2.4.22-pre4/arch/cris/kernel/kgdb.c	2003-07-09 16:56:27.000000000 -0700
@@ -18,6 +18,9 @@
 *! Jul 21 1999  Bjorn Wesen     eLinux port
 *!
 *! $Log: kgdb.c,v $
+*! Revision 1.8  2003/04/09 08:31:15  pkj
+*! Typo correction (taken from Linux 2.5).
+*!
 *! Revision 1.7  2002/07/12 09:14:56  bjornw
 *! Corrected typo
 *!
@@ -58,7 +61,7 @@
 *!
 *!---------------------------------------------------------------------------
 *!
-*! $Id: kgdb.c,v 1.7 2002/07/12 09:14:56 bjornw Exp $
+*! $Id: kgdb.c,v 1.8 2003/04/09 08:31:15 pkj Exp $
 *!
 *! (C) Copyright 1999, Axis Communications AB, LUND, SWEDEN
 *!
@@ -152,7 +155,7 @@
  *    (IPL too high, disabled, ...)
  *
  *  - The gdb stub is currently not reentrant, i.e. errors that happen therein
- *    (e.g. accesing invalid memory) may not be caught correctly. This could
+ *    (e.g. accessing invalid memory) may not be caught correctly. This could
  *    be removed in future by introducing a stack of struct registers.
  *
  */
@@ -1486,7 +1489,7 @@
   move.d   $r0,[reg+0x62]   ; Save the return address in BRP
   move     $usp,[reg+0x66]  ; USP
 
-;; get the serial character (from debugport.c) and check if its a ctrl-c
+;; get the serial character (from debugport.c) and check if it is a ctrl-c
 
   jsr getDebugChar
   cmp.b 3, $r10
diff -urN linux-2.4.22-pre3/arch/cris/kernel/process.c linux-2.4.22-pre4/arch/cris/kernel/process.c
--- linux-2.4.22-pre3/arch/cris/kernel/process.c	2003-06-13 07:51:29.000000000 -0700
+++ linux-2.4.22-pre4/arch/cris/kernel/process.c	2003-07-09 16:56:27.000000000 -0700
@@ -1,4 +1,4 @@
-/* $Id: process.c,v 1.23 2002/10/14 18:29:27 johana Exp $
+/* $Id: process.c,v 1.24 2003/03/06 14:19:32 pkj Exp $
  * 
  *  linux/arch/cris/kernel/process.c
  *
@@ -8,6 +8,9 @@
  *  Authors:   Bjorn Wesen (bjornw@axis.com)
  *
  *  $Log: process.c,v $
+ *  Revision 1.24  2003/03/06 14:19:32  pkj
+ *  Use a cpu_idle() function identical to the one used by i386.
+ *
  *  Revision 1.23  2002/10/14 18:29:27  johana
  *  Call etrax_gpio_wake_up_check() from cpu_idle() to reduce gpio latency
  *  from ~15 ms to ~6 ms.
@@ -114,7 +117,17 @@
  * region by enable_hlt/disable_hlt.
  */
 
-static int hlt_counter=0;
+static int hlt_counter;
+
+/*
+ * Powermanagement idle function, if any..
+ */
+void (*pm_idle)(void);
+
+/*
+ * Power off function, if any
+ */
+void (*pm_power_off)(void);
 
 void disable_hlt(void)
 {
@@ -125,19 +138,42 @@
 {
 	hlt_counter--;
 }
-#ifdef CONFIG_ETRAX_GPIO
-void etrax_gpio_wake_up_check(void); /* drivers/gpio.c */
-#endif
 
-int cpu_idle(void *unused)
+/*
+ * We use this if we don't have any better
+ * idle routine..
+ */
+static void default_idle(void)
 {
-	while(1) {
-		current->counter = -100;
 #ifdef CONFIG_ETRAX_GPIO
-		/* This can reduce latency from 15 ms to 6 ms */
-		etrax_gpio_wake_up_check(); /* drivers/gpio.c */
+	extern void etrax_gpio_wake_up_check(void); /* drivers/gpio.c */
+
+	/* This can reduce latency from 15 ms to 6 ms */
+	etrax_gpio_wake_up_check(); /* drivers/gpio.c */
 #endif
+}
+
+/*
+ * The idle thread. There's no useful work to be
+ * done, so just try to conserve power and have a
+ * low exit latency (ie sit in a loop waiting for
+ * somebody to say that they'd like to reschedule)
+ */
+void cpu_idle(void)
+{
+	/* endless idle loop with no priority at all */
+	init_idle();
+	current->nice = 20;
+	current->counter = -100;
+
+	while(1) {
+		void (*idle)(void) = pm_idle;
+		if (!idle)
+			idle = default_idle;
+		while (!current->need_resched)
+			idle();
 		schedule();
+		check_pgt_cache();
 	}
 }
 
@@ -191,6 +227,8 @@
 
 void machine_power_off(void)
 {
+	if (pm_power_off)
+		pm_power_off();
 }
 
 /*
diff -urN linux-2.4.22-pre3/arch/cris/kernel/setup.c linux-2.4.22-pre4/arch/cris/kernel/setup.c
--- linux-2.4.22-pre3/arch/cris/kernel/setup.c	2003-06-13 07:51:29.000000000 -0700
+++ linux-2.4.22-pre4/arch/cris/kernel/setup.c	2003-07-09 16:56:27.000000000 -0700
@@ -1,9 +1,8 @@
-/* $Id: setup.c,v 1.27 2002/10/11 12:57:15 pkj Exp $
- *
+/*
  *  linux/arch/cris/kernel/setup.c
  *
  *  Copyright (C) 1995  Linus Torvalds
- *  Copyright (c) 2001  Axis Communications AB
+ *  Copyright (c) 2001, 2002, 2003  Axis Communications AB
  */
 
 /*
@@ -94,7 +93,8 @@
 		memory_start = (unsigned long) &_end;
 	} else {
 		/* otherwise the free area starts after the ROM filesystem */
-		printk("ROM fs in RAM, size %lu bytes\n", romfs_length);
+		printk(KERN_INFO "ROM fs in RAM, size %lu bytes\n",
+		       romfs_length);
 		memory_start = romfs_start + romfs_length;
 	}
 
@@ -169,9 +169,14 @@
 
 	*cmdline_p = command_line;
 
+#ifdef CONFIG_ETRAX_CMDLINE
+	strncpy(command_line, CONFIG_ETRAX_CMDLINE, COMMAND_LINE_SIZE);
+#elif defined(CONFIG_ETRAX_ROOT_DEVICE)
 	strncpy(command_line, "root=", COMMAND_LINE_SIZE);
 	strncpy(command_line+5, CONFIG_ETRAX_ROOT_DEVICE,
 			COMMAND_LINE_SIZE-5);
+#endif
+	command_line[COMMAND_LINE_SIZE - 1] = '\0';
 
 	/* Save command line copy for /proc/cmdline */
 	
@@ -180,7 +185,7 @@
 
 	/* give credit for the CRIS port */
 
-	printk("Linux/CRIS port on ETRAX 100LX (c) 2001, 2002 Axis Communications AB\n");
+	printk(KERN_INFO "Linux/CRIS port on ETRAX 100LX (c) 2001, 2002 Axis Communications AB\n");
 
 }
 
diff -urN linux-2.4.22-pre3/arch/cris/kernel/shadows.c linux-2.4.22-pre4/arch/cris/kernel/shadows.c
--- linux-2.4.22-pre3/arch/cris/kernel/shadows.c	2001-05-01 16:04:56.000000000 -0700
+++ linux-2.4.22-pre4/arch/cris/kernel/shadows.c	2003-07-09 16:56:27.000000000 -0700
@@ -1,6 +1,5 @@
-/* $Id: shadows.c,v 1.2 2001/03/15 14:25:16 bjornw Exp $
- * 
- * Various shadow registers. Defines for these are in include/asm-etrax100/io.h
+/*
+ * Various shadow registers. Defines for these are in include/asm-cris/io.h
  */
 
 /* Shadows for internal Etrax-registers */
diff -urN linux-2.4.22-pre3/arch/cris/kernel/signal.c linux-2.4.22-pre4/arch/cris/kernel/signal.c
--- linux-2.4.22-pre3/arch/cris/kernel/signal.c	2003-06-13 07:51:29.000000000 -0700
+++ linux-2.4.22-pre4/arch/cris/kernel/signal.c	2003-07-09 16:56:27.000000000 -0700
@@ -528,7 +528,7 @@
 			case -ERESTARTNOHAND:
 				/* ERESTARTNOHAND means that the syscall should only be
 				   restarted if there was no handler for the signal, and since
-				   we only get here if there is a handler, we dont restart */
+				   we only get here if there is a handler, we don't restart */
 				regs->r10 = -EINTR;
 				break;
 
diff -urN linux-2.4.22-pre3/arch/cris/kernel/sys_cris.c linux-2.4.22-pre4/arch/cris/kernel/sys_cris.c
--- linux-2.4.22-pre3/arch/cris/kernel/sys_cris.c	2001-07-26 15:10:06.000000000 -0700
+++ linux-2.4.22-pre4/arch/cris/kernel/sys_cris.c	2003-07-09 16:56:27.000000000 -0700
@@ -1,5 +1,4 @@
-/* $Id: sys_cris.c,v 1.10 2001/06/27 21:16:15 hp Exp $
- *
+/*
  * linux/arch/cris/kernel/sys_cris.c
  *
  * This file contains various random system calls that
diff -urN linux-2.4.22-pre3/arch/cris/kernel/time.c linux-2.4.22-pre4/arch/cris/kernel/time.c
--- linux-2.4.22-pre3/arch/cris/kernel/time.c	2003-06-13 07:51:29.000000000 -0700
+++ linux-2.4.22-pre4/arch/cris/kernel/time.c	2003-07-09 16:56:27.000000000 -0700
@@ -1,9 +1,8 @@
-/* $Id: time.c,v 1.17 2002/11/15 14:49:24 oskarp Exp $
- *
+/*
  *  linux/arch/cris/kernel/time.c
  *
  *  Copyright (C) 1991, 1992, 1995  Linus Torvalds
- *  Copyright (C) 1999, 2000, 2001, 2002 Axis Communications AB
+ *  Copyright (C) 1999, 2000, 2001, 2002, 2003 Axis Communications AB
  *
  * 1994-07-02    Alan Modra
  *	fixed set_rtc_mmss, fixed time.year for >= 2000, new mktime
@@ -18,15 +17,16 @@
  * Linux/CRIS specific code:
  *
  * Authors:    Bjorn Wesen
- *             Johan Adolfsson  
+ *             Johan Adolfsson
+ *
  * 2002-03-04    Johan Adolfsson
- *      Use prescale timer at 25000 Hz instead of the baudrate timer at 
- *      19200 to get rid of the 64ppm to fast timer (and we get better 
+ *      Use prescale timer at 25000 Hz instead of the baudrate timer at
+ *      19200 to get rid of the 64ppm to fast timer (and we get better
  *      resolution within a jiffie as well.
  * 2002-03-05    Johan Adolfsson
  *      Use prescaler in do_slow_gettimeoffset() to get 1 us resolution (40ns)
  * 2002-09-06    Johan Adolfsson
- *      Handle lost ticks by checking wall_jiffies, more efficient code 
+ *      Handle lost ticks by checking wall_jiffies, more efficient code
  *      by using local vars and not the pointer argument.
  *
  */
@@ -103,6 +103,60 @@
 }
 
 
+/* Convert the clkdiv_low and clkdivb_high fiels in timer_data
+ * (from *R_TIMER_DATA) to nanoseconds (67 ns resolution)
+ */
+unsigned long timer_data_to_ns(unsigned long timer_data) 
+{
+/* low (clkdiv_low lsb toggles with 7.3728MHz so it counts
+ * with 14.7456 MHz = 67.816 ns (0-17361ns)
+ * high (clkdiv_high lsb toggles with 38.4kHz so it counts
+ * with 76.8kHz = 13020.833 ns (0-3333333 ns)
+ * By checking bit 9,8,7 we can now how to compensate the low value
+ * to get a 67 ns resolution all the way.
+Example of R_TIMER_DATA values:
+     bit 98 7   low      9 87     offset
+0289DC00 00 000 0        0 00       0
+0289DC41 00 010 64       0 01       0
+0289DC81 00 100 128      0 10       0
+0289DDC0 01 110 192      1 11       0        13020 = 192        
+0289DD01 01 000 0    256 1 00     +256    
+0289DD41 01 010 64   320 1 01     +256        
+
+0288DE80 10 100 128  384 0 10  0: -128             26040= 384  
+0288DEC1 10 110 192  448 0 11  64 -128            
+0288DE01 10 000 0    512      128 +128    
+0288DF40 11 010 64   576      192 +128             39060 
+0288DF81 11 100 128  640      256 +128   
+0288DFC1 11 110 192  704      320 +128
+                              ..393		 
+*/
+	
+	static const short timer_data_add[8] = {
+		0,   /* 00 0 */
+		0,   /* 00 1 */
+		256, /* 01 0 */
+		0,   /* 01 1 */
+		128, /* 10 0 */
+		-128,/* 10 1 */
+		128, /* 11 0 */
+		128  /* 11 1 */
+	};  
+	unsigned long ns;
+	unsigned long low;
+	unsigned long high;
+  
+	high = (((timer_data & 0x0000FE00)>>8) * 13020833)/1000;
+	ns = high;
+  
+	low = timer_data & 0xFF;
+	low += timer_data_add[(timer_data >>7) & 0x7];
+	ns += (low * 67816)/1000;
+	return ns;
+} /* timer_data_to_ns */
+
+
+
 
 #if CRIS_TEST_TIMERS 
 #define NS_TEST_SIZE 4000
@@ -274,7 +328,7 @@
 	int retval = 0;
 	int real_seconds, real_minutes, cmos_minutes;
 
-	printk("set_rtc_mmss(%lu)\n", nowtime);
+	printk(KERN_INFO "set_rtc_mmss(%lu)\n", nowtime);
 
 	if(!have_rtc)
 		return 0;
@@ -460,7 +514,8 @@
 	mon = CMOS_READ(RTC_MONTH);
 	year = CMOS_READ(RTC_YEAR);
 
-	printk("rtc: sec 0x%x min 0x%x hour 0x%x day 0x%x mon 0x%x year 0x%x\n", 
+	printk(KERN_INFO
+	       "rtc: sec 0x%x min 0x%x hour 0x%x day 0x%x mon 0x%x year 0x%x\n", 
 	       sec, min, hour, day, mon, year);
 
 	BCD_TO_BIN(sec);
@@ -585,7 +640,7 @@
 	/* enable watchdog if we should use one */
 
 #if defined(CONFIG_ETRAX_WATCHDOG) && !defined(CONFIG_SVINTO_SIM)
-	printk("Enabling watchdog...\n");
+	printk(KERN_INFO "Enabling watchdog...\n");
 	start_watchdog();
 
 	/* If we use the hardware watchdog, we want to trap it as an NMI
diff -urN linux-2.4.22-pre3/arch/cris/kernel/traps.c linux-2.4.22-pre4/arch/cris/kernel/traps.c
--- linux-2.4.22-pre3/arch/cris/kernel/traps.c	2003-06-13 07:51:29.000000000 -0700
+++ linux-2.4.22-pre4/arch/cris/kernel/traps.c	2003-07-09 16:56:27.000000000 -0700
@@ -1,12 +1,11 @@
-/* $Id: traps.c,v 1.18 2002/12/02 08:11:41 starvik Exp $
- *
+/*
  *  linux/arch/cris/traps.c
  *
- *  Here we handle the break vectors not used by the system call 
- *  mechanism, as well as some general stack/register dumping 
+ *  Here we handle the break vectors not used by the system call
+ *  mechanism, as well as some general stack/register dumping
  *  things.
- * 
- *  Copyright (C) 2000,2001 Axis Communications AB
+ *
+ *  Copyright (C) 2000, 2001, 2002, 2003 Axis Communications AB
  *
  *  Authors:   Bjorn Wesen
  *  	       Hans-Peter Nilsson
@@ -216,6 +215,8 @@
  * we have the nice doggy development flag set, we halt here
  * instead of rebooting.
  */
+extern void reset_watchdog(void);
+extern void stop_watchdog(void);
 
 void
 watchdog_bite_hook(struct pt_regs *regs)
@@ -239,9 +240,6 @@
 void 
 die_if_kernel(const char * str, struct pt_regs * regs, long err)
 {
-	extern void reset_watchdog(void);
-	extern void stop_watchdog(void);
-
 	if(user_mode(regs))
 		return;
 
diff -urN linux-2.4.22-pre3/arch/cris/lib/checksum.S linux-2.4.22-pre4/arch/cris/lib/checksum.S
--- linux-2.4.22-pre3/arch/cris/lib/checksum.S	2001-10-08 11:43:54.000000000 -0700
+++ linux-2.4.22-pre4/arch/cris/lib/checksum.S	2003-07-09 16:56:27.000000000 -0700
@@ -1,6 +1,6 @@
-/* $Id: checksum.S,v 1.6 2001/10/01 14:47:35 bjornw Exp $
+/*
  * A fast checksum routine using movem
- * Copyright (c) 1998-2001 Axis Communications AB
+ * Copyright (c) 1998-2003 Axis Communications AB
  *
  * csum_partial(const unsigned char * buff, int len, unsigned int sum)
  */
diff -urN linux-2.4.22-pre3/arch/cris/lib/checksumcopy.S linux-2.4.22-pre4/arch/cris/lib/checksumcopy.S
--- linux-2.4.22-pre3/arch/cris/lib/checksumcopy.S	2001-10-08 11:43:54.000000000 -0700
+++ linux-2.4.22-pre4/arch/cris/lib/checksumcopy.S	2003-07-09 16:56:27.000000000 -0700
@@ -1,9 +1,9 @@
-/* $Id: checksumcopy.S,v 1.7 2001/10/01 14:47:35 bjornw Exp $
+/*
  * A fast checksum+copy routine using movem
- * Copyright (c) 1998, 2001 Axis Communications AB
+ * Copyright (c) 1998-2003 Axis Communications AB
  *
  * Authors:	Bjorn Wesen
- * 
+ *
  * csum_partial_copy_nocheck(const char *src, char *dst,
  *		             int len, unsigned int sum)
  */
diff -urN linux-2.4.22-pre3/arch/cris/lib/dmacopy.c linux-2.4.22-pre4/arch/cris/lib/dmacopy.c
--- linux-2.4.22-pre3/arch/cris/lib/dmacopy.c	2001-02-08 16:32:44.000000000 -0800
+++ linux-2.4.22-pre4/arch/cris/lib/dmacopy.c	2003-07-09 16:56:27.000000000 -0700
@@ -1,5 +1,4 @@
-/* $Id: dmacopy.c,v 1.1 2000/07/10 16:25:21 bjornw Exp $ 
- *
+/*
  * memcpy for large blocks, using memory-memory DMA channels 6 and 7 in Etrax
  */
 
@@ -13,11 +12,11 @@
 		 unsigned int pn)
 {
 	static etrax_dma_descr indma, outdma;
-	
+
 	D(printk("dma_memcpy %d bytes... ", pn));
 
 #if 0
-	*R_GEN_CONFIG = genconfig_shadow = 
+	*R_GEN_CONFIG = genconfig_shadow =
 		(genconfig_shadow & ~0x3c0000) |
 		IO_STATE(R_GEN_CONFIG, dma6, intdma7) |
 		IO_STATE(R_GEN_CONFIG, dma7, intdma6);
@@ -32,12 +31,9 @@
 	*R_DMA_CH7_FIRST = &outdma;
 	*R_DMA_CH6_CMD = IO_STATE(R_DMA_CH6_CMD, cmd, start);
 	*R_DMA_CH7_CMD = IO_STATE(R_DMA_CH7_CMD, cmd, start);
-	
+
 	while(*R_DMA_CH7_CMD == 1) /* wait for completion */ ;
 
 	D(printk("done\n"));
 
 }
-
-
-
diff -urN linux-2.4.22-pre3/arch/cris/lib/dram_init.S linux-2.4.22-pre4/arch/cris/lib/dram_init.S
--- linux-2.4.22-pre3/arch/cris/lib/dram_init.S	2003-06-13 07:51:29.000000000 -0700
+++ linux-2.4.22-pre4/arch/cris/lib/dram_init.S	2003-07-09 16:56:27.000000000 -0700
@@ -1,4 +1,4 @@
-/* $Id: dram_init.S,v 1.13 2002/10/30 07:42:28 starvik Exp $
+/* $Id: dram_init.S,v 1.14 2003/03/31 07:07:08 starvik Exp $
  * 
  * DRAM/SDRAM initialization - alter with care
  * This file is intended to be included from other assembler files
@@ -11,6 +11,9 @@
  * Authors:  Mikael Starvik (starvik@axis.com)	
  * 
  * $Log: dram_init.S,v $
+ * Revision 1.14  2003/03/31 07:07:08  starvik
+ * Corrected calculation of end of sdram init commands
+ *
  * Revision 1.13  2002/10/30 07:42:28  starvik
  * Always read SDRAM command sequence from flash
  *
@@ -148,7 +151,7 @@
 	move.d   _sdram_commands_start, $r2
 	and.d    0x00ffffff, $r2 ; Make sure commands are read from flash
 	move.d   _sdram_commands_end,  $r3
-	and.d    0x00ffffff, $r2
+	and.d    0x00ffffff, $r3
 1:	clear.d  $r4
 	move.b   [$r2+], $r4
 	lslq     9, $r4	; Command starts at bit 9
diff -urN linux-2.4.22-pre3/arch/cris/lib/hw_settings.S linux-2.4.22-pre4/arch/cris/lib/hw_settings.S
--- linux-2.4.22-pre3/arch/cris/lib/hw_settings.S	2003-06-13 07:51:29.000000000 -0700
+++ linux-2.4.22-pre4/arch/cris/lib/hw_settings.S	2003-07-09 16:56:27.000000000 -0700
@@ -1,13 +1,11 @@
 /*
- * $Id: hw_settings.S,v 1.5 2002/08/15 09:16:06 bjornw Exp $
- * 
  * This table is used by some tools to extract hardware parameters.
  * The table should be included in the kernel and the decompressor.
  * Don't forget to update the tools if you change this table.
  *
- * Copyright (C) 2001, 2002 Axis Communications AB
+ * Copyright (C) 2001, 2002, 2003 Axis Communications AB
  *
- * Authors:  Mikael Starvik (starvik@axis.com)	
+ * Authors:  Mikael Starvik (starvik@axis.com)
  */
 
 #define PA_SET_VALUE ((CONFIG_ETRAX_DEF_R_PORT_PA_DIR << 8) | \
@@ -15,13 +13,13 @@
 #define PB_SET_VALUE ((CONFIG_ETRAX_DEF_R_PORT_PB_CONFIG << 16) | \
 		(CONFIG_ETRAX_DEF_R_PORT_PB_DIR << 8) | \
 		(CONFIG_ETRAX_DEF_R_PORT_PB_DATA))
-	
+
 	.ascii "HW_PARAM_MAGIC"	; Magic number
 	.dword 0xc0004000	; Where to load the kernel image (physical!)
 
 	; Debug port
 #ifdef CONFIG_ETRAX_DEBUG_PORT0
-	.dword 0		
+	.dword 0
 #elif defined(CONFIG_ETRAX_DEBUG_PORT1)
 	.dword 1
 #elif defined(CONFIG_ETRAX_DEBUG_PORT2)
@@ -30,7 +28,7 @@
 	.dword 3
 #else
 	.dword 4 ; No debug
-#endif			
+#endif
 
 	; SDRAM or EDO DRAM?
 #ifdef CONFIG_ETRAX_SDRAM
@@ -39,7 +37,7 @@
 	.dword 0
 #endif
 
-	; Register values 
+	; Register values
 	.dword R_WAITSTATES
 	.dword CONFIG_ETRAX_DEF_R_WAITSTATES
 	.dword R_BUS_CONFIG
@@ -56,7 +54,7 @@
 	.dword CONFIG_ETRAX_DEF_R_DRAM_TIMING
 #endif
 	.dword R_PORT_PA_SET
-	.dword PA_SET_VALUE 
+	.dword PA_SET_VALUE
 	.dword R_PORT_PB_SET
 	.dword PB_SET_VALUE
 	.dword 0 ; No more register values
diff -urN linux-2.4.22-pre3/arch/cris/lib/old_checksum.c linux-2.4.22-pre4/arch/cris/lib/old_checksum.c
--- linux-2.4.22-pre3/arch/cris/lib/old_checksum.c	2001-02-08 16:32:44.000000000 -0800
+++ linux-2.4.22-pre4/arch/cris/lib/old_checksum.c	2003-07-09 16:56:27.000000000 -0700
@@ -1,5 +1,4 @@
-/* $Id: old_checksum.c,v 1.1 2000/07/10 16:25:21 bjornw Exp $
- *
+/*
  * INET		An implementation of the TCP/IP protocol suite for the LINUX
  *		operating system.  INET is implemented using the  BSD Socket
  *		interface as the means of communication with the user level.
diff -urN linux-2.4.22-pre3/arch/cris/mm/fault.c linux-2.4.22-pre4/arch/cris/mm/fault.c
--- linux-2.4.22-pre3/arch/cris/mm/fault.c	2002-11-28 15:53:09.000000000 -0800
+++ linux-2.4.22-pre4/arch/cris/mm/fault.c	2003-07-09 16:56:27.000000000 -0700
@@ -6,6 +6,10 @@
  *  Authors:  Bjorn Wesen 
  * 
  *  $Log: fault.c,v $
+ *  Revision 1.22  2003/07/07 09:07:04  johana
+ *  Added special CONFIG_ETRAX_DEBUG_INTERRUPT handling here
+ *  to deal with a di in entry.S
+ *
  *  Revision 1.21  2002/05/28 14:24:56  bjornw
  *  Corrected typo
  *
@@ -68,6 +72,7 @@
  *
  */
 
+#include <linux/config.h>
 #include <linux/signal.h>
 #include <linux/sched.h>
 #include <linux/kernel.h>
@@ -126,6 +131,9 @@
 	int errcode;
 	unsigned long address;
 
+#ifdef CONFIG_ETRAX_DEBUG_INTERRUPT /* The di is actually in entry.S */
+	log_int(rdpc(), regs->dccr, 0);
+#endif
 	cause = *R_MMU_CAUSE;
 	select = *R_TLB_SELECT;
 
diff -urN linux-2.4.22-pre3/arch/cris/mm/init.c linux-2.4.22-pre4/arch/cris/mm/init.c
--- linux-2.4.22-pre3/arch/cris/mm/init.c	2002-08-02 17:39:42.000000000 -0700
+++ linux-2.4.22-pre4/arch/cris/mm/init.c	2003-07-09 16:56:27.000000000 -0700
@@ -7,6 +7,15 @@
  *  Authors:  Bjorn Wesen (bjornw@axis.com)
  *
  *  $Log: init.c,v $
+ *  Revision 1.38  2003/04/01 14:12:08  starvik
+ *  Added loglevel for lots of printks
+ *
+ *  Revision 1.37  2003/01/22 06:54:47  starvik
+ *  Fixed warnings issued by GCC 3.2.1
+ *
+ *  Revision 1.36  2003/01/09 17:59:55  starvik
+ *  Added init_ioremap to initcalls
+ *
  *  Revision 1.35  2002/05/17 05:33:59  starvik
  *  Limit cache flush range to the size of the cache
  *
@@ -113,6 +122,7 @@
 #include <linux/swap.h>
 #include <linux/smp.h>
 #include <linux/bootmem.h>
+#include <linux/init.h>
 
 #include <asm/system.h>
 #include <asm/segment.h>
@@ -208,7 +218,7 @@
 	int i;
 	unsigned long zones_size[MAX_NR_ZONES];
 
-	printk("Setting up paging and the MMU.\n");
+	printk(KERN_INFO "Setting up paging and the MMU.\n");
 	
 	/* clear out the init_mm.pgd that will contain the kernel's mappings */
 
@@ -404,7 +414,8 @@
         datasize =  (unsigned long) &_edata - (unsigned long) &_etext;
         initsize =  (unsigned long) &__init_end - (unsigned long) &__init_begin;
 	
-        printk("Memory: %luk/%luk available (%dk kernel code, %dk reserved, %dk data, "
+        printk(KERN_INFO
+	       "Memory: %luk/%luk available (%dk kernel code, %dk reserved, %dk data, "
 	       "%dk init)\n" ,
 	       (unsigned long) nr_free_pages() << (PAGE_SHIFT-10),
 	       max_mapnr << (PAGE_SHIFT-10),
@@ -425,47 +436,40 @@
 	return;
 }
 
-/* Initialize remaps of some I/O-ports. This is designed to be callable
- * multiple times from the drivers init-sections, because we don't know
- * beforehand which driver will get initialized first.
+/* Initialize remaps of some I/O-ports. It is important that this
+ * is called before any driver is initialized.
  */
 
-void 
-init_ioremap(void)
+static int 
+__init init_ioremap(void)
 {
   
 	/* Give the external I/O-port addresses their values */
 
-        static int initialized = 0;
-  
-        if( !initialized ) {
-                initialized++;
-            
 #ifdef CONFIG_CRIS_LOW_MAP
-               /* Simply a linear map (see the KSEG map above in paging_init) */
-               port_cse1_addr = (volatile unsigned long *)(MEM_CSE1_START | 
-                                                           MEM_NON_CACHEABLE);
-               port_csp0_addr = (volatile unsigned long *)(MEM_CSP0_START |
-                                                           MEM_NON_CACHEABLE);
-               port_csp4_addr = (volatile unsigned long *)(MEM_CSP4_START |
-                                                           MEM_NON_CACHEABLE);
-#else
-               /* Note that nothing blows up just because we do this remapping 
-                * it's ok even if the ports are not used or connected 
-                * to anything (or connected to a non-I/O thing) */        
-               port_cse1_addr = (volatile unsigned long *)
-                 ioremap((unsigned long)(MEM_CSE1_START | 
-                                         MEM_NON_CACHEABLE), 16);
-               port_csp0_addr = (volatile unsigned long *)
-                 ioremap((unsigned long)(MEM_CSP0_START |
-                                         MEM_NON_CACHEABLE), 16);
-               port_csp4_addr = (volatile unsigned long *)
-                 ioremap((unsigned long)(MEM_CSP4_START |
-                                         MEM_NON_CACHEABLE), 16);
-#endif	
-        }
+	/* Simply a linear map (see the KSEG map above in paging_init) */
+	port_cse1_addr = (volatile unsigned long *)(MEM_CSE1_START |
+	                                            MEM_NON_CACHEABLE);
+	port_csp0_addr = (volatile unsigned long *)(MEM_CSP0_START |
+	                                            MEM_NON_CACHEABLE);
+	port_csp4_addr = (volatile unsigned long *)(MEM_CSP4_START |
+	                                            MEM_NON_CACHEABLE);
+#else						    
+	/* Note that nothing blows up just because we do this remapping 
+	 * it's ok even if the ports are not used or connected 
+	 * to anything (or connected to a non-I/O thing) */        
+	port_cse1_addr = (volatile unsigned long *)
+	ioremap((unsigned long)(MEM_CSE1_START | MEM_NON_CACHEABLE), 16);
+	port_csp0_addr = (volatile unsigned long *)
+	ioremap((unsigned long)(MEM_CSP0_START | MEM_NON_CACHEABLE), 16);
+	port_csp4_addr = (volatile unsigned long *)
+	ioremap((unsigned long)(MEM_CSP4_START | MEM_NON_CACHEABLE), 16);
+#endif
+	return 0;
 }
 
+__initcall(init_ioremap);
+
 /* Helper function for the two below */
 
 static inline void
@@ -481,7 +485,7 @@
 	length = length > 8192 ? 8192 : length;  /* No need to flush more than cache size */
 
 	while(length > 0) {
-		short tmp = *flushadr;           /* dummy read to flush */
+		*flushadr; /* dummy read to flush */
 		flushadr += (32/sizeof(short));  /* a cacheline is 32 bytes */
 		length -= 32;
 	}
@@ -524,7 +528,7 @@
                 totalram_pages++;
         }
         printk (KERN_INFO "Freeing unused kernel memory: %luk freed\n", 
-		(&__init_end - &__init_begin) >> 10);
+		(unsigned long)((&__init_end - &__init_begin) >> 10));
 }
 
 void 
diff -urN linux-2.4.22-pre3/arch/cris/mm/ioremap.c linux-2.4.22-pre4/arch/cris/mm/ioremap.c
--- linux-2.4.22-pre3/arch/cris/mm/ioremap.c	2003-06-13 07:51:29.000000000 -0700
+++ linux-2.4.22-pre4/arch/cris/mm/ioremap.c	2003-07-09 16:56:27.000000000 -0700
@@ -146,7 +146,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.22-pre3/arch/i386/kernel/Makefile linux-2.4.22-pre4/arch/i386/kernel/Makefile
--- linux-2.4.22-pre3/arch/i386/kernel/Makefile	2003-07-09 16:56:23.000000000 -0700
+++ linux-2.4.22-pre4/arch/i386/kernel/Makefile	2003-07-09 16:56:27.000000000 -0700
@@ -14,7 +14,7 @@
 
 O_TARGET := kernel.o
 
-export-objs     := mca.o mtrr.o msr.o cpuid.o microcode.o i386_ksyms.o time.o
+export-objs     := mca.o mtrr.o msr.o cpuid.o microcode.o i386_ksyms.o time.o setup.o
 
 obj-y	:= process.o semaphore.o signal.o entry.o traps.o irq.o vm86.o \
 		ptrace.o i8259.o ioport.o ldt.o setup.o time.o sys_i386.o \
diff -urN linux-2.4.22-pre3/arch/i386/kernel/setup.c linux-2.4.22-pre4/arch/i386/kernel/setup.c
--- linux-2.4.22-pre3/arch/i386/kernel/setup.c	2003-07-09 16:56:23.000000000 -0700
+++ linux-2.4.22-pre4/arch/i386/kernel/setup.c	2003-07-09 16:56:27.000000000 -0700
@@ -107,6 +107,7 @@
 #include <linux/seq_file.h>
 #include <asm/processor.h>
 #include <linux/console.h>
+#include <linux/module.h>
 #include <asm/mtrr.h>
 #include <asm/uaccess.h>
 #include <asm/system.h>
@@ -175,10 +176,11 @@
 static u32 disabled_x86_caps[NCAPINTS] __initdata = { 0 };
 
 #ifdef CONFIG_ACPI_HT_ONLY
-int acpi_disabled __initdata = 1;
+int acpi_disabled = 1;
 #else
-int acpi_disabled __initdata = 0;
+int acpi_disabled = 0;
 #endif
+EXPORT_SYMBOL(acpi_disabled);
 
 extern int blk_nohighio;
 
diff -urN linux-2.4.22-pre3/arch/ia64/ia32/sys_ia32.c linux-2.4.22-pre4/arch/ia64/ia32/sys_ia32.c
--- linux-2.4.22-pre3/arch/ia64/ia32/sys_ia32.c	2003-07-09 16:56:23.000000000 -0700
+++ linux-2.4.22-pre4/arch/ia64/ia32/sys_ia32.c	2003-07-09 16:56:27.000000000 -0700
@@ -3852,7 +3852,7 @@
 	return result;
 }
 
-struct dqblk32 {
+struct user_dqblk32 {
 	__u32 dqb_bhardlimit;
 	__u32 dqb_bsoftlimit;
 	__u32 dqb_curblocks;
@@ -3864,43 +3864,43 @@
 };
 
 asmlinkage long
-sys32_quotactl (int cmd, unsigned int special, int id, struct dqblk32 *addr)
+sys32_quotactl(int cmd, unsigned int special, int id, caddr_t addr)
 {
 	extern asmlinkage long sys_quotactl (int, const char *, int, caddr_t);
 	int cmds = cmd >> SUBCMDSHIFT;
 	mm_segment_t old_fs;
-	struct dqblk d;
+	struct v1c_mem_dqblk d;
 	char *spec;
 	long err;
 
 	switch (cmds) {
-	      case Q_GETQUOTA:
+	case Q_V1_GETQUOTA:
 		break;
-	      case Q_SETQUOTA:
-	      case Q_SETUSE:
-	      case Q_SETQLIM:
-		if (copy_from_user (&d, addr, sizeof(struct dqblk32)))
+	case Q_V1_SETQUOTA:
+	case Q_V1_SETUSE:
+	case Q_V1_SETQLIM:
+		if (copy_from_user(&d, addr, sizeof(struct user_dqblk32)))
 			return -EFAULT;
-		d.dqb_itime = ((struct dqblk32 *)&d)->dqb_itime;
-		d.dqb_btime = ((struct dqblk32 *)&d)->dqb_btime;
+		d.dqb_itime = ((struct user_dqblk32 *)&d)->dqb_itime;
+		d.dqb_btime = ((struct user_dqblk32 *)&d)->dqb_btime;
 		break;
-	      default:
-		return sys_quotactl(cmd, (void *) A(special), id, (caddr_t) addr);
+	default:
+		return sys_quotactl(cmd, (void *)A(special), id, addr);
 	}
 	spec = getname32((void *) A(special));
 	err = PTR_ERR(spec);
 	if (IS_ERR(spec))
 		return err;
-	old_fs = get_fs ();
+	old_fs = get_fs();
 	set_fs(KERNEL_DS);
 	err = sys_quotactl(cmd, (const char *)spec, id, (caddr_t)&d);
 	set_fs(old_fs);
 	putname(spec);
-	if (cmds == Q_GETQUOTA) {
+	if (cmds == Q_V1_GETQUOTA) {
 		__kernel_time_t b = d.dqb_btime, i = d.dqb_itime;
-		((struct dqblk32 *)&d)->dqb_itime = i;
-		((struct dqblk32 *)&d)->dqb_btime = b;
-		if (copy_to_user(addr, &d, sizeof(struct dqblk32)))
+		((struct user_dqblk32 *)&d)->dqb_itime = i;
+		((struct user_dqblk32 *)&d)->dqb_btime = b;
+		if (copy_to_user(addr, &d, sizeof(struct user_dqblk32)))
 			return -EFAULT;
 	}
 	return err;
diff -urN linux-2.4.22-pre3/arch/parisc/defpalo.conf linux-2.4.22-pre4/arch/parisc/defpalo.conf
--- linux-2.4.22-pre3/arch/parisc/defpalo.conf	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.4.22-pre4/arch/parisc/defpalo.conf	2003-07-09 16:56:28.000000000 -0700
@@ -0,0 +1,21 @@
+# This a generic Palo configuration file.  For more information about how
+# it works try 'palo -?'.
+#
+# Most people using 'make palo' want a bootable file, usable for
+# network or tape booting for example.
+--init-tape=lifimage
+--recoverykernel=vmlinux
+
+########## Pick your ROOT here! ##########
+# You need at least one 'root='!
+#
+# If you want a root ramdisk, use the next 2 lines
+#   (Edit the ramdisk image name!!!!)
+--ramdisk=ram-disk-image-file
+--commandline=0/vmlinux HOME=/ root=/dev/ram initrd=0/ramdisk
+
+# If you want NFS root, use the following command line (Edit the HOSTNAME!!!)
+#--commandline=0/vmlinux HOME=/ root=/dev/nfs nfsroot=HOSTNAME ip=bootp
+
+# If you have root on a disk partition, use this (Edit the partition name!!!)
+#--commandline=0/vmlinux HOME=/ root=/dev/sda1
diff -urN linux-2.4.22-pre3/arch/parisc/kernel/cache.c linux-2.4.22-pre4/arch/parisc/kernel/cache.c
--- linux-2.4.22-pre3/arch/parisc/kernel/cache.c	2002-11-28 15:53:10.000000000 -0800
+++ linux-2.4.22-pre4/arch/parisc/kernel/cache.c	2003-07-09 16:56:28.000000000 -0700
@@ -194,31 +194,69 @@
 
 void disable_sr_hashing(void)
 {
-    int srhash_type;
+	int srhash_type;
 
-    if (boot_cpu_data.cpu_type == pcxl2)
-	return; /* pcxl2 doesn't support space register hashing */
+	switch (boot_cpu_data.cpu_type) {
+	case pcx: /* We shouldn't get this far.  setup.c should prevent it. */
+		BUG();
+		return;
+
+	case pcxs:
+	case pcxt:
+	case pcxt_:
+		srhash_type = SRHASH_PCXST;
+		break;
+
+	case pcxl:
+		srhash_type = SRHASH_PCXL;
+		break;
+
+	case pcxl2: /* pcxl2 doesn't support space register hashing */
+		return;
+
+	default: /* Currently all PA2.0 machines use the same ins. sequence */
+		srhash_type = SRHASH_PA20;
+		break;
+	}
 
-    switch (boot_cpu_data.cpu_type) {
-
-    case pcx:
-	BUG(); /* We shouldn't get here. code in setup.c should prevent it */
-	return;
+	disable_sr_hashing_asm(srhash_type);
+}
 
-    case pcxs:
-    case pcxt:
-    case pcxt_:
-	srhash_type = SRHASH_PCXST;
-	break;
+void __flush_dcache_page(struct page *page)
+{
+	struct mm_struct *mm = current->active_mm;
+	struct vm_area_struct *mpnt;
 
-    case pcxl:
-	srhash_type = SRHASH_PCXL;
-	break;
+	flush_kernel_dcache_page(page_address(page));
 
-    default: /* Currently all PA2.0 machines use the same ins. sequence */
-	srhash_type = SRHASH_PA20;
-	break;
-    }
+	if (!page->mapping)
+		return;
 
-    disable_sr_hashing_asm(srhash_type);
+	for (mpnt = page->mapping->i_mmap_shared;
+	     mpnt != NULL;
+	     mpnt = mpnt->vm_next_share)
+	{
+		unsigned long off;
+
+		/*
+		 * If this VMA is not in our MM, we can ignore it.
+		 */
+		if (mpnt->vm_mm != mm)
+			continue;
+
+		if (page->index < mpnt->vm_pgoff)
+			continue;
+
+		off = page->index - mpnt->vm_pgoff;
+		if (off >= (mpnt->vm_end - mpnt->vm_start) >> PAGE_SHIFT)
+			continue;
+
+		flush_cache_page(mpnt, mpnt->vm_start + (off << PAGE_SHIFT));
+
+		/* All user shared mappings should be equivalently mapped,
+		 * so once we've flushed one we should be ok
+		 */
+		break;
+	}
 }
+
diff -urN linux-2.4.22-pre3/arch/parisc/kernel/ccio-dma.c linux-2.4.22-pre4/arch/parisc/kernel/ccio-dma.c
--- linux-2.4.22-pre3/arch/parisc/kernel/ccio-dma.c	2003-06-13 07:51:31.000000000 -0700
+++ linux-2.4.22-pre4/arch/parisc/kernel/ccio-dma.c	2003-07-09 16:56:28.000000000 -0700
@@ -805,7 +805,7 @@
 
 		DBG_RUN_SG(" %d : %08lx/%05x %p/%05x\n", nents,
 			   (unsigned long)sg_dma_address(startsg), cnt,
-			   startsg->address, startsg->length
+			   sg_virt_addr(startsg), startsg->length
 		);
 
 		/*
@@ -825,7 +825,7 @@
 		** Look for a VCONTIG chunk
 		*/
 		if (cnt) {
-			unsigned long vaddr = (unsigned long)startsg->address;
+			unsigned long vaddr = (unsigned long) sg_virt_addr(startsg);
 			ASSERT(pdirp);
 
 			/* Since multiple Vcontig blocks could make up
@@ -878,8 +878,8 @@
 		*/
 		dma_sg = vcontig_sg = startsg;
 		dma_len = vcontig_len = vcontig_end = startsg->length;
-		vcontig_end += (unsigned long) startsg->address;
-		dma_offset = (unsigned long) startsg->address & ~IOVP_MASK;
+		vcontig_end += (unsigned long) sg_virt_addr(startsg);
+		dma_offset = (unsigned long) sg_virt_addr(startsg) & ~IOVP_MASK;
 
 		/* PARANOID: clear entries */
 		sg_dma_address(startsg) = 0;
@@ -893,7 +893,7 @@
 			unsigned long startsg_end;
 
 			startsg++;
-			startsg_end = (unsigned long)startsg->address + 
+			startsg_end = (unsigned long) sg_virt_addr(startsg) + 
 				startsg->length;
 
 			/* PARANOID: clear entries */
@@ -912,7 +912,7 @@
 			/*
 			** Append the next transaction?
 			*/
-			if(vcontig_end == (unsigned long) startsg->address) {
+			if(vcontig_end == (unsigned long) sg_virt_addr(startsg)) {
 				vcontig_len += startsg->length;
 				vcontig_end += startsg->length;
 				dma_len     += startsg->length;
@@ -981,7 +981,8 @@
 
 	/* Fast path single entry scatterlists. */
 	if(nents == 1) {
-		sg_dma_address(sglist)= ccio_map_single(dev, sglist->address,
+		sg_dma_address(sglist)= ccio_map_single(dev,
+							sg_virt_addr(sglist),
 							sglist->length, 
 							direction);
 		sg_dma_len(sglist)= sglist->length;
@@ -1043,7 +1044,7 @@
 	ioc = GET_IOC(dev);
 
 	DBG_RUN_SG("%s() START %d entries,  %p,%x\n",
-		__FUNCTION__, nents, sglist->address, sglist->length);
+		__FUNCTION__, nents, sg_virt_address(sglist), sglist->length);
 
 #ifdef CONFIG_PROC_FS
 	ioc->usg_calls++;
diff -urN linux-2.4.22-pre3/arch/parisc/kernel/ccio-rm-dma.c linux-2.4.22-pre4/arch/parisc/kernel/ccio-rm-dma.c
--- linux-2.4.22-pre3/arch/parisc/kernel/ccio-rm-dma.c	2003-06-13 07:51:31.000000000 -0700
+++ linux-2.4.22-pre4/arch/parisc/kernel/ccio-rm-dma.c	2003-07-09 16:56:28.000000000 -0700
@@ -118,7 +118,7 @@
 
         /* KISS: map each buffer seperately. */
 	while (nents) {
-		sg_dma_address(sglist) = ccio_map_single(dev, sglist->address, sglist->length, direction);
+		sg_dma_address(sglist) = ccio_map_single(dev, sg_virt_addr(sglist), sglist->length, direction);
 		sg_dma_len(sglist) = sglist->length;
 		nents--;
 		sglist++;
diff -urN linux-2.4.22-pre3/arch/parisc/kernel/drivers.c linux-2.4.22-pre4/arch/parisc/kernel/drivers.c
--- linux-2.4.22-pre3/arch/parisc/kernel/drivers.c	2003-06-13 07:51:31.000000000 -0700
+++ linux-2.4.22-pre4/arch/parisc/kernel/drivers.c	2003-07-09 16:56:28.000000000 -0700
@@ -9,6 +9,7 @@
  * Copyright (c) 1999 The Puffin Group
  * Copyright (c) 2001 Matthew Wilcox for Hewlett Packard
  * Copyright (c) 2001 Helge Deller <deller@gmx.de>
+ * Copyright (c) 2001,2002 Ryan Bradetich 
  * 
  * The file handles registering devices and drivers, then matching them.
  * It's the closest we get to a dating agency.
@@ -23,7 +24,6 @@
 #include <asm/hardware.h>
 #include <asm/io.h>
 #include <asm/pdc.h>
-#include <asm/gsc.h>
 
 /* See comments in include/asm-parisc/pci.h */
 struct pci_dma_ops *hppa_dma_ops;
@@ -416,6 +416,7 @@
 	dev->id.sversion = ((iodc_data[4] & 0x0f) << 16) |
 			(iodc_data[5] << 8) | iodc_data[6];
 	dev->hpa = hpa;
+
 	name = parisc_hardware_description(&dev->id);
 	if (name) {
 		strncpy(dev->name, name, sizeof(dev->name)-1);
@@ -461,30 +462,26 @@
 #define BC_PORT_MASK 0x8
 #define BC_LOWER_PORT 0x8
 
+#define IO_STATUS 	offsetof(struct bc_module, io_status)
+
 
 #define BUS_CONVERTER(dev) \
         ((dev->id.hw_type == HPHW_IOA) || (dev->id.hw_type == HPHW_BCPORT))
 
 #define IS_LOWER_PORT(dev) \
-        ((gsc_readl(&((struct bc_module *)dev->hpa)->io_status) \
-                & BC_PORT_MASK) == BC_LOWER_PORT)
-
-#define READ_IO_IO_LOW(dev) \
-	(dev->id.hw_type == HPHW_IOA ? \
-	        __raw_readl((unsigned long)&((struct bc_module *)dev->hpa)->io_io_low) << 16 : \
-	        __raw_readl((unsigned long)&((struct bc_module *)dev->hpa)->io_io_low))
-
-#define READ_IO_IO_HIGH(dev) \
-	(dev->id.hw_type == HPHW_IOA ? \
-	        __raw_readl((unsigned long)&((struct bc_module *)dev->hpa)->io_io_high) << 16 : \
-	        __raw_readl((unsigned long)&((struct bc_module *)dev->hpa)->io_io_high))
+        ((__raw_readl(dev->hpa + IO_STATUS) & BC_PORT_MASK) == BC_LOWER_PORT)
 
+#define MAX_NATIVE_DEVICES 64
+#define NATIVE_DEVICE_OFFSET 0x1000
 
-static void walk_native_bus(unsigned long io_io_low, unsigned long io_io_high, 
-			    struct parisc_device *parent);
-
-#define FLEX_MASK (unsigned long)0xfffffffffffc0000
+#define FLEX_MASK 	(unsigned long)0xfffffffffffc0000
+#define IO_IO_LOW	offsetof(struct bc_module, io_io_low)
+#define IO_IO_HIGH	offsetof(struct bc_module, io_io_high)
+#define READ_IO_IO_LOW(dev)  (unsigned long)(signed int)__raw_readl(dev->hpa + IO_IO_LOW)
+#define READ_IO_IO_HIGH(dev) (unsigned long)(signed int)__raw_readl(dev->hpa + IO_IO_HIGH)
 
+static void walk_native_bus(unsigned long io_io_low, unsigned long io_io_high,
+                            struct parisc_device *parent);
 
 void walk_lower_bus(struct parisc_device *dev)
 {
@@ -493,15 +490,17 @@
 	if(!BUS_CONVERTER(dev) || IS_LOWER_PORT(dev))
 		return;
 
-	io_io_low = ((unsigned long)(signed int)READ_IO_IO_LOW(dev) + ~FLEX_MASK) & FLEX_MASK;
-	io_io_high = ((unsigned long)(signed int)READ_IO_IO_HIGH(dev) + ~FLEX_MASK) & FLEX_MASK;
+	if(dev->id.hw_type == HPHW_IOA) {
+		io_io_low = (unsigned long)(signed int)(READ_IO_IO_LOW(dev) << 16);
+		io_io_high = io_io_low + MAX_NATIVE_DEVICES * NATIVE_DEVICE_OFFSET;
+	} else {
+		io_io_low = (READ_IO_IO_LOW(dev) + ~FLEX_MASK) & FLEX_MASK;
+		io_io_high = (READ_IO_IO_HIGH(dev)+ ~FLEX_MASK) & FLEX_MASK;
+	}
 
 	walk_native_bus(io_io_low, io_io_high, dev);
 }
 
-#define MAX_NATIVE_DEVICES 64
-#define NATIVE_DEVICE_OFFSET 0x1000
-
 /**
  * walk_native_bus -- Probe a bus for devices
  * @io_io_low: Base address of this bus.
@@ -515,7 +514,7 @@
  * keyboard ports).  This problem is not yet solved.
  */
 static void walk_native_bus(unsigned long io_io_low, unsigned long io_io_high,
-			    struct parisc_device *parent)
+                            struct parisc_device *parent)
 {
 	int i, devices_found = 0;
 	unsigned long hpa = io_io_low;
@@ -523,7 +522,7 @@
 
 	get_node_path(parent, &path);
 	do {
-		for (i = 0; i < MAX_NATIVE_DEVICES; i++, hpa += NATIVE_DEVICE_OFFSET) {
+		for(i = 0; i < MAX_NATIVE_DEVICES; i++, hpa += NATIVE_DEVICE_OFFSET) {
 			struct parisc_device *dev;
 
 			/* Was the device already added by Firmware? */
@@ -539,7 +538,7 @@
 			}
 			walk_lower_bus(dev);
 		}
-	} while (!devices_found && hpa < io_io_high);
+	} while(!devices_found && hpa < io_io_high);
 }
 
 #define CENTRAL_BUS_ADDR (unsigned long) 0xfffffffffff80000
@@ -552,7 +551,7 @@
  */
 void walk_central_bus(void)
 {
-	walk_native_bus(CENTRAL_BUS_ADDR, 
+	walk_native_bus(CENTRAL_BUS_ADDR,
 			CENTRAL_BUS_ADDR + (MAX_NATIVE_DEVICES * NATIVE_DEVICE_OFFSET),
 			&root);
 }
diff -urN linux-2.4.22-pre3/arch/parisc/kernel/entry.S linux-2.4.22-pre4/arch/parisc/kernel/entry.S
--- linux-2.4.22-pre3/arch/parisc/kernel/entry.S	2003-06-13 07:51:31.000000000 -0700
+++ linux-2.4.22-pre4/arch/parisc/kernel/entry.S	2003-07-09 16:56:28.000000000 -0700
@@ -927,11 +927,11 @@
 	ldo		-16(%r30),%r29	/* Reference param save area */
 #endif
 
-	ldil		L%intr_restore, %r2
+	ldil		L%intr_check_sig, %r2
 	copy		%r25, %r16	/* save pt_regs */
 
 	b		handle_interruption
-	ldo		R%intr_restore(%r2), %r2
+	ldo		R%intr_check_sig(%r2), %r2
 
 
 	/*
diff -urN linux-2.4.22-pre3/arch/parisc/kernel/hardware.c linux-2.4.22-pre4/arch/parisc/kernel/hardware.c
--- linux-2.4.22-pre3/arch/parisc/kernel/hardware.c	2002-11-28 15:53:10.000000000 -0800
+++ linux-2.4.22-pre4/arch/parisc/kernel/hardware.c	2003-07-09 16:56:28.000000000 -0700
@@ -99,8 +99,8 @@
 	{HPHW_NPROC,0x481,0x4,0x81,"Wilbur (E25)"},
 	{HPHW_NPROC,0x482,0x4,0x81,"WB-80 (E35)"},
 	{HPHW_NPROC,0x483,0x4,0x81,"WB-96 (E45)"},
-	{HPHW_NPROC,0x48,0x4,0x81,"UL Proc L-100 (811/D210,D310)"},
-	{HPHW_NPROC,0x48,0x4,0x81,"UL Proc L-75 (801/D200)"},
+	{HPHW_NPROC,0x484,0x4,0x81,"UL Proc L-100 (811/D210,D310)"},
+	{HPHW_NPROC,0x485,0x4,0x81,"UL Proc L-75 (801/D200)"},
 	{HPHW_NPROC,0x501,0x4,0x81,"Merlin L2 132 (9000/778/B132L)"},
 	{HPHW_NPROC,0x502,0x4,0x81,"Merlin L2 160 (9000/778/B160L)"},
 	{HPHW_NPROC,0x503,0x4,0x81,"Merlin L2+ 132 (9000/778/B132L)"},
@@ -403,7 +403,7 @@
 	{HPHW_BA, 0x01B, 0x00078, 0x0, "Anole 100 VME BA"}, 
 	{HPHW_BA, 0x024, 0x00078, 0x0, "Fast Pace VME BA"}, 
 	{HPHW_BA, 0x034, 0x00078, 0x0, "Anole T VME BA"}, 
-	{HPHW_BA, 0x04A, 0x00078, 0x0, "Anole L2 132 BME BA"}, 
+	{HPHW_BA, 0x04A, 0x00078, 0x0, "Anole L2 132 VME BA"}, 
 	{HPHW_BA, 0x04C, 0x00078, 0x0, "Anole L2 165 VME BA"}, 
 	{HPHW_BA, 0x011, 0x00081, 0x0, "WB-96 Core BA"}, 
 	{HPHW_BA, 0x012, 0x00081, 0x0, "Orville UX Core BA"}, 
@@ -805,8 +805,8 @@
 	{HPHW_FIO, 0x04E, 0x0007B, 0x0, "Kiji L2 132 Core Audio"}, 
 	{HPHW_FIO, 0x050, 0x0007B, 0x0, "Merlin Jr 132 Core Audio"}, 
 	{HPHW_FIO, 0x051, 0x0007B, 0x0, "Firehawk Audio"}, 
-	{HPHW_FIO, 0x056, 0x0007B, 0x0, "Raven+ w SE FWSCSU Core Audio"}, 
-	{HPHW_FIO, 0x057, 0x0007B, 0x0, "Raven+ w Diff FWSCSU Core Audio"}, 
+	{HPHW_FIO, 0x056, 0x0007B, 0x0, "Raven+ w SE FWSCSI Core Audio"}, 
+	{HPHW_FIO, 0x057, 0x0007B, 0x0, "Raven+ w Diff FWSCSI Core Audio"}, 
 	{HPHW_FIO, 0x058, 0x0007B, 0x0, "FireHawk 200 Audio"}, 
 	{HPHW_FIO, 0x05C, 0x0007B, 0x0, "SummitHawk 230 Core Audio"}, 
 	{HPHW_FIO, 0x800, 0x0007B, 0x0, "Hitachi Tiny 64 Audio"}, 
diff -urN linux-2.4.22-pre3/arch/parisc/kernel/head.S linux-2.4.22-pre4/arch/parisc/kernel/head.S
--- linux-2.4.22-pre3/arch/parisc/kernel/head.S	2002-11-28 15:53:10.000000000 -0800
+++ linux-2.4.22-pre4/arch/parisc/kernel/head.S	2003-07-09 16:56:28.000000000 -0700
@@ -74,7 +74,7 @@
 	ldo             R%PA(_end)(%r4),%r4
 $bss_loop:
 	cmpb,<<,n       %r3,%r4,$bss_loop
-	stb,ma          %r0,1(%r3)
+	stw,ma          %r0,4(%r3)
 
 	/* Save away the arguments the boot loader passed in (32 bit args) */
 
diff -urN linux-2.4.22-pre3/arch/parisc/kernel/ioctl32.c linux-2.4.22-pre4/arch/parisc/kernel/ioctl32.c
--- linux-2.4.22-pre3/arch/parisc/kernel/ioctl32.c	2003-06-13 07:51:31.000000000 -0700
+++ linux-2.4.22-pre4/arch/parisc/kernel/ioctl32.c	2003-07-09 16:56:28.000000000 -0700
@@ -3123,6 +3123,11 @@
 COMPATIBLE_IOCTL(SIOCDRARP)
 COMPATIBLE_IOCTL(SIOCADDDLCI)
 COMPATIBLE_IOCTL(SIOCDELDLCI)
+COMPATIBLE_IOCTL(SIOCGMIIPHY)
+COMPATIBLE_IOCTL(SIOCGMIIREG)
+COMPATIBLE_IOCTL(SIOCSMIIREG)
+COMPATIBLE_IOCTL(SIOCGIFVLAN)
+COMPATIBLE_IOCTL(SIOCSIFVLAN)
 /* SG stuff */
 COMPATIBLE_IOCTL(SG_SET_TIMEOUT)
 COMPATIBLE_IOCTL(SG_GET_TIMEOUT)
diff -urN linux-2.4.22-pre3/arch/parisc/kernel/irq.c linux-2.4.22-pre4/arch/parisc/kernel/irq.c
--- linux-2.4.22-pre3/arch/parisc/kernel/irq.c	2003-06-13 07:51:31.000000000 -0700
+++ linux-2.4.22-pre4/arch/parisc/kernel/irq.c	2003-07-09 16:56:28.000000000 -0700
@@ -57,7 +57,7 @@
 /* Bits in EIEM correlate with cpu_irq_action[].
 ** Numbered *Big Endian*! (ie bit 0 is MSB)
 */
-static unsigned long cpu_eiem = 0;
+static volatile unsigned long cpu_eiem = 0;
 
 static spinlock_t irq_lock = SPIN_LOCK_UNLOCKED;  /* protect IRQ regions */
 
@@ -462,7 +462,7 @@
 
 		for (irq = 0; eirr_val && bit; bit>>=1, irq++)
 		{
-			if (!(bit&eirr_val))
+			if (!(bit&eirr_val&cpu_eiem))
 				continue;
 
 			/* clear bit in mask - can exit loop sooner */
diff -urN linux-2.4.22-pre3/arch/parisc/kernel/parisc_ksyms.c linux-2.4.22-pre4/arch/parisc/kernel/parisc_ksyms.c
--- linux-2.4.22-pre3/arch/parisc/kernel/parisc_ksyms.c	2003-06-13 07:51:31.000000000 -0700
+++ linux-2.4.22-pre4/arch/parisc/kernel/parisc_ksyms.c	2003-07-09 16:56:28.000000000 -0700
@@ -67,6 +67,7 @@
 
 #include <linux/smp.h>
 EXPORT_SYMBOL(smp_num_cpus);
+EXPORT_SYMBOL(smp_call_function);
 #endif /* CONFIG_SMP */
 
 #include <asm/atomic.h>
@@ -127,7 +128,13 @@
 #include <asm/cache.h>
 EXPORT_SYMBOL(flush_kernel_dcache_range_asm);
 EXPORT_SYMBOL(flush_kernel_dcache_page);
-EXPORT_SYMBOL(flush_all_caches);
+
+/* asm/pgalloc.h doesn't include all it's dependencies */
+extern void __flush_dcache_page(struct page *page);
+EXPORT_SYMBOL(__flush_dcache_page);
+
+extern void flush_cache_all_local(void);
+EXPORT_SYMBOL(flush_cache_all_local);
 
 #include <asm/unistd.h>
 extern long sys_open(const char *, int, int);
@@ -150,6 +157,8 @@
 
 #include <asm/pdc.h>
 EXPORT_SYMBOL(pdc_add_valid);
+EXPORT_SYMBOL(pdc_tod_read);
+EXPORT_SYMBOL(pdc_tod_set);
 EXPORT_SYMBOL(pdc_lan_station_id);
 EXPORT_SYMBOL(pdc_get_initiator);
 
diff -urN linux-2.4.22-pre3/arch/parisc/kernel/pci-dma.c linux-2.4.22-pre4/arch/parisc/kernel/pci-dma.c
--- linux-2.4.22-pre3/arch/parisc/kernel/pci-dma.c	2002-11-28 15:53:10.000000000 -0800
+++ linux-2.4.22-pre4/arch/parisc/kernel/pci-dma.c	2003-07-09 16:56:28.000000000 -0700
@@ -428,9 +428,9 @@
 	    BUG();
 
 	for (i = 0; i < nents; i++, sglist++ ) {
-		sg_dma_address(sglist) = (dma_addr_t) virt_to_phys(sglist->address);
+		sg_dma_address(sglist) = (dma_addr_t) virt_to_phys(sg_virt_addr(sglist));
 		sg_dma_len(sglist) = sglist->length;
-		flush_kernel_dcache_range((unsigned long)sglist->address,
+		flush_kernel_dcache_range((unsigned long)sg_virt_addr(sglist),
 				sglist->length);
 	}
 	return nents;
@@ -449,7 +449,7 @@
 	/* once we do combining we'll need to use phys_to_virt(sg_dma_address(sglist)) */
 
 	for (i = 0; i < nents; i++, sglist++ )
-		flush_kernel_dcache_range((unsigned long) sglist->address, sglist->length);
+		flush_kernel_dcache_range((unsigned long) sg_virt_addr(sglist), sglist->length);
 	return;
 }
 
@@ -468,7 +468,7 @@
 	/* once we do combining we'll need to use phys_to_virt(sg_dma_address(sglist)) */
 
 	for (i = 0; i < nents; i++, sglist++ )
-		flush_kernel_dcache_range((unsigned long) sglist->address, sglist->length);
+		flush_kernel_dcache_range((unsigned long) sg_virt_addr(sglist), sglist->length);
 }
 
 struct pci_dma_ops pcxl_dma_ops = {
diff -urN linux-2.4.22-pre3/arch/parisc/kernel/perf.c linux-2.4.22-pre4/arch/parisc/kernel/perf.c
--- linux-2.4.22-pre3/arch/parisc/kernel/perf.c	2002-11-28 15:53:10.000000000 -0800
+++ linux-2.4.22-pre4/arch/parisc/kernel/perf.c	2003-07-09 16:56:28.000000000 -0700
@@ -499,6 +499,8 @@
  */
 static int __init perf_init(void)
 {
+	int ret;
+
 	/* Determine correct processor interface to use */
 	bitmask_array = perf_bitmasks;
 
@@ -517,11 +519,17 @@
 		return -ENODEV;
 	}
 
+	ret = misc_register(&perf_dev);
+	if (ret) {
+		printk(KERN_ERR "Performance monitoring counters: "
+			"cannot register misc device.\n");
+		return ret;
+	}
+
 	/* Patch the images to match the system */
     	perf_patch_images();
 
 	spin_lock_init(&perf_lock);
-	misc_register(&perf_dev);
 
 	/* TODO: this only lets us access the first cpu.. what to do for SMP? */
 	cpu_device = cpu_data[0].dev;
diff -urN linux-2.4.22-pre3/arch/parisc/kernel/processor.c linux-2.4.22-pre4/arch/parisc/kernel/processor.c
--- linux-2.4.22-pre3/arch/parisc/kernel/processor.c	2003-06-13 07:51:31.000000000 -0700
+++ linux-2.4.22-pre4/arch/parisc/kernel/processor.c	2003-07-09 16:56:28.000000000 -0700
@@ -77,8 +77,6 @@
 	unsigned long txn_addr;
 	unsigned long cpuid;
 	struct cpuinfo_parisc *p;
-	extern struct irq_region_ops cpu_irq_ops; /* arch/parisc...irq.c */
-	extern struct irqaction cpu_irq_actions[]; /* arch/parisc...irq.c */
 
 #ifndef CONFIG_SMP
 	if (boot_cpu_data.cpu_count > 0) {
diff -urN linux-2.4.22-pre3/arch/parisc/kernel/sba_iommu.c linux-2.4.22-pre4/arch/parisc/kernel/sba_iommu.c
--- linux-2.4.22-pre3/arch/parisc/kernel/sba_iommu.c	2003-06-13 07:51:31.000000000 -0700
+++ linux-2.4.22-pre4/arch/parisc/kernel/sba_iommu.c	2003-07-09 16:56:28.000000000 -0700
@@ -1056,7 +1056,7 @@
 			printk(KERN_DEBUG " %2d : %08lx/%05x %p/%05x\n",
 				nents,
 				(unsigned long) sg_dma_address(startsg), cnt,
-				sg_virt_address(startsg), startsg->length
+				sg_virt_addr(startsg), startsg->length
 		);
 #else
 		DBG_RUN_SG(" %d : %08lx/%05x %p/%05x\n",
diff -urN linux-2.4.22-pre3/arch/parisc/kernel/smp.c linux-2.4.22-pre4/arch/parisc/kernel/smp.c
--- linux-2.4.22-pre3/arch/parisc/kernel/smp.c	2002-11-28 15:53:10.000000000 -0800
+++ linux-2.4.22-pre4/arch/parisc/kernel/smp.c	2003-07-09 16:56:28.000000000 -0700
@@ -33,6 +33,7 @@
 #include <linux/kernel_stat.h>
 #include <linux/mm.h>
 #include <linux/delay.h>
+#include <linux/reboot.h>
 
 #include <asm/system.h>
 #include <asm/atomic.h>
@@ -434,25 +435,21 @@
 	extern void init_IRQ(void);    /* arch/parisc/kernel/irq.c */
 
 	/* Set modes and Enable floating point coprocessor */
-	(void) init_per_cpu(cpunum);
+	init_per_cpu(cpunum);
 
 	disable_sr_hashing();
-
 	mb();
 
 	/* Well, support 2.4 linux scheme as well. */
-	if (test_and_set_bit(cpunum, (unsigned long *) (&cpu_online_map)))
-	{
-		extern void machine_halt(void); /* arch/parisc.../process.c */
-
+	if (test_and_set_bit(cpunum, (unsigned long *) (&cpu_online_map))) {
 		printk(KERN_CRIT "CPU#%d already initialized!\n", cpunum);
 		machine_halt();
-	}  
+	}
 
 	/* Initialise the idle task for this CPU */
 	atomic_inc(&init_mm.mm_count);
 	current->active_mm = &init_mm;
-	if(current->mm)
+	if (current->mm)
 		BUG();
 	enter_lazy_tlb(&init_mm, current, cpunum);
 
diff -urN linux-2.4.22-pre3/arch/parisc/kernel/traps.c linux-2.4.22-pre4/arch/parisc/kernel/traps.c
--- linux-2.4.22-pre3/arch/parisc/kernel/traps.c	2003-06-13 07:51:31.000000000 -0700
+++ linux-2.4.22-pre4/arch/parisc/kernel/traps.c	2003-07-09 16:56:28.000000000 -0700
@@ -455,13 +455,6 @@
 	unsigned long fault_space = 0;
 	struct siginfo si;
 
-	/* HACK! jsm is going to fix this.
-	 * entry.S will manage I-bit - only enable I-bit if it was
-	 * enabled before we took the "trap".
-	 */
-	if (code != 1)
-		local_irq_enable();
-
 	switch(code) {
 
 	case  1:
@@ -727,6 +720,7 @@
 	    }
 	}
 
+	local_irq_enable();
 	do_page_fault(regs, code, fault_address);
 }
 
diff -urN linux-2.4.22-pre3/arch/parisc/kernel/unaligned.c linux-2.4.22-pre4/arch/parisc/kernel/unaligned.c
--- linux-2.4.22-pre3/arch/parisc/kernel/unaligned.c	2003-06-13 07:51:31.000000000 -0700
+++ linux-2.4.22-pre4/arch/parisc/kernel/unaligned.c	2003-07-09 16:56:28.000000000 -0700
@@ -3,6 +3,7 @@
  *    Unaligned memory access handler
  *
  *    Copyright (C) 2001 Randolph Chung <tausq@debian.org>
+ *    Significantly tweaked by LaMont Jones <lamont@debian.org>
  *
  *    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
@@ -66,28 +67,28 @@
 #define OPCODE3_MASK	OPCODE3(0x3f,1)
 #define OPCODE4_MASK    OPCODE4(0x3f)
 
-/* skip LDB (index) */
+/* skip LDB - never unaligned (index) */
 #define OPCODE_LDH_I	OPCODE1(0x03,0,0x1)
 #define OPCODE_LDW_I	OPCODE1(0x03,0,0x2)
 #define OPCODE_LDD_I	OPCODE1(0x03,0,0x3)
 #define OPCODE_LDDA_I	OPCODE1(0x03,0,0x4)
-/* skip LDCD (index) */
+#define OPCODE_LDCD_I	OPCODE1(0x03,0,0x5)
 #define OPCODE_LDWA_I	OPCODE1(0x03,0,0x6)
-/* skip LDCW (index) */
-/* skip LDB (short) */
+#define OPCODE_LDCW_I	OPCODE1(0x03,0,0x7)
+/* skip LDB - never unaligned (short) */
 #define OPCODE_LDH_S	OPCODE1(0x03,1,0x1)
 #define OPCODE_LDW_S	OPCODE1(0x03,1,0x2)
 #define OPCODE_LDD_S	OPCODE1(0x03,1,0x3)
 #define OPCODE_LDDA_S	OPCODE1(0x03,1,0x4)
-/* skip LDCD (short) */
+#define OPCODE_LDCD_S	OPCODE1(0x03,1,0x5)
 #define OPCODE_LDWA_S	OPCODE1(0x03,1,0x6)
-/* skip LDCW (short) */
-/* skip STB */
+#define OPCODE_LDCW_S	OPCODE1(0x03,1,0x7)
+/* skip STB - never unaligned */
 #define OPCODE_STH	OPCODE1(0x03,1,0x9)
 #define OPCODE_STW	OPCODE1(0x03,1,0xa)
 #define OPCODE_STD	OPCODE1(0x03,1,0xb)
-/* skip STBY */
-/* skip STDBY */
+/* skip STBY - never unaligned */
+/* skip STDBY - never unaligned */
 #define OPCODE_STWA	OPCODE1(0x03,1,0xe)
 #define OPCODE_STDA	OPCODE1(0x03,1,0xf)
 
@@ -103,129 +104,200 @@
 
 #define OPCODE_LDH_L    OPCODE4(0x11)
 #define OPCODE_LDW_L    OPCODE4(0x12)
-#define OPCODE_LDW_L2   OPCODE4(0x13)
+#define OPCODE_LDWM     OPCODE4(0x13)
 #define OPCODE_STH_L    OPCODE4(0x19)
 #define OPCODE_STW_L    OPCODE4(0x1A)
-#define OPCODE_STW_L2   OPCODE4(0x1B)
+#define OPCODE_STWM     OPCODE4(0x1B)
+
+#define MAJOR_OP(i) (((i)>>26)&0x3f)
+#define R1(i) (((i)>>21)&0x1f)
+#define R2(i) (((i)>>16)&0x1f)
+#define R3(i) ((i)&0x1f)
+#define IM(i,n) (((i)>>1&((1<<(n-1))-1))|((i)&1?((0-1L)<<(n-1)):0))
+#define IM5_2(i) IM((i)>>16,5)
+#define IM5_3(i) IM((i),5)
+#define IM14(i) IM((i),14)
 
 int unaligned_enabled = 1;
 
 void die_if_kernel (char *str, struct pt_regs *regs, long err);
 
-static int emulate_load(struct pt_regs *regs, int len, int toreg)
+static int emulate_ldh(struct pt_regs *regs, int toreg)
 {
 	unsigned long saddr = regs->ior;
 	unsigned long val = 0;
-	int ret = 0;
 
-	if (regs->isr != regs->sr[7])
-	{
-		printk(KERN_CRIT "isr verification failed (isr: " RFMT ", sr7: " RFMT "\n",
-			regs->isr, regs->sr[7]);
-		return 1;
-	}
+	DPRINTF("load " RFMT ":" RFMT " to r%d for 2 bytes\n", 
+		regs->isr, regs->ior, toreg);
+
+	__asm__ __volatile__  (
+"	mtsp	%3, %%sr1\n"
+"	ldbs	0(%%sr1,%2), %%r20\n"
+"	ldbs	1(%%sr1,%2), %0\n"
+	"depw	%%r20, 23, 24, %0\n"
+	: "=r" (val)
+	: "0" (val), "r" (saddr), "r" (regs->isr)
+	: "r20" );
+
+	DPRINTF("val = 0x" RFMT "\n", val);
+
+	if (toreg)
+		regs->gr[toreg] = val;
+
+	return 0;
+}
+static int emulate_ldw(struct pt_regs *regs, int toreg)
+{
+	unsigned long saddr = regs->ior;
+	unsigned long val = 0;
 
-	DPRINTF("load " RFMT ":" RFMT " to r%d for %d bytes\n", 
-		regs->isr, regs->ior, toreg, len);
+	DPRINTF("load " RFMT ":" RFMT " to r%d for 4 bytes\n", 
+		regs->isr, regs->ior, toreg);
 
 	__asm__ __volatile__  (
-"       mfsp %%sr1, %%r20\n"
-"       mtsp %6, %%sr1\n"
-"	copy %%r0, %0\n"
-"0:	ldbs,ma	1(%%sr1,%4), %%r19\n"
-"	addi -1, %5, %5\n"
-"	cmpib,>= 0, %5, 2f\n"
-"	or %%r19, %0, %0\n"
-"	b 0b\n"
-	
-#ifdef __LP64__
-	"depd,z %0, 55, 56, %0\n"
-#else
-	"depw,z %0, 23, 24, %0\n"
-#endif
-	
-"1:	ldi	10, %1\n"
-"2:     mtsp %%r20, %%sr1\n"
-"	.section __ex_table,\"a\"\n"
+"	zdep	%2,28,2,%%r19\n"		/* r19=(ofs&3)*8 */
+"	mtsp	%3, %%sr1\n"
+"	depw	%%r0,31,2,%2\n"
+"	ldw	0(%%sr1,%2),%0\n"
+"	ldw	4(%%sr1,%2),%%r20\n"
+"	subi	32,%%r19,%%r19\n"
+"	mtctl	%%r19,11\n"
+"	vshd	%0,%%r20,%0\n"
+	: "=r" (val)
+	: "0" (val), "r" (saddr), "r" (regs->isr)
+	: "r19", "r20" );
+
+	DPRINTF("val = 0x" RFMT "\n", val);
+
+	if (toreg)
+		regs->gr[toreg] = val;
+
+	return 0;
+}
 #ifdef __LP64__
-	".dword 0b, (1b-0b)\n"
-#else
-	".word 0b, (1b-0b)\n"
-#endif
-	".previous\n" 
-	: "=r" (val), "=r" (ret)
-	: "0" (val), "1" (ret), "r" (saddr), "r" (len), "r" (regs->isr)
+static int emulate_ldd(struct pt_regs *regs, int toreg)
+{
+	unsigned long saddr = regs->ior;
+	unsigned long val = 0;
+
+	DPRINTF("load " RFMT ":" RFMT " to r%d for 8 bytes\n", 
+		regs->isr, regs->ior, toreg);
+
+	__asm__ __volatile__  (
+"	depd,z	%2,60,3,%%r19\n"		/* r19=(ofs&7)*8 */
+"	mtsp	%3, %%sr1\n"
+"	depd	%%r0,63,3,%2\n"
+"	ldd	0(%%sr1,%2),%0\n"
+"	ldd	8(%%sr1,%2),%%r20\n"
+"	subi	64,%%r19,%%r19\n"
+"	mtsar	%%r19\n"
+"	shrpd	%0,%%r20,%%sar,%0\n"
+	: "=r" (val)
+	: "0" (val), "r" (saddr), "r" (regs->isr)
 	: "r19", "r20" );
 
 	DPRINTF("val = 0x" RFMT "\n", val);
 
-	regs->gr[toreg] = val;
+	if (toreg)
+		regs->gr[toreg] = val;
 
-	return ret;
+	return 0;
 }
+#endif
 
-static int emulate_store(struct pt_regs *regs, int len, int frreg)
+static int emulate_sth(struct pt_regs *regs, int frreg)
 {
-	int ret = 0;
-#ifdef __LP64__
-	unsigned long val = regs->gr[frreg] << (64 - (len << 3));
-#else
-	unsigned long val = regs->gr[frreg] << (32 - (len << 3));
-#endif
+	unsigned long val = regs->gr[frreg];
+	if (!frreg)
+		val = 0;
 
-	if (regs->isr != regs->sr[7])
-	{
-		printk(KERN_CRIT "isr verification failed (isr: " RFMT ", sr7: " RFMT "\n",
-			regs->isr, regs->sr[7]);
-		return 1;
-	}
+	DPRINTF("store r%d (0x" RFMT ") to " RFMT ":" RFMT " for 2 bytes\n", frreg, 
+		regs->gr[frreg], regs->isr, regs->ior);
+
+	__asm__ __volatile__ (
+"	mtsp %2, %%sr1\n"
+"	extrw,u %0, 23, 8, %%r19\n"
+"	stb %0, 1(%%sr1, %1)\n"
+"	stb %%r19, 0(%%sr1, %1)\n"
+	:
+	: "r" (val), "r" (regs->ior), "r" (regs->isr)
+	: "r19" );
+
+	return 0;
+}
+static int emulate_stw(struct pt_regs *regs, int frreg)
+{
+	unsigned long val = regs->gr[frreg];
+	if (!frreg)
+		val = 0;
 
-	DPRINTF("store r%d (0x" RFMT ") to " RFMT ":" RFMT " for %d bytes\n", frreg, 
-		regs->gr[frreg], regs->isr, regs->ior, len);
+	DPRINTF("store r%d (0x" RFMT ") to " RFMT ":" RFMT " for 4 bytes\n", frreg, 
+		regs->gr[frreg], regs->isr, regs->ior);
 
 
 	__asm__ __volatile__ (
-"       mfsp %%sr1, %%r20\n"		/* save sr1 */
-"       mtsp %5, %%sr1\n"
-#ifdef __LP64__
-"0:	extrd,u %2, 7, 8, %%r19\n"
-#else
-"0:	extrw,u %2, 7, 8, %%r19\n"
-#endif
-"1:	stb,ma %%r19, 1(%%sr1, %3)\n"
-"	addi -1, %4, %4\n"
-"	cmpib,>= 0, %4, 3f\n"
-	
+"	mtsp %2, %%sr1\n"
+"	zdep	%1, 28, 2, %%r19\n"
+"	dep	%%r0, 31, 2, %1\n"
+"	mtsar	%%r19\n"
+"	depwi,z	-2, %%sar, 32, %%r19\n"
+"	ldw	0(%%sr1,%1),%%r20\n"
+"	ldw	4(%%sr1,%1),%%r21\n"
+"	vshd	%%r0, %0, %%r22\n"
+"	vshd	%0, %%r0, %%r1\n"
+"	and	%%r20, %%r19, %%r20\n"
+"	andcm	%%r21, %%r19, %%r21\n"
+"	or	%%r22, %%r20, %%r20\n"
+"	or	%%r1, %%r21, %%r21\n"
+"	stw	%%r20,0(%%sr1,%1)\n"
+"	stw	%%r21,4(%%sr1,%1)\n"
+	:
+	: "r" (val), "r" (regs->ior), "r" (regs->isr)
+	: "r19", "r20", "r21", "r22", "r1" );
+
+	return 0;
+}
 #ifdef __LP64__
-	"depd,z %2, 55, 56, %2\n"
-#else
-	"depw,z %2, 23, 24, %2\n"
-#endif
+static int emulate_std(struct pt_regs *regs, int frreg)
+{
+	unsigned long val = regs->gr[frreg];
+	if (!frreg)
+		val = 0;
 
-"	b 0b\n"
-"	nop\n"
+	DPRINTF("store r%d (0x" RFMT ") to " RFMT ":" RFMT " for 8 bytes\n", frreg, 
+		regs->gr[frreg], regs->isr, regs->ior);
 
-"2:	ldi 11, %0\n"
-"3:     mtsp %%r20, %%sr1\n"
-"	.section __ex_table,\"a\"\n"
-#ifdef __LP64__
-	".dword 1b, (2b-1b)\n"
-#else
-	".word 1b, (2b-1b)\n"
-#endif
-	".previous\n" 
-	: "=r" (ret)
-	: "0" (ret), "r" (val), "r" (regs->ior), "r" (len), "r" (regs->isr)
-	: "r19", "r20" );
 
-	return ret;
-}
+	__asm__ __volatile__ (
+"	mtsp %2, %%sr1\n"
+"	depd,z	%1, 60, 3, %%r19\n"
+"	depd	%%r0, 63, 3, %1\n"
+"	mtsar	%%r19\n"
+"	depdi,z	-2, %%sar, 64, %%r19\n"
+"	ldd	0(%%sr1,%1),%%r20\n"
+"	ldd	8(%%sr1,%1),%%r21\n"
+"	shrpd	%%r0, %0, %%sar, %%r22\n"
+"	shrpd	%0, %%r0, %%sar, %%r1\n"
+"	and	%%r20, %%r19, %%r20\n"
+"	andcm	%%r21, %%r19, %%r21\n"
+"	or	%%r22, %%r20, %%r20\n"
+"	or	%%r1, %%r21, %%r21\n"
+"	std	%%r20,0(%%sr1,%1)\n"
+"	std	%%r21,8(%%sr1,%1)\n"
+	:
+	: "r" (val), "r" (regs->ior), "r" (regs->isr)
+	: "r19", "r20", "r21", "r22", "r1" );
 
+	return 0;
+}
+#endif
 
 void handle_unaligned(struct pt_regs *regs)
 {
 	unsigned long unaligned_count = 0;
 	unsigned long last_time = 0;
+	unsigned long newbase = regs->gr[R1(regs->iir)];
+	int modify = 0;
 	int ret = -1;
 	struct siginfo si;
 
@@ -284,83 +356,169 @@
 	if (!unaligned_enabled)
 		goto force_sigbus;
 
+	/* handle modification - OK, it's ugly, see the instruction manual */
+	switch (MAJOR_OP(regs->iir))
+	{
+	case 0x03:
+	case 0x09:
+	case 0x0b:
+		if (regs->iir&0x20)
+		{
+			modify = 1;
+			if (regs->iir&0x1000)		/* short loads */
+				if (regs->iir&0x200)
+					newbase += IM5_3(regs->iir);
+				else
+					newbase += IM5_2(regs->iir);
+			else if (regs->iir&0x2000)	/* scaled indexed */
+			{
+				int shift=0;
+				switch (regs->iir & OPCODE1_MASK)
+				{
+				case OPCODE_LDH_I:
+					shift= 1; break;
+				case OPCODE_LDW_I:
+					shift= 2; break;
+				case OPCODE_LDD_I:
+				case OPCODE_LDDA_I:
+					shift= 3; break;
+				}
+				newbase += (R2(regs->iir)?regs->gr[R2(regs->iir)]:0)<<shift;
+			} else				/* simple indexed */
+				newbase += (R2(regs->iir)?regs->gr[R2(regs->iir)]:0);
+		}
+		break;
+	case 0x13:
+	case 0x1b:
+		modify = 1;
+		newbase += IM14(regs->iir);
+		break;
+	case 0x14:
+	case 0x1c:
+		if (regs->iir&8)
+		{
+			modify = 1;
+			newbase += IM14(regs->iir&~0xe);
+		}
+		break;
+	case 0x16:
+	case 0x1e:
+		modify = 1;
+		newbase += IM14(regs->iir&6);
+		break;
+	case 0x17:
+	case 0x1f:
+		if (regs->iir&4)
+		{
+			modify = 1;
+			newbase += IM14(regs->iir&~4);
+		}
+		break;
+	}
+
+	if (regs->isr != regs->sr[7])
+	{
+		printk(KERN_CRIT "isr verification failed (isr: " RFMT ", sr7: " RFMT "\n",
+			regs->isr, regs->sr[7]);
+
+		/* don't kill him though, since he has appropriate access to the page, or we
+		 * would never have gotten here.
+		 */
+	}
+
 	/* TODO: make this cleaner... */
 	switch (regs->iir & OPCODE1_MASK)
 	{
 	case OPCODE_LDH_I:
 	case OPCODE_LDH_S:
-		ret = emulate_load(regs, 2, regs->iir & 0x1f);
+		ret = emulate_ldh(regs, R3(regs->iir));
 		break;
 
 	case OPCODE_LDW_I:
 	case OPCODE_LDWA_I:
 	case OPCODE_LDW_S:
 	case OPCODE_LDWA_S:
-		ret = emulate_load(regs, 4, regs->iir&0x1f);
-		break;
-
-	case OPCODE_LDD_I:
-	case OPCODE_LDDA_I:
-	case OPCODE_LDD_S:
-	case OPCODE_LDDA_S:
-		ret = emulate_load(regs, 8, regs->iir&0x1f);
+		ret = emulate_ldw(regs, R3(regs->iir));
 		break;
 
 	case OPCODE_STH:
-		ret = emulate_store(regs, 2, (regs->iir>>16)&0x1f);
+		ret = emulate_sth(regs, R2(regs->iir));
 		break;
 
 	case OPCODE_STW:
 	case OPCODE_STWA:
-		ret = emulate_store(regs, 4, (regs->iir>>16)&0x1f);
+		ret = emulate_stw(regs, R2(regs->iir));
+		break;
+
+#ifdef __LP64__
+	case OPCODE_LDD_I:
+	case OPCODE_LDDA_I:
+	case OPCODE_LDD_S:
+	case OPCODE_LDDA_S:
+		ret = emulate_ldd(regs, R3(regs->iir));
 		break;
 
 	case OPCODE_STD:
 	case OPCODE_STDA:
-		ret = emulate_store(regs, 8, (regs->iir>>16)&0x1f);
+		ret = emulate_std(regs, R2(regs->iir));
+		break;
+#endif
+
+	case OPCODE_LDCD_I:
+	case OPCODE_LDCW_I:
+	case OPCODE_LDCD_S:
+	case OPCODE_LDCW_S:
+		ret = -1;	/* "undefined", but lets kill them. */
 		break;
 	}
+#ifdef __LP64__
 	switch (regs->iir & OPCODE2_MASK)
 	{
 	case OPCODE_LDD_L:
 	case OPCODE_FLDD_L:
-		ret = emulate_load(regs, 8, (regs->iir>>16)&0x1f);
+		ret = emulate_ldd(regs, R2(regs->iir));
 		break;
 
 	case OPCODE_STD_L:
 	case OPCODE_FSTD_L:
-		ret = emulate_store(regs, 8, (regs->iir>>16)&0x1f);
+		ret = emulate_std(regs, R2(regs->iir));
 		break;
 	}
+#endif
 	switch (regs->iir & OPCODE3_MASK)
 	{
 	case OPCODE_LDW_M:
 	case OPCODE_FLDW_L:
-		ret = emulate_load(regs, 4, (regs->iir>>16)&0x1f);
+		ret = emulate_ldw(regs, R2(regs->iir));
 		break;
 
 	case OPCODE_FSTW_L:
 	case OPCODE_STW_M:
-		ret = emulate_store(regs, 4, (regs->iir>>16)&0x1f);
+		ret = emulate_stw(regs, R2(regs->iir));
 		break;
 	}
 	switch (regs->iir & OPCODE4_MASK)
 	{
 	case OPCODE_LDH_L:
-		ret = emulate_load(regs, 2, (regs->iir>>16)&0x1f);
+		ret = emulate_ldh(regs, R2(regs->iir));
 		break;
 	case OPCODE_LDW_L:
-	case OPCODE_LDW_L2:
-		ret = emulate_load(regs, 4, (regs->iir>>16)&0x1f);
+	case OPCODE_LDWM:
+		ret = emulate_ldw(regs, R2(regs->iir));
 		break;
 	case OPCODE_STH_L:
-		ret = emulate_store(regs, 2, (regs->iir>>16)&0x1f);
+		ret = emulate_sth(regs, R2(regs->iir));
 		break;
 	case OPCODE_STW_L:
-	case OPCODE_STW_L2:
-		ret = emulate_store(regs, 4, (regs->iir>>16)&0x1f);
+	case OPCODE_STWM:
+		ret = emulate_stw(regs, R2(regs->iir));
 		break;
 	}
+	/* XXX LJ - need to handle float load/store */
+
+	if (modify && R1(regs->iir))
+		regs->gr[R1(regs->iir)] = newbase;
+
 
 	if (ret < 0)
 		printk(KERN_CRIT "Not-handled unaligned insn 0x%08lx\n", regs->iir);
@@ -424,9 +582,9 @@
 			align_mask = 1UL;
 			break;
 		case OPCODE_LDW_L:
-		case OPCODE_LDW_L2:
+		case OPCODE_LDWM:
 		case OPCODE_STW_L:
-		case OPCODE_STW_L2:
+		case OPCODE_STWM:
 			align_mask = 3UL;
 			break;
 		}
diff -urN linux-2.4.22-pre3/arch/parisc/math-emu/dfadd.c linux-2.4.22-pre4/arch/parisc/math-emu/dfadd.c
--- linux-2.4.22-pre3/arch/parisc/math-emu/dfadd.c	2002-11-28 15:53:10.000000000 -0800
+++ linux-2.4.22-pre4/arch/parisc/math-emu/dfadd.c	2003-07-09 16:56:28.000000000 -0700
@@ -22,7 +22,7 @@
  * BEGIN_DESC
  *
  *  File:
- *	@(#)	pa/spmath/dfadd.c		$Revision: 1.1 $
+ *	@(#)	pa/spmath/dfadd.c		$Revision: $
  *
  *  Purpose:
  *	Double_add: add two double precision values.
diff -urN linux-2.4.22-pre3/arch/parisc/math-emu/dfcmp.c linux-2.4.22-pre4/arch/parisc/math-emu/dfcmp.c
--- linux-2.4.22-pre3/arch/parisc/math-emu/dfcmp.c	2002-11-28 15:53:10.000000000 -0800
+++ linux-2.4.22-pre4/arch/parisc/math-emu/dfcmp.c	2003-07-09 16:56:28.000000000 -0700
@@ -22,7 +22,7 @@
  * BEGIN_DESC
  *
  *  File:
- *	@(#)	pa/spmath/dfcmp.c		$Revision: 1.1 $
+ *	@(#)	pa/spmath/dfcmp.c		$Revision: $
  *
  *  Purpose:
  *	dbl_cmp: compare two values
diff -urN linux-2.4.22-pre3/arch/parisc/math-emu/dfdiv.c linux-2.4.22-pre4/arch/parisc/math-emu/dfdiv.c
--- linux-2.4.22-pre3/arch/parisc/math-emu/dfdiv.c	2002-11-28 15:53:10.000000000 -0800
+++ linux-2.4.22-pre4/arch/parisc/math-emu/dfdiv.c	2003-07-09 16:56:28.000000000 -0700
@@ -22,7 +22,7 @@
  * BEGIN_DESC
  *
  *  File:
- *	@(#)	pa/spmath/dfdiv.c		$Revision: 1.1 $
+ *	@(#)	pa/spmath/dfdiv.c		$Revision: $
  *
  *  Purpose:
  *	Double Precision Floating-point Divide
diff -urN linux-2.4.22-pre3/arch/parisc/math-emu/dfmpy.c linux-2.4.22-pre4/arch/parisc/math-emu/dfmpy.c
--- linux-2.4.22-pre3/arch/parisc/math-emu/dfmpy.c	2002-11-28 15:53:10.000000000 -0800
+++ linux-2.4.22-pre4/arch/parisc/math-emu/dfmpy.c	2003-07-09 16:56:28.000000000 -0700
@@ -22,7 +22,7 @@
  * BEGIN_DESC
  *
  *  File:
- *	@(#)	pa/spmath/dfmpy.c		$Revision: 1.1 $
+ *	@(#)	pa/spmath/dfmpy.c		$Revision: $
  *
  *  Purpose:
  *	Double Precision Floating-point Multiply
diff -urN linux-2.4.22-pre3/arch/parisc/math-emu/dfrem.c linux-2.4.22-pre4/arch/parisc/math-emu/dfrem.c
--- linux-2.4.22-pre3/arch/parisc/math-emu/dfrem.c	2002-11-28 15:53:10.000000000 -0800
+++ linux-2.4.22-pre4/arch/parisc/math-emu/dfrem.c	2003-07-09 16:56:28.000000000 -0700
@@ -22,7 +22,7 @@
  * BEGIN_DESC
  *
  *  File:
- *	@(#)	pa/spmath/dfrem.c		$Revision: 1.1 $
+ *	@(#)	pa/spmath/dfrem.c		$Revision: $
  *
  *  Purpose:
  *	Double Precision Floating-point Remainder
diff -urN linux-2.4.22-pre3/arch/parisc/math-emu/dfsqrt.c linux-2.4.22-pre4/arch/parisc/math-emu/dfsqrt.c
--- linux-2.4.22-pre3/arch/parisc/math-emu/dfsqrt.c	2002-11-28 15:53:10.000000000 -0800
+++ linux-2.4.22-pre4/arch/parisc/math-emu/dfsqrt.c	2003-07-09 16:56:28.000000000 -0700
@@ -22,7 +22,7 @@
  * BEGIN_DESC
  *
  *  File:
- *	@(#)	pa/spmath/dfsqrt.c		$Revision: 1.1 $
+ *	@(#)	pa/spmath/dfsqrt.c		$Revision: $
  *
  *  Purpose:
  *	Double Floating-point Square Root
diff -urN linux-2.4.22-pre3/arch/parisc/math-emu/dfsub.c linux-2.4.22-pre4/arch/parisc/math-emu/dfsub.c
--- linux-2.4.22-pre3/arch/parisc/math-emu/dfsub.c	2002-11-28 15:53:10.000000000 -0800
+++ linux-2.4.22-pre4/arch/parisc/math-emu/dfsub.c	2003-07-09 16:56:28.000000000 -0700
@@ -22,7 +22,7 @@
  * BEGIN_DESC
  *
  *  File:
- *	@(#)	pa/spmath/dfsub.c		$Revision: 1.1 $
+ *	@(#)	pa/spmath/dfsub.c		$Revision: $
  *
  *  Purpose:
  *	Double_subtract: subtract two double precision values.
diff -urN linux-2.4.22-pre3/arch/parisc/math-emu/driver.c linux-2.4.22-pre4/arch/parisc/math-emu/driver.c
--- linux-2.4.22-pre3/arch/parisc/math-emu/driver.c	2002-11-28 15:53:10.000000000 -0800
+++ linux-2.4.22-pre4/arch/parisc/math-emu/driver.c	2003-07-09 16:56:28.000000000 -0700
@@ -80,14 +80,19 @@
 int
 handle_fpe(struct pt_regs *regs)
 {
-	extern void printbinary(unsigned long x, int nbits);
+	extern int printbinary(char *buf, unsigned long x, int nbits);
 	struct siginfo si;
 	unsigned int orig_sw, sw;
 	int signalcode;
+	char buf[128];
 	/* need an intermediate copy of float regs because FPU emulation
 	 * code expects an artificial last entry which contains zero
+	 *
+	 * also, the passed in fr registers contain one word that defines
+	 * the fpu type. the fpu type information is constructed 
+	 * inside the emulation code
 	 */
-	__u64 frcopy[33];
+	__u64 frcopy[36];
 
 	memcpy(frcopy, regs->fr, sizeof regs->fr);
 	frcopy[32] = 0;
@@ -96,8 +101,8 @@
 
 	if (FPUDEBUG) {
 		printk(KERN_DEBUG "FP VZOUICxxxxCQCQCQCQCQCRMxxTDVZOUI ->\n   ");
-		printbinary(orig_sw, 32);
-		printk(KERN_DEBUG "\n");
+		printbinary(buf, orig_sw, 32);
+		printk(KERN_DEBUG "%s\n", buf);
 	}
 
 	signalcode = decode_fpu(frcopy, 0x666);
@@ -107,8 +112,8 @@
 	if (FPUDEBUG) {
 		printk(KERN_DEBUG "VZOUICxxxxCQCQCQCQCQCRMxxTDVZOUI decode_fpu returns %d|0x%x\n",
 			signalcode >> 24, signalcode & 0xffffff);
-		printbinary(sw, 32);
-		printk(KERN_DEBUG "\n");
+		printbinary(buf, sw, 32);
+		printk(KERN_DEBUG "%s\n", buf);
 	}
 
 	memcpy(regs->fr, frcopy, sizeof regs->fr);
diff -urN linux-2.4.22-pre3/arch/parisc/math-emu/fcnvff.c linux-2.4.22-pre4/arch/parisc/math-emu/fcnvff.c
--- linux-2.4.22-pre3/arch/parisc/math-emu/fcnvff.c	2002-11-28 15:53:10.000000000 -0800
+++ linux-2.4.22-pre4/arch/parisc/math-emu/fcnvff.c	2003-07-09 16:56:28.000000000 -0700
@@ -22,7 +22,7 @@
  * BEGIN_DESC
  *
  *  File:
- *	@(#)	pa/spmath/fcnvff.c		$Revision: 1.1 $
+ *	@(#)	pa/spmath/fcnvff.c		$Revision: $
  *
  *  Purpose:
  *	Single Floating-point to Double Floating-point
diff -urN linux-2.4.22-pre3/arch/parisc/math-emu/fcnvfu.c linux-2.4.22-pre4/arch/parisc/math-emu/fcnvfu.c
--- linux-2.4.22-pre3/arch/parisc/math-emu/fcnvfu.c	2002-11-28 15:53:10.000000000 -0800
+++ linux-2.4.22-pre4/arch/parisc/math-emu/fcnvfu.c	2003-07-09 16:56:28.000000000 -0700
@@ -22,7 +22,7 @@
  * BEGIN_DESC
  *
  *  File:
- *	@(#)	pa/spmath/fcnvfu.c		$Revision: 1.1 $
+ *	@(#)	pa/spmath/fcnvfu.c		$Revision: $
  *
  *  Purpose:
  *	Floating-point to Unsigned Fixed-point Converts
diff -urN linux-2.4.22-pre3/arch/parisc/math-emu/fcnvfut.c linux-2.4.22-pre4/arch/parisc/math-emu/fcnvfut.c
--- linux-2.4.22-pre3/arch/parisc/math-emu/fcnvfut.c	2002-11-28 15:53:10.000000000 -0800
+++ linux-2.4.22-pre4/arch/parisc/math-emu/fcnvfut.c	2003-07-09 16:56:28.000000000 -0700
@@ -22,7 +22,7 @@
  * BEGIN_DESC
  *
  *  File:
- *	@(#)	pa/spmath/fcnvfut.c		$Revision: 1.1 $
+ *	@(#)	pa/spmath/fcnvfut.c		$Revision: $
  *
  *  Purpose:
  *	Floating-point to Unsigned Fixed-point Converts with Truncation
diff -urN linux-2.4.22-pre3/arch/parisc/math-emu/fcnvfx.c linux-2.4.22-pre4/arch/parisc/math-emu/fcnvfx.c
--- linux-2.4.22-pre3/arch/parisc/math-emu/fcnvfx.c	2002-11-28 15:53:10.000000000 -0800
+++ linux-2.4.22-pre4/arch/parisc/math-emu/fcnvfx.c	2003-07-09 16:56:28.000000000 -0700
@@ -22,7 +22,7 @@
  * BEGIN_DESC
  *
  *  File:
- *	@(#)	pa/spmath/fcnvfx.c		$Revision: 1.1 $
+ *	@(#)	pa/spmath/fcnvfx.c		$Revision: $
  *
  *  Purpose:
  *	Single Floating-point to Single Fixed-point
diff -urN linux-2.4.22-pre3/arch/parisc/math-emu/fcnvfxt.c linux-2.4.22-pre4/arch/parisc/math-emu/fcnvfxt.c
--- linux-2.4.22-pre3/arch/parisc/math-emu/fcnvfxt.c	2002-11-28 15:53:10.000000000 -0800
+++ linux-2.4.22-pre4/arch/parisc/math-emu/fcnvfxt.c	2003-07-09 16:56:28.000000000 -0700
@@ -22,7 +22,7 @@
  * BEGIN_DESC
  *
  *  File:
- *	@(#)	pa/spmath/fcnvfxt.c		$Revision: 1.1 $
+ *	@(#)	pa/spmath/fcnvfxt.c		$Revision: $
  *
  *  Purpose:
  *	Single Floating-point to Single Fixed-point /w truncated result
diff -urN linux-2.4.22-pre3/arch/parisc/math-emu/fcnvuf.c linux-2.4.22-pre4/arch/parisc/math-emu/fcnvuf.c
--- linux-2.4.22-pre3/arch/parisc/math-emu/fcnvuf.c	2002-11-28 15:53:10.000000000 -0800
+++ linux-2.4.22-pre4/arch/parisc/math-emu/fcnvuf.c	2003-07-09 16:56:28.000000000 -0700
@@ -22,7 +22,7 @@
  * BEGIN_DESC
  *
  *  File:
- *	@(#)	pa/spmath/fcnvuf.c		$Revision: 1.1 $
+ *	@(#)	pa/spmath/fcnvuf.c		$Revision: $
  *
  *  Purpose:
  *	Fixed point to Floating-point Converts
diff -urN linux-2.4.22-pre3/arch/parisc/math-emu/fcnvxf.c linux-2.4.22-pre4/arch/parisc/math-emu/fcnvxf.c
--- linux-2.4.22-pre3/arch/parisc/math-emu/fcnvxf.c	2002-11-28 15:53:10.000000000 -0800
+++ linux-2.4.22-pre4/arch/parisc/math-emu/fcnvxf.c	2003-07-09 16:56:28.000000000 -0700
@@ -22,7 +22,7 @@
  * BEGIN_DESC
  *
  *  File:
- *	@(#)	pa/spmath/fcnvxf.c		$Revision: 1.1 $
+ *	@(#)	pa/spmath/fcnvxf.c		$Revision: $
  *
  *  Purpose:
  *	Single Fixed-point to Single Floating-point
diff -urN linux-2.4.22-pre3/arch/parisc/math-emu/float.h linux-2.4.22-pre4/arch/parisc/math-emu/float.h
--- linux-2.4.22-pre3/arch/parisc/math-emu/float.h	2002-11-28 15:53:10.000000000 -0800
+++ linux-2.4.22-pre4/arch/parisc/math-emu/float.h	2003-07-09 16:56:28.000000000 -0700
@@ -22,7 +22,7 @@
  * BEGIN_DESC
  * 
  *  File: 
- *      @(#)	pa/spmath/float.h		$Revision: 1.2 $
+ *      @(#)	pa/spmath/float.h		$Revision: 1.1 $
  * 
  *  Purpose:
  *      <<please update with a synopis of the functionality provided by this file>>
diff -urN linux-2.4.22-pre3/arch/parisc/math-emu/fmpyfadd.c linux-2.4.22-pre4/arch/parisc/math-emu/fmpyfadd.c
--- linux-2.4.22-pre3/arch/parisc/math-emu/fmpyfadd.c	2002-11-28 15:53:10.000000000 -0800
+++ linux-2.4.22-pre4/arch/parisc/math-emu/fmpyfadd.c	2003-07-09 16:56:28.000000000 -0700
@@ -22,7 +22,7 @@
  * BEGIN_DESC
  *
  *  File:
- *	@(#)	pa/spmath/fmpyfadd.c		$Revision: 1.1 $
+ *	@(#)	pa/spmath/fmpyfadd.c		$Revision: $
  *
  *  Purpose:
  *	Double Floating-point Multiply Fused Add
diff -urN linux-2.4.22-pre3/arch/parisc/math-emu/fpu.h linux-2.4.22-pre4/arch/parisc/math-emu/fpu.h
--- linux-2.4.22-pre3/arch/parisc/math-emu/fpu.h	2002-11-28 15:53:10.000000000 -0800
+++ linux-2.4.22-pre4/arch/parisc/math-emu/fpu.h	2003-07-09 16:56:28.000000000 -0700
@@ -22,7 +22,7 @@
  * BEGIN_DESC
  * 
  *  File: 
- *      @(#)	pa/fp/fpu.h		$Revision: 1.2 $
+ *      @(#)	pa/fp/fpu.h		$Revision: $
  * 
  *  Purpose:
  *      <<please update with a synopis of the functionality provided by this file>>
diff -urN linux-2.4.22-pre3/arch/parisc/math-emu/fpudispatch.c linux-2.4.22-pre4/arch/parisc/math-emu/fpudispatch.c
--- linux-2.4.22-pre3/arch/parisc/math-emu/fpudispatch.c	2002-11-28 15:53:10.000000000 -0800
+++ linux-2.4.22-pre4/arch/parisc/math-emu/fpudispatch.c	2003-07-09 16:56:28.000000000 -0700
@@ -22,7 +22,7 @@
  * BEGIN_DESC
  *
  *  File:
- *	@(#)	pa/fp/fpudispatch.c		$Revision: 1.1 $
+ *	@(#)	pa/fp/fpudispatch.c		$Revision: $
  *
  *  Purpose:
  *	<<please update with a synopsis of the functionality provided by this file>>
@@ -51,6 +51,7 @@
 
 #include "float.h"
 #include "types.h"
+#include <asm/processor.h>
 /* #include <sys/debug.h> */
 /* #include <machine/sys/mdep_private.h> */
 
@@ -166,6 +167,20 @@
 
 #define VASSERT(x)
 
+static void parisc_linux_get_fpu_type(u_int fpregs[])
+{
+	/* on pa-linux the fpu type is not filled in by the
+	 * caller; it is constructed here  
+	 */ 
+	if (boot_cpu_data.cpu_type == pcxs)
+		fpregs[FPU_TYPE_FLAG_POS] = TIMEX_EXTEN_FLAG;
+	else if (boot_cpu_data.cpu_type == pcxt ||
+	         boot_cpu_data.cpu_type == pcxt_)
+		fpregs[FPU_TYPE_FLAG_POS] = ROLEX_EXTEN_FLAG;
+	else if (boot_cpu_data.cpu_type >= pcxu)
+		fpregs[FPU_TYPE_FLAG_POS] = PA2_0_FPU_FLAG;
+}
+
 /*
  * this routine will decode the excepting floating point instruction and
  * call the approiate emulation routine.
@@ -184,6 +199,8 @@
 	/* All FP emulation code assumes that ints are 4-bytes in length */
 	VASSERT(sizeof(int) == 4);
 
+	parisc_linux_get_fpu_type(fpregs);
+
 	fpu_type_flags=fpregs[FPU_TYPE_FLAG_POS];  /* get fpu type flags */
 
 	class = get_class(ir);
diff -urN linux-2.4.22-pre3/arch/parisc/math-emu/sfadd.c linux-2.4.22-pre4/arch/parisc/math-emu/sfadd.c
--- linux-2.4.22-pre3/arch/parisc/math-emu/sfadd.c	2002-11-28 15:53:10.000000000 -0800
+++ linux-2.4.22-pre4/arch/parisc/math-emu/sfadd.c	2003-07-09 16:56:28.000000000 -0700
@@ -22,7 +22,7 @@
  * BEGIN_DESC
  *
  *  File:
- *	@(#)	pa/spmath/sfadd.c		$Revision: 1.1 $
+ *	@(#)	pa/spmath/sfadd.c		$Revision: $
  *
  *  Purpose:
  *	Single_add: add two single precision values.
diff -urN linux-2.4.22-pre3/arch/parisc/math-emu/sfcmp.c linux-2.4.22-pre4/arch/parisc/math-emu/sfcmp.c
--- linux-2.4.22-pre3/arch/parisc/math-emu/sfcmp.c	2002-11-28 15:53:10.000000000 -0800
+++ linux-2.4.22-pre4/arch/parisc/math-emu/sfcmp.c	2003-07-09 16:56:28.000000000 -0700
@@ -22,7 +22,7 @@
  * BEGIN_DESC
  *
  *  File:
- *	@(#)	pa/spmath/sfcmp.c		$Revision: 1.1 $
+ *	@(#)	pa/spmath/sfcmp.c		$Revision: $
  *
  *  Purpose:
  *	sgl_cmp: compare two values
diff -urN linux-2.4.22-pre3/arch/parisc/math-emu/sfdiv.c linux-2.4.22-pre4/arch/parisc/math-emu/sfdiv.c
--- linux-2.4.22-pre3/arch/parisc/math-emu/sfdiv.c	2002-11-28 15:53:10.000000000 -0800
+++ linux-2.4.22-pre4/arch/parisc/math-emu/sfdiv.c	2003-07-09 16:56:28.000000000 -0700
@@ -22,7 +22,7 @@
  * BEGIN_DESC
  *
  *  File:
- *	@(#)	pa/spmath/sfdiv.c		$Revision: 1.1 $
+ *	@(#)	pa/spmath/sfdiv.c		$Revision: $
  *
  *  Purpose:
  *	Single Precision Floating-point Divide
diff -urN linux-2.4.22-pre3/arch/parisc/math-emu/sfmpy.c linux-2.4.22-pre4/arch/parisc/math-emu/sfmpy.c
--- linux-2.4.22-pre3/arch/parisc/math-emu/sfmpy.c	2002-11-28 15:53:10.000000000 -0800
+++ linux-2.4.22-pre4/arch/parisc/math-emu/sfmpy.c	2003-07-09 16:56:28.000000000 -0700
@@ -22,7 +22,7 @@
  * BEGIN_DESC
  *
  *  File:
- *	@(#)	pa/spmath/sfmpy.c		$Revision: 1.1 $
+ *	@(#)	pa/spmath/sfmpy.c		$Revision: $
  *
  *  Purpose:
  *	Single Precision Floating-point Multiply
diff -urN linux-2.4.22-pre3/arch/parisc/math-emu/sfrem.c linux-2.4.22-pre4/arch/parisc/math-emu/sfrem.c
--- linux-2.4.22-pre3/arch/parisc/math-emu/sfrem.c	2002-11-28 15:53:10.000000000 -0800
+++ linux-2.4.22-pre4/arch/parisc/math-emu/sfrem.c	2003-07-09 16:56:28.000000000 -0700
@@ -22,7 +22,7 @@
  * BEGIN_DESC
  *
  *  File:
- *	@(#)	pa/spmath/sfrem.c		$Revision: 1.1 $
+ *	@(#)	pa/spmath/sfrem.c		$Revision: $
  *
  *  Purpose:
  *	Single Precision Floating-point Remainder
diff -urN linux-2.4.22-pre3/arch/parisc/math-emu/sfsqrt.c linux-2.4.22-pre4/arch/parisc/math-emu/sfsqrt.c
--- linux-2.4.22-pre3/arch/parisc/math-emu/sfsqrt.c	2002-11-28 15:53:10.000000000 -0800
+++ linux-2.4.22-pre4/arch/parisc/math-emu/sfsqrt.c	2003-07-09 16:56:28.000000000 -0700
@@ -22,7 +22,7 @@
  * BEGIN_DESC
  *
  *  File:
- *	@(#)	pa/spmath/sfsqrt.c		$Revision: 1.1 $
+ *	@(#)	pa/spmath/sfsqrt.c		$Revision: $
  *
  *  Purpose:
  *	Single Floating-point Square Root
diff -urN linux-2.4.22-pre3/arch/parisc/math-emu/sfsub.c linux-2.4.22-pre4/arch/parisc/math-emu/sfsub.c
--- linux-2.4.22-pre3/arch/parisc/math-emu/sfsub.c	2002-11-28 15:53:10.000000000 -0800
+++ linux-2.4.22-pre4/arch/parisc/math-emu/sfsub.c	2003-07-09 16:56:28.000000000 -0700
@@ -22,7 +22,7 @@
  * BEGIN_DESC
  *
  *  File:
- *	@(#)	pa/spmath/sfsub.c		$Revision: 1.1 $
+ *	@(#)	pa/spmath/sfsub.c		$Revision: $
  *
  *  Purpose:
  *	Single_subtract: subtract two single precision values.
diff -urN linux-2.4.22-pre3/arch/ppc/boot/chrp/main.c linux-2.4.22-pre4/arch/ppc/boot/chrp/main.c
--- linux-2.4.22-pre3/arch/ppc/boot/chrp/main.c	2003-06-13 07:51:31.000000000 -0700
+++ linux-2.4.22-pre4/arch/ppc/boot/chrp/main.c	2003-07-09 16:56:28.000000000 -0700
@@ -43,6 +43,8 @@
 
 static char scratch[SCRATCH_SIZE];	/* 1MB of scratch space for gunzip */
 
+typedef void (*kernel_start_t)(int, int, void *, unsigned int, unsigned int);
+
 void
 chrpboot(int a1, int a2, void *prom)
 {
@@ -93,7 +95,7 @@
     sa = (unsigned long)PROG_START;
     printf("start address = 0x%x\n\r", sa);
 
-    (*(void (*)())sa)(a1, a2, prom, initrd_start, initrd_size);
+    (*(kernel_start_t)sa)(a1, a2, prom, initrd_start, initrd_size);
 
     printf("returned?\n\r");
 
diff -urN linux-2.4.22-pre3/arch/ppc/boot/common/Makefile linux-2.4.22-pre4/arch/ppc/boot/common/Makefile
--- linux-2.4.22-pre3/arch/ppc/boot/common/Makefile	2002-08-02 17:39:43.000000000 -0700
+++ linux-2.4.22-pre4/arch/ppc/boot/common/Makefile	2003-07-09 16:56:28.000000000 -0700
@@ -11,7 +11,6 @@
 USE_STANDARD_AS_RULE	:= true
 
 coffcrt0.o:
-	$(CC) -I$(TOPDIR)/arch/$(ARCH)/kernel $(AFLAGS) -DXCOFF \
-		-traditional -c -o coffcrt0.o crt0.S
+	$(CC) $(AFLAGS) -DXCOFF -traditional -c -o coffcrt0.o crt0.S
 
 include $(TOPDIR)/Rules.make
diff -urN linux-2.4.22-pre3/arch/ppc/boot/pmac/Makefile linux-2.4.22-pre4/arch/ppc/boot/pmac/Makefile
--- linux-2.4.22-pre3/arch/ppc/boot/pmac/Makefile	2003-06-13 07:51:31.000000000 -0700
+++ linux-2.4.22-pre4/arch/ppc/boot/pmac/Makefile	2003-07-09 16:56:28.000000000 -0700
@@ -30,8 +30,6 @@
 
 TFTPIMAGE=/tftpboot/zImage.pmac$(END)
 
-AFLAGS_../common/crt0.o		+= -I$(TOPDIR)/arch/$(ARCH)/kernel
-
 ../common/coffcrt0.o:
 	$(MAKE) -C ../common coffcrt0.o
 
diff -urN linux-2.4.22-pre3/arch/ppc/boot/pmac/chrpmain.c linux-2.4.22-pre4/arch/ppc/boot/pmac/chrpmain.c
--- linux-2.4.22-pre3/arch/ppc/boot/pmac/chrpmain.c	2003-06-13 07:51:31.000000000 -0700
+++ linux-2.4.22-pre4/arch/ppc/boot/pmac/chrpmain.c	2003-07-09 16:56:28.000000000 -0700
@@ -38,6 +38,8 @@
 
 #define SCRATCH_SIZE	(128 << 10)
 
+typedef void (*kernel_start_t)(int, int, void *);
+
 void boot(int a1, int a2, void *prom)
 {
     unsigned sa, len;
@@ -87,7 +89,7 @@
     sa = (unsigned long)PROG_START;
     printf("start address = 0x%x\n", sa);
 
-    (*(void (*)())sa)(a1, a2, prom);
+    (*(kernel_start_t)sa)(a1, a2, prom);
 
     printf("returned?\n");
 
diff -urN linux-2.4.22-pre3/arch/ppc/boot/pmac/coffmain.c linux-2.4.22-pre4/arch/ppc/boot/pmac/coffmain.c
--- linux-2.4.22-pre3/arch/ppc/boot/pmac/coffmain.c	2003-06-13 07:51:31.000000000 -0700
+++ linux-2.4.22-pre4/arch/ppc/boot/pmac/coffmain.c	2003-07-09 16:56:28.000000000 -0700
@@ -44,6 +44,8 @@
 
 static char heap[SCRATCH_SIZE];
 
+typedef void (*kernel_start_t)(int, int, void *);
+
 void boot(int a1, int a2, void *prom)
 {
     unsigned sa, len;
@@ -92,7 +94,7 @@
     sa = (unsigned long)PROG_START;
     printf("start address = 0x%x\n", sa);
 
-    (*(void (*)())sa)(a1, a2, prom);
+    (*(kernel_start_t)sa)(a1, a2, prom);
 
     printf("returned?\n");
 
diff -urN linux-2.4.22-pre3/arch/ppc/boot/pmac/start.c linux-2.4.22-pre4/arch/ppc/boot/pmac/start.c
--- linux-2.4.22-pre3/arch/ppc/boot/pmac/start.c	2003-06-13 07:51:31.000000000 -0700
+++ linux-2.4.22-pre4/arch/ppc/boot/pmac/start.c	2003-07-09 16:56:28.000000000 -0700
@@ -11,7 +11,7 @@
 extern int strlen(const char *s);
 extern void boot(int a1, int a2, void *prom);
 
-int (*prom)();
+int (*prom)(void *args);
 
 void *chosen_handle;
 void *stdin;
@@ -26,7 +26,7 @@
 void
 start(int a1, int a2, void *promptr)
 {
-    prom = (int (*)()) promptr;
+    prom = (int (*)(void *)) promptr;
     chosen_handle = finddevice("/chosen");
     if (chosen_handle == (void *) -1)
 	exit();
diff -urN linux-2.4.22-pre3/arch/ppc/boot/prep/Makefile linux-2.4.22-pre4/arch/ppc/boot/prep/Makefile
--- linux-2.4.22-pre3/arch/ppc/boot/prep/Makefile	2003-06-13 07:51:31.000000000 -0700
+++ linux-2.4.22-pre4/arch/ppc/boot/prep/Makefile	2003-07-09 16:56:28.000000000 -0700
@@ -39,10 +39,6 @@
 
 # Extra include search dirs
 CFLAGS_kbd.o			+= -I$(TOPDIR)/drivers/char
-AFLAGS_head.o			+= -I$(TOPDIR)/arch/$(ARCH)/kernel
-AFLAGS_../common/util.o			+= -I$(TOPDIR)/arch/$(ARCH)/kernel
-AFLAGS_../common/relocate.o	+= -I$(TOPDIR)/arch/$(ARCH)/kernel
-AFLAGS_../simple/legacy.o	+= -I$(TOPDIR)/arch/$(ARCH)/kernel
 
 all: zImage
 
diff -urN linux-2.4.22-pre3/arch/ppc/kernel/ppc405_pci.c linux-2.4.22-pre4/arch/ppc/kernel/ppc405_pci.c
--- linux-2.4.22-pre3/arch/ppc/kernel/ppc405_pci.c	2003-07-09 16:56:24.000000000 -0700
+++ linux-2.4.22-pre4/arch/ppc/kernel/ppc405_pci.c	2003-07-09 16:56:28.000000000 -0700
@@ -29,16 +29,6 @@
  *  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.
- *
- *
- *  	:05/07/02 - Armin
- *  		converted to using ocp register & core_ocp[];
- *
- *  	: 05/24/02 - Armin 
- *  		change get_ocp_paddr to ocp_get_paddr
- *
- *  	: 05029/02 - Armin 
- *  	  added PSR_PCI_ARBIT_EN check , not all 4xx have a pin strap reg
  */
 
 #include <linux/pci.h>
diff -urN linux-2.4.22-pre3/arch/ppc/kernel/setup.c linux-2.4.22-pre4/arch/ppc/kernel/setup.c
--- linux-2.4.22-pre3/arch/ppc/kernel/setup.c	2003-06-13 07:51:31.000000000 -0700
+++ linux-2.4.22-pre4/arch/ppc/kernel/setup.c	2003-07-09 16:56:28.000000000 -0700
@@ -89,7 +89,7 @@
 int icache_bsize;
 int ucache_bsize;
 
-#ifdef CONFIG_VGA_CONSOLE
+#if defined(CONFIG_VGA_CONSOLE) || defined(CONFIG_FB)
 struct screen_info screen_info = {
 	0, 25,			/* orig-x, orig-y */
 	0,			/* unused */
@@ -101,7 +101,7 @@
 	1,			/* orig-video-isVGA */
 	16			/* orig-video-points */
 };
-#endif /* CONFIG_VGA_CONSOLE */
+#endif /* CONFIG_VGA_CONSOLE || CONFIG_FB */
 
 void machine_restart(char *cmd)
 {
diff -urN linux-2.4.22-pre3/arch/ppc/platforms/prep_setup.c linux-2.4.22-pre4/arch/ppc/platforms/prep_setup.c
--- linux-2.4.22-pre3/arch/ppc/platforms/prep_setup.c	2003-06-13 07:51:31.000000000 -0700
+++ linux-2.4.22-pre4/arch/ppc/platforms/prep_setup.c	2003-07-09 16:56:28.000000000 -0700
@@ -659,8 +659,7 @@
 prep_init_vesa(void)
 {
 #if defined(CONFIG_PREP_RESIDUAL) && \
-	(defined(CONFIG_FB_VGA16) || defined(CONFIG_FB_VGA_16_MODULE) || \
-	 defined(CONFIG_FB_VESA))
+	(defined(CONFIG_VGA_CONSOLE) || defined(CONFIG_FB))
 	PPC_DEVICE *vgadev;
 
 	vgadev = residual_find_device(~0, NULL, DisplayController, SVGAController,
diff -urN linux-2.4.22-pre3/arch/ppc64/kernel/sys_ppc32.c linux-2.4.22-pre4/arch/ppc64/kernel/sys_ppc32.c
--- linux-2.4.22-pre3/arch/ppc64/kernel/sys_ppc32.c	2003-07-09 16:56:24.000000000 -0700
+++ linux-2.4.22-pre4/arch/ppc64/kernel/sys_ppc32.c	2003-07-09 16:56:28.000000000 -0700
@@ -454,7 +454,7 @@
 	return err;
 }
 
-struct dqblk32 {
+struct user_dqblk32 {
     __u32 dqb_bhardlimit;
     __u32 dqb_bsoftlimit;
     __u32 dqb_curblocks;
@@ -473,13 +473,13 @@
  * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode)
  * and the register representation of a signed int (msr in 64-bit mode) is performed.
  */
-asmlinkage long sys32_quotactl(u32 cmd_parm, const char *special, u32 id_parm, unsigned long addr)
+asmlinkage long sys32_quotactl(u32 cmd_parm, const char *special, u32 id_parm, caddr_t addr)
 {
-  int cmd = (int)cmd_parm;
-  int id  = (int)id_parm;
+	int cmd = (int)cmd_parm;
+	int id  = (int)id_parm;
 	int cmds = cmd >> SUBCMDSHIFT;
 	int err;
-	struct dqblk d;
+	struct v1c_mem_dqblk d;
 	mm_segment_t old_fs;
 	char *spec;
 	
@@ -487,35 +487,33 @@
 		    current->pid, current, current->comm);
 
 	switch (cmds) {
-	case Q_GETQUOTA:
+	case Q_V1_GETQUOTA:
 		break;
-	case Q_SETQUOTA:
-	case Q_SETUSE:
-	case Q_SETQLIM:
-		if (copy_from_user (&d, (struct dqblk32 *)addr,
-				    sizeof (struct dqblk32)))
+	case Q_V1_SETQUOTA:
+	case Q_V1_SETUSE:
+	case Q_V1_SETQLIM:
+		if (copy_from_user(&d, addr, sizeof(struct user_dqblk32)))
 			return -EFAULT;
-		d.dqb_itime = ((struct dqblk32 *)&d)->dqb_itime;
-		d.dqb_btime = ((struct dqblk32 *)&d)->dqb_btime;
+		d.dqb_itime = ((struct user_dqblk32 *)&d)->dqb_itime;
+		d.dqb_btime = ((struct user_dqblk32 *)&d)->dqb_btime;
 		break;
 	default:
-		return sys_quotactl(cmd, special,
-				    id, (caddr_t)addr);
+		return sys_quotactl(cmd, special, id, addr);
 	}
-	spec = getname (special);
+	spec = getname(special);
 	err = PTR_ERR(spec);
-	if (IS_ERR(spec)) return err;
-	old_fs = get_fs ();
+	if (IS_ERR(spec))
+		return err;
+	old_fs = get_fs();
 	set_fs (KERNEL_DS);
 	err = sys_quotactl(cmd, (const char *)spec, id, (caddr_t)&d);
 	set_fs (old_fs);
 	putname (spec);
-	if (cmds == Q_GETQUOTA) {
+	if (cmds == Q_V1_GETQUOTA) {
 		__kernel_time_t b = d.dqb_btime, i = d.dqb_itime;
-		((struct dqblk32 *)&d)->dqb_itime = i;
-		((struct dqblk32 *)&d)->dqb_btime = b;
-		if (copy_to_user ((struct dqblk32 *)addr, &d,
-				  sizeof (struct dqblk32)))
+		((struct user_dqblk32 *)&d)->dqb_itime = i;
+		((struct user_dqblk32 *)&d)->dqb_btime = b;
+		if (copy_to_user(addr, &d, sizeof(struct user_dqblk32)))
 			return -EFAULT;
 	}
 	
diff -urN linux-2.4.22-pre3/arch/s390x/kernel/linux32.c linux-2.4.22-pre4/arch/s390x/kernel/linux32.c
--- linux-2.4.22-pre3/arch/s390x/kernel/linux32.c	2003-07-09 16:56:24.000000000 -0700
+++ linux-2.4.22-pre4/arch/s390x/kernel/linux32.c	2003-07-09 16:56:28.000000000 -0700
@@ -935,7 +935,7 @@
 	return sys32_fcntl(fd, cmd, arg);
 }
 
-struct dqblk32 {
+struct user_dqblk32 {
     __u32 dqb_bhardlimit;
     __u32 dqb_bsoftlimit;
     __u32 dqb_curblocks;
@@ -948,46 +948,45 @@
                                 
 extern asmlinkage int sys_quotactl(int cmd, const char *special, int id, caddr_t addr);
 
-asmlinkage int sys32_quotactl(int cmd, const char *special, int id, unsigned long addr)
+asmlinkage int sys32_quotactl(int cmd, const char *special, int id, caddr_t addr)
 {
 	int cmds = cmd >> SUBCMDSHIFT;
 	int err;
-	struct dqblk d;
+	struct v1c_mem_dqblk d;
 	mm_segment_t old_fs;
 	char *spec;
 	
 	switch (cmds) {
-	case Q_GETQUOTA:
+	case Q_V1_GETQUOTA:
 		break;
-	case Q_SETQUOTA:
-	case Q_SETUSE:
-	case Q_SETQLIM:
-		if (copy_from_user (&d, (struct dqblk32 *)addr,
-				    sizeof (struct dqblk32)))
+	case Q_V1_SETQUOTA:
+	case Q_V1_SETUSE:
+	case Q_V1_SETQLIM:
+		if (copy_from_user(&d, addr, sizeof (struct user_dqblk32)))
 			return -EFAULT;
-		d.dqb_itime = ((struct dqblk32 *)&d)->dqb_itime;
-		d.dqb_btime = ((struct dqblk32 *)&d)->dqb_btime;
+		d.dqb_itime = ((struct user_dqblk32 *)&d)->dqb_itime;
+		d.dqb_btime = ((struct user_dqblk32 *)&d)->dqb_btime;
 		break;
 	default:
-		return sys_quotactl(cmd, special,
-				    id, (caddr_t)addr);
+		return sys_quotactl(cmd, special, id, addr);
 	}
-	spec = getname (special);
+
+	spec = getname(special);
 	err = PTR_ERR(spec);
-	if (IS_ERR(spec)) return err;
-	old_fs = get_fs ();
+	if (IS_ERR(spec))
+		return err;
+	old_fs = get_fs();
 	set_fs (KERNEL_DS);
 	err = sys_quotactl(cmd, (const char *)spec, id, (caddr_t)&d);
-	set_fs (old_fs);
-	putname (spec);
+	set_fs(old_fs);
+	putname(spec);
 	if (err)
 		return err;
-	if (cmds == Q_GETQUOTA) {
+	if (cmds == Q_V1_GETQUOTA) {
 		__kernel_time_t b = d.dqb_btime, i = d.dqb_itime;
-		((struct dqblk32 *)&d)->dqb_itime = i;
-		((struct dqblk32 *)&d)->dqb_btime = b;
-		if (copy_to_user ((struct dqblk32 *)addr, &d,
-				  sizeof (struct dqblk32)))
+		((struct user_dqblk32 *)&d)->dqb_itime = i;
+		((struct user_dqblk32 *)&d)->dqb_btime = b;
+		if (copy_to_user(addr, &d, sizeof (struct user_dqblk32)))
 			return -EFAULT;
 	}
 	return 0;
diff -urN linux-2.4.22-pre3/arch/sh64/config.in linux-2.4.22-pre4/arch/sh64/config.in
--- linux-2.4.22-pre3/arch/sh64/config.in	2003-07-09 16:56:24.000000000 -0700
+++ linux-2.4.22-pre4/arch/sh64/config.in	2003-07-09 16:56:28.000000000 -0700
@@ -159,18 +159,18 @@
    source net/Config.in
 fi
 
-# mainmenu_option next_comment
-# comment 'ATA/IDE/MFM/RLL support'
-# 
-# tristate 'ATA/IDE/MFM/RLL support' CONFIG_IDE
-# 
-# if [ "$CONFIG_IDE" != "n" ]; then
-#   source drivers/ide/Config.in
-# else
+mainmenu_option next_comment
+comment 'ATA/IDE/MFM/RLL support'
+
+tristate 'ATA/IDE/MFM/RLL support' CONFIG_IDE
+
+if [ "$CONFIG_IDE" != "n" ]; then
+  source drivers/ide/Config.in
+else
    define_bool CONFIG_BLK_DEV_IDE_MODES n
    define_bool CONFIG_BLK_DEV_HD n
-# fi
-# endmenu
+fi
+endmenu
 
 mainmenu_option next_comment
 comment 'SCSI support'
diff -urN linux-2.4.22-pre3/arch/sh64/kernel/pci_sh5.c linux-2.4.22-pre4/arch/sh64/kernel/pci_sh5.c
--- linux-2.4.22-pre3/arch/sh64/kernel/pci_sh5.c	2003-07-09 16:56:24.000000000 -0700
+++ linux-2.4.22-pre4/arch/sh64/kernel/pci_sh5.c	2003-07-09 16:56:28.000000000 -0700
@@ -124,7 +124,7 @@
         uval = SH5PCI_READ(CR);
 
 	/* Set command Register */
-        SH5PCI_WRITE(CR, uval | CR_LOCK_MASK | CR_CFINT| CR_FTO | CR_PFE | CR_PFCS);
+        SH5PCI_WRITE(CR, uval | CR_LOCK_MASK | CR_CFINT| CR_FTO | CR_PFE | CR_PFCS | CR_BMAM );
 
 	uval=SH5PCI_READ(CR);
         dprintk("CR is actually 0x%08x\n",uval);
@@ -318,33 +318,46 @@
 
 static int __init map_cayman_irq(struct pci_dev *dev, u8 slot, u8 pin)
 {
-	dprintk("map_cayman_irq for dev %d on bus %d slot %d, pin is %d\n",
-	       dev->devfn,dev->bus->number,slot,pin);
- 
+	int result = -1;
+
 	if (dev->bus->number == 0) {
-	        switch (slot) {
+	        switch ((slot + (pin-1)) & 3) {
 	        case 0:
-	          return IRQ_INTA;
+	          result = IRQ_INTA;
+		  break;
                 case 1:
-	          return IRQ_INTB;
+	          result = IRQ_INTB;
+		  break;
                 case 2:
-	          return IRQ_INTC;
+	          result = IRQ_INTC;
+		  break;
 		case 3:
-		  return IRQ_INTD;
+		  result = IRQ_INTD;
+		  break;
                 }
         }
 
 	if (dev->bus->number == 2) {
-	        switch(slot) {
+	        switch((slot + (pin-1)) & 3) {
 	        case 0:
-	          return IRQ_P2INTA;
+	          result = IRQ_P2INTA;
+		  break;
                 case 1:
-	          return IRQ_P2INTB;
+	          result = IRQ_P2INTB;
+		  break;
+                case 2:
+	          result = IRQ_P2INTC;
+		  break;
+                case 3:
+	          result = IRQ_P2INTD;
+		  break;
                 }
         }
 
-	/* FIXME - DO PROPER MAPPING FOR GODS SAKE */
-	return -1;
+	dprintk("map_cayman_irq for dev %d on bus %d slot %d, pin is %d : irq=%d\n",
+	       dev->devfn,dev->bus->number,slot,pin,result);
+
+	return result;
 }
 
 #ifdef DEBUG
diff -urN linux-2.4.22-pre3/arch/sh64/kernel/sh_ksyms.c linux-2.4.22-pre4/arch/sh64/kernel/sh_ksyms.c
--- linux-2.4.22-pre3/arch/sh64/kernel/sh_ksyms.c	2003-07-09 16:56:24.000000000 -0700
+++ linux-2.4.22-pre4/arch/sh64/kernel/sh_ksyms.c	2003-07-09 16:56:28.000000000 -0700
@@ -41,10 +41,13 @@
 extern void dump_thread(struct pt_regs *, struct user *);
 extern int dump_fpu(elf_fpregset_t *);
 
+#if 0
+/* Not yet - there's no declaration of drive_info anywhere. */
 #if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_HD) || defined(CONFIG_BLK_DEV_IDE_MODULE) || defined(CONFIG_BLK_DEV_HD_MODULE)
 extern struct drive_info_struct drive_info;
 EXPORT_SYMBOL(drive_info);
 #endif
+#endif
 
 /* platform dependent support */
 EXPORT_SYMBOL(dump_thread);
diff -urN linux-2.4.22-pre3/arch/sh64/kernel/traps.c linux-2.4.22-pre4/arch/sh64/kernel/traps.c
--- linux-2.4.22-pre3/arch/sh64/kernel/traps.c	2003-07-09 16:56:24.000000000 -0700
+++ linux-2.4.22-pre4/arch/sh64/kernel/traps.c	2003-07-09 16:56:28.000000000 -0700
@@ -90,20 +90,28 @@
 DO_ERROR(87, SIGSEGV, "address error (exec)", address_error_exec, current)
 
 #if defined(CONFIG_SH64_ID2815_WORKAROUND)
+
+#define OPCODE_INVALID      0
+#define OPCODE_USER_VALID   1
+#define OPCODE_PRIV_VALID   2
+
+/* getcon/putcon - requires checking which control register is referenced. */
+#define OPCODE_CTRL_REG     3
+
 /* Table of valid opcodes for SHmedia mode.
    Form a 10-bit value by concatenating the major/minor opcodes i.e.
-   opcode[31:26,20:16].  The 5 MSBs of this value index into the following
-   array.  The 5 LSBs select the bit in the entry (bit 0 corresponds to
-   LSBs==5'b00000 etc). */
-static unsigned long valid_shmedia_opcodes[32] = {
-	0xeacfff8a,0x6e667676,0x40400000,0x2307150f,
-	0x8000002a,0x88ffaef7,0x40ffff07,0x2300fff3,
-	0x0022003f,0x7766022b,0x00000000,0x00000000,
-	0x88aa000f,0x80ef0022,0x00000000,0x00000000,
-	0xffffffff,0xffffffff,0xffffffff,0xffffffff,
-	0xffffffff,0xffffffff,0xffffffff,0xffffffff,
-	0x20cf80cc,0xffffffff,0xffffffff,0xffffffff,
-	0x002293fe,0xffffffff,0x00000000,0x00000000
+   opcode[31:26,20:16].  The 6 MSBs of this value index into the following
+   array.  The 4 LSBs select the bit-pair in the entry (bits 1:0 correspond to
+   LSBs==4'b0000 etc). */
+static unsigned long shmedia_opcode_table[64] = {
+	0x55554044,0x54445055,0x15141514,0x14541414,0x00000000,0x10001000,0x01110055,0x04050015,
+	0x00000444,0xc0000000,0x44545515,0x40405555,0x55550015,0x10005555,0x55555505,0x04050000,
+	0x00000555,0x00000404,0x00040445,0x15151414,0x00000000,0x00000000,0x00000000,0x00000000,
+	0x00000055,0x40404444,0x00000404,0xc0009495,0x00000000,0x00000000,0x00000000,0x00000000,
+	0x55555555,0x55555555,0x55555555,0x55555555,0x55555555,0x55555555,0x55555555,0x55555555,
+	0x55555555,0x55555555,0x55555555,0x55555555,0x55555555,0x55555555,0x55555555,0x55555555,
+	0x80005050,0x04005055,0x55555555,0x55555555,0x55555555,0x55555555,0x55555555,0x55555555,
+	0x81055554,0x00000404,0x55555555,0x55555555,0x00000000,0x00000000,0x00000000,0x00000000
 };
 
 void do_reserved_inst(unsigned long error_code, struct pt_regs *regs)
@@ -131,25 +139,58 @@
 			get_user_error = __get_user(opcode, (unsigned long *)aligned_pc);
 		}
 		if (get_user_error >= 0) {
-			unsigned long index, mask;
+			unsigned long index, shift;
 			unsigned long major, minor, combined;
 			unsigned long reserved_field;
 			reserved_field = opcode & 0xf; /* These bits are currently reserved as zero in all valid opcodes */
 			major = (opcode >> 26) & 0x3f;
 			minor = (opcode >> 16) & 0xf;
-			combined = (major << 6) | minor;
-			index = combined >> 5;
-			mask = 1UL << (combined & 0x1f);
-			if ((reserved_field == 0) && (valid_shmedia_opcodes[index] & mask)) {
-#if 0
-				printk("Fixed up errata #2815 at PC=%08lx for pid %d (opcode=%08lx)\n",
-					pc, current->pid, opcode);
-#endif
-				/* Now just return to restart the instruction,
-				   because the branch to the instruction will
-				   now be from a SHmedia RTE the silicon defect
-				   won't be triggered. */
-				return;
+			combined = (major << 4) | minor;
+			index = major;
+			shift = minor << 1;
+			if (reserved_field == 0) {
+				int opcode_state = (shmedia_opcode_table[index] >> shift) & 0x3;
+				switch (opcode_state) {
+					case OPCODE_INVALID:
+						/* Trap. */
+						break;
+					case OPCODE_USER_VALID:
+						/* Restart the instruction : the branch to the instruction will now be from an RTE
+						   not from SHcompact so the silicon defect won't be triggered. */
+						return;
+					case OPCODE_PRIV_VALID:
+						if (!user_mode(regs)) {
+							/* Should only ever get here if a module has
+							   SHcompact code inside it.  If so, the same fix up is needed. */
+							return; /* same reason */
+						}
+						/* Otherwise, user mode trying to execute a privileged instruction - 
+						   fall through to trap. */
+						break;
+					case OPCODE_CTRL_REG:
+						/* If in privileged mode, return as above. */
+						if (!user_mode(regs)) return; 
+						/* In user mode ... */
+						if (combined == 0x9f) { /* GETCON */
+							unsigned long regno = (opcode >> 20) & 0x3f;
+							if (regno >= 62) {
+								return;
+							}
+							/* Otherwise, reserved or privileged control register, => trap */
+						} else if (combined == 0x1bf) { /* PUTCON */
+							unsigned long regno = (opcode >> 4) & 0x3f;
+							if (regno >= 62) {
+								return;
+							}
+							/* Otherwise, reserved or privileged control register, => trap */
+						} else {
+							/* Trap */
+						}
+						break;
+					default:
+						/* Fall through to trap. */
+						break;
+				}
 			}
 			/* fall through to normal resinst processing */
 		} else {
@@ -161,7 +202,7 @@
 			signr = SIGSEGV;
 		}
 	}
-	
+
 	show_excp_regs("do_reserved_inst", trapnr, signr, regs);
 	current->thread.error_code = error_code;
 	current->thread.trap_no = trapnr;
diff -urN linux-2.4.22-pre3/arch/sh64/mach-cayman/irq.c linux-2.4.22-pre4/arch/sh64/mach-cayman/irq.c
--- linux-2.4.22-pre3/arch/sh64/mach-cayman/irq.c	2003-07-09 16:56:24.000000000 -0700
+++ linux-2.4.22-pre4/arch/sh64/mach-cayman/irq.c	2003-07-09 16:56:28.000000000 -0700
@@ -162,7 +162,7 @@
     return intc_irq_describe(p, irq);
   else if (irq < NR_INTC_IRQS + 8)
     return sprintf(p, "(SMSC %d)", irq - NR_INTC_IRQS);
-  else if ((irq > NR_INTC_IRQS + 24) && (irq < NR_INTC_IRQS + 32))
+  else if ((irq >= NR_INTC_IRQS + 24) && (irq < NR_INTC_IRQS + 32))
     return sprintf(p, "(PCI2 %d)", irq - (NR_INTC_IRQS + 24)); 
   else
     return 0;
diff -urN linux-2.4.22-pre3/arch/sparc/kernel/sys_sparc.c linux-2.4.22-pre4/arch/sparc/kernel/sys_sparc.c
--- linux-2.4.22-pre3/arch/sparc/kernel/sys_sparc.c	2003-06-13 07:51:32.000000000 -0700
+++ linux-2.4.22-pre4/arch/sparc/kernel/sys_sparc.c	2003-07-09 16:56:28.000000000 -0700
@@ -123,7 +123,10 @@
 	if (call <= SEMCTL)
 		switch (call) {
 		case SEMOP:
-			err = sys_semop (first, (struct sembuf *)ptr, second);
+			err = sys_semtimedop (first, (struct sembuf *)ptr, second, NULL);
+			goto out;
+		case SEMTIMEDOP:
+			err = sys_semtimedop (first, (struct sembuf *)ptr, second, (const struct timespec *) fifth);
 			goto out;
 		case SEMGET:
 			err = sys_semget (first, second, third);
diff -urN linux-2.4.22-pre3/arch/sparc64/kernel/sys_sparc.c linux-2.4.22-pre4/arch/sparc64/kernel/sys_sparc.c
--- linux-2.4.22-pre3/arch/sparc64/kernel/sys_sparc.c	2003-07-09 16:56:24.000000000 -0700
+++ linux-2.4.22-pre4/arch/sparc64/kernel/sys_sparc.c	2003-07-09 16:56:28.000000000 -0700
@@ -182,7 +182,10 @@
 	if (call <= SEMCTL)
 		switch (call) {
 		case SEMOP:
-			err = sys_semop (first, (struct sembuf *)ptr, second);
+			err = sys_semtimedop (first, (struct sembuf *)ptr, second, NULL);
+			goto out;
+		case SEMTIMEDOP:
+			err = sys_semtimedop (first, (struct sembuf *)ptr, second, (const struct timespec *) fifth);
 			goto out;
 		case SEMGET:
 			err = sys_semget (first, second, (int)third);
diff -urN linux-2.4.22-pre3/arch/sparc64/kernel/sys_sparc32.c linux-2.4.22-pre4/arch/sparc64/kernel/sys_sparc32.c
--- linux-2.4.22-pre3/arch/sparc64/kernel/sys_sparc32.c	2003-07-09 16:56:24.000000000 -0700
+++ linux-2.4.22-pre4/arch/sparc64/kernel/sys_sparc32.c	2003-07-09 16:56:28.000000000 -0700
@@ -32,6 +32,7 @@
 #include <linux/smb_mount.h>
 #include <linux/ncp_fs.h>
 #include <linux/quota.h>
+#include <linux/quotacompat.h>
 #include <linux/module.h>
 #include <linux/sunrpc/svc.h>
 #include <linux/nfsd/nfsd.h>
@@ -762,6 +763,38 @@
 	return err;
 }
 
+static __inline__ void *alloc_user_space(long len)
+{
+	struct pt_regs *regs = current->thread.kregs;
+	unsigned long usp = regs->u_regs[UREG_I6];
+
+	if (!(current->thread.flags & SPARC_FLAG_32BIT))
+		usp += STACK_BIAS;
+
+	return (void *) (usp - len);
+}
+
+struct timespec32 {
+	s32    tv_sec;
+	s32    tv_nsec;
+};
+                
+static int sys32_semtimedop(int semid, struct sembuf *tsems, int nsems,
+			    const struct timespec32 *timeout32)
+{
+	struct timespec32 t32;
+	struct timespec *t64 = alloc_user_space(sizeof(*t64));
+
+	if (copy_from_user(&t32, timeout32, sizeof(t32)))
+		return -EFAULT;
+
+	if (put_user(t32.tv_sec, &t64->tv_sec) ||
+	    put_user(t32.tv_nsec, &t64->tv_nsec))
+		return -EFAULT;
+
+	return sys_semtimedop(semid, tsems, nsems, t64);
+}
+
 asmlinkage int sys32_ipc (u32 call, int first, int second, int third, u32 ptr, u32 fifth)
 {
 	int version, err;
@@ -773,8 +806,10 @@
 		switch (call) {
 		case SEMOP:
 			/* struct sembuf is the same on 32 and 64bit :)) */
-			err = sys_semop (first, (struct sembuf *)AA(ptr), second);
+			err = sys_semtimedop (first, (struct sembuf *)AA(ptr), second, NULL);
 			goto out;
+		case SEMTIMEDOP:
+			err = sys32_semtimedop (first, (struct sembuf *)AA(ptr), second, (const struct timespec32 *) AA(fifth));
 		case SEMGET:
 			err = sys_semget (first, second, third);
 			goto out;
@@ -889,7 +924,7 @@
 	return sys32_fcntl(fd, cmd, arg);
 }
 
-struct dqblk32 {
+struct user_dqblk32 {
     __u32 dqb_bhardlimit;
     __u32 dqb_bsoftlimit;
     __u32 dqb_curblocks;
@@ -902,44 +937,42 @@
                                 
 extern asmlinkage int sys_quotactl(int cmd, const char *special, int id, caddr_t addr);
 
-asmlinkage int sys32_quotactl(int cmd, const char *special, int id, unsigned long addr)
+asmlinkage int sys32_quotactl(int cmd, const char *special, int id, caddr_t addr)
 {
 	int cmds = cmd >> SUBCMDSHIFT;
 	int err;
-	struct dqblk d;
+	struct v1c_mem_dqblk d;
 	mm_segment_t old_fs;
 	char *spec;
 	
 	switch (cmds) {
-	case Q_GETQUOTA:
+	case Q_V1_GETQUOTA:
 		break;
-	case Q_SETQUOTA:
-	case Q_SETUSE:
-	case Q_SETQLIM:
-		if (copy_from_user (&d, (struct dqblk32 *)addr,
-				    sizeof (struct dqblk32)))
+	case Q_V1_SETQUOTA:
+	case Q_V1_SETUSE:
+	case Q_V1_SETQLIM:
+		if (copy_from_user(&d, addr, sizeof(struct user_dqblk32)))
 			return -EFAULT;
-		d.dqb_itime = ((struct dqblk32 *)&d)->dqb_itime;
-		d.dqb_btime = ((struct dqblk32 *)&d)->dqb_btime;
+		d.dqb_itime = ((struct user_dqblk32 *)&d)->dqb_itime;
+		d.dqb_btime = ((struct user_dqblk32 *)&d)->dqb_btime;
 		break;
 	default:
-		return sys_quotactl(cmd, special,
-				    id, (caddr_t)addr);
+		return sys_quotactl(cmd, special, id, addr);
 	}
-	spec = getname (special);
+	spec = getname(special);
 	err = PTR_ERR(spec);
-	if (IS_ERR(spec)) return err;
-	old_fs = get_fs ();
+	if (IS_ERR(spec))
+		return err;
+	old_fs = get_fs();
 	set_fs (KERNEL_DS);
 	err = sys_quotactl(cmd, (const char *)spec, id, (caddr_t)&d);
 	set_fs (old_fs);
 	putname (spec);
-	if (cmds == Q_GETQUOTA) {
+	if (cmds == Q_V1_GETQUOTA) {
 		__kernel_time_t b = d.dqb_btime, i = d.dqb_itime;
-		((struct dqblk32 *)&d)->dqb_itime = i;
-		((struct dqblk32 *)&d)->dqb_btime = b;
-		if (copy_to_user ((struct dqblk32 *)addr, &d,
-				  sizeof (struct dqblk32)))
+		((struct user_dqblk32 *)&d)->dqb_itime = i;
+		((struct user_dqblk32 *)&d)->dqb_btime = b;
+		if (copy_to_user(addr, &d, sizeof(struct user_dqblk32)))
 			return -EFAULT;
 	}
 	return err;
@@ -1947,11 +1980,6 @@
 	return ret;
 }
 
-struct timespec32 {
-	s32    tv_sec;
-	s32    tv_nsec;
-};
-                
 extern asmlinkage int sys_sched_rr_get_interval(pid_t pid, struct timespec *interval);
 
 asmlinkage int sys32_sched_rr_get_interval(__kernel_pid_t32 pid, struct timespec32 *interval)
diff -urN linux-2.4.22-pre3/drivers/acpi/Config.in linux-2.4.22-pre4/drivers/acpi/Config.in
--- linux-2.4.22-pre3/drivers/acpi/Config.in	2003-07-09 16:56:24.000000000 -0700
+++ linux-2.4.22-pre4/drivers/acpi/Config.in	2003-07-09 16:56:28.000000000 -0700
@@ -37,6 +37,7 @@
       tristate     '  ASUS Laptop Extras'  CONFIG_ACPI_ASUS
       tristate     '  Toshiba Laptop Extras'	CONFIG_ACPI_TOSHIBA
       bool         '  Debug Statements'	CONFIG_ACPI_DEBUG
+      bool         '  Relaxed AML Checking'	CONFIG_ACPI_RELAXED_AML
     fi
   fi
 
diff -urN linux-2.4.22-pre3/drivers/acpi/executer/exfldio.c linux-2.4.22-pre4/drivers/acpi/executer/exfldio.c
--- linux-2.4.22-pre3/drivers/acpi/executer/exfldio.c	2003-07-09 16:56:24.000000000 -0700
+++ linux-2.4.22-pre4/drivers/acpi/executer/exfldio.c	2003-07-09 16:56:28.000000000 -0700
@@ -139,7 +139,41 @@
 			field_datum_byte_offset, obj_desc->common_field.access_byte_width,
 			rgn_desc->region.node->name.ascii, rgn_desc->region.length));
 
-		return_ACPI_STATUS (AE_AML_REGION_LIMIT);
+		#ifdef CONFIG_ACPI_RELAXED_AML
+		{
+			/*
+			 * Allow access to the field if it is within the region size
+			 * rounded up to a multiple of the access byte width.  This
+			 * overcomes "off-by-one" programming errors in the AML often
+			 * found in Toshiba laptops.  These errors were allowed by
+			 * the Microsoft ASL compiler.
+			 */
+			u32 rounded_length = ACPI_ROUND_UP(rgn_desc->region.length,
+									obj_desc->common_field.access_byte_width);
+
+			if (rounded_length < (obj_desc->common_field.base_byte_offset
+						+ field_datum_byte_offset
+						+ obj_desc->common_field.access_byte_width)) {
+				return_ACPI_STATUS (AE_AML_REGION_LIMIT);
+			} else {
+				static int	warn_once = 1;
+				if (warn_once) {
+					// Could also associate a flag with each field, and
+					// warn once for each field.
+					ACPI_REPORT_WARNING((
+						"The ACPI AML in your computer contains errors, "
+						"please nag the manufacturer to correct it.\n"));
+					ACPI_REPORT_WARNING((
+						"Allowing relaxed access to fields; "
+						"turn on CONFIG_ACPI_DEBUG for details.\n"));
+					warn_once = 0;
+				}
+				return_ACPI_STATUS (AE_OK);
+			}
+		}
+		#else
+			return_ACPI_STATUS (AE_AML_REGION_LIMIT);
+		#endif
 	}
 
 	return_ACPI_STATUS (AE_OK);
diff -urN linux-2.4.22-pre3/drivers/char/Config.in linux-2.4.22-pre4/drivers/char/Config.in
--- linux-2.4.22-pre3/drivers/char/Config.in	2003-07-09 16:56:24.000000000 -0700
+++ linux-2.4.22-pre4/drivers/char/Config.in	2003-07-09 16:56:28.000000000 -0700
@@ -309,6 +309,9 @@
    bool '  Generic SiS support' CONFIG_AGP_SIS
    bool '  ALI chipset support' CONFIG_AGP_ALI
    bool '  Serverworks LE/HE support' CONFIG_AGP_SWORKS
+   if [ "$CONFIG_X86" = "y" ]; then
+      bool '  NVIDIA chipset support' CONFIG_AGP_NVIDIA
+   fi
    if [ "$CONFIG_IA64" = "y" ]; then
       bool '  HP ZX1 AGP support' CONFIG_AGP_HP_ZX1
    fi
diff -urN linux-2.4.22-pre3/drivers/char/agp/agp.h linux-2.4.22-pre4/drivers/char/agp/agp.h
--- linux-2.4.22-pre3/drivers/char/agp/agp.h	2003-06-13 07:51:32.000000000 -0700
+++ linux-2.4.22-pre4/drivers/char/agp/agp.h	2003-07-09 16:56:28.000000000 -0700
@@ -166,6 +166,9 @@
 #ifndef PCI_DEVICE_ID_VIA_8363_0
 #define PCI_DEVICE_ID_VIA_8363_0      0x0305
 #endif
+#ifndef PCI_DEVICE_ID_VIA_8601_0
+#define PCI_DEVICE_ID_VIA_8601_0      0x0601
+#endif
 #ifndef PCI_DEVICE_ID_VIA_82C694X_0
 #define PCI_DEVICE_ID_VIA_82C694X_0      0x0605
 #endif
@@ -184,6 +187,24 @@
 #ifndef PCI_DEVICE_ID_INTEL_830_M_1
 #define PCI_DEVICE_ID_INTEL_830_M_1     0x3577
 #endif
+#ifndef PCI_DEVICE_ID_INTEL_855_GM_0
+#define PCI_DEVICE_ID_INTEL_855_GM_0	0x3580
+#endif
+#ifndef PCI_DEVICE_ID_INTEL_855_GM_1
+#define PCI_DEVICE_ID_INTEL_855_GM_1	0x3582
+#endif
+#ifndef PCI_DEVICE_ID_INTEL_855_PM_0
+#define PCI_DEVICE_ID_INTEL_855_PM_0	0x3340
+#endif
+#ifndef PCI_DEVICE_ID_INTEL_855_PM_1
+#define PCI_DEVICE_ID_INTEL_855_PM_1	0x3342
+#endif
+#ifndef PCI_DEVICE_ID_INTEL_865_G_0
+#define PCI_DEVICE_ID_INTEL_865_G_0	0x2570
+#endif
+#ifndef PCI_DEVICE_ID_INTEL_865_G_1
+#define PCI_DEVICE_ID_INTEL_865_G_1	0x2572
+#endif
 #ifndef PCI_DEVICE_ID_INTEL_820_0
 #define PCI_DEVICE_ID_INTEL_820_0       0x2500
 #endif
@@ -280,6 +301,28 @@
 #define INTEL_NBXCFG    0x50
 #define INTEL_ERRSTS    0x91
 
+/* Intel 855GM/852GM registers */
+#define I855_GMCH_CTRL             0x52
+#define I855_GMCH_ENABLED          0x4
+#define I855_GMCH_GMS_MASK         (0x7 << 4)
+#define I855_GMCH_GMS_STOLEN_0M    0x0
+#define I855_GMCH_GMS_STOLEN_1M    (0x1 << 4)
+#define I855_GMCH_GMS_STOLEN_4M    (0x2 << 4)
+#define I855_GMCH_GMS_STOLEN_8M    (0x3 << 4)
+#define I855_GMCH_GMS_STOLEN_16M   (0x4 << 4)
+#define I855_GMCH_GMS_STOLEN_32M   (0x5 << 4)
+#define I85X_CAPID                 0x44
+#define I85X_VARIANT_MASK          0x7
+#define I85X_VARIANT_SHIFT         5
+#define I855_GME                   0x0
+#define I855_GM                    0x4
+#define I852_GME                   0x2
+#define I852_GM                    0x5
+#define I855_PME                   0x0
+#define I855_PM                    0x4
+#define I852_PME                   0x2
+#define I852_PM                    0x5
+
 /* intel i830 registers */
 #define I830_GMCH_CTRL             0x52
 #define I830_GMCH_ENABLED          0x4
@@ -412,6 +455,17 @@
 #define SVWRKS_POSTFLUSH  0x14
 #define SVWRKS_DIRFLUSH   0x0c
 
+/* NVIDIA registers */
+#define NVIDIA_0_APBASE     0x10
+#define NVIDIA_0_APSIZE     0x80
+#define NVIDIA_1_WBC        0xf0
+#define NVIDIA_2_GARTCTRL   0xd0
+#define NVIDIA_2_APBASE     0xd8
+#define NVIDIA_2_APLIMIT    0xdc
+#define NVIDIA_2_ATTBASE(i) (0xe0 + (i) * 4)
+#define NVIDIA_3_APBASE     0x50
+#define NVIDIA_3_APLIMIT    0x54
+
 /* HP ZX1 SBA registers */
 #define HP_ZX1_CTRL		0x200
 #define HP_ZX1_IBASE		0x300
diff -urN linux-2.4.22-pre3/drivers/char/agp/agpgart_be.c linux-2.4.22-pre4/drivers/char/agp/agpgart_be.c
--- linux-2.4.22-pre3/drivers/char/agp/agpgart_be.c	2003-06-13 07:51:32.000000000 -0700
+++ linux-2.4.22-pre4/drivers/char/agp/agpgart_be.c	2003-07-09 16:56:28.000000000 -0700
@@ -23,6 +23,12 @@
  * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  *
  */
+
+/*
+ * Intel(R) 855GM/852GM and 865G support, added by
+ * David Dawes <dawes@tungstengraphics.com>.
+ */
+
 #include <linux/config.h>
 #include <linux/version.h>
 #include <linux/module.h>
@@ -43,6 +49,9 @@
 #include <asm/uaccess.h>
 #include <asm/io.h>
 #include <asm/page.h>
+#ifdef CONFIG_AGP_NVIDIA
+    #include <asm/msr.h>
+#endif
 
 #include <linux/agp_backend.h>
 #include "agp.h"
@@ -577,7 +586,7 @@
 	for (page = virt_to_page(table); page <= virt_to_page(table_end); page++)
 		SetPageReserved(page);
 
-	agp_bridge.gatt_table_real = (unsigned long *) table;
+	agp_bridge.gatt_table_real = (u32 *) table;
 	agp_gatt_table = (void *)table;
 #ifdef CONFIG_X86
 	err = change_page_attr(virt_to_page(table), 1<<page_order, PAGE_KERNEL_NOCACHE);
@@ -1112,34 +1121,64 @@
 	u16 gmch_ctrl;
 	int gtt_entries;
 	u8 rdct;
+	int local = 0;
 	static const int ddt[4] = { 0, 16, 32, 64 };
 
 	pci_read_config_word(agp_bridge.dev,I830_GMCH_CTRL,&gmch_ctrl);
 
-	switch (gmch_ctrl & I830_GMCH_GMS_MASK) {
-	case I830_GMCH_GMS_STOLEN_512:
-		gtt_entries = KB(512) - KB(132);
-		printk(KERN_INFO PFX "detected %dK stolen memory.\n",gtt_entries / KB(1));
-		break;
-	case I830_GMCH_GMS_STOLEN_1024:
-		gtt_entries = MB(1) - KB(132);
-		printk(KERN_INFO PFX "detected %dK stolen memory.\n",gtt_entries / KB(1));
-		break;
-	case I830_GMCH_GMS_STOLEN_8192:
-		gtt_entries = MB(8) - KB(132);
-		printk(KERN_INFO PFX "detected %dK stolen memory.\n",gtt_entries / KB(1));
-		break;
-	case I830_GMCH_GMS_LOCAL:
-		rdct = INREG8(intel_i830_private.registers,I830_RDRAM_CHANNEL_TYPE);
-		gtt_entries = (I830_RDRAM_ND(rdct) + 1) * MB(ddt[I830_RDRAM_DDT(rdct)]);
-		printk(KERN_INFO PFX "detected %dK local memory.\n",gtt_entries / KB(1));
-		break;
-	default:
-		printk(KERN_INFO PFX "no video memory detected.\n");
-		gtt_entries = 0;
-		break;
+	if (agp_bridge.dev->device != PCI_DEVICE_ID_INTEL_830_M_0 &&
+	    agp_bridge.dev->device != PCI_DEVICE_ID_INTEL_845_G_0) {
+		switch (gmch_ctrl & I855_GMCH_GMS_MASK) {
+		case I855_GMCH_GMS_STOLEN_1M:
+			gtt_entries = MB(1) - KB(132);
+			break;
+		case I855_GMCH_GMS_STOLEN_4M:
+			gtt_entries = MB(4) - KB(132);
+			break;
+		case I855_GMCH_GMS_STOLEN_8M:
+			gtt_entries = MB(8) - KB(132);
+			break;
+		case I855_GMCH_GMS_STOLEN_16M:
+			gtt_entries = MB(16) - KB(132);
+			break;
+		case I855_GMCH_GMS_STOLEN_32M:
+			gtt_entries = MB(32) - KB(132);
+			break;
+		default:
+			gtt_entries = 0;
+			break;
+		}
+	} else
+	{
+		switch (gmch_ctrl & I830_GMCH_GMS_MASK) {
+		case I830_GMCH_GMS_STOLEN_512:
+			gtt_entries = KB(512) - KB(132);
+			break;
+		case I830_GMCH_GMS_STOLEN_1024:
+			gtt_entries = MB(1) - KB(132);
+			break;
+		case I830_GMCH_GMS_STOLEN_8192:
+			gtt_entries = MB(8) - KB(132);
+			break;
+		case I830_GMCH_GMS_LOCAL:
+			rdct = INREG8(intel_i830_private.registers,
+				      I830_RDRAM_CHANNEL_TYPE);
+			gtt_entries = (I830_RDRAM_ND(rdct) + 1) *
+				      MB(ddt[I830_RDRAM_DDT(rdct)]);
+			local = 1;
+			break;
+		default:
+			gtt_entries = 0;
+			break;
+		}
 	}
 
+	if (gtt_entries > 0)
+		printk(KERN_INFO PFX "Detected %dK %s memory.\n",
+		       gtt_entries / KB(1), local ? "local" : "stolen");
+	else
+		printk(KERN_INFO PFX
+		       "No pre-allocated video memory detected.\n");
 	gtt_entries /= KB(4);
 
 	intel_i830_private.gtt_entries = gtt_entries;
@@ -1192,9 +1231,16 @@
 	u16 gmch_ctrl;
 	aper_size_info_fixed *values;
 
-	pci_read_config_word(agp_bridge.dev,I830_GMCH_CTRL,&gmch_ctrl);
 	values = A_SIZE_FIX(agp_bridge.aperture_sizes);
 
+	if (agp_bridge.dev->device != PCI_DEVICE_ID_INTEL_830_M_0 &&
+	    agp_bridge.dev->device != PCI_DEVICE_ID_INTEL_845_G_0) {
+		agp_bridge.previous_size = agp_bridge.current_size = (void *) values;
+		agp_bridge.aperture_size_idx = 0;
+		return(values[0].size);
+	}
+		
+	pci_read_config_word(agp_bridge.dev,I830_GMCH_CTRL,&gmch_ctrl);
 	if ((gmch_ctrl & I830_GMCH_MEM_MASK) == I830_GMCH_MEM_128M) {
 		agp_bridge.previous_size = agp_bridge.current_size = (void *) values;
 		agp_bridge.aperture_size_idx = 0;
@@ -2450,8 +2496,8 @@
 		return retval;
 	}
 
-	agp_bridge.gatt_table_real = page_dir.real;
-	agp_bridge.gatt_table = page_dir.remapped;
+	agp_bridge.gatt_table_real = (u32 *)page_dir.real;
+	agp_bridge.gatt_table = (u32 *)page_dir.remapped;
 	agp_bridge.gatt_bus_addr = virt_to_bus(page_dir.real);
 
 	/* Get the address for the gart region.
@@ -2477,8 +2523,8 @@
 {
 	amd_page_map page_dir;
    
-	page_dir.real = agp_bridge.gatt_table_real;
-	page_dir.remapped = agp_bridge.gatt_table;
+	page_dir.real = (unsigned long *)agp_bridge.gatt_table_real;
+	page_dir.remapped = (unsigned long *)agp_bridge.gatt_table;
 
 	amd_free_gatt_pages();
 	amd_free_page_map(&page_dir);
@@ -3559,8 +3605,8 @@
 		return retval;
 	}
 
-	agp_bridge.gatt_table_real = page_dir.real;
-	agp_bridge.gatt_table = page_dir.remapped;
+	agp_bridge.gatt_table_real = (u32 *)page_dir.real;
+	agp_bridge.gatt_table = (u32 *)page_dir.remapped;
 	agp_bridge.gatt_bus_addr = virt_to_bus(page_dir.real);
 
 	/* Get the address for the gart region.
@@ -3588,8 +3634,8 @@
 {
 	serverworks_page_map page_dir;
    
-	page_dir.real = agp_bridge.gatt_table_real;
-	page_dir.remapped = agp_bridge.gatt_table;
+	page_dir.real = (unsigned long *)agp_bridge.gatt_table_real;
+	page_dir.remapped = (unsigned long *)agp_bridge.gatt_table;
 
 	serverworks_free_gatt_pages();
 	serverworks_free_page_map(&page_dir);
@@ -4003,6 +4049,330 @@
 
 #endif /* CONFIG_AGP_SWORKS */
 
+#ifdef CONFIG_AGP_NVIDIA
+
+static struct _nvidia_private {
+	struct pci_dev *dev_1;
+	struct pci_dev *dev_2;
+	struct pci_dev *dev_3;
+	volatile u32 *aperture;
+	int num_active_entries;
+	off_t pg_offset;
+} nvidia_private;
+
+static int nvidia_fetch_size(void)
+{
+	int i;
+	u8 size_value;
+	aper_size_info_8 *values;
+
+	pci_read_config_byte(agp_bridge.dev, NVIDIA_0_APSIZE, &size_value);
+	size_value &= 0x0f;
+	values = A_SIZE_8(agp_bridge.aperture_sizes);
+
+	for (i = 0; i < agp_bridge.num_aperture_sizes; i++) {
+		if (size_value == values[i].size_value) {
+			agp_bridge.previous_size =
+				agp_bridge.current_size = (void *) (values + i);
+			agp_bridge.aperture_size_idx = i;
+			return values[i].size;
+		}
+	}
+
+	return 0;
+}
+
+#define SYSCFG          0xC0010010
+#define IORR_BASE0      0xC0010016
+#define IORR_MASK0      0xC0010017
+#define AMD_K7_NUM_IORR 2
+
+static int nvidia_init_iorr(u32 base, u32 size)
+{
+	u32 base_hi, base_lo;
+	u32 mask_hi, mask_lo;
+	u32 sys_hi, sys_lo;
+	u32 iorr_addr, free_iorr_addr;
+
+	/* Find the iorr that is already used for the base */
+	/* If not found, determine the uppermost available iorr */
+	free_iorr_addr = AMD_K7_NUM_IORR;
+	for(iorr_addr = 0; iorr_addr < AMD_K7_NUM_IORR; iorr_addr++) {
+		rdmsr(IORR_BASE0 + 2 * iorr_addr, base_lo, base_hi);
+		rdmsr(IORR_MASK0 + 2 * iorr_addr, mask_lo, mask_hi);
+
+		if ((base_lo & 0xfffff000) == (base & 0xfffff000))
+			break;
+
+		if ((mask_lo & 0x00000800) == 0)
+			free_iorr_addr = iorr_addr;
+	}
+	
+	if (iorr_addr >= AMD_K7_NUM_IORR) {
+		iorr_addr = free_iorr_addr;
+		if (iorr_addr >= AMD_K7_NUM_IORR)
+			return -EINVAL;
+	}
+
+    base_hi = 0x0;
+    base_lo = (base & ~0xfff) | 0x18;
+    mask_hi = 0xf;
+    mask_lo = ((~(size - 1)) & 0xfffff000) | 0x800;
+    wrmsr(IORR_BASE0 + 2 * iorr_addr, base_lo, base_hi);
+    wrmsr(IORR_MASK0 + 2 * iorr_addr, mask_lo, mask_hi);
+
+    rdmsr(SYSCFG, sys_lo, sys_hi);
+    sys_lo |= 0x00100000;
+    wrmsr(SYSCFG, sys_lo, sys_hi);
+
+	return 0;
+}
+
+static int nvidia_configure(void)
+{
+	int i, rc, num_dirs;
+	u32 apbase, aplimit;
+	aper_size_info_8 *current_size;
+	u32 temp;
+
+	current_size = A_SIZE_8(agp_bridge.current_size);
+
+	/* aperture size */
+	pci_write_config_byte(agp_bridge.dev, NVIDIA_0_APSIZE,
+		current_size->size_value);
+
+    /* address to map to */
+	pci_read_config_dword(agp_bridge.dev, NVIDIA_0_APBASE, &apbase);
+	apbase &= PCI_BASE_ADDRESS_MEM_MASK;
+	agp_bridge.gart_bus_addr = apbase;
+	aplimit = apbase + (current_size->size * 1024 * 1024) - 1;
+	pci_write_config_dword(nvidia_private.dev_2, NVIDIA_2_APBASE, apbase);
+	pci_write_config_dword(nvidia_private.dev_2, NVIDIA_2_APLIMIT, aplimit);
+	pci_write_config_dword(nvidia_private.dev_3, NVIDIA_3_APBASE, apbase);
+	pci_write_config_dword(nvidia_private.dev_3, NVIDIA_3_APLIMIT, aplimit);
+	if (0 != (rc = nvidia_init_iorr(apbase, current_size->size * 1024 * 1024)))
+		return rc;
+
+	/* directory size is 64k */
+	num_dirs = current_size->size / 64;
+	nvidia_private.num_active_entries = current_size->num_entries;
+	nvidia_private.pg_offset = 0;
+	if (num_dirs == 0) {
+		num_dirs = 1;
+		nvidia_private.num_active_entries /= (64 / current_size->size);
+		nvidia_private.pg_offset = (apbase & (64 * 1024 * 1024 - 1) &
+			~(current_size->size * 1024 * 1024 - 1)) / PAGE_SIZE;
+	}
+
+	/* attbase */
+	for(i = 0; i < 8; i++) {
+		pci_write_config_dword(nvidia_private.dev_2, NVIDIA_2_ATTBASE(i),
+			(agp_bridge.gatt_bus_addr + (i % num_dirs) * 64 * 1024) | 1);
+	}
+
+	/* gtlb control */
+	pci_read_config_dword(nvidia_private.dev_2, NVIDIA_2_GARTCTRL, &temp);
+	pci_write_config_dword(nvidia_private.dev_2, NVIDIA_2_GARTCTRL,
+		temp | 0x11);
+
+	/* gart control */
+	pci_read_config_dword(agp_bridge.dev, NVIDIA_0_APSIZE, &temp);
+	pci_write_config_dword(agp_bridge.dev, NVIDIA_0_APSIZE,
+		temp | 0x100);
+
+	/* map aperture */
+	nvidia_private.aperture =
+		(volatile u32 *) ioremap(apbase, 33 * PAGE_SIZE);
+
+	return 0;
+}
+
+static void nvidia_cleanup(void)
+{
+	aper_size_info_8 *previous_size;
+	u32 temp;
+
+	/* gart control */
+	pci_read_config_dword(agp_bridge.dev, NVIDIA_0_APSIZE, &temp);
+	pci_write_config_dword(agp_bridge.dev, NVIDIA_0_APSIZE,
+		temp & ~(0x100));
+
+	/* gtlb control */
+	pci_read_config_dword(nvidia_private.dev_2, NVIDIA_2_GARTCTRL, &temp);
+	pci_write_config_dword(nvidia_private.dev_2, NVIDIA_2_GARTCTRL,
+		temp & ~(0x11));
+
+	/* unmap aperture */
+	iounmap((void *) nvidia_private.aperture);
+
+	/* restore previous aperture size */
+	previous_size = A_SIZE_8(agp_bridge.previous_size);
+	pci_write_config_byte(agp_bridge.dev, NVIDIA_0_APSIZE,
+		previous_size->size_value);
+
+	/* restore iorr for previous aperture size */
+	nvidia_init_iorr(agp_bridge.gart_bus_addr,
+		previous_size->size * 1024 * 1024);
+}
+
+static void nvidia_tlbflush(agp_memory * mem)
+{
+	int i;
+	unsigned long end;
+	u32 wbc_reg, wbc_mask, temp;
+
+	/* flush chipset */
+	switch(agp_bridge.type) {
+	case NVIDIA_NFORCE:
+		wbc_mask = 0x00010000;
+		break;
+	case NVIDIA_NFORCE2:
+		wbc_mask = 0x80000000;
+		break;
+	default:
+		wbc_mask = 0;
+		break;
+	}
+
+	if (wbc_mask) {
+		pci_read_config_dword(nvidia_private.dev_1, NVIDIA_1_WBC, &wbc_reg);
+		wbc_reg |= wbc_mask;
+		pci_write_config_dword(nvidia_private.dev_1, NVIDIA_1_WBC, wbc_reg);
+
+		end = jiffies + 3*HZ;
+		do {
+			pci_read_config_dword(nvidia_private.dev_1, NVIDIA_1_WBC, &wbc_reg);
+			if ((signed)(end - jiffies) <= 0) {
+				printk(KERN_ERR "TLB flush took more than 3 seconds.\n");
+			}
+		} while (wbc_reg & wbc_mask);
+	}
+
+	/* flush TLB entries */
+	for(i = 0; i < 32 + 1; i++)
+		temp = nvidia_private.aperture[i * PAGE_SIZE / sizeof(u32)];
+	for(i = 0; i < 32 + 1; i++)
+		temp = nvidia_private.aperture[i * PAGE_SIZE / sizeof(u32)];
+}
+
+static unsigned long nvidia_mask_memory(unsigned long addr, int type)
+{
+	/* Memory type is ignored */
+
+	return addr | agp_bridge.masks[0].mask;
+}
+
+static int nvidia_insert_memory(agp_memory * mem,
+				     off_t pg_start, int type)
+{
+	int i, j;
+	
+	if ((type != 0) || (mem->type != 0))
+		return -EINVAL;
+	
+	if ((pg_start + mem->page_count) >
+		(nvidia_private.num_active_entries - agp_memory_reserved/PAGE_SIZE))
+		return -EINVAL;
+	
+	for(j = pg_start; j < (pg_start + mem->page_count); j++) {
+		if (!PGE_EMPTY(agp_bridge.gatt_table[nvidia_private.pg_offset + j])) {
+			return -EBUSY;
+		}
+	}
+
+	if (mem->is_flushed == FALSE) {
+		CACHE_FLUSH();
+		mem->is_flushed = TRUE;
+	}
+	for (i = 0, j = pg_start; i < mem->page_count; i++, j++) {
+		agp_bridge.gatt_table[nvidia_private.pg_offset + j] = agp_bridge.mask_memory(mem->memory[i], mem->type);
+	}
+
+	agp_bridge.tlb_flush(mem);
+	return 0;
+}
+
+static int nvidia_remove_memory(agp_memory * mem, off_t pg_start,
+				     int type)
+{
+	int i;
+
+	if ((type != 0) || (mem->type != 0))
+		return -EINVAL;
+	
+	for (i = pg_start; i < (mem->page_count + pg_start); i++) {
+		agp_bridge.gatt_table[nvidia_private.pg_offset + i] =
+		    (unsigned long) agp_bridge.scratch_page;
+	}
+
+	agp_bridge.tlb_flush(mem);
+	return 0;
+}
+
+static aper_size_info_8 nvidia_generic_sizes[5] =
+{
+	{512, 131072, 7, 0},
+	{256, 65536, 6, 8},
+	{128, 32768, 5, 12},
+	{64, 16384, 4, 14},
+	/* The 32M mode still requires a 64k gatt */
+	{32, 16384, 4, 15}
+};
+
+static gatt_mask nvidia_generic_masks[] =
+{
+	{0x00000001, 0}
+};
+
+static int __init nvidia_generic_setup (struct pci_dev *pdev)
+{
+	nvidia_private.dev_1 =
+		pci_find_slot((unsigned int)pdev->bus->number, PCI_DEVFN(0, 1));
+	nvidia_private.dev_2 =
+		pci_find_slot((unsigned int)pdev->bus->number, PCI_DEVFN(0, 2));
+	nvidia_private.dev_3 =
+		pci_find_slot((unsigned int)pdev->bus->number, PCI_DEVFN(30, 0));
+	
+	if((nvidia_private.dev_1 == NULL) ||
+		(nvidia_private.dev_2 == NULL) ||
+		(nvidia_private.dev_3 == NULL)) {
+		printk(KERN_INFO PFX "agpgart: Detected an NVIDIA "
+			"nForce/nForce2 chipset, but could not find "
+			"the secondary devices.\n");
+		agp_bridge.type = NOT_SUPPORTED;
+		return -ENODEV;
+	}
+
+	agp_bridge.masks = nvidia_generic_masks;
+	agp_bridge.aperture_sizes = (void *) nvidia_generic_sizes;
+	agp_bridge.size_type = U8_APER_SIZE;
+	agp_bridge.num_aperture_sizes = 5;
+	agp_bridge.dev_private_data = (void *) &nvidia_private;
+	agp_bridge.needs_scratch_page = FALSE;
+	agp_bridge.configure = nvidia_configure;
+	agp_bridge.fetch_size = nvidia_fetch_size;
+	agp_bridge.cleanup = nvidia_cleanup;
+	agp_bridge.tlb_flush = nvidia_tlbflush;
+	agp_bridge.mask_memory = nvidia_mask_memory;
+	agp_bridge.agp_enable = agp_generic_agp_enable;
+	agp_bridge.cache_flush = global_cache_flush;
+	agp_bridge.create_gatt_table = agp_generic_create_gatt_table;
+	agp_bridge.free_gatt_table = agp_generic_free_gatt_table;
+	agp_bridge.insert_memory = nvidia_insert_memory;
+	agp_bridge.remove_memory = nvidia_remove_memory;
+	agp_bridge.alloc_by_type = agp_generic_alloc_by_type;
+	agp_bridge.free_by_type = agp_generic_free_by_type;
+	agp_bridge.agp_alloc_page = agp_generic_alloc_page;
+	agp_bridge.agp_destroy_page = agp_generic_destroy_page;
+	agp_bridge.suspend = agp_generic_suspend;
+	agp_bridge.resume = agp_generic_resume;
+	agp_bridge.cant_use_aperture = 0;
+
+	return 0;
+}
+
+#endif /* CONFIG_AGP_NVIDIA */
+
 #ifdef CONFIG_AGP_HP_ZX1
 
 #ifndef log2
@@ -4516,15 +4886,38 @@
 	{ PCI_DEVICE_ID_INTEL_830_M_0,
 		PCI_VENDOR_ID_INTEL,
 		INTEL_I830_M,
-		"Intel",
-		"i830M",
+		"Intel(R)",
+		"830M",
 		intel_830mp_setup },
-    { PCI_DEVICE_ID_INTEL_845_G_0,
+	
+	{ PCI_DEVICE_ID_INTEL_845_G_0,
 		 PCI_VENDOR_ID_INTEL,
 		 INTEL_I845_G,
-		 "Intel",
-		 "i845G",
+		 "Intel(R)",
+		 "845G",
 		 intel_845_setup },
+
+	{ PCI_DEVICE_ID_INTEL_855_GM_0,
+		 PCI_VENDOR_ID_INTEL,
+		 INTEL_I855_PM,
+		 "Intel(R)",
+		 "855PM",
+		 intel_845_setup },
+
+	{ PCI_DEVICE_ID_INTEL_855_PM_0,
+		 PCI_VENDOR_ID_INTEL,
+		 INTEL_I855_PM,
+		 "Intel(R)",
+		 "855PM",
+		 intel_845_setup },
+
+	{ PCI_DEVICE_ID_INTEL_865_G_0,
+		PCI_VENDOR_ID_INTEL,
+		INTEL_I865_G,
+		"Intel(R)",
+		"865G",
+		 intel_845_setup },
+
 	{ PCI_DEVICE_ID_INTEL_840_0,
 		PCI_VENDOR_ID_INTEL,
 		INTEL_I840,
@@ -4565,12 +4958,24 @@
 		"SiS",
 		"740",
 		sis_generic_setup },
+	{ PCI_DEVICE_ID_SI_651,
+		PCI_VENDOR_ID_SI,
+		SIS_GENERIC,
+		"SiS",
+		"651",
+		sis_generic_setup },
 	{ PCI_DEVICE_ID_SI_650,
 		PCI_VENDOR_ID_SI,
 		SIS_GENERIC,
 		"SiS",
 		"650",
 		sis_generic_setup },
+	{ PCI_DEVICE_ID_SI_651,
+		PCI_VENDOR_ID_SI,
+		SIS_GENERIC,
+		"SiS",
+		"651",
+		sis_generic_setup },
 	{ PCI_DEVICE_ID_SI_645,
 		PCI_VENDOR_ID_SI,
 		SIS_GENERIC,
@@ -4583,6 +4988,12 @@
 		"SiS",
 		"646",
 		sis_generic_setup },
+	{ PCI_DEVICE_ID_SI_648,
+		PCI_VENDOR_ID_SI,
+		SIS_GENERIC,
+		"SiS",
+		"648",
+		sis_generic_setup },		
 	{ PCI_DEVICE_ID_SI_735,
 		PCI_VENDOR_ID_SI,
 		SIS_GENERIC,
@@ -4595,6 +5006,12 @@
 		"SiS",
 		"745",
 		sis_generic_setup },
+	{ PCI_DEVICE_ID_SI_746,
+		PCI_VENDOR_ID_SI,
+		SIS_GENERIC,
+		"SiS",
+		"746",
+		sis_generic_setup },
 	{ PCI_DEVICE_ID_SI_730,
 		PCI_VENDOR_ID_SI,
 		SIS_GENERIC,
@@ -4658,6 +5075,12 @@
 		"Via",
 		"MVP3",
 		via_generic_setup },
+	{ PCI_DEVICE_ID_VIA_8601_0,
+		PCI_VENDOR_ID_VIA,
+		VIA_APOLLO_PLE133,
+		"Via",
+		"Apollo PLE133",
+		via_generic_setup },
 	{ PCI_DEVICE_ID_VIA_82C691_0,
 		PCI_VENDOR_ID_VIA,
 		VIA_APOLLO_PRO,
@@ -4682,17 +5105,29 @@
 		"Via",
 		"Apollo Pro KT266",
 		via_generic_setup },
+	{ PCI_DEVICE_ID_VIA_8375,
+		PCI_VENDOR_ID_VIA,
+		VIA_APOLLO_KM266,
+		"Via",
+		"Apollo Pro KM266 / KL266",
+		via_generic_setup },
+	{ PCI_DEVICE_ID_VIA_8377_0,
+		PCI_VENDOR_ID_VIA,
+		VIA_APOLLO_KT400,
+		"Via",
+		"Apollo Pro KT400",
+		via_generic_setup },
         { PCI_DEVICE_ID_VIA_8377_0,
 		PCI_VENDOR_ID_VIA,
 		VIA_APOLLO_KT400,
 		"Via",
 		"Apollo Pro KT400",
 		via_generic_setup },
-	{ PCI_DEVICE_ID_VIA_P4X333,
+	{ PCI_DEVICE_ID_VIA_P4M266,
 		PCI_VENDOR_ID_VIA,
-		VIA_APOLLO_P4X400,
+		VIA_APOLLO_P4M266,
 		"Via",
-		"Apollo P4X400",
+		"Apollo P4M266",
 		via_generic_setup },
 	{ 0,
 		PCI_VENDOR_ID_VIA,
@@ -4702,6 +5137,27 @@
 		via_generic_setup },
 #endif /* CONFIG_AGP_VIA */
 
+#ifdef CONFIG_AGP_NVIDIA
+	{ PCI_DEVICE_ID_NVIDIA_NFORCE,
+		PCI_VENDOR_ID_NVIDIA,
+		NVIDIA_NFORCE,
+		"NVIDIA",
+		"nForce",
+		nvidia_generic_setup },
+	{ PCI_DEVICE_ID_NVIDIA_NFORCE2,
+		PCI_VENDOR_ID_NVIDIA,
+		NVIDIA_NFORCE2,
+		"NVIDIA",
+		"nForce2",
+		nvidia_generic_setup },
+	{ 0,
+		PCI_VENDOR_ID_NVIDIA,
+		NVIDIA_GENERIC,
+		"NVIDIA",
+		"Generic",
+		nvidia_generic_setup },
+#endif /* CONFIG_AGP_NVIDIA */
+
 #ifdef CONFIG_AGP_HP_ZX1
 	{ PCI_DEVICE_ID_HP_ZX1_LBA,
 		PCI_VENDOR_ID_HP,
@@ -4888,10 +5344,14 @@
                                  * with an external graphics
                                  * card. It will be initialized later 
                                  */
+				printk(KERN_ERR PFX "Detected an "
+				       "Intel(R) 845G, but could not find the"
+				       " secondary device. Assuming a "
+				       "non-integrated video card.\n");
 				agp_bridge.type = INTEL_I845_G;
 				break;
 			}
-			printk(KERN_INFO PFX "Detected an Intel "
+			printk(KERN_INFO PFX "Detected an Intel(R) "
 				   "845G Chipset.\n");
 			agp_bridge.type = INTEL_I810;
 			return intel_i830_setup(i810_dev);
@@ -4913,10 +5373,118 @@
 				agp_bridge.type = INTEL_I830_M;
 				break;
 			}
-			printk(KERN_INFO PFX "Detected an Intel "
+			printk(KERN_INFO PFX "Detected an Intel(R) "
 				   "830M Chipset.\n");
 			agp_bridge.type = INTEL_I810;
 			return intel_i830_setup(i810_dev);
+		case PCI_DEVICE_ID_INTEL_855_GM_0:
+			i810_dev = pci_find_device(PCI_VENDOR_ID_INTEL,
+					PCI_DEVICE_ID_INTEL_855_GM_1, NULL);
+			if(i810_dev && PCI_FUNC(i810_dev->devfn) != 0) {
+				i810_dev = pci_find_device(PCI_VENDOR_ID_INTEL,
+					PCI_DEVICE_ID_INTEL_855_GM_1, i810_dev);
+			}
+			if (i810_dev == NULL) {
+                                /* 
+                                 * We probably have an 855PM chipset
+                                 * with an external graphics
+                                 * card. It will be initialized later.
+                                 */
+				agp_bridge.type = INTEL_I855_PM;
+				break;
+			}
+			{
+				u32 capval = 0;
+				const char *name = "855GM/852GM";
+
+				pci_read_config_dword(dev, I85X_CAPID, &capval);
+				switch ((capval >> I85X_VARIANT_SHIFT) &
+					I85X_VARIANT_MASK) {
+				case I855_GME:
+					name = "855GME";
+					break;
+				case I855_GM:
+					name = "855GM";
+					break;
+				case I852_GME:
+					name = "852GME";
+					break;
+				case I852_GM:
+					name = "852GM";
+					break;
+				}
+				printk(KERN_INFO PFX "Detected an Intel(R) "
+					"%s Chipset.\n", name);
+			}
+			agp_bridge.type = INTEL_I810;
+			return intel_i830_setup(i810_dev);
+		case PCI_DEVICE_ID_INTEL_855_PM_0:
+			i810_dev = pci_find_device(PCI_VENDOR_ID_INTEL,
+					PCI_DEVICE_ID_INTEL_855_PM_1, NULL);
+			if(i810_dev && PCI_FUNC(i810_dev->devfn) != 0) {
+				i810_dev = pci_find_device(PCI_VENDOR_ID_INTEL,
+					PCI_DEVICE_ID_INTEL_855_PM_1, i810_dev);
+			}
+			if (i810_dev == NULL) {
+                                /* 
+                                 * We probably have an 855PM chipset
+                                 * with an external graphics
+                                 * card. It will be initialized later.
+                                 */
+				agp_bridge.type = INTEL_I855_PM;
+				break;
+			}
+			{
+				u32 capval = 0;
+				const char *name = "855PM/852PM";
+
+				pci_read_config_dword(dev, I85X_CAPID, &capval);
+				switch ((capval >> I85X_VARIANT_SHIFT) &
+					I85X_VARIANT_MASK) {
+				case I855_PME:
+					name = "855PME";
+					break;
+				case I855_PM:
+					name = "855PM";
+					break;
+				case I852_PME:
+					name = "852PME";
+					break;
+				case I852_PM:
+					name = "852PM";
+					break;
+				}
+				printk(KERN_INFO PFX "Detected an Intel(R) "
+					"%s Chipset.\n", name);
+			}
+			agp_bridge.type = INTEL_I810;
+			return intel_i830_setup(i810_dev);
+		case PCI_DEVICE_ID_INTEL_865_G_0:
+			i810_dev = pci_find_device(PCI_VENDOR_ID_INTEL,
+					PCI_DEVICE_ID_INTEL_865_G_1, NULL);
+			if(i810_dev && PCI_FUNC(i810_dev->devfn) != 0) {
+				i810_dev = pci_find_device(PCI_VENDOR_ID_INTEL,
+					PCI_DEVICE_ID_INTEL_865_G_1, i810_dev);
+			}
+
+			if (i810_dev == NULL) {
+                                /* 
+                                 * We probably have a 865G chipset
+                                 * with an external graphics
+                                 * card. It will be initialized later 
+                                 */
+				printk(KERN_ERR PFX "Detected an "
+				       "Intel(R) 865G, but could not"
+				       " find the"
+				       " secondary device. Assuming a "
+				       "non-integrated video card.\n");
+				agp_bridge.type = INTEL_I865_G;
+				break;
+			}
+			printk(KERN_INFO PFX "Detected an Intel(R) "
+				   "865G Chipset.\n");
+			agp_bridge.type = INTEL_I810;
+			return intel_i830_setup(i810_dev);
 		default:
 			break;
 		}
diff -urN linux-2.4.22-pre3/drivers/char/drm/drm_agpsupport.h linux-2.4.22-pre4/drivers/char/drm/drm_agpsupport.h
--- linux-2.4.22-pre3/drivers/char/drm/drm_agpsupport.h	2003-06-13 07:51:32.000000000 -0700
+++ linux-2.4.22-pre4/drivers/char/drm/drm_agpsupport.h	2003-07-09 16:56:28.000000000 -0700
@@ -283,8 +283,15 @@
 			break;
 		case VIA_APOLLO_PRO: 	head->chipset = "VIA Apollo Pro";
 			break;
+/*
+ * Alan merged agp_backend.h which removes VIA_APOLLO_P4X400, but 
+ * didnt update this file. I just commented it out so we dont 
+ * get compile errors on -pre4 and it can be fixed in -pre5.
+ * 
 		case VIA_APOLLO_P4X400:	head->chipset = "VIA Apollo P4X400";
 			break;
+	
+*/
 
 		case SIS_GENERIC:	head->chipset = "SiS";           break;
 		case AMD_GENERIC:	head->chipset = "AMD";           break;
diff -urN linux-2.4.22-pre3/drivers/char/random.c linux-2.4.22-pre4/drivers/char/random.c
--- linux-2.4.22-pre3/drivers/char/random.c	2003-06-13 07:51:33.000000000 -0700
+++ linux-2.4.22-pre4/drivers/char/random.c	2003-07-09 16:56:28.000000000 -0700
@@ -175,7 +175,7 @@
  *	chmod 600 $random_seed
  *	poolfile=/proc/sys/kernel/random/poolsize
  *	[ -r $poolfile ] && bytes=`cat $poolfile` || bytes=512
- *	dd if=/dev/urandom of=$random_seed count=1 bs=bytes
+ *	dd if=/dev/urandom of=$random_seed count=1 bs=$bytes
  *
  * and the following lines in an appropriate script which is run as
  * the system is shutdown:
@@ -188,7 +188,7 @@
  *	chmod 600 $random_seed
  *	poolfile=/proc/sys/kernel/random/poolsize
  *	[ -r $poolfile ] && bytes=`cat $poolfile` || bytes=512
- *	dd if=/dev/urandom of=$random_seed count=1 bs=bytes
+ *	dd if=/dev/urandom of=$random_seed count=1 bs=$bytes
  *
  * For example, on most modern systems using the System V init
  * scripts, such code fragments would be found in
diff -urN linux-2.4.22-pre3/drivers/char/serial.c linux-2.4.22-pre4/drivers/char/serial.c
--- linux-2.4.22-pre3/drivers/char/serial.c	2003-06-13 07:51:33.000000000 -0700
+++ linux-2.4.22-pre4/drivers/char/serial.c	2003-07-09 16:56:28.000000000 -0700
@@ -4353,9 +4353,10 @@
 #ifdef CONFIG_DDB5074
 	pbn_nec_nile4,
 #endif
-#if 0
+
+	pbn_dci_pccom4,
 	pbn_dci_pccom8,
-#endif
+
 	pbn_xircom_combo,
 
 	pbn_siig10x_0,
@@ -4449,9 +4450,10 @@
 	{ SPCI_FL_BASE0, 1, 520833,			   /* pbn_nec_nile4 */
 		64, 3, NULL, 0x300 },
 #endif
-#if 0	/* PCI_DEVICE_ID_DCI_PCCOM8 ? */		   /* pbn_dci_pccom8 */
-	{ SPCI_FL_BASE3, 8, 115200, 8 },
-#endif
+
+	{SPCI_FL_BASE3, 4, 115200, 8},			   /* pbn_dci_pccom4 */
+	{SPCI_FL_BASE3, 8, 115200, 8},			   /* pbn_dci_pccom8 */
+
 	{ SPCI_FL_BASE0, 1, 115200,			  /* pbn_xircom_combo */
 		0, 0, pci_xircom_fn },
 
@@ -4912,11 +4914,12 @@
 		pbn_nec_nile4 },
 #endif
 
-#if 0	/* PCI_DEVICE_ID_DCI_PCCOM8 ? */
+	{	PCI_VENDOR_ID_DCI, PCI_DEVICE_ID_DCI_PCCOM4,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_dci_pccom4 },
 	{	PCI_VENDOR_ID_DCI, PCI_DEVICE_ID_DCI_PCCOM8,
 		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
 		pbn_dci_pccom8 },
-#endif
 
        { PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID,
 	 PCI_CLASS_COMMUNICATION_SERIAL << 8, 0xffff00, },
diff -urN linux-2.4.22-pre3/drivers/hotplug/ibmphp_ebda.c linux-2.4.22-pre4/drivers/hotplug/ibmphp_ebda.c
--- linux-2.4.22-pre3/drivers/hotplug/ibmphp_ebda.c	2003-06-13 07:51:33.000000000 -0700
+++ linux-2.4.22-pre4/drivers/hotplug/ibmphp_ebda.c	2003-07-09 16:56:28.000000000 -0700
@@ -292,6 +292,7 @@
 	rio_complete = 0;
 	hs_complete = 0;
 
+	/* FIXME: This is x86-32 specific, and even then PC specific.. */
 	io_mem = ioremap ((0x40 << 4) + 0x0e, 2);
 	if (!io_mem )
 		return -ENOMEM;
@@ -299,6 +300,9 @@
 	iounmap (io_mem);
 	debug ("returned ebda segment: %x\n", ebda_seg);
 	
+	if(ebda_seg == 0)		/* No EBDA */
+		return -ENOENT;
+	
 	io_mem = ioremap (ebda_seg<<4, 65000);
 	if (!io_mem )
 		return -ENOMEM;
diff -urN linux-2.4.22-pre3/drivers/i2c/i2c-algo-bit.c linux-2.4.22-pre4/drivers/i2c/i2c-algo-bit.c
--- linux-2.4.22-pre3/drivers/i2c/i2c-algo-bit.c	2002-08-02 17:39:44.000000000 -0700
+++ linux-2.4.22-pre4/drivers/i2c/i2c-algo-bit.c	2003-07-09 16:56:28.000000000 -0700
@@ -609,7 +609,7 @@
 
 int __init i2c_algo_bit_init (void)
 {
-	printk("i2c-algo-bit.o: i2c bit algorithm module\n");
+	printk(KERN_INFO "i2c-algo-bit.o: i2c bit algorithm module\n");
 	return 0;
 }
 
diff -urN linux-2.4.22-pre3/drivers/i2c/i2c-core.c linux-2.4.22-pre4/drivers/i2c/i2c-core.c
--- linux-2.4.22-pre3/drivers/i2c/i2c-core.c	2002-11-28 15:53:13.000000000 -0800
+++ linux-2.4.22-pre4/drivers/i2c/i2c-core.c	2003-07-09 16:56:28.000000000 -0700
@@ -1264,7 +1264,7 @@
 
 static int __init i2c_init(void)
 {
-	printk("i2c-core.o: i2c core module\n");
+	printk(KERN_INFO "i2c-core.o: i2c core module\n");
 	memset(adapters,0,sizeof(adapters));
 	memset(drivers,0,sizeof(drivers));
 	adap_count=0;
diff -urN linux-2.4.22-pre3/drivers/macintosh/macio-adb.c linux-2.4.22-pre4/drivers/macintosh/macio-adb.c
--- linux-2.4.22-pre3/drivers/macintosh/macio-adb.c	2002-11-28 15:53:13.000000000 -0800
+++ linux-2.4.22-pre4/drivers/macintosh/macio-adb.c	2003-07-09 16:56:28.000000000 -0700
@@ -218,7 +218,6 @@
 				out_8(&adb->ctrl.r, DTB + CRE);
 			} else {
 				out_8(&adb->ctrl.r, DTB);
-				req->complete = 1;
 				current_req = req->next;
 				complete = 1;
 				if (current_req)
@@ -238,7 +237,6 @@
 				for (i = 0; i < req->reply_len; ++i)
 					req->reply[i] = in_8(&adb->data[i].r);
 			}
-			req->complete = 1;
 			current_req = req->next;
 			complete = 1;
 			if (current_req)
@@ -255,8 +253,16 @@
 		out_8(&adb->intr.r, 0);
 	}
 	spin_unlock(&macio_lock);
-	if (complete && req && req->done)
-		(*req->done)(req);
+	if (complete && req) {
+	    void (*done)(struct adb_request *) = req->done;
+	    mb();
+	    req->complete = 1;
+	    /* Here, we assume that if the request has a done member, the
+    	     * struct request will survive to setting req->complete to 1
+	     */
+	    if (done)
+		(*done)(req);
+	}
 	if (ibuf_len)
 		adb_input(ibuf, ibuf_len, regs, autopoll);
 }
diff -urN linux-2.4.22-pre3/drivers/macintosh/via-cuda.c linux-2.4.22-pre4/drivers/macintosh/via-cuda.c
--- linux-2.4.22-pre3/drivers/macintosh/via-cuda.c	2002-11-28 15:53:13.000000000 -0800
+++ linux-2.4.22-pre4/drivers/macintosh/via-cuda.c	2003-07-09 16:56:28.000000000 -0700
@@ -37,7 +37,6 @@
 #ifdef CONFIG_MAC
 #define CUDA_IRQ IRQ_MAC_ADB
 #define __openfirmware
-#define eieio()
 #else
 #define CUDA_IRQ vias->intrs[0].line
 #endif
@@ -175,8 +174,8 @@
     /* for us by the main VIA driver in arch/m68k/mac/via.c        */
 
 #ifndef CONFIG_MAC
-    via[IFR] = 0x7f; eieio();	/* clear interrupts by writing 1s */
-    via[IER] = IER_SET|SR_INT; eieio();	/* enable interrupt from SR */
+    out_8(&via[IFR], 0x7f);	/* clear interrupts by writing 1s */
+    out_8(&via[IER], IER_SET|SR_INT); /* enable interrupt from SR */
 #endif
 
     /* enable autopoll */
@@ -244,7 +243,8 @@
 #endif /* CONFIG_ADB */
 
 #define WAIT_FOR(cond, what)					\
-    do {							\
+    do {                                                        \
+    	int x;							\
 	for (x = 1000; !(cond); --x) {				\
 	    if (x == 0) {					\
 		printk("Timeout waiting for " what "\n");	\
@@ -257,40 +257,40 @@
 static int
 cuda_init_via()
 {
-    int x;
-
-    via[DIRB] = (via[DIRB] | TACK | TIP) & ~TREQ;	/* TACK & TIP out */
-    via[B] |= TACK | TIP;				/* negate them */
-    via[ACR] = (via[ACR] & ~SR_CTRL) | SR_EXT;		/* SR data in */
-    eieio();
-    x = via[SR]; eieio();	/* clear any left-over data */
+    out_8(&via[DIRB], (in_8(&via[DIRB]) | TACK | TIP) & ~TREQ);	/* TACK & TIP out */
+    out_8(&via[B], in_8(&via[B]) | TACK | TIP);			/* negate them */
+    out_8(&via[ACR] ,(in_8(&via[ACR]) & ~SR_CTRL) | SR_EXT);	/* SR data in */
+    (void)in_8(&via[SR]);						/* clear any left-over data */
 #ifndef CONFIG_MAC
-    via[IER] = 0x7f; eieio();	/* disable interrupts from VIA */
+    out_8(&via[IER], 0x7f);					/* disable interrupts from VIA */
+    (void)in_8(&via[IER]);
 #endif
-    eieio();
 
     /* delay 4ms and then clear any pending interrupt */
     mdelay(4);
-    x = via[SR]; eieio();
+    (void)in_8(&via[SR]);
+    out_8(&via[IFR], in_8(&via[IFR]) & 0x7f);
 
     /* sync with the CUDA - assert TACK without TIP */
-    via[B] &= ~TACK; eieio();
+    out_8(&via[B], in_8(&via[B]) & ~TACK);
 
     /* wait for the CUDA to assert TREQ in response */
-    WAIT_FOR((via[B] & TREQ) == 0, "CUDA response to sync");
+    WAIT_FOR((in_8(&via[B]) & TREQ) == 0, "CUDA response to sync");
 
     /* wait for the interrupt and then clear it */
-    WAIT_FOR(via[IFR] & SR_INT, "CUDA response to sync (2)");
-    x = via[SR]; eieio();
+    WAIT_FOR(in_8(&via[IFR]) & SR_INT, "CUDA response to sync (2)");
+    (void)in_8(&via[SR]);
+    out_8(&via[IFR], in_8(&via[IFR]) & 0x7f);
 
     /* finish the sync by negating TACK */
-    via[B] |= TACK; eieio();
+    out_8(&via[B], in_8(&via[B]) | TACK);
 
     /* wait for the CUDA to negate TREQ and the corresponding interrupt */
-    WAIT_FOR(via[B] & TREQ, "CUDA response to sync (3)");
-    WAIT_FOR(via[IFR] & SR_INT, "CUDA response to sync (4)");
-    x = via[SR]; eieio();
-    via[B] |= TIP; eieio();	/* should be unnecessary */
+    WAIT_FOR(in_8(&via[B]) & TREQ, "CUDA response to sync (3)");
+    WAIT_FOR(in_8(&via[IFR]) & SR_INT, "CUDA response to sync (4)");
+    (void)in_8(&via[SR]);
+    out_8(&via[IFR], in_8(&via[IFR]) & 0x7f);
+    out_8(&via[B], in_8(&via[B]) | TIP);	/* should be unnecessary */
 
     return 0;
 }
@@ -413,55 +413,59 @@
     req = current_req;
     if (req == 0)
 	return;
-    if ((via[B] & TREQ) == 0)
+    if ((in_8(&via[B]) & TREQ) == 0)
 	return;			/* a byte is coming in from the CUDA */
 
     /* set the shift register to shift out and send a byte */
-    via[ACR] |= SR_OUT; eieio();
-    via[SR] = req->data[0]; eieio();
-    via[B] &= ~TIP;
+    out_8(&via[ACR], in_8(&via[ACR]) | SR_OUT);
+    out_8(&via[SR], req->data[0]);
+    out_8(&via[B], in_8(&via[B]) & ~TIP);
     cuda_state = sent_first_byte;
 }
 
 void
 cuda_poll()
 {
-    if (via[IFR] & SR_INT) {
-	unsigned long flags;
+    unsigned long flags;
 
-	/* cuda_interrupt only takes a normal lock, we disable
-	 * interrupts here to avoid re-entering and thus deadlocking.
-	 * An option would be to disable only the IRQ source with
-	 * disable_irq(), would that work on m68k ? --BenH
-	 */
-	local_irq_save(flags);
-	cuda_interrupt(0, 0, 0);
-	local_irq_restore(flags);
-    }
+    /* cuda_interrupt only takes a normal lock, we disable
+     * interrupts here to avoid re-entering and thus deadlocking.
+     * An option would be to disable only the IRQ source with
+     * disable_irq(), would that work on m68k ? --BenH
+     */
+    local_irq_save(flags);
+    cuda_interrupt(0, 0, 0);
+    local_irq_restore(flags);
 }
 
 static void
 cuda_interrupt(int irq, void *arg, struct pt_regs *regs)
 {
-    int x, status;
+    int status;
     struct adb_request *req = NULL;
     unsigned char ibuf[16];
     int ibuf_len = 0;
     int complete = 0;
+    unsigned char virq;
     
-    if ((via[IFR] & SR_INT) == 0)
-	return;
-
     spin_lock(&cuda_lock);
-    status = (~via[B] & (TIP|TREQ)) | (via[ACR] & SR_OUT); eieio();
+
+    virq = in_8(&via[IFR]) & 0x7f;
+    out_8(&via[IFR], virq);   
+    if ((virq & SR_INT) == 0) {
+        spin_unlock(&cuda_lock);
+	return;
+    }
+    
+    status = (~in_8(&via[B]) & (TIP|TREQ)) | (in_8(&via[ACR]) & SR_OUT);
     /* printk("cuda_interrupt: state=%d status=%x\n", cuda_state, status); */
     switch (cuda_state) {
     case idle:
 	/* CUDA has sent us the first byte of data - unsolicited */
 	if (status != TREQ)
 	    printk("cuda: state=idle, status=%x\n", status);
-	x = via[SR]; eieio();
-	via[B] &= ~TIP; eieio();
+	(void)in_8(&via[SR]);
+	out_8(&via[B], in_8(&via[B]) & ~TIP);
 	cuda_state = reading;
 	reply_ptr = cuda_rbuf;
 	reading_reply = 0;
@@ -471,8 +475,8 @@
 	/* CUDA has sent us the first byte of data of a reply */
 	if (status != TREQ)
 	    printk("cuda: state=awaiting_reply, status=%x\n", status);
-	x = via[SR]; eieio();
-	via[B] &= ~TIP; eieio();
+	(void)in_8(&via[SR]);
+	out_8(&via[B], in_8(&via[B]) & ~TIP);
 	cuda_state = reading;
 	reply_ptr = current_req->reply;
 	reading_reply = 1;
@@ -481,16 +485,16 @@
     case sent_first_byte:
 	if (status == TREQ + TIP + SR_OUT) {
 	    /* collision */
-	    via[ACR] &= ~SR_OUT; eieio();
-	    x = via[SR]; eieio();
-	    via[B] |= TIP | TACK; eieio();
+	    out_8(&via[ACR], in_8(&via[ACR]) & ~SR_OUT);
+	    (void)in_8(&via[SR]);
+	    out_8(&via[B], in_8(&via[B]) | TIP | TACK);
 	    cuda_state = idle;
 	} else {
 	    /* assert status == TIP + SR_OUT */
 	    if (status != TIP + SR_OUT)
 		printk("cuda: state=sent_first_byte status=%x\n", status);
-	    via[SR] = current_req->data[1]; eieio();
-	    via[B] ^= TACK; eieio();
+	    out_8(&via[SR], current_req->data[1]);
+	    out_8(&via[B], in_8(&via[B]) ^ TACK);
 	    data_index = 2;
 	    cuda_state = sending;
 	}
@@ -499,9 +503,9 @@
     case sending:
 	req = current_req;
 	if (data_index >= req->nbytes) {
-	    via[ACR] &= ~SR_OUT; eieio();
-	    x = via[SR]; eieio();
-	    via[B] |= TACK | TIP; eieio();
+	    out_8(&via[ACR], in_8(&via[ACR]) & ~SR_OUT);
+	    (void)in_8(&via[SR]);
+	    out_8(&via[B], in_8(&via[B]) | TACK | TIP);
 	    req->sent = 1;
 	    if (req->reply_expected) {
 		cuda_state = awaiting_reply;
@@ -513,27 +517,27 @@
 		cuda_start();
 	    }
 	} else {
-	    via[SR] = req->data[data_index++]; eieio();
-	    via[B] ^= TACK; eieio();
+	    out_8(&via[SR], req->data[data_index++]);
+	    out_8(&via[B], in_8(&via[B]) ^ TACK);
 	}
 	break;
 
     case reading:
-	*reply_ptr++ = via[SR]; eieio();
+	*reply_ptr++ = in_8(&via[SR]);
 	if (status == TIP) {
 	    /* that's all folks */
-	    via[B] |= TACK | TIP; eieio();
+	    out_8(&via[B], in_8(&via[B]) | TACK | TIP);
 	    cuda_state = read_done;
 	} else {
 	    /* assert status == TIP | TREQ */
 	    if (status != TIP + TREQ)
 		printk("cuda: state=reading status=%x\n", status);
-	    via[B] ^= TACK; eieio();
+	    out_8(&via[B], in_8(&via[B]) ^ TACK);
 	}
 	break;
 
     case read_done:
-	x = via[SR]; eieio();
+	(void)in_8(&via[SR]);
 	if (reading_reply) {
 	    req = current_req;
 	    req->reply_len = reply_ptr - req->reply;
@@ -548,7 +552,6 @@
 		    memmove(req->reply, req->reply + 2, req->reply_len);
 		}
 	    }
-	    req->complete = 1;
 	    current_req = req->next;
 	    complete = 1;
 	} else {
@@ -563,7 +566,7 @@
 	    memcpy(ibuf, cuda_rbuf, ibuf_len);
 	}
 	if (status == TREQ) {
-	    via[B] &= ~TIP; eieio();
+	    out_8(&via[B], in_8(&via[B]) & ~TIP);
 	    cuda_state = reading;
 	    reply_ptr = cuda_rbuf;
 	    reading_reply = 0;
@@ -577,8 +580,16 @@
 	printk("cuda_interrupt: unknown cuda_state %d?\n", cuda_state);
     }
     spin_unlock(&cuda_lock);
-    if (complete && req && req->done)
-	(*req->done)(req);
+    if (complete && req) {
+    	void (*done)(struct adb_request *) = req->done;
+    	mb();
+    	req->complete = 1;
+    	/* Here, we assume that if the request has a done member, the
+    	 * struct request will survive to setting req->complete to 1
+    	 */
+    	if (done)
+		(*done)(req);
+    }
     if (ibuf_len)
 	cuda_input(ibuf, ibuf_len, regs);
 }
diff -urN linux-2.4.22-pre3/drivers/media/video/cpia_pp.c linux-2.4.22-pre4/drivers/media/video/cpia_pp.c
--- linux-2.4.22-pre3/drivers/media/video/cpia_pp.c	2003-06-13 07:51:34.000000000 -0700
+++ linux-2.4.22-pre4/drivers/media/video/cpia_pp.c	2003-07-09 16:56:28.000000000 -0700
@@ -383,8 +383,8 @@
 			return -EINVAL;
 		}
 		if((err = ReadPacket(cam, buffer, 8)) < 0) {
-			return err;
 			DBG("Error reading command result\n");
+                       return err;
 		}
 		memcpy(data, buffer, databytes);
 	} else if(command[0] == DATA_OUT) {
diff -urN linux-2.4.22-pre3/drivers/message/fusion/Makefile linux-2.4.22-pre4/drivers/message/fusion/Makefile
--- linux-2.4.22-pre3/drivers/message/fusion/Makefile	2002-11-28 15:53:13.000000000 -0800
+++ linux-2.4.22-pre4/drivers/message/fusion/Makefile	2003-07-09 16:56:28.000000000 -0700
@@ -9,7 +9,7 @@
 # parent makefile.
 #
 # Note 3! If you want to turn on various debug defines for an extended period of
-# time but don't want them lingering around in the Makefile when you pass it on 
+# time but don't want them lingering around in the Makefile when you pass it on
 # to someone else, use the MPT_CFLAGS env variable (thanks Steve). -nromer
 
 #=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-{ LSI_LOGIC
@@ -28,6 +28,7 @@
 #EXTRA_CFLAGS += -DMPT_DEBUG
 #EXTRA_CFLAGS += -DMPT_DEBUG_MSG_FRAME
 #EXTRA_CFLAGS += -DMPT_DEBUG_SG
+#EXTRA_CFLAGS += -DMPTSCSIH_DBG_TIMEOUT
 #
 # driver/module specifics...
 #
@@ -36,7 +37,8 @@
 #CFLAGS_mptbase.o += -DMPT_DEBUG_IRQ
 #
 #  For mptscsih:
-#CFLAGS_mptscsih.o += -DMPT_DEBUG_SCANDV
+#CFLAGS_mptscsih.o += -DMPT_DEBUG_DV
+#CFLAGS_mptscsih.o += -DMPT_DEBUG_DV_TINY
 #CFLAGS_mptscsih.o += -DMPT_DEBUG_RESET
 #CFLAGS_mptscsih.o += -DMPT_DEBUG_NEH
 #
diff -urN linux-2.4.22-pre3/drivers/message/fusion/isense.c linux-2.4.22-pre4/drivers/message/fusion/isense.c
--- linux-2.4.22-pre3/drivers/message/fusion/isense.c	2003-07-09 16:56:24.000000000 -0700
+++ linux-2.4.22-pre4/drivers/message/fusion/isense.c	2003-07-09 16:56:28.000000000 -0700
@@ -9,9 +9,9 @@
  *  Written By: Steven J. Ralston
  *  (yes I wrote some of the orig. code back in 1991!)
  *  (mailto:sjralston1@netscape.net)
- *  (mailto:lstephen@lsil.com)
+ *  (mailto:lstephens@lsil.com)
  *
- *  $Id: isense.c,v 1.33 2002/02/27 18:44:19 sralston Exp $
+ *  $Id: isense.c,v 1.34 2003/03/18 22:49:48 pdelaney Exp $
  */
 /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
 /*
@@ -89,7 +89,9 @@
 #define my_VERSION	MPT_LINUX_VERSION_COMMON
 #define MYNAM		"isense"
 
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,62)
 EXPORT_NO_SYMBOLS;
+#endif
 MODULE_AUTHOR(MODULEAUTHOR);
 MODULE_DESCRIPTION(my_NAME);
 MODULE_LICENSE("GPL");
diff -urN linux-2.4.22-pre3/drivers/message/fusion/linux_compat.h linux-2.4.22-pre4/drivers/message/fusion/linux_compat.h
--- linux-2.4.22-pre3/drivers/message/fusion/linux_compat.h	2003-07-09 16:56:24.000000000 -0700
+++ linux-2.4.22-pre4/drivers/message/fusion/linux_compat.h	2003-07-09 16:56:28.000000000 -0700
@@ -75,11 +75,11 @@
 typedef void (*__cleanup_module_func_t)(void);
 #define module_init(x) \
 	int init_module(void) __attribute__((alias(#x))); \
-	extern inline __init_module_func_t __init_module_inline(void) \
+	static inline __init_module_func_t __init_module_inline(void) \
 	{ return x; }
 #define module_exit(x) \
 	void cleanup_module(void) __attribute__((alias(#x))); \
-	extern inline __cleanup_module_func_t __cleanup_module_inline(void) \
+	static inline __cleanup_module_func_t __cleanup_module_inline(void) \
 	{ return x; }
 
 #else
@@ -247,6 +247,8 @@
 
 /*
  *  We use our new error handling code if the kernel version is 2.4.18 or newer.
+ *  Remark: 5/5/03 use old EH code with 2.4 kernels as it runs in a background thread
+ *  2.4 kernels choke on a call to schedule via eh thread.
  */
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,1)
         #define MPT_SCSI_USE_NEW_EH
@@ -270,6 +272,13 @@
 #define mptscsih_sync_irq(_irq) synchronize_irq()
 #endif
 
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,58)
+#define mpt_inc_use_count()
+#define mpt_dec_use_count()
+#else
+#define mpt_inc_use_count() MOD_INC_USE_COUNT
+#define mpt_dec_use_count() MOD_DEC_USE_COUNT
+#endif
 
 
 /*}-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
diff -urN linux-2.4.22-pre3/drivers/message/fusion/lsi/mpi_tool.h linux-2.4.22-pre4/drivers/message/fusion/lsi/mpi_tool.h
--- linux-2.4.22-pre3/drivers/message/fusion/lsi/mpi_tool.h	2002-08-02 17:39:44.000000000 -0700
+++ linux-2.4.22-pre4/drivers/message/fusion/lsi/mpi_tool.h	1969-12-31 16:00:00.000000000 -0800
@@ -1,129 +0,0 @@
-/*
- *  Copyright (c) 2001 LSI Logic Corporation.
- *
- *
- *           Name:  MPI_TOOL.H
- *          Title:  MPI Toolbox structures and definitions
- *  Creation Date:  July 30, 2001
- *
- *    MPI Version:  01.02.02
- *
- *  Version History
- *  ---------------
- *
- *  Date      Version   Description
- *  --------  --------  ------------------------------------------------------
- *  08-08-01  01.02.01  Original release.
- *  08-29-01  01.02.02  Added DIAG_DATA_UPLOAD_HEADER and related defines.
- *  --------------------------------------------------------------------------
- */
-
-#ifndef MPI_TOOL_H
-#define MPI_TOOL_H
-
-#define MPI_TOOLBOX_CLEAN_TOOL                      (0x00)
-#define MPI_TOOLBOX_MEMORY_MOVE_TOOL                (0x01)
-#define MPI_TOOLBOX_DIAG_DATA_UPLOAD_TOOL           (0x02)
-
-
-/****************************************************************************/
-/* Toolbox reply                                                            */
-/****************************************************************************/
-
-typedef struct _MSG_TOOLBOX_REPLY
-{
-    U8                      Tool;                       /* 00h */
-    U8                      Reserved;                   /* 01h */
-    U8                      MsgLength;                  /* 02h */
-    U8                      Function;                   /* 03h */
-    U16                     Reserved1;                  /* 04h */
-    U8                      Reserved2;                  /* 06h */
-    U8                      MsgFlags;                   /* 07h */
-    U32                     MsgContext;                 /* 08h */
-    U16                     Reserved3;                  /* 0Ch */
-    U16                     IOCStatus;                  /* 0Eh */
-    U32                     IOCLogInfo;                 /* 10h */
-} MSG_TOOLBOX_REPLY, MPI_POINTER PTR_MSG_TOOLBOX_REPLY,
-  ToolboxReply_t, MPI_POINTER pToolboxReply_t;
-
-
-/****************************************************************************/
-/* Toolbox Clean Tool request                                               */
-/****************************************************************************/
-
-typedef struct _MSG_TOOLBOX_CLEAN_REQUEST
-{
-    U8                      Tool;                       /* 00h */
-    U8                      Reserved;                   /* 01h */
-    U8                      ChainOffset;                /* 02h */
-    U8                      Function;                   /* 03h */
-    U16                     Reserved1;                  /* 04h */
-    U8                      Reserved2;                  /* 06h */
-    U8                      MsgFlags;                   /* 07h */
-    U32                     MsgContext;                 /* 08h */
-    U32                     Flags;                      /* 0Ch */
-} MSG_TOOLBOX_CLEAN_REQUEST, MPI_POINTER PTR_MSG_TOOLBOX_CLEAN_REQUEST,
-  ToolboxCleanRequest_t, MPI_POINTER pToolboxCleanRequest_t;
-
-#define MPI_TOOLBOX_CLEAN_NVSRAM                    (0x00000001)
-#define MPI_TOOLBOX_CLEAN_SEEPROM                   (0x00000002)
-#define MPI_TOOLBOX_CLEAN_FLASH                     (0x00000004)
-
-
-/****************************************************************************/
-/* Toolbox Memory Move request                                              */
-/****************************************************************************/
-
-typedef struct _MSG_TOOLBOX_MEM_MOVE_REQUEST
-{
-    U8                      Tool;                       /* 00h */
-    U8                      Reserved;                   /* 01h */
-    U8                      ChainOffset;                /* 02h */
-    U8                      Function;                   /* 03h */
-    U16                     Reserved1;                  /* 04h */
-    U8                      Reserved2;                  /* 06h */
-    U8                      MsgFlags;                   /* 07h */
-    U32                     MsgContext;                 /* 08h */
-    SGE_SIMPLE_UNION        SGL;                        /* 0Ch */
-} MSG_TOOLBOX_MEM_MOVE_REQUEST, MPI_POINTER PTR_MSG_TOOLBOX_MEM_MOVE_REQUEST,
-  ToolboxMemMoveRequest_t, MPI_POINTER pToolboxMemMoveRequest_t;
-
-
-/****************************************************************************/
-/* Toolbox Diagnostic Data Upload request                                   */
-/****************************************************************************/
-
-typedef struct _MSG_TOOLBOX_DIAG_DATA_UPLOAD_REQUEST
-{
-    U8                      Tool;                       /* 00h */
-    U8                      Reserved;                   /* 01h */
-    U8                      ChainOffset;                /* 02h */
-    U8                      Function;                   /* 03h */
-    U16                     Reserved1;                  /* 04h */
-    U8                      Reserved2;                  /* 06h */
-    U8                      MsgFlags;                   /* 07h */
-    U32                     MsgContext;                 /* 08h */
-    U32                     Flags;                      /* 0Ch */
-    U32                     Reserved3;                  /* 10h */
-    SGE_SIMPLE_UNION        SGL;                        /* 14h */
-} MSG_TOOLBOX_DIAG_DATA_UPLOAD_REQUEST, MPI_POINTER PTR_MSG_TOOLBOX_DIAG_DATA_UPLOAD_REQUEST,
-  ToolboxDiagDataUploadRequest_t, MPI_POINTER pToolboxDiagDataUploadRequest_t;
-
-typedef struct _DIAG_DATA_UPLOAD_HEADER
-{
-    U32                     DiagDataLength;             /* 00h */
-    U8                      FormatCode;                 /* 04h */
-    U8                      Reserved;                   /* 05h */
-    U16                     Reserved1;                  /* 06h */
-} DIAG_DATA_UPLOAD_HEADER, MPI_POINTER PTR_DIAG_DATA_UPLOAD_HEADER,
-  DiagDataUploadHeader_t, MPI_POINTER pDiagDataUploadHeader_t;
-
-#define MPI_TB_DIAG_FORMAT_SCSI_PRINTF_1            (0x01)
-#define MPI_TB_DIAG_FORMAT_SCSI_2                   (0x02)
-#define MPI_TB_DIAG_FORMAT_SCSI_3                   (0x03)
-#define MPI_TB_DIAG_FORMAT_FC_TRACE_1               (0x04)
-
-
-#endif
-
-
diff -urN linux-2.4.22-pre3/drivers/message/fusion/mptbase.c linux-2.4.22-pre4/drivers/message/fusion/mptbase.c
--- linux-2.4.22-pre3/drivers/message/fusion/mptbase.c	2003-07-09 16:56:24.000000000 -0700
+++ linux-2.4.22-pre4/drivers/message/fusion/mptbase.c	2003-07-09 16:56:28.000000000 -0700
@@ -47,9 +47,9 @@
  *  Copyright (c) 1999-2002 LSI Logic Corporation
  *  Originally By: Steven J. Ralston
  *  (mailto:sjralston1@netscape.net)
- *  (mailto:lstephen@lsil.com)
+ *  (mailto:lstephens@lsil.com)
  *
- *  $Id: mptbase.c,v 1.126 2002/12/16 15:28:45 pdelaney Exp $
+ *  $Id: mptbase.c,v 1.130 2003/05/07 14:08:30 pdelaney Exp $
  */
 /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
 /*
@@ -123,8 +123,10 @@
 /*
  *  cmd line parameters
  */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,59)
 MODULE_PARM(PortIo, "0-1i");
 MODULE_PARM_DESC(PortIo, "[0]=Use mmap, 1=Use port io");
+#endif
 static int PortIo = 0;
 
 #ifdef MFCNT
@@ -444,7 +446,7 @@
 			 */
 			if ((mf) && ((mf >= MPT_INDEX_2_MFPTR(ioc, ioc->req_depth))
 				|| (mf < ioc->req_frames)) ) {
-				printk(MYIOC_s_WARN_FMT 
+				printk(MYIOC_s_WARN_FMT
 					"mpt_interrupt: Invalid mf (%p) req_idx (%d)!\n", ioc->name, (void *)mf, req_idx);
 				cb_idx = 0;
 				pa = 0;
@@ -452,14 +454,14 @@
 			}
 			if ((pa) && (mr) && ((mr >= MPT_INDEX_2_RFPTR(ioc, ioc->req_depth))
 				|| (mr < ioc->reply_frames)) ) {
-				printk(MYIOC_s_WARN_FMT 
+				printk(MYIOC_s_WARN_FMT
 					"mpt_interrupt: Invalid rf (%p)!\n", ioc->name, (void *)mr);
 				cb_idx = 0;
 				pa = 0;
 				freeme = 0;
 			}
 			if (cb_idx > (MPT_MAX_PROTOCOL_DRIVERS-1)) {
-				printk(MYIOC_s_WARN_FMT 
+				printk(MYIOC_s_WARN_FMT
 					"mpt_interrupt: Invalid cb_idx (%d)!\n", ioc->name, cb_idx);
 				cb_idx = 0;
 				pa = 0;
@@ -577,7 +579,7 @@
 		CONFIGPARMS *pCfg;
 		unsigned long flags;
 
-		dprintk((MYIOC_s_INFO_FMT "config_complete (mf=%p,mr=%p)\n",
+		dcprintk((MYIOC_s_INFO_FMT "config_complete (mf=%p,mr=%p)\n",
 				ioc->name, mf, reply));
 
 		pCfg = * ((CONFIGPARMS **)((u8 *) mf + ioc->req_sz - sizeof(void *)));
@@ -600,7 +602,7 @@
 				u16		 status;
 
 				status = le16_to_cpu(pReply->IOCStatus) & MPI_IOCSTATUS_MASK;
-				dprintk((KERN_NOTICE "  IOCStatus=%04xh, IOCLogInfo=%08xh\n",
+				dcprintk((KERN_NOTICE "  IOCStatus=%04xh, IOCLogInfo=%08xh\n",
 				     status, le32_to_cpu(pReply->IOCLogInfo)));
 
 				pCfg->status = status;
@@ -684,7 +686,7 @@
 			MptEvHandlers[i] = NULL;
 			last_drv_idx = i;
 			if (cbfunc != mpt_base_reply) {
-				MOD_INC_USE_COUNT;
+				mpt_inc_use_count();
 			}
 			break;
 		}
@@ -704,7 +706,7 @@
 void
 mpt_deregister(int cb_idx)
 {
-	if (cb_idx && (cb_idx < MPT_MAX_PROTOCOL_DRIVERS)) {
+	if ((cb_idx >= 0) && (cb_idx < MPT_MAX_PROTOCOL_DRIVERS)) {
 		MptCallbacks[cb_idx] = NULL;
 		MptDriverClass[cb_idx] = MPTUNKNOWN_DRIVER;
 		MptEvHandlers[cb_idx] = NULL;
@@ -714,7 +716,7 @@
 			isense_idx++;
 
 		if (cb_idx != mpt_base_index) {
-			MOD_DEC_USE_COUNT;
+			mpt_dec_use_count();
 		}
 	}
 }
@@ -944,7 +946,7 @@
  *	mpt_add_sge - Place a simple SGE at address pAddr.
  *	@pAddr: virtual address for SGE
  *	@flagslength: SGE flags and data transfer length
- *	@dma_addr: Physical address 
+ *	@dma_addr: Physical address
  *
  *	This routine places a MPT request frame back on the MPT adapter's
  *	FreeQ.
@@ -974,7 +976,7 @@
  *	@pAddr: virtual address for SGE
  *	@next: nextChainOffset value (u32's)
  *	@length: length of next SGL segment
- *	@dma_addr: Physical address 
+ *	@dma_addr: Physical address
  *
  *	This routine places a MPT request frame back on the MPT adapter's
  *	FreeQ.
@@ -987,7 +989,7 @@
 		u32 tmp = dma_addr & 0xFFFFFFFF;
 
 		pChain->Length = cpu_to_le16(length);
-		pChain->Flags = MPI_SGE_FLAGS_CHAIN_ELEMENT | mpt_addr_size(); 
+		pChain->Flags = MPI_SGE_FLAGS_CHAIN_ELEMENT | mpt_addr_size();
 
 		pChain->NextChainOffset = next;
 
@@ -1070,7 +1072,7 @@
 
 		CHIPREG_WRITE32(&iocp->chip->IntStatus, 0);
 
-		if ((r = WaitForDoorbellAck(iocp, 1, sleepFlag)) < 0) {
+		if ((r = WaitForDoorbellAck(iocp, 2, sleepFlag)) < 0) {
 			return -2;
 		}
 
@@ -1084,7 +1086,7 @@
 				(req_as_bytes[(ii*4) + 2] << 16) |
 				(req_as_bytes[(ii*4) + 3] << 24));
 			CHIPREG_WRITE32(&iocp->chip->Doorbell, word);
-			if ((r = WaitForDoorbellAck(iocp, 1, sleepFlag)) < 0) {
+			if ((r = WaitForDoorbellAck(iocp, 2, sleepFlag)) < 0) {
 				r = -3;
 				break;
 			}
@@ -1278,17 +1280,19 @@
 	u32		 psize;
 	int		 ii;
 	int		 r = -ENODEV;
-	u64		 mask = 0xffffffffffffffff;
+	u64		 mask = 0xffffffffffffffffULL;
 
 	if (pci_enable_device(pdev))
 		return r;
 
 	if (!pci_set_dma_mask(pdev, mask)) {
-		dprintk((KERN_INFO MYNAM 
-			": 64 BIT PCI BUS DMA ADDRESSING SUPPORTED\n"));
-	} else if (pci_set_dma_mask(pdev, (u64) 0xffffffff)) {
-		printk(KERN_WARNING MYNAM ": 32 BIT PCI BUS DMA ADDRESSING NOT SUPPORTED\n");
-		return r;
+		dprintk((KERN_INFO MYNAM ": 64 BIT PCI BUS DMA ADDRESSING SUPPORTED\n"));
+	} else {
+		if (pci_set_dma_mask(pdev, (u64) 0xffffffff)) {
+			printk(KERN_WARNING MYNAM
+				": 32 BIT PCI BUS DMA ADDRESSING NOT SUPPORTED\n");
+			return r;
+		}
 	}
 
 	ioc = kmalloc(sizeof(MPT_ADAPTER), GFP_ATOMIC);
@@ -1510,7 +1514,7 @@
 	/* NEW!  20010220 -sralston
 	 * Check for "bound ports" (929, 929X, 1030, 1035) to reduce redundant resets.
 	 */
-	if ((ioc->chip_type == FC929) || (ioc->chip_type == C1030) 
+	if ((ioc->chip_type == FC929) || (ioc->chip_type == C1030)
 			|| (ioc->chip_type == C1035) || (ioc->chip_type == FC929X))
 		mpt_detect_bound_ports(ioc, pdev);
 
@@ -1551,20 +1555,23 @@
 	int	 r;
 	int	 ii;
 	int	 handlers;
+	int	 ret = 0;
+	int	 reset_alt_ioc_active = 0;
 
 	printk(KERN_INFO MYNAM ": Initiating %s %s\n",
 			ioc->name, reason==MPT_HOSTEVENT_IOC_BRINGUP ? "bringup" : "recovery");
 
-	/* Disable reply interrupts */
+	/* Disable reply interrupts (also blocks FreeQ) */
 	CHIPREG_WRITE32(&ioc->chip->IntMask, 0xFFFFFFFF);
 	ioc->active = 0;
-	/* NOTE: Access to IOC's request FreeQ is now blocked! */
 
 	if (ioc->alt_ioc) {
-		/* Disable alt-IOC's reply interrupts for a bit ... */
+		if (ioc->alt_ioc->active)
+			reset_alt_ioc_active = 1;
+
+		/* Disable alt-IOC's reply interrupts (and FreeQ) for a bit ... */
 		CHIPREG_WRITE32(&ioc->alt_ioc->chip->IntMask, 0xFFFFFFFF);
 		ioc->alt_ioc->active = 0;
-		/* NOTE: Access to alt-IOC's request FreeQ is now blocked! */
 	}
 
 	hard = 1;
@@ -1572,15 +1579,29 @@
 		hard = 0;
 
 	if ((hard_reset_done = MakeIocReady(ioc, hard, sleepFlag)) < 0) {
-		printk(KERN_WARNING MYNAM ": %s NOT READY WARNING!\n",
-				ioc->name);
+		if (hard_reset_done == -4) {
+			printk(KERN_WARNING MYNAM ": %s Owned by PEER..skipping!\n",
+					ioc->name);
+
+			if (reset_alt_ioc_active && ioc->alt_ioc) {
+				/* (re)Enable alt-IOC! (reply interrupt, FreeQ) */
+				dprintk((KERN_INFO MYNAM ": alt-%s reply irq re-enabled\n",
+						ioc->alt_ioc->name));
+				CHIPREG_WRITE32(&ioc->alt_ioc->chip->IntMask, ~(MPI_HIM_RIM));
+				ioc->alt_ioc->active = 1;
+			}
+
+		} else {
+			printk(KERN_WARNING MYNAM ": %s NOT READY WARNING!\n",
+					ioc->name);
+		}
 		return -1;
 	}
 
 	/* hard_reset_done = 0 if a soft reset was performed
 	 * and 1 if a hard reset was performed.
 	 */
-	if (hard_reset_done && ioc->alt_ioc) {
+	if (hard_reset_done && reset_alt_ioc_active && ioc->alt_ioc) {
 		if ((r = MakeIocReady(ioc->alt_ioc, 0, sleepFlag)) == 0)
 			alt_ioc_ready = 1;
 		else
@@ -1589,42 +1610,55 @@
 					ioc->alt_ioc->name, r);
 	}
 
-	/* Get IOC facts! */
+	/* Get IOC facts! Allow 1 retry */
 	if ((r = GetIocFacts(ioc, sleepFlag, reason)) != 0)
-		return -2;
-	if (reason == MPT_HOSTEVENT_IOC_BRINGUP) {
+		r = GetIocFacts(ioc, sleepFlag, reason);
+
+	if (r) {
+		ret = -2;
+	} else if (reason == MPT_HOSTEVENT_IOC_BRINGUP) {
 		MptDisplayIocCapabilities(ioc);
 	}
 
 	if (alt_ioc_ready) {
-		if ((r = GetIocFacts(ioc->alt_ioc, sleepFlag, reason)) != 0)
-			return -2;
-		if (reason == MPT_HOSTEVENT_IOC_BRINGUP) {
+		if ((r = GetIocFacts(ioc->alt_ioc, sleepFlag, reason)) != 0) {
+			/* Retry - alt IOC was initialized once
+			 */
+			r = GetIocFacts(ioc->alt_ioc, sleepFlag, reason);
+		}
+		if (r) {
+			alt_ioc_ready = 0;
+			reset_alt_ioc_active = 0;
+		} else if (reason == MPT_HOSTEVENT_IOC_BRINGUP) {
 			MptDisplayIocCapabilities(ioc->alt_ioc);
 		}
 	}
 
-	/*
-	 * Prime reply & request queues!
+	/* Prime reply & request queues!
 	 * (mucho alloc's) Must be done prior to
 	 * init as upper addresses are needed for init.
+	 * If fails, continue with alt-ioc processing
 	 */
-	if ((r = PrimeIocFifos(ioc)) != 0)
-		return -3;
+	if ((ret == 0) && ((r = PrimeIocFifos(ioc)) != 0))
+		ret = -3;
 
-	// May need to check/upload firmware & data here!
-	if ((r = SendIocInit(ioc, sleepFlag)) != 0)
-		return -4;
+	/* May need to check/upload firmware & data here!
+	 * If fails, continue with alt-ioc processing
+	 */
+	if ((ret == 0) && ((r = SendIocInit(ioc, sleepFlag)) != 0))
+		ret = -4;
 // NEW!
 	if (alt_ioc_ready && ((r = PrimeIocFifos(ioc->alt_ioc)) != 0)) {
 		printk(KERN_WARNING MYNAM ": alt-%s: (%d) FIFO mgmt alloc WARNING!\n",
 				ioc->alt_ioc->name, r);
 		alt_ioc_ready = 0;
+		reset_alt_ioc_active = 0;
 	}
 
 	if (alt_ioc_ready) {
 		if ((r = SendIocInit(ioc->alt_ioc, sleepFlag)) != 0) {
 			alt_ioc_ready = 0;
+			reset_alt_ioc_active = 0;
 			printk(KERN_WARNING MYNAM
 				": alt-%s: (%d) init failure WARNING!\n",
 					ioc->alt_ioc->name, r);
@@ -1636,13 +1670,18 @@
 			ddlprintk((MYIOC_s_INFO_FMT
 				"firmware upload required!\n", ioc->name));
 
-			r = mpt_do_upload(ioc, sleepFlag);
-			if (r != 0)
-				printk(KERN_WARNING MYNAM ": firmware upload failure!\n");
+			/* Controller is not operational, cannot do upload
+			 */
+			if (ret == 0) {
+				r = mpt_do_upload(ioc, sleepFlag);
+				if (r != 0)
+					printk(KERN_WARNING MYNAM ": firmware upload failure!\n");
+			}
+
 			/* Handle the alt IOC too */
 			if ((alt_ioc_ready) && (ioc->alt_ioc->upload_fw)){
 				ddlprintk((MYIOC_s_INFO_FMT
-					"Alt-ioc firmware upload required!\n", 
+					"Alt-ioc firmware upload required!\n",
 					ioc->name));
 				r = mpt_do_upload(ioc->alt_ioc, sleepFlag);
 				if (r != 0)
@@ -1651,12 +1690,13 @@
 		}
 	}
 
+	if (ret == 0) {
+		/* Enable! (reply interrupt) */
+		CHIPREG_WRITE32(&ioc->chip->IntMask, ~(MPI_HIM_RIM));
+		ioc->active = 1;
+	}
 
-	/* Enable! (reply interrupt) */
-	CHIPREG_WRITE32(&ioc->chip->IntMask, ~(MPI_HIM_RIM));
-	ioc->active = 1;
-
-	if (ioc->alt_ioc) {
+	if (reset_alt_ioc_active && ioc->alt_ioc) {
 		/* (re)Enable alt-IOC! (reply interrupt) */
 		dprintk((KERN_INFO MYNAM ": alt-%s reply irq re-enabled\n",
 				ioc->alt_ioc->name));
@@ -1668,7 +1708,7 @@
 	 *  Enable MPT base driver management of EventNotification
 	 *  and EventAck handling.
 	 */
-	if (!ioc->facts.EventState)
+	if ((ret == 0) && (!ioc->facts.EventState))
 		(void) SendEventNotification(ioc, 1);	/* 1=Enable EventNotification */
 
 	if (ioc->alt_ioc && alt_ioc_ready && !ioc->alt_ioc->facts.EventState)
@@ -1682,7 +1722,7 @@
 	 *	routine calls HardResetHandler, which calls into here again,
 	 *	and we try GetLanConfigPages again...
 	 */
-	if (reason == MPT_HOSTEVENT_IOC_BRINGUP) {
+	if ((ret == 0) && (reason == MPT_HOSTEVENT_IOC_BRINGUP)) {
 		if ((int)ioc->chip_type <= (int)FC929) {
 			/*
 			 *  Pre-fetch FC port WWN and stuff...
@@ -1712,11 +1752,11 @@
 			 */
 			mpt_GetScsiPortSettings(ioc, 0);
 
-			/* Get version and length of SDP 1 
+			/* Get version and length of SDP 1
 			 */
 			mpt_readScsiDevicePageHeaders(ioc, 0);
 
-			/* Find IM volumes 
+			/* Find IM volumes
 			 */
 			if (ioc->facts.MsgVersion >= 0x0102)
 				mpt_findImVolumes(ioc);
@@ -1738,24 +1778,24 @@
 	if (hard_reset_done) {
 		r = handlers = 0;
 		for (ii=MPT_MAX_PROTOCOL_DRIVERS-1; ii; ii--) {
-			if (MptResetHandlers[ii]) {
+			if ((ret == 0) && MptResetHandlers[ii]) {
 				dprintk((MYIOC_s_INFO_FMT "Calling IOC post_reset handler #%d\n",
 						ioc->name, ii));
 				r += (*(MptResetHandlers[ii]))(ioc, MPT_IOC_POST_RESET);
 				handlers++;
+			}
 
-				if (alt_ioc_ready) {
-					dprintk((MYIOC_s_INFO_FMT "Calling alt-%s post_reset handler #%d\n",
-							ioc->name, ioc->alt_ioc->name, ii));
-					r += (*(MptResetHandlers[ii]))(ioc->alt_ioc, MPT_IOC_POST_RESET);
-					handlers++;
-				}
+			if (alt_ioc_ready && MptResetHandlers[ii]) {
+				dprintk((MYIOC_s_INFO_FMT "Calling alt-%s post_reset handler #%d\n",
+						ioc->name, ioc->alt_ioc->name, ii));
+				r += (*(MptResetHandlers[ii]))(ioc->alt_ioc, MPT_IOC_POST_RESET);
+				handlers++;
 			}
 		}
 		/* FIXME?  Examine results here? */
 	}
 
-	return 0;
+	return ret;
 }
 
 /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
@@ -1830,7 +1870,7 @@
 			ddlprintk((KERN_INFO MYNAM ": Pushing FW onto adapter\n"));
 
 			if ((ret = mpt_downloadboot(this, NO_SLEEP)) < 0) {
-				printk(KERN_WARNING MYNAM 
+				printk(KERN_WARNING MYNAM
 					": firmware downloadboot failure (%d)!\n", ret);
 			}
 		}
@@ -2014,8 +2054,8 @@
  *
  *	Returns:
  *		 1 - DIAG reset and READY
- *		 0 - READY initially OR soft reset and READY 
- *		-1 - Any failure on KickStart 
+ *		 0 - READY initially OR soft reset and READY
+ *		-1 - Any failure on KickStart
  *		-2 - Msg Unit Reset Failed
  *		-3 - IO Unit Reset Failed
  *		-4 - IOC owned by a PEER
@@ -2058,7 +2098,7 @@
 			else
 				statefault = 4;
 		}
-	} 
+	}
 
 	/*
 	 *	Check to see if IOC is in FAULT state.
@@ -2106,9 +2146,7 @@
 	 *  Loop here waiting for IOC to come READY.
 	 */
 	ii = 0;
-	cntdn = HZ * 15;
-	if (sleepFlag != CAN_SLEEP)
-		cntdn *= 10;	/* 1500 iterations @ 1msec per */
+	cntdn = ((sleepFlag == CAN_SLEEP) ? HZ : 1000) * 15;	/* 15 seconds */
 
 	while ((ioc_state = mpt_GetIocState(ioc, 1)) != MPI_IOC_STATE_READY) {
 		if (ioc_state == MPI_IOC_STATE_OPERATIONAL) {
@@ -2199,7 +2237,7 @@
 	int			 req_sz;
 	int			 reply_sz;
 	u32			 status;
-
+	
 	/* IOC *must* NOT be in RESET state! */
 	if (ioc->last_state == MPI_IOC_STATE_RESET) {
 		printk(KERN_ERR MYNAM ": ERROR - Can't get IOCFacts, %s NOT READY! (%08x)\n",
@@ -2260,7 +2298,7 @@
 		facts->RequestFrameSize = le16_to_cpu(facts->RequestFrameSize);
 
 		/*
-		 * FC f/w version changed between 1.1 and 1.2 
+		 * FC f/w version changed between 1.1 and 1.2
 		 *	Old: u16{Major(4),Minor(4),SubMinor(8)}
 		 *	New: u32{Major(8),Minor(8),Unit(8),Dev(8)}
 		 */
@@ -2433,10 +2471,10 @@
 	if (ioc->facts.Flags & MPI_IOCFACTS_FLAGS_FW_DOWNLOAD_BOOT) {
 		if ((ioc->cached_fw) || (ioc->alt_ioc && ioc->alt_ioc->cached_fw))
 			ioc_init.Flags = MPI_IOCINIT_FLAGS_DISCARD_FW_IMAGE;
-		else 
+		else
 			ioc->upload_fw = 1;
 	}
-	ddlprintk((MYIOC_s_INFO_FMT "flags %d, upload_fw %d \n", 
+	ddlprintk((MYIOC_s_INFO_FMT "flags %d, upload_fw %d \n",
 		   ioc->name, ioc_init.Flags, ioc->upload_fw));
 
 	if ((int)ioc->chip_type <= (int)FC929) {
@@ -2482,9 +2520,7 @@
 	 *  LoopInit and TargetDiscovery!
 	 */
 	count = 0;
-	cntdn = HZ * 60;					/* chg'd from 30 to 60 seconds */
-	if (sleepFlag != CAN_SLEEP)
-		cntdn *= 10;					/* scale for 1msec delays */
+	cntdn = ((sleepFlag == CAN_SLEEP) ? HZ : 1000) * 60;	/* 60 seconds */
 	state = mpt_GetIocState(ioc, 1);
 	while (state != MPI_IOC_STATE_OPERATIONAL && --cntdn) {
 		if (sleepFlag == CAN_SLEEP) {
@@ -2570,8 +2606,8 @@
  * Outputs: frags - number of fragments needed
  * Return NULL if failed.
  */
-void * 
-mpt_alloc_fw_memory(MPT_ADAPTER *ioc, int size, int *frags, int *alloc_sz) 
+void *
+mpt_alloc_fw_memory(MPT_ADAPTER *ioc, int size, int *frags, int *alloc_sz)
 {
 	fw_image_t	**cached_fw = NULL;
 	u8		*mem = NULL;
@@ -2580,7 +2616,7 @@
 	int		bytes_left, bytes, num_frags;
 	int		sz, ii;
 
-	/* cached_fw 
+	/* cached_fw
 	 */
 	sz = ioc->num_fw_frags * sizeof(void *);
 	mem = kmalloc(sz, GFP_ATOMIC);
@@ -2737,8 +2773,8 @@
 	ioc->num_fw_frags = ioc->req_sz - sizeof(FWUpload_t) + sizeof(dma_addr_t) + sizeof(u32) -1;
 	ioc->num_fw_frags /= sizeof(dma_addr_t) + sizeof(u32);
 
-	ioc->cached_fw = (fw_image_t **) mpt_alloc_fw_memory(ioc, 
-			ioc->facts.FWImageSize, &num_frags, &alloc_sz); 
+	ioc->cached_fw = (fw_image_t **) mpt_alloc_fw_memory(ioc,
+			ioc->facts.FWImageSize, &num_frags, &alloc_sz);
 
 	if (ioc->cached_fw == NULL) {
 		/* Major Failure.
@@ -2785,8 +2821,8 @@
 		sgeoffset += sizeof(u32) + sizeof(dma_addr_t);
 	}
 
-	mpt_add_sge(&request[sgeoffset], 
-			MPT_SGE_FLAGS_SSIMPLE_READ |(u32) ioc->cached_fw[ii]->size, 
+	mpt_add_sge(&request[sgeoffset],
+			MPT_SGE_FLAGS_SSIMPLE_READ |(u32) ioc->cached_fw[ii]->size,
 			ioc->cached_fw[ii]->fw_dma);
 
 	sgeoffset += sizeof(u32) + sizeof(dma_addr_t);
@@ -3133,8 +3169,8 @@
  *		 0 - no reset due to History bit, READY	
  *		-1 - no reset due to History bit but not READY	
  *		     OR reset but failed to come READY
- *		-2 - no reset, could not enter DIAG mode 
- *		-3 - reset but bad FW bit 
+ *		-2 - no reset, could not enter DIAG mode
+ *		-3 - reset but bad FW bit
  */
 static int
 KickStart(MPT_ADAPTER *ioc, int force, int sleepFlag)
@@ -3277,7 +3313,7 @@
 
 		/*
 		 * Disable the ARM (Bug fix)
-		 * 
+		 *
 		 */
 		CHIPREG_WRITE32(&ioc->chip->Diagnostic, diag0val | MPI_DIAG_DISABLE_ARM);
 		mdelay (1);
@@ -3326,7 +3362,7 @@
 #ifdef MPT_DEBUG
 				if (ioc->alt_ioc)
 					diag1val = CHIPREG_READ32(&ioc->alt_ioc->chip->Diagnostic);
-				dprintk((MYIOC_s_INFO_FMT 
+				dprintk((MYIOC_s_INFO_FMT
 					"DbG2b: diag0=%08x, diag1=%08x\n",
 					ioc->name, diag0val, diag1val));
 #endif
@@ -3343,18 +3379,18 @@
 				}
 			}
 			if ((count = mpt_downloadboot(ioc, sleepFlag)) < 0) {
-				printk(KERN_WARNING MYNAM 
+				printk(KERN_WARNING MYNAM
 					": firmware downloadboot failure (%d)!\n", count);
 			}
 
 		} else {
 			/* Wait for FW to reload and for board
 			 * to go to the READY state.
-			 * Maximum wait is 30 seconds.
+			 * Maximum wait is 60 seconds.
 			 * If fail, no error will check again
 			 * with calling program.
 			 */
-			for (count = 0; count < 30; count ++) {
+			for (count = 0; count < 60; count ++) {
 				doorbell = CHIPREG_READ32(&ioc->chip->Doorbell);
 				doorbell &= MPI_IOC_STATE_MASK;
 
@@ -3472,15 +3508,13 @@
 	dprintk((KERN_WARNING MYNAM ": %s: Sending IOC reset(0x%02x)!\n",
 			ioc->name, reset_type));
 	CHIPREG_WRITE32(&ioc->chip->Doorbell, reset_type<<MPI_DOORBELL_FUNCTION_SHIFT);
-	if ((r = WaitForDoorbellAck(ioc, 2, sleepFlag)) < 0)
+	if ((r = WaitForDoorbellAck(ioc, 4, sleepFlag)) < 0)
 		return r;
 
-	/* FW ACK'd request, wait for READY state 
+	/* FW ACK'd request, wait for READY state
 	 */
-	cntdn = HZ * 15;
 	count = 0;
-	if (sleepFlag != CAN_SLEEP)
-		cntdn *= 10;	/* 1500 iterations @ 1msec per */
+	cntdn = ((sleepFlag == CAN_SLEEP) ? HZ : 1000) * 15;	/* 15 seconds */
 
 	while ((state = mpt_GetIocState(ioc, 1)) != MPI_IOC_STATE_READY) {
 		cntdn--;
@@ -3756,7 +3790,7 @@
 	 * our handshake request.
 	 */
 	CHIPREG_WRITE32(&ioc->chip->IntStatus, 0);
-	if (!failcnt && (t = WaitForDoorbellAck(ioc, 2, sleepFlag)) < 0)
+	if (!failcnt && (t = WaitForDoorbellAck(ioc, 4, sleepFlag)) < 0)
 		failcnt++;
 
 	if (!failcnt) {
@@ -3774,7 +3808,7 @@
 				    (req_as_bytes[(ii*4) + 3] << 24));
 
 			CHIPREG_WRITE32(&ioc->chip->Doorbell, word);
-			if ((t = WaitForDoorbellAck(ioc, 2, sleepFlag)) < 0)
+			if ((t = WaitForDoorbellAck(ioc, 4, sleepFlag)) < 0)
 				failcnt++;
 		}
 
@@ -3818,10 +3852,12 @@
 static int
 WaitForDoorbellAck(MPT_ADAPTER *ioc, int howlong, int sleepFlag)
 {
-	int cntdn = HZ * howlong;
+	int cntdn;
 	int count = 0;
 	u32 intstat;
 
+	cntdn = ((sleepFlag == CAN_SLEEP) ? HZ : 1000) * howlong;
+
 	if (sleepFlag == CAN_SLEEP) {
 		while (--cntdn) {
 			intstat = CHIPREG_READ32(&ioc->chip->IntStatus);
@@ -3832,7 +3868,6 @@
 			count++;
 		}
 	} else {
-		cntdn *= 10; /* convert to msec */
 		while (--cntdn) {
 			intstat = CHIPREG_READ32(&ioc->chip->IntStatus);
 			if (! (intstat & MPI_HIS_IOP_DOORBELL_STATUS))
@@ -3840,7 +3875,6 @@
 			mdelay (1);
 			count++;
 		}
-		count /= 10;
 	}
 
 	if (cntdn) {
@@ -3869,10 +3903,11 @@
 static int
 WaitForDoorbellInt(MPT_ADAPTER *ioc, int howlong, int sleepFlag)
 {
-	int cntdn = HZ * howlong;
+	int cntdn;
 	int count = 0;
 	u32 intstat;
 
+	cntdn = ((sleepFlag == CAN_SLEEP) ? HZ : 1000) * howlong;
 	if (sleepFlag == CAN_SLEEP) {
 		while (--cntdn) {
 			intstat = CHIPREG_READ32(&ioc->chip->IntStatus);
@@ -3883,7 +3918,6 @@
 			count++;
 		}
 	} else {
-		cntdn *= 10; /* convert to msec */
 		while (--cntdn) {
 			intstat = CHIPREG_READ32(&ioc->chip->IntStatus);
 			if (intstat & MPI_HIS_DOORBELL_INTERRUPT)
@@ -3891,7 +3925,6 @@
 			mdelay(1);
 			count++;
 		}
-		count /= 10;
 	}
 
 	if (cntdn) {
@@ -4275,7 +4308,7 @@
 	int			 ii;
 	int			 data, rc = 0;
 
-	/* Allocate memory 
+	/* Allocate memory
 	 */
 	if (!ioc->spi_data.nvram) {
 		int	 sz;
@@ -4454,12 +4487,17 @@
 	ioc->spi_data.sdp0version = cfg.hdr->PageVersion;
 	ioc->spi_data.sdp0length = cfg.hdr->PageLength;
 
+	dcprintk((MYIOC_s_INFO_FMT "Headers: 0: version %d length %d\n",
+			ioc->name, ioc->spi_data.sdp0version, ioc->spi_data.sdp0length));
+
+	dcprintk((MYIOC_s_INFO_FMT "Headers: 1: version %d length %d\n",
+			ioc->name, ioc->spi_data.sdp1version, ioc->spi_data.sdp1length));
 	return 0;
 }
 
 /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
 /**
- *	mpt_findImVolumes - Identify IDs of hidden disks and RAID Volumes 
+ *	mpt_findImVolumes - Identify IDs of hidden disks and RAID Volumes
  *	@ioc: Pointer to a Adapter Strucutre
  *	@portnum: IOC port number
  *
@@ -4802,7 +4840,7 @@
 	 */
 	in_isr = in_interrupt();
 	if (in_isr) {
-		dprintk((MYIOC_s_WARN_FMT "Config request not allowed in ISR context!\n",
+		dcprintk((MYIOC_s_WARN_FMT "Config request not allowed in ISR context!\n",
 				ioc->name));
 		return -EPERM;
 	}
@@ -4810,7 +4848,7 @@
 	/* Get and Populate a free Frame
 	 */
 	if ((mf = mpt_get_msg_frame(mpt_base_index, ioc->id)) == NULL) {
-		dprintk((MYIOC_s_WARN_FMT "mpt_config: no msg frames!\n",
+		dcprintk((MYIOC_s_WARN_FMT "mpt_config: no msg frames!\n",
 				ioc->name));
 		return -EAGAIN;
 	}
@@ -4843,7 +4881,7 @@
 
 	mpt_add_sge((char *)&pReq->PageBufferSGE, flagsLength, pCfg->physAddr);
 
-	dprintk((MYIOC_s_INFO_FMT "Sending Config request type %d, page %d and action %d\n",
+	dcprintk((MYIOC_s_INFO_FMT "Sending Config request type %d, page %d and action %d\n",
 		ioc->name, pReq->Header.PageType, pReq->Header.PageNumber, pReq->Action));
 
 	/* Append pCfg pointer to end of mf
@@ -4890,7 +4928,7 @@
 {
 	MPT_ADAPTER *ioc = (MPT_ADAPTER *) data;
 
-	dprintk((MYIOC_s_WARN_FMT "mpt_timer_expired! \n", ioc->name));
+	dcprintk((MYIOC_s_WARN_FMT "mpt_timer_expired! \n", ioc->name));
 
 	/* Perform a FW reload */
 	if (mpt_HardResetHandler(ioc, NO_SLEEP) < 0)
@@ -4900,7 +4938,7 @@
 	 * Hard reset clean-up will wake up
 	 * process and free all resources.
 	 */
-	dprintk((MYIOC_s_WARN_FMT "mpt_timer_expired complete!\n", ioc->name));
+	dcprintk((MYIOC_s_WARN_FMT "mpt_timer_expired complete!\n", ioc->name));
 
 	return;
 }
@@ -4921,9 +4959,12 @@
 
 	dprintk((KERN_WARNING MYNAM
 			": IOC %s_reset routed to MPT base driver!\n",
-			reset_phase==MPT_IOC_PRE_RESET ? "pre" : "post"));
+			reset_phase==MPT_IOC_SETUP_RESET ? "setup" : (
+			reset_phase==MPT_IOC_PRE_RESET ? "pre" : "post")));
 
-	if (reset_phase == MPT_IOC_PRE_RESET) {
+	if (reset_phase == MPT_IOC_SETUP_RESET) {
+		;
+	} else if (reset_phase == MPT_IOC_PRE_RESET) {
 		/* If the internal config Q is not empty -
 		 * delete timer. MF resources will be freed when
 		 * the FIFO's are primed.
@@ -4941,10 +4982,9 @@
 	} else {
 		CONFIGPARMS *pNext;
 
-		/* Search the configQ for internal commands. 
+		/* Search the configQ for internal commands.
 		 * Flush the Q, and wake up all suspended threads.
 		 */
-#if 1
 		spin_lock_irqsave(&ioc->FreeQlock, flags);
 		if (! Q_IS_EMPTY(&ioc->configQ)){
 			pCfg = (CONFIGPARMS *)ioc->configQ.head;
@@ -4961,23 +5001,6 @@
 			} while (pCfg != (CONFIGPARMS *)&ioc->configQ);
 		}
 		spin_unlock_irqrestore(&ioc->FreeQlock, flags);
-#else
-		while (1) {
-			spin_lock_irqsave(&ioc->FreeQlock, flags);
-			if (! Q_IS_EMPTY(&ioc->configQ)){
-				spin_unlock_irqrestore(&ioc->FreeQlock, flags);
-				break;
-			}
-			pCfg = (CONFIGPARMS *)ioc->configQ.head;
-
-			Q_DEL_ITEM(&pCfg->linkage);
-			spin_unlock_irqrestore(&ioc->FreeQlock, flags);
-
-			pCfg->status = MPT_CONFIG_ERROR;
-			pCfg->wait_done = 1;
-			wake_up(&mpt_waitq);
-		}
-#endif
 	}
 
 	return 1;		/* currently means nothing really */
@@ -5425,6 +5448,29 @@
 
 	/* FIXME: If do_ioc_recovery fails, repeat....
 	 */
+	
+	/* The SCSI driver needs to adjust timeouts on all current
+	 * commands prior to the diagnostic reset being issued.
+	 * Prevents timeouts occuring during a diagnostic reset...very bad.
+	 * For all other protocol drivers, this is a no-op.
+	 */
+	{
+		int	 ii;
+		int	 r = 0;
+
+		for (ii=MPT_MAX_PROTOCOL_DRIVERS-1; ii; ii--) {
+			if (MptResetHandlers[ii]) {
+				dprintk((MYIOC_s_INFO_FMT "Calling IOC reset_setup handler #%d\n",
+						ioc->name, ii));
+				r += (*(MptResetHandlers[ii]))(ioc, MPT_IOC_SETUP_RESET);
+				if (ioc->alt_ioc) {
+					dprintk((MYIOC_s_INFO_FMT "Calling alt-%s setup reset handler #%d\n",
+							ioc->name, ioc->alt_ioc->name, ii));
+					r += (*(MptResetHandlers[ii]))(ioc->alt_ioc, MPT_IOC_SETUP_RESET);
+				}
+			}
+		}
+	}
 
 	if ((rc = mpt_do_ioc_recovery(ioc, MPT_HOSTEVENT_IOC_RECOVER, sleepFlag)) != 0) {
 		printk(KERN_WARNING MYNAM ": WARNING - (%d) Cannot recover %s\n",
@@ -5836,7 +5882,7 @@
 		isense_idx = last_drv_idx;
 		r = 1;
 	}
-	MOD_INC_USE_COUNT;
+	mpt_inc_use_count();
 	return r;
 }
 
@@ -5855,7 +5901,7 @@
 	mpt_ScsiOpcodesPtr = NULL;
 	printk(KERN_INFO MYNAM ": English readable SCSI-3 strings disabled)-:\n");
 	isense_idx = -1;
-	MOD_DEC_USE_COUNT;
+	mpt_dec_use_count();
 }
 
 /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
diff -urN linux-2.4.22-pre3/drivers/message/fusion/mptbase.h linux-2.4.22-pre4/drivers/message/fusion/mptbase.h
--- linux-2.4.22-pre3/drivers/message/fusion/mptbase.h	2003-07-09 16:56:24.000000000 -0700
+++ linux-2.4.22-pre4/drivers/message/fusion/mptbase.h	2003-07-09 16:56:28.000000000 -0700
@@ -11,9 +11,9 @@
  *  Copyright (c) 1999-2002 LSI Logic Corporation
  *  Originally By: Steven J. Ralston
  *  (mailto:sjralston1@netscape.net)
- *  (mailto:lstephen@lsil.com)
+ *  (mailto:lstephens@lsil.com)
  *
- *  $Id: mptbase.h,v 1.144 2003/01/28 21:31:56 pdelaney Exp $
+ *  $Id: mptbase.h,v 1.149 2003/05/07 14:08:31 pdelaney Exp $
  */
 /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
 /*
@@ -80,8 +80,8 @@
 #define COPYRIGHT	"Copyright (c) 1999-2002 " MODULEAUTHOR
 #endif
 
-#define MPT_LINUX_VERSION_COMMON	"2.05.00fix"
-#define MPT_LINUX_PACKAGE_NAME		"@(#)mptlinux-2.05.00fix"
+#define MPT_LINUX_VERSION_COMMON	"2.05.05+"
+#define MPT_LINUX_PACKAGE_NAME		"@(#)mptlinux-2.05.05+"
 #define WHAT_MAGIC_STRING		"@" "(" "#" ")"
 
 #define show_mptmod_ver(s,ver)  \
@@ -93,7 +93,7 @@
  */
 #define MPT_MAX_ADAPTERS		18
 #define MPT_MAX_PROTOCOL_DRIVERS	16
-#define MPT_MAX_BUS			1
+#define MPT_MAX_BUS			1	/* Do not change */
 #define MPT_MAX_FC_DEVICES		255
 #define MPT_MAX_SCSI_DEVICES		16
 #define MPT_LAST_LUN			31
@@ -383,12 +383,9 @@
 	u8			 maxWidth;	/* 0 if narrow, 1 if wide*/
 	u8			 negoFlags;	/* bit field, 0 if WDTR/SDTR/QAS allowed */
 	u8			 raidVolume;	/* set, if RAID Volume */
-#ifdef ABORT_FIX
-	u8			 numAborts;
-#else
-	u8			 rsvd;
-#endif
-	u16			 rsvd1raid;
+	u8			 type;		/* byte 0 of Inquiry data */
+	u8			 cflags;	/* controller flags */
+	u8			 rsvd1raid;
 	int			 npaths;
 	u16			 fc_phys_lun;
 	u16			 fc_xlat_lun;
@@ -400,11 +397,8 @@
 	ScsiCmndTracker		 WaitQ;
 	ScsiCmndTracker		 SentQ;
 	ScsiCmndTracker		 DoneQ;
+	u32			 num_luns;
 //--- LUN split here?
-#ifdef MPT_SAVE_AUTOSENSE
-	u8			 sense[SCSI_STD_SENSE_BYTES];		/* 18 */
-	u8			 rsvd2[2];	/* alignment */
-#endif
 	u32			 luns;		/* Max LUNs is 32 */
 	u8			 inq_data[SCSI_STD_INQUIRY_BYTES];	/* 36 */
 	u8			 pad0[4];
@@ -428,13 +422,15 @@
  *  Fibre Channel (SCSI) target device and associated defines...
  */
 #define MPT_TARGET_DEFAULT_DV_STATUS	0
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,55)
+#define MPT_TARGET_FLAGS_CONFIGURED	0x02
+#define MPT_TARGET_FLAGS_Q_YES		0x08
+#else
 #define MPT_TARGET_FLAGS_VALID_NEGO	0x01
 #define MPT_TARGET_FLAGS_VALID_INQUIRY	0x02
-#ifdef MPT_SAVE_AUTOSENSE
-#define MPT_TARGET_FLAGS_VALID_SENSE	0x04
-#endif
 #define MPT_TARGET_FLAGS_Q_YES		0x08
 #define MPT_TARGET_FLAGS_VALID_56	0x10
+#endif
 
 #define MPT_TARGET_NO_NEGO_WIDE		0x01
 #define MPT_TARGET_NO_NEGO_SYNC		0x02
@@ -603,7 +599,7 @@
 	dma_addr_t		 sense_buf_pool_dma;
 	u32			 sense_buf_low_dma;
 	int			 mtrr_reg;
-	void			*pcidev;	/* struct pci_dev pointer */
+	struct pci_dev		*pcidev;	/* struct pci_dev pointer */
 	u8			*memmap;	/* mmap address */
 	struct Scsi_Host	*sh;		/* Scsi Host pointer */
 	ScsiCfgData		spi_data;	/* Scsi config. data */
@@ -616,6 +612,12 @@
 	int			 eventTypes;	/* Event logging parameters */
 	int			 eventContext;	/* Next event context */
 	int			 eventLogSize;	/* Max number of cached events */
+#ifdef MPTSCSIH_DBG_TIMEOUT
+	int			timeout_hard;
+	int			timeout_delta;
+	int			timeout_cnt;
+	int			timeout_maxcnt;
+#endif
 	struct _mpt_ioctl_events *events;	/* pointer to event log */
 	fw_image_t		**cached_fw;	/* Pointer to FW SG List */
 	Q_TRACKER		 configQ;	/* linked list of config. requests */
@@ -656,6 +658,7 @@
 /* reset_phase defs */
 #define MPT_IOC_PRE_RESET		0
 #define MPT_IOC_POST_RESET		1
+#define MPT_IOC_SETUP_RESET		2
 
 /*
  * Invent MPT host event (super-set of MPI Events)
@@ -758,6 +761,19 @@
 #define nehprintk(x)
 #endif
 
+#if defined(MPT_DEBUG_CONFIG) || defined(MPT_DEBUG)
+#define dcprintk(x) printk x
+#else
+#define dcprintk(x)
+#endif
+
+#if defined(MPT_DEBUG_SCSI) || defined(MPT_DEBUG) || defined(MPT_DEBUG_MSG_FRAME)
+#define dsprintk(x) printk x
+#else
+#define dsprintk(x)
+#endif
+
+
 #define MPT_INDEX_2_MFPTR(ioc,idx) \
 	(MPT_FRAME_HDR*)( (u8*)(ioc)->req_frames + (ioc)->req_sz * (idx) )
 
diff -urN linux-2.4.22-pre3/drivers/message/fusion/mptctl.c linux-2.4.22-pre4/drivers/message/fusion/mptctl.c
--- linux-2.4.22-pre3/drivers/message/fusion/mptctl.c	2003-07-09 16:56:24.000000000 -0700
+++ linux-2.4.22-pre4/drivers/message/fusion/mptctl.c	2003-07-09 16:56:28.000000000 -0700
@@ -32,9 +32,9 @@
  *  Copyright (c) 1999-2002 LSI Logic Corporation
  *  Originally By: Steven J. Ralston, Noah Romer
  *  (mailto:sjralston1@netscape.net)
- *  (mailto:lstephen@lsil.com)
+ *  (mailto:lstephens@lsil.com)
  *
- *  $Id: mptctl.c,v 1.63 2002/12/03 21:26:33 pdelaney Exp $
+ *  $Id: mptctl.c,v 1.66 2003/05/07 14:08:32 pdelaney Exp $
  */
 /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
 /*
@@ -102,7 +102,9 @@
 #define my_VERSION	MPT_LINUX_VERSION_COMMON
 #define MYNAM		"mptctl"
 
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,62)
 EXPORT_NO_SYMBOLS;
+#endif
 MODULE_AUTHOR(MODULEAUTHOR);
 MODULE_DESCRIPTION(my_NAME);
 MODULE_LICENSE("GPL");
@@ -136,7 +138,7 @@
 static int mptctl_replace_fw (unsigned long arg);
 
 static int mptctl_do_reset(unsigned long arg);
-static int mptctl_hp_hostinfo(unsigned long arg);
+static int mptctl_hp_hostinfo(unsigned long arg, unsigned int cmd);
 static int mptctl_hp_targetinfo(unsigned long arg);
 
 /*
@@ -267,8 +269,12 @@
 			 */
 			iocStatus = reply->u.reply.IOCStatus & MPI_IOCSTATUS_MASK;
 			if (iocStatus == MPI_IOCSTATUS_SCSI_TASK_MGMT_FAILED) {
-				if (ioc->ioctl->status & MPT_IOCTL_STATUS_TIMER_ACTIVE)
-					mod_timer(&ioc->ioctl->timer, jiffies + HZ);
+				if (ioc->ioctl->status & MPT_IOCTL_STATUS_TIMER_ACTIVE) {
+					ioc->ioctl->reset &= ~MPTCTL_RESET_OK;
+					del_timer (&ioc->ioctl->timer);
+					ioc->ioctl->timer.expires = jiffies + HZ;
+					add_timer(&ioc->ioctl->timer);
+				}
 			}
 			ioc->ioctl->tmPtr = NULL;
 
@@ -334,6 +340,7 @@
 			 */
 			ioc->ioctl->wait_done = 1;
 			wake_up (&mptctl_wait);
+
 		} else if (reply && cmd == MPI_FUNCTION_FW_DOWNLOAD) {
 			/* Two paths to FW DOWNLOAD! */
 			// NOTE: Expects/requires non-Turbo reply!
@@ -518,9 +525,12 @@
 {
 	MPT_IOCTL *ioctl = ioc->ioctl;
 	dctlprintk((KERN_INFO MYNAM ": IOC %s_reset routed to IOCTL driver!\n",
-			reset_phase==MPT_IOC_PRE_RESET ? "pre" : "post"));
+			reset_phase==MPT_IOC_SETUP_RESET ? "setup" : (
+			reset_phase==MPT_IOC_PRE_RESET ? "pre" : "post")));
 
-	if (reset_phase == MPT_IOC_PRE_RESET){
+	if (reset_phase == MPT_IOC_SETUP_RESET){
+		;
+	} else if (reset_phase == MPT_IOC_PRE_RESET){
 
 		/* Someone has called the reset handler to
 		 * do a hard reset. No more replies from the FW.
@@ -537,13 +547,15 @@
 		}
 
 	} else {
+		ioctl->tmPtr = NULL;
+
 		/* Set the status and continue IOCTL
 		 * processing. All memory will be free'd
 		 * by originating thread after wake_up is
 		 * called.
 		 */
 		if (ioctl && (ioctl->status & MPT_IOCTL_STATUS_TIMER_ACTIVE)){
-			ioctl->status = MPT_IOCTL_STATUS_DID_IOCRESET;
+			ioctl->status |= MPT_IOCTL_STATUS_DID_IOCRESET;
 
 			/* Wake up the calling process
 			 */
@@ -625,7 +637,11 @@
 		return -ENODEV;
 	}
 
-
+	if (!iocp->active) {
+		printk(KERN_ERR "%s::mptctl_ioctl() @%d - Controller disabled.\n",
+				__FILE__, __LINE__);
+		return -EFAULT;
+	}
 
 	/* Handle those commands that are just returning
 	 * information stored in the driver.
@@ -656,25 +672,19 @@
 
 	dctlprintk((MYIOC_s_INFO_FMT ": mptctl_ioctl()\n", iocp->name));
 
-	switch(cmd) {
-	case MPTFWDOWNLOAD:
+	if (cmd == MPTFWDOWNLOAD)
 		ret = mptctl_fw_download(arg);
-		break;
-	case MPTCOMMAND:
+	else if (cmd == MPTCOMMAND)
 		ret = mptctl_mpt_command(arg);
-		break;
-	case MPTHARDRESET:
+	else if (cmd == MPTHARDRESET)
 		ret = mptctl_do_reset(arg);
-		break;
-	case HP_GETHOSTINFO:
-		ret = mptctl_hp_hostinfo(arg);
-		break;
-	case HP_GETTARGETINFO:
+	else if ((cmd & ~IOCSIZE_MASK) == (HP_GETHOSTINFO & ~IOCSIZE_MASK))
+		ret = mptctl_hp_hostinfo(arg, _IOC_SIZE(cmd));
+	else if (cmd == HP_GETTARGETINFO)
 		ret = mptctl_hp_targetinfo(arg);
-		break;
-	default:
+	else
 		ret = -EINVAL;
-	}
+
 
 	up(&mptctl_syscall_sem_ioc[iocp->id]);
 
@@ -702,7 +712,7 @@
 		return -ENODEV; /* (-6) No such device or address */
 	}
 
-	if (mpt_HardResetHandler(iocp, NO_SLEEP) != 0) {
+	if (mpt_HardResetHandler(iocp, CAN_SLEEP) != 0) {
 		printk (KERN_ERR "%s@%d::mptctl_do_reset - reset failed.\n",
 			__FILE__, __LINE__);
 		return -1;
@@ -871,7 +881,7 @@
 	 *	96		8
 	 *	64		4
 	 */
-	maxfrags = (iocp->req_sz - sizeof(MPIHeader_t) - sizeof(FWDownloadTCSGE_t)) 
+	maxfrags = (iocp->req_sz - sizeof(MPIHeader_t) - sizeof(FWDownloadTCSGE_t))
 			/ (sizeof(dma_addr_t) + sizeof(u32));
 	if (numfrags > maxfrags) {
 		ret = -EMLINK;
@@ -1225,10 +1235,16 @@
 	u8			revision;
 
 	dctlprintk((": mptctl_getiocinfo called.\n"));
-	if (data_size == sizeof(struct mpt_ioctl_iocinfo))
-		cim_rev = 1;
-	else if (data_size == (sizeof(struct mpt_ioctl_iocinfo) - sizeof(struct mpt_ioctl_pci_info)))
+	/* Add of PCI INFO results in unaligned access for
+	 * IA64 and Sparc. Reset long to int. Return no PCI
+	 * data for obsolete format.
+	 */
+	if (data_size == sizeof(struct mpt_ioctl_iocinfo_rev0))
 		cim_rev = 0;
+	else if (data_size == sizeof(struct mpt_ioctl_iocinfo))
+		cim_rev = 1;
+	else if (data_size == (sizeof(struct mpt_ioctl_iocinfo_rev0)+12))
+		cim_rev = 0;	/* obsolete */
 	else
 		return -EFAULT;
 
@@ -1412,7 +1428,7 @@
 
 	/* Get number of devices
          */
-	if ( (sh = ioc->sh) != NULL) {
+	if ((sh = ioc->sh) != NULL) {
 
 		max_id = sh->max_id - 1;
 		hd = (MPT_SCSI_HOST *) sh->hostdata;
@@ -1435,9 +1451,8 @@
 
 							pdata++;
 
-							if (maxWordsLeft <= 0) {
+							if (maxWordsLeft <= 0)
 								break;
-							}
 						}
 					}
 				}
@@ -1710,7 +1725,7 @@
 	/* Allocate memory for the new FW image
 	 */
 	newFwSize = karg.newImageSize;
-	fwmem = mpt_alloc_fw_memory(ioc, newFwSize, &num_frags, &alloc_sz); 
+	fwmem = mpt_alloc_fw_memory(ioc, newFwSize, &num_frags, &alloc_sz);
 	if (fwmem == NULL)
 		return -ENOMEM;
 
@@ -1730,7 +1745,7 @@
 	}
 
 
-	/* Free the old FW image 
+	/* Free the old FW image
 	 */
 	if (ioc->cached_fw) {
 		mpt_free_fw_memory(ioc, 0);
@@ -1812,22 +1827,20 @@
 	MPT_FRAME_HDR	*mf = NULL;
 	MPIHeader_t	*hdr;
 	char		*psge;
-	MptSge_t	*this_sge = NULL;
-	MptSge_t	*sglbuf = NULL;
 	struct buflist	bufIn;	/* data In buffer */
 	struct buflist	bufOut; /* data Out buffer */
-	dma_addr_t	sglbuf_dma;
-	dma_addr_t	dma_addr;
+	dma_addr_t	dma_addr_in;
+	dma_addr_t	dma_addr_out;
 	int		dir;	/* PCI data direction */
 	int		sgSize = 0;	/* Num SG elements */
-	int		this_alloc;
-	int		 iocnum, flagsLength;
-	int		 sz, rc = 0;
-	int		 msgContext;
+	int		iocnum, flagsLength;
+	int		sz, rc = 0;
+	int		msgContext;
 	int		tm_flags_set = 0;
 	u16		req_idx;
 
 	dctlprintk(("mptctl_do_mpt_command called.\n"));
+	bufIn.kptr = bufOut.kptr = NULL;
 
 	if (((iocnum = mpt_verify_adapter(karg.hdr.iocnum, &ioc)) < 0) ||
 	    (ioc == NULL)) {
@@ -1854,7 +1867,7 @@
 	if (karg.dataOutSize > 0)
 		sz += sizeof(dma_addr_t) + sizeof(u32);
 
-	if ( sz > ioc->req_sz) {
+	if (sz > ioc->req_sz) {
 		printk(KERN_ERR "%s@%d::mptctl_do_mpt_command - "
 			"Request frame too large (%d) maximum (%d)\n",
 				__FILE__, __LINE__, sz, ioc->req_sz);
@@ -1897,12 +1910,16 @@
 	switch (hdr->Function) {
 	case MPI_FUNCTION_IOC_FACTS:
 	case MPI_FUNCTION_PORT_FACTS:
+		karg.dataOutSize  = karg.dataInSize = 0;
+		break;
+
 	case MPI_FUNCTION_CONFIG:
 	case MPI_FUNCTION_FC_COMMON_TRANSPORT_SEND:
 	case MPI_FUNCTION_FC_EX_LINK_SRVC_SEND:
 	case MPI_FUNCTION_FW_UPLOAD:
 	case MPI_FUNCTION_SCSI_ENCLOSURE_PROCESSOR:
 	case MPI_FUNCTION_FW_DOWNLOAD:
+	case MPI_FUNCTION_FC_PRIMITIVE_SEND:
 		break;
 
 	case MPI_FUNCTION_SCSI_IO_REQUEST:
@@ -1938,7 +1955,7 @@
 				cpu_to_le32(ioc->sense_buf_low_dma
 				   + (req_idx * MPT_SENSE_BUFFER_ALLOC));
 
-			if ( (hd = (MPT_SCSI_HOST *) ioc->sh->hostdata)) {
+			if ((hd = (MPT_SCSI_HOST *) ioc->sh->hostdata)) {
 				if (hd->Targets)
 					pTarget = hd->Targets[target];
 			}
@@ -1949,11 +1966,10 @@
 			/* Have the IOCTL driver set the direction based
 			 * on the dataOutSize (ordering issue with Sparc).
 			 */
-			if (karg.dataOutSize > 0 ) {
+			if (karg.dataOutSize > 0) {
 				scsidir = MPI_SCSIIO_CONTROL_WRITE;
 				dataSize = karg.dataOutSize;
-			}
-			else {
+			} else {
 				scsidir = MPI_SCSIIO_CONTROL_READ;
 				dataSize = karg.dataInSize;
 			}
@@ -2006,11 +2022,10 @@
 			/* Have the IOCTL driver set the direction based
 			 * on the dataOutSize (ordering issue with Sparc).
 			 */
-			if (karg.dataOutSize > 0 ) {
+			if (karg.dataOutSize > 0) {
 				scsidir = MPI_SCSIIO_CONTROL_WRITE;
 				dataSize = karg.dataOutSize;
-			}
-			else {
+			} else {
 				scsidir = MPI_SCSIIO_CONTROL_READ;
 				dataSize = karg.dataInSize;
 			}
@@ -2030,7 +2045,7 @@
 		break;
 
 	case MPI_FUNCTION_SCSI_TASK_MGMT:
-		{ 
+		{
 			MPT_SCSI_HOST *hd = NULL;
 			if ((ioc->sh == NULL) || ((hd = (MPT_SCSI_HOST *)ioc->sh->hostdata) == NULL)) {
 				printk(KERN_ERR "%s@%d::mptctl_do_mpt_command - "
@@ -2038,7 +2053,7 @@
 					__FILE__, __LINE__);
 				rc = -EFAULT;
 				goto done_free_mem;
-			}  else if (mptctl_set_tm_flags(hd) != 0) {
+			} else if (mptctl_set_tm_flags(hd) != 0) {
 				rc = -EPERM;
 				goto done_free_mem;
 			}
@@ -2063,7 +2078,7 @@
 			}
 
 			if ((pInit->Flags != 0) || (pInit->MaxDevices != ioc->facts.MaxDevices) ||
-				(pInit->MaxBuses != ioc->facts.MaxBuses) || 
+				(pInit->MaxBuses != ioc->facts.MaxBuses) ||
 				(pInit->ReplyFrameSize != cpu_to_le16(ioc->reply_sz)) ||
 				(pInit->HostMfaHighAddr != high_addr) ||
 				(pInit->SenseBufferHighAddr != sense_high)) {
@@ -2095,7 +2110,6 @@
 			MPI_FUNCTION_FC_LINK_SRVC_BUF_POST
 			MPI_FUNCTION_FC_LINK_SRVC_RSP
 			MPI_FUNCTION_FC_ABORT
-			MPI_FUNCTION_FC_PRIMITIVE_SEND
 			MPI_FUNCTION_LAN_SEND
 			MPI_FUNCTION_LAN_RECEIVE
 		 	MPI_FUNCTION_LAN_RESET
@@ -2113,7 +2127,7 @@
 	 * preceede the data in (read) SGE. psgList is used to free the
 	 * allocated memory.
 	 */
-	psge = (char *) ( ((int *) mf) + karg.dataSgeOffset);
+	psge = (char *) (((int *) mf) + karg.dataSgeOffset);
 	flagsLength = 0;
 
 	/* bufIn and bufOut are used for user to kernel space transfers
@@ -2121,30 +2135,18 @@
 	bufIn.kptr = bufOut.kptr = NULL;
 	bufIn.len = bufOut.len = 0;
 
-	if (karg.dataOutSize > 0 )
+	if (karg.dataOutSize > 0)
 		sgSize ++;
 
-	if (karg.dataInSize > 0 )
+	if (karg.dataInSize > 0)
 		sgSize ++;
 
 	if (sgSize > 0) {
 
-		/* Allocate memory for the SGL.
-		 * Used to free kernel memory once
-		 * the MF is freed.
-		 */
-		sglbuf = pci_alloc_consistent (ioc->pcidev,
-			sgSize*sizeof(MptSge_t), &sglbuf_dma);
-		if (sglbuf == NULL) {
-			rc = -ENOMEM;
-			goto done_free_mem;
-		}
-		this_sge = sglbuf;
-
 		/* Set up the dataOut memory allocation */
 		if (karg.dataOutSize > 0) {
 			dir = PCI_DMA_TODEVICE;
-			if (karg.dataInSize > 0 ) {
+			if (karg.dataInSize > 0) {
 				flagsLength = ( MPI_SGE_FLAGS_SIMPLE_ELEMENT |
 						MPI_SGE_FLAGS_DIRECTION |
 						mpt_addr_size() )
@@ -2153,22 +2155,25 @@
 				flagsLength = MPT_SGE_FLAGS_SSIMPLE_WRITE;
 			}
 			flagsLength |= karg.dataOutSize;
-
-			this_alloc = karg.dataOutSize;
-			bufOut.len = this_alloc;
+			bufOut.len = karg.dataOutSize;
 			bufOut.kptr = pci_alloc_consistent(
-					ioc->pcidev, this_alloc, &dma_addr);
+					ioc->pcidev, bufOut.len, &dma_addr_out);
 
 			if (bufOut.kptr == NULL) {
 				rc = -ENOMEM;
 				goto done_free_mem;
 			} else {
+				/* Set up this SGE.
+				 * Copy to MF and to sglbuf
+				 */
+				mpt_add_sge(psge, flagsLength, dma_addr_out);
+				psge += (sizeof(u32) + sizeof(dma_addr_t));
+
 				/* Copy user data to kernel space.
 				 */
 				if (copy_from_user(bufOut.kptr,
 						karg.dataOutBufPtr,
 						bufOut.len)) {
-
 					printk(KERN_ERR
 						"%s@%d::mptctl_do_mpt_command - Unable "
 						"to read user data "
@@ -2177,16 +2182,6 @@
 					rc =  -EFAULT;
 					goto done_free_mem;
 				}
-
-				/* Set up this SGE.
-				 * Copy to MF and to sglbuf
-				 */
-				mpt_add_sge(psge, flagsLength, dma_addr);
-				psge += (sizeof(u32) + sizeof(dma_addr_t));
-
-				this_sge->FlagsLength = flagsLength;
-				this_sge->Address = dma_addr;
-				this_sge++;
 			}
 		}
 
@@ -2195,10 +2190,10 @@
 			flagsLength = MPT_SGE_FLAGS_SSIMPLE_READ;
 			flagsLength |= karg.dataInSize;
 
-			this_alloc = karg.dataInSize;
-			bufIn.len = this_alloc;
+			bufIn.len = karg.dataInSize;
 			bufIn.kptr = pci_alloc_consistent(ioc->pcidev,
-							this_alloc, &dma_addr);
+					bufIn.len, &dma_addr_in);
+
 			if (bufIn.kptr == NULL) {
 				rc = -ENOMEM;
 				goto done_free_mem;
@@ -2206,11 +2201,7 @@
 				/* Set up this SGE
 				 * Copy to MF and to sglbuf
 				 */
-				mpt_add_sge(psge, flagsLength, dma_addr);
-
-				this_sge->FlagsLength = flagsLength;
-				this_sge->Address = dma_addr;
-				this_sge++;
+				mpt_add_sge(psge, flagsLength, dma_addr_in);
 			}
 		}
 	} else  {
@@ -2234,7 +2225,7 @@
 
 	if (hdr->Function == MPI_FUNCTION_SCSI_TASK_MGMT) {
 		rc = mpt_send_handshake_request(mptctl_id, ioc->id,
-				sizeof(SCSITaskMgmt_t), (u32*)mf, NO_SLEEP);
+				sizeof(SCSITaskMgmt_t), (u32*)mf, CAN_SLEEP);
 		if (rc == 0) {
 			wait_event(mptctl_wait, ioc->ioctl->wait_done);
 		} else {
@@ -2242,45 +2233,41 @@
 			tm_flags_set= 0;
 			del_timer(&ioc->ioctl->timer);
 			ioc->ioctl->status &= ~MPT_IOCTL_STATUS_TIMER_ACTIVE;
-			ioc->ioctl->status = MPT_IOCTL_STATUS_TM_FAILED;
+			ioc->ioctl->status |= MPT_IOCTL_STATUS_TM_FAILED;
+			mpt_free_msg_frame(mptctl_id, ioc->id, mf);
 		}
 	} else {
 		mpt_put_msg_frame(mptctl_id, ioc->id, mf);
 		wait_event(mptctl_wait, ioc->ioctl->wait_done);
 	}
 
-	/* The command is complete.  * Return data to the user.
+	mf = NULL;
+
+	/* MF Cleanup:
+	 * If command failed and failure triggered a diagnostic reset
+	 * OR a diagnostic reset happens during command processing,
+	 * no data, messaging queues are reset (mf cannot be accessed),
+	 * and status is DID_IOCRESET
 	 *
-	 * If command completed,  mf has been freed so cannot
-	 * use this memory.
+	 * If a user-requested bus reset fails to be handshaked, then
+	 * mf is returned to free queue and status is TM_FAILED.
 	 *
-	 * If timeout, a recovery  mechanism has been called.
-	 * Need to free the mf.
+	 * Otherise, the command completed and the mf was freed
+	 # by ISR (mf cannot be touched).
 	 */
 	if (ioc->ioctl->status & MPT_IOCTL_STATUS_DID_IOCRESET) {
-
-		/* A timeout - there is no data to return to the
-		 * the user other than an error.
-		 * The timer callback deleted the
+		/* The timer callback deleted the
 		 * timer and reset the adapter queues.
 		 */
 		printk(KERN_WARNING "%s@%d::mptctl_do_mpt_command - "
 			"Timeout Occurred on IOCTL! Reset IOC.\n", __FILE__, __LINE__);
 		tm_flags_set= 0;
 		rc = -ETIME;
-
-		/* Free memory and return to the calling function
-		 */
-		goto done_free_mem;
 	} else if (ioc->ioctl->status & MPT_IOCTL_STATUS_TM_FAILED) {
-		/* User TM request failed!
+		/* User TM request failed! mf has not been freed.
 		 */
 		rc = -ENODATA;
 	} else {
-		/* Callback freed request frame.
-		 */
-		mf = NULL;
-
 		/* If a valid reply frame, copy to the user.
 		 * Offset 2: reply length in U32's
 		 */
@@ -2338,42 +2325,31 @@
 	}
 
 done_free_mem:
-	/* Clear status bits.
-	 */
-	ioc->ioctl->status = 0;
+	/* Clear all status bits except TMTIMER_ACTIVE, this bit is cleared
+	 * upon completion of the TM command.
+	 * ioc->ioctl->status = 0;
+	 */
+	ioc->ioctl->status &= ~(MPT_IOCTL_STATUS_TIMER_ACTIVE | MPT_IOCTL_STATUS_TM_FAILED |
+			MPT_IOCTL_STATUS_COMMAND_GOOD | MPT_IOCTL_STATUS_SENSE_VALID |
+			MPT_IOCTL_STATUS_RF_VALID | MPT_IOCTL_STATUS_DID_IOCRESET);
 
 	if (tm_flags_set)
 		mptctl_free_tm_flags(ioc);
 
-	if (sglbuf) {
-		this_sge = sglbuf;
-
-		/* Free the allocated memory.
-		 */
-		 if (bufOut.kptr != NULL ) {
-			dma_addr = this_sge->Address;
-			this_sge++;	/* go to next structure */
-			this_alloc = bufOut.len;
-			pci_free_consistent(ioc->pcidev,
-				this_alloc, (void *) bufOut.kptr, dma_addr);
-		}
-
-		if (bufIn.kptr != NULL ) {
-			dma_addr = this_sge->Address;
-			this_alloc = bufIn.len;
-
-			pci_free_consistent(ioc->pcidev,
-					this_alloc, (void *) bufIn.kptr, dma_addr);
-		}
-
-		this_alloc = sgSize * sizeof(MptSge_t);
+	/* Free the allocated memory.
+	 */
+	 if (bufOut.kptr != NULL) {
 		pci_free_consistent(ioc->pcidev,
-				this_alloc, (void *) sglbuf, sglbuf_dma);
+			bufOut.len, (void *) bufOut.kptr, dma_addr_out);
+	}
 
+	if (bufIn.kptr != NULL) {
+		pci_free_consistent(ioc->pcidev,
+			bufIn.len, (void *) bufIn.kptr, dma_addr_in);
 	}
 
-	/* mf will be null if allocation failed OR
-	 * if command completed OK (callback freed)
+	/* mf is null if command issued successfully
+	 * otherwise, failure occured after mf acquired.
 	 */
 	if (mf)
 		mpt_free_msg_frame(mptctl_id, ioc->id, mf);
@@ -2393,7 +2369,7 @@
  *		-ENOMEM if memory allocation error
  */
 static int
-mptctl_hp_hostinfo(unsigned long arg)
+mptctl_hp_hostinfo(unsigned long arg, unsigned int data_size)
 {
 	hp_host_info_t	*uarg = (hp_host_info_t *) arg;
 	MPT_ADAPTER		*ioc;
@@ -2404,9 +2380,18 @@
 	CONFIGPARMS		cfg;
 	ConfigPageHeader_t	hdr;
 	int			iocnum;
-	int			rc;
+	int			rc, cim_rev;
 
 	dctlprintk((": mptctl_hp_hostinfo called.\n"));
+	/* Reset long to int. Should affect IA64 and SPARC only
+	 */
+	if (data_size == sizeof(hp_host_info_t))
+		cim_rev = 1;
+	else if (data_size == sizeof(hp_host_info_rev0_t))
+		cim_rev = 0; /* obsolete */
+	else
+		return -EFAULT;
+
 	if (copy_from_user(&karg, uarg, sizeof(hp_host_info_t))) {
 		printk(KERN_ERR "%s@%d::mptctl_hp_host_info - "
 			"Unable to read in hp_host_info struct @ %p\n",
@@ -2436,7 +2421,7 @@
 	karg.bus = pdev->bus->number;
 
 	/* Save the SCSI host no. if
-	 * SCSI driver loaded 
+	 * SCSI driver loaded
 	 */
 	if (ioc->sh != NULL)
 		karg.host_no = ioc->sh->host_no;
@@ -2524,7 +2509,7 @@
 	if (ioc->sh != NULL) {
 		MPT_SCSI_HOST *hd =  (MPT_SCSI_HOST *)ioc->sh->hostdata;
 
-		if (hd) {
+		if (hd && (cim_rev == 1)) {
 			karg.hard_resets = hd->hard_resets;
 			karg.soft_resets = hd->soft_resets;
 			karg.timeouts = hd->timeouts;
@@ -2618,7 +2603,7 @@
 
 		if ((rc = mpt_config(ioc, &cfg)) == 0) {
 			np = le32_to_cpu(pg0_alloc->NegotiatedParameters);
-			karg.negotiated_width = np & MPI_SCSIDEVPAGE0_NP_WIDE ? 
+			karg.negotiated_width = np & MPI_SCSIDEVPAGE0_NP_WIDE ?
 					HP_BUS_WIDTH_16 : HP_BUS_WIDTH_8;
 
 			if (np & MPI_SCSIDEVPAGE0_NP_NEG_SYNC_OFFSET_MASK) {
@@ -2697,7 +2682,7 @@
 /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
 
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,51)
-#define	owner_THIS_MODULE  owner:		THIS_MODULE,
+#define	owner_THIS_MODULE  .owner = THIS_MODULE,
 #else
 #define	owner_THIS_MODULE
 #endif
@@ -2883,6 +2868,8 @@
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)		/*{*/
 	err = register_ioctl32_conversion(MPTIOCINFO, NULL);
 	if (++where && err) goto out_fail;
+	err = register_ioctl32_conversion(MPTIOCINFO1, NULL);
+	if (++where && err) goto out_fail;
 	err = register_ioctl32_conversion(MPTTARGETINFO, NULL);
 	if (++where && err) goto out_fail;
 	err = register_ioctl32_conversion(MPTTEST, NULL);
@@ -2943,6 +2930,7 @@
 	printk(KERN_ERR MYNAM ": ERROR: Failed to register ioctl32_conversion!"
 			" (%d:err=%d)\n", where, err);
 	unregister_ioctl32_conversion(MPTIOCINFO);
+	unregister_ioctl32_conversion(MPTIOCINFO1);
 	unregister_ioctl32_conversion(MPTTARGETINFO);
 	unregister_ioctl32_conversion(MPTTEST);
 	unregister_ioctl32_conversion(MPTEVENTQUERY);
diff -urN linux-2.4.22-pre3/drivers/message/fusion/mptctl.h linux-2.4.22-pre4/drivers/message/fusion/mptctl.h
--- linux-2.4.22-pre3/drivers/message/fusion/mptctl.h	2003-07-09 16:56:24.000000000 -0700
+++ linux-2.4.22-pre4/drivers/message/fusion/mptctl.h	2003-07-09 16:56:28.000000000 -0700
@@ -18,9 +18,9 @@
  *  Copyright (c) 1999-2002 LSI Logic Corporation
  *  Originally By: Steven J. Ralston
  *  (mailto:sjralston1@netscape.net)
- *  (mailto:lstephen@lsil.com)
+ *  (mailto:lstephens@lsil.com)
  *
- *  $Id: mptctl.h,v 1.13 2002/12/03 21:26:33 pdelaney Exp $
+ *  $Id: mptctl.h,v 1.14 2003/03/18 22:49:51 pdelaney Exp $
  */
 /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
 /*
@@ -90,6 +90,7 @@
 #endif	/*}*/
 
 #define MPTIOCINFO		_IOWR(MPT_MAGIC_NUMBER,17,struct mpt_ioctl_iocinfo)
+#define MPTIOCINFO1		_IOWR(MPT_MAGIC_NUMBER,17,struct mpt_ioctl_iocinfo_rev0)
 #define MPTTARGETINFO		_IOWR(MPT_MAGIC_NUMBER,18,struct mpt_ioctl_targetinfo)
 #define MPTTEST			_IOWR(MPT_MAGIC_NUMBER,19,struct mpt_ioctl_test)
 #define MPTEVENTQUERY		_IOWR(MPT_MAGIC_NUMBER,21,struct mpt_ioctl_eventquery)
@@ -99,7 +100,7 @@
 #define MPTFWREPLACE		_IOWR(MPT_MAGIC_NUMBER,25,struct mpt_ioctl_replace_fw)
 
 /*
- * SPARC PLATFORM REMARK:
+ * SPARC PLATFORM REMARKS:
  * IOCTL data structures that contain pointers
  * will have different sizes in the driver and applications
  * (as the app. will not use 8-byte pointers).
@@ -107,6 +108,8 @@
  * The driver will convert data from
  * mpt_fw_xfer32 (mpt_ioctl_command32) to mpt_fw_xfer (mpt_ioctl_command)
  * internally.
+ *
+ * If data structures change size, must handle as in IOCGETINFO.
  */
 struct mpt_fw_xfer {
 	unsigned int	 iocnum;	/* IOC unit number */
@@ -154,11 +157,11 @@
 struct mpt_ioctl_pci_info {
 	union {
 		struct {
-			unsigned long  deviceNumber   :  5;
-			unsigned long  functionNumber :  3;
-			unsigned long  busNumber      : 24;
+			unsigned int  deviceNumber   :  5;
+			unsigned int  functionNumber :  3;
+			unsigned int  busNumber      : 24;
 		} bits;
-		unsigned long  asUlong;
+		unsigned int  asUlong;
 	} u;
 };
 
@@ -189,6 +192,27 @@
 	struct mpt_ioctl_pci_info  pciInfo; /* Added Rev 1 */
 };
 
+/* Original structure, must always accept these
+ * IOCTLs. 4 byte pads can occur based on arch with
+ * above structure. Wish to re-align, but cannot.
+ */
+struct mpt_ioctl_iocinfo_rev0 {
+	mpt_ioctl_header hdr;
+	int		 adapterType;	/* SCSI or FCP */
+	int		 port;		/* port number */
+	int		 pciId;		/* PCI Id. */
+	int		 hwRev;		/* hardware revision */
+	int		 subSystemDevice;	/* PCI subsystem Device ID */
+	int		 subSystemVendor;	/* PCI subsystem Vendor ID */
+	int		 numDevices;		/* number of devices */
+	int		 FWVersion;		/* FW Version (integer) */
+	int		 BIOSVersion;		/* BIOS Version (integer) */
+	char		 driverVersion[MPT_IOCTL_VERSION_LENGTH];	/* Driver Version (string) */
+	char		 busChangeEvent;
+	char		 hostId;
+	char		 rsvd[2];
+};
+
 /*
  * Device Information Page
  * Report the number of, and ids of, all targets
@@ -318,6 +342,7 @@
 #define CPQFCTS_IOC_MAGIC 'Z'
 #define HP_IOC_MAGIC 'Z'
 #define HP_GETHOSTINFO		_IOR(HP_IOC_MAGIC, 20, hp_host_info_t)
+#define HP_GETHOSTINFO1		_IOR(HP_IOC_MAGIC, 20, hp_host_info_rev0_t)
 #define HP_GETTARGETINFO	_IOR(HP_IOC_MAGIC, 21, hp_target_info_t)
 
 /* All HP IOCTLs must include this header
@@ -330,7 +355,7 @@
 	unsigned int lun;
 } hp_header_t;
 
-/*  
+/*
  *  Header:
  *  iocnum 	required (input)
  *  host 	ignored	
@@ -353,12 +378,35 @@
 	u32		 bus_phys_width;
 	u32		 base_io_addr;
 	u32		 rsvd;
+	unsigned int	 hard_resets;		/* driver initiated resets */
+	unsigned int	 soft_resets;		/* ioc, external resets */
+	unsigned int	 timeouts;		/* num timeouts */
+} hp_host_info_t;
+
+/* replace ulongs with uints, need to preserve backwards
+ * compatibility.
+ */
+typedef struct _hp_host_info_rev0 {
+	hp_header_t	 hdr;
+	u16		 vendor;
+	u16		 device;
+	u16		 subsystem_vendor;
+	u16		 subsystem_id;
+	u8		 devfn;
+	u8		 bus;
+	ushort		 host_no;		/* SCSI Host number, if scsi driver not loaded*/
+	u8		 fw_version[16];	/* string */	
+	u8		 serial_number[24];	/* string */
+	u32		 ioc_status;	
+	u32		 bus_phys_width;
+	u32		 base_io_addr;
+	u32		 rsvd;
 	unsigned long	 hard_resets;		/* driver initiated resets */
 	unsigned long	 soft_resets;		/* ioc, external resets */
 	unsigned long	 timeouts;		/* num timeouts */
-} hp_host_info_t;
+} hp_host_info_rev0_t;
 
-/*  
+/*
  *  Header:
  *  iocnum 	required (input)
  *  host 	required	
diff -urN linux-2.4.22-pre3/drivers/message/fusion/mptlan.c linux-2.4.22-pre4/drivers/message/fusion/mptlan.c
--- linux-2.4.22-pre3/drivers/message/fusion/mptlan.c	2003-06-13 07:51:34.000000000 -0700
+++ linux-2.4.22-pre4/drivers/message/fusion/mptlan.c	2003-07-09 16:56:28.000000000 -0700
@@ -26,7 +26,7 @@
  *  Copyright (c) 2000-2002 LSI Logic Corporation
  *  Originally By: Noah Romer
  *
- *  $Id: mptlan.c,v 1.53 2002/10/17 20:15:58 pdelaney Exp $
+ *  $Id: mptlan.c,v 1.55 2003/05/07 14:08:32 pdelaney Exp $
  */
 /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
 /*
@@ -154,7 +154,7 @@
 static int  mpt_lan_reset(struct net_device *dev);
 static int  mpt_lan_close(struct net_device *dev);
 static void mpt_lan_post_receive_buckets(void *dev_id);
-static void mpt_lan_wake_post_buckets_task(struct net_device *dev, 
+static void mpt_lan_wake_post_buckets_task(struct net_device *dev,
 					   int priority);
 static int  mpt_lan_receive_post_turbo(struct net_device *dev, u32 tmsg);
 static int  mpt_lan_receive_post_reply(struct net_device *dev,
@@ -242,7 +242,7 @@
 			//  would Oops because mf has already been set
 			//  to NULL.  So after return from this func,
 			//  mpt_interrupt() will attempt to put (NULL) mf ptr
-			//  item back onto it's adapter FreeQ - Oops!:-(
+			//  item back onto its adapter FreeQ - Oops!:-(
 			//  It's Ok, since mpt_lan_send_turbo() *currently*
 			//  always returns 0, but..., just in case:
 
@@ -339,12 +339,15 @@
 	struct mpt_lan_priv *priv = (struct mpt_lan_priv *) dev->priv;
 
 	dlprintk((KERN_INFO MYNAM ": IOC %s_reset routed to LAN driver!\n",
-			reset_phase==MPT_IOC_PRE_RESET ? "pre" : "post"));
+			reset_phase==MPT_IOC_SETUP_RESET ? "setup" : (
+			reset_phase==MPT_IOC_PRE_RESET ? "pre" : "post")));
 
 	if (priv->mpt_rxfidx == NULL)
 		return (1);
 
-	if (reset_phase == MPT_IOC_PRE_RESET) {
+	if (reset_phase == MPT_IOC_SETUP_RESET) {
+		;
+	} else if (reset_phase == MPT_IOC_PRE_RESET) {
 		int i;
 		unsigned long flags;
 
@@ -868,7 +871,7 @@
 /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
 static inline void
 mpt_lan_wake_post_buckets_task(struct net_device *dev, int priority)
-/* 
+/*
  * @priority: 0 = put it on the timer queue, 1 = put it on the immediate queue
  */
 {
@@ -878,8 +881,6 @@
 		if (priority) {
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,41)
 			schedule_work(&priv->post_buckets_task);
-#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,40)
-			schedule_task(&priv->post_buckets_task);
 #else
 			queue_task(&priv->post_buckets_task, &tq_immediate);
 			mark_bh(IMMEDIATE_BH);
@@ -887,8 +888,6 @@
 		} else {
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,41)
 			schedule_delayed_work(&priv->post_buckets_task, 1);
-#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,40)
-			schedule_task(&priv->post_buckets_task);
 #else
 			queue_task(&priv->post_buckets_task, &tq_timer);
 #endif
@@ -1191,7 +1190,7 @@
 			remaining, atomic_read(&priv->buckets_out));
 	
 	if ((remaining < priv->bucketthresh) &&
-	    ((atomic_read(&priv->buckets_out) - remaining) > 
+	    ((atomic_read(&priv->buckets_out) - remaining) >
 	     MPT_LAN_BUCKETS_REMAIN_MISMATCH_THRESH)) {
 		
 		printk (KERN_WARNING MYNAM " Mismatch between driver's "
@@ -1525,7 +1524,7 @@
 }
 
 /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
-void __init mpt_lan_exit(void)
+static void mpt_lan_exit(void)
 {
 	int i;
 
@@ -1534,10 +1533,11 @@
 	for (i = 0; mpt_landev[i] != NULL; i++) {
 		struct net_device *dev = mpt_landev[i];
 
-		printk (KERN_INFO MYNAM ": %s/%s: Fusion MPT LAN device unregistered\n",
+		printk (KERN_INFO ": %s/%s: Fusion MPT LAN device unregistered\n",
 			       IOC_AND_NETDEV_NAMES_s_s(dev));
 		unregister_fcdev(dev);
-		mpt_landev[i] = (struct net_device *) 0xdeadbeef; /* Debug */
+		//mpt_landev[i] = (struct net_device *) 0xdeadbeef; /* Debug */
+		mpt_landev[i] = NULL;
 	}
 
 	if (LanCtx >= 0) {
@@ -1550,9 +1550,10 @@
 }
 
 /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
-
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,59)
 MODULE_PARM(tx_max_out_p, "i");
 MODULE_PARM(max_buckets_out, "i"); // Debug stuff. FIXME!
+#endif
 
 module_init(mpt_lan_init);
 module_exit(mpt_lan_exit);
diff -urN linux-2.4.22-pre3/drivers/message/fusion/mptscsih.c linux-2.4.22-pre4/drivers/message/fusion/mptscsih.c
--- linux-2.4.22-pre3/drivers/message/fusion/mptscsih.c	2003-07-09 16:56:24.000000000 -0700
+++ linux-2.4.22-pre4/drivers/message/fusion/mptscsih.c	2003-07-09 16:56:28.000000000 -0700
@@ -24,9 +24,9 @@
  *  Copyright (c) 1999-2002 LSI Logic Corporation
  *  Original author: Steven J. Ralston
  *  (mailto:sjralston1@netscape.net)
- *  (mailto:lstephen@lsil.com)
+ *  (mailto:lstephens@lsil.com)
  *
- *  $Id: mptscsih.c,v 1.106 2003/01/28 21:31:57 pdelaney Exp $
+ *  $Id: mptscsih.c,v 1.1.2.4 2003/05/07 14:08:34 pdelaney Exp $
  */
 /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
 /*
@@ -174,17 +174,14 @@
 static MPT_FRAME_HDR *mptscsih_search_pendingQ(MPT_SCSI_HOST *hd, int scpnt_idx);
 static void	post_pendingQ_commands(MPT_SCSI_HOST *hd);
 
-static int	mptscsih_TMHandler(MPT_SCSI_HOST *hd, u8 type, u8 target, u8 lun, int ctx2abort, int sleepFlag);
-static int	mptscsih_IssueTaskMgmt(MPT_SCSI_HOST *hd, u8 type, u8 target, u8 lun, int ctx2abort, int sleepFlag);
+static int	mptscsih_TMHandler(MPT_SCSI_HOST *hd, u8 type, u8 channel, u8 target, u8 lun, int ctx2abort, int sleepFlag);
+static int	mptscsih_IssueTaskMgmt(MPT_SCSI_HOST *hd, u8 type, u8 channel, u8 target, u8 lun, int ctx2abort, int sleepFlag);
 
 static int	mptscsih_ioc_reset(MPT_ADAPTER *ioc, int post_reset);
 static int	mptscsih_event_process(MPT_ADAPTER *ioc, EventNotificationReply_t *pEvReply);
 
 static VirtDevice	*mptscsih_initTarget(MPT_SCSI_HOST *hd, int bus_id, int target_id, u8 lun, char *data, int dlen);
 void		mptscsih_setTargetNegoParms(MPT_SCSI_HOST *hd, VirtDevice *target, char byte56);
-#ifdef MPT_SAVE_AUTOSENSE
-static void	clear_sense_flag(MPT_SCSI_HOST *hd, SCSIIORequest_t *pReq);
-#endif
 static void	mptscsih_set_dvflags(MPT_SCSI_HOST *hd, SCSIIORequest_t *pReq);
 static void	mptscsih_setDevicePage1Flags (u8 width, u8 factor, u8 offset, int *requestedPtr, int *configurationPtr, u8 flags);
 static void	mptscsih_no_negotiate(MPT_SCSI_HOST *hd, int target_id);
@@ -200,7 +197,7 @@
 static void	mptscsih_domainValidation(void *hd);
 static int	mptscsih_is_phys_disk(MPT_ADAPTER *ioc, int id);
 static void	mptscsih_qas_check(MPT_SCSI_HOST *hd, int id);
-static int	mptscsih_doDv(MPT_SCSI_HOST *hd, int portnum, int target);
+static int	mptscsih_doDv(MPT_SCSI_HOST *hd, int channel, int target);
 static void	mptscsih_dv_parms(MPT_SCSI_HOST *hd, DVPARAMETERS *dv,void *pPage);
 static void	mptscsih_fillbuf(char *buffer, int size, int index, int width);
 #endif
@@ -270,6 +267,10 @@
 static struct mptscsih_driver_setup
 	driver_setup = MPTSCSIH_DRIVER_SETUP;
 
+#ifdef MPTSCSIH_DBG_TIMEOUT
+static Scsi_Cmnd *foo_to[8];
+#endif
+
 /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
 /*
  *  Private inline routines...
@@ -743,9 +744,9 @@
 
 		atomic_dec(&queue_depth);
 
-		/* writeSDP1 will use the ScsiDoneCtx
-		 * There is no processing for the reply.
-		 * Just return to the calling function.
+		/* Remark: writeSDP1 will use the ScsiDoneCtx
+		 * If a SCSI I/O cmd, device disabled by OS and
+		 * completion done. Cannot touch sc struct. Just free mem.
 		 */
 		if (hdr->Function == MPI_FUNCTION_SCSI_IO_REQUEST)
 			printk(MYIOC_s_ERR_FMT "NULL ScsiCmd ptr!\n", ioc->name);
@@ -763,6 +764,26 @@
 	pScsiReq = (SCSIIORequest_t *) mf;
 	pScsiReply = (SCSIIOReply_t *) mr;
 
+#ifdef MPTSCSIH_DBG_TIMEOUT
+	if (ioc->timeout_cnt > 0) {
+		int ii, left = 0;
+
+		for (ii=0; ii < 8; ii++) {
+			if (sc == foo_to[ii]) {
+				printk(MYIOC_s_INFO_FMT "complete (%p, %ld)\n",
+					ioc->name, sc, jiffies);
+				foo_to[ii] = NULL;
+			}
+			if (foo_to[ii] != NULL)
+				left++;
+		}
+
+		if (left == 0) {
+			ioc->timeout_maxcnt = 0;
+			ioc->timeout_cnt = 0;
+		}
+	}
+#endif
 	if (pScsiReply == NULL) {
 		/* special context reply handling */
 
@@ -774,16 +795,13 @@
 			dlen = le32_to_cpu(pScsiReq->DataLength);
 			if (dlen >= SCSI_STD_INQUIRY_BYTES) {
 				mptscsih_initTarget(hd,
-						hd->port,
+						sc->channel,
 						sc->target,
 						pScsiReq->LUN[1],
 						sc->buffer,
 						dlen);
 			}
 		}
-#ifdef MPT_SAVE_AUTOSENSE
-		clear_sense_flag(hd, pScsiReq);
-#endif
 	} else {
 		u32	 xfer_cnt;
 		u16	 status;
@@ -871,9 +889,6 @@
 			 *  precedence!
 			 */
 			sc->result = (DID_OK << 16) | pScsiReply->SCSIStatus;
-#ifdef MPT_SAVE_AUTOSENSE
-			clear_sense_flag(hd, pScsiReq);
-#endif
 			if (scsi_state == 0) {
 				;
 			} else if (scsi_state & MPI_SCSI_STATE_AUTOSENSE_VALID) {
@@ -915,7 +930,7 @@
 			     && xfer_cnt >= SCSI_STD_INQUIRY_BYTES
 			   ) {
 				mptscsih_initTarget(hd,
-						hd->port,
+						sc->channel,
 						sc->target,
 						pScsiReq->LUN[1],
 						sc->buffer,
@@ -926,9 +941,6 @@
 		case MPI_IOCSTATUS_SCSI_RECOVERED_ERROR:	/* 0x0040 */
 		case MPI_IOCSTATUS_SUCCESS:			/* 0x0000 */
 			sc->result = (DID_OK << 16) | pScsiReply->SCSIStatus;
-#ifdef MPT_SAVE_AUTOSENSE
-			clear_sense_flag(hd, pScsiReq);
-#endif
 			if (scsi_state == 0) {
 				;
 			} else if (scsi_state & MPI_SCSI_STATE_AUTOSENSE_VALID) {
@@ -999,7 +1011,7 @@
 			     && xfer_cnt >= SCSI_STD_INQUIRY_BYTES
 			   ) {
 				mptscsih_initTarget(hd,
-						hd->port,
+						sc->channel,
 						sc->target,
 						pScsiReq->LUN[1],
 						sc->buffer,
@@ -1238,7 +1250,7 @@
 
 	/* Flush the doneQ.
 	 */
-	dprintk((KERN_INFO MYNAM ": flush_doneQ called\n"));
+	dtmprintk((KERN_INFO MYNAM ": flush_doneQ called\n"));
 	while (1) {
 		spin_lock_irqsave(&hd->freedoneQlock, flags);
 		if (Q_IS_EMPTY(&hd->doneQ)) {
@@ -1305,6 +1317,22 @@
 	return;
 }
 
+static void
+mptscsih_reset_timeouts (MPT_SCSI_HOST *hd)
+{
+	Scsi_Cmnd	*SCpnt = NULL;
+	int		 ii;
+	int		 max = hd->ioc->req_depth;
+
+	for (ii= 0; ii < max; ii++) {
+		if ((SCpnt = hd->ScsiLookup[ii]) != NULL) {
+			mod_timer(&SCpnt->eh_timeout, jiffies + (HZ * 60));
+			dtmprintk((MYIOC_s_WARN_FMT "resetting SCpnt=%p timeout + 60HZ",
+				(hd && hd->ioc) ? hd->ioc->name : "ioc?", SCpnt));
+		}
+	}
+}
+
 /*
  *	mptscsih_flush_running_cmds - For each command found, search
  *		Scsi_Host instance taskQ and reply to OS.
@@ -1320,6 +1348,7 @@
 {
 	Scsi_Cmnd	*SCpnt = NULL;
 	MPT_FRAME_HDR	*mf = NULL;
+	MPT_DONE_Q	*buffer = NULL;
 	int		 ii;
 	int		 max = hd->ioc->req_depth;
 
@@ -1333,7 +1362,6 @@
 
 			/* Command found.
 			 */
-
 #ifndef MPT_SCSI_USE_NEW_EH
 			/* Search taskQ, if found, delete.
 			 */
@@ -1352,11 +1380,9 @@
 			hd->ScsiLookup[ii] = NULL;
 
 			mf = MPT_INDEX_2_MFPTR(hd->ioc, ii);
-			dmfprintk(( "flush: ScsiDone (mf=%p,sc=%p)\n",
-					mf, SCpnt));
+			dmfprintk(( "flush: ScsiDone (mf=%p,sc=%p)\n", mf, SCpnt));
 
 			/* Set status, free OS resources (SG DMA buffers)
-			 * Do OS callback
 			 * Free driver resources (chain, msg buffers)
 			 */
 			if (SCpnt->use_sg) {
@@ -1372,87 +1398,45 @@
 			}
 			SCpnt->result = DID_RESET << 16;
 			SCpnt->host_scribble = NULL;
-                        MPT_HOST_LOCK(flags);
-			SCpnt->scsi_done(SCpnt);	/* Issue the command callback */
-                        MPT_HOST_UNLOCK(flags);
 
 			/* Free Chain buffers */
 			mptscsih_freeChainBuffers(hd, ii);
 
 			/* Free Message frames */
 			mpt_free_msg_frame(ScsiDoneCtx, hd->ioc->id, mf);
-		}
-	}
-
-	return;
-}
-
-#ifdef DROP_TEST
-/* 	mptscsih_flush_drop_test - Free resources and do callback if
- *		DROP_TEST enabled.
- *
- *	@hd: Pointer to a SCSI HOST structure
- *
- *	Returns: None.
- *
- *	Must be called while new I/Os are being queued.
- */
-static void
-mptscsih_flush_drop_test (MPT_SCSI_HOST *hd)
-{
-	Scsi_Cmnd	*sc;
-	unsigned long	 flags;
-	u16		 req_idx;
 
-	/* Free resources for the drop test MF
-	 * and chain buffers.
-	 */
-	if (dropMfPtr) {
-		req_idx = le16_to_cpu(dropMfPtr->u.frame.hwhdr.msgctxu.fld.req_idx);
-		sc = hd->ScsiLookup[req_idx];
-		if (sc == NULL) {
-			printk(MYIOC_s_ERR_FMT "Drop Test: NULL ScsiCmd ptr!\n",
-					ioc->name);
-		} else {
-			/* unmap OS resources, set status, do callback
-			 * free driver resources
+#if 1
+			/* Post to doneQ, do not reply until POST phase
+			 * of reset handler....prevents new commands from
+			 * being queued.
 			 */
-			if (sc->use_sg) {
-				pci_unmap_sg(ioc->pcidev, (struct scatterlist *) sc->request_buffer,
-					    sc->use_sg, scsi_to_pci_dma_dir(sc->sc_data_direction));
-			} else if (sc->request_bufflen) {
-				scPrivate	*my_priv;
+			spin_lock_irqsave(&hd->freedoneQlock, flags);
+			if (!Q_IS_EMPTY(&hd->freeQ)) {
+				buffer = hd->freeQ.head;
+				Q_DEL_ITEM(buffer);
 
-				my_priv = (scPrivate *) &sc->SCp;
-				pci_unmap_single(ioc->pcidev, (dma_addr_t)(ulong)my_priv->p1,
-					   sc->request_bufflen,
-					   scsi_to_pci_dma_dir(sc->sc_data_direction));
-			}
+				/* Set the Scsi_Cmnd pointer
+				 */
+				buffer->argp = (void *)SCpnt;
 
-			sc->host_scribble = NULL;
-			sc->result = DID_RESET << 16;
-			hd->ScsiLookup[req_idx] = NULL;
-			atomic_dec(&queue_depth);
-			MPT_HOST_LOCK(flags);
-			sc->scsi_done(sc);	/* Issue callback */
-			MPT_HOST_UNLOCK(flags);
+				/* Add to the doneQ
+				 */
+				Q_ADD_TAIL(&hd->doneQ.head, buffer, MPT_DONE_Q);
+				spin_unlock_irqrestore(&hd->freedoneQlock, flags);
+			} else {
+				spin_unlock_irqrestore(&hd->freedoneQlock, flags);
+				SCpnt->scsi_done(SCpnt);
+			}
+#else
+                        MPT_HOST_LOCK(flags);
+			SCpnt->scsi_done(SCpnt);	/* Issue the command callback */
+                        MPT_HOST_UNLOCK(flags);
+#endif
 		}
-
-		mptscsih_freeChainBuffers(hd, req_idx);
-		mpt_free_msg_frame(ScsiDoneCtx, ioc->id, dropMfPtr);
-		printk(MYIOC_s_INFO_FMT "Free'd Dropped cmd (%p)\n",
-					hd->ioc->name, sc);
-		printk(MYIOC_s_INFO_FMT "mf (%p) reqidx (%4x)\n",
-					hd->ioc->name, dropMfPtr, req_idx);
-		printk(MYIOC_s_INFO_FMT "Num Tot (%d) Good (%d) Bad (%d) \n",
-				hd->ioc->name, dropTestNum,
-				dropTestOK, dropTestBad);
 	}
-	dropMfPtr = NULL;
 
 	return;
 }
-#endif
 
 /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
 /*
@@ -1630,6 +1614,7 @@
 	int			 sz, ii;
 	int			 numSGE = 0;
 	int			 scale;
+	int			 ioc_cap;
 	u8			*mem;
 
 	if (! BeenHereDoneThat++) {
@@ -1668,265 +1653,287 @@
 
 	this = mpt_adapter_find_first();
 	while (this != NULL) {
-		int	 portnum;
-		for (portnum=0; portnum < this->facts.NumberOfPorts; portnum++) {
+		/* 20010202 -sralston
+		 *  Added sanity check on readiness of the MPT adapter.
+		 */
+		if (this->last_state != MPI_IOC_STATE_OPERATIONAL) {
+			printk(MYIOC_s_WARN_FMT "Skipping because it's not operational!\n",
+					this->name);
+			continue;
+		}
 
-			/* 20010215 -sralston
-			 *  Added sanity check on SCSI Initiator-mode enabled
-			 *  for this MPT adapter.
-			 */
-			if (!(this->pfacts[portnum].ProtocolFlags & MPI_PORTFACTS_PROTOCOL_INITIATOR)) {
-				printk(MYIOC_s_WARN_FMT "Skipping because SCSI Initiator mode is NOT enabled!\n",
-						this->name);
-				continue;
-			}
+		if (!this->active) {
+			printk(MYIOC_s_WARN_FMT "Skipping because it's disabled!\n",
+					this->name);
+			continue;
+		}
+
+
+		/*  Sanity check - ensure at least 1 port is INITIATOR capable
+		 */
+		ioc_cap = 0;
+		for (ii=0; ii < this->facts.NumberOfPorts; ii++) {
+			if (this->pfacts[ii].ProtocolFlags & MPI_PORTFACTS_PROTOCOL_INITIATOR)
+				ioc_cap ++;
+		}
+
+		if (!ioc_cap) {
+			printk(MYIOC_s_WARN_FMT "Skipping because SCSI Initiator mode is NOT enabled!\n",
+					this->name);
+			continue;
+		}
 
-			/* 20010202 -sralston
-			 *  Added sanity check on readiness of the MPT adapter.
-			 */
-			if (this->last_state != MPI_IOC_STATE_OPERATIONAL) {
-				printk(MYIOC_s_WARN_FMT "Skipping because it's not operational!\n",
-						this->name);
-				continue;
-			}
 
 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)
-			tpnt->proc_dir = &proc_mpt_scsihost;
+		tpnt->proc_dir = &proc_mpt_scsihost;
 #endif
-			tpnt->proc_info = mptscsih_proc_info;
-			sh = scsi_register(tpnt, sizeof(MPT_SCSI_HOST));
-			if (sh != NULL) {
-				spin_lock_irqsave(&this->FreeQlock, flags);
-				sh->io_port = 0;
-				sh->n_io_port = 0;
-				sh->irq = 0;
-
-				/* Yikes!  This is important!
-				 * Otherwise, by default, linux
-				 * only scans target IDs 0-7!
-				 * pfactsN->MaxDevices unreliable
-				 * (not supported in early
-				 *	versions of the FW).
-				 * max_id = 1 + actual max id,
-				 * max_lun = 1 + actual last lun,
-				 *	see hosts.h :o(
+		tpnt->proc_info = mptscsih_proc_info;
+		sh = scsi_register(tpnt, sizeof(MPT_SCSI_HOST));
+		if (sh != NULL) {
+			spin_lock_irqsave(&this->FreeQlock, flags);
+			sh->io_port = 0;
+			sh->n_io_port = 0;
+			sh->irq = 0;
+
+			/* Yikes!  This is important!
+			 * Otherwise, by default, linux
+			 * only scans target IDs 0-7!
+			 * pfactsN->MaxDevices unreliable
+			 * (not supported in early
+			 *	versions of the FW).
+			 * max_id = 1 + actual max id,
+			 * max_lun = 1 + actual last lun,
+			 *	see hosts.h :o(
+			 */
+			if ((int)this->chip_type > (int)FC929)
+				sh->max_id = MPT_MAX_SCSI_DEVICES;
+			else {
+				/* For FC, increase the queue depth
+				 * from MPT_SCSI_CAN_QUEUE (31)
+				 * to MPT_FC_CAN_QUEUE (63).
 				 */
-				if ((int)this->chip_type > (int)FC929)
-					sh->max_id = MPT_MAX_SCSI_DEVICES;
-				else {
-					/* For FC, increase the queue depth
-					 * from MPT_SCSI_CAN_QUEUE (31)
-					 * to MPT_FC_CAN_QUEUE (63).
-					 */
-					sh->can_queue = MPT_FC_CAN_QUEUE;
-					sh->max_id = MPT_MAX_FC_DEVICES<256 ? MPT_MAX_FC_DEVICES : 255;
-				}
-				sh->max_lun = MPT_LAST_LUN + 1;
+				sh->can_queue = MPT_FC_CAN_QUEUE;
+				sh->max_id = MPT_MAX_FC_DEVICES<256 ? MPT_MAX_FC_DEVICES : 255;
+			}
+			sh->max_lun = MPT_LAST_LUN + 1;
 
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,7)
-				sh->max_sectors = MPT_SCSI_MAX_SECTORS;
+			sh->max_sectors = MPT_SCSI_MAX_SECTORS;
 #endif
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,1) || defined CONFIG_HIGHIO
-				sh->highmem_io = 1;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,20) || defined CONFIG_HIGHIO
+			sh->highmem_io = 1;
 #endif
-				sh->this_id = this->pfacts[portnum].PortSCSIID;
+			/* MPI uses {port, bus, id, lun}, but logically maps
+			 * devices on different ports to different buses, i.e.,
+			 * bus 1 may be the 2nd bus on port 0 or the 1st bus on port 1.
+			 * Map bus to channel, ignore port number in SCSI....
+			 *	hd->port = 0;
+			 * If max_channel > 0, need to adjust mem alloc, free, DV
+			 * and all access to VirtDev
+			 */
+			sh->max_channel = 0;
+			sh->this_id = this->pfacts[0].PortSCSIID;
 
 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,44)
-				/* OS entry to allow host drivers to force
-				 * a queue depth on a per device basis.
-				 */
-				sh->select_queue_depths = mptscsih_select_queue_depths;
+			/* OS entry to allow host drivers to force
+			 * a queue depth on a per device basis.
+			 */
+			sh->select_queue_depths = mptscsih_select_queue_depths;
 #endif
-				/* Required entry.
-				 */
-				sh->unique_id = this->id;
-
-				/* Verify that we won't exceed the maximum
-				 * number of chain buffers
-				 * We can optimize:  ZZ = req_sz/sizeof(SGE)
-				 * For 32bit SGE's:
-				 *  numSGE = 1 + (ZZ-1)*(maxChain -1) + ZZ
-				 *               + (req_sz - 64)/sizeof(SGE)
-				 * A slightly different algorithm is required for
-				 * 64bit SGEs.
-				 */
-				scale = this->req_sz/(sizeof(dma_addr_t) + sizeof(u32));
-				if (sizeof(dma_addr_t) == sizeof(u64)) {
-					numSGE = (scale - 1) * (this->facts.MaxChainDepth-1) + scale +
-						(this->req_sz - 60) / (sizeof(dma_addr_t) + sizeof(u32));
-				} else {
-					numSGE = 1 + (scale - 1) * (this->facts.MaxChainDepth-1) + scale +
-						(this->req_sz - 64) / (sizeof(dma_addr_t) + sizeof(u32));
-				}
-
-				if (numSGE < sh->sg_tablesize) {
-					/* Reset this value */
-					dprintk((MYIOC_s_INFO_FMT
-						 "Resetting sg_tablesize to %d from %d\n",
-						 this->name, numSGE, sh->sg_tablesize));
-					sh->sg_tablesize = numSGE;
-				}
+			/* Required entry.
+			 */
+			sh->unique_id = this->id;
 
-				/* Set the pci device pointer in Scsi_Host structure.
-				 */
-				scsi_set_pci_device(sh, this->pcidev);
+			/* Verify that we won't exceed the maximum
+			 * number of chain buffers
+			 * We can optimize:  ZZ = req_sz/sizeof(SGE)
+			 * For 32bit SGE's:
+			 *  numSGE = 1 + (ZZ-1)*(maxChain -1) + ZZ
+			 *               + (req_sz - 64)/sizeof(SGE)
+			 * A slightly different algorithm is required for
+			 * 64bit SGEs.
+			 */
+			scale = this->req_sz/(sizeof(dma_addr_t) + sizeof(u32));
+			if (sizeof(dma_addr_t) == sizeof(u64)) {
+				numSGE = (scale - 1) * (this->facts.MaxChainDepth-1) + scale +
+					(this->req_sz - 60) / (sizeof(dma_addr_t) + sizeof(u32));
+			} else {
+				numSGE = 1 + (scale - 1) * (this->facts.MaxChainDepth-1) + scale +
+					(this->req_sz - 64) / (sizeof(dma_addr_t) + sizeof(u32));
+			}
 
-				spin_unlock_irqrestore(&this->FreeQlock, flags);
+			if (numSGE < sh->sg_tablesize) {
+				/* Reset this value */
+				dprintk((MYIOC_s_INFO_FMT
+					 "Resetting sg_tablesize to %d from %d\n",
+					 this->name, numSGE, sh->sg_tablesize));
+				sh->sg_tablesize = numSGE;
+			}
 
-				hd = (MPT_SCSI_HOST *) sh->hostdata;
-				hd->ioc = this;
-				hd->max_sge = sh->sg_tablesize;
+			/* Set the pci device pointer in Scsi_Host structure.
+			 */
+			scsi_set_pci_device(sh, this->pcidev);
 
-				if ((int)this->chip_type > (int)FC929)
-					hd->is_spi = 1;
+			spin_unlock_irqrestore(&this->FreeQlock, flags);
 
-				if (DmpService &&
-				    (this->chip_type == FC919 || this->chip_type == FC929))
-					hd->is_multipath = 1;
+			hd = (MPT_SCSI_HOST *) sh->hostdata;
+			hd->ioc = this;
+			hd->max_sge = sh->sg_tablesize;
 
-				hd->port = 0;		/* FIXME! */
+			if ((int)this->chip_type > (int)FC929)
+				hd->is_spi = 1;
 
-				/* SCSI needs Scsi_Cmnd lookup table!
-				 * (with size equal to req_depth*PtrSz!)
-				 */
-				sz = hd->ioc->req_depth * sizeof(void *);
-				mem = kmalloc(sz, GFP_ATOMIC);
-				if (mem == NULL)
-					goto done;
+			if (DmpService &&
+			    (this->chip_type == FC919 || this->chip_type == FC929))
+				hd->is_multipath = 1;
 
-				memset(mem, 0, sz);
-				hd->ScsiLookup = (struct scsi_cmnd **) mem;
+			/* SCSI needs Scsi_Cmnd lookup table!
+			 * (with size equal to req_depth*PtrSz!)
+			 */
+			sz = hd->ioc->req_depth * sizeof(void *);
+			mem = kmalloc(sz, GFP_ATOMIC);
+			if (mem == NULL)
+				goto done;
 
-				dprintk((MYIOC_s_INFO_FMT "ScsiLookup @ %p, sz=%d\n",
-					 this->name, hd->ScsiLookup, sz));
+			memset(mem, 0, sz);
+			hd->ScsiLookup = (struct scsi_cmnd **) mem;
 
-				if (mptscsih_initChainBuffers(hd, 1) < 0)
-					goto done;
+			dprintk((MYIOC_s_INFO_FMT "ScsiLookup @ %p, sz=%d\n",
+				 this->name, hd->ScsiLookup, sz));
 
-				/* Allocate memory for free and doneQ's
-				 */
-				sz = sh->can_queue * sizeof(MPT_DONE_Q);
-				mem = kmalloc(sz, GFP_ATOMIC);
-				if (mem == NULL)
-					goto done;
+			if (mptscsih_initChainBuffers(hd, 1) < 0)
+				goto done;
 
-				memset(mem, 0xFF, sz);
-				hd->memQ = mem;
+			/* Allocate memory for free and doneQ's
+			 */
+			sz = sh->can_queue * sizeof(MPT_DONE_Q);
+			mem = kmalloc(sz, GFP_ATOMIC);
+			if (mem == NULL)
+				goto done;
 
-				/* Initialize the free, done and pending Qs.
-				 */
-				Q_INIT(&hd->freeQ, MPT_DONE_Q);
-				Q_INIT(&hd->doneQ, MPT_DONE_Q);
-				Q_INIT(&hd->pendingQ, MPT_DONE_Q);
-				spin_lock_init(&hd->freedoneQlock);
+			memset(mem, 0xFF, sz);
+			hd->memQ = mem;
 
-				mem = hd->memQ;
-				for (ii=0; ii < sh->can_queue; ii++) {
-					freedoneQ = (MPT_DONE_Q *) mem;
-					Q_ADD_TAIL(&hd->freeQ.head, freedoneQ, MPT_DONE_Q);
-					mem += sizeof(MPT_DONE_Q);
-				}
+			/* Initialize the free, done and pending Qs.
+			 */
+			Q_INIT(&hd->freeQ, MPT_DONE_Q);
+			Q_INIT(&hd->doneQ, MPT_DONE_Q);
+			Q_INIT(&hd->pendingQ, MPT_DONE_Q);
+			spin_lock_init(&hd->freedoneQlock);
+
+			mem = hd->memQ;
+			for (ii=0; ii < sh->can_queue; ii++) {
+				freedoneQ = (MPT_DONE_Q *) mem;
+				Q_ADD_TAIL(&hd->freeQ.head, freedoneQ, MPT_DONE_Q);
+				mem += sizeof(MPT_DONE_Q);
+			}
 
-				/* Initialize this Scsi_Host
-				 * internal task Q.
-				 */
-				Q_INIT(&hd->taskQ, MPT_FRAME_HDR);
-				hd->taskQcnt = 0;
+			/* Initialize this Scsi_Host
+			 * internal task Q.
+			 */
+			Q_INIT(&hd->taskQ, MPT_FRAME_HDR);
+			hd->taskQcnt = 0;
 
-				/* Allocate memory for the device structures.
-				 * A non-Null pointer at an offset
-				 * indicates a device exists.
-				 * max_id = 1 + maximum id (hosts.h)
-				 */
-				sz = sh->max_id * sizeof(void *);
-				mem = kmalloc(sz, GFP_ATOMIC);
-				if (mem == NULL)
-					goto done;
+			/* Allocate memory for the device structures.
+			 * A non-Null pointer at an offset
+			 * indicates a device exists.
+			 * max_id = 1 + maximum id (hosts.h)
+			 */
+			sz = sh->max_id * sizeof(void *);
+			mem = kmalloc(sz, GFP_ATOMIC);
+			if (mem == NULL)
+				goto done;
 
-				memset(mem, 0, sz);
-				hd->Targets = (VirtDevice **) mem;
+			memset(mem, 0, sz);
+			hd->Targets = (VirtDevice **) mem;
 
-				dprintk((KERN_INFO "  Targets @ %p, sz=%d\n", hd->Targets, sz));
+			dprintk((KERN_INFO "  Targets @ %p, sz=%d\n", hd->Targets, sz));
 
 
-				/* Clear the TM flags
-				 */
-				hd->tmPending = 0;
+			/* Clear the TM flags
+			 */
+			hd->tmPending = 0;
 #ifdef MPT_SCSI_USE_NEW_EH
-				hd->tmState = TM_STATE_NONE;
+			hd->tmState = TM_STATE_NONE;
 #endif
-				hd->resetPending = 0;
-				hd->abortSCpnt = NULL;
-				hd->tmPtr = NULL;
-				hd->numTMrequests = 0;
-
-				/* Clear the pointer used to store
-				 * single-threaded commands, i.e., those
-				 * issued during a bus scan, dv and
-				 * configuration pages.
-				 */
-				hd->cmdPtr = NULL;
-
-				/* Attach the SCSI Host to the IOC structure
-				 */
-				this->sh = sh;
+			hd->resetPending = 0;
+			hd->abortSCpnt = NULL;
+			hd->tmPtr = NULL;
+			hd->numTMrequests = 0;
 
-				/* Initialize this SCSI Hosts' timers
-				 * To use, set the timer expires field
-				 * and add_timer
-				 */
-				init_timer(&hd->timer);
-				hd->timer.data = (unsigned long) hd;
-				hd->timer.function = mptscsih_timer_expired;
+			/* Clear the pointer used to store
+			 * single-threaded commands, i.e., those
+			 * issued during a bus scan, dv and
+			 * configuration pages.
+			 */
+			hd->cmdPtr = NULL;
 
-				init_timer(&hd->TMtimer);
-				hd->TMtimer.data = (unsigned long) hd;
-				hd->TMtimer.function = mptscsih_taskmgmt_timeout;
-				hd->qtag_tick = jiffies;
+			/* Attach the SCSI Host to the IOC structure
+			 */
+			this->sh = sh;
 
-				/* Moved Earlier Pam D */
-				/* this->sh = sh;	*/
+			/* Initialize this SCSI Hosts' timers
+			 * To use, set the timer expires field
+			 * and add_timer
+			 */
+			init_timer(&hd->timer);
+			hd->timer.data = (unsigned long) hd;
+			hd->timer.function = mptscsih_timer_expired;
+
+			init_timer(&hd->TMtimer);
+			hd->TMtimer.data = (unsigned long) hd;
+			hd->TMtimer.function = mptscsih_taskmgmt_timeout;
+			hd->qtag_tick = jiffies;
+
+			/* Moved Earlier Pam D */
+			/* this->sh = sh;	*/
+
+#ifdef MPTSCSIH_DBG_TIMEOUT
+			hd->ioc->timeout_hard = 0;
+			hd->ioc->timeout_delta = 30 * HZ;
+			hd->ioc->timeout_maxcnt = 0;
+			hd->ioc->timeout_cnt = 0;
+			for (ii=0; ii < 8; ii++)
+				foo_to[ii] = NULL;
+#endif
 
-				if (hd->is_spi) {
-					/* Update with the driver setup
-					 * values.
-					 */
-					if (hd->ioc->spi_data.maxBusWidth > driver_setup.max_width)
-						hd->ioc->spi_data.maxBusWidth = driver_setup.max_width;
-					if (hd->ioc->spi_data.minSyncFactor < driver_setup.min_sync_fac)
-						hd->ioc->spi_data.minSyncFactor = driver_setup.min_sync_fac;
+			if (hd->is_spi) {
+				/* Update with the driver setup
+				 * values.
+				 */
+				if (hd->ioc->spi_data.maxBusWidth > driver_setup.max_width)
+					hd->ioc->spi_data.maxBusWidth = driver_setup.max_width;
+				if (hd->ioc->spi_data.minSyncFactor < driver_setup.min_sync_fac)
+					hd->ioc->spi_data.minSyncFactor = driver_setup.min_sync_fac;
 
-					if (hd->ioc->spi_data.minSyncFactor == MPT_ASYNC)
-						hd->ioc->spi_data.maxSyncOffset = 0;
+				if (hd->ioc->spi_data.minSyncFactor == MPT_ASYNC)
+					hd->ioc->spi_data.maxSyncOffset = 0;
 
-					hd->negoNvram = 0;
+				hd->negoNvram = 0;
 #ifdef MPTSCSIH_DISABLE_DOMAIN_VALIDATION
-					hd->negoNvram = MPT_SCSICFG_USE_NVRAM;
+				hd->negoNvram = MPT_SCSICFG_USE_NVRAM;
 #endif
-					if (driver_setup.dv == 0)
-						hd->negoNvram = MPT_SCSICFG_USE_NVRAM;
+				if (driver_setup.dv == 0)
+					hd->negoNvram = MPT_SCSICFG_USE_NVRAM;
 
-					hd->ioc->spi_data.forceDv = 0;
-					for (ii=0; ii < MPT_MAX_SCSI_DEVICES; ii++)
-						hd->ioc->spi_data.dvStatus[ii] = MPT_SCSICFG_NEGOTIATE;
+				hd->ioc->spi_data.forceDv = 0;
+				for (ii=0; ii < MPT_MAX_SCSI_DEVICES; ii++)
+					hd->ioc->spi_data.dvStatus[ii] = MPT_SCSICFG_NEGOTIATE;
 	
-					if (hd->negoNvram == 0) {
-						for (ii=0; ii < MPT_MAX_SCSI_DEVICES; ii++)
-							hd->ioc->spi_data.dvStatus[ii] |= MPT_SCSICFG_DV_NOT_DONE;
-					}
-
-					ddvprintk((MYIOC_s_INFO_FMT
-						"dv %x width %x factor %x \n",
-						hd->ioc->name, driver_setup.dv,
-						driver_setup.max_width,
-						driver_setup.min_sync_fac));
-
+				if (hd->negoNvram == 0) {
+					for (ii=0; ii < MPT_MAX_SCSI_DEVICES; ii++)
+						hd->ioc->spi_data.dvStatus[ii] |= MPT_SCSICFG_DV_NOT_DONE;
 				}
 
-				mpt_scsi_hosts++;
+				ddvprintk((MYIOC_s_INFO_FMT
+					"dv %x width %x factor %x \n",
+					hd->ioc->name, driver_setup.dv,
+					driver_setup.max_width,
+					driver_setup.min_sync_fac));
 			}
 
-		}	/* for each adapter port */
+			mpt_scsi_hosts++;
+		}
 
 		this = mpt_adapter_find_next(this);
 	}
@@ -2251,11 +2258,129 @@
 	return ((info.pos > info.offset) ? info.pos - info.offset : 0);
 }
 
+#ifndef MPTSCSIH_DBG_TIMEOUT
 static int mptscsih_user_command(MPT_ADAPTER *ioc, char *pbuf, int len)
 {
 	/* Not yet implemented */
 	return len;
 }
+#else
+#define is_digit(c)	((c) >= '0' && (c) <= '9')
+#define digit_to_bin(c)	((c) - '0')
+#define is_space(c)	((c) == ' ' || (c) == '\t')
+
+#define UC_DBG_TIMEOUT		0x01
+#define UC_DBG_HARDRESET	0x02
+
+static int skip_spaces(char *ptr, int len)
+{
+	int cnt, c;
+
+	for (cnt = len; cnt > 0 && (c = *ptr++) && is_space(c); cnt --);
+
+	return (len - cnt);
+}
+
+static int get_int_arg(char *ptr, int len, ulong *pv)
+{
+	int cnt, c;
+	ulong	v;
+	for (v =  0, cnt = len; cnt > 0 && (c=*ptr++) && is_digit(c); cnt --) {
+		v = (v * 10) + digit_to_bin(c);
+	}
+
+	if (pv)
+		*pv = v;
+
+	return (len - cnt);
+}
+
+
+static int is_keyword(char *ptr, int len, char *verb)
+{
+	int verb_len = strlen(verb);
+
+	if (len >= strlen(verb) && !memcmp(verb, ptr, verb_len))
+		return verb_len;
+	else
+		return 0;
+}
+
+#define SKIP_SPACES(min_spaces)						\
+	if ((arg_len = skip_spaces(ptr,len)) < (min_spaces))		\
+		return -EINVAL;						\
+	ptr += arg_len;							\
+	len -= arg_len;
+
+#define GET_INT_ARG(v)							\
+	if (!(arg_len = get_int_arg(ptr,len, &(v))))			\
+		return -EINVAL;						\
+	ptr += arg_len;							\
+	len -= arg_len;
+
+static int mptscsih_user_command(MPT_ADAPTER *ioc, char *buffer, int length)
+{
+	char *ptr = buffer;
+	char btmp[24];	/* REMOVE */
+	int arg_len;
+	int len	= length;
+	int cmd;
+	ulong number = 1;
+	ulong delta = 10;
+
+	if ((len > 0) && (ptr[len -1] == '\n'))
+		--len;
+
+	if (len < 22) {
+		strncpy(btmp, buffer, len);
+		btmp[len+1]='\0';
+	} else {
+		strncpy(btmp, buffer, 22);
+		btmp[23]='\0';
+	}
+	printk("user_command:  ioc %d, buffer %s, length %d\n",
+			ioc->id, btmp, length);
+
+	if ((arg_len = is_keyword(ptr, len, "timeout")) != 0)
+		cmd = UC_DBG_TIMEOUT;
+	else if ((arg_len = is_keyword(ptr, len, "hardreset")) != 0)
+		cmd = UC_DBG_HARDRESET;
+	else
+		return -EINVAL;
+
+	ptr += arg_len;
+	len -= arg_len;
+
+	switch(cmd) {
+		case UC_DBG_TIMEOUT:
+			SKIP_SPACES(1);
+			GET_INT_ARG(number);
+			SKIP_SPACES(1);
+			GET_INT_ARG(delta);
+			break;
+	}
+
+	printk("user_command: cnt=%ld delta=%ld\n", number, delta);
+
+	if (len)
+		return -EINVAL;
+	else {
+		if (cmd == UC_DBG_HARDRESET) {
+			ioc->timeout_hard = 1;
+		} else if (cmd == UC_DBG_TIMEOUT) {
+			/* process this command ...
+			 */
+			ioc->timeout_maxcnt = 0;
+			ioc->timeout_delta = delta < 2 ? 2 : delta;
+			ioc->timeout_cnt = 0;
+			ioc->timeout_maxcnt = number < 8 ? number: 8;
+		}
+	}
+	/* Not yet implemented */
+	return length;
+}
+#endif
+
 
 /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
 /**
@@ -2318,19 +2443,6 @@
 #define ADD_INDEX_LOG(req_ent)	do { } while(0)
 #endif
 
-#ifdef	DROP_TEST
-#define DROP_IOC	1	/* IOC to force failures */
-#define DROP_TARGET	3	/* Target ID to force failures */
-#define	DROP_THIS_CMD	10000	/* iteration to drop command */
-static int dropCounter = 0;
-static int dropTestOK = 0;	/* num did good */
-static int dropTestBad = 0;	/* num did bad */
-static int dropTestNum = 0;	/* total = good + bad + incomplete */
-static int numTotCmds = 0;
-static MPT_FRAME_HDR *dropMfPtr = NULL;
-static int numTMrequested = 0;
-#endif
-
 /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
 /*
  *	mptscsih_put_msgframe - Wrapper routine to post message frame to F/W.
@@ -2403,49 +2515,16 @@
 	dmfprintk((MYIOC_s_INFO_FMT "qcmd: SCpnt=%p, done()=%p\n",
 			(hd && hd->ioc) ? hd->ioc->name : "ioc?", SCpnt, done));
 
-#ifdef MPT_SAVE_AUTOSENSE
-	/* 20000617 -sralston
-	 *  GRRRRR...  Shouldn't have to do this but...
-	 *  Do explicit check for REQUEST_SENSE and cached SenseData.
-	 *  If yes, return cached SenseData.
-	 */
-	if (SCpnt->cmnd[0] == REQUEST_SENSE) {
-		u8 *dest = NULL;
-		int sz;
-
-		if (pTarget && (pTarget->tflags & MPT_TARGET_FLAGS_VALID_SENSE)) {
-			pTarget->tflags &= ~MPT_TARGET_FLAGS_VALID_SENSE;	//sjr-moved-here
-			if (!SCpnt->use_sg) {
-				dest = SCpnt->request_buffer;
-			} else {
-				struct scatterlist *sg = (struct scatterlist *) SCpnt->request_buffer;
-				if (sg)
-					dest = (u8 *)(ulong)sg_dma_address(sg);
-			}
-
-			if (dest) {
-				sz = MIN (SCSI_STD_SENSE_BYTES, SCpnt->request_bufflen);
-				memcpy(dest, pTarget->sense, sz);
-
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)
-				SCpnt->resid = SCpnt->request_bufflen - sz;
-#endif
-				SCpnt->result = 0;
-				SCpnt->scsi_done(SCpnt);
-
-				//sjr-moved-up//pTarget->tflags &= ~MPT_TARGET_FLAGS_VALID_SENSE;
-
-				return 0;
-			}
-		}
-	}
-#endif
-
 	if (hd->resetPending) {
 		/* Prevent new commands from being issued
-		 * while reloading the FW.
+		 * while reloading the FW. Reset timer to 60 seconds,
+		 * as the FW can take some time to come ready.
+		 * For New EH, cmds on doneQ posted to FW.
 		 */
 		did_errcode = 1;
+		mod_timer(&SCpnt->eh_timeout, jiffies + (HZ * 60));
+		dtmprintk((MYIOC_s_WARN_FMT "qcmd: SCpnt=%p timeout + 60HZ\n",
+			(hd && hd->ioc) ? hd->ioc->name : "ioc?", SCpnt));
 		goto did_error;
 	}
 
@@ -2500,8 +2579,8 @@
 
 	/* Use the above information to set up the message frame
 	 */
-	pScsiReq->TargetID = target;
-	pScsiReq->Bus = hd->port;
+	pScsiReq->TargetID = (u8) target;
+	pScsiReq->Bus = (u8) SCpnt->channel;
 	pScsiReq->ChainOffset = 0;
 	pScsiReq->Function = MPI_FUNCTION_SCSI_IO_REQUEST;
 	pScsiReq->CDBLength = SCpnt->cmd_len;
@@ -2554,39 +2633,6 @@
 		hd->ScsiLookup[my_idx] = SCpnt;
 		SCpnt->host_scribble = NULL;
 
-#ifdef	DROP_TEST
-		numTotCmds++;
-		/* If the IOC number and target match, increment
-		 * counter. If counter matches DROP_THIS, do not
-		 * issue command to FW to force a reset.
-		 * Save the MF pointer so we can free resources
-		 * when task mgmt completes.
-		 */
-		if ((hd->ioc->id == DROP_IOC) && (target == DROP_TARGET)) {
-			dropCounter++;
-
-			if (dropCounter == DROP_THIS_CMD) {
-				dropCounter = 0;
-
-				/* If global is set, then we are already
-				 * doing something - so keep issuing commands.
-				 */
-				if (dropMfPtr == NULL) {
-					dropTestNum++;
-					dropMfPtr = mf;
-					atomic_inc(&queue_depth);
-					printk(MYIOC_s_INFO_FMT
-						"Dropped SCSI cmd (%p)\n",
-						hd->ioc->name, SCpnt);
-					printk("mf (%p) req (%4x) tot cmds (%d)\n",
-						mf, my_idx, numTotCmds);
-
-					return 0;
-				}
-			}
-		}
-#endif
-
 		/* SCSI specific processing */
 		issueCmd = 1;
 		if (hd->is_spi) {
@@ -2639,6 +2685,18 @@
 			}
 		}
 
+#ifdef MPTSCSIH_DBG_TIMEOUT
+		if (hd->ioc->timeout_cnt < hd->ioc->timeout_maxcnt) {
+			foo_to[hd->ioc->timeout_cnt] = SCpnt;
+			hd->ioc->timeout_cnt++;
+			//mod_timer(&SCpnt->eh_timeout, jiffies + hd->ioc->timeout_delta);
+			issueCmd = 0;
+			printk(MYIOC_s_WARN_FMT
+				"to pendingQ: (sc=%p, mf=%p, time=%ld)\n",
+				hd->ioc->name, SCpnt, mf, jiffies);
+		}
+#endif
+
 		if (issueCmd) {
 			mptscsih_put_msgframe(ScsiDoneCtx, hd->ioc->id, mf);
 			dmfprintk((MYIOC_s_INFO_FMT "Issued SCSI cmd (%p) mf=%p idx=%d\n",
@@ -2682,6 +2740,8 @@
 	SCpnt->result = (DID_BUS_BUSY << 16);
 	spin_lock_irqsave(&hd->freedoneQlock, flags);
 	if (!Q_IS_EMPTY(&hd->freeQ)) {
+		dtmprintk((MYIOC_s_WARN_FMT "SCpnt=%p to doneQ\n",
+			(hd && hd->ioc) ? hd->ioc->name : "ioc?", SCpnt));
 		buffer = hd->freeQ.head;
 		Q_DEL_ITEM(buffer);
 
@@ -2777,7 +2837,7 @@
  *	Returns 0 for SUCCESS or -1 if FAILED.
  */
 static int
-mptscsih_TMHandler(MPT_SCSI_HOST *hd, u8 type, u8 target, u8 lun, int ctx2abort, int sleepFlag)
+mptscsih_TMHandler(MPT_SCSI_HOST *hd, u8 type, u8 channel, u8 target, u8 lun, int ctx2abort, int sleepFlag)
 {
 	MPT_ADAPTER	*ioc = NULL;
 	int		 rc = -1;
@@ -2792,19 +2852,18 @@
 		return 0;
 
 	ioc = hd->ioc;
-	dtmprintk((MYIOC_s_INFO_FMT "TMHandler Entered!\n", ioc->name));
-
 	if (ioc == NULL) {
 		printk(KERN_ERR MYNAM " TMHandler" " NULL ioc!\n");
-		return 0;
+		return FAILED;
 	}
+	dtmprintk((MYIOC_s_INFO_FMT "TMHandler Entered!\n", ioc->name));
 
 	// SJR - CHECKME - Can we avoid this here?
 	// (mpt_HardResetHandler has this check...)
 	spin_lock_irqsave(&ioc->diagLock, flags);
 	if ((ioc->diagPending) || (ioc->alt_ioc && ioc->alt_ioc->diagPending)) {
 		spin_unlock_irqrestore(&ioc->diagLock, flags);
-		return 0;
+		return FAILED;
 	}
 	spin_unlock_irqrestore(&ioc->diagLock, flags);
 
@@ -2814,6 +2873,39 @@
 	if (hd->numTMrequests > MPT_HOST_TOO_MANY_TM)
 		doTask = 0;
 
+#ifdef MPT_SCSI_USE_NEW_EH
+	/*  Wait a fixed amount of time for the TM pending flag to be cleared.
+	 *  If we time out and not bus reset, then we return a FAILED status to the caller.
+	 *  The call to mptscsih_tm_pending_wait() will set the pending flag if we are
+	 *  successful. Otherwise, reload the FW.
+	 */
+	if (mptscsih_tm_pending_wait(hd) == FAILED) {
+		if (type == MPI_SCSITASKMGMT_TASKTYPE_ABORT_TASK) {
+			nehprintk((KERN_WARNING MYNAM ": %s: TMHandler abort: "
+			   "Timed out waiting for last TM (%d) to complete! \n",
+			   hd->ioc->name, hd->tmPending));
+			return FAILED;
+		} else if (type == MPI_SCSITASKMGMT_TASKTYPE_TARGET_RESET) {
+			nehprintk((KERN_WARNING MYNAM ": %s: TMHandler target reset: "
+			   "Timed out waiting for last TM (%d) to complete! \n",
+			   hd->ioc->name, hd->tmPending));
+			return FAILED;
+		} else if (type == MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS) {
+			nehprintk((KERN_WARNING MYNAM ": %s: TMHandler bus reset: "
+			   "Timed out waiting for last TM (%d) to complete! \n",
+			   hd->ioc->name, hd->tmPending));
+			if (hd->tmPending & (1 << MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS))
+				return FAILED;
+				
+			doTask = 0;
+		}
+	} else {
+		spin_lock_irqsave(&hd->ioc->FreeQlock, flags);
+		hd->tmPending |=  (1 << type);
+		spin_unlock_irqrestore(&hd->ioc->FreeQlock, flags);
+	}
+#endif
+
 	/* Is operational?
 	 */
 	ioc_raw_state = mpt_GetIocState(hd->ioc, 0);
@@ -2821,7 +2913,7 @@
 #ifdef MPT_DEBUG_RESET
 	if ((ioc_raw_state & MPI_IOC_STATE_MASK) != MPI_IOC_STATE_OPERATIONAL) {
 		printk(MYIOC_s_WARN_FMT
-			"TM Handler: IOC Not operational! state 0x%x Calling HardResetHandler\n",
+			"TM Handler: IOC Not operational(0x%x)!\n",
 			hd->ioc->name, ioc_raw_state);
 	}
 #endif
@@ -2833,23 +2925,23 @@
 		 */
 		if (hd->hard_resets < -1)
 			hd->hard_resets++;
-		rc = mptscsih_IssueTaskMgmt(hd, type, target, lun, ctx2abort, sleepFlag);
+		rc = mptscsih_IssueTaskMgmt(hd, type, channel, target, lun, ctx2abort, sleepFlag);
 		if (rc) {
 			printk(MYIOC_s_INFO_FMT "Issue of TaskMgmt failed!\n", hd->ioc->name);
 		} else {
 			dtmprintk((MYIOC_s_INFO_FMT "Issue of TaskMgmt Successful!\n", hd->ioc->name));
 		}
 	}
-#ifdef DROP_TEST
-	numTMrequested++;
-	if (numTMrequested > 5) {
-		rc = 0;		/* set to 1 to force a hard reset */
-		numTMrequested = 0;
-	}
+#ifdef MPTSCSIH_DBG_TIMEOUT
+	if (hd->ioc->timeout_hard)
+		rc = 1;
 #endif
 
-	if (rc || ioc->reload_fw || (ioc->alt_ioc && ioc->alt_ioc->reload_fw)) {
-		dtmprintk((MYIOC_s_INFO_FMT "Falling through to HardReset! \n",
+	/* Only fall through to the HRH if this is a bus reset
+	 */
+	if ((type == MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS) && (rc ||
+		ioc->reload_fw || (ioc->alt_ioc && ioc->alt_ioc->reload_fw))) {
+		dtmprintk((MYIOC_s_INFO_FMT "Calling HardReset! \n",
 			 hd->ioc->name));
 		rc = mpt_HardResetHandler(hd->ioc, sleepFlag);
 	}
@@ -2863,7 +2955,6 @@
 	return rc;
 }
 
-
 /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
 /*
  *	mptscsih_IssueTaskMgmt - Generic send Task Management function.
@@ -2883,7 +2974,7 @@
  *	else other non-zero value returned.
  */
 static int
-mptscsih_IssueTaskMgmt(MPT_SCSI_HOST *hd, u8 type, u8 target, u8 lun, int ctx2abort, int sleepFlag)
+mptscsih_IssueTaskMgmt(MPT_SCSI_HOST *hd, u8 type, u8 channel, u8 target, u8 lun, int ctx2abort, int sleepFlag)
 {
 	MPT_FRAME_HDR	*mf;
 	SCSITaskMgmt_t	*pScsiTm;
@@ -2905,7 +2996,7 @@
 	 */
 	pScsiTm = (SCSITaskMgmt_t *) mf;
 	pScsiTm->TargetID = target;
-	pScsiTm->Bus = hd->port;
+	pScsiTm->Bus = channel;
 	pScsiTm->ChainOffset = 0;
 	pScsiTm->Function = MPI_FUNCTION_SCSI_TASK_MGMT;
 
@@ -2979,6 +3070,9 @@
 		return FAILED;
 	}
 
+	if (hd->resetPending)
+		return FAILED;
+
 	printk(KERN_WARNING MYNAM ": %s: >> Attempting task abort! (sc=%p, numIOs=%d)\n",
 	       hd->ioc->name, SCpnt, atomic_read(&queue_depth));
 
@@ -3001,19 +3095,6 @@
 		return SUCCESS;
 	}
 
-	/*  Wait a fixed amount of time for the TM pending flag to be cleared.
-	 *  If we time out, then we return a FAILED status to the caller.  This
-	 *  call to mptscsih_tm_pending_wait() will set the pending flag if we are
-	 *  successful.
-	 */
-	if (mptscsih_tm_pending_wait(hd) == FAILED){
-		nehprintk((KERN_WARNING MYNAM ": %s: mptscsih_abort: "
-			   "Timed out waiting for previous TM to complete! "
-			   "(sc = %p)\n",
-			   hd->ioc->name, SCpnt));
-		return FAILED;
-	}
-
 	/* If this command is pended, then timeout/hang occurred
 	 * during DV. Post command and flush pending Q
 	 * and then following up with the reset request.
@@ -3022,7 +3103,7 @@
 		mptscsih_put_msgframe(ScsiDoneCtx, hd->ioc->id, mf);
 		post_pendingQ_commands(hd);
 		nehprintk((KERN_WARNING MYNAM ": %s: mptscsih_abort: "
-			   "Found command in pending queue! (sc=%p)\n",
+			   "Posting pended cmd! (sc=%p)\n",
 			   hd->ioc->name, SCpnt));
 	}
 
@@ -3038,7 +3119,7 @@
 
 	hd->abortSCpnt = SCpnt;
 	if (mptscsih_TMHandler(hd, MPI_SCSITASKMGMT_TASKTYPE_ABORT_TASK,
-	                       SCpnt->target, SCpnt->lun, ctx2abort, NO_SLEEP)
+	                       SCpnt->channel, SCpnt->target, SCpnt->lun, ctx2abort, NO_SLEEP)
 		< 0) {
 
 		/* The TM request failed and the subsequent FW-reload failed!
@@ -3081,29 +3162,19 @@
 		return FAILED;
 	}
 
+	if (hd->resetPending)
+		return FAILED;
+
 	printk(KERN_WARNING MYNAM ": %s: >> Attempting target reset! (sc=%p, numIOs=%d)\n",
 	       hd->ioc->name, SCpnt, atomic_read(&queue_depth));
 
-	/* Unsupported for SCSI. Suppored for FCP
+	/* Unsupported for SCSI. Supported for FCP
 	 */
 	if (hd->is_spi)
 		return FAILED;
 
-	/*  Wait a fixed amount of time for the TM pending flag to be cleared.
-	 *  If we time out, then we return a FAILED status to the caller.  This
-	 *  call to mptscsih_tm_pending_wait() will set the pending flag if we are
-	 *  successful.
-	 */
-	if (mptscsih_tm_pending_wait(hd) == FAILED) {
-		nehprintk((KERN_WARNING MYNAM ": %s: mptscsih_dev_reset: "
-			   "Timed out waiting for previous TM to complete! "
-			   "(sc = %p)\n",
-			   hd->ioc->name, SCpnt));
-		return FAILED;
-	}
-
 	if (mptscsih_TMHandler(hd, MPI_SCSITASKMGMT_TASKTYPE_TARGET_RESET,
-	                       SCpnt->target, 0, 0, NO_SLEEP)
+	                       SCpnt->channel, SCpnt->target, 0, 0, NO_SLEEP)
 		< 0){
 		/* The TM request failed and the subsequent FW-reload failed!
 		 * Fatal error case.
@@ -3147,22 +3218,9 @@
 	if (hd->timeouts < -1)
 		hd->timeouts++;
 
-	/*  Wait a fixed amount of time for the TM pending flag to be cleared.
-	 *  If we time out, then we return a FAILED status to the caller.  This
-	 *  call to mptscsih_tm_pending_wait() will set the pending flag if we are
-	 *  successful.
-	 */
-	if (mptscsih_tm_pending_wait(hd) == FAILED) {
-		nehprintk((KERN_WARNING MYNAM ": %s: mptscsih_bus_reset: "
-			   "Timed out waiting for previous TM to complete! "
-			   "(sc = %p)\n",
-			   hd->ioc->name, SCpnt));
-		return FAILED;
-	}
-
 	/* We are now ready to execute the task management request. */
 	if (mptscsih_TMHandler(hd, MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS,
-	                       0, 0, 0, NO_SLEEP)
+	                       SCpnt->channel, 0, 0, 0, NO_SLEEP)
 	    < 0){
 
 		/* The TM request failed and the subsequent FW-reload failed!
@@ -3241,7 +3299,7 @@
 mptscsih_tm_pending_wait(MPT_SCSI_HOST * hd)
 {
 	unsigned long  flags;
-	int            loop_count = 60 * 4;  /* Wait 60 seconds */
+	int            loop_count = 2 * 10 * 4;  /* Wait 2 seconds */
 	int            status = FAILED;
 
 	do {
@@ -3254,6 +3312,8 @@
 			break;
 		}
 		spin_unlock_irqrestore(&hd->ioc->FreeQlock, flags);
+		//set_current_state(TASK_INTERRUPTIBLE);
+		//schedule_timeout(HZ/4);
 		mdelay(250);
 	} while (--loop_count);
 
@@ -3382,10 +3442,6 @@
 		mytaskQ_bh_active = 1;
 		spin_unlock_irqrestore(&mytaskQ_lock, flags);
 
-		/*
-		 *  Oh how cute, no alloc/free/mgmt needed if we use
-		 *  (bottom/unused portion of) MPT request frame.
-		 */
 		ptaskfoo = (struct mpt_work_struct *) &mptscsih_ptaskfoo;
 		MPT_INIT_WORK(&mptscsih_ptaskfoo, mptscsih_taskmgmt_bh, (void *) SCpnt);
 
@@ -3570,6 +3626,15 @@
 					continue;
 				}
 
+#ifdef MPTSCSIH_DBG_TIMEOUT
+				if (ioc->timeout_hard == 1) {
+					mptscsih_TMHandler(hd, 
+						MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS, 
+						0, 0, 0, 0, CAN_SLEEP);
+
+				}
+#endif
+
 				spin_lock_irqsave(&ioc->FreeQlock, flags);
 				if (Q_IS_EMPTY(&hd->taskQ)) {
 					spin_unlock_irqrestore(&ioc->FreeQlock, flags);
@@ -3662,7 +3727,9 @@
 				mpt_free_msg_frame(ScsiTaskCtx, hd->ioc->id, mf);
 				mf = NULL;
 
-				if (mptscsih_TMHandler(hd, task_type, SCpnt->target, SCpnt->lun, ctx2abort, NO_SLEEP) < 0) {
+				if (mptscsih_TMHandler(hd, task_type, SCpnt->channel,
+						      SCpnt->target, SCpnt->lun,
+						       ctx2abort, CAN_SLEEP) < 0) {
 
 					/* The TM request failed and the subsequent FW-reload failed!
 					 * Fatal error case.
@@ -3768,10 +3835,7 @@
 			 */
 			if (tmType == MPI_SCSITASKMGMT_TASKTYPE_ABORT_TASK)
 				hd->abortSCpnt = NULL;
-#ifdef	DROP_TEST
-			if (dropMfPtr)
-				dropTestBad++;
-#endif
+
 			/* If an internal command is present
 			 * or the TM failed - reload the FW.
 			 * FC FW may respond FAILED to an ABORT
@@ -3805,17 +3869,9 @@
 			hd->abortSCpnt = NULL;
 			flush_doneQ(hd);
 
-#ifdef	DROP_TEST
-			if (dropMfPtr)
-				dropTestOK++;
-#endif
 		}
 	}
 
-#ifdef	DROP_TEST
-	mptscsih_flush_drop_test(hd);
-#endif
-
 #ifndef MPT_SCSI_USE_NEW_EH
 	/*
 	 *  Signal to _bh thread that we finished.
@@ -3840,19 +3896,9 @@
  *	This is anyones guess quite frankly.
  */
 int
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,45)
-mptscsih_bios_param(struct scsi_device * sdev, struct block_device *bdev,
-		sector_t capacity, int *ip)
-{
-#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,28)
-mptscsih_bios_param(Disk * disk, struct block_device *bdev, int *ip)
-{
-	sector_t capacity = disk->capacity;
-#else
 mptscsih_bios_param(Disk * disk, kdev_t dev, int *ip)
 {
 	unsigned capacity = disk->capacity;
-#endif
 	int size;
 
 	size = capacity;
@@ -3872,43 +3918,6 @@
  *	Called once per device the bus scan. Use it to force the queue_depth
  *	member to 1 if a device does not support Q tags.
  */
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,44)
-int
-mptscsih_slave_attach(Scsi_Device *device)
-{
-	struct Scsi_Host	*host = device->host;
-	VirtDevice		*pTarget;
-	MPT_SCSI_HOST		*hd;
-
-	hd = (MPT_SCSI_HOST *)host->hostdata;
-	if (hd && (hd->Targets != NULL)) {
-		pTarget = hd->Targets[device->id];
-		if (pTarget) {
-			if (!device->tagged_supported ||
-			    !(pTarget->tflags & MPT_TARGET_FLAGS_Q_YES)) {
-				scsi_adjust_queue_depth(device, 0, 1);
-
-			} else if ((pTarget->tflags & MPT_TARGET_FLAGS_VALID_INQUIRY)
-				   && (pTarget->inq_data[0] & 0x1f) == 0x00
-				   && (pTarget->minSyncFactor <= MPT_ULTRA160 || !hd->is_spi)) {
-				scsi_adjust_queue_depth(device, MSG_SIMPLE_TAG,
-							MPT_SCSI_CMD_PER_DEV_HIGH);
-			} else {
-				scsi_adjust_queue_depth(device, MSG_SIMPLE_TAG,
-							MPT_SCSI_CMD_PER_DEV_LOW);
-			}
-#if 0
-			/* Original Code for 2.5 */
-			} else {
-				scsi_adjust_queue_depth(device, MSG_SIMPLE_TAG,
-						device->host->can_queue >> 1);
-			}
-#endif
-		}
-	}
-	return 0;
-}
-#else /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,44) */
 void
 mptscsih_select_queue_depths(struct Scsi_Host *sh, Scsi_Device *sdList)
 {
@@ -3951,14 +3960,21 @@
 				/* error case - No Inq. Data */
 				device->queue_depth = 1;
 			}
-			dprintk((MYIOC_s_INFO_FMT
-				 "target = %d, sync factor = %x, queue depth = %d\n",
-				 hd->ioc->name, pTarget->target_id,
-				 pTarget->minSyncFactor, device->queue_depth));
+
+			if (pTarget != NULL) {
+				printk(MYIOC_s_INFO_FMT
+					 "scsi%d: Id=%d Lun=%d: Queue depth=%d\n",
+					 hd->ioc->name, sh->host_no,
+					 device->id, device->lun, device->queue_depth);
+
+				dprintk((MYIOC_s_INFO_FMT
+					 "Id = %d, sync factor = %x\n",
+					 hd->ioc->name, pTarget->target_id,
+					 pTarget->minSyncFactor));
+			}
 		}
 	}
 }
-#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,44) */
 
 /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
 /*
@@ -3997,34 +4013,6 @@
 		sense_data = ((u8 *)hd->ioc->sense_buf_pool + (req_index * MPT_SENSE_BUFFER_ALLOC));
 		memcpy(sc->sense_buffer, sense_data, SNS_LEN(sc));
 
-		/* save sense data to the target device
-		 */
-		if (hd->is_spi && target) {
-#ifdef MPT_SAVE_AUTOSENSE
-			int sz;
-
-			sz = MIN(pReq->SenseBufferLength, sense_count);
-			if (sz > SCSI_STD_SENSE_BYTES)
-				sz =  SCSI_STD_SENSE_BYTES;
-			memcpy(target->sense, sense_data, sz);
-			target->tflags |= MPT_TARGET_FLAGS_VALID_SENSE;
-#endif
-
-#ifdef ABORT_FIX
-			if (sz >= SCSI_STD_SENSE_BYTES) {
-				if ((sense_data[02] == ABORTED_COMMAND) &&
-					(sense_data[12] == 0x47) && (sense_data[13] == 0x00)){
-					target->numAborts++;
-					if ((target->raidVolume == 0) && (target->numAborts > 5)) {
-						target->numAborts = 0;
-						target->minSyncFactor++;
-						hd->ioc->spi_data.dvStatus[index] |= MPT_SCSICFG_NEGOTIATE;
-					}
-				}
-			}
-#endif
-		}
-
 		/* Log SMART data (asc = 0x5D, non-IM case only) if required.
 		 */
 		if ((hd->ioc->events) && (hd->ioc->eventTypes & (1 << MPI_EVENT_SCSI_DEVICE_STATUS_CHANGE))) {
@@ -4207,7 +4195,8 @@
 
 	dtmprintk((KERN_WARNING MYNAM
 			": IOC %s_reset routed to SCSI host driver!\n",
-			reset_phase==MPT_IOC_PRE_RESET ? "pre" : "post"));
+			reset_phase==MPT_IOC_SETUP_RESET ? "setup" : (
+			reset_phase==MPT_IOC_PRE_RESET ? "pre" : "post")));
 
 	/* If a FW reload request arrives after base installed but
 	 * before all scsi hosts have been attached, then an alt_ioc
@@ -4218,28 +4207,26 @@
 	else
 		hd = (MPT_SCSI_HOST *) ioc->sh->hostdata;
 
-	if (reset_phase == MPT_IOC_PRE_RESET) {
-		dtmprintk((MYIOC_s_WARN_FMT "Do Pre-Diag Reset handling\n",
-			ioc->name));
-
+	if (reset_phase == MPT_IOC_SETUP_RESET) {
+		dtmprintk((MYIOC_s_WARN_FMT "Setup-Diag Reset\n", ioc->name));
 		/* Clean Up:
 		 * 1. Set Hard Reset Pending Flag
 		 * All new commands go to doneQ
 		 */
 		hd->resetPending = 1;
 
+		/* 2. Reset timeouts on all running commands
+		 */
+		mptscsih_reset_timeouts (hd);
+
+	} else if (reset_phase == MPT_IOC_PRE_RESET) {
+		dtmprintk((MYIOC_s_WARN_FMT "Pre-Diag Reset\n", ioc->name));
+
 		/* 2. Flush running commands
-		 *	Clean drop test code - if compiled
 		 *	Clean ScsiLookup (and associated memory)
 		 *	AND clean mytaskQ
 		 */
 
-		/* 2a. Drop Test Command.
-		 */
-#ifdef	DROP_TEST
-		mptscsih_flush_drop_test(hd);
-#endif
-
 		/* 2b. Reply to OS all known outstanding I/O commands.
 		 */
 		mptscsih_flush_running_cmds(hd);
@@ -4269,14 +4256,16 @@
 		 */
 		clean_taskQ(hd);
 #endif
-		dtmprintk((MYIOC_s_WARN_FMT "Pre-Reset handling complete.\n",
-			ioc->name));
+		
+#ifdef MPTSCSIH_DBG_TIMEOUT
+		ioc->timeout_hard = 0;
+#endif
 
+		dtmprintk((MYIOC_s_WARN_FMT "Pre-Reset complete.\n", ioc->name));
 	} else {
 		ScsiCfgData	*pSpi = NULL;
 
-		dtmprintk((MYIOC_s_WARN_FMT "Do Post-Diag Reset handling\n",
-			ioc->name));
+		dtmprintk((MYIOC_s_WARN_FMT "Post-Diag Reset\n", ioc->name));
 
 		/* Once a FW reload begins, all new OS commands are
 		 * redirected to the doneQ w/ a reset status.
@@ -4335,16 +4324,15 @@
 		 */
 		flush_doneQ(hd);
 
-		dtmprintk((MYIOC_s_WARN_FMT "Post-Reset handling complete.\n",
-			ioc->name));
-
-
-		/* 8. Set then flag to force DV and re-read IOC Page 3
+		/* 8. Set flag to force DV and re-read IOC Page 3
 		 */
-		pSpi = &ioc->spi_data;
-		pSpi->forceDv = MPT_SCSICFG_NEED_DV | MPT_SCSICFG_RELOAD_IOC_PG3;
-		ddvtprintk(("Set reload IOC Pg3 Flag\n"));
+		if (hd->is_spi) {
+			pSpi = &ioc->spi_data;
+			pSpi->forceDv = MPT_SCSICFG_NEED_DV | MPT_SCSICFG_RELOAD_IOC_PG3;
+			ddvtprintk(("Set reload IOC Pg3 Flag\n"));
+		}
 
+		dtmprintk((MYIOC_s_WARN_FMT "Post-Reset complete.\n", ioc->name));
 	}
 
 	return 1;		/* currently means nothing really */
@@ -4800,7 +4788,7 @@
 	/*
 	 * Need to check ASC here; if it is "special," then
 	 * the ASCQ is variable, and indicates failed component number.
-	 * We must treat the ASCQ as a "dont care" while searching the
+	 * We must treat the ASCQ as a "don't care" while searching the
 	 * mptscsih_ASCQ_Table[] by masking it off, and then restoring it later
 	 * on when we actually need to identify the failed component.
 	 */
@@ -5079,7 +5067,7 @@
 	u8 factor = MPT_ASYNC;
 	u8 offset = 0;
 	u8 version, nfactor;
-	u8 noQas = 1;
+	u8 noQas = 0;
 
 	ddvtprintk((KERN_INFO "set Target: (id %d) byte56 0x%x\n", id, byte56));
 
@@ -5090,9 +5078,20 @@
 		}
 		return;
 	}
+
+	/* noQas == 0 => device supports QAS. Need byte 56 of Inq to determine
+	 * support. If available, default QAS to off and allow enabling.
+	 * If not available, default QAS to on, turn off for non-disks.
+	 */
+	if (target->tflags & MPT_TARGET_FLAGS_VALID_56)
+		noQas = 1;
+
 	/* Set flags based on Inquiry data
 	 */
 	if (target->tflags & MPT_TARGET_FLAGS_VALID_INQUIRY) {
+		if ((target->inq_data[0] & 0x1F) != 0x00)
+			noQas = 1;
+
 		version = target->inq_data[2] & 0x07;
 		if (version < 2) {
 			width = 0;
@@ -5200,7 +5199,7 @@
 
 		/* Disable QAS if mixed configuration case
 		 */
-		if ((noQas) && (!pspi_data->noQas) && ((target->inq_data[0] & 0x1F) == 0x00)){
+		if ((noQas) && (!pspi_data->noQas) && ((target->inq_data[0] & 0x1F) != 0x03)){
 			VirtDevice	*vdev;
 			int ii;
 
@@ -5212,29 +5211,10 @@
 					vdev->negoFlags |= MPT_TARGET_NO_NEGO_QAS;
 			}
 		}
-
-	}
-
-	return;
-}
-
-#ifdef MPT_SAVE_AUTOSENSE
-/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
-/*
- *  Clear sense valid flag.
- */
-static void clear_sense_flag(MPT_SCSI_HOST *hd, SCSIIORequest_t *pReq)
-{
-	VirtDevice	*target;
-	int		 index = (int) pReq->TargetID;
-
-	if ((target = hd->Targets[index])) {
-		target->tflags &= ~MPT_TARGET_FLAGS_VALID_SENSE;
 	}
 
 	return;
 }
-#endif
 
 /* If DV disabled (negoNvram set to USE_NVARM) or if not LUN 0, return.
  * Else set the NEED_DV flag after Read Capacity Issued (disks)
@@ -5637,10 +5617,6 @@
 
 	/* If target struct exists, clear sense valid flag.
 	 */
-#ifdef MPT_SAVE_AUTOSENSE
-	clear_sense_flag(hd, pReq);
-#endif
-
 	if (mr == NULL) {
 		completionCode = MPT_SCANDV_GOOD;
 	} else {
@@ -5691,13 +5667,10 @@
 					completionCode = MPT_SCANDV_SOME_ERROR;
 
 			} else if (pReply->SCSIState & MPI_SCSI_STATE_AUTOSENSE_VALID) {
-#ifdef MPT_SAVE_AUTOSENSE
-				VirtDevice	*target;
-#endif
 				u8		*sense_data;
 				int		 sz;
 
-				/* save sense data in global & target structure
+				/* save sense data in global structure
 				 */
 				completionCode = MPT_SCANDV_SENSE;
 				hd->pLocal->scsiStatus = pReply->SCSIStatus;
@@ -5708,15 +5681,6 @@
 							SCSI_STD_SENSE_BYTES);
 				memcpy(hd->pLocal->sense, sense_data, sz);
 
-#ifdef MPT_SAVE_AUTOSENSE
-				target = hd->Targets[pReq->TargetID];
-				if (target) {
-					memcpy(target->sense, sense_data, sz);
-					target->tflags
-						|= MPT_TARGET_FLAGS_VALID_SENSE;
-				}
-#endif
-
 				ddvprintk((KERN_NOTICE "  Check Condition, sense ptr %p\n",
 						sense_data));
 			} else if (pReply->SCSIState & MPI_SCSI_STATE_AUTOSENSE_FAILED) {
@@ -5799,6 +5763,8 @@
 			 *	for bus reset.
 			 */
 #ifndef MPT_SCSI_USE_NEW_EH
+			SCSIIORequest_t	*pReq = (SCSIIORequest_t *) hd->cmdPtr;
+
 			spin_lock_irqsave(&hd->ioc->FreeQlock, flags);
 			if (hd->tmPending) {
 				spin_unlock_irqrestore(&hd->ioc->FreeQlock, flags);
@@ -5806,9 +5772,8 @@
 			} else
 				hd->tmPending = 1;
 			spin_unlock_irqrestore(&hd->ioc->FreeQlock, flags);
-
 			if (mptscsih_TMHandler(hd, MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS,
-							0, 0, 0, NO_SLEEP) < 0) {
+							pReq->Bus, 0, 0, 0, NO_SLEEP) < 0) {
 				printk(MYIOC_s_WARN_FMT "TM FAILED!\n", hd->ioc->name);
 			}
 #else
@@ -6527,7 +6492,7 @@
  *	Return: None.
  */
 static int
-mptscsih_doDv(MPT_SCSI_HOST *hd, int portnum, int id)
+mptscsih_doDv(MPT_SCSI_HOST *hd, int bus_number, int id)
 {
 	MPT_ADAPTER		*ioc = hd->ioc;
 	VirtDevice		*pTarget = NULL;
@@ -6555,6 +6520,7 @@
 	int			 patt;
 	int			 repeat;
 	int			 retcode = 0;
+	int			 nfactor =  MPT_ULTRA320;
 	char			 firstPass = 1;
 	char			 doFallback = 0;
 	char			 readPage0;
@@ -6567,11 +6533,14 @@
 	if (ioc->spi_data.sdp0length == 0)
 		return 0;
 
-	if (id == ioc->pfacts[portnum].PortSCSIID)
+	/* If multiple buses are used, require that the initiator
+	 * id be the same on all buses.
+	 */
+	if (id == ioc->pfacts[0].PortSCSIID)
 		return 0;
 
 	lun = 0;
-	bus = 0;
+	bus = (u8) bus_number;
 	ddvtprintk((MYIOC_s_NOTE_FMT
 			"DV started: numIOs %d bus=%d, id %d dv @ %p\n",
 			ioc->name, atomic_read(&queue_depth), bus, id, &dv));
@@ -6676,16 +6645,33 @@
 
 	/* Skip this ID? Set cfg.hdr to force config page write
 	 */
-	if ((ioc->spi_data.nvram[id] != MPT_HOST_NVRAM_INVALID) &&
-			(!(ioc->spi_data.nvram[id] & MPT_NVRAM_ID_SCAN_ENABLE))) {
-
-		ddvprintk((MYIOC_s_NOTE_FMT "DV Skipped: bus, id, lun (%d, %d, %d)\n",
-			ioc->name, bus, id, lun));
-
-		dv.cmd = MPT_SET_MAX;
-		mptscsih_dv_parms(hd, &dv, (void *)pcfg1Data);
-		cfg.hdr = &header1;
-		goto target_done;
+	{
+		ScsiCfgData *pspi_data = &hd->ioc->spi_data;
+		if (pspi_data->nvram && (pspi_data->nvram[id] != MPT_HOST_NVRAM_INVALID)) {
+			/* Set the factor from nvram */
+			nfactor = (pspi_data->nvram[id] & MPT_NVRAM_SYNC_MASK) >> 8;
+			if (nfactor < pspi_data->minSyncFactor )
+				nfactor = pspi_data->minSyncFactor;
+	
+			if (!(pspi_data->nvram[id] & MPT_NVRAM_ID_SCAN_ENABLE)) {
+	
+				ddvprintk((MYIOC_s_NOTE_FMT "DV Skipped: bus, id, lun (%d, %d, %d)\n",
+					ioc->name, bus, id, lun));
+	
+				dv.cmd = MPT_SET_MAX;
+				mptscsih_dv_parms(hd, &dv, (void *)pcfg1Data);
+				cfg.hdr = &header1;
+	
+				/* Save the final negotiated settings to
+				 * SCSI device page 1.
+				 */
+				cfg.physAddr = cfg1_dma_addr;
+				cfg.action = MPI_CONFIG_ACTION_PAGE_WRITE_CURRENT;
+				cfg.dir = 1;
+				mpt_config(hd->ioc, &cfg);
+				goto target_done;
+			}
+		}
 	}
 
 	/* Finish iocmd inititialization - hidden or visible disk? */
@@ -6807,6 +6793,14 @@
 			}
 		}
 
+		/* Reset the size for disks
+		 */
+		inq0 = (*pbuf1) & 0x1F;
+		if ((inq0 == 0) && pTarget && !pTarget->raidVolume) {
+			sz = 0x40;
+			iocmd.size = sz;
+		}
+
 		/* Another GEM workaround. Check peripheral device type,
 		 * if PROCESSOR, quit DV.
 		 */
@@ -6816,6 +6810,26 @@
 		if (mptscsih_do_cmd(hd, &iocmd) < 0)
 			goto target_done;
 
+		if (sz == 0x40) {
+			if ((pTarget->maxWidth == 1) && (pTarget->maxOffset) && (nfactor < 0x0A)
+				&& (pTarget->minSyncFactor > 0x09)) {
+				if ((pbuf1[56] & 0x04) == 0)
+					;
+				else if ((pbuf1[56] & 0x01) == 1) {
+					pTarget->minSyncFactor = nfactor > MPT_ULTRA320 ? nfactor : MPT_ULTRA320;
+				} else {
+					pTarget->minSyncFactor = nfactor > MPT_ULTRA160 ? nfactor : MPT_ULTRA160;
+				}
+
+				dv.max.factor = pTarget->minSyncFactor;
+
+				if ((pbuf1[56] & 0x02) == 0) {
+					pTarget->negoFlags |= MPT_TARGET_NO_NEGO_QAS;
+					hd->ioc->spi_data.noQas = MPT_TARGET_NO_NEGO_QAS;
+				}
+			}
+		}
+
 		if (doFallback)
 			dv.cmd = MPT_FALLBACK;
 		else
@@ -7323,19 +7337,16 @@
 		dv.cmd = MPT_SAVE;
 		mptscsih_dv_parms(hd, &dv, (void *)pcfg1Data);
 
-#if 0	
-	/* Double writes to SDP1 can cause problems,
-	 * skip here since unnecessary
-	 */
-		/* Save the final negotiated settings to
+		/* Double writes to SDP1 can cause problems,
+		 * skip save of the final negotiated settings to
 		 * SCSI device page 1.
-		 */
+		 *
 		cfg.hdr = &header1;
 		cfg.physAddr = cfg1_dma_addr;
 		cfg.action = MPI_CONFIG_ACTION_PAGE_WRITE_CURRENT;
 		cfg.dir = 1;
 		mpt_config(hd->ioc, &cfg);
-#endif
+		 */
 	}
 
 	/* If this is a RAID Passthrough, enable internal IOs
diff -urN linux-2.4.22-pre3/drivers/message/fusion/mptscsih.h linux-2.4.22-pre4/drivers/message/fusion/mptscsih.h
--- linux-2.4.22-pre3/drivers/message/fusion/mptscsih.h	2003-07-09 16:56:24.000000000 -0700
+++ linux-2.4.22-pre4/drivers/message/fusion/mptscsih.h	2003-07-09 16:56:28.000000000 -0700
@@ -18,9 +18,9 @@
  *  Copyright (c) 1999-2002 LSI Logic Corporation
  *  Originally By: Steven J. Ralston
  *  (mailto:sjralston1@netscape.net)
- *  (mailto:lstephen@lsil.com)
+ *  (mailto:lstephens@lsil.com)
  *
- *  $Id: mptscsih.h,v 1.22 2002/12/16 15:28:48 pdelaney Exp $
+ *  $Id: mptscsih.h,v 1.1.2.2 2003/05/07 14:08:35 pdelaney Exp $
  */
 /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
 /*
@@ -72,7 +72,7 @@
 /*
  *	Try to keep these at 2^N-1
  */
-#define MPT_FC_CAN_QUEUE	63
+#define MPT_FC_CAN_QUEUE	127
 #if defined MPT_SCSI_USE_NEW_EH
 	#define MPT_SCSI_CAN_QUEUE	127
 #else
@@ -217,11 +217,7 @@
 #define x_scsi_taskmgmt_bh	mptscsih_taskmgmt_bh
 #define x_scsi_old_abort	mptscsih_old_abort
 #define x_scsi_old_reset	mptscsih_old_reset
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,44)
-#define x_scsi_slave_attach	mptscsih_slave_attach
-#else
 #define x_scsi_select_queue_depths	mptscsih_select_queue_depths
-#endif
 #define x_scsi_proc_info	mptscsih_proc_info
 
 /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
@@ -241,86 +237,18 @@
 extern	int		 x_scsi_old_abort(Scsi_Cmnd *);
 extern	int		 x_scsi_old_reset(Scsi_Cmnd *, unsigned int);
 #endif
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,45)
-extern int		 x_scsi_bios_param(struct scsi_device * sdev, struct block_device *bdev,
-				sector_t capacity, int *ip);
-#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,28)
-extern	int		 x_scsi_bios_param(Disk *, struct block_device *, int *);
-#else
 extern	int		 x_scsi_bios_param(Disk *, kdev_t, int *);
-#endif
 extern	void		 x_scsi_taskmgmt_bh(void *);
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,44)
-extern	int		 x_scsi_slave_attach(Scsi_Device *);
-#else
 extern	void		 x_scsi_select_queue_depths(struct Scsi_Host *, Scsi_Device *);
-#endif
-
 extern	int		 x_scsi_proc_info(char *, char **, off_t, int, int, int);
 
 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)
 #define PROC_SCSI_DECL
 #else
-#define PROC_SCSI_DECL  proc_name: "mptscsih",
+#define PROC_SCSI_DECL  .proc_name = "mptscsih",
 #endif
 
 #ifdef MPT_SCSI_USE_NEW_EH
-
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,44)
-
-#define MPT_SCSIHOST {						\
-	PROC_SCSI_DECL						\
-	.proc_info			= x_scsi_proc_info,	\
-	.name				= "MPT SCSI Host",	\
-	.detect				= x_scsi_detect,	\
-	.release			= x_scsi_release,	\
-	.info				= x_scsi_info,		\
-	.command			= NULL,			\
-	.queuecommand			= x_scsi_queuecommand,	\
-	.slave_attach			= x_scsi_slave_attach,	\
-	.eh_strategy_handler		= NULL,			\
-	.eh_abort_handler		= x_scsi_abort,		\
-	.eh_device_reset_handler	= x_scsi_dev_reset,	\
-	.eh_bus_reset_handler		= x_scsi_bus_reset,	\
-	.eh_host_reset_handler		= x_scsi_host_reset,	\
-	.bios_param			= x_scsi_bios_param,	\
-	.can_queue			= MPT_SCSI_CAN_QUEUE,	\
-	.this_id			= -1,			\
-	.sg_tablesize			= MPT_SCSI_SG_DEPTH,	\
-	.max_sectors			= MPT_SCSI_MAX_SECTORS,	\
-	.cmd_per_lun			= MPT_SCSI_CMD_PER_LUN,	\
-	.unchecked_isa_dma		= 0,			\
-	.use_clustering			= ENABLE_CLUSTERING,	\
-}
-
-#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,1)
-
-#define MPT_SCSIHOST {						\
-	PROC_SCSI_DECL						\
-	.proc_info			= x_scsi_proc_info,	\
-	.name				= "MPT SCSI Host",	\
-	.detect				= x_scsi_detect,	\
-	.release			= x_scsi_release,	\
-	.info				= x_scsi_info,		\
-	.command			= NULL,			\
-	.queuecommand			= x_scsi_queuecommand,	\
-	.eh_strategy_handler		= NULL,			\
-	.eh_abort_handler		= x_scsi_abort,		\
-	.eh_device_reset_handler	= x_scsi_dev_reset,	\
-	.eh_bus_reset_handler		= x_scsi_bus_reset,	\
-	.eh_host_reset_handler		= x_scsi_host_reset,	\
-	.bios_param			= x_scsi_bios_param,	\
-	.can_queue			= MPT_SCSI_CAN_QUEUE,	\
-	.this_id			= -1,			\
-	.sg_tablesize			= MPT_SCSI_SG_DEPTH,	\
-	.max_sectors			= MPT_SCSI_MAX_SECTORS,	\
-	.cmd_per_lun			= MPT_SCSI_CMD_PER_LUN,	\
-	.unchecked_isa_dma		= 0,			\
-	.use_clustering			= ENABLE_CLUSTERING,	\
-}
-
-#else  /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,1) */
-
 #define MPT_SCSIHOST {						\
 	.next				= NULL,			\
 	PROC_SCSI_DECL						\
@@ -346,8 +274,6 @@
 	.use_new_eh_code		= 1			\
 }
 
-#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,1) */
-
 #else /* MPT_SCSI_USE_NEW_EH */
 
 #define MPT_SCSIHOST {						\
diff -urN linux-2.4.22-pre3/drivers/message/fusion/scsi3.h linux-2.4.22-pre4/drivers/message/fusion/scsi3.h
--- linux-2.4.22-pre3/drivers/message/fusion/scsi3.h	2003-07-09 16:56:24.000000000 -0700
+++ linux-2.4.22-pre4/drivers/message/fusion/scsi3.h	2003-07-09 16:56:28.000000000 -0700
@@ -7,7 +7,7 @@
  *  Copyright (c) 1996-2002 Steven J. Ralston
  *  Written By: Steven J. Ralston (19960517)
  *  (mailto:sjralston1@netscape.net)
- *  (mailto:lstephen@lsil.com)
+ *  (mailto:lstephens@lsil.com)
  *
  *  $Id: scsi3.h,v 1.9 2002/02/27 18:45:02 sralston Exp $
  */
diff -urN linux-2.4.22-pre3/drivers/mtd/chips/gen_probe.c linux-2.4.22-pre4/drivers/mtd/chips/gen_probe.c
--- linux-2.4.22-pre3/drivers/mtd/chips/gen_probe.c	2003-06-13 07:51:34.000000000 -0700
+++ linux-2.4.22-pre4/drivers/mtd/chips/gen_probe.c	2003-07-09 16:56:28.000000000 -0700
@@ -277,6 +277,7 @@
 
 extern cfi_cmdset_fn_t cfi_cmdset_0001;
 extern cfi_cmdset_fn_t cfi_cmdset_0002;
+extern cfi_cmdset_fn_t cfi_cmdset_0020;
 
 static inline struct mtd_info *cfi_cmdset_unknown(struct map_info *map, 
 						  int primary)
diff -urN linux-2.4.22-pre3/drivers/net/eexpress.c linux-2.4.22-pre4/drivers/net/eexpress.c
--- linux-2.4.22-pre3/drivers/net/eexpress.c	2003-06-13 07:51:34.000000000 -0700
+++ linux-2.4.22-pre4/drivers/net/eexpress.c	2003-07-09 16:56:29.000000000 -0700
@@ -654,7 +654,7 @@
 		buf = skb_padto(buf, ETH_ZLEN);
 		if(buf == NULL)
 			return 0;
-		length = buf->len;
+		length = ETH_ZLEN;
 	}
 
 	disable_irq(dev->irq);
diff -urN linux-2.4.22-pre3/drivers/net/wan/sdla_chdlc.c linux-2.4.22-pre4/drivers/net/wan/sdla_chdlc.c
--- linux-2.4.22-pre3/drivers/net/wan/sdla_chdlc.c	2002-11-28 15:53:14.000000000 -0800
+++ linux-2.4.22-pre4/drivers/net/wan/sdla_chdlc.c	2003-07-09 16:56:29.000000000 -0700
@@ -591,8 +591,7 @@
 	
 
 		if (chdlc_set_intr_mode(card, APP_INT_ON_TIMER)){
-			printk (KERN_INFO "%s: 
-				Failed to set interrupt triggers!\n",
+			printk (KERN_INFO "%s: Failed to set interrupt triggers!\n",
 				card->devname);
 			return -EIO;	
         	}
@@ -1089,13 +1088,11 @@
 	
 	set_bit(0,&chdlc_priv_area->config_chdlc);
 	chdlc_priv_area->config_chdlc_timeout=jiffies;
-	del_timer(&chdlc_priv_area->poll_delay_timer);
 
 	/* Start the CHDLC configuration after 1sec delay.
 	 * This will give the interface initilization time
 	 * to finish its configuration */
-	chdlc_priv_area->poll_delay_timer.expires=jiffies+HZ;
-	add_timer(&chdlc_priv_area->poll_delay_timer);
+	mod_timer(&chdlc_priv_area->poll_delay_timer, jiffies + HZ);
 	return err;
 }
 
diff -urN linux-2.4.22-pre3/drivers/net/wan/sdla_fr.c linux-2.4.22-pre4/drivers/net/wan/sdla_fr.c
--- linux-2.4.22-pre3/drivers/net/wan/sdla_fr.c	2002-08-02 17:39:44.000000000 -0700
+++ linux-2.4.22-pre4/drivers/net/wan/sdla_fr.c	2003-07-09 16:56:29.000000000 -0700
@@ -4541,9 +4541,7 @@
 {
 	fr_channel_t* chan = dev->priv;
 
-	del_timer(&chan->fr_arp_timer);
-	chan->fr_arp_timer.expires = jiffies + (chan->inarp_interval * HZ);
-	add_timer(&chan->fr_arp_timer);
+	mod_timer(&chan->fr_arp_timer, jiffies + (chan->inarp_interval) * HZ);
 	return;
 }
 
diff -urN linux-2.4.22-pre3/drivers/net/wan/sdla_x25.c linux-2.4.22-pre4/drivers/net/wan/sdla_x25.c
--- linux-2.4.22-pre3/drivers/net/wan/sdla_x25.c	2003-06-13 07:51:35.000000000 -0700
+++ linux-2.4.22-pre4/drivers/net/wan/sdla_x25.c	2003-07-09 16:56:29.000000000 -0700
@@ -1267,9 +1267,7 @@
 			connect(card);
 			S508_S514_unlock(card, &smp_flags);
 
-			del_timer(&card->u.x.x25_timer);
-			card->u.x.x25_timer.expires=jiffies+HZ;
-			add_timer(&card->u.x.x25_timer);
+			mod_timer(&card->u.x.x25_timer, jiffies + HZ);
 		}
 	}
 	/* Device is not up until the we are in connected state */
diff -urN linux-2.4.22-pre3/drivers/net/wan/z85230.c linux-2.4.22-pre4/drivers/net/wan/z85230.c
--- linux-2.4.22-pre3/drivers/net/wan/z85230.c	2001-11-09 14:03:11.000000000 -0800
+++ linux-2.4.22-pre4/drivers/net/wan/z85230.c	2003-07-09 16:56:29.000000000 -0700
@@ -1451,7 +1451,6 @@
 	c->tx_next_skb=NULL;
 	c->tx_ptr=c->tx_next_ptr;
 	
-	netif_wake_queue(c->netdevice);
 	if(c->tx_skb==NULL)
 	{
 		/* Idle on */
@@ -1513,7 +1512,6 @@
 			/* ABUNDER off */
 			write_zsreg(c, R10, c->regs[10]);
 			write_zsctrl(c, RES_Tx_CRC);
-//???			write_zsctrl(c, RES_EOM_L);
 	
 			while(c->txcount && (read_zsreg(c,R0)&Tx_BUF_EMP))
 			{		
@@ -1523,6 +1521,10 @@
 
 		}
 	}
+	/*
+	 *	Since we emptied tx_skb we can ask for more
+	 */
+	netif_wake_queue(c->netdevice);
 }
 
 /**
@@ -1540,7 +1542,6 @@
 {
 	struct sk_buff *skb;
 
-	netif_wake_queue(c->netdevice);
 	/* Actually this can happen.*/
 	if(c->tx_skb==NULL)
 		return;
@@ -1795,7 +1796,6 @@
 	z8530_tx_begin(c);
 	spin_unlock_irqrestore(c->lock, flags);
 	
-	netif_wake_queue(c->netdevice);
 	return 0;
 }
 
diff -urN linux-2.4.22-pre3/drivers/s390/Config.in linux-2.4.22-pre4/drivers/s390/Config.in
--- linux-2.4.22-pre3/drivers/s390/Config.in	2003-06-13 07:51:35.000000000 -0700
+++ linux-2.4.22-pre4/drivers/s390/Config.in	2003-07-09 16:56:29.000000000 -0700
@@ -80,6 +80,7 @@
     tristate 'Universal TUN/TAP device driver support' CONFIG_TUN
     bool 'Ethernet (10 or 100Mbit)' CONFIG_NET_ETHERNET
     bool 'Token Ring driver support' CONFIG_TR
+    dep_tristate 'Cisco 7000 support' CONFIG_C7000 m
     bool 'FDDI driver support' CONFIG_FDDI
     comment 'S/390 network device drivers'
     bool 'Channel Device Configuration' CONFIG_CHANDEV
diff -urN linux-2.4.22-pre3/drivers/s390/net/Makefile linux-2.4.22-pre4/drivers/s390/net/Makefile
--- linux-2.4.22-pre3/drivers/s390/net/Makefile	2002-08-02 17:39:44.000000000 -0700
+++ linux-2.4.22-pre4/drivers/s390/net/Makefile	2003-07-09 16:56:29.000000000 -0700
@@ -12,6 +12,7 @@
 obj-$(CONFIG_IUCV) += iucv.o fsm.o
 obj-$(CONFIG_CTC) += ctc.o fsm.o
 obj-$(CONFIG_IUCV) += netiucv.o
+obj-$(CONFIG_C7000) += c7000.o
 
 include $(TOPDIR)/Rules.make
 
diff -urN linux-2.4.22-pre3/drivers/s390/net/c7000.c linux-2.4.22-pre4/drivers/s390/net/c7000.c
--- linux-2.4.22-pre3/drivers/s390/net/c7000.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.4.22-pre4/drivers/s390/net/c7000.c	2003-07-09 16:56:29.000000000 -0700
@@ -0,0 +1,3293 @@
+/*
+	Cisco/7000 driver -- Copyright (C) 2000 UTS Global LLC.
+	Author: Bob Scardapane (UTS Global LLC).
+	Version: 3.
+
+	To use this driver, run the LINUX command:
+
+	insmod c7000 base0=0xYYYY lhost0=s1 uhost0=s2 lappl0=s3 uappl0=s4 dbg=x
+
+	base0=0xYYYY defines the base unit address of the interface.
+	lhost0=s1 defines the local host name.
+	uhost0=s2 defines the unit host name.
+	lappl0=s3 defines the local application name.
+	uappl0=s4 defines the unit application name.
+	dbg=x defines the message level.  Higher values will result in
+	additional diagnostic messages.
+
+	Additional interfaces are defined on insmod by using the variable
+	name groups "base1,lhost1,lappl1,uhost1,uappl1", etc... up to three
+	additional groups.
+
+	In addition, the module will automatically detect the unit base
+	addresses by scanning all active irq's for a control unit type
+	of 0x3088 and a model of 0x61 (CLAW mode). The noauto parameter
+	can be used to suppress automatic detection.
+
+	The values of lhostx, lapplx, uhostx and uapplx default to:
+
+	lapplx - TCPIP
+	lhostx - UTS
+	uapplx - TCPIP
+	uhostx - C7011
+
+	Note that the values passed in the insmod command will always
+	override the automatic detection of the unit base addreeses and
+	the default values of lapplx, lhostx, uapplx and uhostx.
+
+	The parameter noauto can be used to disable automatic detection of
+	devices:
+
+	noauto=1 (disable automatic detection)
+	noauto=0 (Enable automatic detectio.  This is the default value.)
+
+	The values in base0 - base3 will be copied to the bases array when
+	the module is loaded. 
+
+	To configure the interface(s), run the LINUX command(s):
+
+	ifconfig ci0 ...
+	ifconfig ci1 ...
+	ifconfig ci2 ...
+	ifconfig ci3 ...
+
+	There is one device structure for each controller in the c7000_devices
+	array.  The base address of each controller is in the bases array.
+	These arrays parallel each other.  There is also one c7000_controller
+	structure for each controller.  This structure is pointed to by field
+	priv in the individual device structure. 
+
+	In each c7000_controller, there are embedded c7000_unit structures.
+	There is one c7000_unit structure per device number that makes up
+	a controller (currently 2 units per controller).
+*/
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include <linux/timer.h>
+#include <linux/sched.h>
+#include <linux/signal.h>
+#include <linux/string.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/if_arp.h>
+#include <linux/tcp.h>
+#include <linux/skbuff.h>
+#include <asm/io.h>
+#include <asm/bitops.h>
+#include <asm/irq.h>
+ 
+/*
+	Global defines
+*/
+
+/*
+	Maximum number of controllers.
+*/
+
+#define	MAX_C7000	4
+
+/*
+	Number of units per controller.
+*/
+
+#define	NUNITS		2
+
+/*
+	Define indexes of read and write units in the cunits array.
+*/
+
+#define	C7000_RD	0
+#define	C7000_WR	1
+
+/*
+	Number of device buffers.
+*/
+
+#define	C7000_MAXBUF	40
+
+/*
+	Transmission queue length.
+*/
+
+#define	C7000_TXQUEUE_LEN	100
+/*
+	Size of the IP packet data.
+*/
+
+#define	C7000_DATAL	4096
+
+/*
+	Size of the read header data.
+*/
+
+#define	C7000_READHDRL	4
+
+/*
+	Size of read flag byte.
+*/
+
+#define	C7000_READFFL	1
+
+/*
+	Size of a device buffer.  This is how it is arranged in memory:
+	4096 (IP packet data) + 4 (read header) + 1 (read flag) = 4101.
+*/
+
+#define	C7000_BUFSIZE	C7000_DATAL + C7000_READHDRL + C7000_READFFL
+
+/*
+	Size of sigsmod data.
+*/
+
+#define	C7000_SIGSMODL	1
+
+/*
+	Flag value that indicates a read was completed in the flag
+	field of a c7000_rd_header.
+*/
+
+#define	FLAG_FF		0xff
+/*
+	Size of C7000 sense id data.
+*/
+
+#define SIDL		32
+
+/*
+	Maximum number of read and write retries.
+*/
+
+#define	C7000_MAX_RETRIES	3
+
+/*
+	Define sense byte0 value for a box reset.
+*/
+
+#define	C7000_BOX_RESET		0x41
+
+/*
+	CCW commands.
+*/
+
+#define	C7000_WRITE_CCW		0x01	/* normal write */
+#define	C7000_READ_CCW		0x02	/* normal read */
+#define	C7000_NOOP_CCW		0x03	/* no operation */
+#define	C7000_SIGSMOD_CCW	0x05	/* signal status modifier */
+#define	C7000_TIC_CCW		0x08	/* transfer in channel */
+#define	C7000_READHDR_CCW	0x12	/* read header  */
+#define	C7000_READFF_CCW	0x22	/* read FF flag */
+#define	C7000_SID_CCW		0xe4	/* sense identification */
+
+/*
+	Control commands.
+*/
+
+#define	C7000_SYS_VALIDATE		1
+#define C7000_SYS_VALIDATE_RESP		2
+#define	C7000_CONN_REQ			33
+#define C7000_CONN_RESP			34
+#define C7000_CONN_CONFRM		35
+#define C7000_DISCONN			36
+#define C7000_BOXERROR			65
+
+/*
+	State machine values.
+*/
+
+#define	C7000_INIT	1
+#define C7000_HALT	2
+#define C7000_SID	3
+#define C7000_SYSVAL	4
+#define	C7000_CONNECT	5
+#define C7000_READY	6
+#define C7000_READ	7
+#define C7000_WRITE	8
+#define	C7000_DISC	9
+#define C7000_STOP	10
+#define	C7000_STOPPED	11
+#define C7000_ERROR	12
+
+/*
+	The lower subchannel is used for read operations and the one that is
+	one higher is used for write operations.
+
+	Both subchannels are initially in state C7000_INIT.  A transition to
+	state C7000_HALT occurs when halt_IO is issued on each.  When the
+	halts completes a transition to state C7000_SID occurs and a channel
+	program is issued to do a sense identification on both subchannels.
+
+	When the sense identification completes, the state C7000_SYSVAL is
+	entered on the read subchannel.  A read channel program is issued.
+
+	When the sense identification completes, the write subchannel enters
+	state C7000_SYSVAL and a system validation request is written.  The
+	read subchannel is also put into this state.
+	
+	When both the system validation response is read and an inbound system
+	validation request is read, the inbound system validation request is
+	responded to and both subchannels enter the C7000_CONNECT state.
+
+	A read channel program is posted to look for the inbound connect
+	request.  When that is received a connection confirmation is written.
+	The state of both subchannels is then changed to C7000_READY.  A
+	read channel program is then posted and the state is changed to
+	C7000_READ.  When a read completes, the packet is sent to the higher
+	layers and the read channel program is restarted.
+
+	When there is a packet to be written, state C7000_WRITE is entered
+	and a channel program is issued to write the data.  The subchannel
+	is in state C7000_READY when there is nothing to be written.
+
+	When the stop method is executed, a disconnect message is sent and
+	the state is changed to C7000_DISC in both subchannels.  A halt_IO
+	will be issued to both subchannels and state C7000_STOP will be entered.
+	When the halt IO completes, state C7000_STOPPED will be set. 
+
+	State C7000_ERROR is set when an error occurs in the interrupt
+	routine.  Recycle the interface (ifconfig down / ifconfig up)
+	to reset this state.
+*/
+
+/*
+	Results from c7000_check_csw.
+*/
+
+enum	c7000_rupt {
+	C7000_NORMAL,
+	C7000_CHANERR,
+	C7000_UCK,
+	C7000_UCK_RESET,
+	C7000_UE,
+	C7000_ATTN,
+	C7000_BUSY,
+	C7000_OTHER
+};
+
+/*
+	Bits set in device structure tbusy field.
+*/
+
+#define	TB_TX		0	/* sk buffer handling in progress */
+#define	TB_STOP		1	/* network device stop in progress */
+#define	TB_RETRY	2	/* retry in progress */
+#define	TB_NOBUFFER	3	/* no buffer on free queue */
+
+/*
+	Bit in c7000_unit.flag_a that indicates the bh routine is busy.
+*/
+
+#define	C7000_BH_ACTIVE	0
+
+#define CPrintk(level, args...) \
+	if (level <= dbg) \
+		printk(args)
+
+/*
+	Maximum length of a system validation string.
+*/
+
+#define	NAMLEN	8
+
+#define	Err_Conn_Confirm	1
+#define	Err_Names_not_Matched	166
+#define	Err_C7000_NOT_READY	167
+#define	Err_Duplicate		170
+#define	Err_Closing		171
+#define	Err_No_Such_App		172
+#define	Err_Host_Not_Ready	173
+#define	Err_CLOSING		174
+#define	Err_Dup_Link		175
+#define	Err_Wrong_Version	179
+#define	Err_Wrong_Frame_Size	180
+
+/*
+	Define a macro to extract the logical link identifier from
+	the c7000 read header command field.
+*/
+
+#define	C7000_LINKID(cmd)	((unsigned char)cmd >> 3)
+
+/*
+	Define the control unit type for a Cisco 7000.
+*/
+
+#define	C7000_CU_TYPE		0x3088
+
+/*
+	Define the control unit model for a Cisco 7000.
+*/
+
+#define	C7000_CU_MODEL		0x61
+
+/*
+	Define the default system validate parameters (lapplx,
+	lhostx, uapplx, uhostx).
+*/
+
+#define	C7000_DFLT_LAPPL	"TCPIP"
+#define	C7000_DFLT_LHOST	"UTS"
+#define	C7000_DFLT_UAPPL	"TCPIP"
+#define	C7000_DFLT_UHOST	"C7011"
+
+/*
+	Global variables.
+*/
+
+/*
+	Base device addresses of the controllers.
+*/
+
+static int	base0 = -1;
+static int	base1 = -1;
+static int	base2 = -1;
+static int	base3 = -1;
+
+static int	bases[MAX_C7000];
+
+/*
+	Local application names.
+*/
+
+static char	*lappl0;
+static char	*lappl1;
+static char	*lappl2;
+static char	*lappl3;
+
+/*
+	Local host names.
+*/
+
+static char	*lhost0;
+static char	*lhost1;
+static char	*lhost2;
+static char	*lhost3;
+
+/*
+	Unit application names.
+*/
+
+static char	*uappl0;
+static char	*uappl1;
+static char	*uappl2;
+static char	*uappl3;
+
+/*
+	Unit hosts names.
+*/
+
+static char	*uhost0;
+static char	*uhost1;
+static char	*uhost2;
+static char	*uhost3;
+
+/*
+	Debugging level (higher numbers emit lower priority messages).
+*/
+
+static unsigned int	dbg = 0;
+
+/*
+	Parameter that controls auto detection.
+*/
+
+static int	noauto = 0;
+
+/*
+	Interface names.
+*/
+
+static char	ifnames[MAX_C7000][8] = {"ci0", "ci1", "ci2", "ci3"};
+
+/*
+	One device structure per controller.
+*/
+
+/* RBH Try out the new code for 2.4.0 */
+#define NEWSTUFF
+
+#ifdef NEWSTUFF
+#define STRUCT_NET_DEVICE struct net_device
+#else
+#define STRUCT_NET_DEVICE struct device
+#endif
+
+STRUCT_NET_DEVICE	c7000_devices[MAX_C7000];
+
+/*
+	Scratch variable filled in with controller name.
+*/
+
+static char	*controller;
+
+/*
+	Identify parameters that can be passed on the LINUX insmod command.
+*/
+
+MODULE_AUTHOR("Robert Scardapane (UTS Global)");
+MODULE_DESCRIPTION("Network module for Cisco 7000 box.");
+
+MODULE_PARM(base0, "1i");
+MODULE_PARM_DESC(base0, "Base unit address for 1st C7000 box.");
+MODULE_PARM(base1, "1i");
+MODULE_PARM_DESC(base1, "Base unit address for 2nd C7000 box.");
+MODULE_PARM(base2, "1i");
+MODULE_PARM_DESC(base2, "Base unit address for 3rd C7000 box.");
+MODULE_PARM(base3, "1i");
+MODULE_PARM_DESC(base3, "Base unit address for 4th C7000 box.");
+
+MODULE_PARM(lappl0, "s");
+MODULE_PARM_DESC(lappl0, "Application name for 1st C7000 box.");
+MODULE_PARM(lappl1, "s");
+MODULE_PARM_DESC(lappl1, "Application name for 2nd C7000 box.");
+MODULE_PARM(lappl2, "s");
+MODULE_PARM_DESC(lappl2, "Application name for 3rd C7000 box.");
+MODULE_PARM(lappl3, "s");
+MODULE_PARM_DESC(lappl3, "Application name for 4th C7000 box.");
+
+MODULE_PARM(lhost0, "s");
+MODULE_PARM_DESC(lhost0, "Host name for 1st C7000 box.");
+MODULE_PARM(lhost1, "s");
+MODULE_PARM_DESC(lhost1, "Host name for 2nd C7000 box.");
+MODULE_PARM(lhost2, "s");
+MODULE_PARM_DESC(lhost2, "Host name for 3rd C7000 box.");
+MODULE_PARM(lhost3, "s");
+MODULE_PARM_DESC(lhost3, "Host name for 4th C7000 box.");
+
+MODULE_PARM(uhost0, "s");
+MODULE_PARM_DESC(uhost0, "Unit name for 1st C7000 box.");
+MODULE_PARM(uhost1, "s");
+MODULE_PARM_DESC(uhost1, "Unit name for 2nd C7000 box.");
+MODULE_PARM(uhost2, "s");
+MODULE_PARM_DESC(uhost2, "Unit name for 3rd C7000 box.");
+MODULE_PARM(uhost3, "s");
+MODULE_PARM_DESC(uhost3, "Unit name for 4th C7000 box.");
+
+MODULE_PARM(uappl0, "s");
+MODULE_PARM_DESC(uappl0, "Unit application name for 1st C7000 box.");
+MODULE_PARM(uappl1, "s");
+MODULE_PARM_DESC(uappl1, "Unit application name for 2nd C7000 box.");
+MODULE_PARM(uappl2, "s");
+MODULE_PARM_DESC(uappl2, "Unit application name for 3rd C7000 box.");
+MODULE_PARM(uappl3, "s");
+MODULE_PARM_DESC(uappl3, "Unit application name for 4th C7000 box.");
+
+MODULE_PARM(dbg, "1i");
+MODULE_PARM_DESC(dbg, "Message level for debugging.");
+
+MODULE_PARM(noauto, "1i");
+MODULE_PARM_DESC(noauto, "Control automatic detection of unit base addresses.");
+
+/*
+	Structure used to manage unit buffers.
+*/
+
+struct	c7000_buffer {
+	ccw1_t			ccws[7];	/* channel program */
+	struct	c7000_buffer	*next;		/* list pointer */
+	char			*data;		/* pointer to actual data */
+	int			len;		/* length of the data */
+};
+
+/*
+	C7000 Control Block.
+ */
+
+struct	c7000_control_blk {
+	unsigned char	cmd;
+	unsigned char	ver;
+	unsigned char	link_id;
+	unsigned char	correlator;
+	unsigned char	ret_code;
+	unsigned char	resvd1[3];
+	unsigned char	unitname[NAMLEN];
+	unsigned char	hostname[NAMLEN];
+	unsigned short	rdsize;		/* read frame size   */
+	unsigned short	wrtsize;	/* write frame size  */
+	unsigned char	resvd2[4];
+};
+
+/*
+	Per unit structure contained within the c7000_controller structure.
+*/
+
+struct	c7000_unit {
+	ccw1_t				ccws[5];	/* control ccws */
+	int				devno;		/* device number */
+	int				irq;		/* subchannel number */
+	int				IO_active;	/* IO activity flag */
+	int				state;		/* fsm state */
+	int				retries;	/* retry counter */
+	unsigned long			flag_a;		/* bh activity flag */
+	devstat_t			devstat;	/* device status */
+#ifdef NEWSTUFF
+	wait_queue_head_t		wait;		/* sleep q head */
+#else
+	struct wait_queue		*wait;		/* sleep q pointer */
+#endif
+	struct c7000_controller		*cntlp;		/* controller pointer */
+	struct c7000_buffer		*free;		/* free buffer anchor */
+	struct c7000_buffer		*proc_head;	/* proc head */
+	struct c7000_buffer		*proc_tail;	/* proc tail */
+	struct c7000_buffer		*bh_head;	/* bh head */
+	struct c7000_buffer		*bh_tail;	/* bh tail */
+	struct tq_struct		tq;		/* bh scheduling */
+	char				senseid[SIDL];	/* sense id data */
+	struct c7000_control_blk	control_blk;	/* control block */
+	unsigned char			sigsmod;	/* sigsmod flag */
+	unsigned char			readhdr[4];	/* read header */
+	unsigned char			readff;		/* readff flag */
+};
+
+/*
+	Private structure pointed to by dev->priv.
+*/
+
+struct c7000_controller {
+	struct	net_device_stats	stats;		/* statistics */
+	STRUCT_NET_DEVICE		*dev;		/* -> device struct */
+	unsigned int			base_addr;	/* base address */
+	char				lappl[NAMLEN];	/* local appl */
+	char				lhost[NAMLEN];	/* local host */
+	char				uappl[NAMLEN];	/* unit appl */
+	char				uhost[NAMLEN];	/* unit host */
+	unsigned char			version;	/* version = 2 */
+	unsigned char			linkid;		/* link id */
+	struct	c7000_unit		cunits[NUNITS];	/* embedded units */
+#ifdef NEWSTUFF
+	int				tbusy;
+#endif
+};
+
+/*
+	This is the structure returned by the C7000_READHDR_CCW.
+*/
+
+struct	c7000_rd_header {
+	unsigned short	len;	/* packet length */
+	unsigned char	cmd;	/* command code */
+	unsigned char	flag;	/* flag */
+};
+
+/*
+	Set the device structure transmission busy flag.
+*/
+
+#ifdef NEWSTUFF
+#define c7000_set_busy(dev) netif_stop_queue(dev)
+#else
+static __inline__ void
+c7000_set_busy(STRUCT_NET_DEVICE *dev)
+{
+	dev->tbusy = 1;
+	eieio();
+	return;
+}
+#endif
+	
+/*
+	Clear the device structure transmission busy flag.
+*/
+
+#ifdef NEWSTUFF
+#define c7000_clear_busy(dev) netif_wake_queue(dev)
+#else
+static __inline__ void
+c7000_clear_busy(STRUCT_NET_DEVICE *dev)
+{
+	dev->tbusy = 0;
+	eieio();
+	return;
+}
+#endif
+
+/*
+	Extract the device structure transmission busy flag.
+*/
+
+#ifdef NEWSTUFF
+#define c7000_check_busy(dev) netif_queue_stopped(dev)
+#else
+static __inline__ int
+c7000_check_busy(STRUCT_NET_DEVICE *dev)
+{
+	eieio();
+	return(dev->tbusy);
+}
+#endif
+
+/*
+	Set a bit in the device structure transmission busy flag.
+*/
+
+static __inline__ void
+c7000_setbit_busy(int nr, STRUCT_NET_DEVICE *dev)
+{
+#ifdef NEWSTUFF
+	netif_stop_queue(dev);
+	test_and_set_bit(nr, &((struct c7000_controller *)dev->priv)->tbusy);
+#else
+	set_bit(nr, (void *)&dev->tbusy);
+#endif
+	return;
+}
+
+/*
+	Clear a bit in the device structure transmission busy flag.
+*/
+
+static __inline__ void
+c7000_clearbit_busy(int nr, STRUCT_NET_DEVICE *dev)
+{
+#ifdef NEWSTUFF
+	clear_bit(nr, &((struct c7000_controller *)dev->priv)->tbusy);
+	netif_wake_queue(dev);
+#else
+	clear_bit(nr, (void *)&dev->tbusy);
+#endif
+	return;
+}
+
+/*
+	Test and set a bit in the device structure transmission busy flag.
+*/
+
+static __inline__ int
+c7000_ts_busy(int nr, STRUCT_NET_DEVICE *dev)
+{
+#ifdef NEWSTUFF
+	netif_stop_queue(dev);
+	return test_and_set_bit(nr, &((struct c7000_controller *)dev->priv)->tbusy);
+#else
+	return(test_and_set_bit(nr, (void *)&dev->tbusy));
+#endif
+}
+
+/*
+	Set the C7000 controller in the error state.
+*/
+
+static void
+c7000_error(struct c7000_controller *ccp)
+{
+	int			i;
+	struct	c7000_unit	*cup;
+	STRUCT_NET_DEVICE	*dev = ccp->dev;
+
+	for (i = 0; i < NUNITS; i++) {
+		cup = &ccp->cunits[i];
+		cup->state = C7000_ERROR;
+	}
+
+	if (dev != NULL)
+#ifdef NEWSTUFF
+		/* RBH XXX Should we be doing this? */
+		dev->state &= ~__LINK_STATE_START;
+#else
+		dev->flags &= ~IFF_RUNNING;
+#endif
+
+	CPrintk(0, "c7000: c7000_error: base unit 0x%x is down\n", ccp->base_addr);
+	return;
+}
+
+/*
+	Based on the SENSE ID information, fill in the
+	controller name.  Note that this is the SENSE ID
+	information saved by LINUX/390 at boot time.
+*/
+
+static int
+c7000_check_type(senseid_t *id)
+{
+
+	switch (id->cu_type) {
+
+		case C7000_CU_TYPE:
+
+			if (id->cu_model == C7000_CU_MODEL) {
+				controller = "C7000  ";
+ 				return(0);
+			}
+
+			break;
+
+                default:
+			break;
+	}
+
+	return(-1);
+}
+
+/*
+	Check the device information for the controller.
+*/
+
+static int
+c7000_check_devices(int devno)
+{
+	int		i;
+	s390_dev_info_t	temp;
+
+	/*
+		Get the SENSE ID information for each device.
+	*/
+
+	for (i = devno; i < (devno + NUNITS); i++) {
+
+		if (get_dev_info_by_devno(devno, &temp) != 0)
+			return(-1);
+		
+		if (c7000_check_type(&temp.sid_data) == -1)
+			return(-1);
+	}
+
+	CPrintk(1, "c7000: c7000_check_devices: device type is %s\n", controller);
+	return(0);
+}
+
+/*
+	Issue a halt I/O to device pointed to by cup.
+*/
+
+static int
+c7000_haltio(struct c7000_unit *cup)
+{
+	__u32			parm;
+	__u8			flags = 0x00;
+	__u32			saveflags;
+	DECLARE_WAITQUEUE(wait, current);
+	int			rc;
+
+	s390irq_spin_lock_irqsave(cup->irq, saveflags);
+	parm = (unsigned long)cup;
+
+	if ((rc = halt_IO(cup->irq, parm, flags)) != 0) {
+		s390irq_spin_unlock_irqrestore(cup->irq, saveflags);
+		return(rc);
+	}
+
+	/*
+		Wait for the halt I/O to finish.
+	*/
+
+	add_wait_queue(&cup->wait, &wait);
+	current->state = TASK_UNINTERRUPTIBLE;
+	s390irq_spin_unlock_irqrestore(cup->irq, saveflags);
+	schedule();
+	remove_wait_queue(&cup->wait, &wait);
+	return(0);
+}
+
+/*
+	Issue a start I/O to device pointed to by cup.
+*/
+
+static int
+c7000_doio(struct c7000_unit *cup)
+{
+	__u32			parm;
+	__u8			flags = 0x00;
+	__u32			saveflags;
+	DECLARE_WAITQUEUE(wait, current);
+	int			rc;
+
+	/*
+		Do no further I/O while the device is in the ERROR, STOP
+		or STOPPED state.
+	*/
+
+	if (cup->state == C7000_ERROR || cup->state == C7000_STOP || cup->state == C7000_STOPPED)
+		return(-1);
+
+	s390irq_spin_lock_irqsave(cup->irq, saveflags);
+	parm = (unsigned long)cup;
+
+	if ((rc = do_IO(cup->irq, &cup->ccws[0], parm, 0xff, flags)) != 0) {
+		s390irq_spin_unlock_irqrestore(cup->irq, saveflags);
+		return(rc);
+	}
+	
+	/*
+		Wait for the I/O to complete.
+	*/
+
+	add_wait_queue(&cup->wait, &wait);
+	current->state = TASK_UNINTERRUPTIBLE;
+	s390irq_spin_unlock_irqrestore(cup->irq, saveflags);
+	schedule();
+	remove_wait_queue(&cup->wait, &wait);
+
+	/*
+		Interrupt handling may have marked the device in ERROR.
+	*/
+
+	if (cup->state == C7000_ERROR)
+		return(-1);
+
+	return(0);
+}
+
+/*
+	Build a channel program to do a sense id channel program.
+*/
+
+static void
+c7000_bld_senseid_chpgm(struct c7000_unit *cup)
+{
+	ccw1_t	*ccwp;
+
+	ccwp = &cup->ccws[0];
+	ccwp->cmd_code = C7000_SID_CCW;
+	ccwp->flags = (CCW_FLAG_SLI | CCW_FLAG_CC);
+	ccwp->cda = (__u32)virt_to_phys(&cup->senseid);
+	ccwp->count = SIDL;
+	ccwp++;
+	ccwp->cmd_code = C7000_NOOP_CCW;
+	ccwp->flags = CCW_FLAG_SLI;
+	ccwp->cda = (__u32)NULL;
+	ccwp->count = 1;
+	return;
+}
+
+/*
+	Build a channel program to write a control message.
+*/
+
+static void
+c7000_bld_wrtctl_chpgm(struct c7000_unit *cup)
+{
+	ccw1_t	*ccwp;
+
+	ccwp = &cup->ccws[0];
+	ccwp->cmd_code = C7000_WRITE_CCW;
+	ccwp->flags = (CCW_FLAG_SLI | CCW_FLAG_CC);
+	ccwp->cda = (__u32)virt_to_phys(&cup->control_blk);
+	ccwp->count = sizeof(struct c7000_control_blk);
+	ccwp++;
+	ccwp->cmd_code = C7000_READFF_CCW;
+	ccwp->flags = (CCW_FLAG_SLI | CCW_FLAG_CC);
+	ccwp->cda = (__u32)virt_to_phys(&cup->readff);
+	ccwp->count = C7000_READFFL;
+	ccwp++;
+	ccwp->cmd_code = C7000_TIC_CCW;
+	ccwp->flags = 0;
+	ccwp->cda = (__u32)virt_to_phys(ccwp + 1);
+	ccwp->count = 0;
+	ccwp++;
+	ccwp->cmd_code = C7000_NOOP_CCW;
+	ccwp->flags = CCW_FLAG_SLI;
+	ccwp->cda = (__u32)NULL;
+	ccwp->count = 1;
+	return;
+}
+
+/*
+	Build a write channel program to write the indicated buffer.
+*/
+
+static void
+c7000_bld_wrt_chpgm(struct c7000_unit *cup, struct c7000_buffer *buf)
+{
+	ccw1_t				*ccwp;
+	struct	c7000_controller	*ccp = cup->cntlp;
+
+	ccwp = &buf->ccws[0];
+	ccwp->cmd_code = C7000_WRITE_CCW | (ccp->linkid << 3);
+	ccwp->flags = (CCW_FLAG_SLI | CCW_FLAG_CC);
+	ccwp->cda = (__u32)virt_to_phys(buf->data);
+	ccwp->count = buf->len;
+	ccwp++;
+	ccwp->cmd_code = C7000_READFF_CCW;
+	ccwp->flags = (CCW_FLAG_SLI | CCW_FLAG_CC);
+	ccwp->cda = (__u32)virt_to_phys(buf->data + C7000_DATAL + C7000_READHDRL);
+	ccwp->count = C7000_READFFL;
+	ccwp++;
+	ccwp->cmd_code = C7000_TIC_CCW;
+	ccwp->flags = 0;
+	ccwp->cda = (__u32)virt_to_phys(ccwp + 1);
+	ccwp->count = 0;
+	ccwp++;
+	ccwp->cmd_code = C7000_NOOP_CCW;
+	ccwp->flags = (CCW_FLAG_SLI);
+	ccwp->cda = (__u32)NULL;
+	ccwp->count = 1;
+	return;
+}
+
+/*
+	Build a channel program to read a control message.
+*/
+
+static void
+c7000_bld_readctl_chpgm(struct c7000_unit *cup)
+{
+	ccw1_t	*ccwp;
+
+	ccwp = &cup->ccws[0];
+	ccwp->cmd_code = C7000_READ_CCW;
+	ccwp->flags = (CCW_FLAG_SLI | CCW_FLAG_CC);
+	ccwp->cda = (__u32)virt_to_phys(&cup->control_blk);
+	ccwp->count = sizeof(struct c7000_control_blk);
+	ccwp++;
+	ccwp->cmd_code = C7000_READHDR_CCW;
+	ccwp->flags = (CCW_FLAG_SLI | CCW_FLAG_CC);
+	ccwp->cda = (__u32)virt_to_phys(&cup->readhdr);
+	ccwp->count = C7000_READHDRL;
+	ccwp++;
+	ccwp->cmd_code = C7000_SIGSMOD_CCW;
+	ccwp->flags = (CCW_FLAG_SLI | CCW_FLAG_CC);
+	ccwp->cda = (__u32)virt_to_phys(&cup->sigsmod);
+	ccwp->count = C7000_SIGSMODL;
+	ccwp++;
+	ccwp->cmd_code = C7000_TIC_CCW;
+	ccwp->flags = 0;
+	ccwp->cda = (__u32)virt_to_phys(ccwp + 1);
+	ccwp->count = 0;
+	ccwp++;
+	ccwp->cmd_code = C7000_NOOP_CCW;
+	ccwp->flags = (CCW_FLAG_SLI);
+	ccwp->cda = (__u32)NULL;
+	ccwp->count = 1;
+	return;
+}
+
+/*
+	Build a channel program to read the indicated buffer.
+*/
+
+static void
+c7000_bld_read_chpgm(struct c7000_unit *cup, struct c7000_buffer *buf)
+{
+	ccw1_t	*ccwp;
+
+	ccwp = &buf->ccws[0];
+	ccwp->cmd_code = C7000_READ_CCW;
+	ccwp->flags = (CCW_FLAG_SLI | CCW_FLAG_CC);
+	ccwp->cda = (__u32)virt_to_phys(buf->data);
+	ccwp->count = C7000_DATAL;
+	ccwp++;
+	ccwp->cmd_code = C7000_READHDR_CCW;
+	ccwp->flags = (CCW_FLAG_SLI | CCW_FLAG_CC);
+	ccwp->cda = (__u32)virt_to_phys(buf->data + C7000_DATAL);
+	ccwp->count = C7000_READHDRL;
+	ccwp++;
+	ccwp->cmd_code = C7000_SIGSMOD_CCW;
+	ccwp->flags = (CCW_FLAG_SLI | CCW_FLAG_CC);
+	ccwp->cda = (__u32)virt_to_phys(&cup->sigsmod);
+	ccwp->count = C7000_SIGSMODL;
+	ccwp++;
+	ccwp->cmd_code = C7000_TIC_CCW;
+	ccwp->flags = 0;
+	ccwp->cda = (__u32)virt_to_phys(ccwp + 3);
+	ccwp->count = 0;
+	ccwp++;
+	ccwp->cmd_code = C7000_READFF_CCW;
+	ccwp->flags = (CCW_FLAG_SLI | CCW_FLAG_CC | CCW_FLAG_PCI);
+	ccwp->cda = (__u32)virt_to_phys(&cup->readff);
+	ccwp->count = C7000_READFFL;
+	ccwp++;
+	ccwp->cmd_code = C7000_TIC_CCW;
+	ccwp->flags = 0;
+	ccwp->cda = (__u32)virt_to_phys(ccwp + 1);
+	ccwp->count = 0;
+	ccwp++;
+	ccwp->cmd_code = C7000_NOOP_CCW;
+	ccwp->flags = (CCW_FLAG_SLI);
+	ccwp->cda = (__u32)NULL;
+	ccwp->count = 1;
+	return;
+}
+
+/*
+	Allocate buffer structure headers and buffers for all units
+	A return value of 0 means that all allocations worked.  A -1
+	means that an allocation failed.  It is expected that the caller
+	will call c7000_free_buffers when -1 is returned.
+*/
+
+static int
+c7000_alloc_buffers(STRUCT_NET_DEVICE *dev)
+{
+	int				i;
+	int				j;
+	char				*data;
+	struct	c7000_buffer		*bufptr;
+	struct	c7000_controller	*ccp = (struct c7000_controller *) dev->priv;
+	struct	c7000_unit		*cup;
+	
+	for (i = 0; i < NUNITS; i++) {
+		cup = &ccp->cunits[i];
+		cup->free = NULL;
+
+		for (j = 0; j < C7000_MAXBUF; j++) {
+			bufptr = kmalloc(sizeof(struct c7000_buffer), GFP_KERNEL);
+			data = kmalloc(C7000_BUFSIZE, GFP_KERNEL);
+
+			if (bufptr == NULL)
+			{
+				if(data)
+					kfree(data);
+				return(-1);
+			}
+
+			/*
+				Place filled in buffer header on free anchor.
+			*/
+
+			bufptr->next = cup->free;
+			bufptr->data = data;
+			bufptr->len = 0;
+			cup->free = bufptr;
+
+			if (data == NULL)
+				return(-1);
+
+			memset(data, '\0', C7000_BUFSIZE);
+		}
+
+	}
+
+	CPrintk(1, "c7000: c7000_alloc_buffers: allocated buffers for base unit 0x%lx\n", dev->base_addr);
+	return(0);
+}
+
+/*
+	Free buffers on a chain.
+*/
+
+static void
+c7000_free_chain(struct c7000_buffer *buf)
+{
+	char			*data;
+	struct	c7000_buffer	*bufptr = buf;
+	struct	c7000_buffer	*tmp;
+
+	while (bufptr != NULL) {
+		data = bufptr->data;
+
+		if (data != NULL)
+			kfree(data);
+
+		tmp = bufptr;
+		bufptr = bufptr->next;
+		kfree(tmp);
+	}
+
+	return;
+}
+
+/*
+	Free buffers on all possible chains for all units.
+*/
+
+static void
+c7000_free_buffers(STRUCT_NET_DEVICE *dev)
+{
+	int				i;
+	struct	c7000_controller	*ccp = (struct c7000_controller *) dev->priv;
+	struct	c7000_unit	*cup;
+	
+	for (i = 0; i < NUNITS; i++) {
+		cup = &ccp->cunits[i];
+		c7000_free_chain(cup->free);
+		cup->free = NULL;
+		c7000_free_chain(cup->proc_head);
+		cup->proc_head = cup->proc_tail = NULL;
+		c7000_free_chain(cup->bh_head);
+		cup->bh_head = cup->bh_tail = NULL;
+	}
+
+	CPrintk(1, "c7000: c7000_free_buffers: freed buffers for base unit 0x%lx\n", dev->base_addr);
+	return;
+}
+
+/*
+	Obtain a free buffer.  Return a pointer to the c7000_buffer
+	structure OR NULL.
+*/
+
+struct c7000_buffer *
+c7000_get_buffer(struct c7000_unit *cup) 
+{
+	struct	c7000_buffer	*buf;
+
+	buf = cup->free;
+
+	if (buf == NULL)
+		return(NULL);
+
+	cup->free = buf->next;
+	buf->next = NULL;
+	return(buf);
+}
+
+/*
+	Release a buffer to the free list.
+*/
+
+void
+c7000_release_buffer(struct c7000_unit *cup, struct c7000_buffer *buf)
+{
+	struct	c7000_buffer	*tmp;
+
+	tmp = cup->free;
+	cup->free = buf;
+	buf->next = tmp;
+	return;
+}
+
+/*
+	Queue a buffer on the end of the processing (proc) chain.
+*/
+
+void
+c7000_queue_buffer(struct c7000_unit *cup, struct c7000_buffer *buf)
+{
+	buf->next = NULL;
+
+	if (cup->proc_head == NULL) {
+		cup->proc_head = cup->proc_tail = buf;
+		return;
+	}
+
+	cup->proc_tail->next = buf;
+	cup->proc_tail = buf;
+	return;
+}
+
+/*
+	Dequeue a buffer from the start of the processing (proc) chain.
+*/
+
+struct c7000_buffer *
+c7000_dequeue_buffer(struct c7000_unit *cup)
+{
+	struct	c7000_buffer	*buf = cup->proc_head;
+
+	if (buf == NULL)
+		return(NULL);
+
+	cup->proc_head = buf->next;
+
+	if (cup->proc_head == NULL)
+		cup->proc_tail = NULL;
+
+	buf->next = NULL;
+	return(buf);
+}
+
+/*
+	Queue a buffer on the end of the bh routine chain.
+*/
+
+void
+c7000_queue_bh_buffer(struct c7000_unit *cup, struct c7000_buffer *buf)
+{
+	buf->next = NULL;
+
+	if (cup->bh_head == NULL) {
+		cup->bh_head = cup->bh_tail = buf;
+		return;
+	}
+
+	cup->bh_tail->next = buf;
+	cup->bh_tail = buf;
+	return;
+}
+
+/*
+	Dequeue a buffer from the start of the bh routine chain.
+*/
+
+struct c7000_buffer *
+c7000_dequeue_bh_buffer(struct c7000_unit *cup)
+{
+	struct	c7000_buffer	*buf = cup->bh_head;
+
+	if (buf == NULL)
+		return(NULL);
+
+	cup->bh_head = buf->next;
+
+	if (cup->bh_head == NULL)
+		cup->bh_tail = NULL;
+
+	buf->next = NULL;
+	return(buf);
+}
+
+/*
+	Build up a list of buffers to read.  Each buffer is described
+	by one c7000_buffer structure.  The c7000_buffer structure
+	contains a channel segment that will read that one buffer.
+	The channel program segments are chained together via TIC
+	CCWS.
+*/
+
+static int
+c7000_bld_read_chain(struct c7000_unit *cup)
+{
+	struct	c7000_buffer	*buf, *pbuf = NULL;
+	struct	c7000_rd_header	*head;
+	int			num = 0;
+
+	while (cup->free != NULL) {
+
+		/*
+			Obtain a buffer for a read channel segment.
+		*/
+
+		if ((buf = c7000_get_buffer(cup)) == NULL) {
+			CPrintk(0, "c7000: c7000_bld_read_chain: can not obtain a read buffer for unit 0x%x\n", cup->devno);
+			return(-ENOMEM);
+		}
+
+		num++;
+		buf->len = 0;
+
+		/*
+			Clear out the read header flag.
+		*/
+
+		head = (struct c7000_rd_header *)(buf->data + C7000_DATAL);
+		head->flag = 0x00;
+		c7000_queue_buffer(cup, buf);
+
+		/*
+			Build the read channel program segment.
+		*/
+
+		c7000_bld_read_chpgm(cup, buf);
+
+		/*
+			Chain the prior (if any) channel program segment to
+			this one.
+		*/
+
+		if (pbuf != NULL)
+			pbuf->ccws[3].cda = pbuf->ccws[5].cda = (__u32)virt_to_phys(&buf->ccws[0]);
+
+		pbuf = buf;
+	}
+
+	CPrintk(1, "c7000: c7000_bld_read_chain: chained %d buffers for unit 0x%x\n", num, cup->devno);
+	return(0);
+}
+
+/*
+	Build up a list of buffers to write.  Each buffer is described
+	by one c7000_buffer structure.  The c7000_buffer structure
+	contains a channel segment that will write that one buffer.
+	The channel program segments are chained together via TIC
+	CCWS.
+*/
+
+static void
+c7000_bld_wrt_chain(struct c7000_unit *cup)
+{
+	struct	c7000_buffer	*buf = cup->proc_head, *pbuf = NULL;
+	int			num = 0;
+
+	while (buf != NULL) {
+		c7000_bld_wrt_chpgm(cup, buf);
+
+		/*
+			Chain the channel program segments together.
+		*/
+
+		if (pbuf != NULL)
+			pbuf->ccws[2].cda = (__u32)virt_to_phys(&buf->ccws[0]);
+
+		pbuf = buf;
+		buf = buf->next;
+		num++;
+	}
+
+	CPrintk(1, "c7000: c7000_bld_wrt_chain: chained %d buffers for unit 0x%x\n", num, cup->devno);
+	return;
+}
+
+/*
+	Interrupt handler bottom half (bh) routine.
+	Process all of the buffers on the c7000_unit bh chain.
+	The bh chain is populated by the interrupt routine when
+	a READ channel program completes on a buffer.
+*/
+
+static void
+c7000_irq_bh(struct c7000_unit *cup)
+{
+	struct	c7000_buffer		*buf, *pbuf;
+	struct	c7000_rd_header		*head;	
+	struct	sk_buff			*skb;
+	struct	c7000_controller	*ccp;
+	STRUCT_NET_DEVICE		*dev;
+	int				rc;
+	__u16				data_length;
+	__u32				parm;
+	__u8				flags = 0x00;
+	__u32				saveflags;
+
+	ccp = cup->cntlp;
+	dev = ccp->dev;
+
+	s390irq_spin_lock_irqsave(cup->irq, saveflags);
+
+	/*
+		Process all buffers sent to bh by the interrupt routine.
+	*/
+
+	while (cup->bh_head != NULL) {
+		buf = c7000_dequeue_bh_buffer(cup);
+
+		/*
+			Deference the data as a c7000 header.
+		*/
+
+		head = (struct c7000_rd_header *)(buf->data + C7000_DATAL);
+
+		/*
+			If it is a control message, release the buffer and 
+			continue the loop.
+		*/
+
+		if (C7000_LINKID(head->cmd) == 0) {
+			CPrintk(0, "c7000: c7000_irq_bh: unexpected control command %d on unit 0x%x\n", head->cmd, cup->devno);
+			c7000_release_buffer(cup, buf);
+			continue;
+		}
+			
+		/*
+			Allocate a socket buffer.
+		*/
+
+		data_length = head->len;
+		skb = dev_alloc_skb(data_length);
+
+		/*
+			Copy the data to the skb.
+			Send it to the upper layers.
+		*/
+
+		if (skb != NULL) {
+			memcpy(skb_put(skb, data_length), buf->data, data_length);
+			skb->dev = dev;
+			skb->protocol = htons(ETH_P_IP);
+			skb->pkt_type = PACKET_HOST;
+			skb->mac.raw = skb->data;
+			skb->ip_summed = CHECKSUM_UNNECESSARY;
+			netif_rx(skb);
+			ccp->stats.rx_packets++;
+		} else {
+			CPrintk(0, "c7000: c7000_irq_bh: can not allocate a skb for unit 0x%x\n", cup->devno);
+			ccp->stats.rx_dropped++;
+		}
+
+		/*
+			Rechain the buffer on the processing list.
+		*/
+
+		head->flag = 0x00;
+		buf->len = 0;
+		pbuf = cup->proc_tail;
+		c7000_queue_buffer(cup, buf);
+
+		/*
+			Rechain the buffer on the running channel program.
+		*/
+
+		if (pbuf != NULL)
+			pbuf->ccws[3].cda = pbuf->ccws[5].cda = (__u32)virt_to_phys(&buf->ccws[0]);
+
+	}
+
+	/*
+		Restart the READ channel program if IO_active is 0.
+	*/
+
+	if (test_and_set_bit(0, (void *)&cup->IO_active) == 0) {
+
+		if ((rc = c7000_bld_read_chain(cup)) != 0) {
+			CPrintk(0, "c7000: c7000_irq_bh: can not build read chain for unit 0x%x, return code %d\n", cup->devno, rc);
+			c7000_error(cup->cntlp);
+			clear_bit(C7000_BH_ACTIVE, (void *)&cup->flag_a);
+			s390irq_spin_unlock_irqrestore(cup->irq, saveflags);
+			return;
+		}
+
+		parm = (__u32)cup;
+		cup->state = C7000_READ;
+
+		if ((rc = do_IO(cup->irq, &cup->proc_head->ccws[0], parm, 0xff, flags)) != 0) {
+			CPrintk(0, "c7000: c7000_irq_bh: can not start READ IO to unit 0x%x, return code %d\n", cup->devno, rc);
+			c7000_error(cup->cntlp);
+			clear_bit(C7000_BH_ACTIVE, (void *)&cup->flag_a);
+			s390irq_spin_unlock_irqrestore(cup->irq, saveflags);
+			return;
+		}
+
+		CPrintk(1, "c7000: c7000_irq_bh: started READ IO to unit 0x%x\n", cup->devno);
+	}
+			
+	/*
+		Clear the bh active indication.
+	*/
+
+	clear_bit(C7000_BH_ACTIVE, (void *)&cup->flag_a);
+	s390irq_spin_unlock_irqrestore(cup->irq, saveflags);
+	return;
+}
+
+/*
+	Send a system validate control command to a unit.
+*/
+
+static int
+c7000_send_sysval(struct c7000_unit *cup)
+{
+	int				rc;
+	struct	c7000_controller	*ccp = cup->cntlp;
+	struct	c7000_control_blk	*ctlblkp = &(cup->control_blk);
+
+	CPrintk(1, "c7000: c7000_send_sysval: send sysval for device 0x%x\n", cup->devno);
+
+	/*
+		Build the system validate control message.
+	*/
+
+	memset(ctlblkp, '\0', sizeof(struct c7000_control_blk));
+	ctlblkp->cmd = C7000_SYS_VALIDATE;
+	ctlblkp->correlator = 0;
+	ctlblkp->link_id = ccp->linkid;
+	ctlblkp->ver = ccp->version; 
+	memcpy(ctlblkp->hostname, ccp->lhost, NAMLEN);
+	memcpy(ctlblkp->unitname, ccp->uhost, NAMLEN);
+	ctlblkp->rdsize = C7000_DATAL;
+        ctlblkp->wrtsize = C7000_DATAL;
+
+	/*
+		Build the channel program.
+	*/
+
+	c7000_bld_wrtctl_chpgm(cup);
+
+	/*
+		Do the IO and wait for write to complete.
+	*/
+
+	if ((rc = c7000_doio(cup)) != 0) {
+		CPrintk(0, "c7000: c7000_send_sysval failed with rc = %d for unit 0x%x\n", rc, cup->devno);
+		return(-1);
+	}
+
+	return(0);
+}
+
+/*
+	Send a system validate response control command to a unit.
+*/
+
+static int
+c7000_send_sysval_resp(struct c7000_unit *cup, unsigned char correlator, int ret_code)
+{
+	int				rc;
+	struct	c7000_controller	*ccp = cup->cntlp;
+	struct	c7000_control_blk	*ctlblkp = &(cup->control_blk);
+
+	CPrintk(1, "c7000: c7000_send_sysval_resp: send sysval response for device 0x%x\n", cup->devno);
+
+	/*
+		Build the system validate response control message.
+	*/
+
+	memset(ctlblkp, '\0', sizeof(struct c7000_control_blk));
+	ctlblkp->cmd = C7000_SYS_VALIDATE_RESP;
+	ctlblkp->correlator = correlator;
+	ctlblkp->ret_code = ret_code;
+	ctlblkp->link_id = ccp->linkid;
+	ctlblkp->ver = ccp->version; 
+	memcpy(ctlblkp->hostname, ccp->lhost, NAMLEN);
+	memcpy(ctlblkp->unitname, ccp->uhost, NAMLEN);
+	ctlblkp->rdsize = C7000_DATAL;
+        ctlblkp->wrtsize = C7000_DATAL;
+
+	/*
+		Build the channel program.
+	*/
+
+	c7000_bld_wrtctl_chpgm(cup);
+
+	/*
+		Do the IO and wait for write to complete.
+	*/
+
+	if ((rc = c7000_doio(cup)) != 0) {
+		CPrintk(0, "c7000: c7000_send_sysval_resp failed with rc = %d for unit 0x%x\n", rc, cup->devno);
+		return(-1);
+	}
+
+	return(0);
+}
+
+/*
+	Check the information read in a SYS_VALIDATE control message.
+*/
+
+static int
+c7000_checkinfo(struct c7000_unit *cup)
+{
+	struct	c7000_controller	*ccp = cup->cntlp;
+ 	struct	c7000_control_blk	*ctlblkp = &cup->control_blk;
+	int				ret_code = 0;
+
+	if (memcmp(ccp->lhost, ctlblkp->hostname, NAMLEN) ||
+		memcmp(ccp->uhost, ctlblkp->unitname, NAMLEN))
+		ret_code = Err_Names_not_Matched;
+
+	if (ctlblkp->ver != ccp->version)
+		ret_code = Err_Wrong_Version;
+
+        if ((ctlblkp->rdsize < C7000_DATAL) || (ctlblkp->wrtsize < C7000_DATAL))
+		ret_code = Err_Wrong_Frame_Size;
+
+	if (ret_code != 0)
+		CPrintk(0, "c7000: c7000_checkinfo: ret_code %d for device 0x%x\n", ret_code, cup->devno);
+
+	return(ret_code);
+}
+
+/*
+	Keep reading until a sysval response comes in or an error.
+*/
+
+static int
+c7000_get_sysval_resp(struct c7000_unit *cup)
+{
+	struct	c7000_controller	*ccp = cup->cntlp;
+	int				resp = 1;
+	int				req = 1;
+	int				rc;
+	int				ret_code = 0;
+
+	CPrintk(1, "c7000: c7000_get_sysval_resp: get sysval response for unit 0x%x\n", cup->devno);
+
+	/*
+		Wait for the response to C7000_SYS_VALIDATE and for an
+		inbound C7000_SYS_VALIDATE.
+	*/
+
+	while (resp || req) {
+
+		/*
+			Build the read channel program.
+		*/
+
+		c7000_bld_readctl_chpgm(cup);
+
+		if ((rc = c7000_doio(cup)) != 0) {
+			CPrintk(0, "c7000: c7000_get_sysval_resp: failed with rc = %d for unit 0x%x\n", rc, cup->devno);
+			return(-1);
+		}
+
+		/*
+			Process the control message.
+		*/
+
+		switch (cup->control_blk.cmd) {
+
+			/*
+				Check that response is positive and return
+				with success. Otherwise, return with an
+				error.
+			*/
+
+			case C7000_SYS_VALIDATE_RESP:
+
+				if (cup->control_blk.ret_code == 0)
+					resp = 0;
+				else {
+					CPrintk(0, "c7000: c7000_get_sysval_resp: receive sysval response for device 0x%x, return code %d\n",
+						cup->devno,
+						cup->control_blk.ret_code);
+					return(-1);
+				}
+
+				break;
+
+			/*
+				Check that the request is reasonable and
+				send a SYS_VALIDATE_RESP.  Otherwise,
+				return with an error.
+			*/
+
+			case C7000_SYS_VALIDATE:
+				CPrintk(1, "c7000: c7000_get_sysval_resp: receive sysval for device 0x%x\n", cup->devno);
+				req = 0;
+				ret_code = c7000_checkinfo(cup);
+
+				if (c7000_send_sysval_resp(&ccp->cunits[C7000_WR], cup->control_blk.correlator, ret_code) != 0)
+					return(-1);
+
+				if (ret_code != 0)
+					return(-1);
+
+				break;
+
+			/*
+				Anything else is unexpected and will result
+				in a return with an error.
+			*/
+
+			default:
+				CPrintk(0, "c7000: c7000_get_sysval_resp: receive unexpected command for device 0x%x, command %d\n", cup->devno, cup->control_blk.cmd);
+				return(-1);
+				break;
+		}
+
+	}
+
+	return(0);
+}
+
+/*
+	Send a connection confirm control message.
+*/
+
+static int
+c7000_conn_confrm(struct c7000_unit *cup, unsigned char correlator, int linkid)
+{
+	int				rc;
+	struct	c7000_controller	*ccp = cup->cntlp;
+	struct	c7000_control_blk	*ctlblkp = &(cup->control_blk);
+
+	CPrintk(1, "c7000: c7000_conn_confrm: send the connection confirmation message for unit 0x%x\n", cup->devno);
+
+	/*
+		Build the connection confirm control message.
+	*/
+
+	memset(ctlblkp, '\0', sizeof(struct c7000_control_blk));
+	ctlblkp->cmd = C7000_CONN_CONFRM;
+	ctlblkp->ver = ccp->version;
+	ctlblkp->link_id = linkid;
+	ctlblkp->correlator = correlator;
+	ctlblkp->rdsize = 0;
+	ctlblkp->wrtsize = 0;
+	memcpy(ctlblkp->hostname, ccp->lappl, NAMLEN);
+	memcpy(ctlblkp->unitname, ccp->uappl, NAMLEN);
+
+	/*
+		Build the channel program.
+	*/
+
+	c7000_bld_wrtctl_chpgm(cup);
+
+	/*
+		Do the IO and wait for write to complete.
+	*/
+
+	if ((rc = c7000_doio(cup)) != 0) {
+		CPrintk(0, "c7000: c7000_conn_confrm: failed with rc = %d for unit 0x%x\n", rc, cup->devno);
+		return(-1);
+	}
+
+	return(0);
+}
+
+/*
+	Send a connection request control message.
+*/
+
+static int
+c7000_send_conn(struct c7000_unit *cup)
+{
+	int				rc;
+	struct	c7000_controller	*ccp = cup->cntlp;
+	struct	c7000_control_blk	*ctlblkp = &(cup->control_blk);
+
+	CPrintk(1, "c7000: c7000_send_conn: send the connection request message for unit 0x%x\n", cup->devno);
+
+	/*
+		Build the connection request control message.
+	*/
+
+	memset(ctlblkp, '\0', sizeof(struct c7000_control_blk));
+	ctlblkp->cmd = C7000_CONN_REQ;
+	ctlblkp->ver = ccp->version;
+	ctlblkp->link_id = 0;
+	ctlblkp->correlator = 0;
+	ctlblkp->rdsize = C7000_DATAL;
+	ctlblkp->wrtsize = C7000_DATAL;
+	memcpy(ctlblkp->hostname, ccp->lappl, NAMLEN);
+	memcpy(ctlblkp->unitname, ccp->uappl, NAMLEN);
+
+	/*
+		Build the channel program.
+	*/
+
+	c7000_bld_wrtctl_chpgm(cup);
+
+	/*
+		Do the IO and wait for write to complete.
+	*/
+
+	if ((rc = c7000_doio(cup)) != 0) {
+		CPrintk(0, "c7000: c7000_send_conn: failed with rc = %d for unit 0x%x\n", rc, cup->devno);
+		return(-1);
+	}
+
+	return(0);
+}
+
+/*
+	Send a disconnect control message to the link with the value of
+	linkid.
+*/
+
+static int
+c7000_send_disc(struct c7000_unit *cup, int linkid)
+{
+	int				rc;
+	struct	c7000_controller	*ccp = cup->cntlp;
+	struct	c7000_control_blk	*ctlblkp = &(cup->control_blk);
+
+	CPrintk(1, "c7000: c7000_send_disc: send disconnect message for unit 0x%x\n", cup->devno);
+
+	/*
+		Build the disconnect control message.
+	*/
+
+	memset(ctlblkp, '\0', sizeof(struct c7000_control_blk));
+	ctlblkp->cmd = C7000_DISCONN;
+	ctlblkp->ver = ccp->version;
+	ctlblkp->link_id = linkid;
+	ctlblkp->correlator = 0;
+	ctlblkp->rdsize = C7000_DATAL;
+	ctlblkp->wrtsize = C7000_DATAL;
+	memcpy(ctlblkp->hostname, ccp->lappl, NAMLEN);
+	memcpy(ctlblkp->unitname, ccp->uappl, NAMLEN);
+
+	/*
+		Build the channel program.
+	*/
+
+	c7000_bld_wrtctl_chpgm(cup);
+
+	/*
+		Do the IO and wait for write to complete.
+	*/
+
+	if ((rc = c7000_doio(cup)) != 0) {
+		CPrintk(0, "c7000: c7000_send_disc: failed with rc = %d for unit 0x%x\n", rc, cup->devno);
+		return(-1);
+	}
+
+	return(0);
+}
+
+/*
+	Resolve the race condition based on the link identifier value.
+	The adapter microcode assigns the identifiers.  A higher value implies
+	that the race was lost. A side effect of this function is that
+	ccp->linkid is set to the link identifier to be used for this
+	connection (provided that 0 is returned).
+*/
+
+static int
+c7000_resolve_race(struct c7000_unit *cup, int local_linkid, int remote_linkid)
+{
+	struct c7000_controller 	*ccp = cup->cntlp;
+
+	CPrintk(1, "c7000: c7000_resolve_race: for unit 0x%x, local linkid %d, remote linkid %d\n", cup->devno, local_linkid, remote_linkid);
+
+	/*
+		This local link identifier should not be zero..
+	*/
+
+	if (local_linkid == 0) {
+		CPrintk(0, "c7000: c7000_resolve_race: error for unit 0x%x, local linkid is null\n", cup->devno);
+		return(-1);
+	}
+
+	/*
+		This indicates that there is no race.  Just use our
+		local link identifier.
+	*/
+
+	if (remote_linkid == 0) {
+		ccp->linkid = local_linkid;
+		return(0);
+	}
+
+	/*
+		Send a connection confirm message if we lost the race to
+		the winning link identifier.
+
+		Either way, save the winning link identifier.
+	*/
+
+	if (local_linkid > remote_linkid) {
+
+		if (c7000_conn_confrm(&ccp->cunits[C7000_WR], cup->control_blk.correlator, remote_linkid) != 0) {
+			CPrintk(0, "c7000: c7000_resolve_race: failed for unit 0x%x\n", cup->devno);
+			return(-1);
+		}
+
+		ccp->linkid = remote_linkid;
+	} else {
+		ccp->linkid = local_linkid;
+	}
+
+	return(0);
+}
+
+/*
+	Get connected by processing the connection request/response/confirm
+	control messages.  A connection request has already been sent by
+	calling function c7000_send_conn.
+*/
+
+static int
+c7000_get_conn(struct c7000_unit *cup)
+{
+	struct c7000_controller 	*ccp = cup->cntlp;
+	int				rc;
+	int				cont = 1;
+	int				remote_linkid = 0;
+	int				local_linkid = 0;
+
+	CPrintk(1, "c7000: c7000_get_conn: read the connected message for unit 0x%x\n", cup->devno);
+	ccp->linkid = 0;
+
+	while (cont == 1) {
+
+		/*
+			Build the read channel program.
+		*/
+
+		c7000_bld_readctl_chpgm(cup);
+
+		/*
+			Start the channel program to read a control message.
+		*/
+
+		if ((rc = c7000_doio(cup)) != 0) {
+			CPrintk(0, "c7000: c7000_get_conn: failed with rc = %d for unit 0x%x\n", rc, cup->devno);
+			return(-1);
+		}
+
+		/*
+			Process the control message that was received based
+			on the command code.
+		*/
+
+		CPrintk(1, "c7000: c7000_get_conn: received command %d for unit 0x%x\n", cup->control_blk.cmd, cup->devno);
+
+		switch(cup->control_blk.cmd) {
+
+			/*
+				Save the remote link_id in the message for
+				a check in c7000_resolve_race.
+			*/
+
+			case C7000_CONN_REQ:
+				remote_linkid = cup->control_blk.link_id;
+				break;
+
+			/*
+				A connection response received.  Resolve
+				the network race condition (if any) by
+				comparing the link identifier values.
+			*/
+
+			case C7000_CONN_RESP:
+
+				if (cup->control_blk.ret_code != 0) {
+					CPrintk(0, "c7000: c7000_get_conn: failed for unit 0x%x , connection response return code %d\n",
+						cup->devno, cup->control_blk.ret_code);
+					return(-1);
+				}
+
+				local_linkid = cup->control_blk.link_id;
+
+				if (c7000_resolve_race(cup, local_linkid, remote_linkid) != 0)
+					return(-1);
+
+				break;
+
+			/*
+				Got a confirmation to our connection request.
+				Disconnect the remote link identifier (if any).
+				Break out of the loop.
+			*/
+
+			case C7000_CONN_CONFRM:
+
+				if (remote_linkid != 0) {
+
+					if (c7000_send_disc(&ccp->cunits[C7000_WR], remote_linkid) != 0) {
+						CPrintk(0, "c7000: c7000_get_conn: send disconnect failed for unit 0x%x\n", cup->devno);
+						return(-1);
+					}
+
+				}
+
+				cont = 0;
+				break;
+
+			/*
+				Got a disconnect to our connection request.
+				Break out of the loop.
+			*/
+
+			case C7000_DISCONN:
+				cont = 0;
+				break;
+
+			/*
+				Anything else must be an error.
+				Return with an error immediately.
+			*/
+
+			default:
+				CPrintk(0, "c7000: c7000_get_conn: failed for unit 0x%x unexpected command %d\n",
+					cup->devno, cup->control_blk.cmd);
+				return(-1);
+		}
+
+	}
+
+	/*
+		Be sure that we now have a link identifier.
+	*/
+
+	if (ccp->linkid == 0)
+		return(-1);
+
+	return(0);
+}
+
+/*
+	Get statistics method.
+*/
+
+struct net_device_stats *
+c7000_stats(STRUCT_NET_DEVICE *dev)
+{
+	struct	c7000_controller	*ccp = (struct c7000_controller *)dev->priv;
+
+	return(&ccp->stats);
+}
+
+/*
+	Open method.
+*/
+
+static int
+c7000_open(STRUCT_NET_DEVICE *dev)
+{
+	int				i;
+	struct	c7000_controller	*ccp = (struct c7000_controller *)dev->priv;
+	struct	c7000_unit		*cup;
+	int				rc;
+	__u32				parm;
+	__u8				flags = 0x00;
+
+	c7000_set_busy(dev);
+
+	/*
+		Allocate space for the unit buffers.
+	*/
+
+	if (c7000_alloc_buffers(dev) == -1) {
+		CPrintk(0, "c7000: c7000_open: can not allocate buffer space for base unit 0x%lx\n", dev->base_addr);
+		c7000_free_buffers(dev);  /* free partially allocated buffers */
+		c7000_clear_busy(dev);
+		return(-ENOMEM);
+	}
+	
+	/*
+		Perform the initialization for all units.
+	*/
+
+	for (i = 0; i < NUNITS; i++) {
+		cup = &ccp->cunits[i];
+
+		/*
+			Initialize task queue structure used for the bottom
+			half routine.
+		*/
+
+#ifndef NEWSTUFF
+		cup->tq.next = NULL;
+#endif
+		cup->tq.sync = 0;
+		cup->tq.routine = (void *)(void *)c7000_irq_bh;
+		cup->tq.data = cup;
+		cup->state = C7000_HALT;
+#ifdef NEWSTUFF
+		init_waitqueue_head(&cup->wait);
+#endif
+		CPrintk(1, "c7000: c7000_open: issuing halt to unit 0x%x\n", cup->devno);
+
+		/*
+			Issue a halt I/O to the unit
+		*/
+
+		if ((rc = c7000_haltio(cup)) != 0) {
+			CPrintk(0, "c7000: c7000_open: halt_IO failed with rc = %d for unit 0x%x\n", rc, cup->devno);
+			continue;
+		}
+
+		cup->IO_active = 0;
+		cup->flag_a = 0;
+		cup->sigsmod = 0x00;
+
+		CPrintk(1, "c7000: c7000_open: halt complete for unit 0x%x\n", cup->devno);
+	}
+
+	/*
+		On each subchannel send a sense id.
+	*/
+
+	for (i = 0; i < NUNITS; i++) {
+		cup = &ccp->cunits[i];
+
+		/*
+			Build SENSE ID channel program.
+		*/
+
+		c7000_bld_senseid_chpgm(cup);
+	
+		/*
+			Issue the start I/O for SENSE ID channel program.
+		*/
+
+		CPrintk(1, "c7000: c7000_open: issuing SENSEID to unit 0x%x\n", cup->devno);
+
+		if ((rc = c7000_doio(cup)) != 0) {
+			CPrintk(0, "c7000: c7000_open: SENSEID failed with rc = %d for unit 0x%x\n", rc, cup->devno);
+			c7000_clear_busy(dev);
+			return(-EIO);
+		}
+	
+		CPrintk(1, "c7000: c7000_open: SENSEID complete for unit 0x%x\n", cup->devno);
+	}
+
+	/*
+		Send the system validation control message.
+	*/
+
+	cup = &ccp->cunits[C7000_WR];
+
+	if (c7000_send_sysval(cup) != 0) {
+		CPrintk(0, "c7000: c7000_open: can not send sysval for unit 0x%x\n", cup->devno);
+		c7000_clear_busy(dev);
+		return(-EIO);
+	}
+
+	CPrintk(1, "c7000: c7000_open: successfully sent sysval for unit 0x%x\n", cup->devno);
+	/*
+		Get the system validation response message.
+	*/
+
+	cup = &ccp->cunits[C7000_RD];
+
+	if (c7000_get_sysval_resp(cup) != 0) {
+		CPrintk(0, "c7000: c7000_open: can not read sysval response for unit 0x%x\n", cup->devno);
+		c7000_clear_busy(dev);
+		return(-EIO);
+	}
+
+	CPrintk(1, "c7000: c7000_open: successfully received sysval reply for unit 0x%x\n", cup->devno);
+	ccp->cunits[C7000_RD].state = ccp->cunits[C7000_WR].state = C7000_CONNECT;
+
+	cup = &ccp->cunits[C7000_WR];
+
+	/*
+		Send a connection request.
+	*/
+
+	if (c7000_send_conn(cup) != 0) {
+		CPrintk(0, "c7000: c7000_open: connection failed for unit 0x%x\n", cup->devno);
+		c7000_clear_busy(dev);
+		return(-EIO);
+	}
+
+	cup = &ccp->cunits[C7000_RD];
+
+	/*
+		Get the response to our connection request Note that a
+		network race may occur.  This is handled in c7000_get_conn.
+	*/
+
+	if (c7000_get_conn(cup) != 0) {
+		CPrintk(0, "c7000: c7000_open: unit 0x%x has connected\n", cup->devno);
+		c7000_clear_busy(dev);
+		return(-EIO);
+	}
+
+	CPrintk(1, "c7000: c7000_open: successfully received connection request for unit 0x%x\n", cup->devno);
+	ccp->cunits[C7000_RD].state = ccp->cunits[C7000_WR].state = C7000_READY;
+
+	/*
+		Clear the interface statistics.
+	*/
+
+	memset(&ccp->stats, '\0', sizeof(struct net_device_stats));
+
+	if ((rc = c7000_bld_read_chain(cup)) != 0) {
+		c7000_clear_busy(dev);
+		return(rc);
+	}
+
+	/*
+		Start the C7000_READ channel program but do not wait for it's
+		completion.
+	*/
+
+	cup->state = C7000_READ;
+	parm = (__u32) cup;
+	set_bit(0, (void *)&cup->IO_active);
+
+	if ((rc = do_IO(cup->irq, &cup->proc_head->ccws[0], parm, 0xff, flags)) != 0) {
+		CPrintk(0, "c7000: c7000_open: READ failed with return code %d for unit 0x%x\n", rc, cup->devno);
+		c7000_error(cup->cntlp);
+		clear_bit(0, (void *)&cup->IO_active);
+		c7000_clear_busy(dev);
+		return(-EIO);
+	}
+
+#ifdef NEWSTUFF
+	netif_start_queue(dev);
+#else
+	dev->start = 1;
+#endif
+	CPrintk(0, "c7000: c7000_open: base unit 0x%lx is opened\n", dev->base_addr);
+	c7000_clear_busy(dev);
+	MOD_INC_USE_COUNT;	/* increment module usage count */
+	return(0);
+}
+
+/*
+	Stop method.
+*/
+
+static int
+c7000_stop(STRUCT_NET_DEVICE *dev)
+{
+	int				i;
+	struct	c7000_controller	*ccp = (struct c7000_controller *)dev->priv;
+	struct	c7000_unit		*cup;
+	int				rc;
+
+#ifdef NEWSTUFF
+/* nothing? */
+#else
+	dev->start = 0;
+#endif
+	c7000_set_busy(dev);
+
+	/*
+		Send a disconnect message.
+	*/
+
+	ccp->cunits[C7000_RD].state = ccp->cunits[C7000_WR].state = C7000_DISC;
+	cup = &ccp->cunits[C7000_WR];
+
+	if (c7000_send_disc(cup, ccp->linkid) != 0) {
+		CPrintk(0, "c7000: c7000_stop: send of disconnect message failed for unit 0x%x\n", cup->devno);
+	}
+
+	CPrintk(1, "c7000: c7000_stop: successfully sent disconnect message to unit 0x%x\n", cup->devno);
+
+	/*
+		Issue a halt I/O to all units.
+	*/
+
+	for (i = 0; i < NUNITS; i++) {
+		cup = &ccp->cunits[i];
+		cup->state = C7000_STOP;
+		CPrintk(1, "c7000: c7000_stop: issuing halt to unit 0x%x\n", cup->devno);
+
+		if ((rc = c7000_haltio(cup)) != 0) {
+			CPrintk(0, "c7000: c7000_stop: halt_IO failed with rc = %d for unit 0x%x\n", rc, cup->devno);
+			continue;
+		}
+
+		CPrintk(1, "c7000: c7000_stop: halt complete for unit 0x%x\n", cup->devno);
+	}
+
+	c7000_free_buffers(dev);
+	CPrintk(0, "c7000: c7000_stop: base unit 0x%lx is stopped\n", dev->base_addr);
+	MOD_DEC_USE_COUNT;	/* Decrement module usage count */
+	return(0);
+}
+
+/*
+	Configure the interface.
+*/
+
+static int
+c7000_config(STRUCT_NET_DEVICE *dev, struct ifmap *map)
+{
+	CPrintk(1, "c7000: c7000_config: entered for base unit 0x%lx\n", dev->base_addr);
+	return(0);
+}
+
+/*
+	Transmit a packet.
+*/
+
+static int
+c7000_xmit(struct sk_buff *skb, STRUCT_NET_DEVICE *dev)
+{
+	struct	c7000_controller	*ccp = (struct c7000_controller *)dev->priv;
+	struct	c7000_unit		*cup;
+	__u32				saveflags;
+	__u32				parm;
+	__u8				flags = 0x00;
+	struct	c7000_buffer		*buf, *pbuf;
+	int				rc;
+
+	CPrintk(1, "c7000: c7000_xmit: entered for base unit 0x%lx\n", dev->base_addr);
+
+	/*
+		When the skb pointer is NULL return.
+	*/
+
+	if (skb == NULL) {
+		CPrintk(0, "c7000: c7000_xmit: skb pointer is null for base unit 0x%lx\n", dev->base_addr);
+		ccp->stats.tx_dropped++;
+		return(-EIO);
+	}
+
+	cup = &ccp->cunits[C7000_WR];
+
+	/*
+		Lock the irq.
+	*/
+
+	s390irq_spin_lock_irqsave(cup->irq, saveflags);
+	
+	/*
+		When the device transmission busy flag is on , no data
+		can be sent.  Unlock the irq and return EBUSY.
+	*/
+
+	if (c7000_check_busy(dev)) {
+		CPrintk(1, "c7000: c7000_xmit: c7000_check_busy returns true for base unit 0x%lx\n", dev->base_addr);
+		s390irq_spin_unlock_irqrestore(cup->irq, saveflags);
+		return(-EBUSY);
+	}
+
+	/*
+		Set the device transmission busy flag on atomically.
+	*/
+
+	if (c7000_ts_busy(TB_TX, dev)) {
+		CPrintk(1, "c7000: c7000_xmit: c7000_ts_busy returns true for base unit 0x%lx\n", dev->base_addr);
+		s390irq_spin_unlock_irqrestore(cup->irq, saveflags);
+		return(-EBUSY);
+	}
+
+	CPrintk(1, "c7000: c7000_xmit: set TB_TX for unit 0x%x\n", cup->devno);
+
+	/*
+		Obtain a free buffer.  If none are free then mark tbusy
+		with TB_NOBUFFER and return EBUSY.
+	*/
+
+	if ((buf = c7000_get_buffer(cup)) == NULL) {
+		CPrintk(1, "c7000: c7000_xmit: setting TB_NOBUFFER for base unit 0x%lx\n", dev->base_addr);
+		c7000_setbit_busy(TB_NOBUFFER, dev);
+		c7000_clearbit_busy(TB_TX, dev);
+		s390irq_spin_unlock_irqrestore(cup->irq, saveflags);
+		return(-EBUSY);
+	}
+
+	CPrintk(1, "c7000: c7000_xmit: Got buffer for unit 0x%x\n", cup->devno);
+
+	/*
+		Save the length of the skb data and then copy it to the
+		buffer.  Queue the buffer on the processing list.
+	*/
+
+	buf->len = skb->len;
+	memcpy(buf->data, skb->data, skb->len);
+	memset(buf->data + C7000_DATAL + C7000_READHDRL, '\0', C7000_READFFL);
+	pbuf = cup->proc_tail;
+	c7000_queue_buffer(cup, buf);
+
+	/*
+		Chain the buffer to the running channel program.
+	*/
+
+	if (test_bit(0, (void *)&cup->IO_active) && pbuf != NULL) {
+		c7000_bld_wrt_chpgm(cup, buf);
+		pbuf->ccws[2].cda = (__u32)virt_to_phys(&buf->ccws[0]);
+	}
+
+	/*
+		Free the socket buffer.
+	*/
+
+	dev_kfree_skb(skb);
+
+	/*
+		If the unit is not currently doing IO, build a channel
+		program and start the IO for the buffers on the processing
+		chain.
+	*/
+
+	if (test_and_set_bit(0, (void *)&cup->IO_active) == 0) {
+		CPrintk(1, "c7000: c7000_xmit: start IO for unit 0x%x\n", cup->devno);
+		c7000_bld_wrt_chain(cup);
+		parm = (__u32) cup;
+		cup->state = C7000_WRITE;
+
+		if ((rc = do_IO(cup->irq, &cup->proc_head->ccws[0], parm, 0xff, flags)) != 0) {
+			CPrintk(0, "c7000: c7000_xmit: do_IO failed with return code %d for unit 0x%x\n", rc, cup->devno);
+			c7000_error(cup->cntlp);
+			c7000_clearbit_busy(TB_TX, dev);
+			s390irq_spin_unlock_irqrestore(cup->irq, saveflags);
+			return(-EIO);
+		}
+
+		dev->trans_start = jiffies;
+		CPrintk(1, "c7000: c7000_xmit: do_IO succeeds for unit 0x%x\n", cup->devno);
+	}
+		
+	/*
+		If the free chain is now NULL, set the TB_NOBUFFER flag.
+	*/
+
+	if (cup->free == NULL) {
+		CPrintk(1, "c7000: c7000_xmit: setting TB_NOBUFFER for base unit 0x%lx\n", dev->base_addr);
+		c7000_setbit_busy(TB_NOBUFFER, dev);
+	}
+
+	c7000_clearbit_busy(TB_TX, dev);
+	s390irq_spin_unlock_irqrestore(cup->irq, saveflags);
+	CPrintk(1, "c7000: c7000_xmit: exits for unit 0x%x\n", cup->devno);
+	return(0);
+}
+
+/*
+	Handle an ioctl from a user process.
+*/
+
+static int
+c7000_ioctl(STRUCT_NET_DEVICE *dev, struct ifreq *ifr, int cmd)
+{
+	CPrintk(1, "c7000: c7000_ioctl: entered for base unit 0x%lx with cmd %d\n", dev->base_addr, cmd);
+	return(0);
+}
+
+/*
+	Analyze the interrupt status and return a value
+	that identifies the type.
+*/
+
+static enum c7000_rupt
+c7000_check_csw(devstat_t *devstat)
+{
+
+	/*
+		Check for channel detected conditions (except PCI).
+	*/
+
+	if ((devstat->cstat & ~SCHN_STAT_PCI) != 0) {
+		CPrintk(0, "c7000: c7000_check_csw: channel status 0x%x for unit 0x%x\n", devstat->cstat, devstat->devno);
+		return(C7000_CHANERR);
+	}
+
+	/*
+		Fast path the normal cases.
+	*/
+
+	if (devstat->dstat == (DEV_STAT_CHN_END | DEV_STAT_DEV_END))
+		return(C7000_NORMAL);
+
+	if (devstat->cstat == SCHN_STAT_PCI)
+		return(C7000_NORMAL);
+
+	/*
+		Check for exceptions.
+	*/
+
+	if (devstat->dstat & DEV_STAT_UNIT_CHECK) {
+		CPrintk(0, "c7000: c7000_check_csw: unit check for unit 0x%x, sense byte0 0x%2.2x\n", devstat->devno, devstat->ii.sense.data[0]);
+
+		if (devstat->ii.sense.data[0] == C7000_BOX_RESET)
+			return(C7000_UCK_RESET);
+		else
+			return(C7000_UCK);
+
+	} else if (devstat->dstat & DEV_STAT_UNIT_EXCEP) {
+		CPrintk(0, "c7000: c7000_check_csw: unit exception for unit 0x%x\n", devstat->devno);
+		return(C7000_UE);
+
+	} else if (devstat->dstat & DEV_STAT_ATTENTION) {
+		CPrintk(0, "c7000: c7000_check_csw: attention for unit 0x%x\n", devstat->devno);
+		return(C7000_ATTN);
+
+	} else if (devstat->dstat & DEV_STAT_BUSY) {
+		CPrintk(0, "c7000: c7000_check_csw: busy for unit 0x%x\n", devstat->devno);
+		return(C7000_BUSY);
+
+	} else {
+		CPrintk(0, "c7000: c7000_check_csw: channel status 0x%2.2x , device status 0x%2.2x, devstat flags 0x%8.8x for unit 0x%x\n",
+			devstat->cstat, devstat->dstat, devstat->flag, devstat->devno);
+		return(C7000_OTHER);
+	}
+
+	/* NOT REACHED */
+
+}
+
+/*
+	Retry the last CCW chain to the unit.
+*/
+
+static void
+c7000_retry_io(struct c7000_unit *cup)
+{
+	int	rc;
+	__u32	parm;
+	__u8	flags = 0x00;
+	ccw1_t	*ccwp;
+
+	if (++cup->retries > C7000_MAX_RETRIES) {
+		c7000_error(cup->cntlp);
+		CPrintk(0, "c7000: c7000_retry_io: retry IO for unit 0x%x exceeds maximum retry count\n", cup->devno);
+		return;
+	}
+
+	set_bit(0, (void *)&cup->IO_active);
+	parm = (__u32)cup;
+
+	if (cup->state == C7000_READ || cup->state == C7000_WRITE)
+		ccwp = &cup->proc_head->ccws[0];
+	else
+		ccwp = &cup->ccws[0];
+
+	if ((rc = do_IO(cup->irq, ccwp, parm, 0xff, flags)) != 0) {
+		CPrintk(0, "c7000: c7000_retry_io: can not retry IO for unit 0x%x, return code %d\n", cup->devno, rc);
+		clear_bit(0, (void *)&cup->IO_active);
+		c7000_error(cup->cntlp);
+	}
+
+	CPrintk(1, "c7000: c7000_retry_io: retry IO for unit 0x%x, retry count %d\n", cup->devno, cup->retries);
+	return;
+}
+
+/*
+	Process a read interrupt by scanning the list of buffers
+	for ones that have completed and queue them for the bottom
+	half to process.
+*/
+
+static void
+c7000_proc_rintr(struct c7000_unit *cup)
+{
+	struct	c7000_buffer	*buf;
+	struct	c7000_rd_header	*head;
+	int			num_read = 0;
+
+	while (cup->proc_head != NULL) {
+		head = (struct c7000_rd_header *)(cup->proc_head->data + C7000_DATAL);
+
+		/*
+			The flag byte in the read header will be set to
+			FLAG_FF when the buffer has been read.
+		*/
+
+		if (head->flag != FLAG_FF)
+			break;
+		
+		/*
+			Dequeue the buffer from the proc chain
+			and enqueue it on the bh chain for
+			the bh routine to process.
+		*/
+
+		buf = c7000_dequeue_buffer(cup);
+		c7000_queue_bh_buffer(cup, buf);
+		num_read++;
+	}
+
+	CPrintk(1, "c7000: c7000_proc_rintr: %d buffers read for unit 0x%x\n", num_read, cup->devno);
+	return;
+}
+
+/*
+	Process all completed buffers on the proc chain.
+	A buffer is completed if it's READFF flag is FLAG_FF.
+*/
+
+static int
+c7000_proc_wintr(struct c7000_unit *cup)
+{
+	struct	c7000_controller	*ccp = cup->cntlp;
+	struct	c7000_buffer		*buf;
+	int				num_write = 0;
+
+	if (cup->proc_head == NULL) {
+		CPrintk(0, "c7000: c7000_proc_wintr: unexpected NULL processing chain pointer for unit 0x%x\n", cup->devno);
+		return(num_write);
+	}
+
+	while (cup->proc_head != NULL) {
+
+		/*
+			Check if the buffer has completed.
+		*/
+
+		if (*(cup->proc_head->data + C7000_DATAL + C7000_READHDRL) != FLAG_FF)
+			break;
+
+		/*
+			Remove buffer from top of processing chain.
+			Place it on free list.
+		*/
+
+		buf = c7000_dequeue_buffer(cup);
+		c7000_release_buffer(cup, buf);
+		num_write++;
+
+		/*
+			Update transmitted packets statistic.
+		*/
+
+		ccp->stats.tx_packets++;
+	}
+
+	CPrintk(1, "c7000: c7000_proc_wintr: %d buffers written for unit 0x%x\n", num_write, cup->devno);
+	return(num_write);
+}
+
+/*
+	Interrupt handler.
+*/
+
+static void
+c7000_intr(int irq, void *initparm, struct pt_regs *regs)
+{
+	devstat_t			*devstat = ((devstat_t *) initparm);
+	struct	c7000_unit		*cup = NULL;
+	struct	c7000_controller	*ccp = NULL;
+	STRUCT_NET_DEVICE		*dev = NULL;
+	__u32				parm;
+	__u8				flags = 0x00;
+	int				rc;
+
+	/*
+		Discard unsolicited interrupts
+	*/
+
+	if (devstat->intparm == 0) {
+		CPrintk(0, "c7000: c7000_intr: unsolicited interrupt for device 0x%x, cstat = 0x%2.2x, dstat = 0x%2.2x, flag = 0x%8.8x\n",
+			devstat->devno, devstat->cstat, devstat->dstat, devstat->flag);
+		return;
+	}
+
+	/*
+		Obtain the c7000_unit structure pointer.
+	*/
+
+	cup = (struct c7000_unit *)(devstat->intparm);
+
+	/*
+		Obtain the c7000_controller structure and device structure
+		pointers.
+	*/
+
+	if (cup == NULL) {
+		CPrintk(0, "c7000: c7000_intr: c7000_unit pointer is NULL in devstat\n");
+		return;
+	}
+
+	ccp = cup->cntlp;
+
+	if (ccp == NULL) {
+		CPrintk(0, "c7000: c7000_intr: c7000_cntlp pointer is NULL in c7000_unit structure 0x%x for unit 0x%x\n", (int)cup, cup->devno);
+		return;
+	}
+
+	dev = ccp->dev;
+
+	if (dev == NULL) {
+		CPrintk(0, "c7000: c7000_intr: device pointer is NULL in c7000_controller structure 0x%x for unit 0x%x\n", (int)ccp, cup->devno);
+		return;
+	}
+
+	/*
+		Finite state machine (fsm) handling.
+	*/
+
+	CPrintk(1, "c7000: c7000_intr: entered with state %d flag 0x%8.8x for unit 0x%x\n", cup->state, devstat->flag, cup->devno);
+
+	switch(cup->state) {
+
+		/*
+			Not expected to be here when in INIT state.
+		*/
+
+		case C7000_INIT:
+
+			break;
+
+		/*
+			Enter state C7000_SID and wakeup the sleeping
+			process in c7000_open.
+		*/
+
+		case C7000_HALT:
+
+			if ((devstat->flag & DEVSTAT_FINAL_STATUS) == 0)
+				break;
+
+			cup->state = C7000_SID;
+			wake_up(&cup->wait);
+			break;
+		
+		/*
+			Enter state C7000_SYSVAL and wakeup the sleeping
+			process in c7000_open.
+		*/
+			
+		case C7000_SID:
+
+			if ((devstat->flag & DEVSTAT_FINAL_STATUS) == 0)
+				break;
+
+			if (c7000_check_csw(devstat) != 0) {
+				c7000_retry_io(cup);
+
+				if (cup->state == C7000_ERROR)
+					wake_up(&cup->wait);
+
+				break;
+			}
+
+			cup->retries = 0;
+			cup->state = C7000_SYSVAL;
+			wake_up(&cup->wait);
+			break;
+
+		/*
+			Wakeup the sleeping process in c7000_open.
+		*/
+
+		case C7000_SYSVAL:
+
+			if ((devstat->flag & DEVSTAT_FINAL_STATUS) == 0)
+				break;
+
+			if (c7000_check_csw(devstat) != 0) {
+				c7000_retry_io(cup);
+
+				if (cup->state == C7000_ERROR)
+					wake_up(&cup->wait);
+
+				break;
+			}
+
+			cup->retries = 0;
+			wake_up(&cup->wait);
+			break;
+
+		/*
+			Wakeup the sleeping process in c7000_open.
+		*/
+
+		case C7000_CONNECT:
+
+			if ((devstat->flag & DEVSTAT_FINAL_STATUS) == 0)
+				break;
+
+			if (c7000_check_csw(devstat) != 0) {
+				c7000_retry_io(cup);
+
+				if (cup->state == C7000_ERROR)
+					wake_up(&cup->wait);
+
+				break;
+			}
+
+			cup->retries = 0;
+			wake_up(&cup->wait);
+			break;
+
+		/*
+			Not expected to be entered here.
+		*/
+
+		case C7000_READY:
+			break;
+
+		/*
+			Process the data that was just read.
+		*/
+
+		case C7000_READ:
+
+			if ((devstat->flag & (DEVSTAT_PCI | DEVSTAT_FINAL_STATUS)) == 0)
+				break;
+
+			CPrintk(1, "c7000: c7000_intr: process read interrupt for unit 0x%x , devstat flag = 0x%8.8x\n", cup->devno, devstat->flag);
+
+			/*
+				Check for serious errors.
+			*/
+
+			if (c7000_check_csw(devstat) != 0) {
+				ccp->stats.rx_errors++;
+				c7000_error(cup->cntlp);
+				break;
+			}
+
+			/*
+				Build the bottom half buffer list.
+			*/
+
+			c7000_proc_rintr(cup);
+
+			/*
+				When final status is received clear
+				the IO active bit.
+			*/
+
+			if (devstat->flag & DEVSTAT_FINAL_STATUS) {
+				clear_bit(0, (void *)&cup->IO_active);
+			}
+
+			/*
+				If there are free buffers redrive the IO.
+			*/
+
+			if ((devstat->flag & DEVSTAT_FINAL_STATUS) &&
+			    (cup->free != NULL)) {
+				c7000_bld_read_chain(cup);
+				parm = (__u32)cup;
+				set_bit(0, (void *)&cup->IO_active);
+
+				if ((rc = do_IO(cup->irq, &cup->proc_head->ccws[0], parm, 0xff, flags)) != 0) {
+					clear_bit(0, (void *)&cup->IO_active);
+					CPrintk(0, "c7000: c7000_intr: do_IO failed with return code %d for unit 0x%x\n", rc, cup->devno);
+					c7000_error(cup->cntlp);
+					break;
+				}
+
+				CPrintk(1, "c7000: c7000_intr: started read io for unit 0x%x\n", cup->devno);
+			}
+
+			/*
+				Initiate bottom half routine to process
+				data that was read.
+			*/
+
+			if (test_and_set_bit(C7000_BH_ACTIVE, (void *)&cup->flag_a) == 0) {
+				queue_task(&cup->tq, &tq_immediate);
+				mark_bh(IMMEDIATE_BH);
+			}
+
+			break;
+
+		/*
+			Free the transmitted buffers and restart the channel
+			process (if necessary).
+		*/
+
+		case C7000_WRITE:
+
+			if ((devstat->flag & DEVSTAT_FINAL_STATUS) == 0)
+				break;
+
+			if (c7000_check_csw(devstat) != 0) {
+				ccp->stats.tx_errors++;
+				c7000_error(cup->cntlp);
+				break;
+			}
+
+			/*
+				If at least one buffer was freed, clear
+				the NOBUFFER indication.
+			*/
+
+			if (c7000_proc_wintr(cup) != 0) {
+				c7000_clearbit_busy(TB_NOBUFFER, dev);
+			}
+
+			/*
+				Restart the channel program if there are more
+				buffers on the processing chain.
+			*/
+
+			if (cup->proc_head != NULL) {
+				c7000_bld_wrt_chain(cup);
+				parm = (__u32)cup;
+				set_bit(0, (void *)&cup->IO_active);
+
+				if ((rc = do_IO(cup->irq, &cup->proc_head->ccws[0], parm, 0xff, flags)) != 0) {
+					CPrintk(0, "c7000: c7000_intr: do_IO failed with return code %d for unit 0x%x\n", rc, cup->devno);
+					clear_bit(0, (void *)&cup->IO_active);
+					c7000_error(cup->cntlp);
+					break;
+				}
+
+				dev->trans_start = jiffies;
+			} else {
+				clear_bit(0, (void *)&cup->IO_active);
+				cup->state = C7000_READY;
+			}
+
+			break;
+
+		/*
+			Disconnect message completed.  Wakeup the
+			sleeping process in c7000_stop.
+		*/
+
+		case C7000_DISC:
+			if ((devstat->flag & DEVSTAT_FINAL_STATUS) == 0)
+				break;
+
+			if (c7000_check_csw(devstat) != 0) {
+				c7000_retry_io(cup);
+
+				if (cup->state == C7000_ERROR)
+					wake_up(&cup->wait);
+
+				break;
+			}
+
+			cup->retries = 0;
+			wake_up(&cup->wait);
+			break;
+
+		/*
+			Subchannel is now halted.  Wakeup the sleeping
+			process in c7000_stop.  Set the state to C7000_STOPPED.
+		*/
+
+		case C7000_STOP:
+
+			cup->state = C7000_STOPPED;
+			wake_up(&cup->wait);
+			break;
+
+		/*
+			When in error state, stay there until the
+			interface is recycled.
+		*/
+
+		case C7000_ERROR:
+
+			break;
+
+		/*
+			Should not reach here
+		*/
+
+		default:
+			CPrintk(0, "c7000: c7000_intr: entered default case for unit 0x%x, state %d\n", cup->devno, cup->state);
+			break;
+
+	}
+
+	CPrintk(1, "c7000: c7000_intr: exited with state %d for unit 0x%x\n", cup->state, cup->devno);
+	return;
+}
+
+/*
+	Fill in system validation name padding it with blanks.
+*/
+
+static void
+c7000_fill_name(char *dst, char *src)
+{
+	char	*tmp = dst;
+	int	i;
+
+	for (i = 0; i < NAMLEN; i++, tmp++) 
+		*tmp = ' ';
+
+	for (i = 0; i < NAMLEN && *src != '\0'; i++)
+		*dst++ = *src++;
+
+	return;
+}
+
+/*
+	Initialization routine called when the device is registered.
+*/
+
+static int
+c7000_init(STRUCT_NET_DEVICE *dev)
+{
+	struct	c7000_controller	*ccp;
+	int				i;
+	int				unitaddr;
+	int				irq;
+
+	/*
+		Find the position of base_addr in the bases array.
+	*/
+
+	for (i = 0; i < MAX_C7000; i++) 
+		if (bases[i] == dev->base_addr)
+			break;
+
+	if (i == MAX_C7000)
+		return(-ENODEV);
+
+	/*
+		Make sure it is a C7000 type of device.
+	*/
+
+	if (c7000_check_devices(dev->base_addr) != 0) {
+		CPrintk(0, "c7000: c7000_init: base unit 0x%lx is not the right type\n", dev->base_addr);
+		return(-ENODEV);
+	}
+
+	/*
+		Initialize the device structure functions.
+		Note that ARP is not done on the CLAW interface.
+		There is no ethernet header.
+	*/
+
+	dev->mtu = C7000_DATAL;
+	dev->hard_header_len = 0;
+        dev->addr_len = 0;
+        dev->type = ARPHRD_SLIP;
+        dev->tx_queue_len = C7000_TXQUEUE_LEN;
+	dev->flags = IFF_NOARP;   
+	dev->open = c7000_open;
+	dev->stop = c7000_stop;
+	dev->set_config = c7000_config;
+	dev->hard_start_xmit = c7000_xmit;
+	dev->do_ioctl = c7000_ioctl;
+	dev->get_stats = c7000_stats;
+
+	/*
+		Allocate space for a private data structure.
+	*/
+
+	if ((ccp = dev->priv = kmalloc(sizeof(struct c7000_controller), GFP_KERNEL)) == NULL) {
+		CPrintk(0, "c7000: c7000_init: base unit 0x%lx can not be initialized\n", dev->base_addr);
+		return(-ENOMEM);
+	}
+
+	CPrintk(1, "c7000: c7000_init: allocated a c7000_controller structure at address 0x%x\n", (int)ccp);
+	memset(ccp, '\0', sizeof(struct c7000_controller));
+	ccp->dev = dev;
+	ccp->base_addr = dev->base_addr;
+
+	/*
+		Populate the system validation name with default values.
+	*/
+
+	c7000_fill_name(ccp->lappl, C7000_DFLT_LAPPL);
+	c7000_fill_name(ccp->lhost, C7000_DFLT_LHOST);
+	c7000_fill_name(ccp->uappl, C7000_DFLT_UAPPL);
+	c7000_fill_name(ccp->uhost, C7000_DFLT_UHOST);
+
+	/*
+		When values have been supplied, replace the prior defaults.
+	*/
+
+	if (i == 0) {
+
+		if (lappl0 != NULL)
+			c7000_fill_name(ccp->lappl, lappl0);
+
+		if (lhost0 != NULL)
+			c7000_fill_name(ccp->lhost, lhost0);
+
+		if (uappl0 != NULL)
+			c7000_fill_name(ccp->uappl, uappl0);
+
+		if (uhost0 != NULL)
+			c7000_fill_name(ccp->uhost, uhost0);
+
+	} else if (i == 1) {
+
+		if (lappl1 != NULL)
+			c7000_fill_name(ccp->lappl, lappl1);
+
+		if (lhost1 != NULL)
+			c7000_fill_name(ccp->lhost, lhost1);
+
+		if (uappl1 != NULL)
+			c7000_fill_name(ccp->uappl, uappl1);
+
+		if (uhost1 != NULL)
+			c7000_fill_name(ccp->uhost, uhost1);
+
+	} else if (i == 2) {
+
+		if (lappl2 != NULL)
+			c7000_fill_name(ccp->lappl, lappl2);
+
+		if (lhost2 != NULL)
+			c7000_fill_name(ccp->lhost, lhost2);
+
+		if (uappl2 != NULL)
+			c7000_fill_name(ccp->uappl, uappl2);
+
+		if (uhost2 != NULL)
+			c7000_fill_name(ccp->uhost, uhost2);
+
+	} else {
+
+		if (lappl3 != NULL)
+			c7000_fill_name(ccp->lappl, lappl3);
+
+		if (lhost3 != NULL)
+			c7000_fill_name(ccp->lhost, lhost3);
+
+		if (uappl3 != NULL)
+			c7000_fill_name(ccp->uappl, uappl3);
+
+		if (uhost3 != NULL)
+			c7000_fill_name(ccp->uhost, uhost3);
+
+	}
+
+	CPrintk(1, "c7000: c7000_init: lappl = %8.8s lhost = %8.8s uappl = %8.8s uhost = %8.8s for base unit 0x%x\n", ccp->lappl, ccp->lhost, ccp->uappl, ccp->uhost, ccp->base_addr);
+	ccp->version = 2;
+	ccp->linkid = 0;
+
+	/*
+		Initialize the fields in the embedded cunits
+		array.  This type of controller occupies a range
+		of three contiguous device numbers.
+	*/
+
+	for (i = 0; i < NUNITS; i++) {
+		unitaddr = dev->base_addr + i;
+
+		/*
+			Get the subchannel number.
+		*/
+
+		if ((irq = ccp->cunits[i].irq = get_irq_by_devno(unitaddr)) == -1) {
+			CPrintk(0, "c7000: c7000_init: can not get subchannel for unit 0x%x\n", unitaddr);
+			return(-ENODEV);
+		}
+
+		/*
+			Get control of the subchannel.
+		*/
+
+		if (request_irq(irq, c7000_intr, SA_INTERRUPT, dev->name, &ccp->cunits[i].devstat) != 0) {
+			CPrintk(0, "c7000: c7000_init: can not get control of subchannel 0x%x for unit 0x%x\n", irq, unitaddr);
+			return(-EBUSY);
+		}
+
+		CPrintk(1, "c7000: c7000_init: obtained control of subchannel 0x%x for unit 0x%x\n", irq, unitaddr);
+		ccp->cunits[i].devno = unitaddr;
+		ccp->cunits[i].IO_active = 0;
+		ccp->cunits[i].state = C7000_INIT;
+		ccp->cunits[i].cntlp = ccp;
+		CPrintk(1, "c7000: c7000_init: initialized unit 0x%x on subchannel 0x%x\n", unitaddr, irq);
+	}
+		
+	return(0);
+}
+
+/*
+	Probe for the Cisco 7000 unit base addresses.
+*/
+
+static void
+c7000_probe(void)
+{
+	s390_dev_info_t	d;
+	int		i;
+	int		j;
+	int		idx;
+
+	/*
+		Probe for up to MAX_C7000 devices.
+		Get the first irq into variable idx.
+	*/
+	
+	idx = get_irq_first();
+
+	for (j = 0; j < MAX_C7000; j++) {
+
+		if (idx < 0)
+			break;
+
+		/*
+			Continue scanning the irq's.  Variable idx
+			maintains the location from the prior scan.
+		*/
+
+		for (i = idx; i >= 0; i = get_irq_next(i)) {
+
+			/*
+				Ignore invalid irq's.
+			*/
+
+			if (get_dev_info_by_irq(i, &d) < 0)
+				continue;
+
+			/*
+				A Cisco 7000 is defined as a 3088 model
+				type 0x61.
+			*/
+
+			if (d.sid_data.cu_type == C7000_CU_TYPE &&
+			    d.sid_data.cu_model == C7000_CU_MODEL) {
+				CPrintk(0, "c7000_probe: unit probe found 0x%x\n", d.devno);
+				bases[j] = d.devno;
+
+				/*
+					Skip the write irq and setup idx
+					to probe for the next box.
+				*/
+
+				idx = get_irq_next(i + 1);
+				break;
+			}
+
+		}
+
+	}
+
+	return;
+}
+
+/*
+	Module loading.  Register each C7000 interface found via probing
+	or insmod command parameters.
+*/
+
+int
+init_module(void)
+{
+	int		result;
+	int		i;
+
+	for (i = 0 ; i < MAX_C7000; i++)
+		bases[i] = -1;
+
+	/*
+		Perform automatic detection provided it has not been disabled
+		by the noauto parameter. 
+	*/
+
+	if (noauto == 0)
+		c7000_probe();
+
+	/*
+		Populate bases array from the module basex parameters replacing
+		what probing found above.
+	*/
+
+	if (base0 != -1)
+		bases[0] = base0;
+
+	if (base1 != -1)
+		bases[1] = base1;
+
+	if (base2 != -1)
+		bases[2] = base2;
+
+	if (base3 != -1)
+		bases[3] = base3;
+
+	for (i = 0; i < MAX_C7000; i++) {
+
+		if (bases[i] == -1)
+			continue;
+		
+		/*
+			Initialize the device structure.
+		*/
+
+		memset(&c7000_devices[i], '\0', sizeof(STRUCT_NET_DEVICE));
+#ifdef NEWSTUFF
+		strcpy(c7000_devices[i].name, ifnames[i]);
+#else
+		c7000_devices[i].name = &ifnames[i][0];
+#endif
+		c7000_devices[i].base_addr = bases[i];
+		c7000_devices[i].init = c7000_init;
+
+		/*
+			Register the device.  This creates the interface
+			such as ci0.
+		*/
+
+		if ((result = register_netdev(&c7000_devices[i])) != 0)  {
+			CPrintk(0, "c7000: init_module: error %d registering base unit 0x%x\n",
+				result, bases[i]);
+			c7000_devices[i].base_addr = -1;
+		} else {
+			CPrintk(1, "c7000: init_module: registered base unit 0x%x on interface %s\n", bases[i], ifnames[i]);
+		}
+
+	}
+
+	CPrintk(0, "c7000: init_module: module loaded\n");
+	return(0);
+}
+
+/*
+	Module unloading.  Unregister the interface and free kernel
+	allocated memory.
+*/
+
+void
+cleanup_module(void)
+{
+	int				i;
+	int				j;
+	struct	c7000_controller	*ccp;
+
+	for (i = 0; i < MAX_C7000; i++) {
+
+		if (bases[i] == -1)
+			continue;
+
+		/*
+			If the device was registered, it must be unregistered
+			prior to unloading the module.
+		*/
+
+		if (c7000_devices[i].base_addr != -1) {
+
+			ccp = (struct c7000_controller *) c7000_devices[i].priv;
+
+			if (ccp != NULL) {
+				
+				for (j = 0; j < NUNITS ; j++) {
+					CPrintk(1, "c7000: clean_module: free subchannel 0x%x for unit 0x%x\n", ccp->cunits[j].irq, ccp->cunits[j].devno);
+					free_irq(ccp->cunits[j].irq, &ccp->cunits[j].devstat);
+				}
+
+				CPrintk(1, "c7000: clean_module: free a c7000_controller structure at address 0x%x\n", (int)ccp);
+				kfree(ccp);
+			}
+
+			unregister_netdev(&c7000_devices[i]);
+		}
+
+		bases[i] = -1;
+	}
+
+	CPrintk(0, "c7000: clean_module: module unloaded\n");
+	return;
+}
diff -urN linux-2.4.22-pre3/drivers/sound/esssolo1.c linux-2.4.22-pre4/drivers/sound/esssolo1.c
--- linux-2.4.22-pre3/drivers/sound/esssolo1.c	2003-06-13 07:51:36.000000000 -0700
+++ linux-2.4.22-pre4/drivers/sound/esssolo1.c	2003-07-09 16:56:29.000000000 -0700
@@ -2378,9 +2378,9 @@
 	return 0;
 
  err:
-	unregister_sound_dsp(s->dev_dmfm);
+	unregister_sound_special(s->dev_dmfm);
  err_dev4:
-	unregister_sound_dsp(s->dev_midi);
+	unregister_sound_midi(s->dev_midi);
  err_dev3:
 	unregister_sound_mixer(s->dev_mixer);
  err_dev2:
@@ -2391,13 +2391,13 @@
  err_irq:
 	if (s->gameport.io)
 		release_region(s->gameport.io, GAMEPORT_EXTENT);
-	release_region(s->iobase, IOBASE_EXTENT);
+	release_region(s->mpubase, MPUBASE_EXTENT);
  err_region4:
-	release_region(s->sbbase+FMSYNTH_EXTENT, SBBASE_EXTENT-FMSYNTH_EXTENT);
- err_region3:
 	release_region(s->ddmabase, DDMABASE_EXTENT);
+ err_region3:
+	release_region(s->sbbase+FMSYNTH_EXTENT, SBBASE_EXTENT-FMSYNTH_EXTENT);
  err_region2:
-	release_region(s->mpubase, MPUBASE_EXTENT);
+	release_region(s->iobase, IOBASE_EXTENT);
  err_region1:
 	kfree(s);
 	return ret;
diff -urN linux-2.4.22-pre3/fs/Config.in linux-2.4.22-pre4/fs/Config.in
--- linux-2.4.22-pre3/fs/Config.in	2003-07-09 16:56:25.000000000 -0700
+++ linux-2.4.22-pre4/fs/Config.in	2003-07-09 16:56:29.000000000 -0700
@@ -5,6 +5,8 @@
 comment 'File systems'
 
 bool 'Quota support' CONFIG_QUOTA
+dep_tristate '  VFS v0 quota format support' CONFIG_QFMT_V2 $CONFIG_QUOTA
+
 tristate 'Kernel automounter support' CONFIG_AUTOFS_FS
 tristate 'Kernel automounter version 4 support (also supports v3)' CONFIG_AUTOFS4_FS
 
diff -urN linux-2.4.22-pre3/fs/Makefile linux-2.4.22-pre4/fs/Makefile
--- linux-2.4.22-pre3/fs/Makefile	2003-07-09 16:56:25.000000000 -0700
+++ linux-2.4.22-pre4/fs/Makefile	2003-07-09 16:56:29.000000000 -0700
@@ -7,20 +7,17 @@
 
 O_TARGET := fs.o
 
-export-objs :=	filesystems.o open.o dcache.o buffer.o
+export-objs :=	filesystems.o open.o dcache.o buffer.o dquot.o
 mod-subdirs :=	nls
 
 obj-y :=	open.o read_write.o devices.o file_table.o buffer.o \
 		super.o block_dev.o char_dev.o stat.o exec.o pipe.o namei.o \
 		fcntl.o ioctl.o readdir.o select.o fifo.o locks.o \
 		dcache.o inode.o attr.o bad_inode.o file.o iobuf.o dnotify.o \
-		filesystems.o namespace.o seq_file.o xattr.o
+		filesystems.o namespace.o seq_file.o xattr.o quota.o
 
-ifeq ($(CONFIG_QUOTA),y)
-obj-y += dquot.o
-else
-obj-y += noquot.o
-endif
+obj-$(CONFIG_QUOTA)		+= dquot.o quota_v1.o
+obj-$(CONFIG_QFMT_V2)		+= quota_v2.o
 
 subdir-$(CONFIG_PROC_FS)	+= proc
 subdir-y			+= partitions
diff -urN linux-2.4.22-pre3/fs/bfs/dir.c linux-2.4.22-pre4/fs/bfs/dir.c
--- linux-2.4.22-pre3/fs/bfs/dir.c	2002-02-25 11:38:07.000000000 -0800
+++ linux-2.4.22-pre4/fs/bfs/dir.c	2003-07-09 16:56:29.000000000 -0700
@@ -256,7 +256,6 @@
 	struct buffer_head * bh;
 	struct bfs_dirent * de;
 	int block, sblock, eblock, off;
-	kdev_t dev;
 	int i;
 
 	dprintf("name=%s, namelen=%d\n", name, namelen);
@@ -266,7 +265,6 @@
 	if (namelen > BFS_NAMELEN)
 		return -ENAMETOOLONG;
 
-	dev = dir->i_dev;
 	sblock = dir->iu_sblock;
 	eblock = dir->iu_eblock;
 	for (block=sblock; block<=eblock; block++) {
@@ -308,11 +306,9 @@
 {
 	unsigned long block, offset;
 	struct buffer_head * bh;
-	struct bfs_sb_info * info;
 	struct bfs_dirent * de;
 
 	*res_dir = NULL;
-	info = &dir->i_sb->u.bfs_sb;
 	if (namelen > BFS_NAMELEN)
 		return NULL;
 	bh = NULL;
diff -urN linux-2.4.22-pre3/fs/binfmt_elf.c linux-2.4.22-pre4/fs/binfmt_elf.c
--- linux-2.4.22-pre3/fs/binfmt_elf.c	2002-08-02 17:39:45.000000000 -0700
+++ linux-2.4.22-pre4/fs/binfmt_elf.c	2003-07-09 16:56:29.000000000 -0700
@@ -444,6 +444,7 @@
 	struct elfhdr interp_elf_ex;
   	struct exec interp_ex;
 	char passed_fileno[6];
+	struct files_struct *files, *ftmp;
 	
 	/* Get the exec-header */
 	elf_ex = *((struct elfhdr *) bprm->buf);
@@ -475,10 +476,17 @@
 	retval = kernel_read(bprm->file, elf_ex.e_phoff, (char *) elf_phdata, size);
 	if (retval < 0)
 		goto out_free_ph;
+		
+	files = current->files;		/* Refcounted so ok */
+	if(unshare_files() < 0)
+		goto out_free_ph;
 
+	/* exec will make our files private anyway, but for the a.out
+	   loader stuff we need to do it earlier */
+	   
 	retval = get_unused_fd();
 	if (retval < 0)
-		goto out_free_ph;
+		goto out_free_fh;
 	get_file(bprm->file);
 	fd_install(elf_exec_fileno = retval, bprm->file);
 
@@ -593,6 +601,9 @@
 	if (retval)
 		goto out_free_dentry;
 
+	/* Discard our unneeded old files struct */
+	put_files_struct(files);
+
 	/* OK, This is the point of no return */
 	current->mm->start_data = 0;
 	current->mm->end_data = 0;
@@ -711,7 +722,8 @@
 			printk(KERN_ERR "Unable to load interpreter\n");
 			kfree(elf_phdata);
 			send_sig(SIGSEGV, current, 0);
-			return 0;
+			retval = -ENOEXEC; /* Nobody gets to see this, but.. */
+			goto out;
 		}
 	}
 
@@ -797,6 +809,10 @@
 		kfree(elf_interpreter);
 out_free_file:
 	sys_close(elf_exec_fileno);
+out_free_fh:
+	ftmp = current->files;
+	current->files = files;
+	put_files_struct(ftmp);
 out_free_ph:
 	kfree(elf_phdata);
 	goto out;
diff -urN linux-2.4.22-pre3/fs/binfmt_som.c linux-2.4.22-pre4/fs/binfmt_som.c
--- linux-2.4.22-pre3/fs/binfmt_som.c	2002-11-28 15:53:15.000000000 -0800
+++ linux-2.4.22-pre4/fs/binfmt_som.c	2003-07-09 16:56:29.000000000 -0700
@@ -213,7 +213,7 @@
 			(char *) hpuxhdr, size);
 	if (retval < 0)
 		goto out_free;
-
+#error "Fix security hole before enabling me"
 	retval = get_unused_fd();
 	if (retval < 0)
 		goto out_free;
diff -urN linux-2.4.22-pre3/fs/buffer.c linux-2.4.22-pre4/fs/buffer.c
--- linux-2.4.22-pre3/fs/buffer.c	2003-07-09 16:56:25.000000000 -0700
+++ linux-2.4.22-pre4/fs/buffer.c	2003-07-09 16:56:29.000000000 -0700
@@ -336,7 +336,7 @@
 
 	lock_kernel();
 	sync_inodes_sb(sb);
-	DQUOT_SYNC(dev);
+	DQUOT_SYNC_SB(sb);
 	lock_super(sb);
 	if (sb->s_dirt && sb->s_op && sb->s_op->write_super)
 		sb->s_op->write_super(sb);
@@ -360,7 +360,7 @@
 
 	lock_kernel();
 	sync_inodes(dev);
-	DQUOT_SYNC(dev);
+	DQUOT_SYNC_DEV(dev);
 	sync_supers(dev, 1);
 	unlock_kernel();
 
@@ -2827,7 +2827,7 @@
 		hash_table = (struct buffer_head **)
 		    __get_free_pages(GFP_ATOMIC, order);
 	} while (hash_table == NULL && --order > 0);
-	printk("Buffer-cache hash table entries: %d (order: %d, %ld bytes)\n",
+	printk(KERN_INFO "Buffer cache hash table entries: %d (order: %d, %ld bytes)\n",
 	       nr_hash, order, (PAGE_SIZE << order));
 
 	if (!hash_table)
diff -urN linux-2.4.22-pre3/fs/dquot.c linux-2.4.22-pre4/fs/dquot.c
--- linux-2.4.22-pre3/fs/dquot.c	2002-08-02 17:39:45.000000000 -0700
+++ linux-2.4.22-pre4/fs/dquot.c	2003-07-09 16:56:29.000000000 -0700
@@ -35,7 +35,7 @@
  *		Jan Kara, <jack@suse.cz>, sponsored by SuSE CR, 10-11/99
  *
  *		Used struct list_head instead of own list struct
- *		Invalidation of dquots with dq_count > 0 no longer possible
+ *		Invalidation of referenced dquots is no longer possible
  *		Improved free_dquots list management
  *		Quota and i_blocks are now updated in one place to avoid races
  *		Warnings are now delayed so we won't block in critical section
@@ -45,6 +45,10 @@
  *		Added dynamic quota structure allocation
  *		Jan Kara <jack@suse.cz> 12/2000
  *
+ *		Rewritten quota interface. Implemented new quota format and
+ *		formats registering.
+ *		Jan Kara, <jack@suse.cz>, 2001,2002
+ *
  * (C) Copyright 1994 - 1997 Marco van Wieringen 
  */
 
@@ -59,20 +63,53 @@
 #include <linux/tty.h>
 #include <linux/file.h>
 #include <linux/slab.h>
+#include <linux/sysctl.h>
 #include <linux/smp_lock.h>
 #include <linux/init.h>
+#include <linux/module.h>
+#include <linux/proc_fs.h>
 
 #include <asm/uaccess.h>
 
-#define __DQUOT_VERSION__	"dquot_6.4.0"
+static char *quotatypes[] = INITQFNAMES;
+static struct quota_format_type *quota_formats;	/* List of registered formats */
 
-int nr_dquots, nr_free_dquots;
+int register_quota_format(struct quota_format_type *fmt)
+{
+	lock_kernel();
+	fmt->qf_next = quota_formats;
+	quota_formats = fmt;
+	unlock_kernel();
+	return 0;
+}
 
-static char *quotatypes[] = INITQFNAMES;
+void unregister_quota_format(struct quota_format_type *fmt)
+{
+	struct quota_format_type **actqf;
 
-static inline struct quota_mount_options *sb_dqopt(struct super_block *sb)
+	lock_kernel();
+	for (actqf = &quota_formats; *actqf && *actqf != fmt; actqf = &(*actqf)->qf_next);
+	if (*actqf)
+		*actqf = (*actqf)->qf_next;
+	unlock_kernel();
+}
+
+static struct quota_format_type *find_quota_format(int id)
 {
-	return &sb->s_dquot;
+	struct quota_format_type *actqf;
+
+	lock_kernel();
+	for (actqf = quota_formats; actqf && actqf->qf_fmt_id != id; actqf = actqf->qf_next);
+	if (actqf && !try_inc_mod_count(actqf->qf_owner))
+		actqf = NULL;
+	unlock_kernel();
+	return actqf;
+}
+
+static void put_quota_format(struct quota_format_type *fmt)
+{
+	if (fmt->qf_owner)
+		__MOD_DEC_USE_COUNT(fmt->qf_owner);
 }
 
 /*
@@ -85,11 +122,11 @@
  * list is used for the sync and invalidate operations, which must look
  * at every dquot.
  *
- * Unused dquots (dq_count == 0) are added to the free_dquots list when
- * freed, and this list is searched whenever we need an available dquot.
- * Dquots are removed from the list as soon as they are used again, and
- * nr_free_dquots gives the number of dquots on the list. When dquot is
- * invalidated it's completely released from memory.
+ * Unused dquots (dq_count == 0) are added to the free_dquots list when freed,
+ * and this list is searched whenever we need an available dquot.  Dquots are
+ * removed from the list as soon as they are used again, and
+ * dqstats.free_dquots gives the number of dquots on the list. When
+ * dquot is invalidated it's completely released from memory.
  *
  * Dquots with a specific identity (device, type and id) are placed on
  * one of the dquot_hash[] hash chains. The provides an efficient search
@@ -115,35 +152,37 @@
 static LIST_HEAD(free_dquots);
 static struct list_head dquot_hash[NR_DQHASH];
 
-static struct dqstats dqstats;
-
 static void dqput(struct dquot *);
 static struct dquot *dqduplicate(struct dquot *);
 
-static inline char is_enabled(struct quota_mount_options *dqopt, short type)
+static inline void get_dquot_ref(struct dquot *dquot)
 {
-	switch (type) {
-		case USRQUOTA:
-			return((dqopt->flags & DQUOT_USR_ENABLED) != 0);
-		case GRPQUOTA:
-			return((dqopt->flags & DQUOT_GRP_ENABLED) != 0);
-	}
-	return(0);
+	dquot->dq_count++;
 }
 
-static inline char sb_has_quota_enabled(struct super_block *sb, short type)
+static inline void put_dquot_ref(struct dquot *dquot)
 {
-	return is_enabled(sb_dqopt(sb), type);
+	dquot->dq_count--;
+}
+
+static inline void get_dquot_dup_ref(struct dquot *dquot)
+{
+	dquot->dq_dup_ref++;
 }
 
-static inline int const hashfn(kdev_t dev, unsigned int id, short type)
+static inline void put_dquot_dup_ref(struct dquot *dquot)
 {
-	return((HASHDEV(dev) ^ id) * (MAXQUOTAS - type)) % NR_DQHASH;
+	dquot->dq_dup_ref--;
+}
+
+static inline int const hashfn(struct super_block *sb, unsigned int id, int type)
+{
+	return((HASHDEV(sb->s_dev) ^ id) * (MAXQUOTAS - type)) % NR_DQHASH;
 }
 
 static inline void insert_dquot_hash(struct dquot *dquot)
 {
-	struct list_head *head = dquot_hash + hashfn(dquot->dq_dev, dquot->dq_id, dquot->dq_type);
+	struct list_head *head = dquot_hash + hashfn(dquot->dq_sb, dquot->dq_id, dquot->dq_type);
 	list_add(&dquot->dq_hash, head);
 }
 
@@ -153,14 +192,14 @@
 	INIT_LIST_HEAD(&dquot->dq_hash);
 }
 
-static inline struct dquot *find_dquot(unsigned int hashent, kdev_t dev, unsigned int id, short type)
+static inline struct dquot *find_dquot(unsigned int hashent, struct super_block *sb, unsigned int id, int type)
 {
 	struct list_head *head;
 	struct dquot *dquot;
 
 	for (head = dquot_hash[hashent].next; head != dquot_hash+hashent; head = head->next) {
 		dquot = list_entry(head, struct dquot, dq_hash);
-		if (dquot->dq_dev == dev && dquot->dq_id == id && dquot->dq_type == type)
+		if (dquot->dq_sb == sb && dquot->dq_id == id && dquot->dq_type == type)
 			return dquot;
 	}
 	return NODQUOT;
@@ -170,14 +209,14 @@
 static inline void put_dquot_head(struct dquot *dquot)
 {
 	list_add(&dquot->dq_free, &free_dquots);
-	nr_free_dquots++;
+	dqstats.free_dquots++;
 }
 
 /* Add a dquot to the tail of the free list */
 static inline void put_dquot_last(struct dquot *dquot)
 {
 	list_add(&dquot->dq_free, free_dquots.prev);
-	nr_free_dquots++;
+	dqstats.free_dquots++;
 }
 
 /* Move dquot to the head of free list (it must be already on it) */
@@ -193,7 +232,7 @@
 		return;
 	list_del(&dquot->dq_free);
 	INIT_LIST_HEAD(&dquot->dq_free);
-	nr_free_dquots--;
+	dqstats.free_dquots--;
 }
 
 static inline void put_inuse(struct dquot *dquot)
@@ -201,12 +240,12 @@
 	/* We add to the back of inuse list so we don't have to restart
 	 * when traversing this list and we block */
 	list_add(&dquot->dq_inuse, inuse_list.prev);
-	nr_dquots++;
+	dqstats.allocated_dquots++;
 }
 
 static inline void remove_inuse(struct dquot *dquot)
 {
-	nr_dquots--;
+	dqstats.allocated_dquots--;
 	list_del(&dquot->dq_inuse);
 }
 
@@ -243,6 +282,7 @@
 	wake_up(&dquot->dq_wait_lock);
 }
 
+/* Wait for dquot to be unused */
 static void __wait_dquot_unused(struct dquot *dquot)
 {
 	DECLARE_WAITQUEUE(wait, current);
@@ -258,85 +298,56 @@
 	current->state = TASK_RUNNING;
 }
 
-/*
- *	We don't have to be afraid of deadlocks as we never have quotas on quota files...
- */
-static void write_dquot(struct dquot *dquot)
+/* Wait for all duplicated dquot references to be dropped */
+static void __wait_dup_drop(struct dquot *dquot)
 {
-	short type = dquot->dq_type;
-	struct file *filp;
-	mm_segment_t fs;
-	loff_t offset;
-	ssize_t ret;
-	struct semaphore *sem = &dquot->dq_sb->s_dquot.dqio_sem;
-	struct dqblk dqbuf;
-
-	down(sem);
-	filp = dquot->dq_sb->s_dquot.files[type];
-	offset = dqoff(dquot->dq_id);
-	fs = get_fs();
-	set_fs(KERNEL_DS);
+	DECLARE_WAITQUEUE(wait, current);
 
-	/*
-	 * Note: clear the DQ_MOD flag unconditionally,
-	 * so we don't loop forever on failure.
-	 */
-	memcpy(&dqbuf, &dquot->dq_dqb, sizeof(struct dqblk));
-	dquot->dq_flags &= ~DQ_MOD;
-	ret = 0;
-	if (filp)
-		ret = filp->f_op->write(filp, (char *)&dqbuf, 
-					sizeof(struct dqblk), &offset);
-	if (ret != sizeof(struct dqblk))
-		printk(KERN_WARNING "VFS: dquota write failed on dev %s\n",
-			kdevname(dquot->dq_dev));
-
-	set_fs(fs);
-	up(sem);
-	dqstats.writes++;
-}
-
-static void read_dquot(struct dquot *dquot)
-{
-	short type = dquot->dq_type;
-	struct file *filp;
-	mm_segment_t fs;
-	loff_t offset;
+	add_wait_queue(&dquot->dq_wait_free, &wait);
+repeat:
+	set_current_state(TASK_UNINTERRUPTIBLE);
+	if (dquot->dq_dup_ref) {
+		schedule();
+		goto repeat;
+	}
+	remove_wait_queue(&dquot->dq_wait_free, &wait);
+	current->state = TASK_RUNNING;
+}
 
-	filp = dquot->dq_sb->s_dquot.files[type];
-	if (filp == (struct file *)NULL)
-		return;
+static int read_dqblk(struct dquot *dquot)
+{
+	int ret;
+	struct quota_info *dqopt = sb_dqopt(dquot->dq_sb);
 
 	lock_dquot(dquot);
-	if (!dquot->dq_sb)	/* Invalidated quota? */
-		goto out_lock;
-	/* Now we are sure filp is valid - the dquot isn't invalidated */
-	down(&dquot->dq_sb->s_dquot.dqio_sem);
-	offset = dqoff(dquot->dq_id);
-	fs = get_fs();
-	set_fs(KERNEL_DS);
-	filp->f_op->read(filp, (char *)&dquot->dq_dqb, sizeof(struct dqblk), &offset);
-	up(&dquot->dq_sb->s_dquot.dqio_sem);
-	set_fs(fs);
-
-	if (dquot->dq_bhardlimit == 0 && dquot->dq_bsoftlimit == 0 &&
-	    dquot->dq_ihardlimit == 0 && dquot->dq_isoftlimit == 0)
-		dquot->dq_flags |= DQ_FAKE;
-	dqstats.reads++;
-out_lock:
+	down(&dqopt->dqio_sem);
+	ret = dqopt->ops[dquot->dq_type]->read_dqblk(dquot);
+	up(&dqopt->dqio_sem);
 	unlock_dquot(dquot);
+	return ret;
+}
+
+static int commit_dqblk(struct dquot *dquot)
+{
+	int ret;
+	struct quota_info *dqopt = sb_dqopt(dquot->dq_sb);
+
+	down(&dqopt->dqio_sem);
+	ret = dqopt->ops[dquot->dq_type]->commit_dqblk(dquot);
+	up(&dqopt->dqio_sem);
+	return ret;
 }
 
 /* Invalidate all dquots on the list, wait for all users. Note that this function is called
  * after quota is disabled so no new quota might be created. As we only insert to the end of
  * inuse list, we don't have to restart searching... */
-static void invalidate_dquots(struct super_block *sb, short type)
+static void invalidate_dquots(struct super_block *sb, int type)
 {
 	struct dquot *dquot;
 	struct list_head *head;
 
 restart:
-	for (head = inuse_list.next; head != &inuse_list; head = head->next) {
+	list_for_each(head, &inuse_list) {
 		dquot = list_entry(head, struct dquot, dq_inuse);
 		if (dquot->dq_sb != sb)
 			continue;
@@ -359,37 +370,107 @@
 	}
 }
 
-int sync_dquots(kdev_t dev, short type)
+static int vfs_quota_sync(struct super_block *sb, int type)
 {
 	struct list_head *head;
 	struct dquot *dquot;
+	struct quota_info *dqopt = sb_dqopt(sb);
+	int cnt;
 
-	lock_kernel();
 restart:
-	for (head = inuse_list.next; head != &inuse_list; head = head->next) {
+	list_for_each(head, &inuse_list) {
 		dquot = list_entry(head, struct dquot, dq_inuse);
-		if (dev && dquot->dq_dev != dev)
+		if (sb && dquot->dq_sb != sb)
 			continue;
                 if (type != -1 && dquot->dq_type != type)
 			continue;
 		if (!dquot->dq_sb)	/* Invalidated? */
 			continue;
-		if (!(dquot->dq_flags & (DQ_MOD | DQ_LOCKED)))
+		if (!dquot_dirty(dquot) && !(dquot->dq_flags & DQ_LOCKED))
 			continue;
-		/* Raise use count so quota won't be invalidated. We can't use dqduplicate() as it does too many tests */
-		dquot->dq_count++;
+		/* Get reference to quota so it won't be invalidated. get_dquot_ref()
+		 * is enough since if dquot is locked/modified it can't be
+		 * on the free list */
+		get_dquot_ref(dquot);
 		if (dquot->dq_flags & DQ_LOCKED)
 			wait_on_dquot(dquot);
-		if (dquot->dq_flags & DQ_MOD)
-			write_dquot(dquot);
+		if (dquot_dirty(dquot))
+			commit_dqblk(dquot);
 		dqput(dquot);
 		goto restart;
 	}
+	for (cnt = 0; cnt < MAXQUOTAS; cnt++)
+		if ((cnt == type || type == -1) && sb_has_quota_enabled(sb, cnt))
+			dqopt->info[cnt].dqi_flags &= ~DQF_ANY_DQUOT_DIRTY;
+	for (cnt = 0; cnt < MAXQUOTAS; cnt++)
+		if ((cnt == type || type == -1) && sb_has_quota_enabled(sb, cnt) && info_dirty(&dqopt->info[cnt]))
+			dqopt->ops[cnt]->write_file_info(sb, cnt);
 	dqstats.syncs++;
-	unlock_kernel();
+
 	return 0;
 }
 
+static struct super_block *get_super_to_sync(int type)
+{
+	struct list_head *head;
+	int cnt, dirty;
+
+restart:
+	spin_lock(&sb_lock);
+	list_for_each(head, &super_blocks) {
+		struct super_block *sb = list_entry(head, struct super_block, s_list);
+
+		for (cnt = 0, dirty = 0; cnt < MAXQUOTAS; cnt++)
+			if ((type == cnt || type == -1) && sb_has_quota_enabled(sb, cnt)
+			    && sb_dqopt(sb)->info[cnt].dqi_flags & DQF_ANY_DQUOT_DIRTY)
+				dirty = 1;
+		if (!dirty)
+			continue;
+		sb->s_count++;
+		spin_unlock(&sb_lock);
+		down_read(&sb->s_umount);
+		if (!sb->s_root) {
+			drop_super(sb);
+			goto restart;
+		}
+		return sb;
+	}
+	spin_unlock(&sb_lock);
+	return NULL;
+}
+
+void sync_dquots_dev(kdev_t dev, int type)
+{
+	struct super_block *sb;
+
+	if (dev) {
+		if ((sb = get_super(dev))) {
+			lock_kernel();
+			if (sb->s_qcop->quota_sync)
+				sb->s_qcop->quota_sync(sb, type);
+			unlock_kernel();
+			drop_super(sb);
+		}
+	}
+	else {
+		while ((sb = get_super_to_sync(type))) {
+			lock_kernel();
+			if (sb->s_qcop->quota_sync)
+				sb->s_qcop->quota_sync(sb, type);
+			unlock_kernel();
+			drop_super(sb);
+		}
+	}
+}
+
+void sync_dquots_sb(struct super_block *sb, int type)
+{
+	lock_kernel();
+	if (sb->s_qcop->quota_sync)
+		sb->s_qcop->quota_sync(sb, type);
+	unlock_kernel();
+}
+
 /* Free unused dquots from cache */
 static void prune_dqcache(int count)
 {
@@ -408,19 +489,38 @@
 	}
 }
 
+/*
+ * This is called from kswapd when we think we need some
+ * more memory, but aren't really sure how much. So we
+ * carefully try to free a _bit_ of our dqcache, but not
+ * too much.
+ *
+ * Priority:
+ *   1 - very urgent: shrink everything
+ *   ...
+ *   6 - base-level: try to shrink a bit.
+ */
+
 int shrink_dqcache_memory(int priority, unsigned int gfp_mask)
 {
+	int count = 0;
+
 	lock_kernel();
-	prune_dqcache(nr_free_dquots / (priority + 1));
+	count = dqstats.free_dquots / priority;
+	prune_dqcache(count);
 	unlock_kernel();
 	return kmem_cache_shrink(dquot_cachep);
 }
 
-/* NOTE: If you change this function please check whether dqput_blocks() works right... */
+/*
+ * Put reference to dquot
+ * NOTE: If you change this function please check whether dqput_blocks() works right...
+ */
 static void dqput(struct dquot *dquot)
 {
 	if (!dquot)
 		return;
+#ifdef __DQUOT_PARANOIA
 	if (!dquot->dq_count) {
 		printk("VFS: dqput: trying to free free dquot\n");
 		printk("VFS: device %s, dquot of %s %d\n",
@@ -428,33 +528,38 @@
 			dquot->dq_id);
 		return;
 	}
+#endif
 
 	dqstats.drops++;
 we_slept:
+	if (dquot->dq_dup_ref && dquot->dq_count - dquot->dq_dup_ref <= 1) {	/* Last unduplicated reference? */
+		__wait_dup_drop(dquot);
+		goto we_slept;
+	}
 	if (dquot->dq_count > 1) {
 		/* We have more than one user... We can simply decrement use count */
-		dquot->dq_count--;
+		put_dquot_ref(dquot);
 		return;
 	}
-	if (dquot->dq_flags & DQ_MOD) {
-		write_dquot(dquot);
+	if (dquot_dirty(dquot)) {
+		commit_dqblk(dquot);
 		goto we_slept;
 	}
 
 	/* sanity check */
 	if (!list_empty(&dquot->dq_free)) {
 		printk(KERN_ERR "dqput: dquot already on free list??\n");
-		dquot->dq_count--;	/* J.K. Just decrementing use count seems safer... */
+		put_dquot_ref(dquot);
 		return;
 	}
-	dquot->dq_count--;
+	put_dquot_ref(dquot);
 	/* If dquot is going to be invalidated invalidate_dquots() is going to free it so */
 	if (!(dquot->dq_flags & DQ_INVAL))
 		put_dquot_last(dquot);	/* Place at end of LRU free queue */
 	wake_up(&dquot->dq_wait_free);
 }
 
-static struct dquot *get_empty_dquot(void)
+static struct dquot *get_empty_dquot(struct super_block *sb, int type)
 {
 	struct dquot *dquot;
 
@@ -468,6 +573,9 @@
 	INIT_LIST_HEAD(&dquot->dq_free);
 	INIT_LIST_HEAD(&dquot->dq_inuse);
 	INIT_LIST_HEAD(&dquot->dq_hash);
+	dquot->dq_sb = sb;
+	dquot->dq_dev = sb->s_dev;
+	dquot->dq_type = type;
 	dquot->dq_count = 1;
 	/* all dquots go on the inuse_list */
 	put_inuse(dquot);
@@ -475,11 +583,11 @@
 	return dquot;
 }
 
-static struct dquot *dqget(struct super_block *sb, unsigned int id, short type)
+static struct dquot *dqget(struct super_block *sb, unsigned int id, int type)
 {
-	unsigned int hashent = hashfn(sb->s_dev, id, type);
+	unsigned int hashent = hashfn(sb, id, type);
 	struct dquot *dquot, *empty = NODQUOT;
-	struct quota_mount_options *dqopt = sb_dqopt(sb);
+	struct quota_info *dqopt = sb_dqopt(sb);
 
 we_slept:
         if (!is_enabled(dqopt, type)) {
@@ -488,23 +596,21 @@
                 return NODQUOT;
 	}
 
-	if ((dquot = find_dquot(hashent, sb->s_dev, id, type)) == NODQUOT) {
+	if ((dquot = find_dquot(hashent, sb, id, type)) == NODQUOT) {
 		if (empty == NODQUOT) {
-			if ((empty = get_empty_dquot()) == NODQUOT)
+			if ((empty = get_empty_dquot(sb, type)) == NODQUOT)
 				schedule();	/* Try to wait for a moment... */
 			goto we_slept;
 		}
 		dquot = empty;
-        	dquot->dq_id = id;
-        	dquot->dq_type = type;
-        	dquot->dq_dev = sb->s_dev;
-        	dquot->dq_sb = sb;
+		dquot->dq_id = id;
 		/* hash it first so it can be found */
 		insert_dquot_hash(dquot);
-        	read_dquot(dquot);
+		read_dqblk(dquot);
 	} else {
-		if (!dquot->dq_count++)
+		if (!dquot->dq_count)
 			remove_free_dquot(dquot);
+		get_dquot_ref(dquot);
 		dqstats.cache_hits++;
 		wait_on_dquot(dquot);
 		if (empty)
@@ -516,30 +622,47 @@
 		dqput(dquot);
 		return NODQUOT;
 	}
-	dquot->dq_referenced++;
+	++dquot->dq_referenced;
 	dqstats.lookups++;
 
 	return dquot;
 }
 
+/* Duplicate reference to dquot got from inode */
 static struct dquot *dqduplicate(struct dquot *dquot)
 {
 	if (dquot == NODQUOT)
 		return NODQUOT;
-	dquot->dq_count++;
+	get_dquot_ref(dquot);
 	if (!dquot->dq_sb) {
 		printk(KERN_ERR "VFS: dqduplicate(): Invalidated quota to be duplicated!\n");
-		dquot->dq_count--;
+		put_dquot_ref(dquot);
 		return NODQUOT;
 	}
 	if (dquot->dq_flags & DQ_LOCKED)
 		printk(KERN_ERR "VFS: dqduplicate(): Locked quota to be duplicated!\n");
+	get_dquot_dup_ref(dquot);
 	dquot->dq_referenced++;
 	dqstats.lookups++;
+
 	return dquot;
 }
 
-static int dqinit_needed(struct inode *inode, short type)
+/* Put duplicated reference */
+static void dqputduplicate(struct dquot *dquot)
+{
+	if (!dquot->dq_dup_ref) {
+		printk(KERN_ERR "VFS: dqputduplicate(): Duplicated dquot put without duplicate reference.\n");
+		return;
+	}
+	put_dquot_dup_ref(dquot);
+	if (!dquot->dq_dup_ref)
+		wake_up(&dquot->dq_wait_free);
+	put_dquot_ref(dquot);
+	dqstats.drops++;
+}
+
+static int dqinit_needed(struct inode *inode, int type)
 {
 	int cnt;
 
@@ -553,16 +676,13 @@
 	return 0;
 }
 
-static void add_dquot_ref(struct super_block *sb, short type)
+static void add_dquot_ref(struct super_block *sb, int type)
 {
 	struct list_head *p;
 
-	if (!sb->dq_op)
-		return;	/* nothing to do */
-
 restart:
 	file_list_lock();
-	for (p = sb->s_files.next; p != &sb->s_files; p = p->next) {
+	list_for_each(p, &sb->s_files) {
 		struct file *filp = list_entry(p, struct file, f_list);
 		struct inode *inode = filp->f_dentry->d_inode;
 		if (filp->f_mode & FMODE_WRITE && dqinit_needed(inode, type)) {
@@ -582,13 +702,15 @@
 /* Return 0 if dqput() won't block (note that 1 doesn't necessarily mean blocking) */
 static inline int dqput_blocks(struct dquot *dquot)
 {
-	if (dquot->dq_count == 1)
+	if (dquot->dq_dup_ref && dquot->dq_count - dquot->dq_dup_ref <= 1)
+		return 1;
+	if (dquot->dq_count <= 1 && dquot->dq_flags & DQ_MOD)
 		return 1;
 	return 0;
 }
 
 /* Remove references to dquots from inode - add dquot to list for freeing if needed */
-int remove_inode_dquot_ref(struct inode *inode, short type, struct list_head *tofree_head)
+int remove_inode_dquot_ref(struct inode *inode, int type, struct list_head *tofree_head)
 {
 	struct dquot *dquot = inode->i_dquot[type];
 	int cnt;
@@ -635,38 +757,38 @@
 
 static inline void dquot_incr_inodes(struct dquot *dquot, unsigned long number)
 {
-	dquot->dq_curinodes += number;
-	dquot->dq_flags |= DQ_MOD;
+	dquot->dq_dqb.dqb_curinodes += number;
+	mark_dquot_dirty(dquot);
 }
 
-static inline void dquot_incr_blocks(struct dquot *dquot, unsigned long number)
+static inline void dquot_incr_space(struct dquot *dquot, qsize_t number)
 {
-	dquot->dq_curblocks += number;
-	dquot->dq_flags |= DQ_MOD;
+	dquot->dq_dqb.dqb_curspace += number;
+	mark_dquot_dirty(dquot);
 }
 
 static inline void dquot_decr_inodes(struct dquot *dquot, unsigned long number)
 {
-	if (dquot->dq_curinodes > number)
-		dquot->dq_curinodes -= number;
+	if (dquot->dq_dqb.dqb_curinodes > number)
+		dquot->dq_dqb.dqb_curinodes -= number;
 	else
-		dquot->dq_curinodes = 0;
-	if (dquot->dq_curinodes < dquot->dq_isoftlimit)
-		dquot->dq_itime = (time_t) 0;
+		dquot->dq_dqb.dqb_curinodes = 0;
+	if (dquot->dq_dqb.dqb_curinodes < dquot->dq_dqb.dqb_isoftlimit)
+		dquot->dq_dqb.dqb_itime = (time_t) 0;
 	dquot->dq_flags &= ~DQ_INODES;
-	dquot->dq_flags |= DQ_MOD;
+	mark_dquot_dirty(dquot);
 }
 
-static inline void dquot_decr_blocks(struct dquot *dquot, unsigned long number)
+static inline void dquot_decr_space(struct dquot *dquot, qsize_t number)
 {
-	if (dquot->dq_curblocks > number)
-		dquot->dq_curblocks -= number;
+	if (dquot->dq_dqb.dqb_curspace > number)
+		dquot->dq_dqb.dqb_curspace -= number;
 	else
-		dquot->dq_curblocks = 0;
-	if (dquot->dq_curblocks < dquot->dq_bsoftlimit)
-		dquot->dq_btime = (time_t) 0;
+		dquot->dq_dqb.dqb_curspace = 0;
+	if (toqb(dquot->dq_dqb.dqb_curspace) < dquot->dq_dqb.dqb_bsoftlimit)
+		dquot->dq_dqb.dqb_btime = (time_t) 0;
 	dquot->dq_flags &= ~DQ_BLKS;
-	dquot->dq_flags |= DQ_MOD;
+	mark_dquot_dirty(dquot);
 }
 
 static inline int need_print_warning(struct dquot *dquot, int flag)
@@ -707,22 +829,22 @@
 	tty_write_message(current->tty, quotatypes[dquot->dq_type]);
 	switch (warntype) {
 		case IHARDWARN:
-			msg = " file limit reached.\n";
+			msg = " file limit reached.\r\n";
 			break;
 		case ISOFTLONGWARN:
-			msg = " file quota exceeded too long.\n";
+			msg = " file quota exceeded too long.\r\n";
 			break;
 		case ISOFTWARN:
-			msg = " file quota exceeded.\n";
+			msg = " file quota exceeded.\r\n";
 			break;
 		case BHARDWARN:
-			msg = " block limit reached.\n";
+			msg = " block limit reached.\r\n";
 			break;
 		case BSOFTLONGWARN:
-			msg = " block quota exceeded too long.\n";
+			msg = " block quota exceeded too long.\r\n";
 			break;
 		case BSOFTWARN:
-			msg = " block quota exceeded.\n";
+			msg = " block quota exceeded.\r\n";
 			break;
 	}
 	tty_write_message(current->tty, msg);
@@ -739,7 +861,10 @@
 
 static inline char ignore_hardlimit(struct dquot *dquot)
 {
-	return capable(CAP_SYS_RESOURCE) && !dquot->dq_sb->s_dquot.rsquash[dquot->dq_type];
+	struct mem_dqinfo *info = &sb_dqopt(dquot->dq_sb)->info[dquot->dq_type];
+
+	return capable(CAP_SYS_RESOURCE) &&
+	    (info->dqi_format->qf_fmt_id != QFMT_VFS_OLD || !(info->dqi_flags & V1_DQF_RSQUASH));
 }
 
 static int check_idq(struct dquot *dquot, ulong inodes, char *warntype)
@@ -748,60 +873,60 @@
 	if (inodes <= 0 || dquot->dq_flags & DQ_FAKE)
 		return QUOTA_OK;
 
-	if (dquot->dq_ihardlimit &&
-	   (dquot->dq_curinodes + inodes) > dquot->dq_ihardlimit &&
+	if (dquot->dq_dqb.dqb_ihardlimit &&
+	   (dquot->dq_dqb.dqb_curinodes + inodes) > dquot->dq_dqb.dqb_ihardlimit &&
             !ignore_hardlimit(dquot)) {
 		*warntype = IHARDWARN;
 		return NO_QUOTA;
 	}
 
-	if (dquot->dq_isoftlimit &&
-	   (dquot->dq_curinodes + inodes) > dquot->dq_isoftlimit &&
-	    dquot->dq_itime && CURRENT_TIME >= dquot->dq_itime &&
+	if (dquot->dq_dqb.dqb_isoftlimit &&
+	   (dquot->dq_dqb.dqb_curinodes + inodes) > dquot->dq_dqb.dqb_isoftlimit &&
+	    dquot->dq_dqb.dqb_itime && CURRENT_TIME >= dquot->dq_dqb.dqb_itime &&
             !ignore_hardlimit(dquot)) {
 		*warntype = ISOFTLONGWARN;
 		return NO_QUOTA;
 	}
 
-	if (dquot->dq_isoftlimit &&
-	   (dquot->dq_curinodes + inodes) > dquot->dq_isoftlimit &&
-	    dquot->dq_itime == 0) {
+	if (dquot->dq_dqb.dqb_isoftlimit &&
+	   (dquot->dq_dqb.dqb_curinodes + inodes) > dquot->dq_dqb.dqb_isoftlimit &&
+	    dquot->dq_dqb.dqb_itime == 0) {
 		*warntype = ISOFTWARN;
-		dquot->dq_itime = CURRENT_TIME + dquot->dq_sb->s_dquot.inode_expire[dquot->dq_type];
+		dquot->dq_dqb.dqb_itime = CURRENT_TIME + sb_dqopt(dquot->dq_sb)->info[dquot->dq_type].dqi_igrace;
 	}
 
 	return QUOTA_OK;
 }
 
-static int check_bdq(struct dquot *dquot, ulong blocks, char prealloc, char *warntype)
+static int check_bdq(struct dquot *dquot, qsize_t space, int prealloc, char *warntype)
 {
 	*warntype = 0;
-	if (blocks <= 0 || dquot->dq_flags & DQ_FAKE)
+	if (space <= 0 || dquot->dq_flags & DQ_FAKE)
 		return QUOTA_OK;
 
-	if (dquot->dq_bhardlimit &&
-	   (dquot->dq_curblocks + blocks) > dquot->dq_bhardlimit &&
+	if (dquot->dq_dqb.dqb_bhardlimit &&
+	   toqb(dquot->dq_dqb.dqb_curspace + space) > dquot->dq_dqb.dqb_bhardlimit &&
             !ignore_hardlimit(dquot)) {
 		if (!prealloc)
 			*warntype = BHARDWARN;
 		return NO_QUOTA;
 	}
 
-	if (dquot->dq_bsoftlimit &&
-	   (dquot->dq_curblocks + blocks) > dquot->dq_bsoftlimit &&
-	    dquot->dq_btime && CURRENT_TIME >= dquot->dq_btime &&
+	if (dquot->dq_dqb.dqb_bsoftlimit &&
+	   toqb(dquot->dq_dqb.dqb_curspace + space) > dquot->dq_dqb.dqb_bsoftlimit &&
+	    dquot->dq_dqb.dqb_btime && CURRENT_TIME >= dquot->dq_dqb.dqb_btime &&
             !ignore_hardlimit(dquot)) {
 		if (!prealloc)
 			*warntype = BSOFTLONGWARN;
 		return NO_QUOTA;
 	}
 
-	if (dquot->dq_bsoftlimit &&
-	   (dquot->dq_curblocks + blocks) > dquot->dq_bsoftlimit &&
-	    dquot->dq_btime == 0) {
+	if (dquot->dq_dqb.dqb_bsoftlimit &&
+	   toqb(dquot->dq_dqb.dqb_curspace + space) > dquot->dq_dqb.dqb_bsoftlimit &&
+	    dquot->dq_dqb.dqb_btime == 0) {
 		if (!prealloc) {
 			*warntype = BSOFTWARN;
-			dquot->dq_btime = CURRENT_TIME + dquot->dq_sb->s_dquot.block_expire[dquot->dq_type];
+			dquot->dq_dqb.dqb_btime = CURRENT_TIME + sb_dqopt(dquot->dq_sb)->info[dquot->dq_type].dqi_bgrace;
 		}
 		else
 			/*
@@ -815,148 +940,15 @@
 }
 
 /*
- * Initialize a dquot-struct with new quota info. This is used by the
- * system call interface functions.
- */ 
-static int set_dqblk(struct super_block *sb, int id, short type, int flags, struct dqblk *dqblk)
-{
-	struct dquot *dquot;
-	int error = -EFAULT;
-	struct dqblk dq_dqblk;
-
-	if (copy_from_user(&dq_dqblk, dqblk, sizeof(struct dqblk)))
-		return error;
-
-	if (sb && (dquot = dqget(sb, id, type)) != NODQUOT) {
-		/* We can't block while changing quota structure... */
-		if (id > 0 && ((flags & SET_QUOTA) || (flags & SET_QLIMIT))) {
-			dquot->dq_bhardlimit = dq_dqblk.dqb_bhardlimit;
-			dquot->dq_bsoftlimit = dq_dqblk.dqb_bsoftlimit;
-			dquot->dq_ihardlimit = dq_dqblk.dqb_ihardlimit;
-			dquot->dq_isoftlimit = dq_dqblk.dqb_isoftlimit;
-		}
-
-		if ((flags & SET_QUOTA) || (flags & SET_USE)) {
-			if (dquot->dq_isoftlimit &&
-			    dquot->dq_curinodes < dquot->dq_isoftlimit &&
-			    dq_dqblk.dqb_curinodes >= dquot->dq_isoftlimit)
-				dquot->dq_itime = CURRENT_TIME + dquot->dq_sb->s_dquot.inode_expire[type];
-			dquot->dq_curinodes = dq_dqblk.dqb_curinodes;
-			if (dquot->dq_curinodes < dquot->dq_isoftlimit)
-				dquot->dq_flags &= ~DQ_INODES;
-			if (dquot->dq_bsoftlimit &&
-			    dquot->dq_curblocks < dquot->dq_bsoftlimit &&
-			    dq_dqblk.dqb_curblocks >= dquot->dq_bsoftlimit)
-				dquot->dq_btime = CURRENT_TIME + dquot->dq_sb->s_dquot.block_expire[type];
-			dquot->dq_curblocks = dq_dqblk.dqb_curblocks;
-			if (dquot->dq_curblocks < dquot->dq_bsoftlimit)
-				dquot->dq_flags &= ~DQ_BLKS;
-		}
-
-		if (id == 0) {
-			dquot->dq_sb->s_dquot.block_expire[type] = dquot->dq_btime = dq_dqblk.dqb_btime;
-			dquot->dq_sb->s_dquot.inode_expire[type] = dquot->dq_itime = dq_dqblk.dqb_itime;
-		}
-
-		if (dq_dqblk.dqb_bhardlimit == 0 && dq_dqblk.dqb_bsoftlimit == 0 &&
-		    dq_dqblk.dqb_ihardlimit == 0 && dq_dqblk.dqb_isoftlimit == 0)
-			dquot->dq_flags |= DQ_FAKE;
-		else
-			dquot->dq_flags &= ~DQ_FAKE;
-
-		dquot->dq_flags |= DQ_MOD;
-		dqput(dquot);
-	}
-	return 0;
-}
-
-static int get_quota(struct super_block *sb, int id, short type, struct dqblk *dqblk)
-{
-	struct dquot *dquot;
-	struct dqblk data;
-	int error = -ESRCH;
-
-	if (!sb || !sb_has_quota_enabled(sb, type))
-		goto out;
-	dquot = dqget(sb, id, type);
-	if (dquot == NODQUOT)
-		goto out;
-
-	memcpy(&data, &dquot->dq_dqb, sizeof(struct dqblk));        /* We copy data to preserve them from changing */
-	dqput(dquot);
-	error = -EFAULT;
-	if (dqblk && !copy_to_user(dqblk, &data, sizeof(struct dqblk)))
-		error = 0;
-out:
-	return error;
-}
-
-static int get_stats(caddr_t addr)
-{
-	int error = -EFAULT;
-	struct dqstats stats;
-
-	dqstats.allocated_dquots = nr_dquots;
-	dqstats.free_dquots = nr_free_dquots;
-
-	/* make a copy, in case we page-fault in user space */
-	memcpy(&stats, &dqstats, sizeof(struct dqstats));
-	if (!copy_to_user(addr, &stats, sizeof(struct dqstats)))
-		error = 0;
-	return error;
-}
-
-static int quota_root_squash(struct super_block *sb, short type, int *addr)
-{
-	int new_value, error;
-
-	if (!sb)
-		return(-ENODEV);
-
-	error = -EFAULT;
-	if (!copy_from_user(&new_value, addr, sizeof(int))) {
-		sb_dqopt(sb)->rsquash[type] = new_value;
-		error = 0;
-	}
-	return error;
-}
-
-#if 0	/* We are not going to support filesystems without i_blocks... */
-/*
- * This is a simple algorithm that calculates the size of a file in blocks.
- * This is only used on filesystems that do not have an i_blocks count.
- */
-static u_long isize_to_blocks(loff_t isize, size_t blksize_bits)
-{
-	u_long blocks;
-	u_long indirect;
-
-	if (!blksize_bits)
-		blksize_bits = BLOCK_SIZE_BITS;
-	blocks = (isize >> blksize_bits) + ((isize & ~((1 << blksize_bits)-1)) ? 1 : 0);
-	if (blocks > 10) {
-		indirect = ((blocks - 11) >> 8) + 1; /* single indirect blocks */
-		if (blocks > (10 + 256)) {
-			indirect += ((blocks - 267) >> 16) + 1; /* double indirect blocks */
-			if (blocks > (10 + 256 + (256 << 8)))
-				indirect++; /* triple indirect blocks */
-		}
-		blocks += indirect;
-	}
-	return blocks;
-}
-#endif
-
-/*
  * Externally referenced functions through dquot_operations in inode.
  *
  * Note: this is a blocking operation.
  */
-void dquot_initialize(struct inode *inode, short type)
+void dquot_initialize(struct inode *inode, int type)
 {
 	struct dquot *dquot[MAXQUOTAS];
 	unsigned int id = 0;
-	short cnt;
+	int cnt;
 
 	if (IS_NOQUOTA(inode))
 		return;
@@ -1002,7 +994,7 @@
 void dquot_drop(struct inode *inode)
 {
 	struct dquot *dquot;
-	short cnt;
+	int cnt;
 
 	inode->i_flags &= ~S_QUOTA;
 	for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
@@ -1017,7 +1009,7 @@
 /*
  * This operation can block, but only after everything is updated
  */
-int dquot_alloc_block(struct inode *inode, unsigned long number, char warn)
+int dquot_alloc_space(struct inode *inode, qsize_t number, int warn)
 {
 	int cnt, ret = NO_QUOTA;
 	struct dquot *dquot[MAXQUOTAS];
@@ -1038,16 +1030,16 @@
 	for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
 		if (dquot[cnt] == NODQUOT)
 			continue;
-		dquot_incr_blocks(dquot[cnt], number);
+		dquot_incr_space(dquot[cnt], number);
 	}
-	inode->i_blocks += number << (BLOCK_SIZE_BITS - 9);
+	inode_add_bytes(inode, number);
 	/* NOBLOCK End */
 	ret = QUOTA_OK;
 warn_put_all:
 	flush_warnings(dquot, warntype);
 	for (cnt = 0; cnt < MAXQUOTAS; cnt++)
 		if (dquot[cnt] != NODQUOT)
-			dqput(dquot[cnt]);
+			dqputduplicate(dquot[cnt]);
 	return ret;
 }
 
@@ -1084,16 +1076,16 @@
 	flush_warnings(dquot, warntype);
 	for (cnt = 0; cnt < MAXQUOTAS; cnt++)
 		if (dquot[cnt] != NODQUOT)
-			dqput(dquot[cnt]);
+			dqputduplicate(dquot[cnt]);
 	return ret;
 }
 
 /*
  * This is a non-blocking operation.
  */
-void dquot_free_block(struct inode *inode, unsigned long number)
+void dquot_free_space(struct inode *inode, qsize_t number)
 {
-	unsigned short cnt;
+	unsigned int cnt;
 	struct dquot *dquot;
 
 	/* NOBLOCK Start */
@@ -1101,10 +1093,10 @@
 		dquot = dqduplicate(inode->i_dquot[cnt]);
 		if (dquot == NODQUOT)
 			continue;
-		dquot_decr_blocks(dquot, number);
-		dqput(dquot);
+		dquot_decr_space(dquot, number);
+		dqputduplicate(dquot);
 	}
-	inode->i_blocks -= number << (BLOCK_SIZE_BITS - 9);
+	inode_sub_bytes(inode, number);
 	/* NOBLOCK End */
 }
 
@@ -1113,7 +1105,7 @@
  */
 void dquot_free_inode(const struct inode *inode, unsigned long number)
 {
-	unsigned short cnt;
+	unsigned int cnt;
 	struct dquot *dquot;
 
 	/* NOBLOCK Start */
@@ -1122,7 +1114,7 @@
 		if (dquot == NODQUOT)
 			continue;
 		dquot_decr_inodes(dquot, number);
-		dqput(dquot);
+		dqputduplicate(dquot);
 	}
 	/* NOBLOCK End */
 }
@@ -1134,7 +1126,7 @@
  */
 int dquot_transfer(struct inode *inode, struct iattr *iattr)
 {
-	unsigned long blocks;
+	qsize_t space;
 	struct dquot *transfer_from[MAXQUOTAS];
 	struct dquot *transfer_to[MAXQUOTAS];
 	int cnt, ret = NO_QUOTA, chuid = (iattr->ia_valid & ATTR_UID) && inode->i_uid != iattr->ia_uid,
@@ -1164,7 +1156,7 @@
 		}
 	}
 	/* NOBLOCK START: From now on we shouldn't block */
-	blocks = (inode->i_blocks >> 1);
+	space = inode_get_bytes(inode);
 	/* Build the transfer_from list and check the limits */
 	for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
 		/* The second test can fail when quotaoff is in progress... */
@@ -1174,7 +1166,7 @@
 		if (transfer_from[cnt] == NODQUOT)	/* Can happen on quotafiles (quota isn't initialized on them)... */
 			continue;
 		if (check_idq(transfer_to[cnt], 1, warntype+cnt) == NO_QUOTA ||
-		    check_bdq(transfer_to[cnt], blocks, 0, warntype+cnt) == NO_QUOTA)
+		    check_bdq(transfer_to[cnt], space, 0, warntype+cnt) == NO_QUOTA)
 			goto warn_put_all;
 	}
 
@@ -1189,10 +1181,10 @@
 			continue;
 
 		dquot_decr_inodes(transfer_from[cnt], 1);
-		dquot_decr_blocks(transfer_from[cnt], blocks);
+		dquot_decr_space(transfer_from[cnt], space);
 
 		dquot_incr_inodes(transfer_to[cnt], 1);
-		dquot_incr_blocks(transfer_to[cnt], blocks);
+		dquot_incr_space(transfer_to[cnt], space);
 
 		if (inode->i_dquot[cnt] == NODQUOT)
 			BUG();
@@ -1208,39 +1200,29 @@
 warn_put_all:
 	flush_warnings(transfer_to, warntype);
 	for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
+		/* First we must put duplicate - otherwise we might deadlock */
 		if (transfer_to[cnt] != NODQUOT)
-			dqput(transfer_to[cnt]);
+			dqputduplicate(transfer_to[cnt]);
 		if (transfer_from[cnt] != NODQUOT)
 			dqput(transfer_from[cnt]);
 	}
 	return ret;
 }
 
-static int __init dquot_init(void)
-{
-	int i;
-
-	for (i = 0; i < NR_DQHASH; i++)
-		INIT_LIST_HEAD(dquot_hash + i);
-	printk(KERN_NOTICE "VFS: Diskquotas version %s initialized\n", __DQUOT_VERSION__);
-	return 0;
-}
-__initcall(dquot_init);
-
 /*
  * Definitions of diskquota operations.
  */
 struct dquot_operations dquot_operations = {
-	dquot_initialize,		/* mandatory */
-	dquot_drop,			/* mandatory */
-	dquot_alloc_block,
-	dquot_alloc_inode,
-	dquot_free_block,
-	dquot_free_inode,
-	dquot_transfer
+	initialize:	dquot_initialize,		/* mandatory */
+	drop:		dquot_drop,			/* mandatory */
+	alloc_space:	dquot_alloc_space,
+	alloc_inode:	dquot_alloc_inode,
+	free_space:	dquot_free_space,
+	free_inode:	dquot_free_inode,
+	transfer:	dquot_transfer
 };
 
-static inline void set_enable_flags(struct quota_mount_options *dqopt, short type)
+static inline void set_enable_flags(struct quota_info *dqopt, int type)
 {
 	switch (type) {
 		case USRQUOTA:
@@ -1252,7 +1234,7 @@
 	}
 }
 
-static inline void reset_enable_flags(struct quota_mount_options *dqopt, short type)
+static inline void reset_enable_flags(struct quota_info *dqopt, int type)
 {
 	switch (type) {
 		case USRQUOTA:
@@ -1265,16 +1247,15 @@
 }
 
 /* Function in inode.c - remove pointers to dquots in icache */
-extern void remove_dquot_ref(struct super_block *, short);
+extern void remove_dquot_ref(struct super_block *, int);
 
 /*
  * Turn quota off on a device. type == -1 ==> quotaoff for all types (umount)
  */
-int quota_off(struct super_block *sb, short type)
+int vfs_quota_off(struct super_block *sb, int type)
 {
-	struct file *filp;
-	short cnt;
-	struct quota_mount_options *dqopt = sb_dqopt(sb);
+	int cnt;
+	struct quota_info *dqopt = sb_dqopt(sb);
 
 	lock_kernel();
 	if (!sb)
@@ -1292,51 +1273,48 @@
 		/* Note: these are blocking operations */
 		remove_dquot_ref(sb, cnt);
 		invalidate_dquots(sb, cnt);
+		if (info_dirty(&dqopt->info[cnt]))
+			dqopt->ops[cnt]->write_file_info(sb, cnt);
+		if (dqopt->ops[cnt]->free_file_info)
+			dqopt->ops[cnt]->free_file_info(sb, cnt);
+		put_quota_format(dqopt->info[cnt].dqi_format);
 
-		filp = dqopt->files[cnt];
+		fput(dqopt->files[cnt]);
 		dqopt->files[cnt] = (struct file *)NULL;
-		dqopt->inode_expire[cnt] = 0;
-		dqopt->block_expire[cnt] = 0;
-		fput(filp);
-	}	
+		dqopt->info[cnt].dqi_flags = 0;
+		dqopt->info[cnt].dqi_igrace = 0;
+		dqopt->info[cnt].dqi_bgrace = 0;
+		dqopt->ops[cnt] = NULL;
+	}
 	up(&dqopt->dqoff_sem);
 out:
 	unlock_kernel();
 	return 0;
 }
 
-static inline int check_quotafile_size(loff_t size)
-{
-	ulong blocks = size >> BLOCK_SIZE_BITS;
-	size_t off = size & (BLOCK_SIZE - 1);
-
-	return !(((blocks % sizeof(struct dqblk)) * BLOCK_SIZE + off % sizeof(struct dqblk)) % sizeof(struct dqblk));
-}
-
-static int quota_on(struct super_block *sb, short type, char *path)
+int vfs_quota_on(struct super_block *sb, int type, int format_id, char *path)
 {
-	struct file *f;
+	struct file *f = NULL;
 	struct inode *inode;
-	struct dquot *dquot;
-	struct quota_mount_options *dqopt = sb_dqopt(sb);
-	char *tmp;
+	struct quota_info *dqopt = sb_dqopt(sb);
+	struct quota_format_type *fmt = find_quota_format(format_id);
 	int error;
 
-	if (is_enabled(dqopt, type))
-		return -EBUSY;
+	if (!fmt)
+		return -ESRCH;
+	if (is_enabled(dqopt, type)) {
+		error = -EBUSY;
+		goto out_fmt;
+	}
 
 	down(&dqopt->dqoff_sem);
-	tmp = getname(path);
-	error = PTR_ERR(tmp);
-	if (IS_ERR(tmp))
-		goto out_lock;
 
-	f = filp_open(tmp, O_RDWR, 0600);
-	putname(tmp);
+	f = filp_open(path, O_RDWR, 0600);
 
 	error = PTR_ERR(f);
 	if (IS_ERR(f))
 		goto out_lock;
+	dqopt->files[type] = f;
 	error = -EIO;
 	if (!f->f_op || !f->f_op->read || !f->f_op->write)
 		goto out_f;
@@ -1345,134 +1323,197 @@
 	if (!S_ISREG(inode->i_mode))
 		goto out_f;
 	error = -EINVAL;
-	if (inode->i_size == 0 || !check_quotafile_size(inode->i_size))
+	if (!fmt->qf_ops->check_quota_file(sb, type))
 		goto out_f;
 	/* We don't want quota on quota files */
 	dquot_drop(inode);
 	inode->i_flags |= S_NOQUOTA;
 
-	dqopt->files[type] = f;
-	sb->dq_op = &dquot_operations;
+	dqopt->ops[type] = fmt->qf_ops;
+	dqopt->info[type].dqi_format = fmt;
+	if ((error = dqopt->ops[type]->read_file_info(sb, type)) < 0)
+		goto out_f;
 	set_enable_flags(dqopt, type);
 
-	dquot = dqget(sb, 0, type);
-	dqopt->inode_expire[type] = (dquot != NODQUOT) ? dquot->dq_itime : MAX_IQ_TIME;
-	dqopt->block_expire[type] = (dquot != NODQUOT) ? dquot->dq_btime : MAX_DQ_TIME;
-	dqput(dquot);
-
 	add_dquot_ref(sb, type);
 
 	up(&dqopt->dqoff_sem);
 	return 0;
 
 out_f:
-	filp_close(f, NULL);
+	if (f)
+		filp_close(f, NULL);
+	dqopt->files[type] = NULL;
 out_lock:
 	up(&dqopt->dqoff_sem);
+out_fmt:
+	put_quota_format(fmt);
 
 	return error; 
 }
 
-/*
- * This is the system call interface. This communicates with
- * the user-level programs. Currently this only supports diskquota
- * calls. Maybe we need to add the process quotas etc. in the future,
- * but we probably should use rlimits for that.
- */
-asmlinkage long sys_quotactl(int cmd, const char *special, int id, caddr_t addr)
+/* Generic routine for getting common part of quota structure */
+static void do_get_dqblk(struct dquot *dquot, struct if_dqblk *di)
 {
-	int cmds = 0, type = 0, flags = 0;
-	kdev_t dev;
-	struct super_block *sb = NULL;
-	int ret = -EINVAL;
+	struct mem_dqblk *dm = &dquot->dq_dqb;
 
-	lock_kernel();
-	cmds = cmd >> SUBCMDSHIFT;
-	type = cmd & SUBCMDMASK;
+	di->dqb_bhardlimit = dm->dqb_bhardlimit;
+	di->dqb_bsoftlimit = dm->dqb_bsoftlimit;
+	di->dqb_curspace = dm->dqb_curspace;
+	di->dqb_ihardlimit = dm->dqb_ihardlimit;
+	di->dqb_isoftlimit = dm->dqb_isoftlimit;
+	di->dqb_curinodes = dm->dqb_curinodes;
+	di->dqb_btime = dm->dqb_btime;
+	di->dqb_itime = dm->dqb_itime;
+	di->dqb_valid = QIF_ALL;
+}
 
-	if ((u_int) type >= MAXQUOTAS)
-		goto out;
-	if (id & ~0xFFFF)
-		goto out;
+int vfs_get_dqblk(struct super_block *sb, int type, qid_t id, struct if_dqblk *di)
+{
+	struct dquot *dquot = dqget(sb, id, type);
 
-	ret = -EPERM;
-	switch (cmds) {
-		case Q_SYNC:
-		case Q_GETSTATS:
-			break;
-		case Q_GETQUOTA:
-			if (((type == USRQUOTA && current->euid != id) ||
-			     (type == GRPQUOTA && !in_egroup_p(id))) &&
-			    !capable(CAP_SYS_ADMIN))
-				goto out;
-			break;
-		default:
-			if (!capable(CAP_SYS_ADMIN))
-				goto out;
-	}
-
-	ret = -EINVAL;
-	dev = NODEV;
-	if (special != NULL || (cmds != Q_SYNC && cmds != Q_GETSTATS)) {
-		mode_t mode;
-		struct nameidata nd;
-
-		ret = user_path_walk(special, &nd);
-		if (ret)
-			goto out;
-
-		dev = nd.dentry->d_inode->i_rdev;
-		mode = nd.dentry->d_inode->i_mode;
-		path_release(&nd);
-
-		ret = -ENOTBLK;
-		if (!S_ISBLK(mode))
-			goto out;
-		ret = -ENODEV;
-		sb = get_super(dev);
-		if (!sb)
-			goto out;
-	}
-
-	ret = -EINVAL;
-	switch (cmds) {
-		case Q_QUOTAON:
-			ret = quota_on(sb, type, (char *) addr);
-			goto out;
-		case Q_QUOTAOFF:
-			ret = quota_off(sb, type);
-			goto out;
-		case Q_GETQUOTA:
-			ret = get_quota(sb, id, type, (struct dqblk *) addr);
-			goto out;
-		case Q_SETQUOTA:
-			flags |= SET_QUOTA;
-			break;
-		case Q_SETUSE:
-			flags |= SET_USE;
-			break;
-		case Q_SETQLIM:
-			flags |= SET_QLIMIT;
-			break;
-		case Q_SYNC:
-			ret = sync_dquots(dev, type);
-			goto out;
-		case Q_GETSTATS:
-			ret = get_stats(addr);
-			goto out;
-		case Q_RSQUASH:
-			ret = quota_root_squash(sb, type, (int *) addr);
-			goto out;
-		default:
-			goto out;
-	}
-
-	ret = -NODEV;
-	if (sb && sb_has_quota_enabled(sb, type))
-		ret = set_dqblk(sb, id, type, flags, (struct dqblk *) addr);
-out:
-	if (sb)
-		drop_super(sb);
-	unlock_kernel();
-	return ret;
+	if (!dquot)
+		return -EINVAL;
+	do_get_dqblk(dquot, di);
+	dqput(dquot);
+	return 0;
+}
+
+/* Generic routine for setting common part of quota structure */
+static void do_set_dqblk(struct dquot *dquot, struct if_dqblk *di)
+{
+	struct mem_dqblk *dm = &dquot->dq_dqb;
+	int check_blim = 0, check_ilim = 0;
+
+	if (di->dqb_valid & QIF_SPACE) {
+		dm->dqb_curspace = di->dqb_curspace;
+		check_blim = 1;
+	}
+	if (di->dqb_valid & QIF_BLIMITS) {
+		dm->dqb_bsoftlimit = di->dqb_bsoftlimit;
+		dm->dqb_bhardlimit = di->dqb_bhardlimit;
+		check_blim = 1;
+	}
+	if (di->dqb_valid & QIF_INODES) {
+		dm->dqb_curinodes = di->dqb_curinodes;
+		check_ilim = 1;
+	}
+	if (di->dqb_valid & QIF_ILIMITS) {
+		dm->dqb_isoftlimit = di->dqb_isoftlimit;
+		dm->dqb_ihardlimit = di->dqb_ihardlimit;
+		check_ilim = 1;
+	}
+	if (di->dqb_valid & QIF_BTIME)
+		dm->dqb_btime = di->dqb_btime;
+	if (di->dqb_valid & QIF_ITIME)
+		dm->dqb_itime = di->dqb_itime;
+
+	if (check_blim) {
+		if (!dm->dqb_bsoftlimit || toqb(dm->dqb_curspace) < dm->dqb_bsoftlimit) {
+			dm->dqb_btime = 0;
+			dquot->dq_flags &= ~DQ_BLKS;
+		}
+		else if (!(di->dqb_valid & QIF_BTIME))	/* Set grace only if user hasn't provided his own... */
+			dm->dqb_btime = CURRENT_TIME + sb_dqopt(dquot->dq_sb)->info[dquot->dq_type].dqi_bgrace;
+	}
+	if (check_ilim) {
+		if (!dm->dqb_isoftlimit || dm->dqb_curinodes < dm->dqb_isoftlimit) {
+			dm->dqb_itime = 0;
+			dquot->dq_flags &= ~DQ_INODES;
+		}
+		else if (!(di->dqb_valid & QIF_ITIME))	/* Set grace only if user hasn't provided his own... */
+			dm->dqb_itime = CURRENT_TIME + sb_dqopt(dquot->dq_sb)->info[dquot->dq_type].dqi_igrace;
+	}
+	if (dm->dqb_bhardlimit || dm->dqb_bsoftlimit || dm->dqb_ihardlimit || dm->dqb_isoftlimit)
+		dquot->dq_flags &= ~DQ_FAKE;
+	else
+		dquot->dq_flags |= DQ_FAKE;
+	dquot->dq_flags |= DQ_MOD;
+}
+
+int vfs_set_dqblk(struct super_block *sb, int type, qid_t id, struct if_dqblk *di)
+{
+	struct dquot *dquot = dqget(sb, id, type);
+
+	if (!dquot)
+		return -EINVAL;
+	do_set_dqblk(dquot, di);
+	dqput(dquot);
+	return 0;
+}
+
+/* Generic routine for getting common part of quota file information */
+int vfs_get_dqinfo(struct super_block *sb, int type, struct if_dqinfo *ii)
+{
+	struct mem_dqinfo *mi = sb_dqopt(sb)->info + type;
+
+	ii->dqi_bgrace = mi->dqi_bgrace;
+	ii->dqi_igrace = mi->dqi_igrace;
+	ii->dqi_flags = mi->dqi_flags & DQF_MASK;
+	ii->dqi_valid = IIF_ALL;
+	return 0;
+}
+
+/* Generic routine for setting common part of quota file information */
+int vfs_set_dqinfo(struct super_block *sb, int type, struct if_dqinfo *ii)
+{
+	struct mem_dqinfo *mi = sb_dqopt(sb)->info + type;
+
+	if (ii->dqi_valid & IIF_BGRACE)
+		mi->dqi_bgrace = ii->dqi_bgrace;
+	if (ii->dqi_valid & IIF_IGRACE)
+		mi->dqi_igrace = ii->dqi_igrace;
+	if (ii->dqi_valid & IIF_FLAGS)
+		mi->dqi_flags = (mi->dqi_flags & ~DQF_MASK) | (ii->dqi_flags & DQF_MASK);
+	mark_info_dirty(mi);
+	return 0;
+}
+
+struct quotactl_ops vfs_quotactl_ops = {
+	quota_on:	vfs_quota_on,
+	quota_off:	vfs_quota_off,
+	quota_sync:	vfs_quota_sync,
+	get_info:	vfs_get_dqinfo,
+	set_info:	vfs_set_dqinfo,
+	get_dqblk:	vfs_get_dqblk,
+	set_dqblk:	vfs_set_dqblk
+};
+
+static ctl_table fs_dqstats_table[] = {
+	{FS_DQ_LOOKUPS, "lookups", &dqstats.lookups, sizeof(int), 0444, NULL, &proc_dointvec},
+	{FS_DQ_DROPS, "drops", &dqstats.drops, sizeof(int), 0444, NULL, &proc_dointvec},
+	{FS_DQ_READS, "reads", &dqstats.reads, sizeof(int), 0444, NULL, &proc_dointvec},
+	{FS_DQ_WRITES, "writes", &dqstats.writes, sizeof(int), 0444, NULL, &proc_dointvec},
+	{FS_DQ_CACHE_HITS, "cache_hits", &dqstats.cache_hits, sizeof(int), 0444, NULL, &proc_dointvec},
+	{FS_DQ_ALLOCATED, "allocated_dquots", &dqstats.allocated_dquots, sizeof(int), 0444, NULL, &proc_dointvec},
+	{FS_DQ_FREE, "free_dquots", &dqstats.free_dquots, sizeof(int), 0444, NULL, &proc_dointvec},
+	{FS_DQ_SYNCS, "syncs", &dqstats.syncs, sizeof(int), 0444, NULL, &proc_dointvec},
+	{},
+};
+
+static ctl_table fs_table[] = {
+	{FS_DQSTATS, "quota", NULL, 0, 0555, fs_dqstats_table},
+	{},
+};
+
+static ctl_table sys_table[] = {
+	{CTL_FS, "fs", NULL, 0, 0555, fs_table},
+	{},
+};
+
+static int __init dquot_init(void)
+{
+	int i;
+
+	register_sysctl_table(sys_table, 0);
+	for (i = 0; i < NR_DQHASH; i++)
+		INIT_LIST_HEAD(dquot_hash + i);
+	printk(KERN_NOTICE "VFS: Disk quotas v%s\n", __DQUOT_VERSION__);
+
+	return 0;
 }
+__initcall(dquot_init);
+
+EXPORT_SYMBOL(register_quota_format);
+EXPORT_SYMBOL(unregister_quota_format);
+EXPORT_SYMBOL(dqstats);
diff -urN linux-2.4.22-pre3/fs/exec.c linux-2.4.22-pre4/fs/exec.c
--- linux-2.4.22-pre3/fs/exec.c	2003-07-09 16:56:25.000000000 -0700
+++ linux-2.4.22-pre4/fs/exec.c	2003-07-09 16:56:29.000000000 -0700
@@ -563,6 +563,7 @@
 	char * name;
 	int i, ch, retval;
 	struct signal_struct * oldsig;
+	struct files_struct * files;
 
 	/*
 	 * Make sure we have a private signal table
@@ -571,6 +572,18 @@
 	retval = make_private_signals();
 	if (retval) goto flush_failed;
 
+	/*
+	 * Make sure we have private file handles. Ask the
+	 * fork helper to do the work for us and the exit
+	 * helper to do the cleanup of the old one.
+	 */
+	 
+	files = current->files;		/* refcounted so safe to hold */
+	retval = unshare_files();
+	if(retval)
+		goto flush_failed;
+	put_files_struct(files);
+	
 	/* 
 	 * Release all of the old mmap stuff
 	 */
diff -urN linux-2.4.22-pre3/fs/ext3/inode.c linux-2.4.22-pre4/fs/ext3/inode.c
--- linux-2.4.22-pre3/fs/ext3/inode.c	2003-06-13 07:51:37.000000000 -0700
+++ linux-2.4.22-pre4/fs/ext3/inode.c	2003-07-09 16:56:29.000000000 -0700
@@ -1413,7 +1413,7 @@
 	length = blocksize - length;
 	iblock = index << (PAGE_CACHE_SHIFT - inode->i_sb->s_blocksize_bits);
 
-	page = grab_cache_page(mapping, index);
+	page = find_or_create_page(mapping, index, GFP_NOFS);
 	err = -ENOMEM;
 	if (!page)
 		goto out;
diff -urN linux-2.4.22-pre3/fs/inode.c linux-2.4.22-pre4/fs/inode.c
--- linux-2.4.22-pre3/fs/inode.c	2003-07-09 16:56:25.000000000 -0700
+++ linux-2.4.22-pre4/fs/inode.c	2003-07-09 16:56:29.000000000 -0700
@@ -106,6 +106,7 @@
 		atomic_set(&inode->i_writecount, 0);
 		inode->i_size = 0;
 		inode->i_blocks = 0;
+		inode->i_bytes = 0;
 		inode->i_generation = 0;
 		memset(&inode->i_dquot, 0, sizeof(inode->i_dquot));
 		inode->i_pipe = NULL;
diff -urN linux-2.4.22-pre3/fs/ioctl.c linux-2.4.22-pre4/fs/ioctl.c
--- linux-2.4.22-pre3/fs/ioctl.c	2001-02-09 11:29:44.000000000 -0800
+++ linux-2.4.22-pre4/fs/ioctl.c	2003-07-09 16:56:29.000000000 -0700
@@ -101,6 +101,16 @@
 				filp->f_flags &= ~FASYNC;
 			break;
 
+		case FIOQSIZE:
+			if (S_ISDIR(filp->f_dentry->d_inode->i_mode) ||
+			    S_ISREG(filp->f_dentry->d_inode->i_mode) ||
+			    S_ISLNK(filp->f_dentry->d_inode->i_mode)) {
+				loff_t res = inode_get_bytes(filp->f_dentry->d_inode);
+				error = copy_to_user((loff_t *)arg, &res, sizeof(res)) ? -EFAULT : 0;
+			}
+			else
+				error = -ENOTTY;
+			break;
 		default:
 			error = -ENOTTY;
 			if (S_ISREG(filp->f_dentry->d_inode->i_mode))
diff -urN linux-2.4.22-pre3/fs/quota.c linux-2.4.22-pre4/fs/quota.c
--- linux-2.4.22-pre3/fs/quota.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.4.22-pre4/fs/quota.c	2003-07-09 16:56:29.000000000 -0700
@@ -0,0 +1,489 @@
+/*
+ * Quota code necessary even when VFS quota support is not compiled
+ * into the kernel.  The interesting stuff is over in dquot.c, here
+ * we have symbols for initial quotactl(2) handling, the sysctl(2)
+ * variables, etc - things needed even when quota support disabled.
+ */
+
+#include <linux/fs.h>
+#include <linux/slab.h>
+#include <asm/current.h>
+#include <asm/uaccess.h>
+#include <linux/kernel.h>
+#include <linux/smp_lock.h>
+#include <linux/quotaops.h>
+#include <linux/quotacompat.h>
+
+struct dqstats dqstats;
+
+/* Check validity of quotactl */
+static int check_quotactl_valid(struct super_block *sb, int type, int cmd, qid_t id)
+{
+	if (type >= MAXQUOTAS)
+		return -EINVAL;
+	if (!sb && cmd != Q_SYNC)
+		return -ENODEV;
+	/* Is operation supported? */
+	if (sb && !sb->s_qcop)
+		return -ENOSYS;
+
+	switch (cmd) {
+		case Q_GETFMT:
+			break;
+		case Q_QUOTAON:
+			if (!sb->s_qcop->quota_on)
+				return -ENOSYS;
+			break;
+		case Q_QUOTAOFF:
+			if (!sb->s_qcop->quota_off)
+				return -ENOSYS;
+			break;
+		case Q_SETINFO:
+			if (!sb->s_qcop->set_info)
+				return -ENOSYS;
+			break;
+		case Q_GETINFO:
+			if (!sb->s_qcop->get_info)
+				return -ENOSYS;
+			break;
+		case Q_SETQUOTA:
+			if (!sb->s_qcop->set_dqblk)
+				return -ENOSYS;
+			break;
+		case Q_GETQUOTA:
+			if (!sb->s_qcop->get_dqblk)
+				return -ENOSYS;
+			break;
+		case Q_SYNC:
+			if (sb && !sb->s_qcop->quota_sync)
+				return -ENOSYS;
+			break;
+		case Q_XQUOTAON:
+		case Q_XQUOTAOFF:
+		case Q_XQUOTARM:
+			if (!sb->s_qcop->set_xstate)
+				return -ENOSYS;
+			break;
+		case Q_XGETQSTAT:
+			if (!sb->s_qcop->get_xstate)
+				return -ENOSYS;
+			break;
+		case Q_XSETQLIM:
+			if (!sb->s_qcop->set_xquota)
+				return -ENOSYS;
+			break;
+		case Q_XGETQUOTA:
+			if (!sb->s_qcop->get_xquota)
+				return -ENOSYS;
+			break;
+		default:
+			return -EINVAL;
+	}
+
+	/* Is quota turned on for commands which need it? */
+	switch (cmd) {
+		case Q_GETFMT:
+		case Q_GETINFO:
+		case Q_QUOTAOFF:
+		case Q_SETINFO:
+		case Q_SETQUOTA:
+		case Q_GETQUOTA:
+			if (!sb_has_quota_enabled(sb, type))
+				return -ESRCH;
+	}
+	/* Check privileges */
+	if (cmd == Q_GETQUOTA || cmd == Q_XGETQUOTA) {
+		if (((type == USRQUOTA && current->euid != id) ||
+		     (type == GRPQUOTA && !in_egroup_p(id))) &&
+		    !capable(CAP_SYS_ADMIN))
+			return -EPERM;
+	}
+	else if (cmd != Q_GETFMT && cmd != Q_SYNC && cmd != Q_GETINFO && cmd != Q_XGETQSTAT)
+		if (!capable(CAP_SYS_ADMIN))
+			return -EPERM;
+	return 0;
+}
+
+/* Resolve device pathname to superblock */
+static struct super_block *resolve_dev(const char *path)
+{
+	int ret;
+	mode_t mode;
+	struct nameidata nd;
+	kdev_t dev;
+	struct super_block *sb;
+
+	ret = user_path_walk(path, &nd);
+	if (ret)
+		goto out;
+
+	dev = nd.dentry->d_inode->i_rdev;
+	mode = nd.dentry->d_inode->i_mode;
+	path_release(&nd);
+
+	ret = -ENOTBLK;
+	if (!S_ISBLK(mode))
+		goto out;
+	ret = -ENODEV;
+	sb = get_super(dev);
+	if (!sb)
+		goto out;
+	return sb;
+out:
+	return ERR_PTR(ret);
+}
+
+/* Copy parameters and call proper function */
+static int do_quotactl(struct super_block *sb, int type, int cmd, qid_t id, caddr_t addr)
+{
+	int ret;
+
+	switch (cmd) {
+		case Q_QUOTAON: {
+			char *pathname;
+
+			if (IS_ERR(pathname = getname(addr)))
+				return PTR_ERR(pathname);
+			ret = sb->s_qcop->quota_on(sb, type, id, pathname);
+			putname(pathname);
+			return ret;
+		}
+		case Q_QUOTAOFF:
+			return sb->s_qcop->quota_off(sb, type);
+
+		case Q_GETFMT: {
+			__u32 fmt;
+
+			fmt = sb_dqopt(sb)->info[type].dqi_format->qf_fmt_id;
+			if (copy_to_user(addr, &fmt, sizeof(fmt)))
+				return -EFAULT;
+			return 0;
+		}
+		case Q_GETINFO: {
+			struct if_dqinfo info;
+
+			if ((ret = sb->s_qcop->get_info(sb, type, &info)))
+				return ret;
+			if (copy_to_user(addr, &info, sizeof(info)))
+				return -EFAULT;
+			return 0;
+		}
+		case Q_SETINFO: {
+			struct if_dqinfo info;
+
+			if (copy_from_user(&info, addr, sizeof(info)))
+				return -EFAULT;
+			return sb->s_qcop->set_info(sb, type, &info);
+		}
+		case Q_GETQUOTA: {
+			struct if_dqblk idq;
+
+			if ((ret = sb->s_qcop->get_dqblk(sb, type, id, &idq)))
+				return ret;
+			if (copy_to_user(addr, &idq, sizeof(idq)))
+				return -EFAULT;
+			return 0;
+		}
+		case Q_SETQUOTA: {
+			struct if_dqblk idq;
+
+			if (copy_from_user(&idq, addr, sizeof(idq)))
+				return -EFAULT;
+			return sb->s_qcop->set_dqblk(sb, type, id, &idq);
+		}
+		case Q_SYNC:
+			if (sb)
+				return sb->s_qcop->quota_sync(sb, type);
+			sync_dquots_dev(NODEV, type);
+			return 0;
+		case Q_XQUOTAON:
+		case Q_XQUOTAOFF:
+		case Q_XQUOTARM: {
+			__u32 flags;
+
+			if (copy_from_user(&flags, addr, sizeof(flags)))
+				return -EFAULT;
+			return sb->s_qcop->set_xstate(sb, flags, cmd);
+		}
+		case Q_XGETQSTAT: {
+			struct fs_quota_stat fqs;
+		
+			if ((ret = sb->s_qcop->get_xstate(sb, &fqs)))
+				return ret;
+			if (copy_to_user(addr, &fqs, sizeof(fqs)))
+				return -EFAULT;
+			return 0;
+		}
+		case Q_XSETQLIM: {
+			struct fs_disk_quota fdq;
+
+			if (copy_from_user(&fdq, addr, sizeof(fdq)))
+				return -EFAULT;
+		       return sb->s_qcop->set_xquota(sb, type, id, &fdq);
+		}
+		case Q_XGETQUOTA: {
+			struct fs_disk_quota fdq;
+
+			if ((ret = sb->s_qcop->get_xquota(sb, type, id, &fdq)))
+				return ret;
+			if (copy_to_user(addr, &fdq, sizeof(fdq)))
+				return -EFAULT;
+			return 0;
+		}
+		/* We never reach here unless validity check is broken */
+		default:
+			BUG();
+	}
+	return 0;
+}
+
+static int check_compat_quotactl_valid(struct super_block *sb, int type, int cmd, qid_t id)
+{
+	if (type >= MAXQUOTAS)
+		return -EINVAL;
+	/* Is operation supported? */
+	/* sb==NULL for GETSTATS calls */
+	if (sb && !sb->s_qcop)
+		return -ENOSYS;
+
+	switch (cmd) {
+		case Q_COMP_QUOTAON:
+			if (!sb->s_qcop->quota_on)
+				return -ENOSYS;
+			break;
+		case Q_COMP_QUOTAOFF:
+			if (!sb->s_qcop->quota_off)
+				return -ENOSYS;
+			break;
+		case Q_COMP_SYNC:
+			if (sb && !sb->s_qcop->quota_sync)
+				return -ENOSYS;
+			break;
+		case Q_V1_SETQLIM:
+		case Q_V1_SETUSE:
+		case Q_V1_SETQUOTA:
+			if (!sb->s_qcop->set_dqblk)
+				return -ENOSYS;
+			break;
+		case Q_V1_GETQUOTA:
+			if (!sb->s_qcop->get_dqblk)
+				return -ENOSYS;
+			break;
+		case Q_V1_RSQUASH:
+			if (!sb->s_qcop->set_info)
+				return -ENOSYS;
+			break;
+		case Q_V1_GETSTATS:
+			return 0;	/* GETSTATS need no other checks */
+		default:
+			return -EINVAL;
+	}
+
+	/* Is quota turned on for commands which need it? */
+	switch (cmd) {
+		case Q_V2_SETFLAGS:
+		case Q_V2_SETGRACE:
+		case Q_V2_SETINFO:
+		case Q_V2_GETINFO:
+		case Q_COMP_QUOTAOFF:
+		case Q_V1_RSQUASH:
+		case Q_V1_SETQUOTA:
+		case Q_V1_SETQLIM:
+		case Q_V1_SETUSE:
+		case Q_V2_SETQUOTA:
+		/* Q_V2_SETQLIM: collision with Q_V1_SETQLIM */
+		case Q_V2_SETUSE:
+		case Q_V1_GETQUOTA:
+		case Q_V2_GETQUOTA:
+			if (!sb_has_quota_enabled(sb, type))
+				return -ESRCH;
+	}
+	if (cmd != Q_COMP_QUOTAON &&
+	    cmd != Q_COMP_QUOTAOFF &&
+	    cmd != Q_COMP_SYNC &&
+	    sb_dqopt(sb)->info[type].dqi_format->qf_fmt_id != QFMT_VFS_OLD)
+		return -ESRCH;
+
+	/* Check privileges */
+	if (cmd == Q_V1_GETQUOTA || cmd == Q_V2_GETQUOTA) {
+		if (((type == USRQUOTA && current->euid != id) ||
+		     (type == GRPQUOTA && !in_egroup_p(id))) &&
+		    !capable(CAP_SYS_ADMIN))
+			return -EPERM;
+	}
+	else if (cmd != Q_V1_GETSTATS && cmd != Q_V2_GETSTATS && cmd != Q_V2_GETINFO && cmd != Q_COMP_SYNC)
+		if (!capable(CAP_SYS_ADMIN))
+			return -EPERM;
+	return 0;
+}
+
+static int v1_set_rsquash(struct super_block *sb, int type, int flag)
+{
+	struct if_dqinfo info;
+
+	info.dqi_valid = IIF_FLAGS;
+	info.dqi_flags = flag ? V1_DQF_RSQUASH : 0;
+	return sb->s_qcop->set_info(sb, type, &info);
+}
+
+static int v1_get_dqblk(struct super_block *sb, int type, qid_t id, struct v1c_mem_dqblk *mdq)
+{
+	struct if_dqblk idq;
+	int ret;
+
+	if ((ret = sb->s_qcop->get_dqblk(sb, type, id, &idq)) < 0)
+		return ret;
+	mdq->dqb_ihardlimit = idq.dqb_ihardlimit;
+	mdq->dqb_isoftlimit = idq.dqb_isoftlimit;
+	mdq->dqb_curinodes = idq.dqb_curinodes;
+	mdq->dqb_bhardlimit = idq.dqb_bhardlimit;
+	mdq->dqb_bsoftlimit = idq.dqb_bsoftlimit;
+	mdq->dqb_curblocks = toqb(idq.dqb_curspace);
+	mdq->dqb_itime = idq.dqb_itime;
+	mdq->dqb_btime = idq.dqb_btime;
+	if (id == 0) {	/* Times for id 0 are in fact grace times */
+		struct if_dqinfo info;
+
+		if ((ret = sb->s_qcop->get_info(sb, type, &info)) < 0)
+			return ret;
+		mdq->dqb_btime = info.dqi_bgrace;
+		mdq->dqb_itime = info.dqi_igrace;
+	}
+	return 0;
+}
+
+static int v1_set_dqblk(struct super_block *sb, int type, int cmd, qid_t id, struct v1c_mem_dqblk *mdq)
+{
+	struct if_dqblk idq;
+	int ret;
+
+	idq.dqb_valid = 0;
+	if (cmd == Q_V1_SETQUOTA || cmd == Q_V1_SETQLIM) {
+		idq.dqb_ihardlimit = mdq->dqb_ihardlimit;
+		idq.dqb_isoftlimit = mdq->dqb_isoftlimit;
+		idq.dqb_bhardlimit = mdq->dqb_bhardlimit;
+		idq.dqb_bsoftlimit = mdq->dqb_bsoftlimit;
+		idq.dqb_valid |= QIF_LIMITS;
+	}
+	if (cmd == Q_V1_SETQUOTA || cmd == Q_V1_SETUSE) {
+		idq.dqb_curinodes = mdq->dqb_curinodes;
+		idq.dqb_curspace = ((qsize_t)mdq->dqb_curblocks) << QUOTABLOCK_BITS;
+		idq.dqb_valid |= QIF_USAGE;
+	}
+	ret = sb->s_qcop->set_dqblk(sb, type, id, &idq);
+	if (!ret && id == 0 && cmd == Q_V1_SETQUOTA) {	/* Times for id 0 are in fact grace times */
+		struct if_dqinfo info;
+
+		info.dqi_bgrace = mdq->dqb_btime;
+		info.dqi_igrace = mdq->dqb_itime;
+		info.dqi_valid = IIF_BGRACE | IIF_IGRACE;
+		ret = sb->s_qcop->set_info(sb, type, &info);
+	}
+	return ret;
+}
+
+static void v1_get_stats(struct v1c_dqstats *dst)
+{
+	memcpy(dst, &dqstats, sizeof(dqstats));
+}
+
+/* Handle requests to old interface */
+static int do_compat_quotactl(struct super_block *sb, int type, int cmd, qid_t id, caddr_t addr)
+{
+	int ret;
+
+	switch (cmd) {
+		case Q_COMP_QUOTAON: {
+			char *pathname;
+
+			if (IS_ERR(pathname = getname(addr)))
+				return PTR_ERR(pathname);
+			ret = sb->s_qcop->quota_on(sb, type, QFMT_VFS_OLD, pathname);
+			putname(pathname);
+			return ret;
+		}
+		case Q_COMP_QUOTAOFF:
+			return sb->s_qcop->quota_off(sb, type);
+		case Q_COMP_SYNC:
+			if (sb)
+				return sb->s_qcop->quota_sync(sb, type);
+			sync_dquots_dev(NODEV, type);
+			return 0;
+		case Q_V1_RSQUASH: {
+			int flag;
+
+			if (copy_from_user(&flag, addr, sizeof(flag)))
+				return -EFAULT;
+			return v1_set_rsquash(sb, type, flag);
+		}
+		case Q_V1_GETQUOTA: {
+			struct v1c_mem_dqblk mdq;
+
+			if ((ret = v1_get_dqblk(sb, type, id, &mdq)))
+				return ret;
+			if (copy_to_user(addr, &mdq, sizeof(mdq)))
+				return -EFAULT;
+			return 0;
+		}
+		case Q_V1_SETQLIM:
+		case Q_V1_SETUSE:
+		case Q_V1_SETQUOTA: {
+			struct v1c_mem_dqblk mdq;
+
+			if (copy_from_user(&mdq, addr, sizeof(mdq)))
+				return -EFAULT;
+			return v1_set_dqblk(sb, type, cmd, id, &mdq);
+		}
+		case Q_V1_GETSTATS: {
+			struct v1c_dqstats dst;
+
+			v1_get_stats(&dst);
+			if (copy_to_user(addr, &dst, sizeof(dst)))
+				return -EFAULT;
+			return 0;
+		}
+	}
+	BUG();
+	return 0;
+}
+
+/* Macros for short-circuiting the compatibility tests */
+#define NEW_COMMAND(c) ((c) & (0x80 << 16))
+#define XQM_COMMAND(c) (((c) & ('X' << 8)) == ('X' << 8))
+
+/*
+ * This is the system call interface. This communicates with
+ * the user-level programs. Currently this only supports diskquota
+ * calls. Maybe we need to add the process quotas etc. in the future,
+ * but we probably should use rlimits for that.
+ */
+asmlinkage long sys_quotactl(unsigned int cmd, const char *special, qid_t id, caddr_t addr)
+{
+	uint cmds, type;
+	struct super_block *sb = NULL;
+	int ret = -EINVAL;
+
+	lock_kernel();
+	cmds = cmd >> SUBCMDSHIFT;
+	type = cmd & SUBCMDMASK;
+
+	if (cmds != Q_V1_GETSTATS && cmds != Q_V2_GETSTATS && IS_ERR(sb = resolve_dev(special))) {
+		ret = PTR_ERR(sb);
+		sb = NULL;
+		goto out;
+	}
+	if (!NEW_COMMAND(cmds) && !XQM_COMMAND(cmds)) {
+		if ((ret = check_compat_quotactl_valid(sb, type, cmds, id)) < 0)
+			goto out;
+		ret = do_compat_quotactl(sb, type, cmds, id, addr);
+		goto out;
+	}
+	if ((ret = check_quotactl_valid(sb, type, cmds, id)) < 0)
+		goto out;
+	ret = do_quotactl(sb, type, cmds, id, addr);
+out:
+	if (sb)
+		drop_super(sb);
+	unlock_kernel();
+	return ret;
+}
diff -urN linux-2.4.22-pre3/fs/quota_v1.c linux-2.4.22-pre4/fs/quota_v1.c
--- linux-2.4.22-pre3/fs/quota_v1.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.4.22-pre4/fs/quota_v1.c	2003-07-09 16:56:29.000000000 -0700
@@ -0,0 +1,239 @@
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/quota.h>
+#include <linux/dqblk_v1.h>
+#include <linux/quotaio_v1.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+
+#include <asm/uaccess.h>
+#include <asm/byteorder.h>
+
+static void v1_disk2mem_dqblk(struct mem_dqblk *m, struct v1_disk_dqblk *d)
+{
+	m->dqb_ihardlimit = d->dqb_ihardlimit;
+	m->dqb_isoftlimit = d->dqb_isoftlimit;
+	m->dqb_curinodes = d->dqb_curinodes;
+	m->dqb_bhardlimit = d->dqb_bhardlimit;
+	m->dqb_bsoftlimit = d->dqb_bsoftlimit;
+	m->dqb_curspace = d->dqb_curblocks << QUOTABLOCK_BITS;
+	m->dqb_itime = d->dqb_itime;
+	m->dqb_btime = d->dqb_btime;
+}
+
+static void v1_mem2disk_dqblk(struct v1_disk_dqblk *d, struct mem_dqblk *m)
+{
+	d->dqb_ihardlimit = m->dqb_ihardlimit;
+	d->dqb_isoftlimit = m->dqb_isoftlimit;
+	d->dqb_curinodes = m->dqb_curinodes;
+	d->dqb_bhardlimit = m->dqb_bhardlimit;
+	d->dqb_bsoftlimit = m->dqb_bsoftlimit;
+	d->dqb_curblocks = toqb(m->dqb_curspace);
+	d->dqb_itime = m->dqb_itime;
+	d->dqb_btime = m->dqb_btime;
+}
+
+static int v1_read_dqblk(struct dquot *dquot)
+{
+	int type = dquot->dq_type;
+	struct file *filp;
+	mm_segment_t fs;
+	loff_t offset;
+	struct v1_disk_dqblk dqblk;
+
+	filp = sb_dqopt(dquot->dq_sb)->files[type];
+	if (filp == (struct file *)NULL)
+		return -EINVAL;
+
+	/* Now we are sure filp is valid */
+	offset = v1_dqoff(dquot->dq_id);
+	fs = get_fs();
+	set_fs(KERNEL_DS);
+	filp->f_op->read(filp, (char *)&dqblk, sizeof(struct v1_disk_dqblk), &offset);
+	set_fs(fs);
+
+	v1_disk2mem_dqblk(&dquot->dq_dqb, &dqblk);
+	if (dquot->dq_dqb.dqb_bhardlimit == 0 && dquot->dq_dqb.dqb_bsoftlimit == 0 &&
+	    dquot->dq_dqb.dqb_ihardlimit == 0 && dquot->dq_dqb.dqb_isoftlimit == 0)
+		dquot->dq_flags |= DQ_FAKE;
+	dqstats.reads++;
+	return 0;
+}
+
+static int v1_commit_dqblk(struct dquot *dquot)
+{
+	short type = dquot->dq_type;
+	struct file *filp;
+	mm_segment_t fs;
+	loff_t offset;
+	ssize_t ret;
+	struct v1_disk_dqblk dqblk;
+
+	filp = sb_dqopt(dquot->dq_sb)->files[type];
+	offset = v1_dqoff(dquot->dq_id);
+	fs = get_fs();
+	set_fs(KERNEL_DS);
+
+	/*
+	 * Note: clear the DQ_MOD flag unconditionally,
+	 * so we don't loop forever on failure.
+	 */
+	v1_mem2disk_dqblk(&dqblk, &dquot->dq_dqb);
+	dquot->dq_flags &= ~DQ_MOD;
+	if (dquot->dq_id == 0) {
+		dqblk.dqb_btime = sb_dqopt(dquot->dq_sb)->info[type].dqi_bgrace;
+		dqblk.dqb_itime = sb_dqopt(dquot->dq_sb)->info[type].dqi_igrace;
+	}
+	ret = 0;
+	if (filp)
+		ret = filp->f_op->write(filp, (char *)&dqblk,
+					sizeof(struct v1_disk_dqblk), &offset);
+	if (ret != sizeof(struct v1_disk_dqblk)) {
+		printk(KERN_WARNING "VFS: dquota write failed on dev %s\n",
+			kdevname(dquot->dq_dev));
+		if (ret >= 0)
+			ret = -EIO;
+		goto out;
+	}
+	ret = 0;
+
+out:
+	set_fs(fs);
+	dqstats.writes++;
+	return ret;
+}
+
+/* Magics of new quota format */
+#define V2_INITQMAGICS {\
+	0xd9c01f11,     /* USRQUOTA */\
+	0xd9c01927      /* GRPQUOTA */\
+}
+
+/* Header of new quota format */
+struct v2_disk_dqheader {
+	__u32 dqh_magic;        /* Magic number identifying file */
+	__u32 dqh_version;      /* File version */
+};
+
+static int v1_check_quota_file(struct super_block *sb, int type)
+{
+	struct file *f = sb_dqopt(sb)->files[type];
+	struct inode *inode = f->f_dentry->d_inode;
+	ulong blocks;
+	size_t off; 
+	struct v2_disk_dqheader dqhead;
+	mm_segment_t fs;
+	ssize_t size;
+	loff_t offset = 0;
+	static const uint quota_magics[] = V2_INITQMAGICS;
+
+	if (!inode->i_size)
+		return 0;
+	blocks = inode->i_size >> BLOCK_SIZE_BITS;
+	off = inode->i_size & (BLOCK_SIZE - 1);
+	if ((blocks % sizeof(struct v1_disk_dqblk) * BLOCK_SIZE + off) % sizeof(struct v1_disk_dqblk))
+		return 0;
+	/* Doublecheck whether we didn't get file with new format - with old quotactl() this could happen */
+	fs = get_fs();
+	set_fs(KERNEL_DS);
+	size = f->f_op->read(f, (char *)&dqhead, sizeof(struct v2_disk_dqheader), &offset);
+	set_fs(fs);
+	if (size != sizeof(struct v2_disk_dqheader))
+		return 1;	/* Probably not new format */
+	if (le32_to_cpu(dqhead.dqh_magic) != quota_magics[type])
+		return 1;	/* Definitely not new format */
+	printk(KERN_INFO "VFS: %s: Refusing to turn on old quota format on given file. It probably contains newer quota format.\n", kdevname(sb->s_dev));
+        return 0;		/* Seems like a new format file -> refuse it */
+}
+
+static int v1_read_file_info(struct super_block *sb, int type)
+{
+	struct quota_info *dqopt = sb_dqopt(sb);
+	mm_segment_t fs;
+	loff_t offset;
+	struct file *filp = dqopt->files[type];
+	struct v1_disk_dqblk dqblk;
+	int ret;
+
+	down(&dqopt->dqio_sem);
+	offset = v1_dqoff(0);
+	fs = get_fs();
+	set_fs(KERNEL_DS);
+	if ((ret = filp->f_op->read(filp, (char *)&dqblk, sizeof(struct v1_disk_dqblk), &offset)) != sizeof(struct v1_disk_dqblk)) {
+		if (ret >= 0)
+			ret = -EIO;
+		goto out;
+	}
+	ret = 0;
+	dqopt->info[type].dqi_igrace = dqblk.dqb_itime ? dqblk.dqb_itime : MAX_IQ_TIME;
+	dqopt->info[type].dqi_bgrace = dqblk.dqb_btime ? dqblk.dqb_btime : MAX_DQ_TIME;
+out:
+	up(&dqopt->dqio_sem);
+	set_fs(fs);
+	return ret;
+}
+
+static int v1_write_file_info(struct super_block *sb, int type)
+{
+	struct quota_info *dqopt = sb_dqopt(sb);
+	mm_segment_t fs;
+	struct file *filp = dqopt->files[type];
+	struct v1_disk_dqblk dqblk;
+	loff_t offset;
+	int ret;
+
+	down(&dqopt->dqio_sem);
+	dqopt->info[type].dqi_flags &= ~DQF_INFO_DIRTY;
+	offset = v1_dqoff(0);
+	fs = get_fs();
+	set_fs(KERNEL_DS);
+	if ((ret = filp->f_op->read(filp, (char *)&dqblk, sizeof(struct v1_disk_dqblk), &offset)) != sizeof(struct v1_disk_dqblk)) {
+		if (ret >= 0)
+			ret = -EIO;
+		goto out;
+	}
+	dqblk.dqb_itime = dqopt->info[type].dqi_igrace;
+	dqblk.dqb_btime = dqopt->info[type].dqi_bgrace;
+	offset = v1_dqoff(0);
+	ret = filp->f_op->write(filp, (char *)&dqblk, sizeof(struct v1_disk_dqblk), &offset);
+	if (ret == sizeof(struct v1_disk_dqblk))
+		ret = 0;
+	else if (ret > 0)
+		ret = -EIO;
+out:
+	up(&dqopt->dqio_sem);
+	set_fs(fs);
+	return ret;
+}
+
+static struct quota_format_ops v1_format_ops = {
+	check_quota_file:	v1_check_quota_file,
+	read_file_info:		v1_read_file_info,
+	write_file_info:	v1_write_file_info,
+	free_file_info:		NULL,
+	read_dqblk:		v1_read_dqblk,
+	commit_dqblk:		v1_commit_dqblk,
+};
+
+static struct quota_format_type v1_quota_format = {
+	qf_fmt_id:	QFMT_VFS_OLD,
+	qf_ops:		&v1_format_ops,
+	qf_owner:	THIS_MODULE
+};
+
+static int __init init_v1_quota_format(void)
+{
+        return register_quota_format(&v1_quota_format);
+}
+
+static void __exit exit_v1_quota_format(void)
+{
+        unregister_quota_format(&v1_quota_format);
+}
+
+EXPORT_NO_SYMBOLS;
+
+module_init(init_v1_quota_format);
+module_exit(exit_v1_quota_format);
+
diff -urN linux-2.4.22-pre3/fs/quota_v2.c linux-2.4.22-pre4/fs/quota_v2.c
--- linux-2.4.22-pre3/fs/quota_v2.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.4.22-pre4/fs/quota_v2.c	2003-07-09 16:56:29.000000000 -0700
@@ -0,0 +1,690 @@
+/*
+ *	vfsv0 quota IO operations on file
+ */
+
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/dqblk_v2.h>
+#include <linux/quotaio_v2.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+
+#include <asm/byteorder.h>
+#include <asm/uaccess.h>
+
+#define __QUOTA_V2_PARANOIA
+
+typedef char *dqbuf_t;
+
+#define GETIDINDEX(id, depth) (((id) >> ((V2_DQTREEDEPTH-(depth)-1)*8)) & 0xff)
+#define GETENTRIES(buf) ((struct v2_disk_dqblk *)(((char *)buf)+sizeof(struct v2_disk_dqdbheader)))
+
+/* Check whether given file is really vfsv0 quotafile */
+static int v2_check_quota_file(struct super_block *sb, int type)
+{
+	struct v2_disk_dqheader dqhead;
+	struct file *f = sb_dqopt(sb)->files[type];
+	mm_segment_t fs;
+	ssize_t size;
+	loff_t offset = 0;
+	static const uint quota_magics[] = V2_INITQMAGICS;
+	static const uint quota_versions[] = V2_INITQVERSIONS;
+ 
+	fs = get_fs();
+	set_fs(KERNEL_DS);
+	size = f->f_op->read(f, (char *)&dqhead, sizeof(struct v2_disk_dqheader), &offset);
+	set_fs(fs);
+	if (size != sizeof(struct v2_disk_dqheader))
+		return 0;
+	if (le32_to_cpu(dqhead.dqh_magic) != quota_magics[type] ||
+	    le32_to_cpu(dqhead.dqh_version) != quota_versions[type])
+		return 0;
+	return 1;
+}
+
+/* Read information header from quota file */
+static int v2_read_file_info(struct super_block *sb, int type)
+{
+	mm_segment_t fs;
+	struct v2_disk_dqinfo dinfo;
+	struct mem_dqinfo *info = sb_dqopt(sb)->info+type;
+	struct file *f = sb_dqopt(sb)->files[type];
+	ssize_t size;
+	loff_t offset = V2_DQINFOOFF;
+
+	fs = get_fs();
+	set_fs(KERNEL_DS);
+	size = f->f_op->read(f, (char *)&dinfo, sizeof(struct v2_disk_dqinfo), &offset);
+	set_fs(fs);
+	if (size != sizeof(struct v2_disk_dqinfo)) {
+		printk(KERN_WARNING "Can't read info structure on device %s.\n",
+			kdevname(f->f_dentry->d_sb->s_dev));
+		return -1;
+	}
+	info->dqi_bgrace = le32_to_cpu(dinfo.dqi_bgrace);
+	info->dqi_igrace = le32_to_cpu(dinfo.dqi_igrace);
+	info->dqi_flags = le32_to_cpu(dinfo.dqi_flags);
+	info->u.v2_i.dqi_blocks = le32_to_cpu(dinfo.dqi_blocks);
+	info->u.v2_i.dqi_free_blk = le32_to_cpu(dinfo.dqi_free_blk);
+	info->u.v2_i.dqi_free_entry = le32_to_cpu(dinfo.dqi_free_entry);
+	return 0;
+}
+
+/* Write information header to quota file */
+static int v2_write_file_info(struct super_block *sb, int type)
+{
+	mm_segment_t fs;
+	struct v2_disk_dqinfo dinfo;
+	struct mem_dqinfo *info = sb_dqopt(sb)->info+type;
+	struct file *f = sb_dqopt(sb)->files[type];
+	ssize_t size;
+	loff_t offset = V2_DQINFOOFF;
+
+	info->dqi_flags &= ~DQF_INFO_DIRTY;
+	dinfo.dqi_bgrace = cpu_to_le32(info->dqi_bgrace);
+	dinfo.dqi_igrace = cpu_to_le32(info->dqi_igrace);
+	dinfo.dqi_flags = cpu_to_le32(info->dqi_flags & DQF_MASK);
+	dinfo.dqi_blocks = cpu_to_le32(info->u.v2_i.dqi_blocks);
+	dinfo.dqi_free_blk = cpu_to_le32(info->u.v2_i.dqi_free_blk);
+	dinfo.dqi_free_entry = cpu_to_le32(info->u.v2_i.dqi_free_entry);
+	fs = get_fs();
+	set_fs(KERNEL_DS);
+	size = f->f_op->write(f, (char *)&dinfo, sizeof(struct v2_disk_dqinfo), &offset);
+	set_fs(fs);
+	if (size != sizeof(struct v2_disk_dqinfo)) {
+		printk(KERN_WARNING "Can't write info structure on device %s.\n",
+			kdevname(f->f_dentry->d_sb->s_dev));
+		return -1;
+	}
+	return 0;
+}
+
+static void disk2memdqb(struct mem_dqblk *m, struct v2_disk_dqblk *d)
+{
+	m->dqb_ihardlimit = le32_to_cpu(d->dqb_ihardlimit);
+	m->dqb_isoftlimit = le32_to_cpu(d->dqb_isoftlimit);
+	m->dqb_curinodes = le32_to_cpu(d->dqb_curinodes);
+	m->dqb_itime = le64_to_cpu(d->dqb_itime);
+	m->dqb_bhardlimit = le32_to_cpu(d->dqb_bhardlimit);
+	m->dqb_bsoftlimit = le32_to_cpu(d->dqb_bsoftlimit);
+	m->dqb_curspace = le64_to_cpu(d->dqb_curspace);
+	m->dqb_btime = le64_to_cpu(d->dqb_btime);
+}
+
+static void mem2diskdqb(struct v2_disk_dqblk *d, struct mem_dqblk *m, qid_t id)
+{
+	d->dqb_ihardlimit = cpu_to_le32(m->dqb_ihardlimit);
+	d->dqb_isoftlimit = cpu_to_le32(m->dqb_isoftlimit);
+	d->dqb_curinodes = cpu_to_le32(m->dqb_curinodes);
+	d->dqb_itime = cpu_to_le64(m->dqb_itime);
+	d->dqb_bhardlimit = cpu_to_le32(m->dqb_bhardlimit);
+	d->dqb_bsoftlimit = cpu_to_le32(m->dqb_bsoftlimit);
+	d->dqb_curspace = cpu_to_le64(m->dqb_curspace);
+	d->dqb_btime = cpu_to_le64(m->dqb_btime);
+	d->dqb_id = cpu_to_le32(id);
+}
+
+static dqbuf_t getdqbuf(void)
+{
+	dqbuf_t buf = kmalloc(V2_DQBLKSIZE, GFP_KERNEL);
+	if (!buf)
+		printk(KERN_WARNING "VFS: Not enough memory for quota buffers.\n");
+	return buf;
+}
+
+static inline void freedqbuf(dqbuf_t buf)
+{
+	kfree(buf);
+}
+
+static ssize_t read_blk(struct file *filp, uint blk, dqbuf_t buf)
+{
+	mm_segment_t fs;
+	ssize_t ret;
+	loff_t offset = blk<<V2_DQBLKSIZE_BITS;
+
+	memset(buf, 0, V2_DQBLKSIZE);
+	fs = get_fs();
+	set_fs(KERNEL_DS);
+	ret = filp->f_op->read(filp, (char *)buf, V2_DQBLKSIZE, &offset);
+	set_fs(fs);
+	return ret;
+}
+
+static ssize_t write_blk(struct file *filp, uint blk, dqbuf_t buf)
+{
+	mm_segment_t fs;
+	ssize_t ret;
+	loff_t offset = blk<<V2_DQBLKSIZE_BITS;
+
+	fs = get_fs();
+	set_fs(KERNEL_DS);
+	ret = filp->f_op->write(filp, (char *)buf, V2_DQBLKSIZE, &offset);
+	set_fs(fs);
+	return ret;
+
+}
+
+/* Remove empty block from list and return it */
+static int get_free_dqblk(struct file *filp, struct mem_dqinfo *info)
+{
+	dqbuf_t buf = getdqbuf();
+	struct v2_disk_dqdbheader *dh = (struct v2_disk_dqdbheader *)buf;
+	int ret, blk;
+
+	if (!buf)
+		return -ENOMEM;
+	if (info->u.v2_i.dqi_free_blk) {
+		blk = info->u.v2_i.dqi_free_blk;
+		if ((ret = read_blk(filp, blk, buf)) < 0)
+			goto out_buf;
+		info->u.v2_i.dqi_free_blk = le32_to_cpu(dh->dqdh_next_free);
+	}
+	else {
+		memset(buf, 0, V2_DQBLKSIZE);
+		if ((ret = write_blk(filp, info->u.v2_i.dqi_blocks, buf)) < 0)	/* Assure block allocation... */
+			goto out_buf;
+		blk = info->u.v2_i.dqi_blocks++;
+	}
+	mark_info_dirty(info);
+	ret = blk;
+out_buf:
+	freedqbuf(buf);
+	return ret;
+}
+
+/* Insert empty block to the list */
+static int put_free_dqblk(struct file *filp, struct mem_dqinfo *info, dqbuf_t buf, uint blk)
+{
+	struct v2_disk_dqdbheader *dh = (struct v2_disk_dqdbheader *)buf;
+	int err;
+
+	dh->dqdh_next_free = cpu_to_le32(info->u.v2_i.dqi_free_blk);
+	dh->dqdh_prev_free = cpu_to_le32(0);
+	dh->dqdh_entries = cpu_to_le16(0);
+	info->u.v2_i.dqi_free_blk = blk;
+	mark_info_dirty(info);
+	if ((err = write_blk(filp, blk, buf)) < 0)	/* Some strange block. We had better leave it... */
+		return err;
+	return 0;
+}
+
+/* Remove given block from the list of blocks with free entries */
+static int remove_free_dqentry(struct file *filp, struct mem_dqinfo *info, dqbuf_t buf, uint blk)
+{
+	dqbuf_t tmpbuf = getdqbuf();
+	struct v2_disk_dqdbheader *dh = (struct v2_disk_dqdbheader *)buf;
+	uint nextblk = le32_to_cpu(dh->dqdh_next_free), prevblk = le32_to_cpu(dh->dqdh_prev_free);
+	int err;
+
+	if (!tmpbuf)
+		return -ENOMEM;
+	if (nextblk) {
+		if ((err = read_blk(filp, nextblk, tmpbuf)) < 0)
+			goto out_buf;
+		((struct v2_disk_dqdbheader *)tmpbuf)->dqdh_prev_free = dh->dqdh_prev_free;
+		if ((err = write_blk(filp, nextblk, tmpbuf)) < 0)
+			goto out_buf;
+	}
+	if (prevblk) {
+		if ((err = read_blk(filp, prevblk, tmpbuf)) < 0)
+			goto out_buf;
+		((struct v2_disk_dqdbheader *)tmpbuf)->dqdh_next_free = dh->dqdh_next_free;
+		if ((err = write_blk(filp, prevblk, tmpbuf)) < 0)
+			goto out_buf;
+	}
+	else {
+		info->u.v2_i.dqi_free_entry = nextblk;
+		mark_info_dirty(info);
+	}
+	freedqbuf(tmpbuf);
+	dh->dqdh_next_free = dh->dqdh_prev_free = cpu_to_le32(0);
+	if (write_blk(filp, blk, buf) < 0)	/* No matter whether write succeeds block is out of list */
+		printk(KERN_ERR "VFS: Can't write block (%u) with free entries.\n", blk);
+	return 0;
+out_buf:
+	freedqbuf(tmpbuf);
+	return err;
+}
+
+/* Insert given block to the beginning of list with free entries */
+static int insert_free_dqentry(struct file *filp, struct mem_dqinfo *info, dqbuf_t buf, uint blk)
+{
+	dqbuf_t tmpbuf = getdqbuf();
+	struct v2_disk_dqdbheader *dh = (struct v2_disk_dqdbheader *)buf;
+	int err;
+
+	if (!tmpbuf)
+		return -ENOMEM;
+	dh->dqdh_next_free = cpu_to_le32(info->u.v2_i.dqi_free_entry);
+	dh->dqdh_prev_free = cpu_to_le32(0);
+	if ((err = write_blk(filp, blk, buf)) < 0)
+		goto out_buf;
+	if (info->u.v2_i.dqi_free_entry) {
+		if ((err = read_blk(filp, info->u.v2_i.dqi_free_entry, tmpbuf)) < 0)
+			goto out_buf;
+		((struct v2_disk_dqdbheader *)tmpbuf)->dqdh_prev_free = cpu_to_le32(blk);
+		if ((err = write_blk(filp, info->u.v2_i.dqi_free_entry, tmpbuf)) < 0)
+			goto out_buf;
+	}
+	freedqbuf(tmpbuf);
+	info->u.v2_i.dqi_free_entry = blk;
+	mark_info_dirty(info);
+	return 0;
+out_buf:
+	freedqbuf(tmpbuf);
+	return err;
+}
+
+/* Find space for dquot */
+static uint find_free_dqentry(struct dquot *dquot, int *err)
+{
+	struct file *filp = sb_dqopt(dquot->dq_sb)->files[dquot->dq_type];
+	struct mem_dqinfo *info = sb_dqopt(dquot->dq_sb)->info+dquot->dq_type;
+	uint blk, i;
+	struct v2_disk_dqdbheader *dh;
+	struct v2_disk_dqblk *ddquot;
+	struct v2_disk_dqblk fakedquot;
+	dqbuf_t buf;
+
+	*err = 0;
+	if (!(buf = getdqbuf())) {
+		*err = -ENOMEM;
+		return 0;
+	}
+	dh = (struct v2_disk_dqdbheader *)buf;
+	ddquot = GETENTRIES(buf);
+	if (info->u.v2_i.dqi_free_entry) {
+		blk = info->u.v2_i.dqi_free_entry;
+		if ((*err = read_blk(filp, blk, buf)) < 0)
+			goto out_buf;
+	}
+	else {
+		blk = get_free_dqblk(filp, info);
+		if ((int)blk < 0) {
+			*err = blk;
+			return 0;
+		}
+		memset(buf, 0, V2_DQBLKSIZE);
+		info->u.v2_i.dqi_free_entry = blk;	/* This is enough as block is already zeroed and entry list is empty... */
+		mark_info_dirty(info);
+	}
+	if (le16_to_cpu(dh->dqdh_entries)+1 >= V2_DQSTRINBLK)	/* Block will be full? */
+		if ((*err = remove_free_dqentry(filp, info, buf, blk)) < 0) {
+			printk(KERN_ERR "VFS: find_free_dqentry(): Can't remove block (%u) from entry free list.\n", blk);
+			goto out_buf;
+		}
+	dh->dqdh_entries = cpu_to_le16(le16_to_cpu(dh->dqdh_entries)+1);
+	memset(&fakedquot, 0, sizeof(struct v2_disk_dqblk));
+	/* Find free structure in block */
+	for (i = 0; i < V2_DQSTRINBLK && memcmp(&fakedquot, ddquot+i, sizeof(struct v2_disk_dqblk)); i++);
+#ifdef __QUOTA_V2_PARANOIA
+	if (i == V2_DQSTRINBLK) {
+		printk(KERN_ERR "VFS: find_free_dqentry(): Data block full but it shouldn't.\n");
+		*err = -EIO;
+		goto out_buf;
+	}
+#endif
+	if ((*err = write_blk(filp, blk, buf)) < 0) {
+		printk(KERN_ERR "VFS: find_free_dqentry(): Can't write quota data block %u.\n", blk);
+		goto out_buf;
+	}
+	dquot->dq_off = (blk<<V2_DQBLKSIZE_BITS)+sizeof(struct v2_disk_dqdbheader)+i*sizeof(struct v2_disk_dqblk);
+	freedqbuf(buf);
+	return blk;
+out_buf:
+	freedqbuf(buf);
+	return 0;
+}
+
+/* Insert reference to structure into the trie */
+static int do_insert_tree(struct dquot *dquot, uint *treeblk, int depth)
+{
+	struct file *filp = sb_dqopt(dquot->dq_sb)->files[dquot->dq_type];
+	struct mem_dqinfo *info = sb_dqopt(dquot->dq_sb)->info + dquot->dq_type;
+	dqbuf_t buf;
+	int ret = 0, newson = 0, newact = 0;
+	u32 *ref;
+	uint newblk;
+
+	if (!(buf = getdqbuf()))
+		return -ENOMEM;
+	if (!*treeblk) {
+		ret = get_free_dqblk(filp, info);
+		if (ret < 0)
+			goto out_buf;
+		*treeblk = ret;
+		memset(buf, 0, V2_DQBLKSIZE);
+		newact = 1;
+	}
+	else {
+		if ((ret = read_blk(filp, *treeblk, buf)) < 0) {
+			printk(KERN_ERR "VFS: Can't read tree quota block %u.\n", *treeblk);
+			goto out_buf;
+		}
+	}
+	ref = (u32 *)buf;
+	newblk = le32_to_cpu(ref[GETIDINDEX(dquot->dq_id, depth)]);
+	if (!newblk)
+		newson = 1;
+	if (depth == V2_DQTREEDEPTH-1) {
+#ifdef __QUOTA_V2_PARANOIA
+		if (newblk) {
+			printk(KERN_ERR "VFS: Inserting already present quota entry (block %u).\n", ref[GETIDINDEX(dquot->dq_id, depth)]);
+			ret = -EIO;
+			goto out_buf;
+		}
+#endif
+		newblk = find_free_dqentry(dquot, &ret);
+	}
+	else
+		ret = do_insert_tree(dquot, &newblk, depth+1);
+	if (newson && ret >= 0) {
+		ref[GETIDINDEX(dquot->dq_id, depth)] = cpu_to_le32(newblk);
+		ret = write_blk(filp, *treeblk, buf);
+	}
+	else if (newact && ret < 0)
+		put_free_dqblk(filp, info, buf, *treeblk);
+out_buf:
+	freedqbuf(buf);
+	return ret;
+}
+
+/* Wrapper for inserting quota structure into tree */
+static inline int dq_insert_tree(struct dquot *dquot)
+{
+	int tmp = V2_DQTREEOFF;
+	return do_insert_tree(dquot, &tmp, 0);
+}
+
+/*
+ *	We don't have to be afraid of deadlocks as we never have quotas on quota files...
+ */
+static int v2_write_dquot(struct dquot *dquot)
+{
+	int type = dquot->dq_type;
+	struct file *filp;
+	mm_segment_t fs;
+	loff_t offset;
+	ssize_t ret;
+	struct v2_disk_dqblk ddquot;
+
+	if (!dquot->dq_off)
+		if ((ret = dq_insert_tree(dquot)) < 0) {
+			printk(KERN_ERR "VFS: Error %d occured while creating quota.\n", ret);
+			return ret;
+		}
+	filp = sb_dqopt(dquot->dq_sb)->files[type];
+	offset = dquot->dq_off;
+	mem2diskdqb(&ddquot, &dquot->dq_dqb, dquot->dq_id);
+	fs = get_fs();
+	set_fs(KERNEL_DS);
+	ret = filp->f_op->write(filp, (char *)&ddquot, sizeof(struct v2_disk_dqblk), &offset);
+	set_fs(fs);
+	if (ret != sizeof(struct v2_disk_dqblk)) {
+		printk(KERN_WARNING "VFS: dquota write failed on dev %s\n", kdevname(dquot->dq_dev));
+		if (ret >= 0)
+			ret = -ENOSPC;
+	}
+	else
+		ret = 0;
+	dqstats.writes++;
+	return ret;
+}
+
+/* Free dquot entry in data block */
+static int free_dqentry(struct dquot *dquot, uint blk)
+{
+	struct file *filp = sb_dqopt(dquot->dq_sb)->files[dquot->dq_type];
+	struct mem_dqinfo *info = sb_dqopt(dquot->dq_sb)->info + dquot->dq_type;
+	struct v2_disk_dqdbheader *dh;
+	dqbuf_t buf = getdqbuf();
+	int ret = 0;
+
+	if (!buf)
+		return -ENOMEM;
+	if (dquot->dq_off >> V2_DQBLKSIZE_BITS != blk) {
+		printk(KERN_ERR "VFS: Quota structure has offset to other block (%u) than it should (%u).\n", blk, (uint)(dquot->dq_off >> V2_DQBLKSIZE_BITS));
+		goto out_buf;
+	}
+	if ((ret = read_blk(filp, blk, buf)) < 0) {
+		printk(KERN_ERR "VFS: Can't read quota data block %u\n", blk);
+		goto out_buf;
+	}
+	dh = (struct v2_disk_dqdbheader *)buf;
+	dh->dqdh_entries = cpu_to_le16(le16_to_cpu(dh->dqdh_entries)-1);
+	if (!le16_to_cpu(dh->dqdh_entries)) {	/* Block got free? */
+		if ((ret = remove_free_dqentry(filp, info, buf, blk)) < 0 ||
+		    (ret = put_free_dqblk(filp, info, buf, blk)) < 0) {
+			printk(KERN_ERR "VFS: Can't move quota data block (%u) to free list.\n", blk);
+			goto out_buf;
+		}
+	}
+	else {
+		memset(buf+(dquot->dq_off & ((1 << V2_DQBLKSIZE_BITS)-1)), 0, sizeof(struct v2_disk_dqblk));
+		if (le16_to_cpu(dh->dqdh_entries) == V2_DQSTRINBLK-1) {
+			/* Insert will write block itself */
+			if ((ret = insert_free_dqentry(filp, info, buf, blk)) < 0) {
+				printk(KERN_ERR "VFS: Can't insert quota data block (%u) to free entry list.\n", blk);
+				goto out_buf;
+			}
+		}
+		else
+			if ((ret = write_blk(filp, blk, buf)) < 0) {
+				printk(KERN_ERR "VFS: Can't write quota data block %u\n", blk);
+				goto out_buf;
+			}
+	}
+	dquot->dq_off = 0;	/* Quota is now unattached */
+out_buf:
+	freedqbuf(buf);
+	return ret;
+}
+
+/* Remove reference to dquot from tree */
+static int remove_tree(struct dquot *dquot, uint *blk, int depth)
+{
+	struct file *filp = sb_dqopt(dquot->dq_sb)->files[dquot->dq_type];
+	struct mem_dqinfo *info = sb_dqopt(dquot->dq_sb)->info + dquot->dq_type;
+	dqbuf_t buf = getdqbuf();
+	int ret = 0;
+	uint newblk;
+	u32 *ref = (u32 *)buf;
+	
+	if (!buf)
+		return -ENOMEM;
+	if ((ret = read_blk(filp, *blk, buf)) < 0) {
+		printk(KERN_ERR "VFS: Can't read quota data block %u\n", *blk);
+		goto out_buf;
+	}
+	newblk = le32_to_cpu(ref[GETIDINDEX(dquot->dq_id, depth)]);
+	if (depth == V2_DQTREEDEPTH-1) {
+		ret = free_dqentry(dquot, newblk);
+		newblk = 0;
+	}
+	else
+		ret = remove_tree(dquot, &newblk, depth+1);
+	if (ret >= 0 && !newblk) {
+		int i;
+		ref[GETIDINDEX(dquot->dq_id, depth)] = cpu_to_le32(0);
+		for (i = 0; i < V2_DQBLKSIZE && !buf[i]; i++);	/* Block got empty? */
+		if (i == V2_DQBLKSIZE) {
+			put_free_dqblk(filp, info, buf, *blk);
+			*blk = 0;
+		}
+		else
+			if ((ret = write_blk(filp, *blk, buf)) < 0)
+				printk(KERN_ERR "VFS: Can't write quota tree block %u.\n", *blk);
+	}
+out_buf:
+	freedqbuf(buf);
+	return ret;	
+}
+
+/* Delete dquot from tree */
+static int v2_delete_dquot(struct dquot *dquot)
+{
+	uint tmp = V2_DQTREEOFF;
+
+	if (!dquot->dq_off)	/* Even not allocated? */
+		return 0;
+	return remove_tree(dquot, &tmp, 0);
+}
+
+/* Find entry in block */
+static loff_t find_block_dqentry(struct dquot *dquot, uint blk)
+{
+	struct file *filp = sb_dqopt(dquot->dq_sb)->files[dquot->dq_type];
+	dqbuf_t buf = getdqbuf();
+	loff_t ret = 0;
+	int i;
+	struct v2_disk_dqblk *ddquot = GETENTRIES(buf);
+
+	if (!buf)
+		return -ENOMEM;
+	if ((ret = read_blk(filp, blk, buf)) < 0) {
+		printk(KERN_ERR "VFS: Can't read quota tree block %u.\n", blk);
+		goto out_buf;
+	}
+	if (dquot->dq_id)
+		for (i = 0; i < V2_DQSTRINBLK && le32_to_cpu(ddquot[i].dqb_id) != dquot->dq_id; i++);
+	else {	/* ID 0 as a bit more complicated searching... */
+		struct v2_disk_dqblk fakedquot;
+
+		memset(&fakedquot, 0, sizeof(struct v2_disk_dqblk));
+		for (i = 0; i < V2_DQSTRINBLK; i++)
+			if (!le32_to_cpu(ddquot[i].dqb_id) && memcmp(&fakedquot, ddquot+i, sizeof(struct v2_disk_dqblk)))
+				break;
+	}
+	if (i == V2_DQSTRINBLK) {
+		printk(KERN_ERR "VFS: Quota for id %u referenced but not present.\n", dquot->dq_id);
+		ret = -EIO;
+		goto out_buf;
+	}
+	else
+		ret = (blk << V2_DQBLKSIZE_BITS) + sizeof(struct v2_disk_dqdbheader) + i * sizeof(struct v2_disk_dqblk);
+out_buf:
+	freedqbuf(buf);
+	return ret;
+}
+
+/* Find entry for given id in the tree */
+static loff_t find_tree_dqentry(struct dquot *dquot, uint blk, int depth)
+{
+	struct file *filp = sb_dqopt(dquot->dq_sb)->files[dquot->dq_type];
+	dqbuf_t buf = getdqbuf();
+	loff_t ret = 0;
+	u32 *ref = (u32 *)buf;
+
+	if (!buf)
+		return -ENOMEM;
+	if ((ret = read_blk(filp, blk, buf)) < 0) {
+		printk(KERN_ERR "VFS: Can't read quota tree block %u.\n", blk);
+		goto out_buf;
+	}
+	ret = 0;
+	blk = le32_to_cpu(ref[GETIDINDEX(dquot->dq_id, depth)]);
+	if (!blk)	/* No reference? */
+		goto out_buf;
+	if (depth < V2_DQTREEDEPTH-1)
+		ret = find_tree_dqentry(dquot, blk, depth+1);
+	else
+		ret = find_block_dqentry(dquot, blk);
+out_buf:
+	freedqbuf(buf);
+	return ret;
+}
+
+/* Find entry for given id in the tree - wrapper function */
+static inline loff_t find_dqentry(struct dquot *dquot)
+{
+	return find_tree_dqentry(dquot, V2_DQTREEOFF, 0);
+}
+
+static int v2_read_dquot(struct dquot *dquot)
+{
+	int type = dquot->dq_type;
+	struct file *filp;
+	mm_segment_t fs;
+	loff_t offset;
+	struct v2_disk_dqblk ddquot;
+	int ret = 0;
+
+	filp = sb_dqopt(dquot->dq_sb)->files[type];
+
+#ifdef __QUOTA_V2_PARANOIA
+	if (!filp || !dquot->dq_sb) {	/* Invalidated quota? */
+		printk(KERN_ERR "VFS: Quota invalidated while reading!\n");
+		return -EIO;
+	}
+#endif
+	offset = find_dqentry(dquot);
+	if (offset <= 0) {	/* Entry not present? */
+		if (offset < 0)
+			printk(KERN_ERR "VFS: Can't read quota structure for id %u.\n", dquot->dq_id);
+		dquot->dq_off = 0;
+		dquot->dq_flags |= DQ_FAKE;
+		memset(&dquot->dq_dqb, 0, sizeof(struct mem_dqblk));
+		ret = offset;
+	}
+	else {
+		dquot->dq_off = offset;
+		fs = get_fs();
+		set_fs(KERNEL_DS);
+		if ((ret = filp->f_op->read(filp, (char *)&ddquot, sizeof(struct v2_disk_dqblk), &offset)) != sizeof(struct v2_disk_dqblk)) {
+			if (ret >= 0)
+				ret = -EIO;
+			printk(KERN_ERR "VFS: Error while reading quota structure for id %u.\n", dquot->dq_id);
+			memset(&ddquot, 0, sizeof(struct v2_disk_dqblk));
+		}
+		else
+			ret = 0;
+		set_fs(fs);
+		disk2memdqb(&dquot->dq_dqb, &ddquot);
+	}
+	dqstats.reads++;
+	return ret;
+}
+
+/* Commit changes of dquot to disk - it might also mean deleting it when quota became fake one and user has no blocks... */
+static int v2_commit_dquot(struct dquot *dquot)
+{
+	/* We clear the flag everytime so we don't loop when there was an IO error... */
+	dquot->dq_flags &= ~DQ_MOD;
+	if (dquot->dq_flags & DQ_FAKE && !(dquot->dq_dqb.dqb_curinodes | dquot->dq_dqb.dqb_curspace))
+		return v2_delete_dquot(dquot);
+	else
+		return v2_write_dquot(dquot);
+}
+
+static struct quota_format_ops v2_format_ops = {
+	check_quota_file:	v2_check_quota_file,
+	read_file_info:		v2_read_file_info,
+	write_file_info:	v2_write_file_info,
+	free_file_info:		NULL,
+	read_dqblk:		v2_read_dquot,
+	commit_dqblk:		v2_commit_dquot,
+};
+
+static struct quota_format_type v2_quota_format = {
+	qf_fmt_id:	QFMT_VFS_V0,
+	qf_ops:		&v2_format_ops,
+	qf_owner:	THIS_MODULE
+};
+
+static int __init init_v2_quota_format(void)
+{
+	return register_quota_format(&v2_quota_format);
+}
+
+static void __exit exit_v2_quota_format(void)
+{
+	unregister_quota_format(&v2_quota_format);
+}
+
+EXPORT_NO_SYMBOLS;
+
+module_init(init_v2_quota_format);
+module_exit(exit_v2_quota_format);
diff -urN linux-2.4.22-pre3/fs/read_write.c linux-2.4.22-pre4/fs/read_write.c
--- linux-2.4.22-pre3/fs/read_write.c	2003-06-13 07:51:37.000000000 -0700
+++ linux-2.4.22-pre4/fs/read_write.c	2003-07-09 16:56:29.000000000 -0700
@@ -322,7 +322,7 @@
 	/* VERIFY_WRITE actually means a read, as we write to user space */
 	if ((ret + (type == VERIFY_WRITE)) > 0)
 		dnotify_parent(file->f_dentry,
-			(type == VERIFY_WRITE) ? DN_MODIFY : DN_ACCESS);
+			(type == VERIFY_WRITE) ? DN_ACCESS : DN_MODIFY);
 	return ret;
 }
 
diff -urN linux-2.4.22-pre3/fs/super.c linux-2.4.22-pre4/fs/super.c
--- linux-2.4.22-pre3/fs/super.c	2003-06-13 07:51:37.000000000 -0700
+++ linux-2.4.22-pre4/fs/super.c	2003-07-09 16:56:29.000000000 -0700
@@ -27,6 +27,7 @@
 #include <linux/devfs_fs_kernel.h>
 #include <linux/major.h>
 #include <linux/acct.h>
+#include <linux/quotaops.h>
 
 #include <asm/uaccess.h>
 
@@ -281,6 +282,8 @@
 		sema_init(&s->s_dquot.dqoff_sem, 1);
 		s->s_maxbytes = MAX_NON_LFS;
 		s->s_op = &empty_sops;
+		s->dq_op = sb_dquot_ops;
+		s->s_qcop = sb_quotactl_ops;
 	}
 	return s;
 }
diff -urN linux-2.4.22-pre3/include/asm-alpha/ioctls.h linux-2.4.22-pre4/include/asm-alpha/ioctls.h
--- linux-2.4.22-pre3/include/asm-alpha/ioctls.h	2002-11-28 15:53:15.000000000 -0800
+++ linux-2.4.22-pre4/include/asm-alpha/ioctls.h	2003-07-09 16:56:29.000000000 -0700
@@ -9,6 +9,7 @@
 #define FIONBIO		_IOW('f', 126, int)
 #define FIONREAD	_IOR('f', 127, int)
 #define TIOCINQ		FIONREAD
+#define FIOQSIZE	_IOR('f', 128, loff_t)
 
 #define TIOCGETP	_IOR('t', 8, struct sgttyb)
 #define TIOCSETP	_IOW('t', 9, struct sgttyb)
diff -urN linux-2.4.22-pre3/include/asm-cris/atomic.h linux-2.4.22-pre4/include/asm-cris/atomic.h
--- linux-2.4.22-pre3/include/asm-cris/atomic.h	2003-06-13 07:51:38.000000000 -0700
+++ linux-2.4.22-pre4/include/asm-cris/atomic.h	2003-07-09 16:56:29.000000000 -0700
@@ -1,5 +1,3 @@
-/* $Id: atomic.h,v 1.4 2002/11/20 18:20:17 hp Exp $ */
-
 #ifndef __ASM_CRIS_ATOMIC__
 #define __ASM_CRIS_ATOMIC__
 
diff -urN linux-2.4.22-pre3/include/asm-cris/bitops.h linux-2.4.22-pre4/include/asm-cris/bitops.h
--- linux-2.4.22-pre3/include/asm-cris/bitops.h	2003-06-13 07:51:38.000000000 -0700
+++ linux-2.4.22-pre4/include/asm-cris/bitops.h	2003-07-09 16:56:29.000000000 -0700
@@ -42,6 +42,7 @@
  */
 
 #define set_bit(nr, addr)    (void)test_and_set_bit(nr, addr)
+#define __set_bit(nr, addr)    (void)__test_and_set_bit(nr, addr)
 
 /*
  * clear_bit - Clears a bit in memory
@@ -55,6 +56,7 @@
  */
 
 #define clear_bit(nr, addr)  (void)test_and_clear_bit(nr, addr)
+#define __clear_bit(nr, addr)  (void)__test_and_clear_bit(nr, addr)
 
 /*
  * change_bit - Toggle a bit in memory
@@ -105,6 +107,18 @@
 	return retval;
 }
 
+extern inline int __test_and_set_bit(int nr, void *addr)
+{
+	unsigned int mask, retval;
+	unsigned int *adr = (unsigned int *)addr;
+	
+	adr += nr >> 5;
+	mask = 1 << (nr & 0x1f);
+	retval = (mask & *adr) != 0;
+	*adr |= mask;
+	return retval;
+}
+
 /*
  * clear_bit() doesn't provide any barrier for the compiler.
  */
diff -urN linux-2.4.22-pre3/include/asm-cris/bugs.h linux-2.4.22-pre4/include/asm-cris/bugs.h
--- linux-2.4.22-pre3/include/asm-cris/bugs.h	2001-02-08 16:32:44.000000000 -0800
+++ linux-2.4.22-pre4/include/asm-cris/bugs.h	2003-07-09 16:56:29.000000000 -0700
@@ -1,8 +1,7 @@
-/* $Id: bugs.h,v 1.2 2001/01/17 17:03:18 bjornw Exp $
- *
+/*
  *  include/asm-cris/bugs.h
  *
- *  Copyright (C) 2001 Axis Communications AB
+ *  Copyright (C) 2001, 2002, 2003 Axis Communications AB
  */
 
 /*
diff -urN linux-2.4.22-pre3/include/asm-cris/byteorder.h linux-2.4.22-pre4/include/asm-cris/byteorder.h
--- linux-2.4.22-pre3/include/asm-cris/byteorder.h	2003-06-13 07:51:38.000000000 -0700
+++ linux-2.4.22-pre4/include/asm-cris/byteorder.h	2003-07-09 16:56:29.000000000 -0700
@@ -1,5 +1,3 @@
-/* $Id: byteorder.h,v 1.2 2002/11/20 18:20:17 hp Exp $ */
-
 #ifndef _CRIS_BYTEORDER_H
 #define _CRIS_BYTEORDER_H
 
@@ -15,14 +13,14 @@
 extern __inline__ __const__ __u32 ___arch__swab32(__u32 x)
 {
 	__asm__ ("swapwb %0" : "=r" (x) : "0" (x));
-  
+
 	return(x);
 }
 
 extern __inline__ __const__ __u16 ___arch__swab16(__u16 x)
 {
 	__asm__ ("swapb %0" : "=r" (x) : "0" (x));
-	
+
 	return(x);
 }
 
@@ -43,5 +41,3 @@
 #include <linux/byteorder/little_endian.h>
 
 #endif
-
-
diff -urN linux-2.4.22-pre3/include/asm-cris/delay.h linux-2.4.22-pre4/include/asm-cris/delay.h
--- linux-2.4.22-pre3/include/asm-cris/delay.h	2001-10-08 11:43:54.000000000 -0700
+++ linux-2.4.22-pre4/include/asm-cris/delay.h	2003-07-09 16:56:29.000000000 -0700
@@ -39,6 +39,13 @@
 	__delay(usecs * loops_per_usec);
 }
 
+/* ETRAX 100 is really too slow to sleep for nanosecs. */
+/* Divide with 1024 to avoid a real division that would take >1 us... */
+extern __inline__ void ndelay(unsigned long nsecs)
+{
+	__delay(1 + nsecs * loops_per_usec / 1024);
+}
+
 #endif /* defined(_CRIS_DELAY_H) */
 
 
diff -urN linux-2.4.22-pre3/include/asm-cris/dma.h linux-2.4.22-pre4/include/asm-cris/dma.h
--- linux-2.4.22-pre3/include/asm-cris/dma.h	2001-07-04 11:50:39.000000000 -0700
+++ linux-2.4.22-pre4/include/asm-cris/dma.h	2003-07-09 16:56:29.000000000 -0700
@@ -1,4 +1,4 @@
-/* $Id: dma.h,v 1.2 2001/05/09 12:17:42 johana Exp $
+/*
  * linux/include/asm/dma.h: Defines for using and allocating dma channels.
  */
 
diff -urN linux-2.4.22-pre3/include/asm-cris/eshlibld.h linux-2.4.22-pre4/include/asm-cris/eshlibld.h
--- linux-2.4.22-pre3/include/asm-cris/eshlibld.h	2001-04-06 10:51:19.000000000 -0700
+++ linux-2.4.22-pre4/include/asm-cris/eshlibld.h	2003-07-09 16:56:29.000000000 -0700
@@ -9,10 +9,9 @@
 *!
 *!---------------------------------------------------------------------------
 *!
-*! (C) Copyright 1998, 1999 Axis Communications AB, LUND, SWEDEN
+*! (C) Copyright 1998-2003 Axis Communications AB, LUND, SWEDEN
 *!
 *!**************************************************************************/
-/* $Id: eshlibld.h,v 1.2 2001/02/23 13:47:33 bjornw Exp $ */
 
 #ifndef _cris_relocate_h
 #define _cris_relocate_h
@@ -111,4 +110,3 @@
 
 #endif _cris_relocate_h
 /********************** END OF FILE eshlibld.h *****************************/
-
diff -urN linux-2.4.22-pre3/include/asm-cris/etraxgpio.h linux-2.4.22-pre4/include/asm-cris/etraxgpio.h
--- linux-2.4.22-pre3/include/asm-cris/etraxgpio.h	2002-11-28 15:53:15.000000000 -0800
+++ linux-2.4.22-pre4/include/asm-cris/etraxgpio.h	2003-07-09 16:56:29.000000000 -0700
@@ -1,4 +1,3 @@
-/* $Id: etraxgpio.h,v 1.8 2002/06/17 15:53:07 johana Exp $ */
 /*
  * The following devices are accessable using this driver using
  * GPIO_MAJOR (120) and a couple of minor numbers:
@@ -6,11 +5,12 @@
  * /dev/gpiob  minor 1, 8 bit GPIO, each bit can change direction
  * /dev/leds   minor 2, Access to leds depending on kernelconfig
  * /dev/gpiog  minor 3
-         g0dir, g8_15dir, g16_23dir, g24 dir configurable in R_GEN_CONFIG
-         g1-g7 and g25-g31 is both input and outputs but on different pins
-         Also note that some bits change pins depending on what interfaces 
-         are enabled.
+ *       g0dir, g8_15dir, g16_23dir, g24 dir configurable in R_GEN_CONFIG
+ *       g1-g7 and g25-g31 is both input and outputs but on different pins
+ *       Also note that some bits change pins depending on what interfaces
+ *       are enabled.
  */
+
 #ifndef _ASM_ETRAXGPIO_H
 #define _ASM_ETRAXGPIO_H
 
@@ -41,7 +41,7 @@
 
 /* GPIO direction ioctl's */
 #define IO_READDIR    0x8  /* Read direction 0=input 1=output  (obsolete) */
-#define IO_SETINPUT   0x9  /* Set direction for bits set, 0=unchanged 1=input, 
+#define IO_SETINPUT   0x9  /* Set direction for bits set, 0=unchanged 1=input,
                               returns mask with current inputs (obsolete) */
 #define IO_SETOUTPUT  0xA  /* Set direction for bits set, 0=unchanged 1=output,
                               returns mask with current outputs (obsolete)*/
@@ -55,11 +55,11 @@
 #define IO_GET_PWR_BT 0xE
 
 /* Bit toggling in driver settings */
-/* bit set in low byte0 is CLK mask (0x00FF), 
-   bit set in byte1 is DATA mask    (0xFF00) 
+/* bit set in low byte0 is CLK mask (0x00FF),
+   bit set in byte1 is DATA mask    (0xFF00)
    msb, data_mask[7:0] , clk_mask[7:0]
  */
-#define IO_CFG_WRITE_MODE 0xF 
+#define IO_CFG_WRITE_MODE 0xF
 #define IO_CFG_WRITE_MODE_VALUE(msb, data_mask, clk_mask) \
   ( (((msb)&1) << 16) | (((data_mask) &0xFF) << 8) | ((clk_mask) & 0xFF) )
 
@@ -76,6 +76,4 @@
                                * *arg updated with current output pins.
                                */
 
-
-
 #endif
diff -urN linux-2.4.22-pre3/include/asm-cris/etraxi2c.h linux-2.4.22-pre4/include/asm-cris/etraxi2c.h
--- linux-2.4.22-pre3/include/asm-cris/etraxi2c.h	2001-04-06 10:51:19.000000000 -0700
+++ linux-2.4.22-pre4/include/asm-cris/etraxi2c.h	2003-07-09 16:56:29.000000000 -0700
@@ -1,5 +1,3 @@
-/* $Id: etraxi2c.h,v 1.1 2001/01/18 15:49:57 bjornw Exp $ */
-
 #ifndef _LINUX_ETRAXI2C_H
 #define _LINUX_ETRAXI2C_H
 
diff -urN linux-2.4.22-pre3/include/asm-cris/etraxvirtex.h linux-2.4.22-pre4/include/asm-cris/etraxvirtex.h
--- linux-2.4.22-pre3/include/asm-cris/etraxvirtex.h	2002-11-28 15:53:15.000000000 -0800
+++ linux-2.4.22-pre4/include/asm-cris/etraxvirtex.h	2003-07-09 16:56:29.000000000 -0700
@@ -1,5 +1,3 @@
-/* $Id: etraxvirtex.h,v 1.1 2002/06/25 10:01:05 stefanl Exp $ */
-
 #ifndef _LINUX_ETRAXVIRTEX_FPGA_H
 #define _LINUX_ETRAXVIRTEX_FPGA_H
 
diff -urN linux-2.4.22-pre3/include/asm-cris/fasttimer.h linux-2.4.22-pre4/include/asm-cris/fasttimer.h
--- linux-2.4.22-pre3/include/asm-cris/fasttimer.h	2002-11-28 15:53:15.000000000 -0800
+++ linux-2.4.22-pre4/include/asm-cris/fasttimer.h	2003-07-09 16:56:29.000000000 -0700
@@ -1,16 +1,17 @@
-/* $Id: fasttimer.h,v 1.2 2002/05/28 17:45:34 johana Exp $
+/*
  * linux/include/asm-cris/fasttimer.h
  *
  * Fast timers for ETRAX100LX
  * This may be useful in other OS than Linux so use 2 space indentation...
- * Copyright (C) 2000, 2002 Axis Communications AB
+ * Copyright (C) 2000, 2001, 2002, 2003 Axis Communications AB
  */
+
 #include <linux/config.h>
 #include <linux/time.h> /* struct timeval */
 #include <linux/timex.h>
 
-/* The timer0 values gives 52us resolution (1/19200) or higher 
- * but interrupts at HZ 
+/* The timer0 values gives 52us resolution (1/19200) or higher
+ * but interrupts at HZ
  */
 /* We use timer1 to generate interrupts at desired times. */
 
@@ -18,7 +19,7 @@
 
 typedef void fast_timer_function_type(unsigned long);
 
-struct fast_timer{ /* Close to timer_list */
+struct fast_timer { /* Close to timer_list */
   struct fast_timer *next;
   struct fast_timer *prev;
   struct timeval tv_set;
@@ -29,6 +30,8 @@
   const char *name;
 };
 
+extern struct fast_timer *fast_timer_list;
+
 void start_one_shot_timer(struct fast_timer *t,
                           fast_timer_function_type *function,
                           unsigned long data,
@@ -38,6 +41,10 @@
 int del_fast_timer(struct fast_timer * t);
 /* return 1 if deleted */
 
+extern inline int fast_timer_pending (const struct fast_timer * t)
+{
+  return (t->next != NULL) || (t->prev != NULL) || (t == fast_timer_list);
+}
 
 void schedule_usleep(unsigned long us);
 
diff -urN linux-2.4.22-pre3/include/asm-cris/ide.h linux-2.4.22-pre4/include/asm-cris/ide.h
--- linux-2.4.22-pre3/include/asm-cris/ide.h	2003-06-13 07:51:38.000000000 -0700
+++ linux-2.4.22-pre4/include/asm-cris/ide.h	2003-07-09 16:56:29.000000000 -0700
@@ -17,6 +17,9 @@
 #ifdef __KERNEL__
 
 #include <asm/svinto.h>
+#include <asm/io.h>
+#include <asm-generic/ide_iops.h>
+
 
 /* ETRAX 100 can support 4 IDE busses on the same pins (serialized) */
 
diff -urN linux-2.4.22-pre3/include/asm-cris/io.h linux-2.4.22-pre4/include/asm-cris/io.h
--- linux-2.4.22-pre3/include/asm-cris/io.h	2003-06-13 07:51:38.000000000 -0700
+++ linux-2.4.22-pre4/include/asm-cris/io.h	2003-07-09 16:56:29.000000000 -0700
@@ -258,6 +258,8 @@
 
 #define IO_SPACE_LIMIT 0xffff
 #define inb(x) (0)
+#define inw(x) (0)
+#define inl(x) (0)
 #define outb(x,y)
 #define outw(x,y)
 #define outl(x,y)
diff -urN linux-2.4.22-pre3/include/asm-cris/ioctls.h linux-2.4.22-pre4/include/asm-cris/ioctls.h
--- linux-2.4.22-pre3/include/asm-cris/ioctls.h	2002-08-02 17:39:45.000000000 -0700
+++ linux-2.4.22-pre4/include/asm-cris/ioctls.h	2003-07-09 16:56:29.000000000 -0700
@@ -69,6 +69,7 @@
 #define TIOCGICOUNT	0x545D	/* read serial port inline interrupt counts */
 #define TIOCGHAYESESP   0x545E  /* Get Hayes ESP configuration */
 #define TIOCSHAYESESP   0x545F  /* Set Hayes ESP configuration */
+#define FIOQSIZE	0x5460
 
 #define TIOCSERSETRS485 0x5460  /* enable rs-485 */
 #define TIOCSERWRRS485  0x5461  /* write rs-485 */
diff -urN linux-2.4.22-pre3/include/asm-cris/irq.h linux-2.4.22-pre4/include/asm-cris/irq.h
--- linux-2.4.22-pre3/include/asm-cris/irq.h	2002-02-25 11:38:11.000000000 -0800
+++ linux-2.4.22-pre4/include/asm-cris/irq.h	2003-07-09 16:56:29.000000000 -0700
@@ -1,7 +1,7 @@
 /*
  * Interrupt handling assembler and defines for Linux/CRIS
  *
- * Copyright (c) 2000, 2001 Axis Communications AB
+ * Copyright (c) 2000, 2001, 2002, 2003 Axis Communications AB
  *
  */
 
@@ -18,60 +18,60 @@
 #include <asm/sv_addr_ag.h>
 
 #define NR_IRQS 32
-#define SOME_IRQ_NBR        IO_BITNR(R_VECT_MASK_RD, some)   /* 0 ? */
-#define NMI_IRQ_NBR         IO_BITNR(R_VECT_MASK_RD, nmi)    /* 1 */
-#define TIMER0_IRQ_NBR      IO_BITNR(R_VECT_MASK_RD, timer0) /* 2 */
-#define TIMER1_IRQ_NBR      IO_BITNR(R_VECT_MASK_RD, timer1) /* 3 */
+#define SOME_IRQ_NBR           IO_BITNR(R_VECT_MASK_RD, some)   /* 0 ? */
+#define NMI_IRQ_NBR            IO_BITNR(R_VECT_MASK_RD, nmi)    /* 1 */
+#define TIMER0_IRQ_NBR         IO_BITNR(R_VECT_MASK_RD, timer0) /* 2 */
+#define TIMER1_IRQ_NBR         IO_BITNR(R_VECT_MASK_RD, timer1) /* 3 */
 /* mio, ata, par0, scsi0 on 4 */
 /* par1, scsi1 on 5 */
 #define NETWORK_STATUS_IRQ_NBR IO_BITNR(R_VECT_MASK_RD, network) /* 6 */
 
-#define SERIAL_IRQ_NBR IO_BITNR(R_VECT_MASK_RD, serial) /* 8 */
-#define PA_IRQ_NBR IO_BITNR(R_VECT_MASK_RD, pa) /* 11 */
+#define SERIAL_IRQ_NBR         IO_BITNR(R_VECT_MASK_RD, serial) /* 8 */
+#define PA_IRQ_NBR             IO_BITNR(R_VECT_MASK_RD, pa) /* 11 */
 /* extdma0 and extdma1 is at irq 12 and 13 and/or same as dma5 and dma6 ? */
-#define EXTDMA0_IRQ_NBR IO_BITNR(R_VECT_MASK_RD, ext_dma0)
-#define EXTDMA1_IRQ_NBR IO_BITNR(R_VECT_MASK_RD, ext_dma1)
+#define EXTDMA0_IRQ_NBR        IO_BITNR(R_VECT_MASK_RD, ext_dma0)
+#define EXTDMA1_IRQ_NBR        IO_BITNR(R_VECT_MASK_RD, ext_dma1)
+
+#define BUS_FAULT_IRQ_NBR      14
+#define MULTIPLE_IRQ_NBR       15
 
 /* dma0-9 is irq 16..25 */
 /* 16,17: network */
-#define DMA0_TX_IRQ_NBR IO_BITNR(R_VECT_MASK_RD, dma0)
-#define DMA1_RX_IRQ_NBR IO_BITNR(R_VECT_MASK_RD, dma1)
+#define DMA0_TX_IRQ_NBR        IO_BITNR(R_VECT_MASK_RD, dma0)
+#define DMA1_RX_IRQ_NBR        IO_BITNR(R_VECT_MASK_RD, dma1)
 #define NETWORK_DMA_TX_IRQ_NBR DMA0_TX_IRQ_NBR
 #define NETWORK_DMA_RX_IRQ_NBR DMA1_RX_IRQ_NBR
 
 /* 18,19: dma2 and dma3 shared by par0, scsi0, ser2 and ata */
-#define DMA2_TX_IRQ_NBR IO_BITNR(R_VECT_MASK_RD, dma2)
-#define DMA3_RX_IRQ_NBR IO_BITNR(R_VECT_MASK_RD, dma3)
-#define SER2_DMA_TX_IRQ_NBR DMA2_TX_IRQ_NBR
-#define SER2_DMA_RX_IRQ_NBR DMA3_RX_IRQ_NBR
+#define DMA2_TX_IRQ_NBR        IO_BITNR(R_VECT_MASK_RD, dma2)
+#define DMA3_RX_IRQ_NBR        IO_BITNR(R_VECT_MASK_RD, dma3)
+#define SER2_DMA_TX_IRQ_NBR    DMA2_TX_IRQ_NBR
+#define SER2_DMA_RX_IRQ_NBR    DMA3_RX_IRQ_NBR
 
 /* 20,21: dma4 and dma5 shared by par1, scsi1, ser3 and extdma0 */
-#define DMA4_TX_IRQ_NBR IO_BITNR(R_VECT_MASK_RD, dma4)
-#define DMA5_RX_IRQ_NBR IO_BITNR(R_VECT_MASK_RD, dma5)
-#define SER3_DMA_TX_IRQ_NBR DMA4_TX_IRQ_NBR
-#define SER3_DMA_RX_IRQ_NBR DMA5_RX_IRQ_NBR
+#define DMA4_TX_IRQ_NBR        IO_BITNR(R_VECT_MASK_RD, dma4)
+#define DMA5_RX_IRQ_NBR        IO_BITNR(R_VECT_MASK_RD, dma5)
+#define SER3_DMA_TX_IRQ_NBR    DMA4_TX_IRQ_NBR
+#define SER3_DMA_RX_IRQ_NBR    DMA5_RX_IRQ_NBR
 
 /* 22,23: dma6 and dma7 shared by ser0, extdma1 and mem2mem */
-#define DMA6_TX_IRQ_NBR IO_BITNR(R_VECT_MASK_RD, dma6)
-#define DMA7_RX_IRQ_NBR IO_BITNR(R_VECT_MASK_RD, dma7)
-#define SER0_DMA_TX_IRQ_NBR DMA6_TX_IRQ_NBR
-#define SER0_DMA_RX_IRQ_NBR DMA7_RX_IRQ_NBR
+#define DMA6_TX_IRQ_NBR        IO_BITNR(R_VECT_MASK_RD, dma6)
+#define DMA7_RX_IRQ_NBR        IO_BITNR(R_VECT_MASK_RD, dma7)
+#define SER0_DMA_TX_IRQ_NBR    DMA6_TX_IRQ_NBR
+#define SER0_DMA_RX_IRQ_NBR    DMA7_RX_IRQ_NBR
 #define MEM2MEM_DMA_TX_IRQ_NBR DMA6_TX_IRQ_NBR
 #define MEM2MEM_DMA_RX_IRQ_NBR DMA7_RX_IRQ_NBR
 
 /* 24,25: dma8 and dma9 shared by ser1 and usb */
-#define DMA8_TX_IRQ_NBR IO_BITNR(R_VECT_MASK_RD, dma8)
-#define DMA9_RX_IRQ_NBR IO_BITNR(R_VECT_MASK_RD, dma9)
-#define SER1_DMA_TX_IRQ_NBR DMA8_TX_IRQ_NBR
-#define SER1_DMA_RX_IRQ_NBR DMA9_RX_IRQ_NBR
-#define USB_DMA_TX_IRQ_NBR DMA8_TX_IRQ_NBR
-#define USB_DMA_RX_IRQ_NBR DMA9_RX_IRQ_NBR
+#define DMA8_TX_IRQ_NBR        IO_BITNR(R_VECT_MASK_RD, dma8)
+#define DMA9_RX_IRQ_NBR        IO_BITNR(R_VECT_MASK_RD, dma9)
+#define SER1_DMA_TX_IRQ_NBR    DMA8_TX_IRQ_NBR
+#define SER1_DMA_RX_IRQ_NBR    DMA9_RX_IRQ_NBR
+#define USB_DMA_TX_IRQ_NBR     DMA8_TX_IRQ_NBR
+#define USB_DMA_RX_IRQ_NBR     DMA9_RX_IRQ_NBR
 
 /* usb: controller at irq 31 + uses DMA8 and DMA9 */
-#define USB_HC_IRQ_NBR IO_BITNR(R_VECT_MASK_RD, usb)
-
-
-
+#define USB_HC_IRQ_NBR         IO_BITNR(R_VECT_MASK_RD, usb)
 
 
 extern void disable_irq(unsigned int);
@@ -94,7 +94,7 @@
 
 #define __STR(x) #x
 #define STR(x) __STR(x)
- 
+
 /* SAVE_ALL saves registers so they match pt_regs */
 
 #define SAVE_ALL \
@@ -102,34 +102,34 @@
   "push $srp\n\t"       /* push subroutine return pointer */ \
   "push $dccr\n\t"      /* push condition codes */ \
   "push $mof\n\t"       /* push multiply overflow reg */ \
-  "di\n\t"             /* need to disable irq's at this point */\
+  "di\n\t"              /* need to disable irq's at this point */\
   "subq 14*4,$sp\n\t"   /* make room for r0-r13 */ \
   "movem $r13,[$sp]\n\t" /* push the r0-r13 registers */ \
   "push $r10\n\t"       /* push orig_r10 */ \
   "clear.d [$sp=$sp-4]\n\t"  /* frametype - this is a normal stackframe */
 
-  /* BLOCK_IRQ and UNBLOCK_IRQ do the same as mask_irq and unmask_irq in irq.c */
+/* BLOCK_IRQ and UNBLOCK_IRQ do the same as mask_irq and unmask_irq in irq.c */
 
 #define BLOCK_IRQ(mask,nr) \
   "move.d " #mask ",$r0\n\t" \
-  "move.d $r0,[0xb00000d8]\n\t" 
-  
+  "move.d $r0,[0xb00000d8]\n\t"
+
 #define UNBLOCK_IRQ(mask) \
   "move.d " #mask ",$r0\n\t" \
-  "move.d $r0,[0xb00000dc]\n\t" 
+  "move.d $r0,[0xb00000dc]\n\t"
 
 #define IRQ_NAME2(nr) nr##_interrupt(void)
 #define IRQ_NAME(nr) IRQ_NAME2(IRQ##nr)
 #define sIRQ_NAME(nr) IRQ_NAME2(sIRQ##nr)
 #define BAD_IRQ_NAME(nr) IRQ_NAME2(bad_IRQ##nr)
 
-  /* the asm IRQ handler makes sure the causing IRQ is blocked, then it calls
-   * do_IRQ (with irq disabled still). after that it unblocks and jumps to
-   * ret_from_intr (entry.S)
-   *
-   * The reason the IRQ is blocked is to allow an sti() before the handler which
-   * will acknowledge the interrupt is run.
-   */
+/* the asm IRQ handler makes sure the causing IRQ is blocked, then it calls
+ * do_IRQ (with irq disabled still). after that it unblocks and jumps to
+ * ret_from_intr (entry.S)
+ *
+ * The reason the IRQ is blocked is to allow an sti() before the handler which
+ * will acknowledge the interrupt is run.
+ */
 
 #define BUILD_IRQ(nr,mask) \
 void IRQ_NAME(nr); \
@@ -154,19 +154,20 @@
           "reti\n\t" \
           "nop\n");
 
-/* This is subtle. The timer interrupt is crucial and it should not be disabled for 
- * too long. However, if it had been a normal interrupt as per BUILD_IRQ, it would
- * have been BLOCK'ed, and then softirq's are run before we return here to UNBLOCK.
- * If the softirq's take too much time to run, the timer irq won't run and the 
- * watchdog will kill us.
+/* This is subtle. The timer interrupt is crucial and it should not be disabled
+ * for too long. However, if it had been a normal interrupt as per BUILD_IRQ,
+ * it would have been BLOCK'ed, and then softirq's are run before we return
+ * here to UNBLOCK. If the softirq's take too much time to run, the timer irq
+ * won't run and the watchdog will kill us.
  *
- * Furthermore, if a lot of other irq's occur before we return here, the multiple_irq
- * handler is run and it prioritizes the timer interrupt. However if we had BLOCK'ed
- * it here, we would not get the multiple_irq at all.
+ * Furthermore, if a lot of other irq's occur before we return here, the
+ * multiple_irq handler is run and it prioritizes the timer interrupt. However
+ * if we had BLOCK'ed it here, we would not get the multiple_irq at all.
  *
- * The non-blocking here is based on the knowledge that the timer interrupt is 
- * registred as a fast interrupt (SA_INTERRUPT) so that we _know_ there will not
- * be an sti() before the timer irq handler is run to acknowledge the interrupt.
+ * The non-blocking here is based on the knowledge that the timer interrupt is
+ * registred as a fast interrupt (SA_INTERRUPT) so that we _know_ there will
+ * not be an sti() before the timer irq handler is run to acknowledge the
+ * interrupt.
  */
 
 #define BUILD_TIMER_IRQ(nr,mask) \
@@ -191,5 +192,3 @@
           "nop\n");
 
 #endif  /* _ASM_IRQ_H */
-
-
diff -urN linux-2.4.22-pre3/include/asm-cris/namei.h linux-2.4.22-pre4/include/asm-cris/namei.h
--- linux-2.4.22-pre3/include/asm-cris/namei.h	2001-02-08 16:32:44.000000000 -0800
+++ linux-2.4.22-pre4/include/asm-cris/namei.h	2003-07-09 16:56:29.000000000 -0700
@@ -1,4 +1,4 @@
-/* $Id: namei.h,v 1.1 2000/07/10 16:32:31 bjornw Exp $
+/*
  * linux/include/asm-cris/namei.h
  *
  * Included from linux/fs/namei.c
@@ -9,7 +9,7 @@
 
 /* used to find file-system prefixes for doing emulations
  * see for example asm-sparc/namei.h
- * we dont use it...
+ * we don't use it...
  */
 
 #define __emul_prefix() NULL
diff -urN linux-2.4.22-pre3/include/asm-cris/pci.h linux-2.4.22-pre4/include/asm-cris/pci.h
--- linux-2.4.22-pre3/include/asm-cris/pci.h	2001-02-08 16:32:44.000000000 -0800
+++ linux-2.4.22-pre4/include/asm-cris/pci.h	2003-07-09 16:56:29.000000000 -0700
@@ -5,5 +5,6 @@
  * includes it even if CONFIG_PCI is not set.
  */
 
+#define PCI_DMA_BUS_IS_PHYS	(0)
 #endif /* __ASM_CRIS_PCI_H */
 
diff -urN linux-2.4.22-pre3/include/asm-cris/posix_types.h linux-2.4.22-pre4/include/asm-cris/posix_types.h
--- linux-2.4.22-pre3/include/asm-cris/posix_types.h	2003-06-13 07:51:38.000000000 -0700
+++ linux-2.4.22-pre4/include/asm-cris/posix_types.h	2003-07-09 16:56:29.000000000 -0700
@@ -1,5 +1,3 @@
-/* $Id: posix_types.h,v 1.2 2002/09/26 14:44:59 hp Exp $ */
-
 /* We cheat a bit and use our C-coded bitops functions from asm/bitops.h */
 /* I guess we should write these in assembler because they are used often. */
 
diff -urN linux-2.4.22-pre3/include/asm-cris/processor.h linux-2.4.22-pre4/include/asm-cris/processor.h
--- linux-2.4.22-pre3/include/asm-cris/processor.h	2003-06-13 07:51:38.000000000 -0700
+++ linux-2.4.22-pre4/include/asm-cris/processor.h	2003-07-09 16:56:29.000000000 -0700
@@ -65,7 +65,7 @@
 /*
  * At user->kernel entry, the pt_regs struct is stacked on the top of the kernel-stack.
  * This macro allows us to find those regs for a task.
- * Notice that subsequent pt_regs stackings, like recursive interrupts occuring while
+ * Notice that subsequent pt_regs stackings, like recursive interrupts occurring while
  * we're in the kernel, won't affect this - only the first user->kernel transition
  * registers are reached by this.
  */
diff -urN linux-2.4.22-pre3/include/asm-cris/rs485.h linux-2.4.22-pre4/include/asm-cris/rs485.h
--- linux-2.4.22-pre3/include/asm-cris/rs485.h	2002-08-02 17:39:45.000000000 -0700
+++ linux-2.4.22-pre4/include/asm-cris/rs485.h	2003-07-09 16:56:29.000000000 -0700
@@ -5,7 +5,7 @@
 struct rs485_control {
         unsigned short rts_on_send;
         unsigned short rts_after_sent;
-        __u32 delay_rts_before_send;
+        unsigned long delay_rts_before_send;
         unsigned short enabled;
 #ifdef __KERNEL__
         int disable_serial_loopback;
@@ -14,7 +14,7 @@
 
 /* Used with ioctl() TIOCSERWRRS485 */
 struct rs485_write {
-        __u16 outc_size;
+        unsigned short outc_size;
         unsigned char *outc;
 };
 
diff -urN linux-2.4.22-pre3/include/asm-cris/rtc.h linux-2.4.22-pre4/include/asm-cris/rtc.h
--- linux-2.4.22-pre3/include/asm-cris/rtc.h	2003-06-13 07:51:38.000000000 -0700
+++ linux-2.4.22-pre4/include/asm-cris/rtc.h	2003-07-09 16:56:29.000000000 -0700
@@ -1,9 +1,6 @@
-/* $Id: rtc.h,v 1.8 2002/11/20 14:27:25 oskarp Exp $ */
-
 #ifndef __RTC_H__
 #define __RTC_H__
 
-
 #include <linux/config.h>
 
 #ifdef CONFIG_ETRAX_DS1302
@@ -20,13 +17,13 @@
    /* Bits in CONTROL register. */
 #  define RTC_CONTROL_WRITEPROTECT 	0x80
 #  define RTC_TRICKLECHARGER 		8
-  
+
   /* Bits in TRICKLECHARGER register TCS TCS TCS TCS DS DS RS RS. */
 #  define RTC_TCR_PATTERN 	0xA0	/* 1010xxxx */
 #  define RTC_TCR_1DIOD 	0x04	/* xxxx01xx */
 #  define RTC_TCR_2DIOD 	0x08	/* xxxx10xx */
 #  define RTC_TCR_DISABLED 	0x00	/* xxxxxx00 Disabled */
-#  define RTC_TCR_2KOHM 	0x01	/* xxxxxx01 2KOhm */
+#  define RTC_TCR_2KOHM 	0x01	/* xxxxxx01 2kOhm */
 #  define RTC_TCR_4KOHM 	0x02	/* xxxxxx10 4kOhm */
 #  define RTC_TCR_8KOHM 	0x03	/* xxxxxx11 8kOhm */
 
@@ -36,11 +33,11 @@
 #  define RTC_I2C_WRITE		0xa2
 
    /* Phillips PCF8563 registers. */
-#  define RTC_CONTROL1		0x00		/* Control/Status register 1. */
-#  define RTC_CONTROL2		0x01		/* Control/Status register 2. */
-#  define RTC_CLOCKOUT_FREQ	0x0d		/* CLKOUT frequency. */
-#  define RTC_TIMER_CONTROL	0x0e		/* Timer control. */
-#  define RTC_TIMER_CNTDOWN	0x0f		/* Timer countdown. */
+#  define RTC_CONTROL1		0x00	/* Control/Status register 1. */
+#  define RTC_CONTROL2		0x01	/* Control/Status register 2. */
+#  define RTC_CLOCKOUT_FREQ	0x0d	/* CLKOUT frequency. */
+#  define RTC_TIMER_CONTROL	0x0e	/* Timer control. */
+#  define RTC_TIMER_CNTDOWN	0x0f	/* Timer countdown. */
 
    /* BCD encoded clock registers. */
 #  define RTC_SECONDS		0x02
@@ -53,7 +50,26 @@
 #  define RTC_MINUTE_ALARM	0x09
 #  define RTC_HOUR_ALARM	0x0a
 #  define RTC_DAY_ALARM		0x0b
-#  define RTC_WEEKDAY_ALARM 0x0c	/* Not coded in BCD! */
+#  define RTC_WEEKDAY_ALARM	0x0c	/* Not coded in BCD! */
+
+/* Control/Status 2 bits */
+#  define RTC_CTRL2_TIE		0x01	/* Timer Interrupt Enable */
+#  define RTC_CTRL2_AIE		0x02	/* Alarm Interrupt Enable */
+#  define RTC_CTRL2_TF		0x04	/* Timer Flag */
+#  define RTC_CTRL2_AF		0x08	/* Alarm Flag */
+#  define RTC_CTRL2_TP		0x10	/* Timer Pulse */
+
+/* Timer Control bits */
+#  define RTC_TC_TD0		0x01
+#  define RTC_TC_TD1		0x02
+#  define RTC_TC_TE		0x80	/* Timer Enable */
+
+/* Timer source clock frequency */
+#  define RTC_TC_FREQ_MASK	(RTC_TC_TD0 | RTC_TC_TD1)
+#  define RTC_TC_FREQ_4096	(0)
+#  define RTC_TC_FREQ_64	(RTC_TC_TD0)
+#  define RTC_TC_FREQ_1		(RTC_TC_TD1)
+#  define RTC_TC_FREQ_1_60	(RTC_TC_TD0 | RTC_TC_TD1)
 
 #endif
 
@@ -84,7 +100,7 @@
 
 /*
  * The struct used to pass data via the following ioctl. Similar to the
- * struct tm in <time.h>, but it needs to be here so that the kernel 
+ * struct tm in <time.h>, but it needs to be here so that the kernel
  * source is self contained, allowing cross-compiles, etc. etc.
  */
 struct rtc_time {
@@ -101,9 +117,11 @@
 
 /* ioctl() calls that are permitted to the /dev/rtc interface. */
 #define RTC_MAGIC 'p'
-#define RTC_RD_TIME		_IOR(RTC_MAGIC, 0x09, struct rtc_time)	/* Read RTC time. */
-#define RTC_SET_TIME		_IOW(RTC_MAGIC, 0x0a, struct rtc_time)	/* Set RTC time. */
-#define RTC_SET_CHARGE  	_IOW(RTC_MAGIC, 0x0b, int) 		
-#define RTC_MAX_IOCTL 0x0b
+#define RTC_RD_TIME     _IOR(RTC_MAGIC, 0x09, struct rtc_time)	/* Read RTC time. */
+#define RTC_SET_TIME    _IOW(RTC_MAGIC, 0x0a, struct rtc_time)	/* Set RTC time. */
+#define RTC_SET_CHARGE  _IOW(RTC_MAGIC, 0x0b, int)
+#define RTC_VLOW_RD     _IOR(RTC_MAGIC, 0x11, int)  /* Voltage Low detector */
+#define RTC_VLOW_SET    _IO(RTC_MAGIC, 0x12)        /* Clear voltage low information */
+#define RTC_MAX_IOCTL 0x12
 
 #endif /* __RTC_H__ */
diff -urN linux-2.4.22-pre3/include/asm-cris/semaphore-helper.h linux-2.4.22-pre4/include/asm-cris/semaphore-helper.h
--- linux-2.4.22-pre3/include/asm-cris/semaphore-helper.h	2003-06-13 07:51:38.000000000 -0700
+++ linux-2.4.22-pre4/include/asm-cris/semaphore-helper.h	2003-07-09 16:56:29.000000000 -0700
@@ -1,7 +1,6 @@
-/* $Id: semaphore-helper.h,v 1.4 2002/11/20 18:20:17 hp Exp $
- *
+/*
  * SMP- and interrupt-safe semaphores helper functions. Generic versions, no
- * optimizations whatsoever... 
+ * optimizations whatsoever...
  *
  */
 
@@ -73,5 +72,3 @@
 }
 
 #endif /* _ASM_SEMAPHORE_HELPER_H */
-
-
diff -urN linux-2.4.22-pre3/include/asm-cris/semaphore.h linux-2.4.22-pre4/include/asm-cris/semaphore.h
--- linux-2.4.22-pre3/include/asm-cris/semaphore.h	2003-06-13 07:51:38.000000000 -0700
+++ linux-2.4.22-pre4/include/asm-cris/semaphore.h	2003-07-09 16:56:29.000000000 -0700
@@ -1,5 +1,3 @@
-/* $Id: semaphore.h,v 1.5 2002/12/02 08:14:08 starvik Exp $ */
-
 /* On the i386 these are coded in asm, perhaps we should as well. Later.. */
 
 #ifndef _CRIS_SEMAPHORE_H
@@ -15,7 +13,7 @@
 #include <asm/atomic.h>
 
 /*
- * CRIS semaphores, implemented in C-only so far. 
+ * CRIS semaphores, implemented in C-only so far.
  */
 
 int printk(const char *fmt, ...);
@@ -140,7 +138,7 @@
  * jumps for both down() and up().
  */
 extern inline void up(struct semaphore * sem)
-{  
+{
 	unsigned long flags;
 	int wakeup;
 
diff -urN linux-2.4.22-pre3/include/asm-cris/sigcontext.h linux-2.4.22-pre4/include/asm-cris/sigcontext.h
--- linux-2.4.22-pre3/include/asm-cris/sigcontext.h	2001-02-08 16:32:44.000000000 -0800
+++ linux-2.4.22-pre4/include/asm-cris/sigcontext.h	2003-07-09 16:56:29.000000000 -0700
@@ -1,16 +1,14 @@
-/* $Id: sigcontext.h,v 1.1 2000/07/10 16:32:31 bjornw Exp $ */
-
 #ifndef _ASM_CRIS_SIGCONTEXT_H
 #define _ASM_CRIS_SIGCONTEXT_H
 
 #include <asm/ptrace.h>
 
-/* This struct is saved by setup_frame in signal.c, to keep the current context while
-   a signal handler is executed. It's restored by sys_sigreturn.
-   
-   To keep things simple, we use pt_regs here even though normally you just specify
-   the list of regs to save. Then we can use copy_from_user on the entire regs instead
-   of a bunch of get_user's as well...
+/* This struct is saved by setup_frame in signal.c, to keep the current context
+   while a signal handler is executed. It's restored by sys_sigreturn.
+
+   To keep things simple, we use pt_regs here even though normally you just
+   specify the list of regs to save. Then we can use copy_from_user on the
+   entire regs instead of a bunch of get_user's as well...
 
 */
 
@@ -21,4 +19,3 @@
 };
 
 #endif
-
diff -urN linux-2.4.22-pre3/include/asm-cris/system.h linux-2.4.22-pre4/include/asm-cris/system.h
--- linux-2.4.22-pre3/include/asm-cris/system.h	2003-06-13 07:51:38.000000000 -0700
+++ linux-2.4.22-pre4/include/asm-cris/system.h	2003-07-09 16:56:29.000000000 -0700
@@ -14,6 +14,15 @@
 #define switch_to(prev,next,last) last = resume(prev,next, \
 					 (int)&((struct task_struct *)0)->thread)
 
+/* read the CPU PC register */
+
+extern inline unsigned long rdpc(void)
+{ 
+	unsigned long pc;
+	__asm__ volatile ("move.d $pc,%0" : "=rm" (pc));
+	return pc;
+}
+
 /* read the CPU version register */
 
 extern inline unsigned long rdvr(void) { 
@@ -54,6 +63,7 @@
 struct __xchg_dummy { unsigned long a[100]; };
 #define __xg(x) ((struct __xchg_dummy *)(x))
 
+#ifdef CONFIG_ETRAX_DEBUG_INTERRUPT
 #if 0
 /* use these and an oscilloscope to see the fraction of time we're running with IRQ's disabled */
 /* it assumes the LED's are on port 0x90000000 of course. */
@@ -62,6 +72,60 @@
 #define save_flags(x) __asm__ __volatile__ ("move $ccr,%0" : "=rm" (x) : : "memory");
 #define restore_flags(x) __asm__ __volatile__ ("move %0,$ccr\n\tbtstq 5,%0\n\tbpl 1f\n\tnop\n\tpush $r0\n\tmoveq 0,$r0\n\tmove.d $r0,[0x90000000]\n\tpop $r0\n1:\n" : : "r" (x) : "memory");
 #else
+
+/* Log when interrupts are turned on and off and who did it. */
+#define CCR_EI_MASK (1 << 5)
+/* in debug.c */
+extern int log_int_pos;
+extern int log_int_size;
+extern int log_int_enable;
+extern int log_int_trig0_pos;
+extern int log_int_trig1_pos;
+extern void log_int(unsigned long pc, unsigned long prev_ccr, unsigned long next_ccr);
+
+/* If you only want to log changes - change to 1 to a 0 below */
+#define LOG_INT(pc, curr_ccr, next_ccr) do { \
+  if (1 || (curr_ccr ^ next_ccr) & CCR_EI_MASK) \
+          log_int((pc), curr_ccr, next_ccr); \
+  }while(0)
+
+#define __save_flags(x) __asm__ __volatile__ ("move $ccr,%0" : "=rm" (x) : : "memory");
+
+extern inline void __cli(void)
+{
+  unsigned long pc = rdpc();  
+  unsigned long curr_ccr; __save_flags(curr_ccr); 
+  LOG_INT(pc, curr_ccr, 0); 
+  __asm__ __volatile__ ( "di" : : :"memory");
+}
+
+
+extern inline void __sti(void)
+{
+  unsigned long pc = rdpc();  
+  unsigned long curr_ccr; __save_flags(curr_ccr); 
+  LOG_INT(pc, curr_ccr, CCR_EI_MASK); 
+  __asm__ __volatile__ ( "ei" : : :"memory");
+}
+
+extern inline void __restore_flags(unsigned long x)
+{
+  unsigned long pc = rdpc();
+  unsigned long curr_ccr; __save_flags(curr_ccr);
+  LOG_INT(pc, curr_ccr, x);   
+  __asm__ __volatile__ ("move %0,$ccr" : : "rm" (x) : "memory");
+}
+
+/* For spinlocks etc */
+#define local_irq_save(x) do { __save_flags(x); __cli(); }while (0)
+#define local_irq_restore(x) restore_flags(x)
+
+#define local_irq_disable()  cli()
+#define local_irq_enable()   sti()
+
+#endif
+
+#else
 #define __cli() __asm__ __volatile__ ( "di" : : :"memory");
 #define __sti() __asm__ __volatile__ ( "ei" : : :"memory");
 #define __save_flags(x) __asm__ __volatile__ ("move $ccr,%0" : "=rm" (x) : : "memory");
diff -urN linux-2.4.22-pre3/include/asm-cris/termbits.h linux-2.4.22-pre4/include/asm-cris/termbits.h
--- linux-2.4.22-pre3/include/asm-cris/termbits.h	2001-02-08 16:32:44.000000000 -0800
+++ linux-2.4.22-pre4/include/asm-cris/termbits.h	2003-07-09 16:56:29.000000000 -0700
@@ -1,5 +1,3 @@
-/* $Id: termbits.h,v 1.1 2000/07/10 16:32:31 bjornw Exp $ */
-
 #ifndef __ARCH_ETRAX100_TERMBITS_H__
 #define __ARCH_ETRAX100_TERMBITS_H__
 
@@ -128,6 +126,7 @@
 #define  B1843200 0010006
 #define  B6250000 0010007
 #define CIBAUD	  002003600000	/* input baud rate (not used) */
+#define CMSPAR	  010000000000		/* mark or space (stick) parity, PARODD => mark parity */
 #define CRTSCTS	  020000000000		/* flow control */
 
 /* c_lflag bits */
diff -urN linux-2.4.22-pre3/include/asm-cris/timex.h linux-2.4.22-pre4/include/asm-cris/timex.h
--- linux-2.4.22-pre3/include/asm-cris/timex.h	2003-06-13 07:51:38.000000000 -0700
+++ linux-2.4.22-pre4/include/asm-cris/timex.h	2003-07-09 16:56:29.000000000 -0700
@@ -26,6 +26,8 @@
 
 unsigned long get_ns_in_jiffie(void);
 
+unsigned long timer_data_to_ns(unsigned long timer_data);
+
 extern inline unsigned long get_us_in_jiffie_highres(void)
 {
 	return get_ns_in_jiffie()/1000;
diff -urN linux-2.4.22-pre3/include/asm-cris/uaccess.h linux-2.4.22-pre4/include/asm-cris/uaccess.h
--- linux-2.4.22-pre3/include/asm-cris/uaccess.h	2003-06-13 07:51:38.000000000 -0700
+++ linux-2.4.22-pre4/include/asm-cris/uaccess.h	2003-07-09 16:56:29.000000000 -0700
@@ -3,6 +3,15 @@
  *	       Hans-Peter Nilsson (hp@axis.com)
  *
  * $Log: uaccess.h,v $
+ * Revision 1.12  2003/06/17 14:00:42  starvik
+ * Merge of Linux 2.4.21
+ *
+ * Revision 1.11  2003/06/04 19:36:45  hp
+ * Remove unused copy-pasted register clobber from __asm_clear
+ *
+ * Revision 1.10  2003/04/09 08:22:38  pkj
+ * Typo correction (taken from Linux 2.5).
+ *
  * Revision 1.9  2002/11/20 18:20:17  hp
  * Make all static inline functions extern inline.
  *
@@ -129,7 +138,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
@@ -775,7 +784,7 @@
 		"	.previous"			\
 		: "=r" (to), "=r" (ret)			\
 		: "0" (to), "1" (ret)			\
-		: "r9", "memory")
+		: "memory")
 
 #define __asm_clear_1(to, ret) \
 	__asm_clear(to, ret,			\
diff -urN linux-2.4.22-pre3/include/asm-i386/cpufeature.h linux-2.4.22-pre4/include/asm-i386/cpufeature.h
--- linux-2.4.22-pre3/include/asm-i386/cpufeature.h	2003-07-09 16:56:25.000000000 -0700
+++ linux-2.4.22-pre4/include/asm-i386/cpufeature.h	2003-07-09 16:56:29.000000000 -0700
@@ -67,9 +67,6 @@
 /* Intel defined CPU features, CPUID level 0x00000001 (ecx), word 4 */
 #define X86_FEATURE_EST		(4*32+ 7) /* Enhanced SpeedStep */
 
-#define cpu_has(c, bit)            test_bit(bit, (c)->x86_capability)
-
-
 #define cpu_has(c, bit)		test_bit(bit, (c)->x86_capability)
 #define boot_cpu_has(bit)	test_bit(bit, boot_cpu_data.x86_capability)
 
diff -urN linux-2.4.22-pre3/include/asm-i386/io_apic.h linux-2.4.22-pre4/include/asm-i386/io_apic.h
--- linux-2.4.22-pre3/include/asm-i386/io_apic.h	2003-07-09 16:56:25.000000000 -0700
+++ linux-2.4.22-pre4/include/asm-i386/io_apic.h	2003-07-09 16:56:29.000000000 -0700
@@ -45,7 +45,7 @@
 } __attribute__ ((packed));
 
 struct IO_APIC_reg_03 {
-	__u32	boot_DT		:  1,
+	__u32	boot_DT		: 1,
 		__reserved_1	: 31;
 } __attribute__ ((packed));
 
diff -urN linux-2.4.22-pre3/include/asm-i386/ioctls.h linux-2.4.22-pre4/include/asm-i386/ioctls.h
--- linux-2.4.22-pre3/include/asm-i386/ioctls.h	2002-08-02 17:39:45.000000000 -0700
+++ linux-2.4.22-pre4/include/asm-i386/ioctls.h	2003-07-09 16:56:29.000000000 -0700
@@ -67,6 +67,7 @@
 #define TIOCGICOUNT	0x545D	/* read serial port inline interrupt counts */
 #define TIOCGHAYESESP   0x545E  /* Get Hayes ESP configuration */
 #define TIOCSHAYESESP   0x545F  /* Set Hayes ESP configuration */
+#define FIOQSIZE	0x5460
 
 /* Used for packet mode */
 #define TIOCPKT_DATA		 0
diff -urN linux-2.4.22-pre3/include/asm-ia64/ioctls.h linux-2.4.22-pre4/include/asm-ia64/ioctls.h
--- linux-2.4.22-pre3/include/asm-ia64/ioctls.h	2002-11-28 15:53:15.000000000 -0800
+++ linux-2.4.22-pre4/include/asm-ia64/ioctls.h	2003-07-09 16:56:29.000000000 -0700
@@ -72,6 +72,7 @@
 #define TIOCGICOUNT	0x545D	/* read serial port inline interrupt counts */
 #define TIOCGHAYESESP   0x545E  /* Get Hayes ESP configuration */
 #define TIOCSHAYESESP   0x545F  /* Set Hayes ESP configuration */
+#define FIOQSIZE	0x5460
 
 /* Used for packet mode */
 #define TIOCPKT_DATA		 0
diff -urN linux-2.4.22-pre3/include/asm-m68k/ioctls.h linux-2.4.22-pre4/include/asm-m68k/ioctls.h
--- linux-2.4.22-pre3/include/asm-m68k/ioctls.h	1998-02-12 16:25:04.000000000 -0800
+++ linux-2.4.22-pre4/include/asm-m68k/ioctls.h	2003-07-09 16:56:30.000000000 -0700
@@ -65,6 +65,7 @@
 
 #define TIOCMIWAIT	0x545C	/* wait for a change on serial input line(s) */
 #define TIOCGICOUNT	0x545D	/* read serial port inline interrupt counts */
+#define FIOQSIZE	0x545E
 
 /* Used for packet mode */
 #define TIOCPKT_DATA		 0
diff -urN linux-2.4.22-pre3/include/asm-ppc/ocp_ids.h linux-2.4.22-pre4/include/asm-ppc/ocp_ids.h
--- linux-2.4.22-pre3/include/asm-ppc/ocp_ids.h	2003-07-09 16:56:26.000000000 -0700
+++ linux-2.4.22-pre4/include/asm-ppc/ocp_ids.h	2003-07-09 16:56:30.000000000 -0700
@@ -4,7 +4,8 @@
  * BRIEF MODULE DESCRIPTION: 
  * OCP device ids based on the ideas from PCI
  *
- * Maintained by: Armin <akuster@mvista.com>
+ * The numbers below are almost completely arbitrary, and in fact
+ * strings might work better.  -- paulus
  *
  *
  *  This program is free software; you can redistribute  it and/or modify it
@@ -27,8 +28,6 @@
  *  with this program; if not, write  to the Free Software Foundation, Inc.,
  *  675 Mass Ave, Cambridge, MA 02139, USA.
  *
- *  Version 1.0 08/22/02 -Armin
- *  	initial release
  */
 
 /*
diff -urN linux-2.4.22-pre3/include/asm-ppc/open_pic.h linux-2.4.22-pre4/include/asm-ppc/open_pic.h
--- linux-2.4.22-pre3/include/asm-ppc/open_pic.h	2003-06-13 07:51:38.000000000 -0700
+++ linux-2.4.22-pre4/include/asm-ppc/open_pic.h	2003-07-09 16:56:30.000000000 -0700
@@ -1,5 +1,5 @@
 /*
- *  arch/ppc/kernel/open_pic.h -- OpenPIC Interrupt Handling
+ *  include/asm-ppc/open_pic.h -- OpenPIC Interrupt Handling
  *
  *  Copyright (C) 1997 Geert Uytterhoeven
  *
diff -urN linux-2.4.22-pre3/include/asm-ppc/pc_serial.h linux-2.4.22-pre4/include/asm-ppc/pc_serial.h
--- linux-2.4.22-pre3/include/asm-ppc/pc_serial.h	2003-07-09 16:56:26.000000000 -0700
+++ linux-2.4.22-pre4/include/asm-ppc/pc_serial.h	2003-07-09 16:56:30.000000000 -0700
@@ -1,6 +1,4 @@
 /*
- * BK Id: %F% %I% %G% %U% %#%
- *
  * include/asm-ppc/pc_serial.h
  *
  * This is basically a copy of include/asm-i386/serial.h.
diff -urN linux-2.4.22-pre3/include/asm-ppc/posix_types.h linux-2.4.22-pre4/include/asm-ppc/posix_types.h
--- linux-2.4.22-pre3/include/asm-ppc/posix_types.h	2003-06-13 07:51:38.000000000 -0700
+++ linux-2.4.22-pre4/include/asm-ppc/posix_types.h	2003-07-09 16:56:30.000000000 -0700
@@ -8,7 +8,7 @@
  */
 
 typedef unsigned int	__kernel_dev_t;
-typedef unsigned int	__kernel_ino_t;
+typedef unsigned long	__kernel_ino_t;
 typedef unsigned int	__kernel_mode_t;
 typedef unsigned short	__kernel_nlink_t;
 typedef long		__kernel_off_t;
diff -urN linux-2.4.22-pre3/include/asm-ppc/processor.h linux-2.4.22-pre4/include/asm-ppc/processor.h
--- linux-2.4.22-pre3/include/asm-ppc/processor.h	2003-07-09 16:56:26.000000000 -0700
+++ linux-2.4.22-pre4/include/asm-ppc/processor.h	2003-07-09 16:56:30.000000000 -0700
@@ -229,6 +229,9 @@
 #define	  HID0_NAP	(1<<22)
 #define	  HID0_SLEEP	(1<<21)
 #define	  HID0_DPM	(1<<20)
+#define	  HID0_BHTCLR	(1<<18)		/* Clear branch history table - 7450 */
+#define	  HID0_XAEN	(1<<17)		/* Extended addressing enable - 7450 */
+#define   HID0_NHR	(1<<16)		/* Not hard reset (software bit-7450)*/
 #define	  HID0_ICE	(1<<15)		/* Instruction Cache Enable */
 #define	  HID0_DCE	(1<<14)		/* Data Cache Enable */
 #define	  HID0_ILOCK	(1<<13)		/* Instruction Cache Lock */
@@ -247,7 +250,16 @@
 #define	  HID0_BTCD	(1<<1)		/* Branch target cache disable */
 #define	  HID0_NOPDST	(1<<1)		/* No-op dst, dstt, etc. instr. */
 #define	  HID0_NOPTI	(1<<0)		/* No-op dcbt and dcbst instr. */
+
 #define	SPRN_HID1	0x3F1	/* Hardware Implementation Register 1 */
+#define	  HID1_EMCP	(1<<31)		/* 7450 Machine Check Pin Enable */
+#define   HID1_PC0	(1<<16)		/* 7450 PLL_CFG[0] */
+#define   HID1_PC1	(1<<15)		/* 7450 PLL_CFG[1] */
+#define   HID1_PC2	(1<<14)		/* 7450 PLL_CFG[2] */
+#define   HID1_PC3	(1<<13)		/* 7450 PLL_CFG[3] */
+#define	  HID1_SYNCBE	(1<<11)		/* 7450 ABE for sync, eieio */
+#define	  HID1_ABE	(1<<10)		/* 7450 Address Broadcast Enable */
+#define	SPRN_HID2	0x3F8	/* Hardware Implementation Register 2 */
 #define	SPRN_IABR	0x3F2	/* Instruction Address Breakpoint Register */
 #define	SPRN_IAC1	0x3F4	/* Instruction Address Compare 1 */
 #define	SPRN_IAC2	0x3F5	/* Instruction Address Compare 2 */
@@ -273,6 +285,10 @@
 #define	SPRN_ICDBDR	0x3D3	/* Instruction Cache Debug Data Register */
 #define	SPRN_ICMP	0x3D5	/* Instruction TLB Compare Register */
 #define	SPRN_ICTC	0x3FB	/* Instruction Cache Throttling Control Reg */
+#define	SPRN_ICTRL 	0x3F3	/* 1011 7450 icache and interrupt ctrl */
+#define   ICTRL_EICE		0x08000000	/* enable icache parity errs */
+#define   ICTRL_EDCE		0x04000000	/* enable dcache parity errs */
+#define   ICTRL_EICP		0x00000100	/* enable icache par. check */
 #define	SPRN_IMISS	0x3D4	/* Instruction TLB Miss Register */
 #define	SPRN_IMMR	0x27E  	/* Internal Memory Map Register */
 #define	SPRN_L2CR	0x3F9	/* Level 2 Cache Control Regsiter */
@@ -328,7 +344,6 @@
 #define L3CR_PMSIZ		0x00000001	/* L3 private memory size */
 #define SPRN_MSSCR0	0x3f6	/* Memory Subsystem Control Register 0 */
 #define SPRN_MSSSR0	0x3f7	/* Memory Subsystem Status Register 1 */
-#define SPRN_ICTRL	0x3f3	/* Instruction Cache & Interrupt control reg */
 #define SPRN_LDSTCR	0x3f8	/* Load/Store control register */
 #define SPRN_LDSTDB	0x3f4	/* */
 #define	SPRN_LR		0x008	/* Link Register */
@@ -345,6 +360,8 @@
 #define	SPRN_PMC2	0x3BA	/* Performance Counter Register 2 */
 #define	SPRN_PMC3	0x3BD	/* Performance Counter Register 3 */
 #define	SPRN_PMC4	0x3BE	/* Performance Counter Register 4 */
+#define	SPRN_PTEHI	0x3D5	/* 981 7450 PTE HI word (S/W TLB load) */
+#define	SPRN_PTELO	0x3D6	/* 982 7450 PTE LO word (S/W TLB load)  */
 #define	SPRN_PVR	0x11F	/* Processor Version Register */
 #define	SPRN_RPA	0x3D6	/* Required Physical Address Register */
 #define	SPRN_SDA	0x3BF	/* Sampled Data Address Register */
@@ -353,6 +370,7 @@
 #define	  SGR_NORMAL		0
 #define	  SGR_GUARDED		1
 #define	SPRN_SIA	0x3BB	/* Sampled Instruction Address Register */
+#define	SPRN_SLER	0x3BB	/* Little-endian real mode */
 #define	SPRN_SPRG0	0x110	/* Special Purpose Register General 0 */
 #define	SPRN_SPRG1	0x111	/* Special Purpose Register General 1 */
 #define	SPRN_SPRG2	0x112	/* Special Purpose Register General 2 */
@@ -406,6 +424,7 @@
 #define	SPRN_THRM2	0x3FD	/* Thermal Management Register 2 */
 #define	SPRN_THRM3	0x3FE	/* Thermal Management Register 3 */
 #define	  THRM3_E		(1<<0)
+#define	SPRN_TLBMISS	0x3D4	/* 980 7450 TLB Miss Register */
 #define	SPRN_TSR	0x3D8	/* Timer Status Register */
 #define	  TSR_ENW		0x80000000	/* Enable Next Watchdog */
 #define	  TSR_WIS		0x40000000	/* WDT Interrupt Status */
@@ -556,6 +575,7 @@
 #define	PVR_750P	PVR_740P
 #define	PVR_7400	0x000C0000
 #define	PVR_7410	0x800C0000
+#define	PVR_7450	0x80000000
 /*
  * For the 8xx processors, all of them report the same PVR family for
  * the PowerPC core. The various versions of these processors must be
diff -urN linux-2.4.22-pre3/include/asm-sh/ioctls.h linux-2.4.22-pre4/include/asm-sh/ioctls.h
--- linux-2.4.22-pre3/include/asm-sh/ioctls.h	1999-11-06 10:40:31.000000000 -0800
+++ linux-2.4.22-pre4/include/asm-sh/ioctls.h	2003-07-09 16:56:30.000000000 -0700
@@ -9,6 +9,7 @@
 #define FIONBIO		_IOW('f', 126, int)
 #define FIONREAD	_IOR('f', 127, int)
 #define TIOCINQ		FIONREAD
+#define FIOQSIZE	_IOR('f', 128, loff_t)
 
 #define TCGETS		0x5401
 #define TCSETS		0x5402
diff -urN linux-2.4.22-pre3/include/asm-sh64/hdreg.h linux-2.4.22-pre4/include/asm-sh64/hdreg.h
--- linux-2.4.22-pre3/include/asm-sh64/hdreg.h	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.4.22-pre4/include/asm-sh64/hdreg.h	2003-07-09 16:56:30.000000000 -0700
@@ -0,0 +1,13 @@
+/*
+ *  linux/include/asm-sh64/hdreg.h
+ *
+ *  Copyright (C) 1994-1996  Linus Torvalds & authors
+ */
+
+#ifndef __ASM_SH64_HDREG_H
+#define __ASM_SH64_HDREG_H
+
+//typedef unsigned short ide_ioreg_t;
+typedef unsigned long ide_ioreg_t;
+
+#endif /* __ASM_SH64_HDREG_H */
diff -urN linux-2.4.22-pre3/include/asm-sh64/ide.h linux-2.4.22-pre4/include/asm-sh64/ide.h
--- linux-2.4.22-pre3/include/asm-sh64/ide.h	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.4.22-pre4/include/asm-sh64/ide.h	2003-07-09 16:56:30.000000000 -0700
@@ -0,0 +1,65 @@
+/*
+ *  linux/include/asm-sh64/ide.h
+ *
+ *  Copyright (C) 1994-1996  Linus Torvalds & authors
+ */
+
+/*
+ *  This file contains the sh64 architecture specific IDE code.
+ */
+
+#ifndef __ASM_SH64_IDE_H
+#define __ASM_SH64_IDE_H
+
+#ifdef __KERNEL__
+
+#ifndef MAX_HWIFS
+#define MAX_HWIFS	4
+#endif
+
+#if 0
+#include <asm/arch/ide.h>
+#endif
+
+#define ide_default_io_base(i)		((ide_ioreg_t)0)
+#define ide_default_irq(b)		(0)
+
+static __inline__ void ide_init_hwif_ports(hw_regs_t *hw, ide_ioreg_t data_port, ide_ioreg_t ctrl_port, int *irq)
+{
+	ide_ioreg_t reg = data_port;
+	int i;
+
+	for (i = IDE_DATA_OFFSET; i <= IDE_STATUS_OFFSET; i++) {
+		hw->io_ports[i] = reg;
+		reg += 1;
+	}
+	if (ctrl_port) {
+		hw->io_ports[IDE_CONTROL_OFFSET] = ctrl_port;
+	} else {
+		hw->io_ports[IDE_CONTROL_OFFSET] = hw->io_ports[IDE_DATA_OFFSET] + 0x206;
+	}
+	if (irq != NULL)
+		*irq = 0;
+	hw->io_ports[IDE_IRQ_OFFSET] = 0;
+}
+
+static __inline__ void ide_init_default_hwifs(void)
+{
+#ifndef CONFIG_BLK_DEV_IDEPCI
+	hw_regs_t hw;
+	int index;
+
+	for(index = 0; index < MAX_HWIFS; index++) {
+		ide_init_hwif_ports(&hw, ide_default_io_base(index), 0, NULL);
+		hw.irq = ide_default_irq(ide_default_io_base(index));
+		ide_register_hw(&hw, NULL);
+	}
+#endif /* CONFIG_BLK_DEV_IDEPCI */
+}
+
+#include <asm-generic/ide_iops.h>
+
+
+#endif /* __KERNEL__ */
+
+#endif /* __ASM_SH64_IDE_H */
diff -urN linux-2.4.22-pre3/include/asm-sh64/ioctls.h linux-2.4.22-pre4/include/asm-sh64/ioctls.h
--- linux-2.4.22-pre3/include/asm-sh64/ioctls.h	2003-07-09 16:56:26.000000000 -0700
+++ linux-2.4.22-pre4/include/asm-sh64/ioctls.h	2003-07-09 16:56:30.000000000 -0700
@@ -20,6 +20,7 @@
 #define FIONBIO		_IOW('f', 126, int)
 #define FIONREAD	_IOR('f', 127, int)
 #define TIOCINQ		FIONREAD
+#define FIOQSIZE	_IOR('f', 128, loff_t)
 
 #define TCGETS		0x5401
 #define TCSETS		0x5402
diff -urN linux-2.4.22-pre3/include/asm-sh64/mc146818rtc.h linux-2.4.22-pre4/include/asm-sh64/mc146818rtc.h
--- linux-2.4.22-pre3/include/asm-sh64/mc146818rtc.h	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.4.22-pre4/include/asm-sh64/mc146818rtc.h	2003-07-09 16:56:30.000000000 -0700
@@ -0,0 +1,7 @@
+/*
+ * linux/include/asm-sh64/mc146818rtc.h
+ *
+*/
+
+/* For now, an empty place-holder to get IDE to compile. */
+
diff -urN linux-2.4.22-pre3/include/asm-sparc/ioctls.h linux-2.4.22-pre4/include/asm-sparc/ioctls.h
--- linux-2.4.22-pre3/include/asm-sparc/ioctls.h	2002-08-02 17:39:45.000000000 -0700
+++ linux-2.4.22-pre4/include/asm-sparc/ioctls.h	2003-07-09 16:56:30.000000000 -0700
@@ -86,6 +86,7 @@
 #define FIONBIO		_IOW('f', 126, int)
 #define FIONREAD	_IOR('f', 127, int)
 #define TIOCINQ		FIONREAD
+#define FIOQSIZE	_IOR('f', 128, loff_t)
 
 /* SCARY Rutgers local SunOS kernel hackery, perhaps I will support it
  * someday.  This is completely bogus, I know...
diff -urN linux-2.4.22-pre3/include/asm-sparc/ipc.h linux-2.4.22-pre4/include/asm-sparc/ipc.h
--- linux-2.4.22-pre3/include/asm-sparc/ipc.h	1999-12-20 22:05:52.000000000 -0800
+++ linux-2.4.22-pre4/include/asm-sparc/ipc.h	2003-07-09 16:56:30.000000000 -0700
@@ -14,6 +14,7 @@
 #define SEMOP		 1
 #define SEMGET		 2
 #define SEMCTL		 3
+#define SEMTIMEDOP	 4
 #define MSGSND		11
 #define MSGRCV		12
 #define MSGGET		13
diff -urN linux-2.4.22-pre3/include/asm-sparc64/ioctls.h linux-2.4.22-pre4/include/asm-sparc64/ioctls.h
--- linux-2.4.22-pre3/include/asm-sparc64/ioctls.h	2002-08-02 17:39:45.000000000 -0700
+++ linux-2.4.22-pre4/include/asm-sparc64/ioctls.h	2003-07-09 16:56:30.000000000 -0700
@@ -87,6 +87,7 @@
 #define FIONBIO		_IOW('f', 126, int)
 #define FIONREAD	_IOR('f', 127, int)
 #define TIOCINQ		FIONREAD
+#define FIOQSIZE	_IOR('f', 128, loff_t)
 
 /* SCARY Rutgers local SunOS kernel hackery, perhaps I will support it
  * someday.  This is completely bogus, I know...
diff -urN linux-2.4.22-pre3/include/asm-sparc64/ipc.h linux-2.4.22-pre4/include/asm-sparc64/ipc.h
--- linux-2.4.22-pre3/include/asm-sparc64/ipc.h	1999-12-20 22:05:52.000000000 -0800
+++ linux-2.4.22-pre4/include/asm-sparc64/ipc.h	2003-07-09 16:56:30.000000000 -0700
@@ -14,6 +14,7 @@
 #define SEMOP		 1
 #define SEMGET		 2
 #define SEMCTL		 3
+#define SEMTIMEDOP	 4
 #define MSGSND		11
 #define MSGRCV		12
 #define MSGGET		13
diff -urN linux-2.4.22-pre3/include/linux/agp_backend.h linux-2.4.22-pre4/include/linux/agp_backend.h
--- linux-2.4.22-pre3/include/linux/agp_backend.h	2003-06-13 07:51:38.000000000 -0700
+++ linux-2.4.22-pre4/include/linux/agp_backend.h	2003-07-09 16:56:30.000000000 -0700
@@ -52,16 +52,20 @@
 	INTEL_I840,
 	INTEL_I845,
 	INTEL_I850,
+	INTEL_I855_PM,
 	INTEL_I860,
+	INTEL_I865_G,
 	VIA_GENERIC,
 	VIA_VP3,
 	VIA_MVP3,
 	VIA_MVP4,
+	VIA_APOLLO_PLE133,
 	VIA_APOLLO_PRO,
 	VIA_APOLLO_KX133,
 	VIA_APOLLO_KT133,
-	VIA_APOLLO_P4X400,
+	VIA_APOLLO_KM266,
 	VIA_APOLLO_KT400,
+	VIA_APOLLO_P4M266,
 	SIS_GENERIC,
 	AMD_GENERIC,
 	AMD_IRONGATE,
@@ -81,6 +85,9 @@
 	SVWRKS_HE,
 	SVWRKS_LE,
 	SVWRKS_GENERIC,
+	NVIDIA_NFORCE,
+	NVIDIA_NFORCE2,
+	NVIDIA_GENERIC,
 	HP_ZX1,
 };
 
diff -urN linux-2.4.22-pre3/include/linux/dqblk_v1.h linux-2.4.22-pre4/include/linux/dqblk_v1.h
--- linux-2.4.22-pre3/include/linux/dqblk_v1.h	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.4.22-pre4/include/linux/dqblk_v1.h	2003-07-09 16:56:30.000000000 -0700
@@ -0,0 +1,18 @@
+/*
+ *	File with in-memory structures of old quota format
+ */
+
+#ifndef _LINUX_DQBLK_V1_H
+#define _LINUX_DQBLK_V1_H
+
+/* Id of quota format */
+#define QFMT_VFS_OLD 1
+
+/* Root squash turned on */
+#define V1_DQF_RSQUASH 1
+
+/* Special information about quotafile */
+struct v1_mem_dqinfo {
+};
+
+#endif	/* _LINUX_DQBLK_V1_H */
diff -urN linux-2.4.22-pre3/include/linux/dqblk_v2.h linux-2.4.22-pre4/include/linux/dqblk_v2.h
--- linux-2.4.22-pre3/include/linux/dqblk_v2.h	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.4.22-pre4/include/linux/dqblk_v2.h	2003-07-09 16:56:30.000000000 -0700
@@ -0,0 +1,20 @@
+/*
+ *	Definitions of structures for vfsv0 quota format
+ */
+
+#ifndef _LINUX_DQBLK_V2_H
+#define _LINUX_DQBLK_V2_H
+
+#include <linux/types.h>
+
+/* id numbers of quota format */
+#define QFMT_VFS_V0 2
+
+/* Inmemory copy of version specific information */
+struct v2_mem_dqinfo {
+	unsigned int dqi_blocks;
+	unsigned int dqi_free_blk;
+	unsigned int dqi_free_entry;
+};
+
+#endif /* _LINUX_DQBLK_V2_H */
diff -urN linux-2.4.22-pre3/include/linux/dqblk_xfs.h linux-2.4.22-pre4/include/linux/dqblk_xfs.h
--- linux-2.4.22-pre3/include/linux/dqblk_xfs.h	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.4.22-pre4/include/linux/dqblk_xfs.h	2003-07-09 16:56:30.000000000 -0700
@@ -0,0 +1,159 @@
+/*
+ * Copyright (c) 1995-2001 Silicon Graphics, Inc.  All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2.1 of the GNU Lesser General Public License
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like.  Any license provided herein, whether implied or
+ * otherwise, applies only to this software file.  Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307,
+ * USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA  94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+#ifndef _LINUX_DQBLK_XFS_H
+#define _LINUX_DQBLK_XFS_H
+
+#include <linux/types.h>
+
+/*
+ * Disk quota - quotactl(2) commands for the XFS Quota Manager (XQM).
+ */
+
+#define XQM_CMD(x)	(('X'<<8)+(x))	/* note: forms first QCMD argument */
+#define Q_XQUOTAON	XQM_CMD(0x1)	/* enable accounting/enforcement */
+#define Q_XQUOTAOFF	XQM_CMD(0x2)	/* disable accounting/enforcement */
+#define Q_XGETQUOTA	XQM_CMD(0x3)	/* get disk limits and usage */
+#define Q_XSETQLIM	XQM_CMD(0x4)	/* set disk limits */
+#define Q_XGETQSTAT	XQM_CMD(0x5)	/* get quota subsystem status */
+#define Q_XQUOTARM	XQM_CMD(0x6)	/* free disk space used by dquots */
+
+/*
+ * fs_disk_quota structure:
+ *
+ * This contains the current quota information regarding a user/proj/group.
+ * It is 64-bit aligned, and all the blk units are in BBs (Basic Blocks) of
+ * 512 bytes.
+ */
+#define FS_DQUOT_VERSION	1	/* fs_disk_quota.d_version */
+typedef struct fs_disk_quota {
+	__s8		d_version;	/* version of this structure */
+	__s8		d_flags;	/* XFS_{USER,PROJ,GROUP}_QUOTA */
+	__u16		d_fieldmask;	/* field specifier */
+	__u32		d_id;		/* user, project, or group ID */
+	__u64		d_blk_hardlimit;/* absolute limit on disk blks */
+	__u64		d_blk_softlimit;/* preferred limit on disk blks */
+	__u64		d_ino_hardlimit;/* maximum # allocated inodes */
+	__u64		d_ino_softlimit;/* preferred inode limit */
+	__u64		d_bcount;	/* # disk blocks owned by the user */
+	__u64		d_icount;	/* # inodes owned by the user */
+	__s32		d_itimer;	/* zero if within inode limits */
+					/* if not, we refuse service */
+	__s32		d_btimer;	/* similar to above; for disk blocks */
+	__u16	  	d_iwarns;       /* # warnings issued wrt num inodes */
+	__u16	  	d_bwarns;       /* # warnings issued wrt disk blocks */
+	__s32		d_padding2;	/* padding2 - for future use */
+	__u64		d_rtb_hardlimit;/* absolute limit on realtime blks */
+	__u64		d_rtb_softlimit;/* preferred limit on RT disk blks */
+	__u64		d_rtbcount;	/* # realtime blocks owned */
+	__s32		d_rtbtimer;	/* similar to above; for RT disk blks */
+	__u16	  	d_rtbwarns;     /* # warnings issued wrt RT disk blks */
+	__s16		d_padding3;	/* padding3 - for future use */	
+	char		d_padding4[8];	/* yet more padding */
+} fs_disk_quota_t;
+
+/*
+ * These fields are sent to Q_XSETQLIM to specify fields that need to change.
+ */
+#define FS_DQ_ISOFT	(1<<0)
+#define FS_DQ_IHARD	(1<<1)
+#define FS_DQ_BSOFT	(1<<2)
+#define FS_DQ_BHARD 	(1<<3)
+#define FS_DQ_RTBSOFT	(1<<4)
+#define FS_DQ_RTBHARD	(1<<5)
+#define FS_DQ_LIMIT_MASK	(FS_DQ_ISOFT | FS_DQ_IHARD | FS_DQ_BSOFT | \
+				 FS_DQ_BHARD | FS_DQ_RTBSOFT | FS_DQ_RTBHARD)
+/*
+ * These timers can only be set in super user's dquot. For others, timers are
+ * automatically started and stopped. Superusers timer values set the limits
+ * for the rest.  In case these values are zero, the DQ_{F,B}TIMELIMIT values
+ * defined below are used. 
+ * These values also apply only to the d_fieldmask field for Q_XSETQLIM.
+ */
+#define FS_DQ_BTIMER	(1<<6)
+#define FS_DQ_ITIMER	(1<<7)
+#define FS_DQ_RTBTIMER 	(1<<8)
+#define FS_DQ_TIMER_MASK	(FS_DQ_BTIMER | FS_DQ_ITIMER | FS_DQ_RTBTIMER)
+
+/*
+ * The following constants define the default amount of time given a user
+ * before the soft limits are treated as hard limits (usually resulting
+ * in an allocation failure).  These may be modified by the quotactl(2)
+ * system call with the Q_XSETQLIM command.
+ */
+#define	DQ_FTIMELIMIT	(7 * 24*60*60)		/* 1 week */
+#define	DQ_BTIMELIMIT	(7 * 24*60*60)		/* 1 week */
+
+/*
+ * Various flags related to quotactl(2).  Only relevant to XFS filesystems.
+ */
+#define XFS_QUOTA_UDQ_ACCT	(1<<0)  /* user quota accounting */
+#define XFS_QUOTA_UDQ_ENFD	(1<<1)  /* user quota limits enforcement */
+#define XFS_QUOTA_GDQ_ACCT	(1<<2)  /* group quota accounting */
+#define XFS_QUOTA_GDQ_ENFD	(1<<3)  /* group quota limits enforcement */
+
+#define XFS_USER_QUOTA		(1<<0)	/* user quota type */
+#define XFS_PROJ_QUOTA		(1<<1)	/* (IRIX) project quota type */
+#define XFS_GROUP_QUOTA		(1<<2)	/* group quota type */
+
+/*
+ * fs_quota_stat is the struct returned in Q_XGETQSTAT for a given file system.
+ * Provides a centralized way to get meta infomation about the quota subsystem.
+ * eg. space taken up for user and group quotas, number of dquots currently
+ * incore.
+ */
+#define FS_QSTAT_VERSION	1	/* fs_quota_stat.qs_version */
+
+/*
+ * Some basic infomation about 'quota files'.
+ */
+typedef struct fs_qfilestat {
+	__u64		qfs_ino;	/* inode number */
+	__u64		qfs_nblks;	/* number of BBs 512-byte-blks */
+	__u32		qfs_nextents;	/* number of extents */
+} fs_qfilestat_t;
+
+typedef struct fs_quota_stat {
+	__s8		qs_version;	/* version number for future changes */
+	__u16		qs_flags;	/* XFS_QUOTA_{U,P,G}DQ_{ACCT,ENFD} */
+	__s8		qs_pad;		/* unused */
+	fs_qfilestat_t	qs_uquota;	/* user quota storage information */
+	fs_qfilestat_t	qs_gquota;	/* group quota storage information */
+	__u32		qs_incoredqs;	/* number of dquots incore */
+	__s32		qs_btimelimit;  /* limit for blks timer */	
+	__s32		qs_itimelimit;  /* limit for inodes timer */	
+	__s32		qs_rtbtimelimit;/* limit for rt blks timer */	
+	__u16		qs_bwarnlimit;	/* limit for num warnings */
+	__u16		qs_iwarnlimit;	/* limit for num warnings */
+} fs_quota_stat_t;
+
+#endif	/* _LINUX_DQBLK_XFS_H */
diff -urN linux-2.4.22-pre3/include/linux/fs.h linux-2.4.22-pre4/include/linux/fs.h
--- linux-2.4.22-pre3/include/linux/fs.h	2003-07-09 16:56:26.000000000 -0700
+++ linux-2.4.22-pre4/include/linux/fs.h	2003-07-09 16:56:30.000000000 -0700
@@ -455,6 +455,7 @@
 	unsigned long		i_blksize;
 	unsigned long		i_blocks;
 	unsigned long		i_version;
+	unsigned short          i_bytes;
 	struct semaphore	i_sem;
 	struct rw_semaphore	i_alloc_sem;
 	struct semaphore	i_zombie;
@@ -516,6 +517,39 @@
 	} u;
 };
 
+static inline void inode_add_bytes(struct inode *inode, loff_t bytes)
+{
+	inode->i_blocks += bytes >> 9;
+	bytes &= 511;
+	inode->i_bytes += bytes;
+	if (inode->i_bytes >= 512) {
+		inode->i_blocks++;
+		inode->i_bytes -= 512;
+	}
+}
+
+static inline void inode_sub_bytes(struct inode *inode, loff_t bytes)
+{
+	inode->i_blocks -= bytes >> 9;
+	bytes &= 511;
+	if (inode->i_bytes < bytes) {
+		inode->i_blocks--;
+		inode->i_bytes += 512;
+	}
+	inode->i_bytes -= bytes;
+}
+
+static inline loff_t inode_get_bytes(struct inode *inode)
+{
+	return (((loff_t)inode->i_blocks) << 9) + inode->i_bytes;
+}
+
+static inline void inode_set_bytes(struct inode *inode, loff_t bytes)
+{
+	inode->i_blocks = bytes >> 9;
+	inode->i_bytes = bytes & 511;
+}
+
 struct fown_struct {
 	int pid;		/* pid or -pgrp where SIGIO should be sent */
 	uid_t uid, euid;	/* uid/euid of process setting the owner */
@@ -661,20 +695,6 @@
 	int last_type;
 };
 
-#define DQUOT_USR_ENABLED	0x01		/* User diskquotas enabled */
-#define DQUOT_GRP_ENABLED	0x02		/* Group diskquotas enabled */
-
-struct quota_mount_options
-{
-	unsigned int flags;			/* Flags for diskquotas on this device */
-	struct semaphore dqio_sem;		/* lock device while I/O in progress */
-	struct semaphore dqoff_sem;		/* serialize quota_off() and quota_on() on device */
-	struct file *files[MAXQUOTAS];		/* fp's to quotafiles */
-	time_t inode_expire[MAXQUOTAS];		/* expiretime for inode-quota */
-	time_t block_expire[MAXQUOTAS];		/* expiretime for block-quota */
-	char rsquash[MAXQUOTAS];		/* for quotas threat root as any other user */
-};
-
 /*
  *	Umount options
  */
@@ -722,6 +742,7 @@
 	struct file_system_type	*s_type;
 	struct super_operations	*s_op;
 	struct dquot_operations	*dq_op;
+	struct quotactl_ops	*s_qcop;
 	unsigned long		s_flags;
 	unsigned long		s_magic;
 	struct dentry		*s_root;
@@ -736,7 +757,7 @@
 
 	struct block_device	*s_bdev;
 	struct list_head	s_instances;
-	struct quota_mount_options s_dquot;	/* Diskquota specific options */
+	struct quota_info	s_dquot;	/* Diskquota specific options */
 
 	union {
 		struct minix_sb_info	minix_sb;
@@ -960,16 +981,6 @@
 	__mark_inode_dirty(inode, I_DIRTY_PAGES);
 }
 
-struct dquot_operations {
-	void (*initialize) (struct inode *, short);
-	void (*drop) (struct inode *);
-	int (*alloc_block) (struct inode *, unsigned long, char);
-	int (*alloc_inode) (const struct inode *, unsigned long);
-	void (*free_block) (struct inode *, unsigned long);
-	void (*free_inode) (const struct inode *, unsigned long);
-	int (*transfer) (struct inode *, struct iattr *);
-};
-
 struct file_system_type {
 	const char *name;
 	int fs_flags;
@@ -1526,6 +1537,9 @@
 extern int inode_change_ok(struct inode *, struct iattr *);
 extern int inode_setattr(struct inode *, struct iattr *);
 
+/* kernel/fork.c */
+extern int unshare_files(void);
+
 /*
  * Common dentry functions for inclusion in the VFS
  * or in other stackable file systems.  Some of these
diff -urN linux-2.4.22-pre3/include/linux/mtd/mtd.h linux-2.4.22-pre4/include/linux/mtd/mtd.h
--- linux-2.4.22-pre3/include/linux/mtd/mtd.h	2003-06-13 07:51:38.000000000 -0700
+++ linux-2.4.22-pre4/include/linux/mtd/mtd.h	2003-07-09 16:56:30.000000000 -0700
@@ -287,10 +287,11 @@
 #define MTD_DEBUG_LEVEL3	(3)	/* Noisy   */
 
 #ifdef CONFIG_MTD_DEBUG
-#define DEBUG(n, args...)			\
-	if (n <=  CONFIG_MTD_DEBUG_VERBOSE) {	\
-		printk(KERN_INFO args);	\
-	}
+#define DEBUG(n, args...)				\
+ 	do {						\
+		if (n <= CONFIG_MTD_DEBUG_VERBOSE)	\
+			printk(KERN_INFO args);		\
+	} while(0)
 #else /* CONFIG_MTD_DEBUG */
 #define DEBUG(n, args...)
 #endif /* CONFIG_MTD_DEBUG */
diff -urN linux-2.4.22-pre3/include/linux/pci.h linux-2.4.22-pre4/include/linux/pci.h
--- linux-2.4.22-pre3/include/linux/pci.h	2003-06-13 07:51:39.000000000 -0700
+++ linux-2.4.22-pre4/include/linux/pci.h	2003-07-09 16:56:30.000000000 -0700
@@ -773,6 +773,11 @@
 	pdev->driver_data = data;
 }
 
+static inline char *pci_name(struct pci_dev *pdev)
+{
+	return pdev->slot_name;
+}
+
 /*
  *  The world is not perfect and supplies us with broken PCI devices.
  *  For at least a part of these bugs we need a work-around, so both
diff -urN linux-2.4.22-pre3/include/linux/quota.h linux-2.4.22-pre4/include/linux/quota.h
--- linux-2.4.22-pre3/include/linux/quota.h	2001-11-22 10:38:31.000000000 -0800
+++ linux-2.4.22-pre4/include/linux/quota.h	2003-07-09 16:56:30.000000000 -0700
@@ -40,30 +40,22 @@
 #define _LINUX_QUOTA_
 
 #include <linux/errno.h>
+#include <linux/types.h>
 
-/*
- * Convert diskblocks to blocks and the other way around.
- */
-#define dbtob(num) (num << BLOCK_SIZE_BITS)
-#define btodb(num) (num >> BLOCK_SIZE_BITS)
+#define __DQUOT_VERSION__	"dquot_6.5.1"
+#define __DQUOT_NUM_VERSION__	6*10000+5*100+1
 
-/*
- * Convert count of filesystem blocks to diskquota blocks, meant
- * for filesystems where i_blksize != BLOCK_SIZE
- */
-#define fs_to_dq_blocks(num, blksize) (((num) * (blksize)) / BLOCK_SIZE)
+typedef __kernel_uid32_t qid_t; /* Type in which we store ids in memory */
+typedef __u64 qsize_t;          /* Type in which we store sizes */
 
-/*
- * Definitions for disk quotas imposed on the average user
- * (big brother finally hits Linux).
- *
- * The following constants define the amount of time given a user
- * before the soft limits are treated as hard limits (usually resulting
- * in an allocation failure). The timer is started when the user crosses
- * their soft limit, it is reset when they go below their soft limit.
- */
-#define MAX_IQ_TIME  604800	/* (7*24*60*60) 1 week */
-#define MAX_DQ_TIME  604800	/* (7*24*60*60) 1 week */
+/* Size of blocks in which are counted size limits */
+#define QUOTABLOCK_BITS 10
+#define QUOTABLOCK_SIZE (1 << QUOTABLOCK_BITS)
+
+/* Conversion routines from and to quota blocks */
+#define qb2kb(x) ((x) << (QUOTABLOCK_BITS-10))
+#define kb2qb(x) ((x) >> (QUOTABLOCK_BITS-10))
+#define toqb(x) (((x) + QUOTABLOCK_SIZE - 1) >> QUOTABLOCK_BITS)
 
 #define MAXQUOTAS 2
 #define USRQUOTA  0		/* element used for user quotas */
@@ -78,9 +70,6 @@
 	"undefined", \
 };
 
-#define QUOTAFILENAME "quota"
-#define QUOTAGROUP "staff"
-
 /*
  * Command definitions for the 'quotactl' system call.
  * The commands are broken into a main command defined below
@@ -91,61 +80,122 @@
 #define SUBCMDSHIFT 8
 #define QCMD(cmd, type)  (((cmd) << SUBCMDSHIFT) | ((type) & SUBCMDMASK))
 
-#define Q_QUOTAON  0x0100	/* enable quotas */
-#define Q_QUOTAOFF 0x0200	/* disable quotas */
-#define Q_GETQUOTA 0x0300	/* get limits and usage */
-#define Q_SETQUOTA 0x0400	/* set limits and usage */
-#define Q_SETUSE   0x0500	/* set usage */
-#define Q_SYNC     0x0600	/* sync disk copy of a filesystems quotas */
-#define Q_SETQLIM  0x0700	/* set limits */
-#define Q_GETSTATS 0x0800	/* get collected stats */
-#define Q_RSQUASH  0x1000	/* set root_squash option */
-
-/*
- * The following structure defines the format of the disk quota file
- * (as it appears on disk) - the file is an array of these structures
- * indexed by user or group number.
+#define Q_SYNC     0x800001	/* sync disk copy of a filesystems quotas */
+#define Q_QUOTAON  0x800002	/* turn quotas on */
+#define Q_QUOTAOFF 0x800003	/* turn quotas off */
+#define Q_GETFMT   0x800004	/* get quota format used on given filesystem */
+#define Q_GETINFO  0x800005	/* get information about quota files */
+#define Q_SETINFO  0x800006	/* set information about quota files */
+#define Q_GETQUOTA 0x800007	/* get user quota structure */
+#define Q_SETQUOTA 0x800008	/* set user quota structure */
+
+/*
+ * Quota structure used for communication with userspace via quotactl
+ * Following flags are used to specify which fields are valid
  */
-struct dqblk {
+#define QIF_BLIMITS	1
+#define QIF_SPACE	2
+#define QIF_ILIMITS	4
+#define QIF_INODES	8
+#define QIF_BTIME	16
+#define QIF_ITIME	32
+#define QIF_LIMITS	(QIF_BLIMITS | QIF_ILIMITS)
+#define QIF_USAGE	(QIF_SPACE | QIF_INODES)
+#define QIF_TIMES	(QIF_BTIME | QIF_ITIME)
+#define QIF_ALL		(QIF_LIMITS | QIF_USAGE | QIF_TIMES)
+
+struct if_dqblk {
+	__u64 dqb_bhardlimit;
+	__u64 dqb_bsoftlimit;
+	__u64 dqb_curspace;
+	__u64 dqb_ihardlimit;
+	__u64 dqb_isoftlimit;
+	__u64 dqb_curinodes;
+	__u64 dqb_btime;
+	__u64 dqb_itime;
+	__u32 dqb_valid;
+};
+
+/*
+ * Structure used for setting quota information about file via quotactl
+ * Following flags are used to specify which fields are valid
+ */
+#define IIF_BGRACE	1
+#define IIF_IGRACE	2
+#define IIF_FLAGS	4
+#define IIF_ALL		(IIF_BGRACE | IIF_IGRACE | IIF_FLAGS)
+
+struct if_dqinfo {
+	__u64 dqi_bgrace;
+	__u64 dqi_igrace;
+	__u32 dqi_flags;
+	__u32 dqi_valid;
+};
+
+#ifdef __KERNEL__
+
+#include <linux/dqblk_xfs.h>
+#include <linux/dqblk_v1.h>
+#include <linux/dqblk_v2.h>
+
+/*
+ * Data for one user/group kept in memory
+ */
+struct mem_dqblk {
 	__u32 dqb_bhardlimit;	/* absolute limit on disk blks alloc */
 	__u32 dqb_bsoftlimit;	/* preferred limit on disk blks */
-	__u32 dqb_curblocks;	/* current block count */
+	qsize_t dqb_curspace;	/* current used space */
 	__u32 dqb_ihardlimit;	/* absolute limit on allocated inodes */
 	__u32 dqb_isoftlimit;	/* preferred inode limit */
 	__u32 dqb_curinodes;	/* current # allocated inodes */
-	time_t dqb_btime;		/* time limit for excessive disk use */
-	time_t dqb_itime;		/* time limit for excessive inode use */
+	time_t dqb_btime;	/* time limit for excessive disk use */
+	time_t dqb_itime;	/* time limit for excessive inode use */
 };
 
 /*
- * Shorthand notation.
+ * Data for one quotafile kept in memory
  */
-#define	dq_bhardlimit	dq_dqb.dqb_bhardlimit
-#define	dq_bsoftlimit	dq_dqb.dqb_bsoftlimit
-#define	dq_curblocks	dq_dqb.dqb_curblocks
-#define	dq_ihardlimit	dq_dqb.dqb_ihardlimit
-#define	dq_isoftlimit	dq_dqb.dqb_isoftlimit
-#define	dq_curinodes	dq_dqb.dqb_curinodes
-#define	dq_btime	dq_dqb.dqb_btime
-#define	dq_itime	dq_dqb.dqb_itime
+struct quota_format_type;
+
+struct mem_dqinfo {
+	struct quota_format_type *dqi_format;
+	int dqi_flags;
+	unsigned int dqi_bgrace;
+	unsigned int dqi_igrace;
+	union {
+		struct v1_mem_dqinfo v1_i;
+		struct v2_mem_dqinfo v2_i;
+	} u;
+};
+
+#define DQF_MASK 0xffff		/* Mask for format specific flags */
+#define DQF_INFO_DIRTY 0x10000  /* Is info dirty? */
+#define DQF_ANY_DQUOT_DIRTY 0x20000	/* Is any dquot dirty? */
 
-#define dqoff(UID)      ((loff_t)((UID) * sizeof (struct dqblk)))
+extern inline void mark_info_dirty(struct mem_dqinfo *info)
+{
+	info->dqi_flags |= DQF_INFO_DIRTY;
+}
+
+#define info_dirty(info) ((info)->dqi_flags & DQF_INFO_DIRTY)
+
+#define info_any_dirty(info) ((info)->dqi_flags & DQF_INFO_DIRTY ||\
+			      (info)->dqi_flags & DQF_ANY_DQUOT_DIRTY)
+
+#define sb_dqopt(sb) (&(sb)->s_dquot)
 
 struct dqstats {
-	__u32 lookups;
-	__u32 drops;
-	__u32 reads;
-	__u32 writes;
-	__u32 cache_hits;
-	__u32 allocated_dquots;
-	__u32 free_dquots;
-	__u32 syncs;
+	int lookups;
+	int drops;
+	int reads;
+	int writes;
+	int cache_hits;
+	int allocated_dquots;
+	int free_dquots;
+	int syncs;
 };
 
-#ifdef __KERNEL__
-
-extern int nr_dquots, nr_free_dquots;
-extern int dquot_root_squash;
+extern struct dqstats dqstats;
 
 #define NR_DQHASH 43            /* Just an arbitrary number */
 
@@ -162,37 +212,113 @@
 	struct list_head dq_free;	/* Free list element */
 	wait_queue_head_t dq_wait_lock;	/* Pointer to waitqueue on dquot lock */
 	wait_queue_head_t dq_wait_free;	/* Pointer to waitqueue for quota to be unused */
-	int dq_count;			/* Reference count */
+	int dq_count;			/* Use count */
+	int dq_dup_ref;			/* Number of duplicated refences */
 
 	/* fields after this point are cleared when invalidating */
 	struct super_block *dq_sb;	/* superblock this applies to */
 	unsigned int dq_id;		/* ID this applies to (uid, gid) */
 	kdev_t dq_dev;			/* Device this applies to */
+	loff_t dq_off;			/* Offset of dquot on disk */
 	short dq_type;			/* Type of quota */
 	short dq_flags;			/* See DQ_* */
 	unsigned long dq_referenced;	/* Number of times this dquot was 
 					   referenced during its lifetime */
-	struct dqblk dq_dqb;		/* Diskquota usage */
+	struct mem_dqblk dq_dqb;	/* Diskquota usage */
 };
 
 #define NODQUOT (struct dquot *)NULL
 
-/*
- * Flags used for set_dqblk.
- */
-#define SET_QUOTA         0x02
-#define SET_USE           0x04
-#define SET_QLIMIT        0x08
-
 #define QUOTA_OK          0
 #define NO_QUOTA          1
 
+/* Operations which must be implemented by each quota format */
+struct quota_format_ops {
+	int (*check_quota_file)(struct super_block *sb, int type);	/* Detect whether file is in our format */
+	int (*read_file_info)(struct super_block *sb, int type);	/* Read main info about file - called on quotaon() */
+	int (*write_file_info)(struct super_block *sb, int type);	/* Write main info about file */
+	int (*free_file_info)(struct super_block *sb, int type);	/* Called on quotaoff() */
+	int (*read_dqblk)(struct dquot *dquot);		/* Read structure for one user */
+	int (*commit_dqblk)(struct dquot *dquot);	/* Write (or delete) structure for one user */
+};
+
+/* Operations working with dquots */
+struct dquot_operations {
+	void (*initialize) (struct inode *, int);
+	void (*drop) (struct inode *);
+	int (*alloc_space) (struct inode *, qsize_t, int);
+	int (*alloc_inode) (const struct inode *, unsigned long);
+	void (*free_space) (struct inode *, qsize_t);
+	void (*free_inode) (const struct inode *, unsigned long);
+	int (*transfer) (struct inode *, struct iattr *);
+};
+
+/* Operations handling requests from userspace */
+struct quotactl_ops {
+	int (*quota_on)(struct super_block *, int, int, char *);
+	int (*quota_off)(struct super_block *, int);
+	int (*quota_sync)(struct super_block *, int);
+	int (*get_info)(struct super_block *, int, struct if_dqinfo *);
+	int (*set_info)(struct super_block *, int, struct if_dqinfo *);
+	int (*get_dqblk)(struct super_block *, int, qid_t, struct if_dqblk *);
+	int (*set_dqblk)(struct super_block *, int, qid_t, struct if_dqblk *);
+	int (*get_xstate)(struct super_block *, struct fs_quota_stat *);
+	int (*set_xstate)(struct super_block *, unsigned int, int);
+	int (*get_xquota)(struct super_block *, int, qid_t, struct fs_disk_quota *);
+	int (*set_xquota)(struct super_block *, int, qid_t, struct fs_disk_quota *);
+};
+
+struct quota_format_type {
+	int qf_fmt_id;	/* Quota format id */
+	struct quota_format_ops *qf_ops;	/* Operations of format */
+	struct module *qf_owner;		/* Module implementing quota format */
+	struct quota_format_type *qf_next;
+};
+
+#define DQUOT_USR_ENABLED	0x01		/* User diskquotas enabled */
+#define DQUOT_GRP_ENABLED	0x02		/* Group diskquotas enabled */
+
+struct quota_info {
+	unsigned int flags;			/* Flags for diskquotas on this device */
+	struct semaphore dqio_sem;		/* lock device while I/O in progress */
+	struct semaphore dqoff_sem;		/* serialize quota_off() and quota_on() on device */
+	struct file *files[MAXQUOTAS];		/* fp's to quotafiles */
+	struct mem_dqinfo info[MAXQUOTAS];	/* Information for each quota type */
+	struct quota_format_ops *ops[MAXQUOTAS];	/* Operations for each type */
+};
+
+/* Inline would be better but we need to dereference super_block which is not defined yet */
+#define mark_dquot_dirty(dquot) do {\
+	dquot->dq_flags |= DQ_MOD;\
+	sb_dqopt(dquot->dq_sb)->info[dquot->dq_type].dqi_flags |= DQF_ANY_DQUOT_DIRTY;\
+} while (0)
+
+#define dquot_dirty(dquot) ((dquot)->dq_flags & DQ_MOD)
+
+static inline int is_enabled(struct quota_info *dqopt, int type)
+{
+	switch (type) {
+		case USRQUOTA:
+			return dqopt->flags & DQUOT_USR_ENABLED;
+		case GRPQUOTA:
+			return dqopt->flags & DQUOT_GRP_ENABLED;
+	}
+	return 0;
+}
+
+#define sb_any_quota_enabled(sb) (is_enabled(sb_dqopt(sb), USRQUOTA) | is_enabled(sb_dqopt(sb), GRPQUOTA))
+
+#define sb_has_quota_enabled(sb, type) (is_enabled(sb_dqopt(sb), type))
+
+int register_quota_format(struct quota_format_type *fmt);
+void unregister_quota_format(struct quota_format_type *fmt);
+
 #else
 
 # /* nodep */ include <sys/cdefs.h>
 
 __BEGIN_DECLS
-long quotactl __P ((int, const char *, int, caddr_t));
+long quotactl __P ((unsigned int, const char *, int, caddr_t));
 __END_DECLS
 
 #endif /* __KERNEL__ */
diff -urN linux-2.4.22-pre3/include/linux/quotacompat.h linux-2.4.22-pre4/include/linux/quotacompat.h
--- linux-2.4.22-pre3/include/linux/quotacompat.h	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.4.22-pre4/include/linux/quotacompat.h	2003-07-09 16:56:30.000000000 -0700
@@ -0,0 +1,86 @@
+/*
+ *	Definition of symbols used for backward compatible interface
+ */
+
+#ifndef _LINUX_QUOTACOMPAT_
+#define _LINUX_QUOTACOMPAT_
+
+#include <linux/types.h>
+#include <linux/quota.h>
+
+struct v1c_mem_dqblk {
+	__u32 dqb_bhardlimit;	/* absolute limit on disk blks alloc */
+	__u32 dqb_bsoftlimit;	/* preferred limit on disk blks */
+	__u32 dqb_curblocks;	/* current block count */
+	__u32 dqb_ihardlimit;	/* maximum # allocated inodes */
+	__u32 dqb_isoftlimit;	/* preferred inode limit */
+	__u32 dqb_curinodes;	/* current # allocated inodes */
+	time_t dqb_btime;	/* time limit for excessive disk use */
+	time_t dqb_itime;	/* time limit for excessive files */
+};
+
+struct v1c_dqstats {
+	__u32 lookups;
+	__u32 drops;
+	__u32 reads;
+	__u32 writes;
+	__u32 cache_hits;
+	__u32 allocated_dquots;
+	__u32 free_dquots;
+	__u32 syncs;
+};
+
+struct v2c_mem_dqblk {
+	unsigned int dqb_ihardlimit;
+	unsigned int dqb_isoftlimit;
+	unsigned int dqb_curinodes;
+	unsigned int dqb_bhardlimit;
+	unsigned int dqb_bsoftlimit;
+	qsize_t dqb_curspace;
+	__kernel_time_t dqb_btime;
+	__kernel_time_t dqb_itime;
+};
+
+struct v2c_mem_dqinfo {
+	unsigned int dqi_bgrace;
+	unsigned int dqi_igrace;
+	unsigned int dqi_flags;
+	unsigned int dqi_blocks;
+	unsigned int dqi_free_blk;
+	unsigned int dqi_free_entry;
+};
+
+struct v2c_dqstats {
+	__u32 lookups;
+	__u32 drops;
+	__u32 reads;
+	__u32 writes;
+	__u32 cache_hits;
+	__u32 allocated_dquots;
+	__u32 free_dquots;
+	__u32 syncs;
+	__u32 version;
+};
+
+#define Q_COMP_QUOTAON  0x0100	/* enable quotas */
+#define Q_COMP_QUOTAOFF 0x0200	/* disable quotas */
+#define Q_COMP_SYNC     0x0600	/* sync disk copy of a filesystems quotas */
+
+#define Q_V1_GETQUOTA 0x0300	/* get limits and usage */
+#define Q_V1_SETQUOTA 0x0400	/* set limits and usage */
+#define Q_V1_SETUSE   0x0500	/* set usage */
+#define Q_V1_SETQLIM  0x0700	/* set limits */
+#define Q_V1_GETSTATS 0x0800	/* get collected stats */
+#define Q_V1_RSQUASH  0x1000	/* set root_squash option */
+
+#define Q_V2_SETQLIM  0x0700	/* set limits */
+#define Q_V2_GETINFO  0x0900	/* get info about quotas - graces, flags... */
+#define Q_V2_SETINFO  0x0A00	/* set info about quotas */
+#define Q_V2_SETGRACE 0x0B00	/* set inode and block grace */
+#define Q_V2_SETFLAGS 0x0C00	/* set flags for quota */
+#define Q_V2_GETQUOTA 0x0D00	/* get limits and usage */
+#define Q_V2_SETQUOTA 0x0E00	/* set limits and usage */
+#define Q_V2_SETUSE   0x0F00	/* set usage */
+#define Q_V2_GETSTATS 0x1100	/* get collected stats */
+
+#endif
diff -urN linux-2.4.22-pre3/include/linux/quotaio_v1.h linux-2.4.22-pre4/include/linux/quotaio_v1.h
--- linux-2.4.22-pre3/include/linux/quotaio_v1.h	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.4.22-pre4/include/linux/quotaio_v1.h	2003-07-09 16:56:30.000000000 -0700
@@ -0,0 +1,33 @@
+#ifndef _LINUX_QUOTAIO_V1_H
+#define _LINUX_QUOTAIO_V1_H
+
+#include <linux/types.h>
+
+/*
+ * The following constants define the amount of time given a user
+ * before the soft limits are treated as hard limits (usually resulting
+ * in an allocation failure). The timer is started when the user crosses
+ * their soft limit, it is reset when they go below their soft limit.
+ */
+#define MAX_IQ_TIME  604800	/* (7*24*60*60) 1 week */
+#define MAX_DQ_TIME  604800	/* (7*24*60*60) 1 week */
+
+/*
+ * The following structure defines the format of the disk quota file
+ * (as it appears on disk) - the file is an array of these structures
+ * indexed by user or group number.
+ */
+struct v1_disk_dqblk {
+	__u32 dqb_bhardlimit;	/* absolute limit on disk blks alloc */
+	__u32 dqb_bsoftlimit;	/* preferred limit on disk blks */
+	__u32 dqb_curblocks;	/* current block count */
+	__u32 dqb_ihardlimit;	/* absolute limit on allocated inodes */
+	__u32 dqb_isoftlimit;	/* preferred inode limit */
+	__u32 dqb_curinodes;	/* current # allocated inodes */
+	time_t dqb_btime;	/* time limit for excessive disk use */
+	time_t dqb_itime;	/* time limit for excessive inode use */
+};
+
+#define v1_dqoff(UID)      ((loff_t)((UID) * sizeof (struct v1_disk_dqblk)))
+
+#endif	/* _LINUX_QUOTAIO_V1_H */
diff -urN linux-2.4.22-pre3/include/linux/quotaio_v2.h linux-2.4.22-pre4/include/linux/quotaio_v2.h
--- linux-2.4.22-pre3/include/linux/quotaio_v2.h	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.4.22-pre4/include/linux/quotaio_v2.h	2003-07-09 16:56:30.000000000 -0700
@@ -0,0 +1,79 @@
+/*
+ *	Definitions of structures for vfsv0 quota format
+ */
+
+#ifndef _LINUX_QUOTAIO_V2_H
+#define _LINUX_QUOTAIO_V2_H
+
+#include <linux/types.h>
+#include <linux/quota.h>
+
+/*
+ * Definitions of magics and versions of current quota files
+ */
+#define V2_INITQMAGICS {\
+	0xd9c01f11,	/* USRQUOTA */\
+	0xd9c01927	/* GRPQUOTA */\
+}
+
+#define V2_INITQVERSIONS {\
+	0,		/* USRQUOTA */\
+	0		/* GRPQUOTA */\
+}
+
+/*
+ * The following structure defines the format of the disk quota file
+ * (as it appears on disk) - the file is a radix tree whose leaves point
+ * to blocks of these structures.
+ */
+struct v2_disk_dqblk {
+	__u32 dqb_id;		/* id this quota applies to */
+	__u32 dqb_ihardlimit;	/* absolute limit on allocated inodes */
+	__u32 dqb_isoftlimit;	/* preferred inode limit */
+	__u32 dqb_curinodes;	/* current # allocated inodes */
+	__u32 dqb_bhardlimit;	/* absolute limit on disk space (in QUOTABLOCK_SIZE) */
+	__u32 dqb_bsoftlimit;	/* preferred limit on disk space (in QUOTABLOCK_SIZE) */
+	__u64 dqb_curspace;	/* current space occupied (in bytes) */
+	__u64 dqb_btime;	/* time limit for excessive disk use */
+	__u64 dqb_itime;	/* time limit for excessive inode use */
+};
+
+/*
+ * Here are header structures as written on disk and their in-memory copies
+ */
+/* First generic header */
+struct v2_disk_dqheader {
+	__u32 dqh_magic;	/* Magic number identifying file */
+	__u32 dqh_version;	/* File version */
+};
+
+/* Header with type and version specific information */
+struct v2_disk_dqinfo {
+	__u32 dqi_bgrace;	/* Time before block soft limit becomes hard limit */
+	__u32 dqi_igrace;	/* Time before inode soft limit becomes hard limit */
+	__u32 dqi_flags;	/* Flags for quotafile (DQF_*) */
+	__u32 dqi_blocks;	/* Number of blocks in file */
+	__u32 dqi_free_blk;	/* Number of first free block in the list */
+	__u32 dqi_free_entry;	/* Number of block with at least one free entry */
+};
+
+/*
+ *  Structure of header of block with quota structures. It is padded to 16 bytes so
+ *  there will be space for exactly 18 quota-entries in a block
+ */
+struct v2_disk_dqdbheader {
+	__u32 dqdh_next_free;	/* Number of next block with free entry */
+	__u32 dqdh_prev_free;	/* Number of previous block with free entry */
+	__u16 dqdh_entries;	/* Number of valid entries in block */
+	__u16 dqdh_pad1;
+	__u32 dqdh_pad2;
+};
+
+#define V2_DQINFOOFF	sizeof(struct v2_disk_dqheader)	/* Offset of info header in file */
+#define V2_DQBLKSIZE_BITS	10
+#define V2_DQBLKSIZE	(1 << V2_DQBLKSIZE_BITS)	/* Size of block with quota structures */
+#define V2_DQTREEOFF	1		/* Offset of tree in file in blocks */
+#define V2_DQTREEDEPTH	4		/* Depth of quota tree */
+#define V2_DQSTRINBLK	((V2_DQBLKSIZE - sizeof(struct v2_disk_dqdbheader)) / sizeof(struct v2_disk_dqblk))	/* Number of entries in one blocks */
+
+#endif /* _LINUX_QUOTAIO_V2_H */
diff -urN linux-2.4.22-pre3/include/linux/quotaops.h linux-2.4.22-pre4/include/linux/quotaops.h
--- linux-2.4.22-pre3/include/linux/quotaops.h	2002-08-02 17:39:45.000000000 -0700
+++ linux-2.4.22-pre4/include/linux/quotaops.h	2003-07-09 16:56:30.000000000 -0700
@@ -20,15 +20,16 @@
 /*
  * declaration of quota_function calls in kernel.
  */
-extern void dquot_initialize(struct inode *inode, short type);
+extern void sync_dquots_dev(kdev_t dev, int type);
+extern void sync_dquots_sb(struct super_block *sb, int type);
+
+extern void dquot_initialize(struct inode *inode, int type);
 extern void dquot_drop(struct inode *inode);
-extern int  quota_off(struct super_block *sb, short type);
-extern int  sync_dquots(kdev_t dev, short type);
 
-extern int  dquot_alloc_block(struct inode *inode, unsigned long number, char prealloc);
+extern int  dquot_alloc_space(struct inode *inode, qsize_t number, int prealloc);
 extern int  dquot_alloc_inode(const struct inode *inode, unsigned long number);
 
-extern void dquot_free_block(struct inode *inode, unsigned long number);
+extern void dquot_free_space(struct inode *inode, qsize_t number);
 extern void dquot_free_inode(const struct inode *inode, unsigned long number);
 
 extern int  dquot_transfer(struct inode *inode, struct iattr *iattr);
@@ -36,7 +37,11 @@
 /*
  * Operations supported for diskquotas.
  */
-#define sb_any_quota_enabled(sb) ((sb)->s_dquot.flags & (DQUOT_USR_ENABLED | DQUOT_GRP_ENABLED))
+extern struct dquot_operations dquot_operations;
+extern struct quotactl_ops vfs_quotactl_ops;
+
+#define sb_dquot_ops (&dquot_operations)
+#define sb_quotactl_ops (&vfs_quotactl_ops)
 
 static __inline__ void DQUOT_INIT(struct inode *inode)
 {
@@ -59,50 +64,50 @@
 	unlock_kernel();
 }
 
-static __inline__ int DQUOT_PREALLOC_BLOCK_NODIRTY(struct inode *inode, int nr)
+static __inline__ int DQUOT_PREALLOC_SPACE_NODIRTY(struct inode *inode, qsize_t nr)
 {
 	lock_kernel();
 	if (sb_any_quota_enabled(inode->i_sb)) {
-		/* Number of used blocks is updated in alloc_block() */
-		if (inode->i_sb->dq_op->alloc_block(inode, fs_to_dq_blocks(nr, inode->i_sb->s_blocksize), 1) == NO_QUOTA) {
+		/* Used space is updated in alloc_space() */
+		if (inode->i_sb->dq_op->alloc_space(inode, nr, 1) == NO_QUOTA) {
 			unlock_kernel();
 			return 1;
 		}
 	}
 	else
-		inode->i_blocks += nr << (inode->i_sb->s_blocksize_bits - 9);
+		inode_add_bytes(inode, nr);
 	unlock_kernel();
 	return 0;
 }
 
-static __inline__ int DQUOT_PREALLOC_BLOCK(struct inode *inode, int nr)
+static __inline__ int DQUOT_PREALLOC_SPACE(struct inode *inode, qsize_t nr)
 {
 	int ret;
-        if (!(ret =  DQUOT_PREALLOC_BLOCK_NODIRTY(inode, nr)))
+        if (!(ret =  DQUOT_PREALLOC_SPACE_NODIRTY(inode, nr)))
 		mark_inode_dirty(inode);
 	return ret;
 }
 
-static __inline__ int DQUOT_ALLOC_BLOCK_NODIRTY(struct inode *inode, int nr)
+static __inline__ int DQUOT_ALLOC_SPACE_NODIRTY(struct inode *inode, qsize_t nr)
 {
 	lock_kernel();
 	if (sb_any_quota_enabled(inode->i_sb)) {
-		/* Number of used blocks is updated in alloc_block() */
-		if (inode->i_sb->dq_op->alloc_block(inode, fs_to_dq_blocks(nr, inode->i_sb->s_blocksize), 0) == NO_QUOTA) {
+		/* Used space is updated in alloc_space() */
+		if (inode->i_sb->dq_op->alloc_space(inode, nr, 0) == NO_QUOTA) {
 			unlock_kernel();
 			return 1;
 		}
 	}
 	else
-		inode->i_blocks += nr << (inode->i_sb->s_blocksize_bits - 9);
+		inode_add_bytes(inode, nr);
 	unlock_kernel();
 	return 0;
 }
 
-static __inline__ int DQUOT_ALLOC_BLOCK(struct inode *inode, int nr)
+static __inline__ int DQUOT_ALLOC_SPACE(struct inode *inode, qsize_t nr)
 {
 	int ret;
-	if (!(ret = DQUOT_ALLOC_BLOCK_NODIRTY(inode, nr)))
+	if (!(ret = DQUOT_ALLOC_SPACE_NODIRTY(inode, nr)))
 		mark_inode_dirty(inode);
 	return ret;
 }
@@ -121,19 +126,19 @@
 	return 0;
 }
 
-static __inline__ void DQUOT_FREE_BLOCK_NODIRTY(struct inode *inode, int nr)
+static __inline__ void DQUOT_FREE_SPACE_NODIRTY(struct inode *inode, qsize_t nr)
 {
 	lock_kernel();
 	if (sb_any_quota_enabled(inode->i_sb))
-		inode->i_sb->dq_op->free_block(inode, fs_to_dq_blocks(nr, inode->i_sb->s_blocksize));
+		inode->i_sb->dq_op->free_space(inode, nr);
 	else
-		inode->i_blocks -= nr << (inode->i_sb->s_blocksize_bits - 9);
+		inode_sub_bytes(inode, nr);
 	unlock_kernel();
 }
 
-static __inline__ void DQUOT_FREE_BLOCK(struct inode *inode, int nr)
+static __inline__ void DQUOT_FREE_SPACE(struct inode *inode, qsize_t nr)
 {
-	DQUOT_FREE_BLOCK_NODIRTY(inode, nr);
+	DQUOT_FREE_SPACE_NODIRTY(inode, nr);
 	mark_inode_dirty(inode);
 }
 
@@ -159,63 +164,86 @@
 	return 0;
 }
 
-#define DQUOT_SYNC(dev)	sync_dquots(dev, -1)
-#define DQUOT_OFF(sb)	quota_off(sb, -1)
+#define DQUOT_SYNC_DEV(dev)	sync_dquots_dev(dev, -1)
+#define DQUOT_SYNC_SB(sb)	sync_dquots_sb(sb, -1)
+
+static __inline__ int DQUOT_OFF(struct super_block *sb)
+{
+	int ret = -ENOSYS;
+
+	lock_kernel();
+	if (sb->s_qcop && sb->s_qcop->quota_off)
+		ret = sb->s_qcop->quota_off(sb, -1);
+	unlock_kernel();
+	return ret;
+}
 
 #else
 
 /*
  * NO-OP when quota not configured.
  */
+#define sb_dquot_ops				(NULL)
+#define sb_quotactl_ops				(NULL)
+#define sync_dquots_dev(dev, type)		do { } while(0)
 #define DQUOT_INIT(inode)			do { } while(0)
 #define DQUOT_DROP(inode)			do { } while(0)
 #define DQUOT_ALLOC_INODE(inode)		(0)
 #define DQUOT_FREE_INODE(inode)			do { } while(0)
-#define DQUOT_SYNC(dev)				do { } while(0)
+#define DQUOT_SYNC_DEV(dev)			do { } while(0)
+#define DQUOT_SYNC_SB(sb)			do { } while(0)
 #define DQUOT_OFF(sb)				do { } while(0)
 #define DQUOT_TRANSFER(inode, iattr)		(0)
-extern __inline__ int DQUOT_PREALLOC_BLOCK_NODIRTY(struct inode *inode, int nr)
+extern __inline__ int DQUOT_PREALLOC_SPACE_NODIRTY(struct inode *inode, qsize_t nr)
 {
 	lock_kernel();
-	inode->i_blocks += nr << (inode->i_sb->s_blocksize_bits - 9);
+	inode_add_bytes(inode, nr);
 	unlock_kernel();
 	return 0;
 }
 
-extern __inline__ int DQUOT_PREALLOC_BLOCK(struct inode *inode, int nr)
+extern __inline__ int DQUOT_PREALLOC_SPACE(struct inode *inode, qsize_t nr)
 {
-	DQUOT_PREALLOC_BLOCK_NODIRTY(inode, nr);
+	DQUOT_PREALLOC_SPACE_NODIRTY(inode, nr);
 	mark_inode_dirty(inode);
 	return 0;
 }
 
-extern __inline__ int DQUOT_ALLOC_BLOCK_NODIRTY(struct inode *inode, int nr)
+extern __inline__ int DQUOT_ALLOC_SPACE_NODIRTY(struct inode *inode, qsize_t nr)
 {
 	lock_kernel();
-	inode->i_blocks += nr << (inode->i_sb->s_blocksize_bits - 9);
+	inode_add_bytes(inode, nr);
 	unlock_kernel();
 	return 0;
 }
 
-extern __inline__ int DQUOT_ALLOC_BLOCK(struct inode *inode, int nr)
+extern __inline__ int DQUOT_ALLOC_SPACE(struct inode *inode, qsize_t nr)
 {
-	DQUOT_ALLOC_BLOCK_NODIRTY(inode, nr);
+	DQUOT_ALLOC_SPACE_NODIRTY(inode, nr);
 	mark_inode_dirty(inode);
 	return 0;
 }
 
-extern __inline__ void DQUOT_FREE_BLOCK_NODIRTY(struct inode *inode, int nr)
+extern __inline__ void DQUOT_FREE_SPACE_NODIRTY(struct inode *inode, qsize_t nr)
 {
 	lock_kernel();
-	inode->i_blocks -= nr << (inode->i_sb->s_blocksize_bits - 9);
+	inode_sub_bytes(inode, nr);
 	unlock_kernel();
 }
 
-extern __inline__ void DQUOT_FREE_BLOCK(struct inode *inode, int nr)
+extern __inline__ void DQUOT_FREE_SPACE(struct inode *inode, qsize_t nr)
 {
-	DQUOT_FREE_BLOCK_NODIRTY(inode, nr);
+	DQUOT_FREE_SPACE_NODIRTY(inode, nr);
 	mark_inode_dirty(inode);
 }	
 
 #endif /* CONFIG_QUOTA */
+
+#define DQUOT_PREALLOC_BLOCK_NODIRTY(inode, nr)	DQUOT_PREALLOC_SPACE_NODIRTY(inode, ((qsize_t)(nr)) << (inode)->i_sb->s_blocksize_bits)
+#define DQUOT_PREALLOC_BLOCK(inode, nr)	DQUOT_PREALLOC_SPACE(inode, ((qsize_t)(nr)) << (inode)->i_sb->s_blocksize_bits)
+#define DQUOT_ALLOC_BLOCK_NODIRTY(inode, nr) DQUOT_ALLOC_SPACE_NODIRTY(inode, ((qsize_t)(nr)) << (inode)->i_sb->s_blocksize_bits)
+#define DQUOT_ALLOC_BLOCK(inode, nr) DQUOT_ALLOC_SPACE(inode, ((qsize_t)(nr)) << (inode)->i_sb->s_blocksize_bits)
+#define DQUOT_FREE_BLOCK_NODIRTY(inode, nr) DQUOT_FREE_SPACE_NODIRTY(inode, ((qsize_t)(nr)) << (inode)->i_sb->s_blocksize_bits)
+#define DQUOT_FREE_BLOCK(inode, nr) DQUOT_FREE_SPACE(inode, ((qsize_t)(nr)) << (inode)->i_sb->s_blocksize_bits)
+
 #endif /* _LINUX_QUOTAOPS_ */
diff -urN linux-2.4.22-pre3/include/linux/sysctl.h linux-2.4.22-pre4/include/linux/sysctl.h
--- linux-2.4.22-pre3/include/linux/sysctl.h	2003-07-09 16:56:26.000000000 -0700
+++ linux-2.4.22-pre4/include/linux/sysctl.h	2003-07-09 16:56:30.000000000 -0700
@@ -550,7 +550,7 @@
 	FS_STATINODE=2,
 	FS_MAXINODE=3,	/* int:maximum number of inodes that can be allocated */
 	FS_NRDQUOT=4,	/* int:current number of allocated dquots */
-	FS_MAXDQUOT=5,	/* int:maximum number of dquots that can be allocated */
+	/* was FS_MAXDQUOT */
 	FS_NRFILE=6,	/* int:current number of allocated filedescriptors */
 	FS_MAXFILE=7,	/* int:maximum number of filedescriptors that can be allocated */
 	FS_DENTRY=8,
@@ -561,6 +561,20 @@
 	FS_LEASES=13,	/* int: leases enabled */
 	FS_DIR_NOTIFY=14,	/* int: directory notification enabled */
 	FS_LEASE_TIME=15,	/* int: maximum time to wait for a lease break */
+	FS_DQSTATS=16,	/* dir: disc quota usage statistics */
+	FS_XFS=17,	/* struct: control xfs parameters */
+};
+
+/* /proc/sys/fs/quota/ */
+enum {
+	FS_DQ_LOOKUPS = 1,
+	FS_DQ_DROPS = 2,
+	FS_DQ_READS = 3,
+	FS_DQ_WRITES = 4,
+	FS_DQ_CACHE_HITS = 5,
+	FS_DQ_ALLOCATED = 6,
+	FS_DQ_FREE = 7,
+	FS_DQ_SYNCS = 8,
 };
 
 /* CTL_DEBUG names: */
diff -urN linux-2.4.22-pre3/include/net/pkt_sched.h linux-2.4.22-pre4/include/net/pkt_sched.h
--- linux-2.4.22-pre3/include/net/pkt_sched.h	2002-08-02 17:39:46.000000000 -0700
+++ linux-2.4.22-pre4/include/net/pkt_sched.h	2003-07-09 16:56:30.000000000 -0700
@@ -59,7 +59,7 @@
 	int 			(*enqueue)(struct sk_buff *, struct Qdisc *);
 	struct sk_buff *	(*dequeue)(struct Qdisc *);
 	int 			(*requeue)(struct sk_buff *, struct Qdisc *);
-	int			(*drop)(struct Qdisc *);
+	unsigned int		(*drop)(struct Qdisc *);
 
 	int			(*init)(struct Qdisc *, struct rtattr *arg);
 	void			(*reset)(struct Qdisc *);
diff -urN linux-2.4.22-pre3/kernel/fork.c linux-2.4.22-pre4/kernel/fork.c
--- linux-2.4.22-pre3/kernel/fork.c	2003-06-13 07:51:39.000000000 -0700
+++ linux-2.4.22-pre4/kernel/fork.c	2003-07-09 16:56:30.000000000 -0700
@@ -448,6 +448,11 @@
 		goto out;
 	}
 
+	/*
+	 * Note: we may be using current for both targets (See exec.c)
+	 * This works because we cache current->files (old) as oldf. Don't
+	 * break this.
+	 */
 	tsk->files = NULL;
 	error = -ENOMEM;
 	newf = kmem_cache_alloc(files_cachep, SLAB_KERNEL);
@@ -537,6 +542,33 @@
 	goto out;
 }
 
+/*
+ *	Helper to unshare the files of the current task. 
+ *	We don't want to expose copy_files internals to 
+ *	the exec layer of the kernel.
+ */
+
+int unshare_files(void)
+{
+	struct files_struct *files  = current->files;
+	int rc;
+	
+	if(!files)
+		BUG();
+		
+	/* This can race but the race causes us to copy when we don't
+	   need to and drop the copy */
+	if(atomic_read(&files->count) == 1)
+	{
+		atomic_inc(&files->count);
+		return 0;
+	}
+	rc = copy_files(0, current);
+	if(rc)
+		current->files = files;
+	return rc;
+}		
+
 static inline int copy_sighand(unsigned long clone_flags, struct task_struct * tsk)
 {
 	struct signal_struct *sig;
diff -urN linux-2.4.22-pre3/kernel/ksyms.c linux-2.4.22-pre4/kernel/ksyms.c
--- linux-2.4.22-pre3/kernel/ksyms.c	2003-07-09 16:56:26.000000000 -0700
+++ linux-2.4.22-pre4/kernel/ksyms.c	2003-07-09 16:56:30.000000000 -0700
@@ -590,6 +590,7 @@
 
 EXPORT_SYMBOL(tasklist_lock);
 EXPORT_SYMBOL(pidhash);
+EXPORT_SYMBOL(unshare_files);
 
 /* debug */
 EXPORT_SYMBOL(dump_stack);
diff -urN linux-2.4.22-pre3/kernel/sys.c linux-2.4.22-pre4/kernel/sys.c
--- linux-2.4.22-pre3/kernel/sys.c	2003-07-09 16:56:26.000000000 -0700
+++ linux-2.4.22-pre4/kernel/sys.c	2003-07-09 16:56:30.000000000 -0700
@@ -1143,6 +1143,8 @@
 		return -EINVAL;
 	if(copy_from_user(&new_rlim, rlim, sizeof(*rlim)))
 		return -EFAULT;
+       if (new_rlim.rlim_cur > new_rlim.rlim_max)
+               return -EINVAL;
 	old_rlim = current->rlim + resource;
 	if (((new_rlim.rlim_cur > old_rlim->rlim_max) ||
 	     (new_rlim.rlim_max > old_rlim->rlim_max)) &&
diff -urN linux-2.4.22-pre3/kernel/sysctl.c linux-2.4.22-pre4/kernel/sysctl.c
--- linux-2.4.22-pre3/kernel/sysctl.c	2003-07-09 16:56:26.000000000 -0700
+++ linux-2.4.22-pre4/kernel/sysctl.c	2003-07-09 16:56:30.000000000 -0700
@@ -306,8 +306,6 @@
 	 0444, NULL, &proc_dointvec},
 	{FS_MAXFILE, "file-max", &files_stat.max_files, sizeof(int),
 	 0644, NULL, &proc_dointvec},
-	{FS_NRDQUOT, "dquot-nr", &nr_dquots, 2*sizeof(int),
-	 0444, NULL, &proc_dointvec},
 	{FS_DENTRY, "dentry-state", &dentry_stat, 6*sizeof(int),
 	 0444, NULL, &proc_dointvec},
 	{FS_OVERFLOWUID, "overflowuid", &fs_overflowuid, sizeof(int), 0644, NULL,
diff -urN linux-2.4.22-pre3/mm/oom_kill.c linux-2.4.22-pre4/mm/oom_kill.c
--- linux-2.4.22-pre3/mm/oom_kill.c	2002-11-28 15:53:15.000000000 -0800
+++ linux-2.4.22-pre4/mm/oom_kill.c	2003-07-09 16:56:30.000000000 -0700
@@ -61,6 +61,10 @@
 
 	if (!p->mm)
 		return 0;
+
+	if (p->flags & PF_MEMDIE)
+		return 0;
+
 	/*
 	 * The memory size of the process is the basis for the badness.
 	 */
diff -urN linux-2.4.22-pre3/net/ipv4/ipconfig.c linux-2.4.22-pre4/net/ipv4/ipconfig.c
--- linux-2.4.22-pre3/net/ipv4/ipconfig.c	2002-11-28 15:53:15.000000000 -0800
+++ linux-2.4.22-pre4/net/ipv4/ipconfig.c	2003-07-09 16:56:30.000000000 -0700
@@ -1114,6 +1114,10 @@
 				       "nameserver %u.%u.%u.%u\n",
 				       NIPQUAD(ic_nameservers[i]));
 	}
+	if (ic_servaddr != INADDR_NONE)
+		len += sprintf(buffer + len,
+			       "bootserver %u.%u.%u.%u\n",
+			       NIPQUAD(ic_servaddr));
 
 	if (offset > len)
 		offset = len;
diff -urN linux-2.4.22-pre3/net/netsyms.c linux-2.4.22-pre4/net/netsyms.c
--- linux-2.4.22-pre3/net/netsyms.c	2003-07-09 16:56:26.000000000 -0700
+++ linux-2.4.22-pre4/net/netsyms.c	2003-07-09 16:56:30.000000000 -0700
@@ -560,6 +560,7 @@
 #ifdef CONFIG_NET_SCHED
 PSCHED_EXPORTLIST;
 EXPORT_SYMBOL(pfifo_qdisc_ops);
+EXPORT_SYMBOL(bfifo_qdisc_ops);
 EXPORT_SYMBOL(register_qdisc);
 EXPORT_SYMBOL(unregister_qdisc);
 EXPORT_SYMBOL(qdisc_get_rtab);
diff -urN linux-2.4.22-pre3/net/packet/af_packet.c linux-2.4.22-pre4/net/packet/af_packet.c
--- linux-2.4.22-pre3/net/packet/af_packet.c	2002-08-02 17:39:46.000000000 -0700
+++ linux-2.4.22-pre4/net/packet/af_packet.c	2003-07-09 16:56:30.000000000 -0700
@@ -1378,8 +1378,13 @@
 		po = sk->protinfo.af_packet;
 
 		switch (msg) {
-		case NETDEV_DOWN:
 		case NETDEV_UNREGISTER:
+#ifdef CONFIG_PACKET_MULTICAST
+			if (po->mclist)
+				packet_dev_mclist(dev, po->mclist, -1);
+			// fallthrough
+#endif
+		case NETDEV_DOWN:
 			if (dev->ifindex == po->ifindex) {
 				spin_lock(&po->bind_lock);
 				if (po->running) {
@@ -1396,10 +1401,6 @@
 				}
 				spin_unlock(&po->bind_lock);
 			}
-#ifdef CONFIG_PACKET_MULTICAST
-			if (po->mclist)
-				packet_dev_mclist(dev, po->mclist, -1);
-#endif
 			break;
 		case NETDEV_UP:
 			spin_lock(&po->bind_lock);
@@ -1409,10 +1410,6 @@
 				po->running = 1;
 			}
 			spin_unlock(&po->bind_lock);
-#ifdef CONFIG_PACKET_MULTICAST
-			if (po->mclist)
-				packet_dev_mclist(dev, po->mclist, +1);
-#endif
 			break;
 		}
 	}
diff -urN linux-2.4.22-pre3/net/sched/sch_atm.c linux-2.4.22-pre4/net/sched/sch_atm.c
--- linux-2.4.22-pre3/net/sched/sch_atm.c	2003-07-09 16:56:26.000000000 -0700
+++ linux-2.4.22-pre4/net/sched/sch_atm.c	2003-07-09 16:56:30.000000000 -0700
@@ -547,15 +547,16 @@
 }
 
 
-static int atm_tc_drop(struct Qdisc *sch)
+static unsigned int atm_tc_drop(struct Qdisc *sch)
 {
 	struct atm_qdisc_data *p = PRIV(sch);
 	struct atm_flow_data *flow;
+	unsigned int len;
 
 	DPRINTK("atm_tc_drop(sch %p,[qdisc %p])\n",sch,p);
 	for (flow = p->flows; flow; flow = flow->next)
-		if (flow->q->ops->drop && flow->q->ops->drop(flow->q))
-			return 1;
+		if (flow->q->ops->drop && (len = flow->q->ops->drop(flow->q)))
+			return len;
 	return 0;
 }
 
diff -urN linux-2.4.22-pre3/net/sched/sch_cbq.c linux-2.4.22-pre4/net/sched/sch_cbq.c
--- linux-2.4.22-pre3/net/sched/sch_cbq.c	2003-07-09 16:56:26.000000000 -0700
+++ linux-2.4.22-pre4/net/sched/sch_cbq.c	2003-07-09 16:56:30.000000000 -0700
@@ -1231,11 +1231,12 @@
 	}
 }
 
-static int cbq_drop(struct Qdisc* sch)
+static unsigned int cbq_drop(struct Qdisc* sch)
 {
 	struct cbq_sched_data *q = (struct cbq_sched_data *)sch->data;
 	struct cbq_class *cl, *cl_head;
 	int prio;
+	unsigned int len;
 
 	for (prio = TC_CBQ_MAXPRIO; prio >= 0; prio--) {
 		if ((cl_head = q->active[prio]) == NULL)
@@ -1243,9 +1244,9 @@
 
 		cl = cl_head;
 		do {
-			if (cl->q->ops->drop && cl->q->ops->drop(cl->q)) {
+			if (cl->q->ops->drop && (len = cl->q->ops->drop(cl->q))) {
 				sch->q.qlen--;
-				return 1;
+				return len;
 			}
 		} while ((cl = cl->next_alive) != cl_head);
 	}
diff -urN linux-2.4.22-pre3/net/sched/sch_dsmark.c linux-2.4.22-pre4/net/sched/sch_dsmark.c
--- linux-2.4.22-pre3/net/sched/sch_dsmark.c	2001-12-21 09:42:06.000000000 -0800
+++ linux-2.4.22-pre4/net/sched/sch_dsmark.c	2003-07-09 16:56:30.000000000 -0700
@@ -301,17 +301,18 @@
 }
 
 
-static int dsmark_drop(struct Qdisc *sch)
+static unsigned int dsmark_drop(struct Qdisc *sch)
 {
 	struct dsmark_qdisc_data *p = PRIV(sch);
-
+	unsigned int len;
+	
 	DPRINTK("dsmark_reset(sch %p,[qdisc %p])\n",sch,p);
 	if (!p->q->ops->drop)
 		return 0;
-	if (!p->q->ops->drop(p->q))
+	if (!(len = p->q->ops->drop(p->q)))
 		return 0;
 	sch->q.qlen--;
-	return 1;
+	return len;
 }
 
 
diff -urN linux-2.4.22-pre3/net/sched/sch_fifo.c linux-2.4.22-pre4/net/sched/sch_fifo.c
--- linux-2.4.22-pre3/net/sched/sch_fifo.c	2001-12-21 09:42:06.000000000 -0800
+++ linux-2.4.22-pre4/net/sched/sch_fifo.c	2003-07-09 16:56:30.000000000 -0700
@@ -80,16 +80,17 @@
 	return skb;
 }
 
-static int
+static unsigned int 
 fifo_drop(struct Qdisc* sch)
 {
 	struct sk_buff *skb;
 
 	skb = __skb_dequeue_tail(&sch->q);
 	if (skb) {
-		sch->stats.backlog -= skb->len;
+		unsigned int len = skb->len;
+		sch->stats.backlog -= len;
 		kfree_skb(skb);
-		return 1;
+		return len;
 	}
 	return 0;
 }
diff -urN linux-2.4.22-pre3/net/sched/sch_gred.c linux-2.4.22-pre4/net/sched/sch_gred.c
--- linux-2.4.22-pre3/net/sched/sch_gred.c	2003-06-13 07:51:39.000000000 -0700
+++ linux-2.4.22-pre4/net/sched/sch_gred.c	2003-07-09 16:56:30.000000000 -0700
@@ -259,8 +259,7 @@
 	return NULL;
 }
 
-static int
-gred_drop(struct Qdisc* sch)
+static unsigned int gred_drop(struct Qdisc* sch)
 {
 	struct sk_buff *skb;
 
@@ -269,20 +268,21 @@
 
 	skb = __skb_dequeue_tail(&sch->q);
 	if (skb) {
-		sch->stats.backlog -= skb->len;
+		unsigned int len = skb->len;
+		sch->stats.backlog -= len;
 		sch->stats.drops++;
 		q= t->tab[(skb->tc_index&0xf)];
 		if (q) {
-			q->backlog -= skb->len;
+			q->backlog -= len;
 			q->other++;
 			if (!q->backlog && !t->eqp)
 				PSCHED_GET_TIME(q->qidlestart);
-			} else {
-				D2PRINTK("gred_dequeue: skb has bad tcindex %x\n",skb->tc_index&0xf); 
-			}
+		} else {
+			D2PRINTK("gred_dequeue: skb has bad tcindex %x\n",skb->tc_index&0xf); 
+		}
 
 		kfree_skb(skb);
-		return 1;
+		return len;
 	}
 
 	q=t->tab[t->def];
diff -urN linux-2.4.22-pre3/net/sched/sch_htb.c linux-2.4.22-pre4/net/sched/sch_htb.c
--- linux-2.4.22-pre3/net/sched/sch_htb.c	2003-07-09 16:56:26.000000000 -0700
+++ linux-2.4.22-pre4/net/sched/sch_htb.c	2003-07-09 16:56:30.000000000 -0700
@@ -1070,7 +1070,7 @@
 }
 
 /* try to drop from each class (by prio) until one succeed */
-static int htb_drop(struct Qdisc* sch)
+static unsigned int htb_drop(struct Qdisc* sch)
 {
 	struct htb_sched *q = (struct htb_sched *)sch->data;
 	int prio;
@@ -1078,14 +1078,15 @@
 	for (prio = TC_HTB_NUMPRIO - 1; prio >= 0; prio--) {
 		struct list_head *p;
 		list_for_each (p,q->drops+prio) {
-			struct htb_class *cl = list_entry(p,struct htb_class,
-					un.leaf.drop_list);
+			struct htb_class *cl = list_entry(p, struct htb_class,
+							  un.leaf.drop_list);
+			unsigned int len;
 			if (cl->un.leaf.q->ops->drop && 
-				cl->un.leaf.q->ops->drop(cl->un.leaf.q)) {
+				(len = cl->un.leaf.q->ops->drop(cl->un.leaf.q))) {
 				sch->q.qlen--;
 				if (!cl->un.leaf.q->q.qlen)
 					htb_deactivate (q,cl);
-				return 1;
+				return len;
 			}
 		}
 	}
diff -urN linux-2.4.22-pre3/net/sched/sch_ingress.c linux-2.4.22-pre4/net/sched/sch_ingress.c
--- linux-2.4.22-pre3/net/sched/sch_ingress.c	2003-07-09 16:56:26.000000000 -0700
+++ linux-2.4.22-pre4/net/sched/sch_ingress.c	2003-07-09 16:56:30.000000000 -0700
@@ -190,7 +190,7 @@
 	return 0;
 }
 
-static int ingress_drop(struct Qdisc *sch)
+static unsigned int ingress_drop(struct Qdisc *sch)
 {
 #ifdef DEBUG_INGRESS
 	struct ingress_qdisc_data *p = PRIV(sch);
diff -urN linux-2.4.22-pre3/net/sched/sch_prio.c linux-2.4.22-pre4/net/sched/sch_prio.c
--- linux-2.4.22-pre3/net/sched/sch_prio.c	2003-06-13 07:51:39.000000000 -0700
+++ linux-2.4.22-pre4/net/sched/sch_prio.c	2003-07-09 16:56:30.000000000 -0700
@@ -124,18 +124,18 @@
 
 }
 
-static int
-prio_drop(struct Qdisc* sch)
+static unsigned int prio_drop(struct Qdisc* sch)
 {
 	struct prio_sched_data *q = (struct prio_sched_data *)sch->data;
 	int prio;
+	unsigned int len;
 	struct Qdisc *qdisc;
 
 	for (prio = q->bands-1; prio >= 0; prio--) {
 		qdisc = q->queues[prio];
-		if (qdisc->ops->drop(qdisc)) {
+		if ((len = qdisc->ops->drop(qdisc)) != 0) {
 			sch->q.qlen--;
-			return 1;
+			return len;
 		}
 	}
 	return 0;
diff -urN linux-2.4.22-pre3/net/sched/sch_red.c linux-2.4.22-pre4/net/sched/sch_red.c
--- linux-2.4.22-pre3/net/sched/sch_red.c	2001-12-21 09:42:06.000000000 -0800
+++ linux-2.4.22-pre4/net/sched/sch_red.c	2003-07-09 16:56:30.000000000 -0700
@@ -342,19 +342,19 @@
 	return NULL;
 }
 
-static int
-red_drop(struct Qdisc* sch)
+static unsigned int red_drop(struct Qdisc* sch)
 {
 	struct sk_buff *skb;
 	struct red_sched_data *q = (struct red_sched_data *)sch->data;
 
 	skb = __skb_dequeue_tail(&sch->q);
 	if (skb) {
-		sch->stats.backlog -= skb->len;
+		unsigned int len = skb->len;
+		sch->stats.backlog -= len;
 		sch->stats.drops++;
 		q->st.other++;
 		kfree_skb(skb);
-		return 1;
+		return len;
 	}
 	PSCHED_GET_TIME(q->qidlestart);
 	return 0;
diff -urN linux-2.4.22-pre3/net/sched/sch_sfq.c linux-2.4.22-pre4/net/sched/sch_sfq.c
--- linux-2.4.22-pre3/net/sched/sch_sfq.c	2003-06-13 07:51:39.000000000 -0700
+++ linux-2.4.22-pre4/net/sched/sch_sfq.c	2003-07-09 16:56:30.000000000 -0700
@@ -209,11 +209,12 @@
 	sfq_link(q, x);
 }
 
-static int sfq_drop(struct Qdisc *sch)
+static unsigned int sfq_drop(struct Qdisc *sch)
 {
 	struct sfq_sched_data *q = (struct sfq_sched_data *)sch->data;
 	sfq_index d = q->max_depth;
 	struct sk_buff *skb;
+	unsigned int len;
 
 	/* Queue is full! Find the longest slot and
 	   drop a packet from it */
@@ -221,12 +222,13 @@
 	if (d > 1) {
 		sfq_index x = q->dep[d+SFQ_DEPTH].next;
 		skb = q->qs[x].prev;
+		len = skb->len;
 		__skb_unlink(skb, &q->qs[x]);
 		kfree_skb(skb);
 		sfq_dec(q, x);
 		sch->q.qlen--;
 		sch->stats.drops++;
-		return 1;
+		return len;
 	}
 
 	if (d == 1) {
@@ -235,13 +237,14 @@
 		q->next[q->tail] = q->next[d];
 		q->allot[q->next[d]] += q->quantum;
 		skb = q->qs[d].prev;
+		len = skb->len;
 		__skb_unlink(skb, &q->qs[d]);
 		kfree_skb(skb);
 		sfq_dec(q, d);
 		sch->q.qlen--;
 		q->ht[q->hash[d]] = SFQ_DEPTH;
 		sch->stats.drops++;
-		return 1;
+		return len;
 	}
 
 	return 0;
diff -urN linux-2.4.22-pre3/net/sched/sch_tbf.c linux-2.4.22-pre4/net/sched/sch_tbf.c
--- linux-2.4.22-pre3/net/sched/sch_tbf.c	2001-12-21 09:42:06.000000000 -0800
+++ linux-2.4.22-pre4/net/sched/sch_tbf.c	2003-07-09 16:56:30.000000000 -0700
@@ -7,6 +7,8 @@
  *		2 of the License, or (at your option) any later version.
  *
  * Authors:	Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ *		Dmitry Torokhov <dtor@mail.ru> - allow attaching inner qdiscs -
+ *						 original idea by Martin Devera
  *
  */
 
@@ -123,62 +125,63 @@
 	long	ptokens;		/* Current number of P tokens */
 	psched_time_t	t_c;		/* Time check-point */
 	struct timer_list wd_timer;	/* Watchdog timer */
+	struct Qdisc	*qdisc;		/* Inner qdisc, default - bfifo queue */
 };
 
 #define L2T(q,L)   ((q)->R_tab->data[(L)>>(q)->R_tab->rate.cell_log])
 #define L2T_P(q,L) ((q)->P_tab->data[(L)>>(q)->P_tab->rate.cell_log])
 
-static int
-tbf_enqueue(struct sk_buff *skb, struct Qdisc* sch)
+static int tbf_enqueue(struct sk_buff *skb, struct Qdisc* sch)
 {
 	struct tbf_sched_data *q = (struct tbf_sched_data *)sch->data;
+	int ret;
 
-	if (skb->len > q->max_size)
-		goto drop;
-	__skb_queue_tail(&sch->q, skb);
-	if ((sch->stats.backlog += skb->len) <= q->limit) {
-		sch->stats.bytes += skb->len;
-		sch->stats.packets++;
-		return 0;
-	}
-
-	/* Drop action: undo the things that we just did,
-	 * i.e. make tail drop
-	 */
-
-	__skb_unlink(skb, &sch->q);
-	sch->stats.backlog -= skb->len;
-
-drop:
-	sch->stats.drops++;
+	if (skb->len > q->max_size || sch->stats.backlog + skb->len > q->limit) {
+		sch->stats.drops++;
 #ifdef CONFIG_NET_CLS_POLICE
-	if (sch->reshape_fail==NULL || sch->reshape_fail(skb, sch))
+		if (sch->reshape_fail == NULL || sch->reshape_fail(skb, sch))
 #endif
-		kfree_skb(skb);
-	return NET_XMIT_DROP;
-}
-
-static int
-tbf_requeue(struct sk_buff *skb, struct Qdisc* sch)
-{
-	__skb_queue_head(&sch->q, skb);
+			kfree_skb(skb);
+	
+		return NET_XMIT_DROP;
+	}
+	
+	if ((ret = q->qdisc->enqueue(skb, q->qdisc)) != 0) {
+		sch->stats.drops++;
+		return ret;
+	}	
+	
+	sch->q.qlen++;
 	sch->stats.backlog += skb->len;
+	sch->stats.bytes += skb->len;
+	sch->stats.packets++;
 	return 0;
 }
 
-static int
-tbf_drop(struct Qdisc* sch)
+static int tbf_requeue(struct sk_buff *skb, struct Qdisc* sch)
 {
-	struct sk_buff *skb;
+	struct tbf_sched_data *q = (struct tbf_sched_data *)sch->data;
+	int ret;
+	
+	if ((ret = q->qdisc->ops->requeue(skb, q->qdisc)) == 0) {
+		sch->q.qlen++; 
+		sch->stats.backlog += skb->len;
+	}
+	
+	return ret;
+}
 
-	skb = __skb_dequeue_tail(&sch->q);
-	if (skb) {
-		sch->stats.backlog -= skb->len;
+static unsigned int tbf_drop(struct Qdisc* sch)
+{
+	struct tbf_sched_data *q = (struct tbf_sched_data *)sch->data;
+	unsigned int len;
+	
+	if ((len = q->qdisc->ops->drop(q->qdisc)) != 0) {
+		sch->q.qlen--;
+		sch->stats.backlog -= len;
 		sch->stats.drops++;
-		kfree_skb(skb);
-		return 1;
 	}
-	return 0;
+	return len;
 }
 
 static void tbf_watchdog(unsigned long arg)
@@ -189,19 +192,19 @@
 	netif_schedule(sch->dev);
 }
 
-static struct sk_buff *
-tbf_dequeue(struct Qdisc* sch)
+static struct sk_buff *tbf_dequeue(struct Qdisc* sch)
 {
 	struct tbf_sched_data *q = (struct tbf_sched_data *)sch->data;
 	struct sk_buff *skb;
 	
-	skb = __skb_dequeue(&sch->q);
+	skb = q->qdisc->dequeue(q->qdisc);
 
 	if (skb) {
 		psched_time_t now;
 		long toks;
 		long ptoks = 0;
-
+		unsigned int len = skb->len;
+		
 		PSCHED_GET_TIME(now);
 
 		toks = PSCHED_TDIFF_SAFE(now, q->t_c, q->buffer, 0);
@@ -210,18 +213,19 @@
 			ptoks = toks + q->ptokens;
 			if (ptoks > (long)q->mtu)
 				ptoks = q->mtu;
-			ptoks -= L2T_P(q, skb->len);
+			ptoks -= L2T_P(q, len);
 		}
 		toks += q->tokens;
 		if (toks > (long)q->buffer)
 			toks = q->buffer;
-		toks -= L2T(q, skb->len);
+		toks -= L2T(q, len);
 
 		if ((toks|ptoks) >= 0) {
 			q->t_c = now;
 			q->tokens = toks;
 			q->ptokens = ptoks;
-			sch->stats.backlog -= skb->len;
+			sch->stats.backlog -= len;
+			sch->q.qlen--;
 			sch->flags &= ~TCQ_F_THROTTLED;
 			return skb;
 		}
@@ -245,20 +249,25 @@
 		   This is the main idea of all FQ algorithms
 		   (cf. CSZ, HPFQ, HFSC)
 		 */
-		__skb_queue_head(&sch->q, skb);
-
+		
+		if (q->qdisc->ops->requeue(skb, q->qdisc) != NET_XMIT_SUCCESS) {
+			/* When requeue fails skb is dropped */ 
+			sch->q.qlen--;
+			sch->stats.backlog -= len;
+			sch->stats.drops++;
+		}	
+		
 		sch->flags |= TCQ_F_THROTTLED;
 		sch->stats.overlimits++;
 	}
 	return NULL;
 }
 
-
-static void
-tbf_reset(struct Qdisc* sch)
+static void tbf_reset(struct Qdisc* sch)
 {
 	struct tbf_sched_data *q = (struct tbf_sched_data *)sch->data;
 
+	qdisc_reset(q->qdisc);
 	skb_queue_purge(&sch->q);
 	sch->stats.backlog = 0;
 	PSCHED_GET_TIME(q->t_c);
@@ -268,6 +277,31 @@
 	del_timer(&q->wd_timer);
 }
 
+static struct Qdisc *tbf_create_dflt_qdisc(struct net_device *dev, u32 limit)
+{
+	struct Qdisc *q = qdisc_create_dflt(dev, &bfifo_qdisc_ops);
+        struct rtattr *rta;
+	int ret;
+	
+	if (q) {
+		rta = kmalloc(RTA_LENGTH(sizeof(struct tc_fifo_qopt)), GFP_KERNEL);
+		if (rta) {
+			rta->rta_type = RTM_NEWQDISC;
+			rta->rta_len = RTA_LENGTH(sizeof(struct tc_fifo_qopt)); 
+			((struct tc_fifo_qopt *)RTA_DATA(rta))->limit = limit;
+			
+			ret = q->ops->change(q, rta);
+			kfree(rta);
+			
+			if (ret == 0)
+				return q;
+		}
+		qdisc_destroy(q);
+	}
+
+	return NULL;	
+}
+
 static int tbf_change(struct Qdisc* sch, struct rtattr *opt)
 {
 	int err = -EINVAL;
@@ -276,6 +310,7 @@
 	struct tc_tbf_qopt *qopt;
 	struct qdisc_rate_table *rtab = NULL;
 	struct qdisc_rate_table *ptab = NULL;
+	struct Qdisc *child = NULL;
 	int max_size,n;
 
 	if (rtattr_parse(tb, TCA_TBF_PTAB, RTA_DATA(opt), RTA_PAYLOAD(opt)) ||
@@ -308,8 +343,14 @@
 	}
 	if (max_size < 0)
 		goto done;
+	
+	if (q->qdisc == &noop_qdisc) {
+		if ((child = tbf_create_dflt_qdisc(sch->dev, qopt->limit)) == NULL)
+			goto done;
+	}
 
 	sch_tree_lock(sch);
+	if (child) q->qdisc = child;
 	q->limit = qopt->limit;
 	q->mtu = qopt->mtu;
 	q->max_size = max_size;
@@ -342,6 +383,8 @@
 	init_timer(&q->wd_timer);
 	q->wd_timer.function = tbf_watchdog;
 	q->wd_timer.data = (unsigned long)sch;
+
+	q->qdisc = &noop_qdisc;
 	
 	if ((err = tbf_change(sch, opt)) != 0) {
 		MOD_DEC_USE_COUNT;
@@ -359,6 +402,9 @@
 		qdisc_put_rtab(q->P_tab);
 	if (q->R_tab)
 		qdisc_put_rtab(q->R_tab);
+	
+	qdisc_destroy(q->qdisc);
+	q->qdisc = &noop_qdisc;
 
 	MOD_DEC_USE_COUNT;
 }
@@ -391,10 +437,93 @@
 	return -1;
 }
 
+static int tbf_dump_class(struct Qdisc *sch, unsigned long cl,
+	       		  struct sk_buff *skb, struct tcmsg *tcm)
+{
+	struct tbf_sched_data *q = (struct tbf_sched_data*)sch->data;
+
+	if (cl != 1) 	/* only one class */ 
+		return -ENOENT;
+    
+	tcm->tcm_parent = TC_H_ROOT;
+	tcm->tcm_handle = 1;
+	tcm->tcm_info = q->qdisc->handle;
+
+	return 0;
+}
+
+static int tbf_graft(struct Qdisc *sch, unsigned long arg, struct Qdisc *new,
+		     struct Qdisc **old)
+{
+	struct tbf_sched_data *q = (struct tbf_sched_data *)sch->data;
+
+	if (new == NULL)
+		new = &noop_qdisc;
+
+	sch_tree_lock(sch);	
+	*old = xchg(&q->qdisc, new);
+	qdisc_reset(*old);
+	sch_tree_unlock(sch);
+	
+	return 0;
+}
+
+static struct Qdisc *tbf_leaf(struct Qdisc *sch, unsigned long arg)
+{
+	struct tbf_sched_data *q = (struct tbf_sched_data *)sch->data;
+	return q->qdisc;
+}
+
+static unsigned long tbf_get(struct Qdisc *sch, u32 classid)
+{
+	return 1;
+}
+
+static void tbf_put(struct Qdisc *sch, unsigned long arg)
+{
+}
+
+static int tbf_change_class(struct Qdisc *sch, u32 classid, u32 parentid, 
+			struct rtattr **tca, unsigned long *arg)
+{
+	return -ENOSYS;
+}
+
+static int tbf_delete(struct Qdisc *sch, unsigned long arg)
+{
+	return -ENOSYS;
+}
+
+static void tbf_walk(struct Qdisc *sch, struct qdisc_walker *walker)
+{
+	struct tbf_sched_data *q = (struct tbf_sched_data *)sch->data;
+
+	if (!walker->stop) {
+		if (walker->count >= walker->skip) 
+			if (walker->fn(sch, (unsigned long)q, walker) < 0) { 
+				walker->stop = 1;
+				return;
+			}
+		walker->count++;
+	}
+}
+
+static struct Qdisc_class_ops tbf_class_ops =
+{
+	.graft		= 	tbf_graft,
+	.leaf		=	tbf_leaf,
+	.get		=	tbf_get,
+	.put		=	tbf_put,
+	.change		=	tbf_change_class,
+	.delete		=	tbf_delete,
+	.walk		=	tbf_walk,
+	.dump		=	tbf_dump_class,
+};
+
 struct Qdisc_ops tbf_qdisc_ops =
 {
 	NULL,
-	NULL,
+	&tbf_class_ops,
 	"tbf",
 	sizeof(struct tbf_sched_data),
 
diff -urN linux-2.4.22-pre3/net/sunrpc/clnt.c linux-2.4.22-pre4/net/sunrpc/clnt.c
--- linux-2.4.22-pre3/net/sunrpc/clnt.c	2003-07-09 16:56:26.000000000 -0700
+++ linux-2.4.22-pre4/net/sunrpc/clnt.c	2003-07-09 16:56:30.000000000 -0700
@@ -709,14 +709,14 @@
 
 	dprintk("RPC: %4d call_timeout (major)\n", task->tk_pid);
 	if (clnt->cl_softrtry) {
-		if (clnt->cl_chatty && !task->tk_exit)
+		if (clnt->cl_chatty)
 			printk(KERN_NOTICE "%s: server %s not responding, timed out\n",
 				clnt->cl_protname, clnt->cl_server);
 		rpc_exit(task, -EIO);
 		return;
 	}
 
-	if (clnt->cl_chatty && !(task->tk_flags & RPC_CALL_MAJORSEEN) && rpc_ntimeo(&clnt->cl_rtt) > 7) {
+	if (clnt->cl_chatty && !(task->tk_flags & RPC_CALL_MAJORSEEN)) {
 		task->tk_flags |= RPC_CALL_MAJORSEEN;
 		printk(KERN_NOTICE "%s: server %s not responding, still trying\n",
 			clnt->cl_protname, clnt->cl_server);
diff -urN linux-2.4.22-pre3/net/sunrpc/xprt.c linux-2.4.22-pre4/net/sunrpc/xprt.c
--- linux-2.4.22-pre3/net/sunrpc/xprt.c	2003-07-09 16:56:26.000000000 -0700
+++ linux-2.4.22-pre4/net/sunrpc/xprt.c	2003-07-09 16:56:30.000000000 -0700
@@ -1046,21 +1046,6 @@
 }
 
 /*
- * Exponential backoff for UDP retries
- */
-static inline int
-xprt_expbackoff(struct rpc_task *task, struct rpc_rqst *req)
-{
-	int backoff;
-
-	req->rq_ntimeo++;
-	backoff = min(rpc_ntimeo(&task->tk_client->cl_rtt), XPRT_MAX_BACKOFF);
-	if (req->rq_ntimeo < (1 << backoff))
-		return 1;
-	return 0;
-}
-
-/*
  * RPC receive timeout handler.
  */
 static void
@@ -1073,14 +1058,7 @@
 	if (req->rq_received)
 		goto out;
 
-	if (!xprt->nocong) {
-		if (xprt_expbackoff(task, req)) {
-			rpc_add_timer(task, xprt_timer);
-			goto out_unlock;
-		}
-		rpc_inc_timeo(&task->tk_client->cl_rtt);
-		xprt_adjust_cwnd(req->rq_xprt, -ETIMEDOUT);
-	}
+	xprt_adjust_cwnd(req->rq_xprt, -ETIMEDOUT);
 	req->rq_nresend++;
 
 	dprintk("RPC: %4d xprt_timer (%s request)\n",
@@ -1090,7 +1068,6 @@
 out:
 	task->tk_timeout = 0;
 	rpc_wake_up_task(task);
-out_unlock:
 	spin_unlock(&xprt->sock_lock);
 }
 
@@ -1228,16 +1205,17 @@
 	return;
  out_receive:
 	dprintk("RPC: %4d xmit complete\n", task->tk_pid);
+	spin_lock_bh(&xprt->sock_lock);
 	/* Set the task's receive timeout value */
 	if (!xprt->nocong) {
 		task->tk_timeout = rpc_calc_rto(&clnt->cl_rtt,
 				rpcproc_timer(clnt, task->tk_msg.rpc_proc));
-		req->rq_ntimeo = 0;
+		task->tk_timeout <<= clnt->cl_timeout.to_retries
+			- req->rq_timeout.to_retries;
 		if (task->tk_timeout > req->rq_timeout.to_maxval)
 			task->tk_timeout = req->rq_timeout.to_maxval;
 	} else
 		task->tk_timeout = req->rq_timeout.to_current;
-	spin_lock_bh(&xprt->sock_lock);
 	/* Don't race with disconnect */
 	if (!xprt_connected(xprt))
 		task->tk_status = -ENOTCONN;
