## Automatically generated incremental diff
## From:   linux-2.2.15-pre5
## To:     linux-2.2.15-pre6
## Robot:  $Id: make-incremental-diff,v 1.7 2001/12/04 05:19:54 hpa Exp $

diff -urN linux-2.2.15-pre5/CREDITS linux/CREDITS
--- linux-2.2.15-pre5/CREDITS	Mon Dec  3 21:58:40 2001
+++ linux/CREDITS	Mon Dec  3 21:58:46 2001
@@ -1408,7 +1408,7 @@
 S: Netherlands
 
 N: David S. Miller
-E: davem@dm.cobaltmicro.com
+E: davem@redhat.com
 D: Sparc and blue box hacker
 D: Vger Linux mailing list co-maintainer
 D: Linux Emacs elf/qmagic support + other libc/gcc things
@@ -2058,12 +2058,8 @@
 S: Germany
 
 N: Greg Ungerer
-E: gerg@stallion.com
+E: gerg@moreton.com.au
 D: Author of Stallion multiport serial drivers
-S: Stallion Technologies
-S: 33 Woodstock Rd
-S: Toowong, QLD. 4066
-S: Australia
 
 N: Jeffrey A. Uphoff
 E: juphoff@nrao.edu
diff -urN linux-2.2.15-pre5/Documentation/Configure.help linux/Documentation/Configure.help
--- linux-2.2.15-pre5/Documentation/Configure.help	Mon Dec  3 21:58:40 2001
+++ linux/Documentation/Configure.help	Mon Dec  3 21:58:46 2001
@@ -12103,6 +12103,33 @@
   If you do not have any Quicknet telephony cards, you can safely
   ignore this option.
  
+CPiA Video For Linux
+CONFIG_VIDEO_CPIA
+  This is the video4linux driver for cameras based on Vision's CPiA
+  (Colour Processor Interface ASIC), such as the Creative Labs Video
+  Blaster Webcam II. If you have one of these cameras, say Y here
+  and select parallel port lowlevel support below (the USB-version,
+  is not supported on this kernel-version) otherwise say N. This will
+  not work with the Creative Webcam III. It is also available as a
+  module (cpia.o).
+
+  For more information (supported camera models, module autoloading, DMA
+  interrupt settings, supported applications, etc.) read
+  Documentation/video4linux/README.cpia.
+
+CPiA Parallel Port Lowlevel Support
+CONFIG_VIDEO_CPIA_PP
+  This is the lowlevel parallel port support for cameras based on
+  Vision's CPiA (Colour Processor Interface ASIC), such as the
+  Creative Webcam II. If you have the parallel port version of one
+  of these cameras, say Y here, otherwise say N. It is also available
+  as a module (cpia_pp.o).
+
+CPiA Parallel Port DMA Support
+CONFIG_VIDEO_CPIA_PP_DMA
+  This will use DMA if possible to reduce CPU usage.  If in doubt,
+  say Y here.
+   
 #
 # A couple of things I keep forgetting:
 #   capitalize: AppleTalk, Ethernet, DOS, DMA, FAT, FTP, Internet, 
diff -urN linux-2.2.15-pre5/Documentation/README.DAC960 linux/Documentation/README.DAC960
--- linux-2.2.15-pre5/Documentation/README.DAC960	Wed Aug 25 17:29:45 1999
+++ linux/Documentation/README.DAC960	Mon Dec  3 21:58:46 2001
@@ -1,17 +1,17 @@
 	   Mylex DAC960/DAC1100 PCI RAID Controller Driver for Linux
 
-			Version 2.2.4 for Linux 2.2.11
-			Version 2.0.4 for Linux 2.0.37
+			Version 2.2.5 for Linux 2.2.14
+			Version 2.0.5 for Linux 2.0.38
 
 			      PRODUCTION RELEASE
 
-				23 August 1999
+				23 January 2000
 
 			       Leonard N. Zubkoff
 			       Dandelion Digital
 			       lnz@dandelion.com
 
-	 Copyright 1998-1999 by Leonard N. Zubkoff <lnz@dandelion.com>
+	 Copyright 1998-2000 by Leonard N. Zubkoff <lnz@dandelion.com>
 
 
 				 INTRODUCTION
@@ -87,7 +87,9 @@
 The following list comprises the supported DAC960 and DAC1100 PCI RAID
 Controllers as of the date of this document.  It is recommended that anyone
 purchasing a Mylex PCI RAID Controller not in the following table contact the
-author beforehand to verify that it is or will be supported.
+author beforehand to verify that it is or will be supported.  The eXtremeRAID
+2000, eXtremeRAID 3000, and AcceleRAID 352 have an entirely new firmware
+interface and are not yet supported by this driver.
 
 eXtremeRAID 1100 (DAC1164P)
 	    3 Wide Ultra-2/LVD SCSI channels
@@ -163,16 +165,16 @@
 
 			      DRIVER INSTALLATION
 
-This distribution was prepared for Linux kernel version 2.2.11 or 2.0.37.
+This distribution was prepared for Linux kernel version 2.2.14 or 2.0.38.
 
 To install the DAC960 RAID driver, you may use the following commands,
 replacing "/usr/src" with wherever you keep your Linux kernel source tree:
 
   cd /usr/src
-  tar -xvzf DAC960-2.2.4.tar.gz (or DAC960-2.0.4.tar.gz)
+  tar -xvzf DAC960-2.2.5.tar.gz (or DAC960-2.0.5.tar.gz)
   mv README.DAC960 linux/Documentation
   mv DAC960.[ch] linux/drivers/block
-  patch -p0 < DAC960.patch
+  patch -p0 < DAC960.patch (driver 2.0.5 only)
   cd linux
   make config
   make depend
diff -urN linux-2.2.15-pre5/Documentation/cpqarray.txt linux/Documentation/cpqarray.txt
--- linux-2.2.15-pre5/Documentation/cpqarray.txt	Mon Aug  9 12:04:38 1999
+++ linux/Documentation/cpqarray.txt	Mon Dec  3 21:58:46 2001
@@ -1,11 +1,5 @@
 This driver is for Compaq's SMART2 Intellegent Disk Array Controllers.
 
-WARNING:
---------
-
-This driver comes with NO WARRANTY.  It is not officially supported by
-Compaq.  Do not call technical support.  Use at your own risk.
-
 Supported Cards:
 ----------------
 
@@ -22,6 +16,7 @@
 	* Integrated Smart Array Controller
 	* SA 4200
 	* SA 4250ES
+	* SA 431
 
 It should also work with some really old Disk array adapters, but I am
 unable to test against these cards:
diff -urN linux-2.2.15-pre5/Documentation/networking/bonding.txt linux/Documentation/networking/bonding.txt
--- linux-2.2.15-pre5/Documentation/networking/bonding.txt	Mon Dec  3 21:58:40 2001
+++ linux/Documentation/networking/bonding.txt	Mon Dec  3 21:58:46 2001
@@ -1,7 +1,7 @@
 Installation:
 
 Apply patch.  (if your reading this in
-/usr/src/linux/Documentation/network/bonding.txt, then it's already
+/usr/src/linux/Documentation/networking/bonding.txt, then it's already
 applied.)
 
 Run make menuconfig/xconfig/config, and select 'bonding device' in
diff -urN linux-2.2.15-pre5/Documentation/video4linux/README.cpia linux/Documentation/video4linux/README.cpia
--- linux-2.2.15-pre5/Documentation/video4linux/README.cpia	Wed Dec 31 16:00:00 1969
+++ linux/Documentation/video4linux/README.cpia	Mon Dec  3 21:58:46 2001
@@ -0,0 +1,204 @@
+This is a driver for the CPiA PPC2 driven parallel connected
+Camera. For example the Creative WebcamII is CPiA driven.
+
+   © [1]Peter Pregler, Linz 2000, published under the [2]GNU GPL
+
+---------------------------------------------------------------------------
+
+USAGE:
+
+General:
+========
+
+1) Make sure you have created the video devices (/dev/video*):
+
+- if you have a recent MAKEDEV do a 'cd /dev;./MAKEDEV video'
+- otherwise do a:
+
+cd /dev
+mknod video0 c 81 0
+ln -s video0 video
+
+2) Compile the kernel (see below for the list of options to use),
+   configure your parport and reboot.
+
+3) If all worked well you should get messages similar
+   to the following (your versions may be different) on the console:
+
+V4L-Driver for Vision CPiA based cameras v0.5.0
+parport0: read2 timeout.
+parport0: Multimedia device, VLSI Vision Ltd PPC2
+Parallel port driver for Vision CPiA based camera
+  using DMA mode (irq 7, DMA 3)
+  CPIA Version: 1.20 (2.0)
+  CPIA PnP-ID: 0553:0002:0100
+  VP-Version: 1.0 0100
+  1 camera(s) found
+
+
+As modules:
+===========
+
+Make sure you have selected the following kernel options (you can
+select all stuff as modules):
+
+The cpia-stuff is in the section 'Character devices -> Video For Linux'.
+The PnP-stuff is in the section 'Plug and Play support'.
+The parport-stuff is in the section 'General setup'.
+
+CONFIG_PARPORT=m
+CONFIG_PARPORT_PC=m
+CONFIG_PNP=y
+CONFIG_PNP_PARPORT=m
+CONFIG_VIDEO_DEV=m
+CONFIG_VIDEO_CPIA=m
+CONFIG_VIDEO_CPIA_PP=m
+CONFIG_VIDEO_CPIA_PP_DMA=y
+
+For autoloading of all those modules you need to tell kerneld some
+stuff. Add the following line to your kerneld config-file
+(e.g. /etc/modules.conf or wherever your distribution does store that
+stuff):
+
+options parport_pc dma=3 irq=7
+alias char-major-81 cpia_pp
+
+The first line tells the dma/irq channels to use. Those _must_ match
+the settings of your BIOS. Do NOT simply use the values above.  See
+Documentation/parport.txt for more information about this. The second
+line associates the video-device file with the driver. Of cause you
+can also load the modules once upon boot (usually done in /etc/modules).
+
+Linked into the kernel:
+=======================
+
+Make sure you have selected the following kernel options. Note that
+you cannot compile the parport-stuff as modules and the cpia-driver
+statically (the other way round is okay though).
+
+The cpia-stuff is in the section 'Character devices -> Video For Linux'.
+The PnP-stuff is in the section 'Plug and Play support'.
+The parport-stuff is in the section 'General setup'.
+
+CONFIG_PARPORT=y
+CONFIG_PARPORT_PC=y
+CONFIG_PNP=y
+CONFIG_PNP_PARPORT=y
+CONFIG_VIDEO_DEV=y
+CONFIG_VIDEO_CPIA=y
+CONFIG_VIDEO_CPIA_PP=y
+CONFIG_VIDEO_CPIA_PP_DMA=y
+
+To use DMA/irq you will need to tell the kernel upon boot time the
+hardware configuration of the parport. You can give the boot-parameter
+at the LILO-prompt or specify it in lilo.conf. I use the following
+append-line in lilo.conf:
+
+        append="parport=0x378,7,3"
+
+See Documentation/parport.txt for more information about the
+configuration of the parport and the values given above. Do not simply
+use the values given above.
+
+---------------------------------------------------------------------------
+FEATURES:
+
+- mmap/read v4l-interface (but no overlay)
+- image formats: CIF/QCIF, SIF/QSIF, various others used by isabel;
+  note: all sizes except CIF/QCIF are implemented by clipping, i.e.
+  pixels are not uploaded from the camera
+- palettes: VIDEO_PALETTE_GRAY, VIDEO_PALETTE_RGB565, VIDEO_PALETTE_RGB555,
+  VIDEO_PALETTE_RGB24, VIDEO_PALETTE_RGB32, VIDEO_PALETTE_YUYV,
+  VIDEO_PALETTE_UYVY, VIDEO_PALETTE_YUV422
+- DMA-transfer for Intel-architecture
+- state information (color balance, exposure, ...) is preserved between
+  device opens
+- complete control over camera via proc-interface (_all_ camera settings are
+  supported), there is also a python-gtk application available for this
+- works under SMP (but the driver is completly serialized and synchronous)
+  so you get no benefit from SMP
+- might work for non-Intel architecture, let me know about this
+
+---------------------------------------------------------------------------
+TESTED APPLICATIONS:
+
+- a simple test application based on Xt is available at [3]
+- another test-application based on gqcam-0.4 (uses GTK)
+- gqcam-0.5 should work
+- xawtv-3.x (also the webcam)
+- xawtv-2.46
+- w3cam (cgi-interface and vidcat, e.g. you may try out 'vidcat  |xv
+  -maxpect -root -quit +noresetroot -rmode 5 -')
+- vic, the MBONE video conferencing tool (version 2.8ucl4-1)
+- isabel 3R4beta (barely working, but AFAICT all the problems are on
+  their side)
+- camserv-0.40
+
+See [3] for pointers to v4l-applications.
+
+---------------------------------------------------------------------------
+KNOWN PROBLEMS:
+
+- some applications do not handle the image format correctly, you will
+  see strange horizontal stripes instead of a nice picture -> make sure
+  your application does use a supported image size or queries the driver
+  for the actually used size (reason behind this: the camera cannot
+  provide any image format, so if size NxM is requested the driver will
+  use a format to the closest fitting N1xM1, the application should now
+  query for this granted size, most applications do not).
+- all the todo ;)
+- if there is not enough light and the picture is too dark try to
+  adjust the SetSensorFPS setting, automatic frame rate adjustment
+  has its price
+- do not try out isabel 3R4beta (built 135), you will be disappointed
+- my linux box crashed during a ppp-dialup session lately for an unknown
+  reason, the driver was idle, but who knows. So there might be
+  so buffer overflow somewhere, you are warned anyway
+
+---------------------------------------------------------------------------
+TODO:
+
+- port to linux 2.3.X
+- using ieee1284 support in the kernel (when it works)
+- multiple camera support (struct camera or something) - This should work,
+  but hasn't been tested yet.
+- architecture independence
+- integrate USB support
+- SMP-safe asynchronous mmap interface
+- nibble mode for old parport interfaces
+- streamin capture, this should give a performance gain
+
+---------------------------------------------------------------------------
+IMPLEMENTATION NOTES:
+
+The camera can act in two modes, streaming or grabbing. Right now a
+polling grab-scheme is used. Maybe interrupt driven streaming will be
+used for a ansychronous mmap interface in the next major release of the
+driver. This might give a better frame rate.
+
+---------------------------------------------------------------------------
+THANKS (in no particular order):
+
+- Karoly Erdei <Karoly.Erdei@risc.uni-linz.ac.at> and RISC-Linz for being
+  my boss ;) resp. my employer and for providing me the hardware and
+  allow me to devote some working time to this project
+- Bas Huisman <bhuism@cs.utwente.nl> for writing the initial parport code
+- Scott J. Bertin <sbertin@mindspring.com> for cleanups, the proc-filesystem
+  and much more
+- Jarl Totland <Jarl.Totland@bdc.no> for setting up the mailing list 
+  and maintaining the web-server[3]
+- Manuel J. Petit de Gabriel <mpetit@dit.upm.es> for providing help
+  with Isabel (http://isabel.dit.upm.es/)
+- Henry Bruce <whb@vvl.co.uk> for providing developers information about
+  the CPiA chip, I wish all companies would treat Linux as seriously
+- FIXME for fixes related to the 1.02 firmware
+- special kudos to all the tester whose machines crashed and/or
+  will crash. :)
+
+---------------------------------------------------------------------------
+REFERENCES
+
+   1. http://www.risc.uni-linz.ac.at/people/ppregler
+      mailto:Peter_Pregler@email.com
+   2. see the file COPYING in the top directory of the kernel tree
+   3. http://home.eunet.no/~jtotland/vision/
diff -urN linux-2.2.15-pre5/MAINTAINERS linux/MAINTAINERS
--- linux-2.2.15-pre5/MAINTAINERS	Mon Dec  3 21:58:40 2001
+++ linux/MAINTAINERS	Mon Dec  3 21:58:46 2001
@@ -815,9 +815,7 @@
 S:	Supported
 
 STALLION TECHNOLOGIES MULTIPORT SERIAL BOARDS
-P:	Greg Ungerer
 M:	support@stallion.oz.au
-M:	gerg@stallion.com
 W:	http://www.stallion.com
 S:	Supported
 
diff -urN linux-2.2.15-pre5/Makefile linux/Makefile
--- linux-2.2.15-pre5/Makefile	Mon Dec  3 21:58:40 2001
+++ linux/Makefile	Mon Dec  3 21:58:46 2001
@@ -1,7 +1,7 @@
 VERSION = 2
 PATCHLEVEL = 2
 SUBLEVEL = 15
-EXTRAVERSION = pre5
+EXTRAVERSION = pre6
 
 ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ -e s/arm.*/arm/ -e s/sa110/arm/)
 
diff -urN linux-2.2.15-pre5/arch/i386/kernel/setup.c linux/arch/i386/kernel/setup.c
--- linux-2.2.15-pre5/arch/i386/kernel/setup.c	Mon Dec  3 21:58:40 2001
+++ linux/arch/i386/kernel/setup.c	Mon Dec  3 21:58:46 2001
@@ -667,10 +667,18 @@
                 /* It isnt really a PCI quirk directly, but the cure is the
        	           same. The MediaGX has deep magic SMM stuff that handles the
                    SB emulation. It thows away the fifo on disable_dma() which
-                   is wrong and ruins the audio. */
+                   is wrong and ruins the audio. 
+                   
+                   Bug2: VSA1 has a wrap bug so that using maximum sized DMA 
+                   causes bad things. According to NatSemi VSA2 has another
+                   bug to do with 'hlt'. I've not seen any boards using VSA2
+                   and X doesn't seem to support it either so who cares 8).
+                   VSA1 we work around however.
+                   
+                  */
 
 		printk(KERN_INFO "Working around Cyrix MediaGX virtual DMA bug.\n");
-                isa_dma_bridge_buggy = 1;
+                isa_dma_bridge_buggy = 2;
                   	                                                                     	        
 #endif
 		/* GXm supports extended cpuid levels 'ala' AMD */
diff -urN linux-2.2.15-pre5/arch/ppc/kernel/head.S linux/arch/ppc/kernel/head.S
--- linux-2.2.15-pre5/arch/ppc/kernel/head.S	Tue Jan  4 10:12:12 2000
+++ linux/arch/ppc/kernel/head.S	Mon Dec  3 21:58:46 2001
@@ -1739,6 +1739,7 @@
 	.globl	__secondary_start_psurge
 __secondary_start_psurge:
 	li	r24,1			/* cpu # */
+	li	r3,0			/* starting physical address */
 	b	__secondary_start
 
 	.globl	__secondary_hold
diff -urN linux-2.2.15-pre5/arch/sparc/config.in linux/arch/sparc/config.in
--- linux-2.2.15-pre5/arch/sparc/config.in	Mon Dec  3 21:58:40 2001
+++ linux/arch/sparc/config.in	Mon Dec  3 21:58:46 2001
@@ -173,6 +173,7 @@
 	bool 'Network device support' CONFIG_NETDEVICES
 	if [ "$CONFIG_NETDEVICES" = "y" ]; then
 		tristate 'Dummy net driver support' CONFIG_DUMMY
+		tristate 'Bonding driver support' CONFIG_BONDING
 		tristate 'PPP (point-to-point) support' CONFIG_PPP
 		if [ ! "$CONFIG_PPP" = "n" ]; then
 		   comment 'CCP compressors for PPP are only built as modules.'
diff -urN linux-2.2.15-pre5/arch/sparc/defconfig linux/arch/sparc/defconfig
--- linux-2.2.15-pre5/arch/sparc/defconfig	Mon Dec  3 21:58:40 2001
+++ linux/arch/sparc/defconfig	Mon Dec  3 21:58:46 2001
@@ -203,6 +203,7 @@
 #
 CONFIG_NETDEVICES=y
 CONFIG_DUMMY=m
+CONFIG_BONDING=m
 CONFIG_PPP=m
 
 #
diff -urN linux-2.2.15-pre5/arch/sparc64/config.in linux/arch/sparc64/config.in
--- linux-2.2.15-pre5/arch/sparc64/config.in	Mon Dec  3 21:58:40 2001
+++ linux/arch/sparc64/config.in	Mon Dec  3 21:58:46 2001
@@ -217,6 +217,7 @@
 	bool 'Network device support' CONFIG_NETDEVICES
 	if [ "$CONFIG_NETDEVICES" = "y" ]; then
 		tristate 'Dummy net driver support' CONFIG_DUMMY
+		tristate 'Bonding driver support' CONFIG_BONDING
 		tristate 'PPP (point-to-point) support' CONFIG_PPP
 		if [ ! "$CONFIG_PPP" = "n" ]; then
 		   comment 'CCP compressors for PPP are only built as modules.'
diff -urN linux-2.2.15-pre5/arch/sparc64/defconfig linux/arch/sparc64/defconfig
--- linux-2.2.15-pre5/arch/sparc64/defconfig	Mon Dec  3 21:58:40 2001
+++ linux/arch/sparc64/defconfig	Mon Dec  3 21:58:46 2001
@@ -240,6 +240,7 @@
 #
 CONFIG_NETDEVICES=y
 CONFIG_DUMMY=m
+CONFIG_BONDING=m
 CONFIG_PPP=m
 
 #
diff -urN linux-2.2.15-pre5/arch/sparc64/kernel/binfmt_elf32.c linux/arch/sparc64/kernel/binfmt_elf32.c
--- linux-2.2.15-pre5/arch/sparc64/kernel/binfmt_elf32.c	Sun Oct  4 10:22:43 1998
+++ linux/arch/sparc64/kernel/binfmt_elf32.c	Mon Dec  3 21:58:46 2001
@@ -1,7 +1,7 @@
 /*
  * binfmt_elf32.c: Support 32-bit Sparc ELF binaries on Ultra.
  *
- * Copyright (C) 1995, 1996, 1997, 1998 David S. Miller	(davem@dm.cobaltmicro.com)
+ * Copyright (C) 1995, 1996, 1997, 1998 David S. Miller	(davem@redhat.com)
  * Copyright (C) 1995, 1996, 1997, 1998 Jakub Jelinek	(jj@ultra.linux.cz)
  */
 
diff -urN linux-2.2.15-pre5/arch/sparc64/kernel/dtlb_backend.S linux/arch/sparc64/kernel/dtlb_backend.S
--- linux-2.2.15-pre5/arch/sparc64/kernel/dtlb_backend.S	Tue Jan  4 10:12:13 2000
+++ linux/arch/sparc64/kernel/dtlb_backend.S	Mon Dec  3 21:58:46 2001
@@ -1,8 +1,8 @@
-/* $Id: dtlb_backend.S,v 1.7.2.1 1999/12/05 10:41:57 davem Exp $
+/* $Id: dtlb_backend.S,v 1.7.2.2 2000/01/31 05:02:35 davem Exp $
  * dtlb_backend.S: Back end to DTLB miss replacement strategy.
  *                 This is included directly into the trap table.
  *
- * Copyright (C) 1996,1998 David S. Miller (davem@dm.cobaltmicro.com)
+ * Copyright (C) 1996,1998 David S. Miller (davem@redhat.com)
  * Copyright (C) 1997,1998 Jakub Jelinek   (jj@ultra.linux.cz)
  */
 
diff -urN linux-2.2.15-pre5/arch/sparc64/kernel/dtlb_base.S linux/arch/sparc64/kernel/dtlb_base.S
--- linux-2.2.15-pre5/arch/sparc64/kernel/dtlb_base.S	Tue Aug  4 16:03:35 1998
+++ linux/arch/sparc64/kernel/dtlb_base.S	Mon Dec  3 21:58:46 2001
@@ -1,8 +1,8 @@
-/* $Id: dtlb_base.S,v 1.4 1998/06/15 16:59:30 jj Exp $
+/* $Id: dtlb_base.S,v 1.4.2.1 2000/01/31 05:02:35 davem Exp $
  * dtlb_base.S:	Front end to DTLB miss replacement strategy.
  *              This is included directly into the trap table.
  *
- * Copyright (C) 1996,1998 David S. Miller (davem@dm.cobaltmicro.com)
+ * Copyright (C) 1996,1998 David S. Miller (davem@redhat.com)
  * Copyright (C) 1997,1998 Jakub Jelinek   (jj@ultra.linux.cz)
  */
 
diff -urN linux-2.2.15-pre5/arch/sparc64/kernel/dtlb_prot.S linux/arch/sparc64/kernel/dtlb_prot.S
--- linux-2.2.15-pre5/arch/sparc64/kernel/dtlb_prot.S	Wed Mar 10 16:53:37 1999
+++ linux/arch/sparc64/kernel/dtlb_prot.S	Mon Dec  3 21:58:46 2001
@@ -1,8 +1,8 @@
-/* $Id: dtlb_prot.S,v 1.18 1999/03/02 15:42:14 jj Exp $
+/* $Id: dtlb_prot.S,v 1.18.2.1 2000/01/31 05:02:35 davem Exp $
  * dtlb_prot.S: DTLB protection trap strategy.
  *              This is included directly into the trap table.
  *
- * Copyright (C) 1996,1998 David S. Miller (davem@dm.cobaltmicro.com)
+ * Copyright (C) 1996,1998 David S. Miller (davem@redhat.com)
  * Copyright (C) 1997,1998 Jakub Jelinek   (jj@ultra.linux.cz)
  */
 
diff -urN linux-2.2.15-pre5/arch/sparc64/kernel/itlb_base.S linux/arch/sparc64/kernel/itlb_base.S
--- linux-2.2.15-pre5/arch/sparc64/kernel/itlb_base.S	Wed Mar 10 16:53:37 1999
+++ linux/arch/sparc64/kernel/itlb_base.S	Mon Dec  3 21:58:46 2001
@@ -1,8 +1,8 @@
-/* $Id: itlb_base.S,v 1.7 1999/03/02 15:42:12 jj Exp $
+/* $Id: itlb_base.S,v 1.7.2.1 2000/01/31 05:02:35 davem Exp $
  * itlb_base.S:	Front end to ITLB miss replacement strategy.
  *              This is included directly into the trap table.
  *
- * Copyright (C) 1996,1998 David S. Miller (davem@dm.cobaltmicro.com)
+ * Copyright (C) 1996,1998 David S. Miller (davem@redhat.com)
  * Copyright (C) 1997,1998 Jakub Jelinek   (jj@ultra.linux.cz)
  */
 
diff -urN linux-2.2.15-pre5/arch/sparc64/kernel/starfire.c linux/arch/sparc64/kernel/starfire.c
--- linux-2.2.15-pre5/arch/sparc64/kernel/starfire.c	Wed Mar 10 16:53:37 1999
+++ linux/arch/sparc64/kernel/starfire.c	Mon Dec  3 21:58:46 2001
@@ -1,7 +1,7 @@
-/* $Id: starfire.c,v 1.2 1998/12/09 18:53:11 davem Exp $
+/* $Id: starfire.c,v 1.2.2.1 2000/01/31 05:02:35 davem Exp $
  * starfire.c: Starfire/E10000 support.
  *
- * Copyright (C) 1998 David S. Miller (davem@dm.cobaltmicro.com)
+ * Copyright (C) 1998 David S. Miller (davem@redhat.com)
  */
 
 #include <linux/kernel.h>
diff -urN linux-2.2.15-pre5/arch/sparc64/lib/debuglocks.c linux/arch/sparc64/lib/debuglocks.c
--- linux-2.2.15-pre5/arch/sparc64/lib/debuglocks.c	Tue Oct 27 09:52:20 1998
+++ linux/arch/sparc64/lib/debuglocks.c	Mon Dec  3 21:58:46 2001
@@ -1,7 +1,7 @@
-/* $Id: debuglocks.c,v 1.2 1998/10/13 09:07:27 davem Exp $
+/* $Id: debuglocks.c,v 1.2.2.1 2000/01/31 05:02:31 davem Exp $
  * debuglocks.c: Debugging versions of SMP locking primitives.
  *
- * Copyright (C) 1998 David S. Miller (davem@dm.cobaltmicro.com)
+ * Copyright (C) 1998 David S. Miller (davem@redhat.com)
  */
 
 #include <linux/kernel.h>
diff -urN linux-2.2.15-pre5/arch/sparc64/lib/memscan.S linux/arch/sparc64/lib/memscan.S
--- linux-2.2.15-pre5/arch/sparc64/lib/memscan.S	Tue Aug  4 16:03:35 1998
+++ linux/arch/sparc64/lib/memscan.S	Mon Dec  3 21:58:46 2001
@@ -1,8 +1,8 @@
-/* $Id: memscan.S,v 1.2 1998/05/21 14:42:22 jj Exp $
+/* $Id: memscan.S,v 1.2.2.1 2000/01/31 05:02:32 davem Exp $
  * memscan.S: Optimized memscan for Sparc64.
  *
  * Copyright (C) 1997,1998 Jakub Jelinek (jj@ultra.linux.cz)
- * Copyright (C) 1998 David S. Miller (davem@dm.cobaltmicro.com)
+ * Copyright (C) 1998 David S. Miller (davem@redhat.com)
  */
 
 #define HI_MAGIC	0x8080808080808080
diff -urN linux-2.2.15-pre5/drivers/block/Config.in linux/drivers/block/Config.in
--- linux-2.2.15-pre5/drivers/block/Config.in	Tue Jan  4 10:12:14 2000
+++ linux/drivers/block/Config.in	Mon Dec  3 21:58:46 2001
@@ -44,6 +44,7 @@
             bool '     NS87415 chipset support (EXPERIMENTAL)' CONFIG_BLK_DEV_NS87415
             bool '     VIA82C586 chipset support (EXPERIMENTAL)' CONFIG_BLK_DEV_VIA82C586
             bool '     CMD646 chipset support (EXPERIMENTAL)' CONFIG_BLK_DEV_CMD646
+            bool '     Cyrix CS5530 MediaGX chipset support' CONFIG_BLK_DEV_CS5530 
           fi
         fi
       fi
diff -urN linux-2.2.15-pre5/drivers/block/DAC960.c linux/drivers/block/DAC960.c
--- linux-2.2.15-pre5/drivers/block/DAC960.c	Wed Aug 25 17:29:46 1999
+++ linux/drivers/block/DAC960.c	Mon Dec  3 21:58:46 2001
@@ -19,8 +19,8 @@
 */
 
 
-#define DAC960_DriverVersion			"2.2.4"
-#define DAC960_DriverDate			"23 August 1999"
+#define DAC960_DriverVersion			"2.2.5"
+#define DAC960_DriverDate			"23 January 2000"
 
 
 #include <linux/version.h>
@@ -120,7 +120,7 @@
   DAC960_Announce("***** DAC960 RAID Driver Version "
 		  DAC960_DriverVersion " of "
 		  DAC960_DriverDate " *****\n", Controller);
-  DAC960_Announce("Copyright 1998-1999 by Leonard N. Zubkoff "
+  DAC960_Announce("Copyright 1998-2000 by Leonard N. Zubkoff "
 		  "<lnz@dandelion.com>\n", Controller);
 }
 
@@ -194,6 +194,23 @@
 
 
 /*
+  DAC960_WaitForCommand waits for a wake_up on Controller's Command Wait Queue.
+*/
+
+static void DAC960_WaitForCommand(DAC960_Controller_T *Controller)
+{
+  WaitQueue_T WaitQueueEntry = { current, NULL };
+  add_wait_queue(&Controller->CommandWaitQueue, &WaitQueueEntry);
+  current->state = TASK_UNINTERRUPTIBLE;
+  spin_unlock(&io_request_lock);
+  schedule();
+  current->state = TASK_RUNNING;
+  remove_wait_queue(&Controller->CommandWaitQueue, &WaitQueueEntry);
+  spin_lock_irq(&io_request_lock);
+}
+
+
+/*
   DAC960_QueueCommand queues Command.
 */
 
@@ -317,6 +334,62 @@
 
 
 /*
+  DAC960_ReportErrorStatus reports Controller BIOS Messages passed through
+  the Error Status Register when the driver performs the BIOS handshaking.
+  It returns true for fatal errors and false otherwise.
+*/
+
+static boolean DAC960_ReportErrorStatus(DAC960_Controller_T *Controller,
+					unsigned char ErrorStatus,
+					unsigned char Parameter0,
+					unsigned char Parameter1)
+{
+  switch (ErrorStatus)
+    {
+    case 0x00:
+      DAC960_Notice("Physical Drive %d:%d Not Responding\n",
+		    Controller, Parameter1, Parameter0);
+      break;
+    case 0x08:
+      if (Controller->DriveSpinUpMessageDisplayed) break;
+      DAC960_Notice("Spinning Up Drives\n", Controller);
+      Controller->DriveSpinUpMessageDisplayed = true;
+      break;
+    case 0x30:
+      DAC960_Notice("Configuration Checksum Error\n", Controller);
+      break;
+    case 0x60:
+      DAC960_Notice("Mirror Race Recovery Failed\n", Controller);
+      break;
+    case 0x70:
+      DAC960_Notice("Mirror Race Recovery In Progress\n", Controller);
+      break;
+    case 0x90:
+      DAC960_Notice("Physical Drive %d:%d COD Mismatch\n",
+		    Controller, Parameter1, Parameter0);
+      break;
+    case 0xA0:
+      DAC960_Notice("Logical Drive Installation Aborted\n", Controller);
+      break;
+    case 0xB0:
+      DAC960_Notice("Mirror Race On A Critical Logical Drive\n", Controller);
+      break;
+    case 0xD0:
+      DAC960_Notice("New Controller Configuration Found\n", Controller);
+      break;
+    case 0xF0:
+      DAC960_Error("Fatal Memory Parity Error for Controller at\n", Controller);
+      return true;
+    default:
+      DAC960_Error("Unknown Initialization Error %02X for Controller at\n",
+		   Controller, ErrorStatus);
+      return true;
+    }
+  return false;
+}
+
+
+/*
   DAC960_EnableMemoryMailboxInterface enables the Memory Mailbox Interface.
 */
 
@@ -382,7 +455,7 @@
       case DAC960_V5_Controller:
 	while (--TimeoutCounter >= 0)
 	  {
-	    if (DAC960_V5_HardwareMailboxEmptyP(ControllerBaseAddress))
+	    if (!DAC960_V5_HardwareMailboxFullP(ControllerBaseAddress))
 	      break;
 	    udelay(10);
 	  }
@@ -474,11 +547,13 @@
       unsigned char DeviceFunction = PCI_Device->devfn;
       unsigned char Device = DeviceFunction >> 3;
       unsigned char Function = DeviceFunction & 0x7;
+      unsigned char ErrorStatus, Parameter0, Parameter1;
       unsigned int IRQ_Channel = PCI_Device->irq;
       unsigned long BaseAddress0 = PCI_Device->base_address[0];
       unsigned long BaseAddress1 = PCI_Device->base_address[1];
       unsigned short SubsystemVendorID, SubsystemDeviceID;
       int CommandIdentifier;
+      void *BaseAddress;
       pci_read_config_word(PCI_Device, PCI_SUBSYSTEM_VENDOR_ID,
 			   &SubsystemVendorID);
       pci_read_config_word(PCI_Device, PCI_SUBSYSTEM_ID,
@@ -524,24 +599,6 @@
       Controller->Function = Function;
       sprintf(Controller->ControllerName, "c%d", Controller->ControllerNumber);
       /*
-	Acquire shared access to the IRQ Channel.
-      */
-      if (IRQ_Channel == 0)
-	{
-	  DAC960_Error("IRQ Channel %d illegal for Controller at\n",
-		       Controller, IRQ_Channel);
-	  goto Failure;
-	}
-      strcpy(Controller->FullModelName, "DAC960");
-      if (request_irq(IRQ_Channel, DAC960_InterruptHandler,
-		      SA_SHIRQ, Controller->FullModelName, Controller) < 0)
-	{
-	  DAC960_Error("Unable to acquire IRQ Channel %d for Controller at\n",
-		       Controller, IRQ_Channel);
-	  goto Failure;
-	}
-      Controller->IRQ_Channel = IRQ_Channel;
-      /*
 	Map the Controller Register Window.
       */
       if (MemoryWindowSize < PAGE_SIZE)
@@ -556,34 +613,87 @@
 		       "Controller at\n", Controller);
 	  goto Failure;
 	}
+      BaseAddress = Controller->BaseAddress;
       switch (ControllerType)
 	{
 	case DAC960_V5_Controller:
-	  DAC960_V5_DisableInterrupts(Controller->BaseAddress);
+	  DAC960_V5_DisableInterrupts(BaseAddress);
+	  DAC960_V5_AcknowledgeHardwareMailboxStatus(BaseAddress);
+	  udelay(1000);
+	  while (DAC960_V5_InitializationInProgressP(BaseAddress))
+	    {
+	      if (DAC960_V5_ReadErrorStatus(BaseAddress, &ErrorStatus,
+					    &Parameter0, &Parameter1) &&
+		  DAC960_ReportErrorStatus(Controller, ErrorStatus,
+					   Parameter0, Parameter1))
+		goto Failure;
+	      udelay(10);
+	    }
 	  if (!DAC960_EnableMemoryMailboxInterface(Controller))
 	    {
 	      DAC960_Error("Unable to Enable Memory Mailbox Interface "
 			   "for Controller at\n", Controller);
 	      goto Failure;
 	    }
-	  DAC960_V5_EnableInterrupts(Controller->BaseAddress);
+	  DAC960_V5_EnableInterrupts(BaseAddress);
 	  break;
 	case DAC960_V4_Controller:
-	  DAC960_V4_DisableInterrupts(Controller->BaseAddress);
+	  DAC960_V4_DisableInterrupts(BaseAddress);
+	  DAC960_V4_AcknowledgeHardwareMailboxStatus(BaseAddress);
+	  udelay(1000);
+	  while (DAC960_V4_InitializationInProgressP(BaseAddress))
+	    {
+	      if (DAC960_V4_ReadErrorStatus(BaseAddress, &ErrorStatus,
+					    &Parameter0, &Parameter1) &&
+		  DAC960_ReportErrorStatus(Controller, ErrorStatus,
+					   Parameter0, Parameter1))
+		goto Failure;
+	      udelay(10);
+	    }
 	  if (!DAC960_EnableMemoryMailboxInterface(Controller))
 	    {
 	      DAC960_Error("Unable to Enable Memory Mailbox Interface "
 			   "for Controller at\n", Controller);
 	      goto Failure;
 	    }
-	  DAC960_V4_EnableInterrupts(Controller->BaseAddress);
+	  DAC960_V4_EnableInterrupts(BaseAddress);
 	  break;
 	case DAC960_V3_Controller:
 	  request_region(Controller->IO_Address, 0x80,
 			 Controller->FullModelName);
-	  DAC960_V3_EnableInterrupts(Controller->BaseAddress);
+	  DAC960_V3_DisableInterrupts(BaseAddress);
+	  DAC960_V3_AcknowledgeStatus(BaseAddress);
+	  udelay(1000);
+	  while (DAC960_V3_InitializationInProgressP(BaseAddress))
+	    {
+	      if (DAC960_V3_ReadErrorStatus(BaseAddress, &ErrorStatus,
+					    &Parameter0, &Parameter1) &&
+		  DAC960_ReportErrorStatus(Controller, ErrorStatus,
+					   Parameter0, Parameter1))
+		goto Failure;
+	      udelay(10);
+	    }
+	  DAC960_V3_EnableInterrupts(BaseAddress);
 	  break;
 	}
+      /*
+	Acquire shared access to the IRQ Channel.
+      */
+      if (IRQ_Channel == 0)
+	{
+	  DAC960_Error("IRQ Channel %d illegal for Controller at\n",
+		       Controller, IRQ_Channel);
+	  goto Failure;
+	}
+      strcpy(Controller->FullModelName, "DAC960");
+      if (request_irq(IRQ_Channel, DAC960_InterruptHandler,
+		      SA_SHIRQ, Controller->FullModelName, Controller) < 0)
+	{
+	  DAC960_Error("Unable to acquire IRQ Channel %d for Controller at\n",
+		       Controller, IRQ_Channel);
+	  goto Failure;
+	}
+      Controller->IRQ_Channel = IRQ_Channel;
       DAC960_ActiveControllerCount++;
       for (CommandIdentifier = 0;
 	   CommandIdentifier < DAC960_MaxChannels;
@@ -604,11 +714,11 @@
 			"0x%X PCI Address 0x%X\n", Controller,
 			Bus, Device, Function, IO_Address, PCI_Address);
       if (Controller == NULL) break;
-      if (Controller->IRQ_Channel > 0)
-	free_irq(IRQ_Channel, Controller);
       if (Controller->MemoryMappedAddress != NULL)
 	iounmap(Controller->MemoryMappedAddress);
       DAC960_Controllers[Controller->ControllerNumber] = NULL;
+      if (Controller->IRQ_Channel > 0)
+	free_irq(IRQ_Channel, Controller);
     Ignore:
       kfree(Controller);
     }
@@ -1282,9 +1392,7 @@
       Command = DAC960_AllocateCommand(Controller);
       if (Command != NULL) break;
       if (!WaitForCommand) return false;
-      spin_unlock(&io_request_lock);
-      sleep_on(&Controller->CommandWaitQueue);
-      spin_lock_irq(&io_request_lock);
+      DAC960_WaitForCommand(Controller);
     }
   DAC960_ClearCommand(Command);
   if (Request->cmd == READ)
@@ -1836,6 +1944,20 @@
 	      }
 	  else if (NewEnquiry->RebuildFlag == DAC960_BackgroundCheckInProgress)
 	    Controller->NeedConsistencyCheckProgress = true;
+	  if (CommandType != DAC960_MonitoringCommand &&
+	      Controller->RebuildFlagPending)
+	    {
+	      DAC960_Enquiry_T *Enquiry = (DAC960_Enquiry_T *)
+		Bus_to_Virtual(Command->CommandMailbox.Type3.BusAddress);
+	      Enquiry->RebuildFlag = Controller->PendingRebuildFlag;
+	      Controller->RebuildFlagPending = false;
+	    }
+	  else if (CommandType == DAC960_MonitoringCommand &&
+		   NewEnquiry->RebuildFlag > DAC960_BackgroundCheckInProgress)
+	    {
+	      Controller->PendingRebuildFlag = NewEnquiry->RebuildFlag;
+	      Controller->RebuildFlagPending = true;
+	    }
 	}
       else if (CommandOpcode == DAC960_PerformEventLogOperation)
 	{
@@ -2019,6 +2141,9 @@
 	    Controller->RebuildProgress.LogicalDriveSize;
 	  unsigned int BlocksCompleted =
 	    LogicalDriveSize - Controller->RebuildProgress.RemainingBlocks;
+	  if (CommandStatus == DAC960_NoRebuildOrCheckInProgress &&
+	      Controller->LastRebuildStatus == DAC960_NormalCompletion)
+	    CommandStatus = DAC960_RebuildSuccessful;
 	  switch (CommandStatus)
 	    {
 	    case DAC960_NormalCompletion:
@@ -2046,13 +2171,28 @@
 			      "Failure of Drive Being Rebuilt\n", Controller);
 	      break;
 	    case DAC960_NoRebuildOrCheckInProgress:
-	      if (Controller->LastRebuildStatus != DAC960_NormalCompletion)
-		break;
+	      break;
 	    case DAC960_RebuildSuccessful:
 	      DAC960_Progress("Rebuild Completed Successfully\n", Controller);
 	      break;
+	    case DAC960_RebuildSuccessfullyTerminated:
+	      DAC960_Progress("Rebuild Successfully Terminated\n", Controller);
+	      break;
 	    }
 	  Controller->LastRebuildStatus = CommandStatus;
+	  if (CommandType != DAC960_MonitoringCommand &&
+	      Controller->RebuildStatusPending)
+	    {
+	      Command->CommandStatus = Controller->PendingRebuildStatus;
+	      Controller->RebuildStatusPending = false;
+	    }
+	  else if (CommandType == DAC960_MonitoringCommand &&
+		   CommandStatus != DAC960_NormalCompletion &&
+		   CommandStatus != DAC960_NoRebuildOrCheckInProgress)
+	    {
+	      Controller->PendingRebuildStatus = CommandStatus;
+	      Controller->RebuildStatusPending = true;
+	    }
 	}
       else if (CommandOpcode == DAC960_RebuildStat)
 	{
@@ -2267,7 +2407,7 @@
   if (CommandType == DAC960_QueuedCommand)
     {
       DAC960_KernelCommand_T *KernelCommand = Command->KernelCommand;
-      KernelCommand->CommandStatus = CommandStatus;
+      KernelCommand->CommandStatus = Command->CommandStatus;
       Command->KernelCommand = NULL;
       if (CommandOpcode == DAC960_DCDB)
 	Controller->DirectCommandActive[KernelCommand->DCDB->Channel]
@@ -2288,9 +2428,12 @@
       return;
     }
   /*
-    Deallocate the Command, and wake up any processes waiting on a free Command.
+    Deallocate the Command.
   */
   DAC960_DeallocateCommand(Command);
+  /*
+    Wake up any processes waiting on a free Command.
+  */
   wake_up(&Controller->CommandWaitQueue);
 }
 
@@ -2696,19 +2839,14 @@
 	  }
 	if (CommandOpcode == DAC960_DCDB)
 	  {
-	    while (true)
-	      {
-		DAC960_AcquireControllerLock(Controller, &ProcessorFlags);
-		if (!Controller->DirectCommandActive[DCDB.Channel]
-						    [DCDB.TargetID])
-		  Command = DAC960_AllocateCommand(Controller);
-		if (Command != NULL)
-		  Controller->DirectCommandActive[DCDB.Channel]
-						 [DCDB.TargetID] = true;
-		DAC960_ReleaseControllerLock(Controller, &ProcessorFlags);
-		if (Command != NULL) break;
-		sleep_on(&Controller->CommandWaitQueue);
-	      }
+	    DAC960_AcquireControllerLock(Controller, &ProcessorFlags);
+	    while (Controller->DirectCommandActive[DCDB.Channel]
+						  [DCDB.TargetID] ||
+		   (Command = DAC960_AllocateCommand(Controller)) == NULL)
+	      DAC960_WaitForCommand(Controller);
+	    Controller->DirectCommandActive[DCDB.Channel]
+					   [DCDB.TargetID] = true;
+	    DAC960_ReleaseControllerLock(Controller, &ProcessorFlags);
 	    DAC960_ClearCommand(Command);
 	    Command->CommandType = DAC960_ImmediateCommand;
 	    memcpy(&Command->CommandMailbox, &UserCommand.CommandMailbox,
@@ -2718,14 +2856,10 @@
 	  }
 	else
 	  {
-	    while (true)
-	      {
-		DAC960_AcquireControllerLock(Controller, &ProcessorFlags);
-		Command = DAC960_AllocateCommand(Controller);
-		DAC960_ReleaseControllerLock(Controller, &ProcessorFlags);
-		if (Command != NULL) break;
-		sleep_on(&Controller->CommandWaitQueue);
-	      }
+	    DAC960_AcquireControllerLock(Controller, &ProcessorFlags);
+	    while ((Command = DAC960_AllocateCommand(Controller)) == NULL)
+	      DAC960_WaitForCommand(Controller);
+	    DAC960_ReleaseControllerLock(Controller, &ProcessorFlags);
 	    DAC960_ClearCommand(Command);
 	    Command->CommandType = DAC960_ImmediateCommand;
 	    memcpy(&Command->CommandMailbox, &UserCommand.CommandMailbox,
@@ -3127,14 +3261,10 @@
   DAC960_CommandMailbox_T *CommandMailbox;
   ProcessorFlags_T ProcessorFlags;
   unsigned char Channel, TargetID, LogicalDriveNumber;
-  while (true)
-    {
-      DAC960_AcquireControllerLock(Controller, &ProcessorFlags);
-      Command = DAC960_AllocateCommand(Controller);
-      DAC960_ReleaseControllerLock(Controller, &ProcessorFlags);
-      if (Command != NULL) break;
-      sleep_on(&Controller->CommandWaitQueue);
-    }
+  DAC960_AcquireControllerLock(Controller, &ProcessorFlags);
+  while ((Command = DAC960_AllocateCommand(Controller)) == NULL)
+    DAC960_WaitForCommand(Controller);
+  DAC960_ReleaseControllerLock(Controller, &ProcessorFlags);
   Controller->UserStatusLength = 0;
   DAC960_ClearCommand(Command);
   Command->CommandType = DAC960_ImmediateCommand;
diff -urN linux-2.2.15-pre5/drivers/block/DAC960.h linux/drivers/block/DAC960.h
--- linux-2.2.15-pre5/drivers/block/DAC960.h	Wed Aug 25 17:29:46 1999
+++ linux/drivers/block/DAC960.h	Mon Dec  3 21:58:46 2001
@@ -55,17 +55,17 @@
 
 
 /*
-  Define a 32 bit I/O Address data type.
+  Define a 32/64 bit I/O Address data type.
 */
 
-typedef unsigned int DAC960_IO_Address_T;
+typedef unsigned long DAC960_IO_Address_T;
 
 
 /*
-  Define a 32 bit PCI Bus Address data type.
+  Define a 32/64 bit PCI Bus Address data type.
 */
 
-typedef unsigned int DAC960_PCI_Address_T;
+typedef unsigned long DAC960_PCI_Address_T;
 
 
 /*
@@ -196,6 +196,7 @@
 #define DAC960_RebuildFailed_BadBlocksOnOther	0x0003	/* Consistency */
 #define DAC960_RebuildFailed_NewDriveFailed	0x0004	/* Consistency */
 #define DAC960_RebuildSuccessful		0x0100	/* Consistency */
+#define DAC960_RebuildSuccessfullyTerminated	0x0107	/* Consistency */
 #define DAC960_AddCapacityInProgress		0x0004	/* Consistency */
 #define DAC960_AddCapacityFailedOrSuspended	0x00F4	/* Consistency */
 #define DAC960_Config2ChecksumError		0x0002	/* Configuration */
@@ -513,7 +514,7 @@
   unsigned char SynchronousMultiplier;			/* Byte 4 */
   unsigned char SynchronousOffset:5;			/* Byte 5 Bits 0-4 */
   unsigned char :3;					/* Byte 5 Bits 5-7 */
-  unsigned long DiskSize __attribute__ ((packed));	/* Bytes 6-9 */
+  unsigned int DiskSize __attribute__ ((packed));	/* Bytes 6-9 */
 }
 DAC960_DeviceState_T;
 
@@ -1212,6 +1213,7 @@
   unsigned char LogicalDriveCount;
   unsigned char GeometryTranslationHeads;
   unsigned char GeometryTranslationSectors;
+  unsigned char PendingRebuildFlag;
   unsigned short ControllerQueueDepth;
   unsigned short DriverQueueDepth;
   unsigned short MaxBlocksPerCommand;
@@ -1247,6 +1249,9 @@
   boolean NeedRebuildProgress;
   boolean NeedConsistencyCheckProgress;
   boolean EphemeralProgressMessage;
+  boolean RebuildFlagPending;
+  boolean RebuildStatusPending;
+  boolean DriveSpinUpMessageDisplayed;
   Timer_T MonitoringTimer;
   GenericDiskInfo_T GenericDiskInfo;
   DAC960_Command_T *FreeCommands;
@@ -1269,6 +1274,7 @@
   DAC960_EventLogEntry_T EventLogEntry;
   DAC960_RebuildProgress_T RebuildProgress;
   DAC960_CommandStatus_T LastRebuildStatus;
+  DAC960_CommandStatus_T PendingRebuildStatus;
   DAC960_LogicalDriveInformation_T
     LogicalDriveInformation[2][DAC960_MaxLogicalDrives];
   DAC960_LogicalDriveState_T LogicalDriveInitialState[DAC960_MaxLogicalDrives];
@@ -1393,7 +1399,8 @@
   DAC960_V5_MailboxRegister11Offset =		0x5B,
   DAC960_V5_MailboxRegister12Offset =		0x5C,
   DAC960_V5_StatusCommandIdentifierRegOffset =	0x5D,
-  DAC960_V5_StatusRegisterOffset =		0x5E
+  DAC960_V5_StatusRegisterOffset =		0x5E,
+  DAC960_V5_ErrorStatusRegisterOffset =		0x63
 }
 DAC960_V5_RegisterOffsets_T;
 
@@ -1415,7 +1422,8 @@
   } Write;
   struct {
     boolean HardwareMailboxEmpty:1;			/* Bit 0 */
-    unsigned char :7;					/* Bits 1-7 */
+    boolean InitializationNotInProgress:1;		/* Bit 1 */
+    unsigned char :6;					/* Bits 2-7 */
   } Read;
 }
 DAC960_V5_InboundDoorBellRegister_T;
@@ -1459,6 +1467,22 @@
 
 
 /*
+  Define the structure of the DAC960 V5 Error Status Register.
+*/
+
+typedef union DAC960_V5_ErrorStatusRegister
+{
+  unsigned char All;
+  struct {
+    unsigned int :2;					/* Bits 0-1 */
+    boolean ErrorStatusPending:1;			/* Bit 2 */
+    unsigned int :5;					/* Bits 3-7 */
+  } Bits;
+}
+DAC960_V5_ErrorStatusRegister_T;
+
+
+/*
   Define inline functions to provide an abstraction for reading and writing the
   DAC960 V5 Controller Interface Registers.
 */
@@ -1514,12 +1538,21 @@
 }
 
 static inline
-boolean DAC960_V5_HardwareMailboxEmptyP(void *ControllerBaseAddress)
+boolean DAC960_V5_HardwareMailboxFullP(void *ControllerBaseAddress)
 {
   DAC960_V5_InboundDoorBellRegister_T InboundDoorBellRegister;
   InboundDoorBellRegister.All =
     readb(ControllerBaseAddress + DAC960_V5_InboundDoorBellRegisterOffset);
-  return InboundDoorBellRegister.Read.HardwareMailboxEmpty;
+  return !InboundDoorBellRegister.Read.HardwareMailboxEmpty;
+}
+
+static inline
+boolean DAC960_V5_InitializationInProgressP(void *ControllerBaseAddress)
+{
+  DAC960_V5_InboundDoorBellRegister_T InboundDoorBellRegister;
+  InboundDoorBellRegister.All =
+    readb(ControllerBaseAddress + DAC960_V5_InboundDoorBellRegisterOffset);
+  return !InboundDoorBellRegister.Read.InitializationNotInProgress;
 }
 
 static inline
@@ -1575,7 +1608,7 @@
 void DAC960_V5_EnableInterrupts(void *ControllerBaseAddress)
 {
   DAC960_V5_InterruptMaskRegister_T InterruptMaskRegister;
-  InterruptMaskRegister.All = 0;
+  InterruptMaskRegister.All = 0xFF;
   InterruptMaskRegister.Bits.DisableInterrupts = false;
   writeb(InterruptMaskRegister.All,
 	 ControllerBaseAddress + DAC960_V5_InterruptMaskRegisterOffset);
@@ -1585,7 +1618,7 @@
 void DAC960_V5_DisableInterrupts(void *ControllerBaseAddress)
 {
   DAC960_V5_InterruptMaskRegister_T InterruptMaskRegister;
-  InterruptMaskRegister.All = 0;
+  InterruptMaskRegister.All = 0xFF;
   InterruptMaskRegister.Bits.DisableInterrupts = true;
   writeb(InterruptMaskRegister.All,
 	 ControllerBaseAddress + DAC960_V5_InterruptMaskRegisterOffset);
@@ -1607,7 +1640,9 @@
   NextCommandMailbox->Words[1] = CommandMailbox->Words[1];
   NextCommandMailbox->Words[2] = CommandMailbox->Words[2];
   NextCommandMailbox->Words[3] = CommandMailbox->Words[3];
+  wmb();
   NextCommandMailbox->Words[0] = CommandMailbox->Words[0];
+  mb();
 }
 
 static inline
@@ -1637,6 +1672,26 @@
   return readw(ControllerBaseAddress + DAC960_V5_StatusRegisterOffset);
 }
 
+static inline boolean
+DAC960_V5_ReadErrorStatus(void *ControllerBaseAddress,
+			  unsigned char *ErrorStatus,
+			  unsigned char *Parameter0,
+			  unsigned char *Parameter1)
+{
+  DAC960_V5_ErrorStatusRegister_T ErrorStatusRegister;
+  ErrorStatusRegister.All =
+    readb(ControllerBaseAddress + DAC960_V5_ErrorStatusRegisterOffset);
+  if (!ErrorStatusRegister.Bits.ErrorStatusPending) return false;
+  ErrorStatusRegister.Bits.ErrorStatusPending = false;
+  *ErrorStatus = ErrorStatusRegister.All;
+  *Parameter0 =
+    readb(ControllerBaseAddress + DAC960_V5_CommandOpcodeRegisterOffset);
+  *Parameter1 =
+    readb(ControllerBaseAddress + DAC960_V5_CommandIdentifierRegisterOffset);
+  writeb(0xFF, ControllerBaseAddress + DAC960_V5_ErrorStatusRegisterOffset);
+  return true;
+}
+
 static inline
 void DAC960_V5_SaveMemoryMailboxInfo(DAC960_Controller_T *Controller)
 {
@@ -1695,7 +1750,8 @@
   DAC960_V4_MailboxRegister11Offset =		0x100B,
   DAC960_V4_MailboxRegister12Offset =		0x100C,
   DAC960_V4_StatusCommandIdentifierRegOffset =	0x1018,
-  DAC960_V4_StatusRegisterOffset =		0x101A
+  DAC960_V4_StatusRegisterOffset =		0x101A,
+  DAC960_V4_ErrorStatusRegisterOffset =		0x103F
 }
 DAC960_V4_RegisterOffsets_T;
 
@@ -1717,7 +1773,8 @@
   } Write;
   struct {
     boolean HardwareMailboxFull:1;			/* Bit 0 */
-    unsigned int :31;					/* Bits 1-31 */
+    boolean InitializationInProgress:1;			/* Bit 1 */
+    unsigned int :30;					/* Bits 2-31 */
   } Read;
 }
 DAC960_V4_InboundDoorBellRegister_T;
@@ -1762,6 +1819,22 @@
 
 
 /*
+  Define the structure of the DAC960 V4 Error Status Register.
+*/
+
+typedef union DAC960_V4_ErrorStatusRegister
+{
+  unsigned char All;
+  struct {
+    unsigned int :2;					/* Bits 0-1 */
+    boolean ErrorStatusPending:1;			/* Bit 2 */
+    unsigned int :5;					/* Bits 3-7 */
+  } Bits;
+}
+DAC960_V4_ErrorStatusRegister_T;
+
+
+/*
   Define inline functions to provide an abstraction for reading and writing the
   DAC960 V4 Controller Interface Registers.
 */
@@ -1826,6 +1899,15 @@
 }
 
 static inline
+boolean DAC960_V4_InitializationInProgressP(void *ControllerBaseAddress)
+{
+  DAC960_V4_InboundDoorBellRegister_T InboundDoorBellRegister;
+  InboundDoorBellRegister.All =
+    readl(ControllerBaseAddress + DAC960_V4_InboundDoorBellRegisterOffset);
+  return InboundDoorBellRegister.Read.InitializationInProgress;
+}
+
+static inline
 void DAC960_V4_AcknowledgeHardwareMailboxInterrupt(void *ControllerBaseAddress)
 {
   DAC960_V4_OutboundDoorBellRegister_T OutboundDoorBellRegister;
@@ -1914,7 +1996,9 @@
   NextCommandMailbox->Words[1] = CommandMailbox->Words[1];
   NextCommandMailbox->Words[2] = CommandMailbox->Words[2];
   NextCommandMailbox->Words[3] = CommandMailbox->Words[3];
+  wmb();
   NextCommandMailbox->Words[0] = CommandMailbox->Words[0];
+  mb();
 }
 
 static inline
@@ -1944,11 +2028,31 @@
   return readw(ControllerBaseAddress + DAC960_V4_StatusRegisterOffset);
 }
 
+static inline boolean
+DAC960_V4_ReadErrorStatus(void *ControllerBaseAddress,
+			  unsigned char *ErrorStatus,
+			  unsigned char *Parameter0,
+			  unsigned char *Parameter1)
+{
+  DAC960_V4_ErrorStatusRegister_T ErrorStatusRegister;
+  ErrorStatusRegister.All =
+    readb(ControllerBaseAddress + DAC960_V4_ErrorStatusRegisterOffset);
+  if (!ErrorStatusRegister.Bits.ErrorStatusPending) return false;
+  ErrorStatusRegister.Bits.ErrorStatusPending = false;
+  *ErrorStatus = ErrorStatusRegister.All;
+  *Parameter0 =
+    readb(ControllerBaseAddress + DAC960_V4_CommandOpcodeRegisterOffset);
+  *Parameter1 =
+    readb(ControllerBaseAddress + DAC960_V4_CommandIdentifierRegisterOffset);
+  writeb(0, ControllerBaseAddress + DAC960_V4_ErrorStatusRegisterOffset);
+  return true;
+}
+
 static inline
 void DAC960_V4_SaveMemoryMailboxInfo(DAC960_Controller_T *Controller)
 {
   void *ControllerBaseAddress = Controller->BaseAddress;
-  writel(0xAABBFFFF,
+  writel(0x743C485E,
 	 ControllerBaseAddress + DAC960_V4_CommandOpcodeRegisterOffset);
   writel((unsigned long) Controller->FirstCommandMailbox,
 	 ControllerBaseAddress + DAC960_V4_MailboxRegister4Offset);
@@ -1966,7 +2070,7 @@
 {
   void *ControllerBaseAddress = Controller->BaseAddress;
   if (readl(ControllerBaseAddress
-	    + DAC960_V4_CommandOpcodeRegisterOffset) != 0xAABBFFFF)
+	    + DAC960_V4_CommandOpcodeRegisterOffset) != 0x743C485E)
     return;
   *MemoryMailboxAddress =
     (void *) readl(ControllerBaseAddress + DAC960_V4_MailboxRegister4Offset);
@@ -2000,6 +2104,7 @@
   DAC960_V3_MailboxRegister12Offset =		0x0C,
   DAC960_V3_StatusCommandIdentifierRegOffset =	0x0D,
   DAC960_V3_StatusRegisterOffset =		0x0E,
+  DAC960_V3_ErrorStatusRegisterOffset =		0x3F,
   DAC960_V3_InboundDoorBellRegisterOffset =	0x40,
   DAC960_V3_OutboundDoorBellRegisterOffset =	0x41,
   DAC960_V3_InterruptEnableRegisterOffset =	0x43
@@ -2023,7 +2128,8 @@
   } Write;
   struct {
     boolean MailboxFull:1;				/* Bit 0 */
-    unsigned char :7;					/* Bits 1-7 */
+    boolean InitializationInProgress:1;			/* Bit 1 */
+    unsigned char :6;					/* Bits 2-7 */
   } Read;
 }
 DAC960_V3_InboundDoorBellRegister_T;
@@ -2064,6 +2170,22 @@
 
 
 /*
+  Define the structure of the DAC960 V3 Error Status Register.
+*/
+
+typedef union DAC960_V3_ErrorStatusRegister
+{
+  unsigned char All;
+  struct {
+    unsigned int :2;					/* Bits 0-1 */
+    boolean ErrorStatusPending:1;			/* Bit 2 */
+    unsigned int :5;					/* Bits 3-7 */
+  } Bits;
+}
+DAC960_V3_ErrorStatusRegister_T;
+
+
+/*
   Define inline functions to provide an abstraction for reading and writing the
   DAC960 V3 Controller Interface Registers.
 */
@@ -2118,6 +2240,15 @@
 }
 
 static inline
+boolean DAC960_V3_InitializationInProgressP(void *ControllerBaseAddress)
+{
+  DAC960_V3_InboundDoorBellRegister_T InboundDoorBellRegister;
+  InboundDoorBellRegister.All =
+    readb(ControllerBaseAddress + DAC960_V3_InboundDoorBellRegisterOffset);
+  return InboundDoorBellRegister.Read.InitializationInProgress;
+}
+
+static inline
 void DAC960_V3_AcknowledgeInterrupt(void *ControllerBaseAddress)
 {
   DAC960_V3_OutboundDoorBellRegister_T OutboundDoorBellRegister;
@@ -2190,6 +2321,26 @@
 DAC960_V3_ReadStatusRegister(void *ControllerBaseAddress)
 {
   return readw(ControllerBaseAddress + DAC960_V3_StatusRegisterOffset);
+}
+
+static inline boolean
+DAC960_V3_ReadErrorStatus(void *ControllerBaseAddress,
+			  unsigned char *ErrorStatus,
+			  unsigned char *Parameter0,
+			  unsigned char *Parameter1)
+{
+  DAC960_V3_ErrorStatusRegister_T ErrorStatusRegister;
+  ErrorStatusRegister.All =
+    readb(ControllerBaseAddress + DAC960_V3_ErrorStatusRegisterOffset);
+  if (!ErrorStatusRegister.Bits.ErrorStatusPending) return false;
+  ErrorStatusRegister.Bits.ErrorStatusPending = false;
+  *ErrorStatus = ErrorStatusRegister.All;
+  *Parameter0 =
+    readb(ControllerBaseAddress + DAC960_V3_CommandOpcodeRegisterOffset);
+  *Parameter1 =
+    readb(ControllerBaseAddress + DAC960_V3_CommandIdentifierRegisterOffset);
+  writeb(0, ControllerBaseAddress + DAC960_V3_ErrorStatusRegisterOffset);
+  return true;
 }
 
 
diff -urN linux-2.2.15-pre5/drivers/block/Makefile linux/drivers/block/Makefile
--- linux-2.2.15-pre5/drivers/block/Makefile	Mon Aug  9 12:04:38 1999
+++ linux/drivers/block/Makefile	Mon Dec  3 21:58:46 2001
@@ -162,6 +162,10 @@
 IDE_OBJS += cmd646.o
 endif
 
+ifeq ($(CONFIG_BLK_DEV_CS5530),y)
+IDE_OBJS += cs5530.o
+endif     
+
 ifeq ($(CONFIG_BLK_DEV_SL82C105),y)
 IDE_OBJS += sl82c105.o
 endif
diff -urN linux-2.2.15-pre5/drivers/block/cmd646.c linux/drivers/block/cmd646.c
--- linux-2.2.15-pre5/drivers/block/cmd646.c	Mon Mar 15 16:11:29 1999
+++ linux/drivers/block/cmd646.c	Mon Dec  3 21:58:46 2001
@@ -1,4 +1,4 @@
-/* $Id: cmd646.c,v 1.11 1998/12/13 08:36:54 davem Exp $
+/* $Id: cmd646.c,v 1.11.2.1 2000/01/31 05:02:39 davem Exp $
  * cmd646.c: Enable interrupts at initialization time on Ultra/PCI machines.
  *           Note, this driver is not used at all on other systems because
  *           there the "BIOS" has done all of the following already.
@@ -6,7 +6,7 @@
  *           on the 646U2 and not on the 646U.
  *
  * Copyright (C) 1998       Eddie C. Dost  (ecd@skynet.be)
- * Copyright (C) 1998       David S. Miller (davem@dm.cobaltmicro.com)
+ * Copyright (C) 1998       David S. Miller (davem@redhat.com)
  */
 
 #include <linux/types.h>
diff -urN linux-2.2.15-pre5/drivers/block/cpqarray.c linux/drivers/block/cpqarray.c
--- linux-2.2.15-pre5/drivers/block/cpqarray.c	Mon Aug  9 12:04:38 1999
+++ linux/drivers/block/cpqarray.c	Mon Dec  3 21:58:46 2001
@@ -41,8 +41,8 @@
 
 #define SMART2_DRIVER_VERSION(maj,min,submin) ((maj<<16)|(min<<8)|(submin))
 
-#define DRIVER_NAME "Compaq SMART2 Driver (v 1.0.5)"
-#define DRIVER_VERSION SMART2_DRIVER_VERSION(1,0,5)
+#define DRIVER_NAME "Compaq SMART2 Driver (v 1.0.6)"
+#define DRIVER_VERSION SMART2_DRIVER_VERSION(1,0,6)
 #define MAJOR_NR COMPAQ_SMART2_MAJOR
 #include <linux/blk.h>
 #include <linux/blkdev.h>
@@ -84,6 +84,7 @@
 	{ 0x40400E11, "Integrated Array",	&smart4_access },
 	{ 0x40500E11, "Smart Array 4200",	&smart4_access },
 	{ 0x40510E11, "Smart Array 4250ES",	&smart4_access },
+	{ 0x40580E11, "Smart Array 431",	&smart4_access },
 };
 
 static struct hd_struct * ida;
@@ -1223,7 +1224,7 @@
 			return(error);
 		}
 		copy_from_user(p, (void*)io->sg[0].addr, io->sg[0].size);
-		c->req.bp = virt_to_bus(&(io->c));
+		c->req.hdr.blk = virt_to_bus(&(io->c));
 		c->req.sg[0].size = io->sg[0].size;
 		c->req.sg[0].addr = virt_to_bus(p);
 		c->req.hdr.sg_cnt = 1;
diff -urN linux-2.2.15-pre5/drivers/block/cpqarray.h linux/drivers/block/cpqarray.h
--- linux-2.2.15-pre5/drivers/block/cpqarray.h	Mon Aug  9 12:04:38 1999
+++ linux/drivers/block/cpqarray.h	Mon Dec  3 21:58:46 2001
@@ -30,7 +30,6 @@
 #include <linux/locks.h>
 #include <linux/malloc.h>
 #include <linux/proc_fs.h>
-#include <linux/md.h>
 #include <linux/timer.h>
 #endif
 
diff -urN linux-2.2.15-pre5/drivers/block/cs5530.c linux/drivers/block/cs5530.c
--- linux-2.2.15-pre5/drivers/block/cs5530.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/block/cs5530.c	Mon Dec  3 21:58:46 2001
@@ -0,0 +1,467 @@
+/*
+ * linux/drivers/block/cs5530.c		Version 0.4	Feb 6, 2000
+ *
+ * Copyright (C) 2000			Mark Lord <mlord@pobox.com>
+ * May be copied or modified under the terms of the GNU General Public License
+ *
+ * Development of this chipset driver was funded
+ * by the nice folks at National Semiconductor.
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/mm.h>
+#include <linux/ioport.h>
+#include <linux/blkdev.h>
+#include <linux/hdreg.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+
+#ifndef SETFEATURES_XFER
+	#define CS5530_2_2_KERNEL
+#endif
+
+#ifdef CS5530_2_2_KERNEL
+	#include "ide.h"
+	#define XFER_UDMA_6	0x46	/* 0100|0110 */
+	#define XFER_UDMA_5	0x45	/* 0100|0101 */
+	#define XFER_UDMA_4	0x44	/* 0100|0100 */
+	#define XFER_UDMA_3	0x43	/* 0100|0011 */
+	#define XFER_UDMA_2	0x42	/* 0100|0010 */
+	#define XFER_UDMA_1	0x41	/* 0100|0001 */
+	#define XFER_UDMA_0	0x40	/* 0100|0000 */
+	#define XFER_MW_DMA_2	0x22	/* 0010|0010 */
+	#define XFER_MW_DMA_1	0x21	/* 0010|0001 */
+	#define XFER_MW_DMA_0	0x20	/* 0010|0000 */
+	#define XFER_SW_DMA_2	0x12	/* 0001|0010 */
+	#define XFER_SW_DMA_1	0x11	/* 0001|0001 */
+	#define XFER_SW_DMA_0	0x10	/* 0001|0000 */
+	#define XFER_PIO_4	0x0C	/* 0000|1100 */
+	#define XFER_PIO_3	0x0B	/* 0000|1011 */
+	#define XFER_PIO_2	0x0A	/* 0000|1010 */
+	#define XFER_PIO_1	0x09	/* 0000|1001 */
+	#define XFER_PIO_0	0x08	/* 0000|1000 */
+	#define XFER_PIO_SLOW	0x00	/* 0000|0000 */
+	#define SETFEATURES_XFER 0x03	/* Set transfer mode */
+	#define pci_for_each_dev(dev) \
+        	for(dev = pci_devices; dev; dev=dev->next)
+#else
+	#include <linux/ide.h>
+#endif	/* CS5530_2_2_KERNEL */
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include "ide_modes.h"
+
+/*
+ * Return the mode name for a drive transfer mode value:
+ */
+static const char *strmode (byte mode)
+{
+	switch (mode) {
+		case XFER_UDMA_4:	return("UDMA4");
+		case XFER_UDMA_3:	return("UDMA3");
+		case XFER_UDMA_2:	return("UDMA2");
+		case XFER_UDMA_1:	return("UDMA1");
+		case XFER_UDMA_0:	return("UDMA0");
+		case XFER_MW_DMA_2:	return("MDMA2");
+		case XFER_MW_DMA_1:	return("MDMA1");
+		case XFER_MW_DMA_0:	return("MDMA0");
+		case XFER_SW_DMA_2:	return("SDMA2");
+		case XFER_SW_DMA_1:	return("SDMA1");
+		case XFER_SW_DMA_0:	return("SDMA0");
+		case XFER_PIO_4:	return("PIO4");
+		case XFER_PIO_3:	return("PIO3");
+		case XFER_PIO_2:	return("PIO2");
+		case XFER_PIO_1:	return("PIO1");
+		case XFER_PIO_0:	return("PIO0");
+		default:		return("???");
+	}
+}
+
+/*
+ * Set a new transfer mode at the drive
+ */
+int cs5530_set_xfer_mode (ide_drive_t *drive, byte mode)
+{
+	int		i, error = 1;
+	byte		stat;
+	ide_hwif_t	*hwif = HWIF(drive);
+
+	printk("%s: cs5530_set_xfer_mode(%s)\n", drive->name, strmode(mode));
+	/*
+	 * If this is a DMA mode setting, then turn off all DMA bits.
+	 * We will set one of them back on afterwards, if all goes well.
+	 *
+	 * Not sure why this is needed (it looks very silly),
+	 * but other IDE chipset drivers also do this fiddling.  ???? -ml
+ 	 */
+	switch (mode) {
+		case XFER_UDMA_4:
+		case XFER_UDMA_3:
+		case XFER_UDMA_2:
+		case XFER_UDMA_1:
+		case XFER_UDMA_0:
+		case XFER_MW_DMA_2:
+		case XFER_MW_DMA_1:
+		case XFER_MW_DMA_0:
+		case XFER_SW_DMA_2:
+		case XFER_SW_DMA_1:
+		case XFER_SW_DMA_0:
+			drive->id->dma_ultra &= ~0xFF00;
+			drive->id->dma_mword &= ~0x0F00;
+			drive->id->dma_1word &= ~0x0F00;
+	}
+
+	/*
+	 * Select the drive, and issue the SETFEATURES command
+	 */
+	disable_irq(hwif->irq);
+	udelay(1);
+	SELECT_DRIVE(HWIF(drive), drive);
+	udelay(1);
+	if (IDE_CONTROL_REG)
+		OUT_BYTE(drive->ctl | 2, IDE_CONTROL_REG);
+	OUT_BYTE(mode, IDE_NSECTOR_REG);
+	OUT_BYTE(SETFEATURES_XFER, IDE_FEATURE_REG);
+	OUT_BYTE(WIN_SETFEATURES, IDE_COMMAND_REG);
+	udelay(1);	/* spec allows drive 400ns to assert "BUSY" */
+
+	/*
+	 * Wait for drive to become non-BUSY
+	 */
+	if ((stat = GET_STAT()) & BUSY_STAT) {
+		unsigned long flags, timeout;
+		__save_flags(flags);	/* local CPU only */
+		ide__sti();		/* local CPU only -- for jiffies */
+		timeout = jiffies + WAIT_CMD;
+		while ((stat = GET_STAT()) & BUSY_STAT) {
+			if (0 < (signed long)(jiffies - timeout))
+				break;
+		}
+		__restore_flags(flags); /* local CPU only */
+	}
+
+	/*
+	 * Allow status to settle, then read it again.
+	 * A few rare drives vastly violate the 400ns spec here,
+	 * so we'll wait up to 10usec for a "good" status
+	 * rather than expensively fail things immediately.
+	 */
+	for (i = 0; i < 10; i++) {
+		udelay(1);
+		if (OK_STAT((stat = GET_STAT()), DRIVE_READY, BUSY_STAT|DRQ_STAT|ERR_STAT)) {
+			error = 0;
+			break;
+		}
+	}
+	enable_irq(hwif->irq);
+
+	/*
+	 * Turn dma bit on if all is okay
+	 */
+	if (error) {
+		(void) ide_dump_status(drive, "cs5530_set_xfer_mode", stat);
+	} else {
+		switch (mode) {
+			case XFER_UDMA_4:   drive->id->dma_ultra |= 0x1010; break;
+			case XFER_UDMA_3:   drive->id->dma_ultra |= 0x0808; break;
+			case XFER_UDMA_2:   drive->id->dma_ultra |= 0x0404; break;
+			case XFER_UDMA_1:   drive->id->dma_ultra |= 0x0202; break;
+			case XFER_UDMA_0:   drive->id->dma_ultra |= 0x0101; break;
+			case XFER_MW_DMA_2: drive->id->dma_mword |= 0x0404; break;
+			case XFER_MW_DMA_1: drive->id->dma_mword |= 0x0202; break;
+			case XFER_MW_DMA_0: drive->id->dma_mword |= 0x0101; break;
+			case XFER_SW_DMA_2: drive->id->dma_1word |= 0x0404; break;
+			case XFER_SW_DMA_1: drive->id->dma_1word |= 0x0202; break;
+			case XFER_SW_DMA_0: drive->id->dma_1word |= 0x0101; break;
+		}
+	}
+	return error;
+}
+
+/*
+ * Here are the standard PIO mode 0-4 timings for each "format".
+ * Format-0 uses fast data reg timings, with slower command reg timings.
+ * Format-1 uses fast timings for all registers, but won't work with all drives.
+ */
+static unsigned int cs5530_pio_timings[2][5] =
+	{{0x00009172, 0x00012171, 0x00020080, 0x00032010, 0x00040010},
+	 {0xd1329172, 0x71212171, 0x30200080, 0x20102010, 0x00100010}};
+
+/*
+ * After chip reset, the PIO timings are set to 0x0000e132, which is not valid.
+ */
+#define CS5530_BAD_PIO(timings) (((timings)&~0x80000000)==0x0000e132)
+#define CS5530_BASEREG(hwif)	(((hwif)->dma_base & ~0xf) + ((hwif)->channel ? 0x30 : 0x20))
+
+/*
+ * cs5530_tuneproc() handles selection/setting of PIO modes
+ * for both the chipset and drive.
+ *
+ * The ide_init_cs5530() routine guarantees that all drives
+ * will have valid default PIO timings set up before we get here.
+ */
+static void cs5530_tuneproc (ide_drive_t *drive, byte pio)	/* pio=255 means "autotune" */
+{
+	ide_hwif_t	*hwif = HWIF(drive);
+	unsigned int	format, basereg = CS5530_BASEREG(hwif);
+	static byte	modes[5] = {XFER_PIO_0, XFER_PIO_1, XFER_PIO_2, XFER_PIO_3, XFER_PIO_4};
+
+	pio = ide_get_best_pio_mode(drive, pio, 4, NULL);
+	if (!cs5530_set_xfer_mode(drive, modes[pio])) {
+		format = (inl(basereg+4) >> 31) & 1;
+		outl(cs5530_pio_timings[format][pio], basereg+(drive->select.b.unit<<3));
+	}
+}
+
+/*
+ * cs5530_config_dma() handles selection/setting of DMA/UDMA modes
+ * for both the chipset and drive.
+ */
+static int cs5530_config_dma (ide_drive_t *drive)
+{
+	int			udma_ok = 1, mode = 0;
+	ide_hwif_t		*hwif = HWIF(drive);
+	int			unit = drive->select.b.unit;
+	ide_drive_t		*mate = &hwif->drives[unit^1];
+	struct hd_driveid	*id = drive->id;
+	unsigned int		basereg, reg, timings;
+
+
+	/*
+	 * Default to DMA-off in case we run into trouble here.
+	 */
+	(void)hwif->dmaproc(ide_dma_off_quietly, drive);	/* turn off DMA while we fiddle */
+	outb(inb(hwif->dma_base+2)&~(unit?0x40:0x20), hwif->dma_base+2); /* clear DMA_capable bit */
+
+	/*
+	 * The CS5530 specifies that two drives sharing a cable cannot
+	 * mix UDMA/MDMA.  It has to be one or the other, for the pair,
+	 * though different timings can still be chosen for each drive.
+	 * We could set the appropriate timing bits on the fly,
+	 * but that might be a bit confusing.  So, for now we statically
+	 * handle this requirement by looking at our mate drive to see
+	 * what it is capable of, before choosing a mode for our own drive.
+	 */
+	if (mate->present) {
+		struct hd_driveid *mateid = mate->id;
+		if (mateid && (mateid->capability & 1)) {
+#ifndef CS5530_2_2_KERNEL
+			if (!hwif->dmaproc(ide_dma_bad_drive, mate))
+#endif /* CS5530_2_2_KERNEL */
+			{
+				if ((mateid->field_valid & 4) && (mateid->dma_ultra & 7))
+					udma_ok = 1;
+				else if ((mateid->field_valid & 2) && (mateid->dma_mword & 7))
+					udma_ok = 0;
+				else
+					udma_ok = 1;
+			}
+		}
+	}
+
+	/*
+	 * Now see what the current drive is capable of,
+	 * selecting UDMA only if the mate said it was ok.
+	 */
+	if (id && (id->capability & 1) && hwif->autodma) {
+#ifndef CS5530_2_2_KERNEL
+		if (!hwif->dmaproc(ide_dma_bad_drive, drive))
+#endif /* CS5530_2_2_KERNEL */
+		{
+			if (udma_ok && (id->field_valid & 4) && (id->dma_ultra & 7)) {
+				if      (id->dma_ultra & 4)
+					mode = XFER_UDMA_2;
+				else if (id->dma_ultra & 2)
+					mode = XFER_UDMA_1;
+				else if (id->dma_ultra & 1)
+					mode = XFER_UDMA_0;
+			}
+			if (!mode && (id->field_valid & 2) && (id->dma_mword & 7)) {
+				if      (id->dma_mword & 4)
+					mode = XFER_MW_DMA_2;
+				else if (id->dma_mword & 2)
+					mode = XFER_MW_DMA_1;
+				else if (id->dma_mword & 1)
+					mode = XFER_MW_DMA_0;
+			}
+		}
+	}
+
+	/*
+	 * Tell the drive to switch to the new mode; abort on failure.
+	 */
+	if (!mode || cs5530_set_xfer_mode(drive, mode))
+		return 1;	/* failure */
+
+	/*
+	 * Now tune the chipset to match the drive:
+	 */
+	switch (mode) {
+		case XFER_UDMA_0:	timings = 0x00921250; break;
+		case XFER_UDMA_1:	timings = 0x00911140; break;
+		case XFER_UDMA_2:	timings = 0x00911030; break;
+		case XFER_MW_DMA_0:	timings = 0x00077771; break;
+		case XFER_MW_DMA_1:	timings = 0x00012121; break;
+		case XFER_MW_DMA_2:	timings = 0x00002020; break;
+		default:
+			printk("%s: cs5530_config_dma: huh? mode=%02x\n", drive->name, mode);
+			return 1;	/* failure */
+	}
+	basereg = CS5530_BASEREG(hwif);
+	reg = inl(basereg+4);			/* get drive0 config register */
+	timings |= reg & 0x80000000;		/* preserve PIO format bit */
+	if (unit == 0) {			/* are we configuring drive0? */
+		outl(timings, basereg+4);	/* write drive0 config register */
+	} else {
+		if (timings & 0x00100000)
+			reg |=  0x00100000;	/* enable UDMA timings for both drives */
+		else
+			reg &= ~0x00100000;	/* disable UDMA timings for both drives */
+		outl(reg,     basereg+4);	/* write drive0 config register */
+		outl(timings, basereg+12);	/* write drive1 config register */
+	}
+	outb(inb(hwif->dma_base+2)|(unit?0x40:0x20), hwif->dma_base+2);	/* set DMA_capable bit */
+
+	/*
+	 * Finally, turn DMA on in software, and exit.
+	 */
+	return hwif->dmaproc(ide_dma_on, drive);	/* success */
+}
+
+/*
+ * This is a CS5530-specific wrapper for the standard ide_dmaproc().
+ * We need it for our custom "ide_dma_check" function.
+ * All other requests are forwarded to the standard ide_dmaproc().
+ */
+int cs5530_dmaproc (ide_dma_action_t func, ide_drive_t *drive)
+{
+	switch (func) {
+		case ide_dma_check:
+			return cs5530_config_dma(drive);
+		default:
+			return ide_dmaproc(func, drive);
+	}
+}
+
+/*
+ * Initialize the cs5530 bridge for reliable IDE DMA operation.
+ */
+unsigned int __init pci_init_cs5530 (struct pci_dev *dev, const char *name)
+{
+	struct pci_dev *master_0 = NULL, *cs5530_0 = NULL;
+	unsigned short pcicmd = 0;
+	unsigned long flags;
+
+	pci_for_each_dev (dev) {
+		if (dev->vendor == PCI_VENDOR_ID_CYRIX) {
+			switch (dev->device) {
+				case PCI_DEVICE_ID_CYRIX_PCI_MASTER:
+					master_0 = dev;
+					break;
+				case PCI_DEVICE_ID_CYRIX_5530_LEGACY:
+					cs5530_0 = dev;
+					break;
+			}
+		}
+	}
+	if (!master_0) {
+		printk("%s: unable to locate PCI MASTER function\n", name);
+		return 0;
+	}
+	if (!cs5530_0) {
+		printk("%s: unable to locate CS5530 LEGACY function\n", name);
+		return 0;
+	}
+
+	save_flags(flags);
+	cli();	/* all CPUs (there should only be one CPU with this chipset) */
+
+	/*
+	 * Enable BusMaster and MemoryWriteAndInvalidate for the cs5530:
+	 * -->  OR 0x14 into 16-bit PCI COMMAND reg of function 0 of the cs5530
+	 */
+	pci_read_config_word (cs5530_0, PCI_COMMAND, &pcicmd);
+	pci_write_config_word(cs5530_0, PCI_COMMAND, pcicmd | PCI_COMMAND_MASTER | PCI_COMMAND_INVALIDATE);
+
+	/*
+	 * Set PCI CacheLineSize to 16-bytes:
+	 * --> Write 0x04 into 8-bit PCI CACHELINESIZE reg of function 0 of the cs5530
+	 */
+	pci_write_config_byte(cs5530_0, PCI_CACHE_LINE_SIZE, 0x04);
+
+	/*
+	 * Disable trapping of UDMA register accesses (Win98 hack):
+	 * --> Write 0x5006 into 16-bit reg at offset 0xd0 of function 0 of the cs5530
+	 */
+	pci_write_config_word(cs5530_0, 0xd0, 0x5006);
+
+	/*
+	 * Bit-1 at 0x40 enables MemoryWriteAndInvalidate on internal X-bus:
+	 * The other settings are what is necessary to get the register
+	 * into a sane state for IDE DMA operation.
+	 */
+	pci_write_config_byte(master_0, 0x40, 0x1e);
+
+	/* 
+	 * Set max PCI burst size (16-bytes seems to work best):
+	 *	   16bytes: set bit-1 at 0x41 (reg value of 0x16)
+	 *	all others: clear bit-1 at 0x41, and do:
+	 *	  128bytes: OR 0x00 at 0x41
+	 *	  256bytes: OR 0x04 at 0x41
+	 *	  512bytes: OR 0x08 at 0x41
+	 *	 1024bytes: OR 0x0c at 0x41
+	 */
+	pci_write_config_byte(master_0, 0x41, 0x14);
+
+	/*
+	 * These settings are necessary to get the chip
+	 * into a sane state for IDE DMA operation.
+	 */
+	pci_write_config_byte(master_0, 0x42, 0x00);
+	pci_write_config_byte(master_0, 0x43, 0xc1);
+
+	restore_flags(flags);
+	return 0;
+}
+
+/*
+ * This gets invoked by the IDE driver once for each channel,
+ * and performs channel-specific pre-initialization before drive probing.
+ */
+void __init ide_init_cs5530 (ide_hwif_t *hwif)
+{
+#ifndef CS5530_2_2_KERNEL
+	static int already_done = 0;
+	if (!already_done) {
+		++already_done;
+		(void) pci_init_cs5530 (hwif->pci_dev, "CS5530");
+	}
+#endif /* CS5530_2_2_KERNEL */
+	if (hwif->mate)
+		hwif->serialized = hwif->mate->serialized = 1;
+	if (!hwif->dma_base) {
+		hwif->autodma = 0;
+	} else {
+		unsigned int basereg, d0_timings;
+
+		hwif->dmaproc  = &cs5530_dmaproc;
+		hwif->tuneproc = &cs5530_tuneproc;
+		basereg = CS5530_BASEREG(hwif);
+		d0_timings = inl(basereg+0);
+		if (CS5530_BAD_PIO(d0_timings)) {	/* PIO timings not initialized? */
+			outl(cs5530_pio_timings[(d0_timings>>31)&1][0], basereg+0);
+			if (!hwif->drives[0].autotune)
+				hwif->drives[0].autotune = 1;	/* needs autotuning later */
+		}
+		if (CS5530_BAD_PIO(inl(basereg+8))) {	/* PIO timings not initialized? */
+			outl(cs5530_pio_timings[(d0_timings>>31)&1][0], basereg+8);
+			if (!hwif->drives[1].autotune)
+				hwif->drives[1].autotune = 1;	/* needs autotuning later */
+		}
+	}
+}
diff -urN linux-2.2.15-pre5/drivers/block/ida_cmd.h linux/drivers/block/ida_cmd.h
--- linux-2.2.15-pre5/drivers/block/ida_cmd.h	Mon Aug  9 12:04:38 1999
+++ linux/drivers/block/ida_cmd.h	Mon Dec  3 21:58:46 2001
@@ -191,7 +191,7 @@
 	__u8	expn_fail;
 	__u8	unit_flags;
 	__u16	big_fail_map[8];
-	__u16	big_remap_map[8];
+	__u16	big_remap_map[128];
 	__u16	big_repl_map[8];
 	__u16	big_act_spare_map[8];
 	__u8	big_spar_repl_map[128];
@@ -336,7 +336,7 @@
 	__u32	sense_info;
 	__u8	sense_code;
 	__u8	sense_qual;
-	__u8	residual;
+	__u32	residual;
 	__u8	reserved[4];
 	__u8	cdb[12];	
 } scsi_param_t;
diff -urN linux-2.2.15-pre5/drivers/block/ide-disk.c linux/drivers/block/ide-disk.c
--- linux-2.2.15-pre5/drivers/block/ide-disk.c	Tue Jan  4 10:12:14 2000
+++ linux/drivers/block/ide-disk.c	Mon Dec  3 21:58:46 2001
@@ -571,7 +571,7 @@
 	drive->special.b.recalibrate  = 1;
 	if (OK_TO_RESET_CONTROLLER)
 		drive->mult_count = 0;
-	if (!drive->keep_settings)
+	if (!drive->keep_settings && !drive->using_dma)
 		drive->mult_req = 0;
 	if (drive->mult_req != drive->mult_count)
 		drive->special.b.set_multmode = 1;
diff -urN linux-2.2.15-pre5/drivers/block/ide-pci.c linux/drivers/block/ide-pci.c
--- linux-2.2.15-pre5/drivers/block/ide-pci.c	Sun Jan 24 21:54:35 1999
+++ linux/drivers/block/ide-pci.c	Mon Dec  3 21:58:46 2001
@@ -47,6 +47,7 @@
 #define DEVID_UM8886A	((ide_pci_devid_t){PCI_VENDOR_ID_UMC,     PCI_DEVICE_ID_UMC_UM8886A})
 #define DEVID_UM8886BF	((ide_pci_devid_t){PCI_VENDOR_ID_UMC,     PCI_DEVICE_ID_UMC_UM8886BF})
 #define DEVID_HPT343	((ide_pci_devid_t){PCI_VENDOR_ID_TTI,     PCI_DEVICE_ID_TTI_HPT343})
+#define DEVID_CS5530	((ide_pci_devid_t){PCI_VENDOR_ID_CYRIX,   PCI_DEVICE_ID_CYRIX_5530_IDE})
 
 #define IDE_IGNORE	((void *)-1)
 
@@ -103,6 +104,13 @@
 #define	INIT_VIA82C586	NULL
 #endif
 
+#ifdef CONFIG_BLK_DEV_CS5530
+extern void ide_init_cs5530(ide_hwif_t *);
+#define INIT_CS5530     &ide_init_cs5530
+#else
+#define INIT_CS5530     NULL
+#endif
+
 typedef struct ide_pci_enablebit_s {
 	byte	reg;	/* byte pci reg holding the enable-bit */
 	byte	mask;	/* mask to isolate the enable-bit */
@@ -141,6 +149,7 @@
 	{DEVID_UM8886A,	"UM8886A",	NULL,		{{0x00,0x00,0x00}, {0x00,0x00,0x00}},	ON_BOARD,	0 },
 	{DEVID_UM8886BF,"UM8886BF",	NULL,		{{0x00,0x00,0x00}, {0x00,0x00,0x00}}, 	ON_BOARD,	0 },
 	{DEVID_HPT343,	"HPT343",	NULL,		{{0x00,0x00,0x00}, {0x00,0x00,0x00}},	NEVER_BOARD,	16 },
+	{DEVID_CS5530,	"CS5530",	INIT_CS5530,	{{0x00,0x00,0x00}, {0x00,0x00,0x00}},	ON_BOARD,	112 },
 	{IDE_PCI_DEVID_NULL, "PCI_IDE",	NULL,		{{0x00,0x00,0x00}, {0x00,0x00,0x00}}, 	ON_BOARD,	0 }};
 
 /*
@@ -393,6 +402,7 @@
 		if (IDE_PCI_DEVID_EQ(d->devid, DEVID_PDC20246) ||
 		    IDE_PCI_DEVID_EQ(d->devid, DEVID_AEC6210) ||
 		    IDE_PCI_DEVID_EQ(d->devid, DEVID_HPT343) ||
+		    IDE_PCI_DEVID_EQ(d->devid, DEVID_CS5530) ||
 		    ((dev->class >> 8) == PCI_CLASS_STORAGE_IDE && (dev->class & 0x80))) {
 			unsigned long dma_base = ide_get_or_set_dma_base(hwif, (!mate && d->extra) ? d->extra : 0, d->name);
 			if (dma_base && !(pcicmd & PCI_COMMAND_MASTER)) {
diff -urN linux-2.2.15-pre5/drivers/block/ide.c linux/drivers/block/ide.c
--- linux-2.2.15-pre5/drivers/block/ide.c	Tue Jan  4 10:12:14 2000
+++ linux/drivers/block/ide.c	Mon Dec  3 21:58:46 2001
@@ -598,14 +598,16 @@
 
 static void pre_reset (ide_drive_t *drive)
 {
+	if (drive->driver != NULL)
+		DRIVER(drive)->pre_reset(drive);
 	if (!drive->keep_settings) {
-		drive->unmask = 0;
-		drive->io_32bit = 0;
-		if (drive->using_dma)
+		if (drive->using_dma) {
 			(void) HWIF(drive)->dmaproc(ide_dma_off, drive);
+		} else {
+			drive->unmask = 0;
+			drive->io_32bit = 0;
+		}
 	}
-	if (drive->driver != NULL)
-		DRIVER(drive)->pre_reset(drive);
 }
 
 /*
@@ -830,8 +832,8 @@
 		if ((stat & DRQ_STAT) && rq->cmd != WRITE)
 			try_to_flush_leftover_data(drive);
 	}
-	if (GET_STAT() & (BUSY_STAT|DRQ_STAT))
-		rq->errors |= ERROR_RESET;	/* Mmmm.. timing problem */
+	if (GET_STAT() & (BUSY_STAT|DRQ_STAT))	/* possible timing problem? */
+		OUT_BYTE(WIN_IDLEIMMEDIATE,IDE_COMMAND_REG); /* force an abort */
 
 	if (rq->errors >= ERROR_MAX) {
 		if (drive->driver != NULL)
diff -urN linux-2.2.15-pre5/drivers/block/ide_modes.h linux/drivers/block/ide_modes.h
--- linux-2.2.15-pre5/drivers/block/ide_modes.h	Tue May 11 10:35:44 1999
+++ linux/drivers/block/ide_modes.h	Mon Dec  3 21:58:46 2001
@@ -15,7 +15,7 @@
  * breaking the fragile cmd640.c support.
  */
 
-#if defined(CONFIG_BLK_DEV_CMD640) || defined(CONFIG_IDE_CHIPSETS) || defined(CONFIG_BLK_DEV_OPTI621) || defined(CONFIG_BLK_DEV_IDE_PMAC)
+#if defined(CONFIG_BLK_DEV_CMD640) || defined(CONFIG_IDE_CHIPSETS) || defined(CONFIG_BLK_DEV_OPTI621) || defined(CONFIG_BLK_DEV_IDE_PMAC) || defined(CONFIG_BLK_DEV_CS5530)
 
 /*
  * Standard (generic) timings for PIO modes, from ATA2 specification.
diff -urN linux-2.2.15-pre5/drivers/block/ll_rw_blk.c linux/drivers/block/ll_rw_blk.c
--- linux-2.2.15-pre5/drivers/block/ll_rw_blk.c	Tue Jan  4 10:12:14 2000
+++ linux/drivers/block/ll_rw_blk.c	Mon Dec  3 21:58:46 2001
@@ -847,8 +847,7 @@
 #ifdef CONFIG_BLK_DEV_FD
 	floppy_init();
 #else
-#if !defined (__mc68000__) && !defined(CONFIG_PMAC) && !defined(__sparc__)\
-    && !defined(CONFIG_APUS)
+#if !defined (__mc68000__) && !defined(CONFIG_PPC) && !defined(__sparc__)
 	outb_p(0xc, 0x3f2);
 #endif
 #endif
diff -urN linux-2.2.15-pre5/drivers/block/raid1.c linux/drivers/block/raid1.c
--- linux-2.2.15-pre5/drivers/block/raid1.c	Tue Dec 22 08:12:38 1998
+++ linux/drivers/block/raid1.c	Mon Dec  3 21:58:46 2001
@@ -211,7 +211,11 @@
 	while (!( /* FIXME: now we are rather fault tolerant than nice */
 	r1_bh = kmalloc (sizeof (struct raid1_bh), GFP_KERNEL)
 	) )
+	{
 		printk ("raid1_make_request(#1): out of memory\n");
+		current->policy |= SCHED_YIELD;
+		schedule();
+	}
 	memset (r1_bh, 0, sizeof (struct raid1_bh));
 
 /*
@@ -299,7 +303,11 @@
 		while (!( /* FIXME: now we are rather fault tolerant than nice */
 		mirror_bh[i] = kmalloc (sizeof (struct buffer_head), GFP_KERNEL)
 		) )
+		{
 			printk ("raid1_make_request(#2): out of memory\n");
+			current->policy |= SCHED_YIELD;
+			schedule();
+		}
 		memset (mirror_bh[i], 0, sizeof (struct buffer_head));
 
 	/*
@@ -711,7 +719,11 @@
 	while (!( /* FIXME: now we are rather fault tolerant than nice */
 	mddev->private = kmalloc (sizeof (struct raid1_data), GFP_KERNEL)
 	) )
+	{
 		printk ("raid1_run(): out of memory\n");
+		current->policy |= SCHED_YIELD;
+		schedule();
+	}
 	raid_conf = mddev->private;
 	memset(raid_conf, 0, sizeof(*raid_conf));
 
diff -urN linux-2.2.15-pre5/drivers/char/Config.in linux/drivers/char/Config.in
--- linux-2.2.15-pre5/drivers/char/Config.in	Mon Dec  3 21:58:40 2001
+++ linux/drivers/char/Config.in	Mon Dec  3 21:58:46 2001
@@ -154,6 +154,17 @@
     dep_tristate 'Quickcam BW Video For Linux' CONFIG_VIDEO_BWQCAM $CONFIG_VIDEO_DEV $CONFIG_PARPORT
     dep_tristate 'Colour QuickCam Video For Linux (EXPERIMENTAL)' CONFIG_VIDEO_CQCAM $CONFIG_VIDEO_DEV $CONFIG_PARPORT
   fi
+  if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+    dep_tristate 'CPiA Video For Linux (EXPERIMENTAL)' CONFIG_VIDEO_CPIA $CONFIG_VIDEO_DEV
+    if [ "$CONFIG_VIDEO_CPIA" != "n" ]; then
+      if [ "$CONFIG_PARPORT" != "n" ]; then
+        dep_tristate 'CPiA Parallel Port Lowlevel Support (EXPERIMENTAL)' CONFIG_VIDEO_CPIA_PP $CONFIG_VIDEO_CPIA $CONFIG_PARPORT
+        if [ "$CONFIG_VIDEO_CPIA_PP" != "n" ]; then
+          bool 'CPiA Parallel Port DMA Support (EXPERIMENTAL)' CONFIG_VIDEO_CPIA_PP_DMA
+        fi
+      fi
+    fi
+  fi
   dep_tristate 'Mediavision Pro Movie Studio Video For Linux' CONFIG_VIDEO_PMS $CONFIG_VIDEO_DEV
   if [ "$CONFIG_PMAC" = "y" ]; then
     dep_tristate 'PlanB Video-In on PowerMac' CONFIG_VIDEO_PLANB $CONFIG_VIDEO_DEV
diff -urN linux-2.2.15-pre5/drivers/char/Makefile linux/drivers/char/Makefile
--- linux-2.2.15-pre5/drivers/char/Makefile	Mon Dec  3 21:58:40 2001
+++ linux/drivers/char/Makefile	Mon Dec  3 21:58:46 2001
@@ -475,6 +475,22 @@
   endif
 endif
 
+ifeq ($(CONFIG_VIDEO_CPIA),y)
+LX_OBJS += cpia.o
+else
+  ifeq ($(CONFIG_VIDEO_CPIA),m)
+    MX_OBJS += cpia.o
+  endif
+endif
+
+ifeq ($(CONFIG_VIDEO_CPIA_PP),y)
+L_OBJS += cpia_pp.o
+else
+  ifeq ($(CONFIG_VIDEO_CPIA_PP),m)
+    M_OBJS += cpia_pp.o
+  endif
+endif
+
 ifeq ($(CONFIG_RADIO_AZTECH),y)
 L_OBJS += radio-aztech.o
 else
diff -urN linux-2.2.15-pre5/drivers/char/cpia.c linux/drivers/char/cpia.c
--- linux-2.2.15-pre5/drivers/char/cpia.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/cpia.c	Mon Dec  3 21:58:46 2001
@@ -0,0 +1,3375 @@
+/*
+ * cpia CPiA driver
+ *
+ * Supports CPiA based Video Camera's.
+ *
+ * (C) 1999 Peter Pregler,
+ *          Scott J. Bertin,
+ *          Johannes Erdfelt
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/config.h>
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/wrapper.h>
+#include <linux/fs.h>
+#include <linux/vmalloc.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+#include <linux/proc_fs.h>
+#include <linux/ctype.h>
+#include <linux/videodev.h>
+#include <asm/io.h>
+#include <asm/semaphore.h>
+
+#ifdef CONFIG_KMOD
+#include <linux/kmod.h>
+#endif
+
+#undef _CPIA_DEBUG_		/* define for verbose debug output */
+#include <linux/cpia.h>
+
+#ifdef CONFIG_VIDEO_CPIA_PP
+extern int cpia_pp_init(void);
+#endif
+#ifdef CONFIG_VIDEO_CPIA_USB
+extern int cpia_usb_init(void);
+#endif
+
+#ifdef MODULE
+MODULE_AUTHOR("Scott J. Bertin <sbertin@mindspring.com> & Peter Pregler <Peter_Pregler@email.com>");
+MODULE_DESCRIPTION("V4L-driver for Vision CPiA based cameras");
+MODULE_SUPPORTED_DEVICE("video");
+#endif
+
+#define ABOUT "V4L-Driver for Vision CPiA based cameras"
+
+#ifndef VID_HARDWARE_CPIA
+#define VID_HARDWARE_CPIA 24    /* FIXME -> from linux/videodev.h */
+#endif
+
+#define CPIA_MODULE_CPIA			(0<<5)
+#define CPIA_MODULE_SYSTEM			(1<<5)
+#define CPIA_MODULE_VP_CTRL			(5<<5)
+#define CPIA_MODULE_CAPTURE			(6<<5)
+#define CPIA_MODULE_DEBUG			(7<<5)
+
+#define INPUT (DATA_IN << 8)
+#define OUTPUT (DATA_OUT << 8)
+
+#define CPIA_COMMAND_GetCPIAVersion	(INPUT | CPIA_MODULE_CPIA | 1)
+#define CPIA_COMMAND_GetPnPID		(INPUT | CPIA_MODULE_CPIA | 2)
+#define CPIA_COMMAND_GetCameraStatus	(INPUT | CPIA_MODULE_CPIA | 3)
+#define CPIA_COMMAND_GotoHiPower	(OUTPUT | CPIA_MODULE_CPIA | 4)
+#define CPIA_COMMAND_GotoLoPower	(OUTPUT | CPIA_MODULE_CPIA | 5)
+#define CPIA_COMMAND_GotoSuspend	(OUTPUT | CPIA_MODULE_CPIA | 7)
+#define CPIA_COMMAND_GotoPassThrough	(OUTPUT | CPIA_MODULE_CPIA | 8)
+#define CPIA_COMMAND_ModifyCameraStatus	(OUTPUT | CPIA_MODULE_CPIA | 10)
+
+#define CPIA_COMMAND_ReadVCRegs		(INPUT | CPIA_MODULE_SYSTEM | 1)
+#define CPIA_COMMAND_WriteVCReg		(OUTPUT | CPIA_MODULE_SYSTEM | 2)
+#define CPIA_COMMAND_ReadMCPorts	(INPUT | CPIA_MODULE_SYSTEM | 3)
+#define CPIA_COMMAND_WriteMCPort	(OUTPUT | CPIA_MODULE_SYSTEM | 4)
+#define CPIA_COMMAND_SetBaudRate	(OUTPUT | CPIA_MODULE_SYSTEM | 5)
+#define CPIA_COMMAND_SetECPTiming	(OUTPUT | CPIA_MODULE_SYSTEM | 6)
+#define CPIA_COMMAND_ReadIDATA		(INPUT | CPIA_MODULE_SYSTEM | 7)
+#define CPIA_COMMAND_WriteIDATA		(OUTPUT | CPIA_MODULE_SYSTEM | 8)
+#define CPIA_COMMAND_GenericCall	(OUTPUT | CPIA_MODULE_SYSTEM | 9)
+#define CPIA_COMMAND_I2CStart		(OUTPUT | CPIA_MODULE_SYSTEM | 10)
+#define CPIA_COMMAND_I2CStop		(OUTPUT | CPIA_MODULE_SYSTEM | 11)
+#define CPIA_COMMAND_I2CWrite		(OUTPUT | CPIA_MODULE_SYSTEM | 12)
+#define CPIA_COMMAND_I2CRead		(INPUT | CPIA_MODULE_SYSTEM | 13)
+
+#define CPIA_COMMAND_GetVPVersion	(INPUT | CPIA_MODULE_VP_CTRL | 1)
+#define CPIA_COMMAND_SetColourParams	(OUTPUT | CPIA_MODULE_VP_CTRL | 3)
+#define CPIA_COMMAND_SetExposure	(OUTPUT | CPIA_MODULE_VP_CTRL | 4)
+#define CPIA_COMMAND_SetColourBalance	(OUTPUT | CPIA_MODULE_VP_CTRL | 6)
+#define CPIA_COMMAND_SetSensorFPS	(OUTPUT | CPIA_MODULE_VP_CTRL | 7)
+#define CPIA_COMMAND_SetVPDefaults	(OUTPUT | CPIA_MODULE_VP_CTRL | 8)
+#define CPIA_COMMAND_SetApcor		(OUTPUT | CPIA_MODULE_VP_CTRL | 9)
+#define CPIA_COMMAND_SetFlickerCtrl	(OUTPUT | CPIA_MODULE_VP_CTRL | 10)
+#define CPIA_COMMAND_SetVLOffset	(OUTPUT | CPIA_MODULE_VP_CTRL | 11)
+#define CPIA_COMMAND_GetColourParams	(INPUT | CPIA_MODULE_VP_CTRL | 16)
+#define CPIA_COMMAND_GetColourBalance	(INPUT | CPIA_MODULE_VP_CTRL | 17)
+#define CPIA_COMMAND_GetExposure	(INPUT | CPIA_MODULE_VP_CTRL | 18)
+#define CPIA_COMMAND_SetSensorMatrix	(OUTPUT | CPIA_MODULE_VP_CTRL | 19)
+#define CPIA_COMMAND_ColourBars		(OUTPUT | CPIA_MODULE_VP_CTRL | 25)
+#define CPIA_COMMAND_ReadVPRegs		(INPUT | CPIA_MODULE_VP_CTRL | 30)
+#define CPIA_COMMAND_WriteVPReg		(OUTPUT | CPIA_MODULE_VP_CTRL | 31)
+
+#define CPIA_COMMAND_GrabFrame		(OUTPUT | CPIA_MODULE_CAPTURE | 1)
+#define CPIA_COMMAND_UploadFrame	(OUTPUT | CPIA_MODULE_CAPTURE | 2)
+#define CPIA_COMMAND_SetGrabMode	(OUTPUT | CPIA_MODULE_CAPTURE | 3)
+#define CPIA_COMMAND_InitStreamCap	(OUTPUT | CPIA_MODULE_CAPTURE | 4)
+#define CPIA_COMMAND_FiniStreamCap	(OUTPUT | CPIA_MODULE_CAPTURE | 5)
+#define CPIA_COMMAND_StartStreamCap	(OUTPUT | CPIA_MODULE_CAPTURE | 6)
+#define CPIA_COMMAND_EndStreamCap	(OUTPUT | CPIA_MODULE_CAPTURE | 7)
+#define CPIA_COMMAND_SetFormat		(OUTPUT | CPIA_MODULE_CAPTURE | 8)
+#define CPIA_COMMAND_SetROI		(OUTPUT | CPIA_MODULE_CAPTURE | 9)
+#define CPIA_COMMAND_SetCompression	(OUTPUT | CPIA_MODULE_CAPTURE | 10)
+#define CPIA_COMMAND_SetCompressionTarget (OUTPUT | CPIA_MODULE_CAPTURE | 11)
+#define CPIA_COMMAND_SetYUVThresh	(OUTPUT | CPIA_MODULE_CAPTURE | 12)
+#define CPIA_COMMAND_SetCompressionParams (OUTPUT | CPIA_MODULE_CAPTURE | 13)
+#define CPIA_COMMAND_DiscardFrame	(OUTPUT | CPIA_MODULE_CAPTURE | 14)
+
+#define CPIA_COMMAND_OutputRS232	(OUTPUT | CPIA_MODULE_DEBUG | 1)
+#define CPIA_COMMAND_AbortProcess	(OUTPUT | CPIA_MODULE_DEBUG | 4)
+#define CPIA_COMMAND_SetDramPage	(OUTPUT | CPIA_MODULE_DEBUG | 5)
+#define CPIA_COMMAND_StartDramUpload	(OUTPUT | CPIA_MODULE_DEBUG | 6)
+#define CPIA_COMMAND_StartDummyDtream	(OUTPUT | CPIA_MODULE_DEBUG | 8)
+#define CPIA_COMMAND_AbortStream	(OUTPUT | CPIA_MODULE_DEBUG | 9)
+#define CPIA_COMMAND_DownloadDRAM	(OUTPUT | CPIA_MODULE_DEBUG | 10)
+
+struct cam_params {
+	struct {
+		u8 firmwareVersion;
+		u8 firmwareRevision;
+		u8 vcVersion;
+		u8 vcRevision;
+	} version;
+	struct {
+		u16 vendor;
+		u16 product;
+		u16 deviceRevision;
+	} pnpID;
+	struct {
+		u8 vpVersion;
+		u8 vpRevision;
+		u16 cameraHeadID;
+	} vpVersion;
+	struct {
+		u8 systemState;
+		u8 grabState;
+		u8 streamState;
+		u8 fatalError;
+		u8 cmdError;
+		u8 debugFlags;
+		u8 vpStatus;
+		u8 errorCode;
+	} status;
+	struct {
+		u8 brightness;
+		u8 contrast;
+		u8 saturation;
+	} colourParams;
+	struct {
+		u8 gainMode;
+		u8 expMode;
+		u8 compMode;
+		u8 centreWeight;
+		u8 gain;
+		u8 fineExp;
+		u8 coarseExpLo;
+		u8 coarseExpHi;
+		u8 redComp;
+		u8 green1Comp;
+		u8 green2Comp;
+		u8 blueComp;
+	} exposure;
+	struct {
+		u8 balanceMode;
+		u8 redGain;
+		u8 greenGain;
+		u8 blueGain;
+	} colourBalance;
+	struct {
+		u8 divisor;
+		u8 baserate;
+	} sensorFps;
+	struct {
+		u8 gain1;
+		u8 gain2;
+		u8 gain4;
+		u8 gain8;
+	} apcor;
+	struct {
+		u8 flickerMode;
+		u8 coarseJump;
+		u8 allowableOverExposure;
+	} flickerControl;
+	struct {
+		u8 gain1;
+		u8 gain2;
+		u8 gain4;
+		u8 gain8;
+	} vlOffset;
+	struct {
+		u8 mode;
+		u8 decimation;
+	} compression;
+	struct {
+		u8 frTargeting;
+		u8 targetFR;
+		u8 targetQ;
+	} compressionTarget;
+	struct {
+		u8 yThreshold;
+		u8 uvThreshold;
+	} yuvThreshold;
+	struct {
+		u8 hysteresis;
+		u8 threshMax;
+		u8 smallStep;
+		u8 largeStep;
+		u8 decimationHysteresis;
+		u8 frDiffStepThresh;
+		u8 qDiffStepThresh;
+		u8 decimationThreshMod;
+	} compressionParams;
+	struct {
+		u8 videoSize;		/* CIF/QCIF */
+		u8 subSample;
+		u8 yuvOrder;
+	} format;
+	struct {
+		u8 colStart;		/* skip first 8*colStart pixels */
+		u8 colEnd;		/* finish at 8*colEnd pixels */
+		u8 rowStart;		/* skip first 4*rowStart lines */
+		u8 rowEnd;		/* finish at 4*rowEnd lines */
+	} roi;
+	u8 ecpTiming;
+	u8 streamStartLine;
+};
+
+enum {
+	FRAME_READY,		/* Ready to grab into */
+	FRAME_GRABBING,		/* In the process of being grabbed into */
+	FRAME_DONE,		/* Finished grabbing, but not been synced yet */
+	FRAME_UNUSED,		/* Unused (no MCAPTURE) */
+};
+
+#define FRAME_NUM	2	/* double buffering for now */
+struct cpia_frame {
+	u8 *data;
+	int count;
+	int width;
+	int height;
+	volatile int state;
+};
+
+enum v4l_camstates {
+	CPIA_V4L_IDLE = 0,
+	CPIA_V4L_ERROR,
+	CPIA_V4L_COMMAND,
+	CPIA_V4L_GRABBING,
+	CPIA_V4L_STREAMING,
+	CPIA_V4L_STREAMING_PAUSED,
+};
+
+#define COMMAND_NONE			0x0000
+#define COMMAND_SETCOMPRESSION		0x0001
+#define COMMAND_SETCOMPRESSIONTARGET	0x0002
+#define COMMAND_SETCOLOURPARAMS		0x0004
+#define COMMAND_SETFORMAT		0x0008
+#define COMMAND_PAUSE			0x0010
+#define COMMAND_RESUME			0x0020
+#define COMMAND_SETYUVTHRESH		0x0040
+#define COMMAND_SETECPTIMING		0x0080
+#define COMMAND_SETCOMPRESSIONPARAMS	0x0100
+#define COMMAND_SETEXPOSURE		0x0200
+#define COMMAND_SETCOLOURBALANCE	0x0400
+#define COMMAND_SETSENSORFPS		0x0800
+#define COMMAND_SETAPCOR		0x1000
+#define COMMAND_SETFLICKERCTRL		0x2000
+#define COMMAND_SETVLOFFSET		0x4000
+
+struct cam_data {
+	int index;			/* which camera is this */
+        struct semaphore busy_lock;     /* guard against SMP multithreading */
+	struct cpia_camera_ops *ops;	/* lowlevel driver operations */
+	void *lowlevel_data;		/* private data for lowlevel driver */
+	u8 *raw_image;			/* buffer for raw image data */
+	struct cpia_frame decompressed_frame;
+                                        /* buffer to hold decompressed frame */
+	int image_size;		        /* sizeof last decompressed image */
+	int open_count;			/* # of process that have camera open */
+				/* camera status */
+	int fps;			/* actual fps reported by the camera */
+	int transfer_rate;		/* transfer rate from camera in kB/s */
+	u8 mainsFreq;			/* for flicker control */
+
+				/* proc interface */
+	struct semaphore param_lock;	/* params lock for this camera */
+	struct cam_params params;	/* camera settings */
+	struct proc_dir_entry *proc_entry;	/* /proc/cpia/videoX */
+	
+					/* v4l */
+	int video_size;			/* VIDEO_SIZE_ */
+	volatile enum v4l_camstates camstate;	/* v4l layer status */
+	struct video_device vdev;	/* v4l videodev */
+	struct video_picture vp;	/* v4l camera settings */
+	struct video_window vw;		/* v4l capture area */
+
+				/* mmap interface */
+	int curframe;			/* the current frame to grab into */
+	u8 *frame_buf;			/* frame buffer data */
+        struct cpia_frame frame[FRAME_NUM];
+				/* FRAME_NUM-buffering, so we need a array */
+
+        int first_frame;
+	volatile u32 cmd_queue;		/* queued commands */
+};
+
+static struct cam_data *camera[CPIA_MAXCAMS] ={ [0 ... CPIA_MAXCAMS-1] = NULL };
+
+/* Developer's Guide Table 5 p 3-34
+ * indexed by [mains][sensorFps.baserate][sensorFps.divisor]*/
+static u8 flicker_jumps[2][2][4] =
+{ { { 76, 38, 19, 9 }, { 92, 46, 23, 11 } },
+  { { 64, 32, 16, 8 }, { 76, 38, 19, 9} }
+};
+
+/* forward declaration of local function */
+static void reset_camera_struct(struct cam_data *cam);
+
+/**********************************************************************
+ *
+ * Memory management
+ *
+ * This is a shameless copy from the USB-cpia driver (linux kernel
+ * version 2.3.13 or so, I have no idea what this code actually does ;).
+ * Actually it seems to be a copy of a shameless copy of the bttv-driver.
+ * Or that is a copy of a shameless copy of ... (To the powers: is there
+ * no generic kernel-function to do this sort of stuff?)
+ *
+ **********************************************************************/
+
+static inline unsigned long uvirt_to_phys(unsigned long adr)
+{
+	pgd_t *pgd;
+	pmd_t *pmd;
+	pte_t *ptep, pte;
+
+	pgd = pgd_offset(current->mm, adr);
+	if (pgd_none(*pgd)) return 0;
+	pmd = pmd_offset(pgd, adr);
+	if (pmd_none(*pmd)) return 0;
+	ptep = pte_offset(pmd, adr/*&(~PGDIR_MASK)*/);
+	pte = *ptep;
+	if(pte_present(pte))
+		return 
+		  virt_to_phys((void *)(pte_page(pte)|(adr&(PAGE_SIZE-1))));
+	return 0;
+}
+
+static inline unsigned long kvirt_to_phys(unsigned long adr) 
+{
+	return uvirt_to_phys(VMALLOC_VMADDR(adr));
+}
+
+static void * rvmalloc(unsigned long size)
+{
+	void * mem;
+	unsigned long adr, page;
+
+	size += (PAGE_SIZE - 1);
+	size &= ~(PAGE_SIZE - 1);
+
+	mem=vmalloc(size);
+	if (mem) {
+		/* Clear the ram out, no junk to the user */
+		memset(mem, 0, size);
+		adr=(unsigned long) mem;
+		while (size > 0) {
+			page = kvirt_to_phys(adr);
+			mem_map_reserve(MAP_NR(phys_to_virt(page)));
+			adr+=PAGE_SIZE;
+			if (size > PAGE_SIZE) size-=PAGE_SIZE;
+			else  size=0;
+		}
+	}
+	return mem;
+}
+
+static void rvfree(void * mem, unsigned long size)
+{
+	unsigned long adr, page;
+        
+	size += (PAGE_SIZE - 1);
+	size &= ~(PAGE_SIZE - 1);
+
+	if (mem) {
+		adr=(unsigned long) mem;
+		while (size > 0) {
+			page = kvirt_to_phys(adr);
+			mem_map_unreserve(MAP_NR(phys_to_virt(page)));
+			adr+=PAGE_SIZE;
+			if (size > PAGE_SIZE) size-=PAGE_SIZE;
+			else size=0;
+		}
+		vfree(mem);
+	}
+}
+
+/**********************************************************************
+ *
+ * /proc interface
+ *
+ **********************************************************************/
+static struct proc_dir_entry *cpia_proc_root=NULL;
+
+static int cpia_read_proc(char *page, char **start, off_t off,
+                          int count, int *eof, void *data)
+{
+	char *out = page;
+	int len, tmp;
+	struct cam_data *cam = data;
+	char tmpstr[20];
+
+	/* IMPORTANT: This output MUST be kept under 4k (FIXME: PAGE_SIZE?)
+	 *            or we need to get more sophisticated. */
+
+	out += sprintf(out, "read-only\n-----------------------\n");
+	out += sprintf(out, "CPIA Version:             %d.%02d (%d.%d)\n",
+	               cam->params.version.firmwareVersion,
+	               cam->params.version.firmwareRevision,
+	               cam->params.version.vcVersion,
+	               cam->params.version.vcRevision);
+	out += sprintf(out, "CPIA PnP-ID:              %04x:%04x:%04x\n",
+	               cam->params.pnpID.vendor, cam->params.pnpID.product,
+	               cam->params.pnpID.deviceRevision);
+	out += sprintf(out, "VP-Version:               %d.%d %04x\n",
+	               cam->params.vpVersion.vpVersion,
+	               cam->params.vpVersion.vpRevision,
+	               cam->params.vpVersion.cameraHeadID);
+	
+	out += sprintf(out, "system_state:             %#04x\n",
+	               cam->params.status.systemState);
+	out += sprintf(out, "grab_state:               %#04x\n",
+	               cam->params.status.grabState);
+	out += sprintf(out, "stream_state:             %#04x\n",
+	               cam->params.status.streamState);
+	out += sprintf(out, "fatal_error:              %#04x\n",
+	               cam->params.status.fatalError);
+	out += sprintf(out, "cmd_error:                %#04x\n",
+	               cam->params.status.cmdError);
+	out += sprintf(out, "debug_flags:              %#04x\n",
+	               cam->params.status.debugFlags);
+	out += sprintf(out, "vp_status:                %#04x\n",
+	               cam->params.status.vpStatus);
+	out += sprintf(out, "error_code:               %#04x\n",
+	               cam->params.status.errorCode);
+	out += sprintf(out, "video_size:               %s\n",
+	               cam->params.format.videoSize == VIDEOSIZE_CIF ?
+		       "CIF " : "QCIF");
+	out += sprintf(out, "sub_sample:               %s\n",
+	               cam->params.format.subSample == SUBSAMPLE_420 ?
+		       "420" : "422");
+	out += sprintf(out, "yuv_order:                %s\n",
+	               cam->params.format.yuvOrder == YUVORDER_YUYV ?
+		       "YUYV" : "UYVY");
+	out += sprintf(out, "roi:                      (%3d, %3d) to (%3d, %3d)\n",
+	               cam->params.roi.colStart*8,
+	               cam->params.roi.rowStart*4,
+	               cam->params.roi.colEnd*8,
+	               cam->params.roi.rowEnd*4);
+	out += sprintf(out, "actual_fps:               %d\n", cam->fps);
+	out += sprintf(out, "transfer_rate:            %dkB/s\n",
+	               cam->transfer_rate);
+	
+	out += sprintf(out, "\nread-write\n");
+	out += sprintf(out, "-----------------------  current       min"
+	               "       max   default  comment\n");
+	out += sprintf(out, "brightness:             %8d  %8d  %8d  %8d\n",
+	               cam->params.colourParams.brightness, 0, 100, 50);
+	if(cam->params.version.firmwareVersion == 1 &&
+	   cam->params.version.firmwareRevision == 2) {
+		/* 1-02 firmware limits contrast to 80 */
+		tmp = 80;
+	} else {
+		tmp = 96;
+	}
+	out += sprintf(out, "contrast:               %8d  %8d  %8d  %8d"
+	               "  steps of 8\n",
+	               cam->params.colourParams.contrast, 0, tmp, 48);
+	out += sprintf(out, "saturation:             %8d  %8d  %8d  %8d\n",
+	               cam->params.colourParams.saturation, 0, 100, 50);
+	tmp = (25000+5000*cam->params.sensorFps.baserate)/
+	      (1<<cam->params.sensorFps.divisor);
+	out += sprintf(out, "sensor_fps:             %4d.%03d  %8d  %8d  %8d\n",
+	               tmp/1000, tmp%1000, 3, 30, 15);
+	out += sprintf(out, "stream_start_line:      %8d  %8d  %8d  %8d\n",
+	               2*cam->params.streamStartLine, 0,
+		       cam->params.format.videoSize == VIDEOSIZE_CIF ? 288:144,
+		       cam->params.format.videoSize == VIDEOSIZE_CIF ? 240:120);
+	out += sprintf(out, "ecp_timing:             %8s  %8s  %8s  %8s\n",
+	               cam->params.ecpTiming ? "slow" : "normal", "slow",
+		       "normal", "normal");
+
+	switch(cam->params.colourBalance.balanceMode) {
+	case 1:
+	  // FIXME case 3:
+		sprintf(tmpstr, "manual");
+		break;
+	case 2:
+		sprintf(tmpstr, "auto");
+		break;
+	default:
+		sprintf(tmpstr, "unknown");
+		break;
+	}
+	out += sprintf(out, "color_balance_mode:     %8s  %8s  %8s"
+		       "  %8s\n",  tmpstr, "manual", "auto", "auto");
+	out += sprintf(out, "red_gain:               %8d  %8d  %8d  %8d\n",
+	               cam->params.colourBalance.redGain, 0, 212, 32);
+	out += sprintf(out, "green_gain:             %8d  %8d  %8d  %8d\n",
+	               cam->params.colourBalance.greenGain, 0, 212, 6);
+	out += sprintf(out, "blue_gain:              %8d  %8d  %8d  %8d\n",
+	               cam->params.colourBalance.blueGain, 0, 212, 92);
+
+	if(cam->params.version.firmwareVersion == 1 &&
+	   cam->params.version.firmwareRevision == 2) {
+		/* 1-02 firmware limits gain to 2 */
+		sprintf(tmpstr, "%8d  %8d", 1, 2);
+	} else {
+		sprintf(tmpstr, "1,2,4,8");
+	}
+	if(cam->params.exposure.gainMode == 0) {
+		out += sprintf(out, "max_gain:                unknown  %18s"
+		               "  %8d\n", tmpstr, 2); 
+	} else {
+		out += sprintf(out, "max_gain:               %8d  %18s  %8d\n", 
+		               1<<(cam->params.exposure.gainMode-1), tmpstr, 2);
+	}
+	switch(cam->params.exposure.expMode) {
+	case 1:
+	case 3:
+		sprintf(tmpstr, "manual");
+		break;
+	case 2:
+		sprintf(tmpstr, "auto");
+		break;
+	default:
+		sprintf(tmpstr, "unknown");
+		break;
+	}
+	out += sprintf(out, "exposure_mode:          %8s  %8s  %8s"
+		       "  %8s\n",  tmpstr, "manual", "auto", "auto");
+	out += sprintf(out, "centre_weight:          %8s  %8s  %8s  %8s\n",
+	               (2-cam->params.exposure.centreWeight) ? "on" : "off",
+	               "off", "on", "on");
+	out += sprintf(out, "gain:                   %8d  %8d  max_gain  %8d  1,2,4,8 possible\n",
+	               1<<cam->params.exposure.gain, 1, 1);
+	if(cam->params.version.firmwareVersion == 1 &&
+	   cam->params.version.firmwareRevision == 2) {
+		/* 1-02 firmware limits fineExp to 127 */
+		tmp = 255;
+	} else {
+		tmp = 511;
+	}
+	out += sprintf(out, "fine_exp:               %8d  %8d  %8d  %8d\n",
+	               cam->params.exposure.fineExp*2, 0, tmp, 0);
+	if(cam->params.version.firmwareVersion == 1 &&
+	   cam->params.version.firmwareRevision == 2) {
+		/* 1-02 firmware limits coarseExpHi to 0 */
+		tmp = 255;
+	} else {
+		tmp = 65535;
+	}
+	out += sprintf(out, "coarse_exp:             %8d  %8d  %8d"
+		       "  %8d\n", cam->params.exposure.coarseExpLo+
+		       256*cam->params.exposure.coarseExpHi, 0, tmp, 185);
+	out += sprintf(out, "red_comp:               %8d  %8d  %8d  %8d\n",
+	               cam->params.exposure.redComp, 220, 255, 220);
+	out += sprintf(out, "green1_comp:            %8d  %8d  %8d  %8d\n",
+	               cam->params.exposure.green1Comp, 214, 255, 214);
+	out += sprintf(out, "green2_comp:            %8d  %8d  %8d  %8d\n",
+	               cam->params.exposure.green2Comp, 214, 255, 214);
+	out += sprintf(out, "blue_comp:              %8d  %8d  %8d  %8d\n",
+	               cam->params.exposure.blueComp, 230, 255, 230);
+	
+	out += sprintf(out, "apcor_gain1:            %#8x  %#8x  %#8x  %#8x\n",
+	               cam->params.apcor.gain1, 0, 0xff, 0x1c);
+	out += sprintf(out, "apcor_gain2:            %#8x  %#8x  %#8x  %#8x\n",
+	               cam->params.apcor.gain2, 0, 0xff, 0x1a);
+	out += sprintf(out, "apcor_gain4:            %#8x  %#8x  %#8x  %#8x\n",
+	               cam->params.apcor.gain4, 0, 0xff, 0x2d);
+	out += sprintf(out, "apcor_gain8:            %#8x  %#8x  %#8x  %#8x\n",
+	               cam->params.apcor.gain8, 0, 0xff, 0x2a);
+	out += sprintf(out, "vl_offset_gain1:        %8d  %8d  %8d  %8d\n",
+	               cam->params.vlOffset.gain1, 0, 255, 24);
+	out += sprintf(out, "vl_offset_gain2:        %8d  %8d  %8d  %8d\n",
+	               cam->params.vlOffset.gain2, 0, 255, 28);
+	out += sprintf(out, "vl_offset_gain4:        %8d  %8d  %8d  %8d\n",
+	               cam->params.vlOffset.gain4, 0, 255, 30);
+	out += sprintf(out, "vl_offset_gain8:        %8d  %8d  %8d  %8d\n",
+	               cam->params.vlOffset.gain8, 0, 255, 30);
+	out += sprintf(out, "flicker_control:        %8s  %8s  %8s  %8s\n",
+	               cam->params.flickerControl.flickerMode ? "on" : "off",
+		       "off", "on", "off");
+	out += sprintf(out, "mains_frequency:        %8d  %8d  %8d  %8d"
+	               " only 50/60\n",
+	               cam->mainsFreq ? 60 : 50, 50, 60, 50);
+	out += sprintf(out, "allowable_overexposure: %8d  %8d  %8d  %8d\n",
+	               cam->params.flickerControl.allowableOverExposure, 0,
+		       255, 0);
+	out += sprintf(out, "compression_mode:       ");
+	switch(cam->params.compression.mode) {
+	case CPIA_COMPRESSION_NONE:
+		out += sprintf(out, "%8s", "none");
+		break;
+	case CPIA_COMPRESSION_AUTO:
+		out += sprintf(out, "%8s", "auto");
+		break;
+	case CPIA_COMPRESSION_MANUAL:
+		out += sprintf(out, "%8s", "manual");
+		break;
+	default:
+		out += sprintf(out, "%8s", "unknown");
+		break;
+	}
+	out += sprintf(out, "    none,auto,manual      auto\n");
+	out += sprintf(out, "decimation:             %8s  %8s  %8s  %8s\n",
+        	       cam->params.compression.decimation == 
+		       DECIMATION_ENAB ? "on":"off", "off", "off",
+		       "off");
+	out += sprintf(out, "compression_target:    %9s %9s %9s %9s\n",
+	               cam->params.compressionTarget.frTargeting  == 
+		       CPIA_COMPRESSION_TARGET_FRAMERATE ?
+		       "framerate":"quality",
+		       "framerate", "quality", "quality");
+	out += sprintf(out, "target_framerate:       %8d  %8d  %8d  %8d\n",
+	               cam->params.compressionTarget.targetFR, 0, 30, 7);
+	out += sprintf(out, "target_quality:         %8d  %8d  %8d  %8d\n",
+	               cam->params.compressionTarget.targetQ, 0, 255, 10);
+	out += sprintf(out, "y_threshold:            %8d  %8d  %8d  %8d\n",
+	               cam->params.yuvThreshold.yThreshold, 0, 31, 15);
+	out += sprintf(out, "uv_threshold:           %8d  %8d  %8d  %8d\n",
+	               cam->params.yuvThreshold.uvThreshold, 0, 31, 15);
+	out += sprintf(out, "hysteresis:             %8d  %8d  %8d  %8d\n",
+	               cam->params.compressionParams.hysteresis, 0, 255, 3);
+	out += sprintf(out, "threshold_max:          %8d  %8d  %8d  %8d\n",
+	               cam->params.compressionParams.threshMax, 0, 255, 11);
+	out += sprintf(out, "small_step:             %8d  %8d  %8d  %8d\n",
+	               cam->params.compressionParams.smallStep, 0, 255, 1);
+	out += sprintf(out, "large_step:             %8d  %8d  %8d  %8d\n",
+	               cam->params.compressionParams.largeStep, 0, 255, 3);
+	out += sprintf(out, "decimation_hysteresis:  %8d  %8d  %8d  %8d\n",
+	               cam->params.compressionParams.decimationHysteresis,
+		       0, 255, 2);
+	out += sprintf(out, "fr_diff_step_thresh:    %8d  %8d  %8d  %8d\n",
+	               cam->params.compressionParams.frDiffStepThresh,
+		       0, 255, 5);
+	out += sprintf(out, "q_diff_step_thresh:     %8d  %8d  %8d  %8d\n",
+	               cam->params.compressionParams.qDiffStepThresh,
+		       0, 255, 3);
+	out += sprintf(out, "decimation_thresh_mod:  %8d  %8d  %8d  %8d\n",
+	               cam->params.compressionParams.decimationThreshMod,
+		       0, 255, 2);
+	
+	len = out - page;
+	len -= off;
+	if (len < count) {
+		*eof = 1;
+		if (len <= 0) return 0;
+	} else {
+		len = count;
+	}
+	*start = page + off;
+	return len;
+}
+
+static int cpia_write_proc(struct file *file, const char *buffer,
+                           unsigned long count, void *data)
+{
+	struct cam_data *cam = data;
+	struct cam_params new_params;
+	int retval, len, colon_found;
+	int size = count;
+	char *p;
+	unsigned long val;
+	u32 command_flags = 0;
+	u8 new_mains;
+	
+	if(down_interruptible(&cam->param_lock)) {
+		return -ERESTARTSYS;
+	}
+	
+	/*
+	 * Skip over leading whitespace
+	 */
+	while (count && isspace(*buffer)) {
+		--count;
+		++buffer;
+	}
+	
+	memcpy(&new_params, &cam->params, sizeof(struct cam_params));
+	new_mains = cam->mainsFreq;
+	
+#define MATCH(x) (len=strlen(x), len <= count && strncmp(buffer, x, len) == 0)
+#define FIRMWARE_VERSION(x,y) (new_params.version.firmwareVersion == (x) && \
+                               new_params.version.firmwareRevision == (y))
+#define VALUE \
+	simple_strtoul(buffer, &p, 0); \
+	if(p == buffer) { \
+		retval = -EINVAL; \
+	} else { \
+		count -= p-buffer; \
+		buffer = p; \
+	}
+#define FIND_VALUE \
+	buffer += len;\
+	count -= len; \
+	colon_found=0; \
+	while(count && (*buffer == ' ' || *buffer == '\t' || \
+	                (!colon_found && *buffer == ':'))) { \
+		if(*buffer == ':') colon_found = 1; \
+		--count; \
+		++buffer; \
+	} \
+	if(!count || !colon_found) { \
+		retval = -EINVAL; \
+	}
+#define FIND_END \
+	if(retval == 0) { \
+		while(count && isspace(*buffer) && *buffer != '\n'){ \
+			--count; \
+			++buffer; \
+		} \
+		if(count) { \
+			if(*buffer != '\n' && *buffer != ';') { \
+				retval = -EINVAL; \
+			} else { \
+				--count; \
+				++buffer; \
+			} \
+		} \
+	}
+	retval = 0;
+	while(count && !retval) {
+		if(MATCH("brightness")) {
+			FIND_VALUE
+			if(!retval) { val = VALUE }
+			if(!retval) {
+				if(val <= 100)
+					new_params.colourParams.brightness=val;
+				else retval = -EINVAL;
+			}
+			command_flags |= COMMAND_SETCOLOURPARAMS;
+			FIND_END
+		} else if(MATCH("contrast")) {
+			FIND_VALUE
+			if(!retval) { val = VALUE }
+			if(!retval) {
+				if(val <= 100) {
+					/* contrast is in steps of 8, so round*/
+					val = ((val + 3) / 8) * 8;
+					/* 1-02 firmware limits contrast to 80*/
+					if(FIRMWARE_VERSION(1,2) && val > 80)
+						val = 80;
+					new_params.colourParams.contrast = val;
+				} else retval = -EINVAL;
+			}
+			command_flags |= COMMAND_SETCOLOURPARAMS;
+			FIND_END
+		} else if(MATCH("saturation")) {
+			FIND_VALUE
+			if(!retval) { val = VALUE }
+			if(!retval) {
+				if(val <= 100)
+					new_params.colourParams.saturation=val;
+				else retval = -EINVAL;
+			}
+			command_flags |= COMMAND_SETCOLOURPARAMS;
+			FIND_END
+		} else if(MATCH("sensor_fps")) {
+			FIND_VALUE
+			if(!retval) { val = VALUE }
+			if(!retval) {
+				/* find values so that sensorFPS is minimized,
+				 * but >= val */
+				if(val > 30) {
+					retval = -EINVAL;
+				} else if(val > 25) {
+					new_params.sensorFps.divisor = 0;
+					new_params.sensorFps.baserate = 1;
+				} else if(val > 15) {
+					new_params.sensorFps.divisor = 0;
+					new_params.sensorFps.baserate = 0;
+				} else if(val > 12) {
+					new_params.sensorFps.divisor = 1;
+					new_params.sensorFps.baserate = 1;
+				} else if(val > 7) {
+					new_params.sensorFps.divisor = 1;
+					new_params.sensorFps.baserate = 0;
+				} else if(val > 6) {
+					new_params.sensorFps.divisor = 2;
+					new_params.sensorFps.baserate = 1;
+				} else if(val > 3) {
+					new_params.sensorFps.divisor = 2;
+					new_params.sensorFps.baserate = 0;
+				} else {
+					new_params.sensorFps.divisor = 3;
+					/* Either base rate would work here */
+					new_params.sensorFps.baserate = 1;
+				}
+				new_params.flickerControl.coarseJump = 
+					flicker_jumps[new_mains]
+					[new_params.sensorFps.baserate]
+					[new_params.sensorFps.divisor];
+				if(new_params.flickerControl.flickerMode)
+					command_flags |= COMMAND_SETFLICKERCTRL;
+			}
+			command_flags |= COMMAND_SETSENSORFPS;
+			FIND_END
+		} else if(MATCH("stream_start_line")) {
+			FIND_VALUE
+			if(!retval) { val = VALUE }
+			if(!retval) {
+				int max_line = 288;
+				if(new_params.format.videoSize==VIDEOSIZE_QCIF)
+					max_line = 144;
+				if(val <= max_line)
+					new_params.streamStartLine = val/2;
+				else retval = -EINVAL;
+			}
+			FIND_END
+		} else if(MATCH("ecp_timing")) {
+			FIND_VALUE
+			if(!retval && MATCH("normal")) {
+				buffer += len;
+				count -= len;
+				new_params.ecpTiming = 0;
+			} else if(!retval && MATCH("slow")) {
+				buffer += len;
+				count -= len;
+				new_params.ecpTiming = 1;
+			} else {
+				retval = -EINVAL;
+			}
+			command_flags |= COMMAND_SETECPTIMING;
+			FIND_END
+		} else if(MATCH("color_balance_mode")) {
+			FIND_VALUE
+			if(!retval && MATCH("manual")) {
+				buffer += len;
+				count -= len;
+				new_params.colourBalance.balanceMode=1;
+			} else if(!retval && MATCH("auto")) {
+				buffer += len;
+				count -= len;
+				new_params.colourBalance.balanceMode=2;
+			} else {
+				retval = -EINVAL;
+			}
+			command_flags |= COMMAND_SETCOLOURBALANCE;
+			FIND_END
+		} else if(MATCH("red_gain")) {
+			FIND_VALUE
+			if(!retval) { val = VALUE }
+			if(!retval) {
+				if(val <= 212)
+					new_params.colourBalance.redGain = val;
+				else retval = -EINVAL;
+			}
+			command_flags |= COMMAND_SETCOLOURBALANCE;
+			FIND_END
+		} else if(MATCH("green_gain")) {
+			FIND_VALUE
+			if(!retval) { val = VALUE }
+			if(!retval) {
+				if(val <= 212)
+					new_params.colourBalance.greenGain=val;
+				else retval = -EINVAL;
+			}
+			command_flags |= COMMAND_SETCOLOURBALANCE;
+			FIND_END
+		} else if(MATCH("blue_gain")) {
+			FIND_VALUE
+			if(!retval) { val = VALUE }
+			if(!retval) {
+				if(val <= 212)
+					new_params.colourBalance.blueGain = val;
+				else retval = -EINVAL;
+			}
+			command_flags |= COMMAND_SETCOLOURBALANCE;
+			FIND_END
+		} else if(MATCH("max_gain")) {
+			FIND_VALUE
+			if(!retval) { val = VALUE }
+			if(!retval) {
+				/* 1-02 firmware limits gain to 2 */
+				if(FIRMWARE_VERSION(1,2) && val > 2)
+					val = 2;
+				switch(val) {
+				case 1:
+					new_params.exposure.gainMode = 1;
+					break;
+				case 2:
+					new_params.exposure.gainMode = 2;
+					break;
+				case 4:
+					new_params.exposure.gainMode = 3;
+					break;
+				case 8:
+					new_params.exposure.gainMode = 4;
+					break;
+				default:
+					retval = -EINVAL;
+					break;
+				}
+			}
+			command_flags |= COMMAND_SETEXPOSURE;
+			FIND_END
+		} else if(MATCH("exposure_mode")) {
+			FIND_VALUE
+			if(!retval && MATCH("auto")) {
+				buffer += len;
+				count -= len;
+				new_params.exposure.expMode = 2;
+			} else if(!retval && MATCH("manual")) {
+				buffer += len;
+				count -= len;
+				if(new_params.exposure.expMode == 2)
+					new_params.exposure.expMode = 3;
+				new_params.flickerControl.flickerMode = 0;
+				command_flags |= COMMAND_SETFLICKERCTRL;
+			} else {
+				retval = -EINVAL;
+			}
+			command_flags |= COMMAND_SETEXPOSURE;
+			FIND_END
+		} else if(MATCH("centre_weight")) {
+			FIND_VALUE
+			if(!retval && MATCH("on")) {
+				buffer += len;
+				count -= len;
+				new_params.exposure.centreWeight = 1;
+			} else if(!retval && MATCH("off")) {
+				buffer += len;
+				count -= len;
+				new_params.exposure.centreWeight = 2;
+			} else {
+				retval = -EINVAL;
+			}
+			command_flags |= COMMAND_SETEXPOSURE;
+			FIND_END
+		} else if(MATCH("gain")) {
+			FIND_VALUE
+			if(!retval) { val = VALUE }
+			if(!retval) {
+				switch(val) {
+				case 1:
+					new_params.exposure.gain = 0;
+					new_params.exposure.expMode = 1;
+					new_params.flickerControl.flickerMode = 0;
+					command_flags |= COMMAND_SETFLICKERCTRL;
+					break;
+				case 2:
+					new_params.exposure.gain = 1;
+					new_params.exposure.expMode = 1;
+					new_params.flickerControl.flickerMode = 0;
+					command_flags |= COMMAND_SETFLICKERCTRL;
+					break;
+				case 4:
+					new_params.exposure.gain = 2;
+					new_params.exposure.expMode = 1;
+					new_params.flickerControl.flickerMode = 0;
+					command_flags |= COMMAND_SETFLICKERCTRL;
+					break;
+				case 8:
+					new_params.exposure.gain = 3;
+					new_params.exposure.expMode = 1;
+					new_params.flickerControl.flickerMode = 0;
+					command_flags |= COMMAND_SETFLICKERCTRL;
+					break;
+				default:
+					retval = -EINVAL;
+					break;
+				}
+				command_flags |= COMMAND_SETEXPOSURE;
+				if(new_params.exposure.gain >
+				   new_params.exposure.gainMode-1)
+					retval = -EINVAL;
+			}
+			FIND_END
+		} else if(MATCH("fine_exp")) {
+			FIND_VALUE
+			if(!retval) { val = VALUE }
+			if(!retval) {
+				if(val < 256) {
+					/* 1-02 firmware limits fineExp to 127*/
+					if(FIRMWARE_VERSION(1,2) && val > 127)
+						val = 127;
+					new_params.exposure.fineExp = val;
+					new_params.exposure.expMode = 1;
+					command_flags |= COMMAND_SETEXPOSURE;
+					new_params.flickerControl.flickerMode = 0;
+					command_flags |= COMMAND_SETFLICKERCTRL;
+				} else retval = -EINVAL;
+			}
+			FIND_END
+		} else if(MATCH("coarse_exp")) {
+			FIND_VALUE
+			if(!retval) { val = VALUE }
+			if(!retval) {
+				if(val < 65536) {
+					/* 1-02 firmware limits
+					 * coarseExp to 255 */
+					if(FIRMWARE_VERSION(1,2) && val > 255)
+						val = 255;
+					new_params.exposure.coarseExpLo =
+						val & 0xff;
+					new_params.exposure.coarseExpHi =
+						val >> 8;
+					new_params.exposure.expMode = 1;
+					command_flags |= COMMAND_SETEXPOSURE;
+					new_params.flickerControl.flickerMode = 0;
+					command_flags |= COMMAND_SETFLICKERCTRL;
+				} else retval = -EINVAL;
+			}
+			FIND_END
+		} else if(MATCH("red_comp")) {
+			FIND_VALUE
+			if(!retval) { val = VALUE }
+			if(!retval) {
+				if(val >= 220 && val <= 255) {
+					new_params.exposure.redComp = val;
+					command_flags |= COMMAND_SETEXPOSURE;
+				} else retval = -EINVAL;
+			}
+			FIND_END
+		} else if(MATCH("green1_comp")) {
+			FIND_VALUE
+			if(!retval) { val = VALUE }
+			if(!retval) {
+				if(val >= 214 && val <= 255) {
+					new_params.exposure.green1Comp = val;
+					command_flags |= COMMAND_SETEXPOSURE;
+				} else retval = -EINVAL;
+			}
+			FIND_END
+		} else if(MATCH("green2_comp")) {
+			FIND_VALUE
+			if(!retval) { val = VALUE }
+			if(!retval) {
+				if(val >= 214 && val <= 255) {
+					new_params.exposure.green2Comp = val;
+					command_flags |= COMMAND_SETEXPOSURE;
+				} else retval = -EINVAL;
+			}
+			FIND_END
+		} else if(MATCH("blue_comp")) {
+			FIND_VALUE
+			if(!retval) { val = VALUE }
+			if(!retval) {
+				if(val >= 230 && val <= 255) {
+					new_params.exposure.blueComp = val;
+					command_flags |= COMMAND_SETEXPOSURE;
+				} else retval = -EINVAL;
+			}
+			FIND_END
+		} else if(MATCH("apcor_gain1")) {
+			FIND_VALUE
+			if(!retval) { val = VALUE }
+			if(!retval) {
+				command_flags |= COMMAND_SETAPCOR;
+				if(val <= 0xff) new_params.apcor.gain1 = val;
+				else retval = -EINVAL;
+			}
+			FIND_END
+		} else if(MATCH("apcor_gain2")) {
+			FIND_VALUE
+			if(!retval) { val = VALUE }
+			if(!retval) {
+				command_flags |= COMMAND_SETAPCOR;
+				if(val <= 0xff) new_params.apcor.gain2 = val;
+				else retval = -EINVAL;
+			}
+			FIND_END
+		} else if(MATCH("apcor_gain4")) {
+			FIND_VALUE
+			if(!retval) { val = VALUE }
+			if(!retval) {
+				command_flags |= COMMAND_SETAPCOR;
+				if(val <= 0xff) new_params.apcor.gain4 = val;
+				else retval = -EINVAL;
+			}
+			FIND_END
+		} else if(MATCH("apcor_gain8")) {
+			FIND_VALUE
+			if(!retval) { val = VALUE }
+			if(!retval) {
+				command_flags |= COMMAND_SETAPCOR;
+				if(val <= 0xff) new_params.apcor.gain8 = val;
+				else retval = -EINVAL;
+			}
+			FIND_END
+		} else if(MATCH("vl_offset_gain1")) {
+			FIND_VALUE
+			if(!retval) { val = VALUE }
+			if(!retval) {
+				if(val <= 0xff) new_params.vlOffset.gain1 = val;
+				else retval = -EINVAL;
+			}
+			FIND_END
+			command_flags |= COMMAND_SETVLOFFSET;
+		} else if(MATCH("vl_offset_gain2")) {
+			FIND_VALUE
+			if(!retval) { val = VALUE }
+			if(!retval) {
+				if(val <= 0xff) new_params.vlOffset.gain2 = val;
+				else retval = -EINVAL;
+			}
+			FIND_END
+			command_flags |= COMMAND_SETVLOFFSET;
+		} else if(MATCH("vl_offset_gain4")) {
+			FIND_VALUE
+			if(!retval) { val = VALUE }
+			if(!retval) {
+				if(val <= 0xff) new_params.vlOffset.gain4 = val;
+				else retval = -EINVAL;
+			}
+			FIND_END
+			command_flags |= COMMAND_SETVLOFFSET;
+		} else if(MATCH("vl_offset_gain8")) {
+			FIND_VALUE
+			if(!retval) { val = VALUE }
+			if(!retval) {
+				if(val <= 0xff) new_params.vlOffset.gain8 = val;
+				else retval = -EINVAL;
+			}
+			FIND_END
+			command_flags |= COMMAND_SETVLOFFSET;
+		} else if(MATCH("flicker_control")) {
+			FIND_VALUE
+			if(!retval && MATCH("on")) {
+				buffer += len;
+				count -= len;
+				new_params.flickerControl.flickerMode = 1;
+				new_params.exposure.expMode = 2;
+				command_flags |= COMMAND_SETEXPOSURE;
+			} else if(!retval && MATCH("off")) {
+				buffer += len;
+				count -= len;
+				new_params.flickerControl.flickerMode = 0;
+			} else {
+				retval = -EINVAL;
+			}
+			command_flags |= COMMAND_SETFLICKERCTRL;
+			FIND_END
+		} else if(MATCH("mains_frequency")) {
+			FIND_VALUE
+			if(!retval && MATCH("50")) {
+				buffer += len;
+				count -= len;
+				new_mains = 0;
+				new_params.flickerControl.coarseJump = 
+					flicker_jumps[new_mains]
+					[new_params.sensorFps.baserate]
+					[new_params.sensorFps.divisor];
+				if(new_params.flickerControl.flickerMode)
+					command_flags |= COMMAND_SETFLICKERCTRL;
+			} else if(!retval && MATCH("60")) {
+				buffer += len;
+				count -= len;
+				new_mains = 1;
+				new_params.flickerControl.coarseJump = 
+					flicker_jumps[new_mains]
+					[new_params.sensorFps.baserate]
+					[new_params.sensorFps.divisor];
+				if(new_params.flickerControl.flickerMode)
+					command_flags |= COMMAND_SETFLICKERCTRL;
+			} else {
+				retval = -EINVAL;
+			}
+			FIND_END
+		} else if(MATCH("allowable_overexposure")) {
+			FIND_VALUE
+			if(!retval) { val = VALUE }
+			if(!retval) {
+				if(val <= 0xff) {
+					new_params.flickerControl.
+						allowableOverExposure = val;
+					command_flags |= COMMAND_SETFLICKERCTRL;
+				} else retval = -EINVAL;
+			}
+			FIND_END
+		} else if(MATCH("compression_mode")) {
+			FIND_VALUE
+			if(!retval && MATCH("none")) {
+				buffer += len;
+				count -= len;
+				new_params.compression.mode =
+					CPIA_COMPRESSION_NONE;
+			} else if(!retval && MATCH("auto")) {
+				buffer += len;
+				count -= len;
+				new_params.compression.mode =
+					CPIA_COMPRESSION_AUTO;
+			} else if(!retval && MATCH("manual")) {
+				buffer += len;
+				count -= len;
+				new_params.compression.mode =
+					CPIA_COMPRESSION_MANUAL;
+			} else {
+				retval = -EINVAL;
+			}
+			command_flags |= COMMAND_SETCOMPRESSION;
+			FIND_END
+		} else if(MATCH("decimation")) {
+			FIND_VALUE
+			if(!retval && MATCH("off")) {
+				buffer += len;
+				count -= len;
+				new_params.compression.decimation = 0;
+			} else {
+				retval = -EINVAL;
+			}
+			command_flags |= COMMAND_SETCOMPRESSION;
+			FIND_END
+		} else if(MATCH("compression_target")) {
+			FIND_VALUE
+			if(!retval && MATCH("quality")) {
+				buffer += len;
+				count -= len;
+				new_params.compressionTarget.frTargeting = 
+					CPIA_COMPRESSION_TARGET_QUALITY;
+			} else if(!retval && MATCH("framerate")) {
+				buffer += len;
+				count -= len;
+				new_params.compressionTarget.frTargeting = 
+					CPIA_COMPRESSION_TARGET_FRAMERATE;
+			} else {
+				retval = -EINVAL;
+			}
+			command_flags |= COMMAND_SETCOMPRESSIONTARGET;
+			FIND_END
+		} else if(MATCH("target_framerate")) {
+			FIND_VALUE
+			if(!retval) { val = VALUE }
+			if(!retval)
+				new_params.compressionTarget.targetFR = val;
+			command_flags |= COMMAND_SETCOMPRESSIONTARGET;
+			FIND_END
+		} else if(MATCH("target_quality")) {
+			FIND_VALUE
+			if(!retval) { val = VALUE }
+			if(!retval) new_params.compressionTarget.targetQ = val;
+			command_flags |= COMMAND_SETCOMPRESSIONTARGET;
+			FIND_END
+		} else if(MATCH("y_threshold")) {
+			FIND_VALUE
+			if(!retval) { val = VALUE }
+			if(!retval) {
+				if(val < 32)
+					new_params.yuvThreshold.yThreshold=val;
+				else retval = -EINVAL;
+			}
+			command_flags |= COMMAND_SETYUVTHRESH;
+			FIND_END
+		} else if(MATCH("uv_threshold")) {
+			FIND_VALUE
+			if(!retval) { val = VALUE }
+			if(!retval) {
+				if(val < 32)
+					new_params.yuvThreshold.uvThreshold=val;
+				else retval = -EINVAL;
+			}
+			command_flags |= COMMAND_SETYUVTHRESH;
+			FIND_END
+		} else if(MATCH("hysteresis")) {
+			FIND_VALUE
+			if(!retval) { val = VALUE }
+			if(!retval) {
+				if(retval <= 0xff)
+				    new_params.compressionParams.hysteresis=val;
+				else retval = -EINVAL;
+			}
+			command_flags |= COMMAND_SETCOMPRESSIONPARAMS;
+			FIND_END
+		} else if(MATCH("threshold_max")) {
+			FIND_VALUE
+			if(!retval) { val = VALUE }
+			if(!retval) {
+				if(retval <= 0xff)
+				    new_params.compressionParams.threshMax=val;
+				else retval = -EINVAL;
+			}
+			command_flags |= COMMAND_SETCOMPRESSIONPARAMS;
+			FIND_END
+		} else if(MATCH("small_step")) {
+			FIND_VALUE
+			if(!retval) { val = VALUE }
+			if(!retval) {
+				if(retval <= 0xff)
+				    new_params.compressionParams.smallStep=val;
+				else retval = -EINVAL;
+			}
+			command_flags |= COMMAND_SETCOMPRESSIONPARAMS;
+			FIND_END
+		} else if(MATCH("large_step")) {
+			FIND_VALUE
+			if(!retval) { val = VALUE }
+			if(!retval) {
+				if(retval <= 0xff)
+				    new_params.compressionParams.largeStep=val;
+				else retval = -EINVAL;
+			}
+			command_flags |= COMMAND_SETCOMPRESSIONPARAMS;
+			FIND_END
+		} else if(MATCH("decimation_hysteresis")) {
+			FIND_VALUE
+			if(!retval) { val = VALUE }
+			if(!retval) {
+				if(retval <= 0xff)
+				    new_params.compressionParams.
+				        decimationHysteresis = val;
+				else retval = -EINVAL;
+			}
+			command_flags |= COMMAND_SETCOMPRESSIONPARAMS;
+			FIND_END
+		} else if(MATCH("fr_diff_step_thresh")) {
+			FIND_VALUE
+			if(!retval) { val = VALUE }
+			if(!retval) {
+				if(retval <= 0xff)
+				    new_params.compressionParams.
+				        frDiffStepThresh = val;
+				else retval = -EINVAL;
+			}
+			command_flags |= COMMAND_SETCOMPRESSIONPARAMS;
+			FIND_END
+		} else if(MATCH("q_diff_step_thresh")) {
+			FIND_VALUE
+			if(!retval) { val = VALUE }
+			if(!retval) {
+				if(retval <= 0xff)
+				    new_params.compressionParams.
+				        qDiffStepThresh = val;
+				else retval = -EINVAL;
+			command_flags |= COMMAND_SETCOMPRESSIONPARAMS;
+			FIND_END
+		} else if(MATCH("decimation_thresh_mod")) {
+			}
+			FIND_VALUE
+			if(!retval) { val = VALUE }
+			if(!retval) {
+				if(retval <= 0xff)
+				    new_params.compressionParams.
+				        decimationThreshMod = val;
+				else retval = -EINVAL;
+			}
+			command_flags |= COMMAND_SETCOMPRESSIONPARAMS;
+			FIND_END
+		} else {
+			retval = -EINVAL;
+		}
+	}
+#undef MATCH	
+#undef FIRMWARE_VERSION
+#undef VALUE
+#undef FIND_VALUE
+#undef FIND_END
+	if(retval == 0) {
+		if(command_flags & COMMAND_SETCOLOURPARAMS) {
+			/* Adjust cam->vp to reflect these changes */
+			cam->vp.brightness =
+				new_params.colourParams.brightness*65535/100;
+			cam->vp.contrast =
+				new_params.colourParams.contrast*65535/100;
+			cam->vp.colour =
+				new_params.colourParams.saturation*65535/100;
+		}
+		
+		memcpy(&cam->params, &new_params, sizeof(struct cam_params));
+		cam->mainsFreq = new_mains;
+		cam->cmd_queue |= command_flags;
+		retval = size;
+	} else {
+		DBG("error: %d\n", retval);
+	}
+	
+	up(&cam->param_lock);
+	
+	return retval;
+}
+
+static void create_proc_cpia_cam(struct cam_data *cam)
+{
+	char name[7];
+	struct proc_dir_entry *ent;
+	
+	if(cpia_proc_root == NULL || cam == NULL) {
+		return;
+	}
+	sprintf(name, "video%d", cam->vdev.minor);
+	
+	ent = create_proc_entry(name, S_IFREG|S_IRUGO|S_IWUSR, cpia_proc_root);
+	if (!ent) return;
+	ent->data = cam;
+	ent->read_proc = cpia_read_proc;
+	ent->write_proc = cpia_write_proc;
+	ent->size = 3623;
+	cam->proc_entry = ent;
+}
+
+static void destroy_proc_cpia_cam(struct cam_data *cam)
+{
+	char name[7];
+	
+	if(cam->proc_entry == NULL) return;
+	
+	sprintf(name, "video%d", cam->vdev.minor);
+	remove_proc_entry(name, cpia_proc_root);
+}
+
+static void proc_cpia_create(void)
+{
+	cpia_proc_root = create_proc_entry("cpia", S_IFDIR, 0);
+}
+
+#ifdef MODULE
+static void proc_cpia_destroy(void)
+{
+	remove_proc_entry("cpia", 0);
+}
+#endif
+
+/* ----------------------- debug functions ---------------------- */
+
+#define printstatus(cam) \
+  DBG("%02x %02x %02x %02x %02x %02x %02x %02x\n",\
+	cam->params.status.systemState, cam->params.status.grabState, \
+	cam->params.status.streamState, cam->params.status.fatalError, \
+	cam->params.status.cmdError, cam->params.status.debugFlags, \
+	cam->params.status.vpStatus, cam->params.status.errorCode);
+
+/* ----------------------- v4l helpers -------------------------- */
+
+/* supported frame palettes and depths */
+static inline int valid_mode(u16 palette, u16 depth)
+{
+	return (palette == VIDEO_PALETTE_GREY && depth == 8) ||
+	       (palette == VIDEO_PALETTE_RGB555 && depth == 16) ||
+	       (palette == VIDEO_PALETTE_RGB565 && depth == 16) ||
+	       (palette == VIDEO_PALETTE_RGB24 && depth == 24) ||
+	       (palette == VIDEO_PALETTE_RGB32 && depth == 32) ||
+	       (palette == VIDEO_PALETTE_YUV422 && depth == 16) ||
+	       (palette == VIDEO_PALETTE_YUYV && depth == 16) ||
+	       (palette == VIDEO_PALETTE_UYVY && depth == 16);
+}
+
+static int match_videosize( int width, int height )
+{
+	/* return the best match, where 'best' is as always
+	 * the largest that is not bigger than what is requested. */
+	if(width>=352 && height>=288) {
+		return VIDEOSIZE_352_288; /* CIF */
+	}
+	if(width>=320 && height>=240) {
+		return VIDEOSIZE_320_240; /* SIF */
+	}
+	if(width>=288 && height>=216) {
+		return VIDEOSIZE_288_216;
+	}
+	if(width>=256 && height>=192) {
+		return VIDEOSIZE_256_192;
+	}
+	if(width>=224 && height>=168) {
+		return VIDEOSIZE_224_168;
+	}
+	if(width>=192 && height>=144) {
+		return VIDEOSIZE_192_144;
+	}
+	if(width>=176 && height>=144) {
+		return VIDEOSIZE_176_144; /* QCIF */
+	}
+	if(width>=160 && height>=120) {
+		return VIDEOSIZE_160_120; /* QSIF */
+	}
+	if(width>=128 && height>=96) {
+		return VIDEOSIZE_128_96;
+	}
+	if(width>=64 && height>=48) {
+		return VIDEOSIZE_64_48;
+	}
+	if(width>=48 && height>=48) {
+		return VIDEOSIZE_48_48;
+	}
+
+	return -1;
+}
+
+/* these are the capture sizes we support */
+static void set_vw_size(struct cam_data *cam)
+{
+	/* the col/row/start/end values are the result of simple math    */
+	/* study the SetROI-command in cpia developers guide p 2-22      */
+	/* streamStartLine is set to the recommended value in the cpia   */
+	/*  developers guide p 3-37                                      */
+	switch(cam->video_size) {
+	case VIDEOSIZE_CIF:
+		cam->vw.width = 352;
+		cam->vw.height = 288;
+		cam->params.format.videoSize=VIDEOSIZE_CIF;
+		cam->params.roi.colStart=0;
+		cam->params.roi.colEnd=44;
+		cam->params.roi.rowStart=0;
+		cam->params.roi.rowEnd=72;
+		cam->params.streamStartLine = 120;
+		break;
+	case VIDEOSIZE_SIF:
+		cam->vw.width = 320;
+		cam->vw.height = 240;
+		cam->params.format.videoSize=VIDEOSIZE_CIF;
+		cam->params.roi.colStart=2;
+		cam->params.roi.colEnd=42;
+		cam->params.roi.rowStart=6;
+		cam->params.roi.rowEnd=66;
+		cam->params.streamStartLine = 120;
+		break;
+	case VIDEOSIZE_288_216:
+		cam->vw.width = 288;
+		cam->vw.height = 216;
+		cam->params.format.videoSize=VIDEOSIZE_CIF;
+		cam->params.roi.colStart=4;
+		cam->params.roi.colEnd=40;
+		cam->params.roi.rowStart=9;
+		cam->params.roi.rowEnd=63;
+		cam->params.streamStartLine = 120;
+		break;
+	case VIDEOSIZE_256_192:
+		cam->vw.width = 256;
+		cam->vw.height = 192;
+		cam->params.format.videoSize=VIDEOSIZE_CIF;
+		cam->params.roi.colStart=6;
+		cam->params.roi.colEnd=38;
+		cam->params.roi.rowStart=12;
+		cam->params.roi.rowEnd=60;
+		cam->params.streamStartLine = 120;
+		break;
+	case VIDEOSIZE_224_168:
+		cam->vw.width = 224;
+		cam->vw.height = 168;
+		cam->params.format.videoSize=VIDEOSIZE_CIF;
+		cam->params.roi.colStart=8;
+		cam->params.roi.colEnd=36;
+		cam->params.roi.rowStart=15;
+		cam->params.roi.rowEnd=57;
+		cam->params.streamStartLine = 120;
+		break;
+	case VIDEOSIZE_192_144:
+		cam->vw.width = 192;
+		cam->vw.height = 144;
+		cam->params.format.videoSize=VIDEOSIZE_CIF;
+		cam->params.roi.colStart=10;
+		cam->params.roi.colEnd=34;
+		cam->params.roi.rowStart=18;
+		cam->params.roi.rowEnd=54;
+		cam->params.streamStartLine = 120;
+		break;
+	case VIDEOSIZE_QCIF:
+		cam->vw.width = 176;
+		cam->vw.height = 144;
+		cam->params.format.videoSize=VIDEOSIZE_QCIF;
+		cam->params.roi.colStart=0;
+		cam->params.roi.colEnd=22;
+		cam->params.roi.rowStart=0;
+		cam->params.roi.rowEnd=36;
+		cam->params.streamStartLine = 60;
+		break;
+	case VIDEOSIZE_QSIF:
+		cam->vw.width = 160;
+		cam->vw.height = 120;
+		cam->params.format.videoSize=VIDEOSIZE_QCIF;
+		cam->params.roi.colStart=1;
+		cam->params.roi.colEnd=21;
+		cam->params.roi.rowStart=3;
+		cam->params.roi.rowEnd=33;
+		cam->params.streamStartLine = 60;
+		break;
+	case VIDEOSIZE_128_96:
+		cam->vw.width = 128;
+		cam->vw.height = 96;
+		cam->params.format.videoSize=VIDEOSIZE_QCIF;
+		cam->params.roi.colStart=3;
+		cam->params.roi.colEnd=19;
+		cam->params.roi.rowStart=6;
+		cam->params.roi.rowEnd=30;
+		cam->params.streamStartLine = 60;
+		break;
+	case VIDEOSIZE_64_48:
+		cam->vw.width = 64;
+		cam->vw.height = 48;
+		cam->params.format.videoSize=VIDEOSIZE_QCIF;
+		cam->params.roi.colStart=7;
+		cam->params.roi.colEnd=15;
+		cam->params.roi.rowStart=12;
+		cam->params.roi.rowEnd=24;
+		cam->params.streamStartLine = 60;
+		break;
+	case VIDEOSIZE_48_48:
+		cam->vw.width = 48;
+		cam->vw.height = 48;
+		cam->params.format.videoSize=VIDEOSIZE_QCIF;
+		cam->params.roi.colStart=8;
+		cam->params.roi.colEnd=14;
+		cam->params.roi.rowStart=6;
+		cam->params.roi.rowEnd=30;
+		cam->params.streamStartLine = 60;
+		break;
+	default:
+		LOG("bad videosize value: %d\n", cam->video_size);
+	}
+
+	return;
+}
+
+static int allocate_frame_buf(struct cam_data *cam) {
+	int i;
+
+	cam->frame_buf = rvmalloc(FRAME_NUM*CPIA_MAX_FRAME_SIZE);
+	if (!cam->frame_buf) {
+		return -ENOBUFS;
+	}
+	for( i=0; i<FRAME_NUM; i++ ) {
+		cam->frame[i].data = cam->frame_buf + i*CPIA_MAX_FRAME_SIZE;
+	}
+	return 0;
+}
+
+static int free_frame_buf(struct cam_data *cam) {
+	int i;
+	
+	rvfree(cam->frame_buf, FRAME_NUM*CPIA_MAX_FRAME_SIZE);
+	cam->frame_buf=0;
+	for( i=0; i<FRAME_NUM; i++ ) {
+		cam->frame[i].data = NULL;
+	}
+	return 0;
+}
+
+
+static void inline free_frames(struct cpia_frame frame[FRAME_NUM])
+{
+	int i;
+	for( i=0; i<FRAME_NUM; i++ ) {
+		frame[i].state=FRAME_UNUSED;
+	}
+	return;
+}
+
+/**********************************************************************
+ *
+ * General functions
+ *
+ **********************************************************************/
+/* send an arbitrary command to the camera */
+static int do_command(struct cam_data *cam, u16 command, u8 a, u8 b, u8 c, u8 d)
+{
+	int retval, datasize;
+	u8 cmd[8], data[8];
+
+	switch(command) {
+	case CPIA_COMMAND_GetCPIAVersion:
+	case CPIA_COMMAND_GetPnPID:
+	case CPIA_COMMAND_GetCameraStatus:
+	case CPIA_COMMAND_GetVPVersion:
+		datasize=8;
+		break;
+	case CPIA_COMMAND_GetColourParams:
+	case CPIA_COMMAND_GetColourBalance:
+	case CPIA_COMMAND_GetExposure:
+		down(&cam->param_lock);
+		datasize=8;
+		break;
+	default:
+		datasize=0;
+		break;
+	}
+
+	cmd[0] = command>>8;
+	cmd[1] = command&0xff;
+	cmd[2] = a;
+	cmd[3] = b;
+	cmd[4] = c;
+	cmd[5] = d;
+	cmd[6] = datasize;
+	cmd[7] = 0;
+
+	retval = cam->ops->transferCmd(cam->lowlevel_data, cmd, data);
+	if(retval) {
+		LOG("%x - failed\n", command);
+	} else {
+		switch(command) {
+		case CPIA_COMMAND_GetCPIAVersion:
+			cam->params.version.firmwareVersion = data[0];
+			cam->params.version.firmwareRevision = data[1];
+			cam->params.version.vcVersion = data[2];
+			cam->params.version.vcRevision = data[3];
+			break;
+		case CPIA_COMMAND_GetPnPID:
+			cam->params.pnpID.vendor = data[0]+(((u16)data[1])<<8);
+			cam->params.pnpID.product = data[2]+(((u16)data[3])<<8);
+			cam->params.pnpID.deviceRevision =
+				data[4]+(((u16)data[5])<<8);
+			break;
+		case CPIA_COMMAND_GetCameraStatus:
+			cam->params.status.systemState = data[0];
+			cam->params.status.grabState = data[1];
+			cam->params.status.streamState = data[2];
+			cam->params.status.fatalError = data[3];
+			cam->params.status.cmdError = data[4];
+			cam->params.status.debugFlags = data[5];
+			cam->params.status.vpStatus = data[6];
+			cam->params.status.errorCode = data[7];
+			break;
+		case CPIA_COMMAND_GetVPVersion:
+			cam->params.vpVersion.vpVersion = data[0];
+			cam->params.vpVersion.vpRevision = data[1];
+			cam->params.vpVersion.cameraHeadID =
+				data[2]+(((u16)data[3])<<8);
+			break;
+		case CPIA_COMMAND_GetColourParams:
+			cam->params.colourParams.brightness = data[0];
+			cam->params.colourParams.contrast = data[1];
+			cam->params.colourParams.saturation = data[2];
+			up(&cam->param_lock);
+			break;
+		case CPIA_COMMAND_GetColourBalance:
+			cam->params.colourBalance.redGain = data[0];
+			cam->params.colourBalance.greenGain = data[1];
+			cam->params.colourBalance.blueGain = data[2];
+			up(&cam->param_lock);
+			break;
+		case CPIA_COMMAND_GetExposure:
+			cam->params.exposure.gain = data[0];
+			cam->params.exposure.fineExp = data[1];
+			cam->params.exposure.coarseExpLo = data[2];
+			cam->params.exposure.coarseExpHi = data[3];
+			cam->params.exposure.redComp = data[4];
+			cam->params.exposure.green1Comp = data[5];
+			cam->params.exposure.green2Comp = data[6];
+			cam->params.exposure.blueComp = data[7];
+			up(&cam->param_lock);
+			break;
+		default:
+			break;
+		}
+	}
+	return retval;
+}
+
+/* send a command  to the camera with an additional data transaction */
+static int do_command_extended(struct cam_data *cam, u16 command,
+                               u8 a, u8 b, u8 c, u8 d,
+                               u8 e, u8 f, u8 g, u8 h,
+                               u8 i, u8 j, u8 k, u8 l)
+{
+	int retval;
+	u8 cmd[8], data[8];
+	cmd[0] = command>>8;
+	cmd[1] = command&0xff;
+	cmd[2] = a;
+	cmd[3] = b;
+	cmd[4] = c;
+	cmd[5] = d;
+	cmd[6] = 8;
+	cmd[7] = 0;
+	data[0] = e;
+	data[1] = f;
+	data[2] = g;
+	data[3] = h;
+	data[4] = i;
+	data[5] = j;
+	data[6] = k;
+	data[7] = l;
+
+	retval = cam->ops->transferCmd(cam->lowlevel_data, cmd, data);
+	if(retval) {
+		LOG("%x - failed\n", command);
+	}
+	return retval;
+}
+
+/**********************************************************************
+ *
+ * Colorspace conversion
+ *
+ **********************************************************************/
+#define LIMIT(x) ((((x)>0xffffff)?0xff0000:(((x)<=0xffff)?0:(x)&0xff0000))>>16)
+
+static int yuvconvert(unsigned char *yuv, unsigned char *rgb, int out_fmt,
+                      int in_uyvy)
+{
+	int y, u, v, r, g, b, y1;
+	switch(out_fmt) {
+	case VIDEO_PALETTE_RGB555:
+	case VIDEO_PALETTE_RGB565:
+	case VIDEO_PALETTE_RGB24:
+	case VIDEO_PALETTE_RGB32:
+		if(in_uyvy) {
+			u = *yuv++ - 128;
+			y = (*yuv++ - 16) * 76310;
+			v = *yuv++ - 128;
+			y1 = (*yuv - 16) * 76310;
+		} else {
+			y = (*yuv++ - 16) * 76310;
+			u = *yuv++ - 128;
+			y1 = (*yuv++ - 16) * 76310;
+			v = *yuv - 128;
+		}
+		r = 104635 * v;
+		g = -25690 * u + -53294 * v;
+		b = 132278 * u;
+		break;
+	default:
+		y = *yuv++;
+		u = *yuv++;
+		y1 = *yuv++;
+		v = *yuv;
+		/* Just to avoid compiler warnings */
+		r = 0;
+		g = 0;
+		b = 0;
+		break;
+	}
+	switch(out_fmt) {
+	case VIDEO_PALETTE_RGB555:
+		*rgb++ = ((LIMIT(g+y) & 0xf8) << 2) | (LIMIT(b+y) >> 3);
+		*rgb++ = ((LIMIT(r+y) & 0xf8) >> 1) | (LIMIT(g+y) >> 6);
+		*rgb++ = ((LIMIT(g+y1) & 0xf8) << 2) | (LIMIT(b+y1) >> 3);
+		*rgb = ((LIMIT(r+y1) & 0xf8) >> 1) | (LIMIT(g+y1) >> 6);
+		return 4;
+	case VIDEO_PALETTE_RGB565:
+		*rgb++ = ((LIMIT(g+y) & 0xfc) << 3) | (LIMIT(b+y) >> 3);
+		*rgb++ = (LIMIT(r+y) & 0xf8) | (LIMIT(g+y) >> 5);
+		*rgb++ = ((LIMIT(g+y1) & 0xfc) << 3) | (LIMIT(b+y1) >> 3);
+		*rgb = (LIMIT(r+y1) & 0xf8) | (LIMIT(g+y1) >> 5);
+		return 4;
+	case VIDEO_PALETTE_RGB24:
+		*rgb++ = LIMIT(b+y);
+		*rgb++ = LIMIT(g+y);
+		*rgb++ = LIMIT(r+y);
+		*rgb++ = LIMIT(b+y1);
+		*rgb++ = LIMIT(g+y1);
+		*rgb = LIMIT(r+y1);
+		return 6;
+	case VIDEO_PALETTE_RGB32:
+		*rgb++ = LIMIT(b+y);
+		*rgb++ = LIMIT(g+y);
+		*rgb++ = LIMIT(r+y);
+		rgb++;
+		*rgb++ = LIMIT(b+y1);
+		*rgb++ = LIMIT(g+y1);
+		*rgb = LIMIT(r+y1);
+		return 8;
+	case VIDEO_PALETTE_GREY:
+		*rgb++ = y;
+		*rgb = y1;
+		return 2;
+	case VIDEO_PALETTE_YUV422:
+	case VIDEO_PALETTE_YUYV:
+		*rgb++ = y;
+		*rgb++ = u;
+		*rgb++ = y1;
+		*rgb = v;
+		return 4;
+	case VIDEO_PALETTE_UYVY:
+		*rgb++ = u;
+		*rgb++ = y;
+		*rgb++ = v;
+		*rgb = y1;
+		return 4;
+	default:
+		return 0;
+	}
+}
+
+static int skipcount(int count, int fmt) {
+	switch(fmt) {
+	case VIDEO_PALETTE_GREY:
+	case VIDEO_PALETTE_RGB555:
+	case VIDEO_PALETTE_RGB565:
+	case VIDEO_PALETTE_YUV422:
+	case VIDEO_PALETTE_YUYV:
+	case VIDEO_PALETTE_UYVY:
+		return 2*count;
+	case VIDEO_PALETTE_RGB24:
+		return 3*count;
+	case VIDEO_PALETTE_RGB32:
+		return 4*count;
+	default:
+		return 0;
+	}
+}
+
+static int parse_picture(struct cam_data *cam, int size)
+{
+	u8 *obuf, *ibuf, *end_obuf;
+	int ll, in_uyvy, compressed, origsize, out_fmt;
+
+	/* make sure params don't change while we are decoding */
+	down(&cam->param_lock);
+	
+	obuf = cam->decompressed_frame.data;
+	end_obuf = obuf+CPIA_MAX_FRAME_SIZE;
+	ibuf = cam->raw_image;
+	origsize = size;
+	out_fmt = cam->vp.palette;
+	
+	if((ibuf[0] != MAGIC_0) || (ibuf[1] != MAGIC_1)) {
+		LOG("header not found\n");
+		up(&cam->param_lock);
+		return -1;
+	}
+
+	if((ibuf[16] != VIDEOSIZE_QCIF) && (ibuf[16] != VIDEOSIZE_CIF)) {
+		LOG("wrong video size\n");
+		up(&cam->param_lock);
+		return -1;
+	}
+	
+	if(ibuf[17] != SUBSAMPLE_422) {
+		LOG("illegal subtype %d\n",ibuf[17]);
+		up(&cam->param_lock);
+		return -1;
+	}
+	
+	if(ibuf[18] != YUVORDER_YUYV && ibuf[18] != YUVORDER_UYVY) {
+		LOG("illegal yuvorder %d\n",ibuf[18]);
+		up(&cam->param_lock);
+		return -1;
+	}
+	in_uyvy = ibuf[18] == YUVORDER_UYVY;
+	
+#if 0
+	/* FIXME: ROI mismatch occurs when switching capture sizes */
+	if((ibuf[24] != cam->params.roi.colStart) ||
+	   (ibuf[25] != cam->params.roi.colEnd) ||
+	   (ibuf[26] != cam->params.roi.rowStart) ||
+	   (ibuf[27] != cam->params.roi.rowEnd)) {
+		LOG("ROI mismatch\n");
+		up(&cam->param_lock);
+		return -1;
+	}
+#endif
+	
+	if((ibuf[28] != NOT_COMPRESSED) && (ibuf[28] != COMPRESSED)) {
+		LOG("illegal compression %d\n",ibuf[28]);
+		up(&cam->param_lock);
+		return -1;
+	}
+	compressed = (ibuf[28] == COMPRESSED);
+	
+	if(ibuf[29] != NO_DECIMATION) {
+		LOG("decimation not supported\n");
+		up(&cam->param_lock);
+		return -1;
+	}
+	
+	cam->params.yuvThreshold.yThreshold = ibuf[30];
+	cam->params.yuvThreshold.uvThreshold = ibuf[31];
+	cam->params.status.systemState = ibuf[32];
+	cam->params.status.grabState = ibuf[33];
+	cam->params.status.streamState = ibuf[34];
+	cam->params.status.fatalError = ibuf[35];
+	cam->params.status.cmdError = ibuf[36];
+	cam->params.status.debugFlags = ibuf[37];
+	cam->params.status.vpStatus = ibuf[38];
+	cam->params.status.errorCode = ibuf[39];
+	cam->fps = ibuf[41];
+	
+	ibuf += 64;
+	size -= 64;
+	ll = ibuf[0] | (ibuf[1] << 8);
+	ibuf += 2;
+	while(size > 0) {
+		size -= (ll+2);
+		if(size < 0) {
+			LOG("Insufficient data in buffer\n");
+			up(&cam->param_lock);
+			return -1;
+		}
+		while(ll > 1) {
+			if(!compressed || (compressed && !(*ibuf & 1))) {
+				obuf += yuvconvert(ibuf,obuf,out_fmt,in_uyvy);
+				ibuf += 4;
+				ll -= 4;
+			} else {
+				/*skip compressed interval from previous frame*/
+				int skipsize = skipcount(*ibuf >> 1, out_fmt);
+				obuf += skipsize;
+				if( obuf > end_obuf ) {
+					LOG("Insufficient data in buffer\n");
+					up(&cam->param_lock);
+					return -1;
+				}
+				++ibuf;
+				ll--;
+			}
+		}
+		if(ll == 1) {
+			if(*ibuf != EOL) {
+				LOG("EOL not found giving up after %d/%d"
+				    " bytes\n", origsize-size, origsize);
+				up(&cam->param_lock);
+				return -1;
+			}
+
+			ibuf++; /* skip over EOL */
+
+			if((size > 3) && (ibuf[0] == EOI) && (ibuf[1] == EOI) &&
+			   (ibuf[2] == EOI) && (ibuf[3] == EOI)) {
+			 	size -= 4;
+				break;
+			}
+
+			if(size > 1) {
+				ll = ibuf[0] | (ibuf[1] << 8);
+				ibuf += 2; /* skip over line length */
+			}
+		} else {
+			LOG("line length was not 1 but %d after %d/%d bytes\n",
+			    ll, origsize-size, origsize);
+			up(&cam->param_lock);
+			return -1;
+		}
+	}
+	
+	cam->decompressed_frame.count = obuf-cam->decompressed_frame.data;
+
+	up(&cam->param_lock);
+	
+	return cam->decompressed_frame.count;
+}
+
+/* InitStreamCap wrapper to select correct start line */
+static inline int init_stream_cap(struct cam_data *cam) {
+	return do_command(cam, CPIA_COMMAND_InitStreamCap,
+	                  0, cam->params.streamStartLine, 0, 0);
+}
+
+/* update various camera modes and settings */
+static void dispatch_commands(struct cam_data *cam ) {
+	down(&cam->param_lock);
+	if( cam->cmd_queue==COMMAND_NONE ) {
+		up(&cam->param_lock);
+		return;
+	}
+	DEB_BYTE(cam->cmd_queue);
+	DEB_BYTE(cam->cmd_queue>>8);
+	if( cam->cmd_queue & COMMAND_SETCOLOURPARAMS ) {
+		do_command(cam, CPIA_COMMAND_SetColourParams,
+		           cam->params.colourParams.brightness,
+		           cam->params.colourParams.contrast,
+		           cam->params.colourParams.saturation, 0);
+	}
+	if( cam->cmd_queue & COMMAND_SETCOMPRESSION ) {
+		do_command(cam, CPIA_COMMAND_SetCompression,
+		           cam->params.compression.mode,
+			   cam->params.compression.decimation, 0, 0);
+	}
+	if( cam->cmd_queue & COMMAND_SETFORMAT ) {
+		do_command(cam, CPIA_COMMAND_SetFormat,
+	        	   cam->params.format.videoSize,
+	        	   cam->params.format.subSample,
+	        	   cam->params.format.yuvOrder, 0);
+		do_command(cam, CPIA_COMMAND_SetROI,
+	        	   cam->params.roi.colStart, cam->params.roi.colEnd,
+	        	   cam->params.roi.rowStart, cam->params.roi.rowEnd);
+		cam->first_frame = 1;
+	}
+	if( cam->cmd_queue & COMMAND_SETCOMPRESSIONTARGET ) {
+		do_command(cam, CPIA_COMMAND_SetCompressionTarget,
+		           cam->params.compressionTarget.frTargeting,
+		           cam->params.compressionTarget.targetFR,
+		           cam->params.compressionTarget.targetQ, 0);
+	}
+	if( cam->cmd_queue & COMMAND_SETYUVTHRESH ) {
+		do_command(cam, CPIA_COMMAND_SetYUVThresh,
+		           cam->params.yuvThreshold.yThreshold,
+		           cam->params.yuvThreshold.uvThreshold, 0, 0);
+	}
+	if( cam->cmd_queue & COMMAND_SETECPTIMING ) {
+		do_command(cam, CPIA_COMMAND_SetECPTiming,
+		           cam->params.ecpTiming, 0, 0, 0);
+	}
+	if( cam->cmd_queue & COMMAND_SETCOMPRESSIONPARAMS ) {
+		do_command_extended(cam, CPIA_COMMAND_SetCompressionParams,
+		            0, 0, 0, 0,
+		            cam->params.compressionParams.hysteresis,
+		            cam->params.compressionParams.threshMax,
+		            cam->params.compressionParams.smallStep,
+		            cam->params.compressionParams.largeStep,
+		            cam->params.compressionParams.decimationHysteresis,
+		            cam->params.compressionParams.frDiffStepThresh,
+		            cam->params.compressionParams.qDiffStepThresh,
+		            cam->params.compressionParams.decimationThreshMod);
+	}
+	if( cam->cmd_queue & COMMAND_SETEXPOSURE ) {
+		do_command_extended(cam, CPIA_COMMAND_SetExposure,
+		                    cam->params.exposure.gainMode,
+		                    cam->params.exposure.expMode,
+		                    cam->params.exposure.compMode,
+		                    cam->params.exposure.centreWeight,
+		                    cam->params.exposure.gain,
+		                    cam->params.exposure.fineExp,
+		                    cam->params.exposure.coarseExpLo,
+		                    cam->params.exposure.coarseExpHi,
+		                    cam->params.exposure.redComp,
+		                    cam->params.exposure.green1Comp,
+		                    cam->params.exposure.green2Comp,
+		                    cam->params.exposure.blueComp);
+	}
+	if( cam->cmd_queue & COMMAND_SETCOLOURBALANCE ) {
+		do_command(cam, CPIA_COMMAND_SetColourBalance,
+		           cam->params.colourBalance.balanceMode,
+		           cam->params.colourBalance.redGain,
+		           cam->params.colourBalance.greenGain,
+		           cam->params.colourBalance.blueGain);
+	}
+	if( cam->cmd_queue & COMMAND_SETSENSORFPS ) {
+		do_command(cam, CPIA_COMMAND_SetSensorFPS,
+		           cam->params.sensorFps.divisor,
+		           cam->params.sensorFps.baserate, 0, 0);
+	}
+	if( cam->cmd_queue & COMMAND_SETAPCOR ) {
+		do_command(cam, CPIA_COMMAND_SetApcor,
+		           cam->params.apcor.gain1,
+		           cam->params.apcor.gain2,
+		           cam->params.apcor.gain4,
+		           cam->params.apcor.gain8);
+	}
+	if( cam->cmd_queue & COMMAND_SETFLICKERCTRL ) {
+		do_command(cam, CPIA_COMMAND_SetFlickerCtrl,
+		           cam->params.flickerControl.flickerMode,
+		           cam->params.flickerControl.coarseJump,
+		           cam->params.flickerControl.allowableOverExposure, 0);
+	}
+	if( cam->cmd_queue & COMMAND_SETVLOFFSET ) {
+		do_command(cam, CPIA_COMMAND_SetVLOffset,
+		           cam->params.vlOffset.gain1,
+		           cam->params.vlOffset.gain2,
+		           cam->params.vlOffset.gain4,
+		           cam->params.vlOffset.gain8);
+	}
+	if( cam->cmd_queue & COMMAND_PAUSE ) {
+		do_command(cam, CPIA_COMMAND_EndStreamCap, 0, 0, 0, 0);
+	}
+	if( cam->cmd_queue & COMMAND_RESUME ) {
+		init_stream_cap( cam );
+	}
+	up(&cam->param_lock);
+	cam->cmd_queue=COMMAND_NONE;
+	return;
+}
+
+/* kernel thread function to read image from camera */
+static void fetch_frame(void *data)
+{
+	int image_size;
+	struct cam_data *cam = (struct cam_data *)data;
+	unsigned long oldjif, rate, diff;
+
+        /* load first frame always uncompressed */
+        if( cam->first_frame &&
+            cam->params.compression.mode != CPIA_COMPRESSION_NONE ) {
+                do_command(cam, CPIA_COMMAND_SetCompression,
+                           CPIA_COMPRESSION_NONE,
+                           NO_DECIMATION, 0, 0);
+        }
+
+        /* init camera upload */
+        if(do_command(cam, CPIA_COMMAND_SetGrabMode, CPIA_GRAB_CONTINUOUS,
+                      0, 0, 0)) goto end;
+        if(do_command(cam, CPIA_COMMAND_GrabFrame, 0,
+                      cam->params.streamStartLine, 0, 0)) goto end;
+
+	/* loop until image ready */
+	do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0);
+	while(cam->params.status.streamState != STREAM_READY) {
+		if(current->need_resched) {schedule();}
+		current->state=TASK_INTERRUPTIBLE;
+		schedule_timeout(10*HZ/1000); /* 10 ms, hopefully ;) */
+		if(signal_pending(current)) {
+			goto end;
+		}
+		do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0);
+	}
+
+	/* grab image from camera */
+	if(current->need_resched) {schedule();}
+	oldjif = jiffies;
+	image_size=cam->ops->streamRead(cam->lowlevel_data, cam->raw_image, 0);
+	if(image_size<=0) {
+		DBG("read frame failed\n");
+		goto end;
+	}
+	rate = image_size * HZ / 1024;
+	diff = jiffies-oldjif;
+	rate = diff==0 ? rate : rate/diff; /* unlikely but possible */
+	/* Keep track of transfer_rate as a runnung average over 3 frames
+	 * to smooth out any inconsistencies */
+	cam->transfer_rate = (2*cam->transfer_rate + rate) / 3;
+
+	/* camera idle now so dispatch queued commands */
+	dispatch_commands( cam );
+
+	/* Update our knowledge of the camera state - FIXME: necessary? */
+	do_command(cam, CPIA_COMMAND_GetColourBalance, 0, 0, 0, 0);
+	do_command(cam, CPIA_COMMAND_GetExposure, 0, 0, 0, 0);
+	
+	/* decompress and convert image to by copying it from
+	*  raw_image to decompressed_frame
+	*/
+	if(current->need_resched) {schedule();}
+	cam->image_size = parse_picture(cam, image_size);
+	if( cam->image_size <= 0 ) {
+		DBG("parse frame failed\n");
+		goto end;
+	}
+
+	/* FIXME: this only works for double buffering */
+	if( cam->frame[cam->curframe].state == FRAME_READY ) {
+		memcpy(cam->frame[cam->curframe].data,
+		       cam->decompressed_frame.data,
+		       cam->decompressed_frame.count);
+		cam->frame[cam->curframe].state = FRAME_DONE;
+	} else {
+		cam->decompressed_frame.state = FRAME_DONE;
+	}
+
+#if 0
+	if( cam->first_frame &&
+	    cam->params.compression.mode != CPIA_COMPRESSION_NONE ) {
+		cam->first_frame = 0;
+		cam->cmd_queue |= COMMAND_SETCOMPRESSION;
+	}
+#else
+	if( cam->first_frame ) {
+		cam->first_frame = 0;
+		cam->cmd_queue |= COMMAND_SETCOMPRESSION;
+		cam->cmd_queue |= COMMAND_SETEXPOSURE;
+	}
+#endif
+end:
+	return;
+}
+
+static int capture_frame(struct cam_data *cam, struct video_mmap *vm)
+{
+	int retval = 0;
+
+	if( cam->frame_buf == NULL ) { /* we do lazy allocation */
+		if( (retval = allocate_frame_buf(cam)) ) {
+			return retval;
+		}
+	}
+	
+	/* FIXME: the first frame seems to be captured by the camera
+	   without regards to any initial settings, so we throw away
+	   that one, the next one is generated with our settings
+	   (exposure, color balance, ...)
+	*/
+	if(cam->first_frame) {
+	  cam->curframe = vm->frame;
+	  cam->frame[cam->curframe].state = FRAME_READY;
+	  fetch_frame(cam);
+	  if(cam->frame[cam->curframe].state != FRAME_DONE) retval=-EIO;
+	}
+	cam->curframe = vm->frame;
+        cam->frame[cam->curframe].state = FRAME_READY;
+        fetch_frame(cam);
+        if(cam->frame[cam->curframe].state != FRAME_DONE) retval=-EIO;
+
+	return retval;
+}
+  
+static int goto_high_power(struct cam_data *cam)
+{
+	if(do_command(cam, CPIA_COMMAND_GotoHiPower, 0, 0, 0, 0)) return -1;
+	if(do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0)) return -1;
+	if(cam->params.status.systemState == HI_POWER_STATE) {
+		DBG("camera now in HIGH power state\n");
+		return 0;
+	}
+	printstatus(cam);
+	return -1;
+}
+
+static int goto_low_power(struct cam_data *cam)
+{
+	if(do_command(cam, CPIA_COMMAND_GotoLoPower, 0, 0, 0, 0)) return -1;
+	if(do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0)) return -1;
+	if(cam->params.status.systemState == LO_POWER_STATE) {
+		DBG("camera now in LOW power state\n");
+		return 0;
+	}
+	printstatus(cam);
+	return -1;
+}
+
+static void save_camera_state(struct cam_data *cam)
+{
+	do_command(cam, CPIA_COMMAND_GetColourBalance, 0, 0, 0, 0);
+	do_command(cam, CPIA_COMMAND_GetExposure, 0, 0, 0, 0);
+
+	DBG("%d/%d/%d/%d/%d/%d/%d/%d\n",
+	     cam->params.exposure.gain,
+	     cam->params.exposure.fineExp,
+	     cam->params.exposure.coarseExpLo,
+	     cam->params.exposure.coarseExpHi,
+	     cam->params.exposure.redComp,
+	     cam->params.exposure.green1Comp,
+	     cam->params.exposure.green2Comp,
+	     cam->params.exposure.blueComp);
+	DBG("%d/%d/%d\n",
+	     cam->params.colourBalance.redGain,
+	     cam->params.colourBalance.greenGain,
+	     cam->params.colourBalance.blueGain);
+
+	return;
+}
+
+static void set_camera_state(struct cam_data *cam)
+{
+	if(cam->params.colourBalance.balanceMode != 1) {
+		do_command(cam, CPIA_COMMAND_SetColourBalance, 
+	        	   1,
+	        	   cam->params.colourBalance.redGain,
+	        	   cam->params.colourBalance.greenGain,
+	        	   cam->params.colourBalance.blueGain);
+	}
+	if(cam->params.colourBalance.balanceMode == 0)
+		cam->params.colourBalance.balanceMode = 2;
+
+	do_command_extended(cam, CPIA_COMMAND_SetExposure,
+			    cam->params.exposure.gainMode, 1, 1,
+			    cam->params.exposure.centreWeight,
+	                    cam->params.exposure.gain,
+	                    cam->params.exposure.fineExp,
+	                    cam->params.exposure.coarseExpLo,
+	                    cam->params.exposure.coarseExpHi,
+	                    cam->params.exposure.redComp,
+	                    cam->params.exposure.green1Comp,
+	                    cam->params.exposure.green2Comp,
+	                    cam->params.exposure.blueComp);
+	do_command_extended(cam, CPIA_COMMAND_SetExposure,
+			    0, 3, 0, 0,
+			    0, 0, 0, 0, 0, 0, 0, 0);
+
+	if(cam->params.exposure.gainMode == 0)
+		cam->params.exposure.gainMode = 2;
+	if(cam->params.exposure.expMode == 0)
+		cam->params.exposure.expMode = 2;
+	if(cam->params.exposure.centreWeight == 0)
+		cam->params.exposure.centreWeight = 1;
+	
+	cam->cmd_queue = COMMAND_SETCOMPRESSION |
+	                 COMMAND_SETCOMPRESSIONTARGET |
+	                 COMMAND_SETCOLOURPARAMS |
+	                 COMMAND_SETFORMAT |
+	                 COMMAND_SETYUVTHRESH |
+	                 COMMAND_SETECPTIMING |
+	                 COMMAND_SETCOMPRESSIONPARAMS |
+#if 0
+	                 COMMAND_SETEXPOSURE |
+#endif
+	                 COMMAND_SETCOLOURBALANCE |
+	                 COMMAND_SETSENSORFPS |
+	                 COMMAND_SETAPCOR |
+	                 COMMAND_SETFLICKERCTRL |
+	                 COMMAND_SETVLOFFSET;
+	dispatch_commands(cam);
+	save_camera_state(cam);
+
+	return;
+}
+
+static void get_version_information(struct cam_data *cam)
+{
+	/* GetCPIAVersion */
+	do_command(cam, CPIA_COMMAND_GetCPIAVersion, 0, 0, 0, 0);
+	printk(KERN_INFO "  CPIA Version: %d.%02d (%d.%d)\n",
+	       cam->params.version.firmwareVersion,
+	       cam->params.version.firmwareRevision,
+	       cam->params.version.vcVersion,
+	       cam->params.version.vcRevision);
+
+	/* GetPnPID */
+	do_command(cam, CPIA_COMMAND_GetPnPID, 0, 0, 0, 0);
+	printk(KERN_INFO "  CPIA PnP-ID: %04x:%04x:%04x\n",
+	       cam->params.pnpID.vendor, cam->params.pnpID.product,
+	       cam->params.pnpID.deviceRevision);
+}
+
+/* initialize camera */
+static int reset_camera(struct cam_data *cam)
+{
+	/* Start the camera in low power mode */
+	if(goto_low_power(cam)) {
+		if( cam->params.status.systemState != 0x4 ) {
+			return -ENODEV;
+		}
+		/* FIXME: this is just dirty trial and error */
+		reset_camera_struct(cam);
+		goto_high_power(cam);
+		do_command(cam, CPIA_COMMAND_DiscardFrame, 0, 0, 0, 0);
+		if(goto_low_power(cam)) return -NODEV;
+	}
+	
+	/* procedure described in developer's guide p3-28 */
+	
+	/* Check the firmware version FIXME: should we check PNPID? */
+	cam->params.version.firmwareVersion = 0;
+	get_version_information(cam);
+	if(cam->params.version.firmwareVersion != 1) {
+		return -ENODEV;
+	}
+
+	/* The fatal error checking should be done after the camera powers up (developer's guide p 3-38) */
+
+	/* Set streamState before transition to high power to avoid bug
+	 * in firmware 1-02 */
+	do_command(cam, CPIA_COMMAND_ModifyCameraStatus, STREAMSTATE, 0,
+	           STREAM_NOT_READY, 0);
+	
+	/* GotoHiPower */
+	if(goto_high_power(cam))
+		return -ENODEV;
+
+	
+	/* Check the camera status */
+	if(do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0))
+		return -EIO;
+	if(cam->params.status.fatalError) {
+		DBG("fatal_error:              %#04x\n",cam->params.status.fatalError);
+		DBG("vp_status:                %#04x\n",cam->params.status.vpStatus);
+		if(cam->params.status.fatalError & ~(COM_FLAG|CPIA_FLAG)) {
+			/* Fatal error in camera */
+			return -EIO;
+		} else if(cam->params.status.fatalError & (COM_FLAG|CPIA_FLAG)){
+			/* Firmware 1-02 may do this for parallel port cameras,
+			 * just clear the flags (developer's guide p 3-38) */
+			do_command(cam, CPIA_COMMAND_ModifyCameraStatus,
+			           FATALERROR, ~(COM_FLAG|CPIA_FLAG), 0, 0);
+		}
+	}
+		
+	/* Check the camera status again */
+	if(cam->params.status.fatalError) {
+		if(cam->params.status.fatalError)
+			return -EIO;
+	}
+	
+	/* VPVersion can't be retrieved before the camera is in HiPower,
+	 * so get it here instead of in get_version_information. */
+	do_command(cam, CPIA_COMMAND_GetVPVersion, 0, 0, 0, 0);
+	printk(KERN_INFO "  VP-Version: %d.%d %04x\n",
+	       cam->params.vpVersion.vpVersion,
+	       cam->params.vpVersion.vpRevision,
+	       cam->params.vpVersion.cameraHeadID);
+
+	/* set camera to a known state */
+	set_camera_state(cam);
+	
+	return 0;
+}
+
+/* ------------------------- V4L interface --------------------- */
+static int cpia_open(struct video_device *dev, int flags)
+{
+	int i;
+	struct cam_data *cam = dev->priv;
+
+	DBG("cpia_open\n");
+
+	if(cam->open_count > 0) {
+		DBG("Camera[%d] already open\n",cam->index);
+		return -EBUSY;
+	}
+	
+	if(cam->raw_image == NULL) {
+		if((cam->raw_image=rvmalloc(CPIA_MAX_IMAGE_SIZE)) == NULL){
+			return -ENOMEM;
+		}
+	}
+	if(cam->decompressed_frame.data == NULL) {
+		cam->decompressed_frame.data=rvmalloc(CPIA_MAX_FRAME_SIZE);
+		if(cam->decompressed_frame.data == NULL){
+			rvfree(cam->raw_image, CPIA_MAX_IMAGE_SIZE);
+			cam->raw_image=NULL;
+			return -ENOMEM;
+		}
+	}
+	
+	/* open cpia */
+	if (cam->ops->open(cam->index, &cam->lowlevel_data)) {
+		rvfree(cam->decompressed_frame.data, CPIA_MAX_FRAME_SIZE);
+		cam->decompressed_frame.data=NULL;
+		rvfree(cam->raw_image, CPIA_MAX_IMAGE_SIZE);
+		cam->raw_image=NULL;
+		return -ENODEV;
+	}
+	
+	/* reset the camera */
+	if((i = reset_camera(cam)) != 0) {
+		cam->ops->close(cam->lowlevel_data);
+		rvfree(cam->decompressed_frame.data, CPIA_MAX_FRAME_SIZE);
+		cam->decompressed_frame.data=NULL;
+		rvfree(cam->raw_image, CPIA_MAX_IMAGE_SIZE);
+		cam->raw_image=NULL;
+		return i;
+	}
+	
+	/* Set ownership of /proc/cpia/videoX to current user */
+	cam->proc_entry->uid = current->uid;
+	
+	/* set mark for loading first frame uncompressed */
+	cam->first_frame = 1;
+	cam->busy_lock = MUTEX;
+        
+	++cam->open_count;
+#ifdef MODULE
+	MOD_INC_USE_COUNT;
+#endif
+	return 0;
+}
+
+/* FIXME */
+static void cpia_close(struct video_device *dev)
+{
+	struct cam_data *cam;
+
+	DBG("cpia_close\n");
+	cam = dev->priv;
+
+	if(cam->ops == NULL) {
+		if(--cam->open_count == 0) {
+			int i;
+			video_unregister_device(dev);
+			/* Need a lock when adding/removing cameras */
+			lock_kernel();
+			if(cam->raw_image) {
+				rvfree(cam->raw_image, CPIA_MAX_IMAGE_SIZE);
+				cam->raw_image=0;
+			}
+			if(cam->decompressed_frame.data) {
+				rvfree(cam->decompressed_frame.data,
+				       CPIA_MAX_FRAME_SIZE);
+				cam->decompressed_frame.data=0;
+			}
+			if(cam->frame_buf) {
+				free_frame_buf(cam);
+			}
+			for(i=0; i<CPIA_MAXCAMS; ++i) {
+				if(camera[i] == cam) break;
+			}
+			kfree(cam);
+			if(i != CPIA_MAXCAMS) { /* should always be true */
+				camera[i] = NULL;
+			}
+			unlock_kernel();
+		}
+#ifdef MODULE
+		MOD_DEC_USE_COUNT;
+#endif
+		return;
+	}
+	
+	/* Return ownership of /proc/cpia/videoX to root */
+	cam->proc_entry->uid = 0;
+	
+	/* save camera state for later open (developers guide ch 3.5.3) */
+	save_camera_state(cam);
+
+	/* GotoLoPower */
+	goto_low_power(cam);
+
+	/* cleanup internal state stuff */
+	free_frames(cam->frame);
+
+	/* Update the camera ststus */
+	do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0);
+
+	/* close cpia */
+	cam->ops->close(cam->lowlevel_data);
+
+	if(--cam->open_count == 0) {
+		/* clean up capture-buffers */
+		if(cam->raw_image) {
+			rvfree(cam->raw_image, CPIA_MAX_IMAGE_SIZE);
+			cam->raw_image = 0;
+		}
+		if(cam->decompressed_frame.data) {
+			rvfree(cam->decompressed_frame.data, CPIA_MAX_FRAME_SIZE);
+			cam->decompressed_frame.data = 0;
+		}
+		if(cam->frame_buf) {
+			free_frame_buf(cam);
+		}
+	}
+	
+
+#ifdef MODULE
+	MOD_DEC_USE_COUNT;
+#endif
+	return;
+}
+
+static long cpia_read(struct video_device *dev, char *buf,
+                     unsigned long count, int noblock)
+{
+	struct cam_data *cam = dev->priv;
+
+	/* make this _really_ smp and multithredi-safe */
+	if( down_interruptible(&cam->busy_lock) ) {
+		return -EINTR;
+	}
+
+	if (!buf) {
+		DBG("buf NULL\n");
+		up(&cam->busy_lock);
+		return -EINVAL;
+	}
+	if(count == 0) {
+		DBG("count 0\n");
+		up(&cam->busy_lock);
+		return 0;
+	}
+	if(!cam->ops) {
+		DBG("ops NULL\n");
+		up(&cam->busy_lock);
+		return -ENODEV;
+	}
+
+	/* upload frame */
+	cam->decompressed_frame.state = FRAME_READY;
+	fetch_frame(cam);
+	if( cam->decompressed_frame.state != FRAME_DONE ) {
+		DBG("upload failed %d/%d\n", cam->decompressed_frame.count,
+		    cam->decompressed_frame.state);
+		up(&cam->busy_lock);
+		return -EIO;
+	}
+	cam->decompressed_frame.state = FRAME_UNUSED;
+
+	/* copy data to user space */
+	if(cam->decompressed_frame.count > count) {
+		DBG("count wrong\n");
+		up(&cam->busy_lock);
+		return -EFAULT;
+	}
+	if(copy_to_user(buf, cam->decompressed_frame.data,
+	                cam->decompressed_frame.count)) {
+		DBG("copy_to_user failed\n");
+		up(&cam->busy_lock);
+		return -EFAULT;
+	}
+
+	up(&cam->busy_lock);
+	return cam->decompressed_frame.count;
+}
+
+static int cpia_ioctl(struct video_device *dev, unsigned int ioctlnr, void *arg)
+{
+	struct cam_data *cam = dev->priv;
+	int retval = 0;
+
+	/* make this _really_ smp-safe */
+	if( down_interruptible(&cam->busy_lock) ) {
+		return -EINTR;
+	}
+	//DBG("cpia_ioctl: %u\n", ioctlnr);
+
+	switch (ioctlnr) {
+
+	/* query capabilites */
+	case VIDIOCGCAP:
+	{
+		struct video_capability b;
+
+		DBG("VIDIOCGCAP\n");
+		strcpy(b.name, "CPiA Parport Camera");
+		b.type = VID_TYPE_CAPTURE;
+		b.channels = 1;
+		b.audios = 0;
+		b.maxwidth = 352;         /* VIDEOSIZE_CIF */
+		b.maxheight = 288;
+		b.minwidth = 48;         /* VIDEOSIZE_48_48 */
+		b.minheight = 48;
+
+		if (copy_to_user(arg, &b, sizeof(b)))
+			retval = -EFAULT;
+		break;
+	}
+
+	/* get/set video source - we are a camera and nothing else */
+	case VIDIOCGCHAN:
+	{
+		struct video_channel v;
+
+		DBG("VIDIOCGCHAN\n");
+		if (copy_from_user(&v, arg, sizeof(v))) {
+			retval = -EFAULT;
+			break;
+		}
+		if (v.channel != 0) {
+			retval = -EINVAL;
+			break;
+		}
+
+		v.channel = 0;
+		strcpy(v.name, "Camera");
+		v.tuners = 0;
+		v.flags = 0;
+		v.type = VIDEO_TYPE_CAMERA;
+		v.norm = 0;
+
+		if (copy_to_user(arg, &v, sizeof(v)))
+			retval = -EFAULT;
+		break;
+	}
+	
+	case VIDIOCSCHAN:
+	{
+		int v;
+
+		DBG("VIDIOCSCHAN\n");
+		if (copy_from_user(&v, arg, sizeof(v)))
+			retval = -EFAULT;
+
+		if (retval == 0 && v != 0)
+			retval = -EINVAL;
+
+		break;
+	}
+
+	/* image properties */
+	case VIDIOCGPICT:
+		DBG("VIDIOCGPICT\n");
+		if (copy_to_user(arg, &cam->vp, sizeof(struct video_picture)))
+			retval = -EFAULT;
+		break;
+	
+	case VIDIOCSPICT:
+	{
+		struct video_picture vp;
+
+		DBG("VIDIOCSPICT\n");
+
+		/* copy_from_user */
+		if (copy_from_user(&vp, arg, sizeof(vp))) {
+			retval = -EFAULT;
+			break;
+		}
+		/* check validity */
+		DBG("palette: %d\n", vp.palette);
+		DBG("depth: %d\n", vp.depth);
+		if ( !valid_mode(vp.palette, vp.depth) ) {
+			retval = -EINVAL;
+			break;
+		}
+		down(&cam->param_lock);
+		/* brightness, colour, contrast need no check 0-65535 */
+		memcpy( &cam->vp, &vp, sizeof(vp) );
+		/* update cam->params.colourParams */
+		cam->params.colourParams.brightness = vp.brightness*100/65535;
+		cam->params.colourParams.contrast = vp.contrast*100/65535;
+		cam->params.colourParams.saturation = vp.colour*100/65535;
+		/* contrast is in steps of 8, so round */
+		cam->params.colourParams.contrast =
+			((cam->params.colourParams.contrast + 3) / 8) * 8;
+		if(cam->params.version.firmwareVersion == 1 &&
+		   cam->params.version.firmwareRevision == 2 &&
+		   cam->params.colourParams.contrast > 80) {
+			/* 1-02 firmware limits contrast to 80 */
+			cam->params.colourParams.contrast = 80;
+		}
+		/* queue command to update camera */
+		cam->cmd_queue |= COMMAND_SETCOLOURPARAMS;
+		up(&cam->param_lock);
+		DBG("VIDIOCSPICT: %d / %d // %d / %d / %d / %d\n",
+		    vp.depth, vp.palette, vp.brightness, vp.hue, vp.colour,
+		    vp.contrast);
+		break;
+	}
+
+	/* get/set capture window */
+	case VIDIOCGWIN:
+		DBG("VIDIOCGWIN\n");
+
+		if (copy_to_user(arg, &cam->vw, sizeof(struct video_window)))
+			retval = -EFAULT;
+		break;
+	
+	case VIDIOCSWIN:
+	{
+		/* copy_from_user, check validity, copy to internal structure */
+		struct video_window vw;
+		DBG("VIDIOCSWIN\n");
+		if (copy_from_user(&vw, arg, sizeof(vw))) {
+			retval = -EFAULT;
+			break;
+		}
+
+		if (vw.clipcount != 0) {    /* clipping not supported */
+			retval = -EINVAL;
+			break;
+		}
+		if (vw.clips != NULL) {     /* clipping not supported */
+			retval = -EINVAL;
+			break;
+		}
+
+		/* we set the video window to something smaller or equal to what
+		* is requested by the user???
+		*/
+		down(&cam->param_lock);
+		if(vw.width!=cam->vw.width || vw.height!=cam->vw.height) {
+			int video_size = match_videosize(vw.width, vw.height);
+			if(video_size < 0) {
+				retval = -EINVAL;
+				up(&cam->param_lock);
+				break;
+			}
+			cam->video_size = video_size;
+			set_vw_size(cam);
+			DBG("%d / %d\n", cam->vw.width, cam->vw.height);
+			cam->cmd_queue |= COMMAND_SETFORMAT;
+		}
+
+		// FIXME needed??? memcpy(&cam->vw, &vw, sizeof(vw));
+		up(&cam->param_lock);
+
+		/* setformat ignored by camera during streaming,
+		 * so stop/dispatch/start */
+		if(cam->cmd_queue & COMMAND_SETFORMAT) {
+			DBG("\n");
+			dispatch_commands(cam);
+		}
+		DBG("%d/%d:%d\n", cam->video_size,
+		    cam->vw.width, cam->vw.height);
+		break;
+	}
+
+	/* mmap interface */
+	case VIDIOCGMBUF:
+	{
+		struct video_mbuf vm;
+		int i;
+
+		DBG("VIDIOCGMBUF\n");
+		memset(&vm, 0, sizeof(vm));
+		vm.size = CPIA_MAX_FRAME_SIZE*FRAME_NUM;
+		vm.frames = FRAME_NUM;
+		for( i=0; i<FRAME_NUM; i++ ) {
+			vm.offsets[i] = CPIA_MAX_FRAME_SIZE*i;
+		}
+		if(copy_to_user((void *)arg, (void *)&vm, sizeof(vm)))
+			retval = -EFAULT;
+
+		break;
+	}
+	
+	case VIDIOCMCAPTURE:
+	{
+		struct video_mmap vm;
+		int video_size;
+
+		if (copy_from_user((void *)&vm, (void *)arg, sizeof(vm))) {
+			retval = -EFAULT;
+			break;
+		}
+#if 0
+		DBG("VIDIOCMCAPTURE: %d / %d / %dx%d\n", vm.format, vm.frame,
+		    vm.width, vm.height);
+#endif
+		if (vm.frame<0||vm.frame>FRAME_NUM) {
+			retval = -EINVAL;
+			break;
+		}
+
+		/* set video format */
+		cam->vp.palette = vm.format;
+		switch(vm.format) {
+		case VIDEO_PALETTE_GREY:
+		case VIDEO_PALETTE_RGB555:
+		case VIDEO_PALETTE_RGB565:
+		case VIDEO_PALETTE_YUV422:
+		case VIDEO_PALETTE_YUYV:
+		case VIDEO_PALETTE_UYVY:
+			cam->vp.depth = 16;
+			break;
+		case VIDEO_PALETTE_RGB24:
+			cam->vp.depth = 24;
+			break;
+		case VIDEO_PALETTE_RGB32:
+			cam->vp.depth = 32;
+			break;
+		default:
+			retval = -EINVAL;
+			break;
+		}
+		if(retval != 0) break;
+
+		/* set video size */
+		video_size = match_videosize(vm.width, vm.height);
+		if(cam->video_size<0) {
+			retval = -EINVAL;
+			break;
+		}
+		if( video_size != cam->video_size ) {
+			cam->video_size = video_size;
+			set_vw_size(cam);
+			cam->cmd_queue |= COMMAND_SETFORMAT;
+			dispatch_commands(cam);
+		}
+#if 0
+		DBG("VIDIOCMCAPTURE: %d / %d/%d\n", cam->video_size,
+		    cam->vw.width, cam->vw.height);
+#endif
+		/* according to v4l-spec we must start streaming here */
+		retval = capture_frame(cam, &vm);
+
+		break;
+	}
+	
+	case VIDIOCSYNC:
+	{
+		int frame;
+
+		if (copy_from_user((void *)&frame, arg, sizeof(int))) {
+			retval = -EFAULT;
+			break;
+		}
+		//DBG("VIDIOCSYNC: %d\n", frame);
+
+		if (frame<0||frame>=FRAME_NUM) {
+			retval = -EINVAL;
+			break;
+		}
+
+		switch (cam->frame[frame].state) {
+		case FRAME_UNUSED:
+		case FRAME_READY:
+		case FRAME_GRABBING:
+			DBG("sync to unused frame %d\n", frame);
+			retval = -EINVAL;
+			break;
+
+		case FRAME_DONE:
+			cam->frame[frame].state = FRAME_UNUSED;
+			//DBG("VIDIOCSYNC: %d synced\n", frame);
+			break;
+		}
+		if(retval == -EINTR) {
+			/* FIXME - xawtv does not handle this nice */
+			retval = 0;
+		}
+		break;
+	}
+
+	/* pointless to implement overlay with this camera */
+	case VIDIOCCAPTURE:
+		retval = -EINVAL;
+		break;
+	case VIDIOCGFBUF:
+		retval = -EINVAL;
+		break;
+	case VIDIOCSFBUF:
+		retval = -EINVAL;
+		break;
+	case VIDIOCKEY:
+		retval = -EINVAL;
+		break;
+
+	/* tuner interface - we have none */
+	case VIDIOCGTUNER:
+		retval = -EINVAL;
+		break;
+	case VIDIOCSTUNER:
+		retval = -EINVAL;
+		break;
+	case VIDIOCGFREQ:
+		retval = -EINVAL;
+		break;
+	case VIDIOCSFREQ:
+		retval = -EINVAL;
+		break;
+
+	/* audio interface - we have none */
+	case VIDIOCGAUDIO:
+		retval = -EINVAL;
+		break;
+	case VIDIOCSAUDIO:
+		retval = -EINVAL;
+		break;
+	default:
+		retval = -ENOIOCTLCMD;
+		break;
+	}
+
+	up(&cam->param_lock);
+	up(&cam->busy_lock);
+	return retval;
+} 
+
+/* FIXME */
+static int cpia_mmap(struct video_device *dev, const char *adr,
+                     unsigned long size)
+{
+	unsigned long start = (unsigned long)adr;
+	unsigned long page, pos;
+	struct cam_data *cam;
+	int retval;
+
+	DBG("cpia_mmap: %ld\n", size);
+
+	if(size > FRAME_NUM*CPIA_MAX_FRAME_SIZE){
+		return -EINVAL;
+	}
+
+	cam = dev->priv;
+
+	/* make this _really_ smp-safe */
+	if( down_interruptible(&cam->busy_lock) ) {
+		return -EINTR;
+	}
+
+	if( cam->frame_buf == NULL ) { /* we do lazy allocation */
+		if( (retval = allocate_frame_buf(cam)) ) {
+			up(&cam->busy_lock);
+			return retval;
+		}
+	}
+	pos = (unsigned long)(cam->frame_buf);
+	while (size > 0) {
+		page = kvirt_to_phys(pos);
+		if (remap_page_range(start, page, PAGE_SIZE, PAGE_SHARED)) {
+			up(&cam->busy_lock);
+			return -EAGAIN;
+		}
+		start+=PAGE_SIZE;
+		pos+=PAGE_SIZE;
+		if (size > PAGE_SIZE) size-=PAGE_SIZE;
+		else size=0;
+	}
+
+	DBG("cpia_mmap: %ld\n", size);
+	up(&cam->busy_lock);
+	return 0;
+}
+
+int cpia_video_init(struct video_device *vdev)
+{
+	create_proc_cpia_cam(vdev->priv);
+	return 0;
+}
+
+static struct video_device cpia_template = {
+	"CPiA Camera",
+	VID_TYPE_CAPTURE,
+	VID_HARDWARE_CPIA,      /* FIXME */
+	cpia_open,              /* open */
+	cpia_close,             /* close */
+	cpia_read,              /* read */
+	NULL,                   /* no write */
+	NULL,                   /* no poll */
+	cpia_ioctl,             /* ioctl */
+	cpia_mmap,              /* mmap */
+	cpia_video_init,        /* initialize */
+	NULL,                   /* priv */
+	0,                      /* busy */
+	-1                      /* minor - unset */
+};
+
+/* initialise cam_data structure  */
+static void reset_camera_struct(struct cam_data *cam)
+{
+	/* The following parameter values are the defaults from
+	 * "Software Developer's Guide for CPiA Cameras".  Any changes
+	 * to the defaults are noted in comments. */
+	cam->params.colourParams.brightness = 50;
+	cam->params.colourParams.contrast = 48;
+	cam->params.colourParams.saturation = 50;
+	cam->params.exposure.gainMode = 2;
+	cam->params.exposure.expMode = 2;		/* AEC */
+	cam->params.exposure.compMode = 1;
+	cam->params.exposure.centreWeight = 1;
+	cam->params.exposure.gain = 0;
+	cam->params.exposure.fineExp = 0;
+	cam->params.exposure.coarseExpLo = 185;
+	cam->params.exposure.coarseExpHi = 0;
+	cam->params.exposure.redComp = 220;
+	cam->params.exposure.green1Comp = 214;
+	cam->params.exposure.green2Comp = 214;
+	cam->params.exposure.blueComp = 230;
+	cam->params.colourBalance.balanceMode = 2;	/* auto color balance */
+	cam->params.colourBalance.redGain = 32;
+	cam->params.colourBalance.greenGain = 6;
+	cam->params.colourBalance.blueGain = 92;
+	cam->params.apcor.gain1 = 0x1c;
+	cam->params.apcor.gain2 = 0x1a;
+	cam->params.apcor.gain4 = 0x2d;
+	cam->params.apcor.gain8 = 0x2a;
+	cam->params.flickerControl.flickerMode = 0;
+	cam->params.flickerControl.coarseJump = 
+		flicker_jumps[cam->mainsFreq]
+		             [cam->params.sensorFps.baserate]
+		             [cam->params.sensorFps.divisor];
+	cam->params.vlOffset.gain1 = 24;
+	cam->params.vlOffset.gain2 = 28;
+	cam->params.vlOffset.gain4 = 30;
+	cam->params.vlOffset.gain8 = 30;
+	cam->params.compressionParams.hysteresis = 3;
+	cam->params.compressionParams.threshMax = 11;
+	cam->params.compressionParams.smallStep = 1;
+	cam->params.compressionParams.largeStep = 3;
+	cam->params.compressionParams.decimationHysteresis = 2;
+	cam->params.compressionParams.frDiffStepThresh = 5;
+	cam->params.compressionParams.qDiffStepThresh = 3;
+	cam->params.compressionParams.decimationThreshMod = 2;
+	/* End of default values from Software Developer's Guide */
+	
+	/* Start with a reasonable transfer rate */
+	cam->transfer_rate = 500;
+	
+	/* Set Sensor FPS to 15fps. This seems better than 30fps
+	 * for indoor lighting. */
+	cam->params.sensorFps.divisor = 1;
+	cam->params.sensorFps.baserate = 1;
+	
+	cam->params.yuvThreshold.yThreshold = 15; /* FIXME? */
+	cam->params.yuvThreshold.uvThreshold = 15; /* FIXME? */
+	
+	cam->params.format.subSample = SUBSAMPLE_422;
+	cam->params.format.yuvOrder = YUVORDER_YUYV;
+	
+	cam->params.compression.mode = CPIA_COMPRESSION_AUTO;
+	cam->params.compressionTarget.frTargeting =
+		CPIA_COMPRESSION_TARGET_QUALITY;
+	cam->params.compressionTarget.targetFR = 7; /* FIXME? */
+	cam->params.compressionTarget.targetQ = 10; /* FIXME? */
+
+	cam->video_size = VIDEOSIZE_CIF;
+	
+	cam->vp.colour = 32768;      /* 50% */
+	cam->vp.hue = 32768;         /* 50% */
+	cam->vp.brightness = 32768;  /* 50% */
+	cam->vp.contrast = 32768;    /* 50% */
+	cam->vp.whiteness = 0;       /* not used -> grayscale only */
+	cam->vp.depth = 0;           /* FIXME: to be set by user? */
+	cam->vp.palette = 0;         /* FIXME: to be set by user? */
+
+	cam->vw.x = 0;
+	cam->vw.y = 0;
+	set_vw_size(cam);
+	cam->vw.chromakey = 0;
+	/* PP NOTE: my extension to use vw.flags for this, bear it! */
+	cam->vw.flags = 0;
+	cam->vw.clipcount = 0;
+	cam->vw.clips = NULL;
+
+	cam->cmd_queue = COMMAND_NONE;
+	cam->first_frame = 0;
+
+	return;
+}
+
+/* initialize cam_data structure  */
+static void init_camera_struct(struct cam_data *cam,
+                               struct cpia_camera_ops *ops )
+{
+	int i;
+	/* Default everything to 0 */
+	memset(cam, 0, sizeof(struct cam_data));
+
+	cam->ops = ops;
+	cam->param_lock = MUTEX;
+
+	reset_camera_struct(cam);
+
+	cam->proc_entry = NULL;
+
+	memcpy(&cam->vdev, &cpia_template, sizeof(cpia_template));
+	cam->vdev.priv = cam;
+	
+	cam->curframe = 0;
+	for( i=0; i<FRAME_NUM; i++) {
+		cam->frame[i].width = 0;
+		cam->frame[i].height = 0;
+		cam->frame[i].state = FRAME_UNUSED;
+		cam->frame[i].data = NULL;
+	}
+	cam->decompressed_frame.width = 0;
+	cam->decompressed_frame.height = 0;
+	cam->decompressed_frame.state = FRAME_UNUSED;
+	cam->decompressed_frame.data = NULL;
+
+	return;
+}
+
+int cpia_register_camera(struct cpia_camera_ops *ops)
+{
+	int i;
+	struct cam_data *cam;
+	/* Need a lock when adding/removing cameras.  This doesn't happen
+	 * often and doesn't take very long, so grabbing the kernel lock
+	 * should be OK. */
+	lock_kernel();
+	for(i=0; i < CPIA_MAXCAMS && camera[i] != NULL; ++i); /* no loop body */
+	if(i == CPIA_MAXCAMS) {
+		unlock_kernel();
+		return -ENODEV;
+	}
+	
+	if((camera[i] = kmalloc(sizeof(struct cam_data), GFP_KERNEL)) == NULL) {
+		unlock_kernel();
+		return -ENOMEM;
+	}
+	
+	init_camera_struct( camera[i], ops );
+	camera[i]->index = i;
+
+	/* register v4l device */
+	if (video_register_device(&camera[i]->vdev, VFL_TYPE_GRABBER) == -1) {
+		kfree(camera[i]);
+		camera[i] = NULL;
+		unlock_kernel();
+		printk(KERN_DEBUG "video_register_device failed\n");
+		return -ENODEV;
+	}
+
+	unlock_kernel();
+	
+	/* FIXME */
+#if 1
+	cam = camera[i];
+	/* open cpia */
+	if (cam->ops->open(cam->index, &cam->lowlevel_data)) {
+		rvfree(cam->decompressed_frame.data, CPIA_MAX_FRAME_SIZE);
+		cam->decompressed_frame.data=NULL;
+		rvfree(cam->raw_image, CPIA_MAX_IMAGE_SIZE);
+		cam->raw_image=NULL;
+		return -ENODEV;
+	}
+	
+	/* reset the camera */
+	if((i = reset_camera(cam)) != 0) {
+		cam->ops->close(cam->lowlevel_data);
+		rvfree(cam->decompressed_frame.data, CPIA_MAX_FRAME_SIZE);
+		cam->decompressed_frame.data=NULL;
+		rvfree(cam->raw_image, CPIA_MAX_IMAGE_SIZE);
+		cam->raw_image=NULL;
+		return i;
+	}
+#endif	
+	return i;
+}
+
+void cpia_unregister_camera(int camnr)
+{
+	struct cam_data *cam;
+	if(camnr >= CPIA_MAXCAMS || camera[camnr] == NULL) return;
+	cam = camera[camnr];
+	
+	DBG("unregistering video\n");
+	/* FIXME: Is this safe if the device is open? */
+	video_unregister_device(&cam->vdev);
+	
+	/* Need a lock when adding/removing cameras.  This doesn't happen
+	 * often and doesn't take very long, so grabbing the kernel lock
+	 * should be OK. */
+	lock_kernel();
+	
+	DBG("destroying /proc/cpia/video%d\n", cam->vdev.minor);
+	destroy_proc_cpia_cam(cam);
+	
+	if(cam->open_count == 0) {
+		DBG("freeing camera\n");
+		kfree(camera[camnr]);
+		camera[camnr] = NULL;
+	} else {
+		DBG("camera open -- setting ops to NULL\n");
+		cam->ops = NULL;
+	}
+	unlock_kernel();
+}
+
+/****************************************************************************
+ *
+ *  Module routines
+ *
+ ***************************************************************************/
+
+#ifdef MODULE
+int init_module(void)
+{
+	printk(KERN_INFO "%s v%d.%d.%d\n", ABOUT,
+	       CPIA_MAJ_VER, CPIA_MIN_VER, CPIA_PATCH_VER);
+	proc_cpia_create();
+#ifdef CONFIG_KMOD
+#ifdef CONFIG_VIDEO_CPIA_PP_MODULE
+	request_module("cpia_pp");
+#endif
+#ifdef CONFIG_VIDEO_CPIA_USB_MODULE
+	request_module("cpia_usb");
+#endif
+#endif
+return 0;
+}
+
+void cleanup_module(void)
+{
+	int i;
+	for(i=0; i<CPIA_MAXCAMS; ++i) {
+		if(camera[i]) {
+			video_unregister_device(&camera[i]->vdev);
+		}
+	}
+	
+	proc_cpia_destroy();
+}
+
+#else
+
+int cpia_init(struct video_init *unused)
+{
+	printk(KERN_INFO "%s v%d.%d.%d\n", ABOUT,
+	       CPIA_MAJ_VER, CPIA_MIN_VER, CPIA_PATCH_VER);
+	proc_cpia_create();
+
+#ifdef MODULE
+#ifdef CONFIG_KMOD
+#ifdef CONFIG_VIDEO_CPIA_PP_MODULE
+	request_module("cpia_pp");
+#endif
+#ifdef CONFIG_VIDEO_CPIA_USB_MODULE
+	request_module("cpia_usb");
+#endif
+#endif	/* CONFIG_KMOD */
+#else	/* !MODULE */
+#ifdef CONFIG_VIDEO_CPIA_PP
+	cpia_pp_init();
+#endif
+#ifdef CONFIG_VIDEO_CPIA_USB
+	cpia_usb_init();
+#endif
+#endif	/* !MODULE */
+	return 0;
+}
+
+/* Exported symbols for modules. */
+
+EXPORT_SYMBOL(cpia_register_camera);
+EXPORT_SYMBOL(cpia_unregister_camera);
+
+#endif
diff -urN linux-2.2.15-pre5/drivers/char/cpia_pp.c linux/drivers/char/cpia_pp.c
--- linux-2.2.15-pre5/drivers/char/cpia_pp.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/cpia_pp.c	Mon Dec  3 21:58:46 2001
@@ -0,0 +1,1497 @@
+/*
+ * cpia_pp CPiA Parallel Port driver
+ *
+ * Supports CPiA based parallel port Video Camera's.
+ *
+ * (C) 1999 Bas Huisman <bhuism@cs.utwente.nl>
+ *          Scott J. Bertin <sbertin@mindspring.com>,
+ *          Peter Pregler <Peter_Pregler@email.com>
+ *          Johannes Erdfelt
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/config.h>
+
+#include <linux/module.h>
+#include <linux/init.h>
+
+#include <linux/kernel.h>
+#include <linux/parport.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#ifdef CONFIG_VIDEO_CPIA_PP_DMA
+#include <asm/dma.h>
+#endif
+
+#ifdef CONFIG_KMOD
+#include <linux/kmod.h>
+#endif
+
+
+/* If this is a module and parport_pc is not, some parport_pc_* functions
+ * are not directly availbale.  parport.h messes this up.
+ * This fixes what we need.
+ */
+#if defined(MODULE) && !defined(CONFIG_PARPORT_PC_MODULE)
+#define PARPORT_NEED_GENERIC_OPS
+#undef parport_enable_irq
+#undef parport_disable_irq
+#undef parport_read_fifo
+#define parport_read_fifo(p)		(p)->ops->read_fifo(p)
+#endif
+
+/* These should be defined in linux/parport.h, but aren't.  The #ifndef
+ * parport_enable_irq is in case they are at some future time.
+ */
+#ifdef PARPORT_NEED_GENERIC_OPS
+#ifndef parport_enable_irq
+#define parport_enable_irq(p) (p)->ops->enable_irq(p)
+#define parport_disable_irq(p) (p)->ops->disable_irq(p)
+#endif				/* parport_enable_irq */
+#else				/* !PARPORT_NEED_GENERIC_OPS */
+#ifndef parport_enable_irq
+#define parport_enable_irq(p) parport_pc_enable_irq(p)
+#define parport_disable_irq(p) parport_pc_disable_irq(p)
+#endif				/* parport_enable_irq */
+#endif				/* !PARPORT_NEED_GENERIC_OPS */
+
+#undef _CPIA_DEBUG_		/* define for verbose debug output */
+#include <linux/cpia.h>
+
+static int cpia_pp_open(int camnr, void **privdata);
+static int cpia_pp_registerCallback(void *privdata, void (*cb) (void *cbdata),
+                                    void *cbdata);
+static int cpia_pp_transferCmd(void *privdata, u8 *command, u8 *data);
+static int cpia_pp_streamStart(void *privdata);
+static int cpia_pp_streamStop(void *privdata);
+static int cpia_pp_streamRead(void *privdata, u8 *buffer, int noblock);
+static int cpia_pp_close(void *privdata);
+
+#define ABOUT "Parallel port driver for Vision CPiA based cameras"
+
+/* IEEE 1284 Compatiblity Mode signal names 	*/
+#define nStrobe		PARPORT_CONTROL_STROBE	  /* inverted */
+#define nAutoFd		PARPORT_CONTROL_AUTOFD	  /* inverted */
+#define nInit		PARPORT_CONTROL_INIT
+#define nSelectIn	PARPORT_CONTROL_SELECT
+#define IntrEnable	PARPORT_CONTROL_INTEN	  /* normally zero for no IRQ */
+#define DirBit		PARPORT_CONTROL_DIRECTION /* 0 = Forward, 1 = Reverse	*/
+
+#define nFault		PARPORT_STATUS_ERROR
+#define Select		PARPORT_STATUS_SELECT
+#define PError		PARPORT_STATUS_PAPEROUT
+#define nAck		PARPORT_STATUS_ACK
+#define Busy		PARPORT_STATUS_BUSY	  /* inverted */	
+
+/* some more */
+#define HostClk		nStrobe
+#define HostAck		nAutoFd
+#define nReverseRequest	nInit
+#define Active_1284	nSelectIn
+#define nPeriphRequest	nFault
+#define XFlag		Select
+#define nAckReverse	PError
+#define PeriphClk	nAck
+#define PeriphAck	Busy
+
+/* these can be used to correct for the inversion on some bits */
+#define STATUS_INVERSION_MASK	(Busy)
+#define CONTROL_INVERSION_MASK	(nStrobe|nAutoFd|nSelectIn)
+
+#define ECR_empty	0x01
+#define ECR_full	0x02
+#define ECR_serviceIntr 0x04
+#define ECR_dmaEn	0x08
+#define ECR_nErrIntrEn	0x10
+
+#define ECR_mode_mask	0xE0
+#define ECR_SPP_mode	0x00
+#define ECR_PS2_mode	0x20
+#define ECR_FIFO_mode	0x40
+#define ECR_ECP_mode	0x60
+
+#define ECP_FIFO_SIZE	16
+#define DMA_BUFFER_SIZE               PAGE_SIZE
+	/* for 16bit DMA make sure DMA_BUFFER_SIZE is 16 bit aligned */
+
+#define GetECRMasked(port,mask)	(parport_read_econtrol(port) & (mask))
+#define GetStatus(port)		((parport_read_status(port)^STATUS_INVERSION_MASK)&(0xf8))
+#define SetStatus(port,val)	parport_write_status(port,(val)^STATUS_INVERSION_MASK)
+#define GetControl(port)	((parport_read_control(port)^CONTROL_INVERSION_MASK)&(0x3f))
+#define SetControl(port,val)	parport_write_control(port,(val)^CONTROL_INVERSION_MASK)
+
+#define GetStatusMasked(port,mask)	(GetStatus(port) & (mask))
+#define GetControlMasked(port,mask)	(GetControl(port) & (mask))
+#define SetControlMasked(port,mask)	SetControl(port,GetControl(port) | (mask));
+#define ClearControlMasked(port,mask)	SetControl(port,GetControl(port)&~(mask));
+#define FrobControlBit(port,mask,value)	SetControl(port,(GetControl(port)&~(mask))|((value)&(mask)));
+
+#define PP_MAXCAMS	(PARPORT_MAX<CPIA_MAXCAMS?PARPORT_MAX:CPIA_MAXCAMS)
+#define PACKET_LENGTH 	8
+
+/* Magic numbers for defining port-device mappings */
+#define PPCPIA_PARPORT_UNSPEC -4
+#define PPCPIA_PARPORT_AUTO -3
+#define PPCPIA_PARPORT_OFF -2
+#define PPCPIA_PARPORT_NONE -1
+
+#ifdef MODULE
+static int parport_nr[PP_MAXCAMS] = {[0 ... PP_MAXCAMS - 1] = PPCPIA_PARPORT_UNSPEC};
+static char *parport[PP_MAXCAMS] = {NULL,};
+
+MODULE_DESCRIPTION("Parallel port driver for Vision CPiA based cameras");
+MODULE_PARM(parport, "1-" __MODULE_STRING(PP_MAXCAMS) "s");
+MODULE_PARM_DESC(parport, "'auto' or a list of parallel port numbers. Just like lp.");
+#else
+static int parport_nr[PP_MAXCAMS] __initdata =
+	{[0 ... PP_MAXCAMS - 1] = PPCPIA_PARPORT_UNSPEC};
+static int parport_ptr = 0;
+#endif
+
+enum comstates { CPIA_FORWARD, CPIA_REVERSE };	//fixme
+enum camstates {
+	CPIA_PHASE_idle = 0,
+	CPIA_PHASE_neg2s,
+	CPIA_PHASE_setup,
+	CPIA_PHASE_f2rev,
+	CPIA_PHASE_r2for,
+	CPIA_PHASE_term,
+	CPIA_PHASE_secpread,
+	CPIA_PHASE_ecpread,
+	CPIA_PHASE_secpwrite,
+};
+#ifdef _CPIA_DEBUG_
+static char camstatesstr[12][40] =
+{
+	"CPIA_PHASE_idle",
+	"CPIA_PHASE_neg2s",
+	"CPIA_PHASE_setup",
+	"CPIA_PHASE_f2rev",
+	"CPIA_PHASE_r2for",
+	"CPIA_PHASE_term",
+	"CPIA_PHASE_secpread",
+	"CPIA_PHASE_ecpread",
+	"CPIA_PHASE_secpwrite",
+};
+#endif
+struct pp_cam_entry {
+	struct pardevice *pdev;
+	struct parport *port;
+	enum comstates state;
+	enum camstates camstate;
+	struct tq_struct cb_task;
+	int open_count;
+	int camnr;
+	struct wait_queue *wq_stream;
+	
+	/* image state flags */
+	int image_ready;	/* we got an interrupt */
+	int image_complete;	/* we have seen 4 EOI */
+
+	int streaming; /* we are in streaming mode */
+	int stream_irq;
+	
+#ifdef CONFIG_VIDEO_CPIA_PP_DMA
+	/* dma stuff */
+	int dma_on;
+	int dma_intr;
+	int block_size; /* read block_size bytes per DMA */
+	unsigned char *dma_buf;	/* into dma_buf */
+	struct wait_queue *wq_dma;
+	struct tq_struct dma_task;
+	/* counters used in dma-interrupt handler */
+	unsigned char *buf; /* current position in read buffer */
+	int readbytes; /* # bytes read */
+	int bytes; /* maximum # of bytes to read */
+	int endseen; /* number of EOI read */
+#endif
+};
+
+static struct pp_cam_entry *camera[PP_MAXCAMS] = {[0 ... PP_MAXCAMS - 1] = NULL};
+
+static struct cpia_camera_ops cpia_pp_ops = 
+{
+	cpia_pp_open,
+	cpia_pp_registerCallback,
+	cpia_pp_transferCmd,
+	cpia_pp_streamStart,
+	cpia_pp_streamStop,
+	cpia_pp_streamRead,
+	cpia_pp_close
+};
+
+#ifdef _CPIA_DEBUG_
+#define DEB_PORT(port) { \
+u8 controll = GetControl(port); \
+u8 statusss = GetStatus(port); \
+DBG("nsel %c per %c naut %c nstrob %c nak %c busy %c nfaul %c sel %c init %c dir %c\n",\
+((controll & nSelectIn)	? 'U' : 'D'), \
+((statusss & PError)	? 'U' : 'D'), \
+((controll & nAutoFd)	? 'U' : 'D'), \
+((controll & nStrobe)	? 'U' : 'D'), \
+((statusss & nAck)	? 'U' : 'D'), \
+((statusss & Busy)	? 'U' : 'D'), \
+((statusss & nFault)	? 'U' : 'D'), \
+((statusss & Select)	? 'U' : 'D'), \
+((controll & nInit)	? 'U' : 'D'), \
+((controll & DirBit)	? 'R' : 'F')  \
+); }
+#else
+#define DEB_PORT(port) {}
+#endif
+
+#define WHILE_OUT_TIMEOUT (HZ/10)
+#define DMA_TIMEOUT 10*HZ
+
+/* FIXME */
+static void cpia_parport_enable_irq( struct parport *port ) {
+  parport_enable_irq(port);
+  mdelay(10);
+  return;
+}
+
+static void cpia_parport_disable_irq( struct parport *port ) {
+  parport_disable_irq(port);
+  mdelay(10);
+  return;
+}
+
+static int while_out(struct pp_cam_entry *cam)
+{
+	struct parport *port = cam->port;
+	unsigned long endjif;
+
+	if (GetECRMasked(port, ECR_full)) {
+		return 1;
+	}
+
+	endjif = jiffies + WHILE_OUT_TIMEOUT;
+	while( jiffies<endjif ) {
+		schedule();
+		if (GetECRMasked(port, ECR_full)) {
+			return 1;
+		}
+	}
+	DBG("WhileoutError at line waited %d\n", WHILE_OUT_TIMEOUT);
+	return 0;
+}
+
+static int my_wait_peripheral(struct parport *port, int mask, int result)
+{
+	unsigned long oldjiffies = jiffies;
+
+	result &= mask;
+
+	if (GetStatusMasked(port, mask) == result) {
+		return 0;
+	}
+
+	while (jiffies < oldjiffies + (HZ / 3)) {
+		if (GetStatusMasked(port, mask) == result) {
+			return 0;
+		}
+	}
+
+	DBG("failed 0x%x should be 0x%x waited %ld jiffies\n", GetStatus(port) & mask, result, jiffies - oldjiffies);
+	return 1;
+}
+
+static void EmptyFifo(struct parport *port)
+{
+	while (!GetECRMasked(port, ECR_empty)) {
+		parport_read_fifo(port);
+	}
+}
+
+/****************************************************************************
+ *
+ *  Negotiate2Setup
+ *
+ ***************************************************************************/
+static int Negotiate2SetupPhase(struct pp_cam_entry *cam, int extensibility)
+{
+	struct parport *port = cam->port;
+	u8 status = GetStatus(port);
+	u8 control = GetControl(port);
+
+	cam->camstate = CPIA_PHASE_neg2s;
+
+	if (control & nSelectIn) DBG("nSelIn should be low at beginning\n");
+	if (status & PError) DBG("PError should be low at beginning\n");
+	if (!(control & nAutoFd)) DBG("nAutFb should be high at beginning\n");
+	if (!(control & nStrobe)) DBG("nStrobe should be high at beginning\n");
+
+	/*
+	   a high nack idicates a new image is ready (see the docs)
+	   this line can be used for a ISR.
+	 */
+	//if (!(status & nAck)) DBG("nAck should be high at beginning\n");
+
+	if (!(status & Busy)) DBG("Busy should be high at beginning\n");
+	// FIXME ?if (status & nFault) DBG("nFault should be low at beginning\n");
+	if (!(status & Select)) DBG("Select should be high at beginning\n");
+	if (!(control & nInit)) DBG("nInit should be high at beginning\n");
+
+	if (cam->state != CPIA_FORWARD)
+		DBG("port was not in CPIA_FORWARD direction\n");
+
+	parport_write_data(port, extensibility ? 0x80 : 0x10);
+
+	FrobControlBit(port, nSelectIn | nAutoFd, nSelectIn);	//A nAutoFd low,nSelectin high
+
+	if (my_wait_peripheral(port, PError | nFault | Select | nAck, PError | nFault | Select)) {	//B
+		DBG("B failed\n");
+		return -1;
+	}
+
+	ClearControlMasked(port, nStrobe);
+	ClearControlMasked(port, nStrobe);
+	ClearControlMasked(port, nStrobe);
+	ClearControlMasked(port, nStrobe);
+
+	SetControlMasked(port, nAutoFd | nStrobe);	// C
+
+	if (my_wait_peripheral(port, nAck | Select | PError, nAck | Select)) {	// D niet perror+busy low ? nfault high ?
+		DBG("D failed nAck,nInit should become high, Perror low\n");
+		return -1;
+	}
+
+	if (extensibility) {
+		parport_write_data(port, 8 | 3);
+		ClearControlMasked(port, nStrobe);
+		if (my_wait_peripheral(port, nAck, 0)) {
+			DBG("nAck should go low in asking for extensibility mode\n");
+			DEB_PORT(port);
+			return -1;
+		}
+		SetControlMasked(port, nStrobe);
+		if (my_wait_peripheral(port, nAck | Select, nAck | Select)) {
+			DBG("nAck,Xflag should go high in asking for extensibility mode\n");
+			DEB_PORT(port);
+			return -1;
+		}
+	}
+	return 0;
+}
+
+/****************************************************************************
+ *
+ *  EcpSetupPhase
+ *
+ ***************************************************************************/
+static int ECPSetupPhase(struct pp_cam_entry *cam)
+{
+	cam->camstate = CPIA_PHASE_setup;
+	ClearControlMasked(cam->port, nAutoFd);	// E
+	//if (my_wait_peripheral(cam->port,PError,PError)) //pport style
+
+	if (my_wait_peripheral(cam->port, PError | nAck | Busy, PError | nAck)) {	// F
+		DBG("F failed PError,nAck high Busy Low\n");
+		DEB_PORT(cam->port);
+		return -1;
+	}
+	EmptyFifo(cam->port);
+	return 0;
+}
+
+/****************************************************************************
+ *
+ *  Forward2Reverse
+ *
+ ***************************************************************************/
+static int Forward2Reverse(struct pp_cam_entry *cam)
+{
+	struct parport *port = cam->port;
+
+	cam->camstate = CPIA_PHASE_f2rev;
+
+	SetControlMasked(port, DirBit);		// door ClearControlBit(port,nReverseRequest) komt de eerste byte beter als DirBit hier wordt gezet
+	//mdelay(3);
+
+	ClearControlMasked(port, nReverseRequest);	// G
+	//if (my_wait_peripheral(port,PError,0)) //pportq style
+
+	if (my_wait_peripheral(port, PError | Busy | nFault | Select, Busy | Select)) {	//H 
+		DBG("H failed\n");
+		DEB_PORT(port);
+		ClearControlMasked(port, DirBit);	// DirBit repareren;
+		//mdelay(3);
+
+		return -1;
+	}
+	cam->state = CPIA_REVERSE;
+	return 0;
+}
+
+
+/****************************************************************************
+ *
+ *  Reverse2Forward
+ *
+ ***************************************************************************/
+static void Reverse2Forward(struct pp_cam_entry *cam)
+{
+	struct parport *port = cam->port;
+
+	cam->camstate = CPIA_PHASE_r2for;
+
+	if (cam->state != CPIA_REVERSE)
+		DBG("port was not in CPIA_REVERSE direction\n");
+
+	SetControlMasked(port, nReverseRequest);
+	if (my_wait_peripheral(port, nAckReverse, nAckReverse))
+		DBG("nAckReverse should fo high in Reverse2Forward\n");
+
+	ClearControlMasked(port, DirBit);
+	//mdelay(3);
+
+	cam->state = CPIA_FORWARD;
+}
+
+
+/****************************************************************************
+ *
+ *  Valid1284Termination
+ *
+ ***************************************************************************/
+static void Valid1284Termination(struct pp_cam_entry *cam)
+{
+	struct parport *port = cam->port;
+
+	cam->camstate = CPIA_PHASE_term;
+
+	if (cam->state != CPIA_FORWARD)
+		DBG("port was not in CPIA_FORWARD direction\n");
+
+	FrobControlBit(port, nSelectIn | nAutoFd | nStrobe | nInit, nAutoFd | nStrobe | nInit);		//20
+
+	if (my_wait_peripheral(port, nAck, 0)) {	//21
+		DBG("nAck should go low in Valid1284Termination\n");
+		DEB_PORT(port);
+	}
+
+	ClearControlMasked(port, nAutoFd);	//22
+
+	if (my_wait_peripheral(port, nAck, nAck))	//23
+		DBG("nAck should go high again in Valid1284Termination\n");
+
+	SetControlMasked(port, nAutoFd);	//24
+
+	if (my_wait_peripheral(port, PError | nAck | Busy | nFault | Select,
+	                       nAck | Busy | Select))
+		DBG("status bit did not go to the correct value\n");
+	
+	cam->camstate = CPIA_PHASE_idle;
+}
+
+/****************************************************************************
+ *
+ *  SimECPReadBuffer (does termination)
+ *
+ ***************************************************************************/
+static int SimECPReadBuffer(struct pp_cam_entry *cam, u8 *buf, int bytes)
+{
+	int readbytes = 0;
+	struct parport *port = cam->port;
+
+	cam->camstate = CPIA_PHASE_secpread;
+
+	if (cam->state != CPIA_REVERSE) DBG("cam not in CPIA_REVERSE\n");
+	if (!GetControlMasked(port, DirBit))
+		DBG("parport is in forward mode, "
+		    "guess you won't be reading much\n");
+
+	while (readbytes < bytes) {
+		ClearControlMasked(port, nAutoFd);	//moet hier staan: als de while loop exit moet nAutoFd high blijven ...
+
+		if (my_wait_peripheral(port, nAck, 0)) {
+			DBG("nAck didn't went down after read %d bytes no more data ?\n", readbytes);
+			break;
+		}
+		*buf++ = parport_read_data(port);
+		SetControlMasked(port, nAutoFd);
+
+		readbytes++;
+		if (my_wait_peripheral(port, nAck, nAck)) {
+			DBG("nAck didn't went up after read %d bytes\n", readbytes);
+			break;
+		}
+	}
+	return readbytes;
+}
+
+/****************************************************************************
+ *
+ *  ECPReadBuffer_PIO
+ *
+ ***************************************************************************/
+static int ECPReadBuffer_PIO(struct pp_cam_entry *cam, u8 *buf, int bytes)
+{
+	int readbytes = 0, endseen = 0, j;
+	struct parport *port = cam->port;
+
+	if (my_wait_peripheral(port, nAck, 0)) {
+		DBG("nAck didn't went down after read %d bytes no more data ?\n", readbytes);
+		DEB_PORT(port);
+		goto WhileoutError;
+	}
+
+	*buf = parport_read_data(port);	// we take the first byte manually
+	if (*buf == EOI) {
+		endseen++;
+	} else {
+		endseen = 0;
+	}
+	buf++;
+	readbytes++;
+
+	/* acknowledge for this BYTE */
+	SetControlMasked(port, nAutoFd);
+	if (my_wait_peripheral(port, nAck, nAck)) {
+		DBG("nAck didn't went up after read %d bytes\n", readbytes);
+		goto WhileoutError;
+	}
+
+	parport_frob_econtrol(port,
+		ECR_mode_mask|ECR_serviceIntr|ECR_dmaEn|ECR_nErrIntrEn,
+		ECR_ECP_mode|ECR_serviceIntr|ECR_nErrIntrEn);
+
+	while ((((bytes - readbytes) / ECP_FIFO_SIZE) > 0) && (endseen < 4)) {
+		if( current->need_resched ) {
+			schedule();
+		}
+		if (while_out(cam) <= 0) {
+			goto WhileoutError;
+		}
+		for (j = 0; j < ECP_FIFO_SIZE; j++) {
+			*buf = parport_read_fifo(port);
+			if (*buf == EOI) {
+				endseen++;
+			} else {
+				endseen = 0;
+			}
+			buf++;
+			readbytes++;
+		}
+		if( current->need_resched ) schedule();
+	}
+
+	/* switch off automatic filling of the FIFO */
+	ClearControlMasked(port, nAutoFd);
+
+	while ((!GetECRMasked(port, ECR_empty)) && (readbytes < bytes))	{
+		*buf = parport_read_fifo(port);
+		if (*buf == EOI) {
+			endseen++;
+		} else {
+			endseen = 0;
+		}
+		buf++;
+		readbytes++;
+	}
+
+WhileoutError:
+
+	parport_write_econtrol(port, PARPORT_MODE_PCECR);
+	parport_frob_econtrol(port,
+	                      ECR_serviceIntr|ECR_dmaEn|ECR_nErrIntrEn,
+	                      ECR_serviceIntr|ECR_nErrIntrEn);
+	
+	if( endseen > 3 ) {
+		cam->image_complete=1;
+	}
+	
+	return readbytes;
+}
+
+#ifdef CONFIG_VIDEO_CPIA_PP_DMA
+/****************************************************************************
+ *
+ * ECPReadBuffer_DMA
+ *
+ ****************************************************************************/
+
+int ECPReadBuffer_DMA(struct pp_cam_entry *cam, u8 *buf, int bytes)
+{
+	struct parport *port = cam->port;
+	unsigned long flags;
+	int channel = port->dma;
+	int end_jiffies;
+
+	cam->buf=buf;
+	cam->bytes=bytes;
+	cam->endseen=0;
+	cam->readbytes=0;
+	cam->image_complete=0;
+
+	if (my_wait_peripheral(port,nAck,0)) {
+		DBG("nAck didn't went down after read %d bytes no more data ?\n",cam->readbytes);
+		DEB_PORT(port);
+		return cam->readbytes;
+	}
+
+	*(cam->buf) = parport_read_data(port); // we take the first byte manually
+	if (*(cam->buf) == EOI) {
+		cam->endseen++;
+	} else {
+		cam->endseen = 0;
+	}
+	cam->buf++;
+	cam->readbytes++;
+
+	/* acknowledge for this BYTE */
+	SetControlMasked( port, nAutoFd );	  				
+	if (my_wait_peripheral(port,nAck,nAck)) {
+		DBG("nAck didn't went up after read %d bytes\n",cam->readbytes);
+		return cam->readbytes;
+	}
+
+	/* switch port to EPC mode, disable all interrupts for now */
+	parport_frob_econtrol(port, ECR_mode_mask, ECR_ECP_mode);
+	parport_frob_econtrol(port,
+			      ECR_nErrIntrEn|ECR_serviceIntr|ECR_dmaEn,
+			      ECR_nErrIntrEn|ECR_serviceIntr);
+
+	/* prepare controller for dma - align for 16bit DMA */
+	cam->block_size=
+		bytes<DMA_BUFFER_SIZE?(bytes+(bytes%2)):DMA_BUFFER_SIZE;
+
+	flags=claim_dma_lock();
+	disable_dma(channel);
+	clear_dma_ff(channel);
+	set_dma_mode(channel, DMA_MODE_READ);
+	set_dma_addr(channel, virt_to_bus(cam->dma_buf));
+	set_dma_count(channel, cam->block_size);
+	enable_dma(channel);
+
+	cam->dma_on=1;
+	cam->dma_intr = 0;
+
+	/* switch on dma transfer */
+	parport_frob_econtrol(port, ECR_dmaEn, ECR_dmaEn);
+	parport_frob_econtrol(port,ECR_serviceIntr, 0);
+
+	release_dma_lock(flags);
+
+	end_jiffies = jiffies+DMA_TIMEOUT;
+	while(cam->dma_on && !cam->image_complete &&
+	      jiffies < end_jiffies
+	      /*&& !signal_pending(current)*/) {
+		interruptible_sleep_on(&cam->wq_dma);
+	}
+
+	/* we are done with dma */
+	flags=claim_dma_lock();
+	if(cam->dma_on) {
+		cam->dma_on=0;
+		interruptible_sleep_on(&cam->wq_dma);
+	}
+
+	disable_dma(channel);
+
+	parport_frob_econtrol(port,ECR_mode_mask, ECR_PS2_mode);
+	parport_frob_econtrol(port,ECR_serviceIntr, ECR_serviceIntr);
+	parport_frob_econtrol(port, ECR_dmaEn, 0);
+
+	release_dma_lock(flags);
+	return cam->readbytes;
+}
+#endif
+
+/****************************************************************************
+ *
+ * ECPReadBuffer (does termination) (stops when reading EOI)
+ *
+ ****************************************************************************/
+
+int ECPReadBuffer(struct pp_cam_entry *cam, u8 *buf, int bytes)
+{
+	struct parport *port = cam->port;
+	int readbytes;
+
+	cam->camstate = CPIA_PHASE_ecpread;
+
+	if (cam->state != CPIA_REVERSE) DBG("cam not in CPIA_REVERSE\n");
+	if (!GetControlMasked(port,DirBit)) DBG("parport is in forward mode, guess you won't be reading much\n");
+
+#ifdef CONFIG_VIDEO_CPIA_PP_DMA
+	if(cam->dma_buf) {
+		readbytes = ECPReadBuffer_DMA(cam, buf, bytes);
+	} else {
+#endif
+		readbytes = ECPReadBuffer_PIO(cam, buf, bytes);
+#ifdef CONFIG_VIDEO_CPIA_PP_DMA
+	}
+#endif
+
+	return readbytes;
+};
+
+/****************************************************************************
+ *
+ *  SimECPWriteBuffer (does termination)
+ *
+ ***************************************************************************/
+static int SimECPWriteBuffer(struct pp_cam_entry *cam, const u8 *buf, int size)
+{
+	int written = 0;
+	struct parport *port = cam->port;
+
+	cam->camstate = CPIA_PHASE_secpwrite;
+
+	if (cam->state != CPIA_FORWARD) DBG("cam not in CPIA_FORWARD\n");
+	if (GetControlMasked(port, DirBit))
+		DBG("parport is in reverse mode, "
+		    "guess you won't be writing much\n");
+
+	parport_write_data(port, *buf);
+	SetControlMasked(port, HostAck);
+	for (written = 0; written < size; written++) {
+		parport_write_data(port, *buf++);
+		ClearControlMasked(port, HostClk);
+		if (my_wait_peripheral(port, PeriphAck, PeriphAck)) {
+			DBG("PeriphAck never went up signaling read a byte\n");
+			break;
+		}
+		//DBG("written byte: %02x\n",*(u8*)(buf-1));
+		SetControlMasked(port, HostClk);
+		if (my_wait_peripheral(port, PeriphAck, 0)) {
+			DBG("PeriphAck never went down signaling ready for next byte\n");
+			break;
+		}
+	}
+
+	if (written != size)
+		DBG("failed; written %db should be %db\n", written, size);
+
+	return written;
+}
+
+/****************************************************************************
+ *
+ *  EndTransferMode
+ *
+ ***************************************************************************/
+static void EndTransferMode(struct pp_cam_entry *cam)
+{
+	if (!cam) return;
+	if (cam->state == CPIA_REVERSE)  Reverse2Forward(cam);
+	Valid1284Termination(cam);
+	if(cam->stream_irq) cpia_parport_enable_irq(cam->port);
+}
+
+/****************************************************************************
+ *
+ *  ForwardSetup
+ *
+ ***************************************************************************/
+static int ForwardSetup(struct pp_cam_entry *cam)
+{
+	if(cam->stream_irq) cpia_parport_disable_irq(cam->port);
+	if (!Negotiate2SetupPhase(cam, 0)) {
+		if (!ECPSetupPhase(cam)) {
+			return 0;
+		} else {
+			DBG("could not finish setup phase\n");
+		}
+	} else {
+		DBG("could not negotiate for setup phase\n");
+	}
+	EndTransferMode(cam);
+	return -1;
+}
+
+/****************************************************************************
+ *
+ *  ReverseSetup
+ *
+ ***************************************************************************/
+static int ReverseSetup(struct pp_cam_entry *cam, int extensibility)
+{
+	if(cam->stream_irq) cpia_parport_disable_irq(cam->port);
+	if (!Negotiate2SetupPhase(cam, extensibility)) {
+		if (!ECPSetupPhase(cam)) {
+			if (!Forward2Reverse(cam)) {
+				return 0;	/* don't terminate */
+			} else {
+				DBG("could not forward to reverse the port in order to read anything\n");
+			}
+		} else {
+			DBG("could not finish setup phase\n");
+		}
+	} else {
+		DBG("could not negotiate for setup phase\n");
+	}
+	EndTransferMode(cam);
+	return -1;
+}
+
+#ifdef CONFIG_VIDEO_CPIA_PP_DMA
+/****************************************************************************
+ *
+ *  DMA routines
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ *
+ *  Initiate a DMA read buffer
+ *
+ ****************************************************************************/
+
+static inline void receive_buf_dma(struct pp_cam_entry *cam, int num_bytes)
+{
+	int channel = cam->port->dma;
+
+	disable_dma(channel);
+	clear_dma_ff(channel);
+	set_dma_mode(channel, DMA_MODE_READ);
+	set_dma_addr(channel, virt_to_bus(cam->dma_buf));
+	set_dma_count(channel, num_bytes);
+	enable_dma(channel);
+
+	parport_frob_econtrol(cam->port,ECR_serviceIntr, 0);
+
+	return;
+}
+
+static inline void stop_receive_buf_dma(struct pp_cam_entry *cam)
+{
+	unsigned long flags;
+
+	flags=claim_dma_lock();
+	disable_dma(cam->port->dma);
+	cam->dma_on=0;
+
+	parport_frob_econtrol(cam->port,ECR_mode_mask, ECR_PS2_mode);
+	parport_frob_econtrol(cam->port,ECR_serviceIntr,
+                	      ECR_serviceIntr);
+	parport_frob_econtrol(cam->port, ECR_dmaEn, 0);
+
+	if( waitqueue_active(&cam->wq_dma) ) {
+        	wake_up_interruptible(&cam->wq_dma);
+        }
+	release_dma_lock(flags);
+
+	return;
+}
+
+/****************************************************************************
+ *
+ *  DMA bottom half
+ *
+ ****************************************************************************/
+
+static void dma_handler(void *handle)
+{
+	struct pp_cam_entry *cam = handle;
+	int i;
+	unsigned char *dma_ptr, *buf_ptr;
+	unsigned long flags;
+
+	flags=claim_dma_lock();
+	disable_dma(cam->port->dma);
+	parport_frob_econtrol(cam->port,ECR_serviceIntr,
+                	      ECR_serviceIntr);
+	release_dma_lock(flags);
+
+	cam->dma_intr--;
+	if(cam->dma_intr) {
+        	DBG("extra interrupt: %d\n", cam->dma_intr);
+        	stop_receive_buf_dma(cam);
+        	return;
+        }
+	if(cam->dma_on==0 || cam->image_complete) {
+        	return;
+        }
+	flags=claim_dma_lock();
+	clear_dma_ff(cam->port->dma);
+	if((i=get_dma_residue(cam->port->dma))!=0) {
+        	DBG("dma_residue: %d\n", i);
+        	stop_receive_buf_dma(cam);
+        	release_dma_lock(flags);
+        	return;
+        }
+	release_dma_lock(flags);
+	if((i=cam->readbytes+cam->block_size)>cam->bytes) {
+        	DBG("read too many bytes: %d/%d/%d\n",
+		    i, cam->readbytes, cam->bytes);
+        	stop_receive_buf_dma(cam);
+        	return;
+        }
+	dma_ptr=cam->dma_buf;
+	buf_ptr=cam->buf;
+	i=-1;
+	while(++i<cam->block_size && cam->endseen<4) {
+        	if(*dma_ptr==EOI) {
+                	cam->endseen++;
+                } else {
+                	cam->endseen=0;
+                }
+        	(*buf_ptr++)=(*dma_ptr++);
+        }
+	cam->readbytes+=i;
+	cam->buf+=i;
+	if( cam->endseen==4 ) {
+        	cam->image_complete=1;
+        	stop_receive_buf_dma(cam);
+        	return;
+        }
+
+	if( cam->bytes-cam->readbytes <= DMA_BUFFER_SIZE ) {
+		cam->block_size=cam->bytes-cam->readbytes;
+		cam->block_size=cam->block_size-(cam->block_size%2);
+        	if( cam->block_size==0 ) {
+                	stop_receive_buf_dma(cam);
+                	return;
+                }
+        }
+
+	if( cam->dma_on) {
+        	receive_buf_dma(cam, cam->block_size);
+        } else {
+        	stop_receive_buf_dma(cam);
+        }
+	return;
+}
+#endif
+
+/****************************************************************************
+ *
+ *  IRQ handler
+ *
+ ***************************************************************************/
+
+static void cpia_pp_irq_handler(int irq, void *handle, struct pt_regs *b)
+{
+	struct pp_cam_entry *cam = handle;
+	if (cam==NULL) {
+		return;
+	}
+#ifdef CONFIG_VIDEO_CPIA_PP_DMA
+	if(cam->dma_on) {
+		cam->dma_intr++;
+		if( cam->dma_intr == 1 ) {
+			queue_task(&cam->dma_task, &tq_immediate);
+			mark_bh(IMMEDIATE_BH);
+		} else {
+			DBG("%d\n", cam->dma_intr);
+		}
+		return;
+	}
+#endif
+	if (cam->camstate == CPIA_PHASE_ecpread) return;
+	if( cam->camstate != CPIA_PHASE_idle )
+		DBG("got IRQ(%d) when in %s\n",
+		    irq, camstatesstr[cam->camstate]);
+
+	cam->image_ready++;
+	if(cam->image_ready > 1) {
+		cam->image_ready=0;
+		DBG("image skipped?\n");
+	}
+	if(cam->cb_task.routine!=0) {
+		queue_task(&cam->cb_task, &tq_scheduler);
+	}
+
+	if(waitqueue_active(&cam->wq_stream)) {
+        	wake_up_interruptible(&cam->wq_stream);
+        }
+	return;
+}
+
+/****************************************************************************
+ *
+ *  WritePacket
+ *
+ ***************************************************************************/
+static int WritePacket(struct pp_cam_entry *cam, const u8 *packet, size_t size)
+{
+	int retval=0;
+	if (packet == NULL) {
+		return -EINVAL;
+	}
+	if (ForwardSetup(cam)) {
+		return -EIO;
+	}
+	if (SimECPWriteBuffer(cam, packet, size) != size) {
+		retval = -EIO;
+	}
+	EndTransferMode(cam);
+	return retval;
+}
+
+/****************************************************************************
+ *
+ *  ReadPacket
+ *
+ ***************************************************************************/
+static int ReadPacket(struct pp_cam_entry *cam, u8 *packet, size_t size)
+{
+	int retval=0;
+	if (packet == NULL) {
+		return -EINVAL;
+	}
+	if (ReverseSetup(cam, 0)) {
+		return -EIO;
+	}
+	if (SimECPReadBuffer(cam, packet, size) != size) {
+		retval = -EIO;
+	}
+	EndTransferMode(cam);
+	return retval;
+}
+
+/****************************************************************************
+ *
+ *  cpia_pp_streamStart
+ *
+ ***************************************************************************/
+static int cpia_pp_streamStart(void *privdata)
+{
+	struct pp_cam_entry *cam = privdata;
+	DBG("\n");
+	cam->streaming=1;
+	cam->image_ready=0;
+	//if (ReverseSetup(cam,1)) return -EIO;
+	if(cam->stream_irq) cpia_parport_enable_irq(cam->port);
+
+	return 0;
+}
+
+/****************************************************************************
+ *
+ *  cpia_pp_streamStop
+ *
+ ***************************************************************************/
+static int cpia_pp_streamStop(void *privdata)
+{
+	struct pp_cam_entry *cam = privdata;
+
+	DBG("\n");
+	cam->streaming=0;
+	cpia_parport_disable_irq(cam->port);
+	//EndTransferMode(cam);
+
+	return 0;
+}
+
+/****************************************************************************
+ *
+ *  cpia_pp_streamRead
+ *
+ ***************************************************************************/
+static int cpia_pp_streamRead(void *privdata, u8 *buffer, int noblock)
+{
+	struct pp_cam_entry *cam = privdata;
+	int read_bytes = 0;
+	if(cam == NULL) {
+		DBG("Internal driver error: cam is NULL\n");
+		return -EINVAL;
+	}
+	if(buffer == NULL) {
+		DBG("Internal driver error: buffer is NULL\n");
+		return -EINVAL;
+	}
+	//if(cam->streaming) DBG("%d / %d\n", cam->image_ready, noblock);
+	if( cam->stream_irq ) {
+		DBG("%d\n", cam->image_ready);
+		cam->image_ready--;
+	}
+	cam->image_complete=0;
+	if (0/*cam->streaming*/) {
+		if(!cam->image_ready) {
+			if(noblock) return -EWOULDBLOCK;
+			interruptible_sleep_on(&cam->wq_stream);
+			if( signal_pending(current) ) return -EINTR;
+			DBG("%d\n", cam->image_ready);
+		}
+	} else {
+		if (ReverseSetup(cam, 1)) return -EIO;
+	}
+	read_bytes = ECPReadBuffer(cam, buffer, CPIA_MAX_IMAGE_SIZE);
+	if( 1/*!cam->streaming*/) {
+		EndTransferMode(cam);
+	}
+	if(!cam->image_complete) {
+		if( !signal_pending(current) )
+			DBG("incomplete image: %ld / %d / %d\n",
+			    jiffies, cam->image_complete, read_bytes);
+	}
+	return cam->image_complete ? read_bytes : -EIO;
+}
+
+/****************************************************************************
+ *
+ *  cpia_pp_transferCmd
+ *
+ ***************************************************************************/
+static int cpia_pp_transferCmd(void *privdata, u8 *command, u8 *data)
+{
+	int err;
+	int retval=0;
+	int databytes;
+	struct pp_cam_entry *cam = privdata;
+	if(cam == NULL) {
+		DBG("Internal driver error: cam is NULL\n");
+		return -EINVAL;
+	}
+	if(command == NULL) {
+		DBG("Internal driver error: command is NULL\n");
+		return -EINVAL;
+	}
+	databytes = (((int)command[7])<<8) | command[6];
+	if ((err = WritePacket(cam, command, PACKET_LENGTH)) < 0) {
+		return err;
+	}
+	if(command[0] == DATA_IN) {
+		u8 buffer[8];
+		if(data == NULL) {
+			DBG("Internal driver error: data is NULL\n");
+			return -EINVAL;
+		}
+		err = ReadPacket(cam, buffer, 8);
+		if(err < 0) {
+			return err;
+		}
+		memcpy(data, buffer, databytes);
+	} else if(command[0] == DATA_OUT) {
+		if(databytes > 0) {
+			if(data == NULL) {
+				DBG("Internal driver error: data is NULL\n");
+				retval = -EINVAL;
+			} else {
+				WritePacket(cam, data, databytes);
+			}
+		}
+	} else {
+		DBG("Unexpected first byte of command: %x\n", command[0]);
+		retval = -EINVAL;
+	}
+	return retval;
+}
+
+/****************************************************************************
+ *
+ *  cpia_pp_open
+ *
+ ***************************************************************************/
+static int cpia_pp_open(int camnr, void **privdata)
+{
+	struct pp_cam_entry *cam=NULL;
+	int i;
+	for(i=0; i<PP_MAXCAMS; ++i) {
+		cam=camera[i];
+		if(cam == NULL) {
+			DBG("Trying to open non-existent camera[%d]\n", camnr);
+			return -ENXIO;
+		}
+		if(cam->camnr == camnr) {
+			DBG("Found camera[%d]\n", camnr);
+			break;
+		}
+	}
+	
+	*privdata = cam;
+	
+	if(cam->open_count == 0) {
+		if (parport_claim(cam->pdev)) {
+			DBG("failed to claim the port\n");
+			return -EBUSY;
+		}
+		parport_write_econtrol(cam->port, PARPORT_MODE_PCECR);
+		parport_frob_econtrol(cam->port,
+		                      ECR_serviceIntr|ECR_dmaEn|ECR_nErrIntrEn,
+		                      ECR_serviceIntr|ECR_nErrIntrEn);
+#ifdef CONFIG_VIDEO_CPIA_PP_DMA
+		cam->dma_task.routine=dma_handler;
+		cam->dma_task.data=cam;
+#endif
+		cam->stream_irq=0;
+		cpia_parport_disable_irq(cam->port);
+	}
+	
+	++cam->open_count;
+	
+#ifdef MODULE
+	MOD_INC_USE_COUNT;
+#endif
+	return 0;
+}
+
+/****************************************************************************
+ *
+ *  cpia_pp_registerCallback
+ *
+ ***************************************************************************/
+static int cpia_pp_registerCallback(void *privdata, void (*cb)(void *cbdata), void *cbdata)
+{
+	struct pp_cam_entry *cam = privdata;
+	int retval = 0;
+	
+	if(cam->port->irq != PARPORT_IRQ_NONE) {
+		cam->cb_task.routine = cb;
+		cam->cb_task.data = cbdata;
+	} else {
+		retval = -1;
+	}
+	return retval;
+}
+
+/****************************************************************************
+ *
+ *  cpia_pp_close
+ *
+ ***************************************************************************/
+static int cpia_pp_close(void *privdata)
+{
+	struct pp_cam_entry *cam = privdata;
+#ifdef CONFIG_VIDEO_CPIA_PP_DMA
+	unsigned int flags;
+#endif
+#ifdef MODULE
+	MOD_DEC_USE_COUNT;
+#endif
+	if (--cam->open_count == 0) {
+		if (cam->port->irq != PARPORT_IRQ_NONE) {
+			cpia_parport_disable_irq(cam->port);
+		}
+		
+#ifdef CONFIG_VIDEO_CPIA_PP_DMA
+		save_flags(flags);
+		cli();
+		if (cam->dma_on) {
+			cam->dma_on=0;
+			interruptible_sleep_on(&cam->wq_dma);
+		}
+		restore_flags(flags);
+#endif
+		if (waitqueue_active(&cam->wq_stream)) { /* FIXME */
+			wake_up(&cam->wq_stream);
+		}
+		
+		parport_release(cam->pdev);
+	}
+	return 0;
+}
+
+/****************************************************************************
+ *
+ *  cpia_pp_register
+ *
+ ***************************************************************************/
+static int cpia_pp_register(int nr, struct parport *port)
+{
+	struct pardevice *pdev = NULL;
+	int camnr;
+
+	if (!(port->modes & PARPORT_MODE_PCECP)) {
+		LOG("port is not ECP capable\n");
+		return -ENXIO;
+	}
+
+	if((camnr = cpia_register_camera(&cpia_pp_ops)) < 0) {
+		LOG("failed to cpia_register_camera\n");
+		return -ENXIO;
+	}
+	camera[nr] = kmalloc(sizeof(struct pp_cam_entry), GFP_KERNEL);
+	if (camera[nr] == NULL) {
+		LOG("failed to allocate camera structure\n");
+		cpia_unregister_camera(camnr);
+		return -ENOMEM;
+	}
+	memset(camera[nr],0,sizeof(struct pp_cam_entry));
+	
+	pdev = parport_register_device(port, "cpia_pp", NULL, NULL,
+	                               cpia_pp_irq_handler, 0, camera[nr]);
+
+	if (!pdev) {
+		LOG("failed to parport_register_device\n");
+		cpia_unregister_camera(camnr);
+		kfree(camera[nr]);
+		return -ENXIO;
+	}
+
+	camera[nr]->pdev = pdev;
+	camera[nr]->port = port;
+	camera[nr]->state = CPIA_FORWARD;
+	camera[nr]->camstate = CPIA_PHASE_idle;
+	camera[nr]->camnr = camnr;
+	init_waitqueue(&camera[nr]->wq_stream);
+
+	camera[nr]->streaming = 0;
+	camera[nr]->stream_irq = 0;
+
+#ifdef CONFIG_VIDEO_CPIA_PP_DMA
+	if (pdev->port->irq != PARPORT_IRQ_NONE &&
+	    pdev->port->dma != PARPORT_DMA_NONE) {
+		if(request_dma(pdev->port->dma, "cpia_pp")) {
+			LOG("failed to register dma %d\n", pdev->port->dma);
+			parport_unregister_device(pdev);
+			cpia_unregister_camera(camnr);
+			kfree(camera[nr]);
+			return -ENXIO;
+		}
+		camera[nr]->dma_buf=kmalloc(DMA_BUFFER_SIZE, GFP_DMA);
+		if(camera[nr]->dma_buf == NULL) {
+			free_dma(camera[nr]->pdev->port->dma);
+			LOG("failed to allocate dma buffer, using FIFO mode\n");
+		} else {
+			init_waitqueue(&camera[nr]->wq_dma);
+			printk(KERN_INFO "  using DMA mode (irq %d, DMA %d)\n", pdev->port->irq, pdev->port->dma);
+		}
+		memset(camera[nr]->dma_buf, 0, DMA_BUFFER_SIZE);
+	} else {
+		printk(KERN_INFO "  using PIO mode\n");
+	}
+#endif
+
+	return 0;
+}
+
+int cpia_pp_init(void)
+{
+	struct parport *port;
+	int i, count = 0;
+
+	printk(KERN_INFO "%s v%d.%d.%d\n",ABOUT, 
+	       CPIA_PP_MAJ_VER,CPIA_PP_MIN_VER,CPIA_PP_PATCH_VER);
+
+	switch (parport_nr[0]) {
+	case PPCPIA_PARPORT_OFF:
+		printk("  disabled\n");
+		return 0;
+
+	case PPCPIA_PARPORT_UNSPEC:
+	case PPCPIA_PARPORT_AUTO:
+		for (port = parport_enumerate(); port; port = port->next) {
+
+#if defined(CONFIG_PNP_PARPORT) || \
+    (defined(MODULE) && defined(CONFIG_PNP_PARPORT_MODULE))
+			if (port->probe_info.class != PARPORT_CLASS_MEDIA ||
+			    port->probe_info.cmdset == NULL ||
+			    strncmp(port->probe_info.cmdset, "CPIA_1", 6) != 0){
+				continue;
+			}
+#endif
+
+			if (cpia_pp_register(count, port) == 0 &&
+			    ++count == PP_MAXCAMS) {
+				break;
+			}
+		}
+		break;
+
+	default:
+		for (i = 0; i < PP_MAXCAMS; i++) {
+			for (port = parport_enumerate(); port;
+			     port = port->next) {
+				if (port->number == parport_nr[i]) {
+					if (!cpia_pp_register(i, port)) {
+						count++;
+					}
+					break;
+				}
+			}
+		}
+		break;
+	}
+
+	printk("  %d camera(s) found\n", count);
+	if (count == 0) {
+		return -ENODEV;
+	}
+	return 0;
+}
+
+#ifdef MODULE
+int init_module(void)
+{
+	if (parport[0]) {
+		/* The user gave some parameters.  Let's see what they were. */
+		if (!strncmp(parport[0], "auto", 4)) {
+			parport_nr[0] = PPCPIA_PARPORT_AUTO;
+		} else {
+			int n;
+			for (n = 0; n < PP_MAXCAMS && parport[n]; n++) {
+				if (!strncmp(parport[n], "none", 4)) {
+					parport_nr[n] = PPCPIA_PARPORT_NONE;
+				} else {
+					char *ep;
+					unsigned long r = simple_strtoul(parport[n], &ep, 0);
+					if (ep != parport[n]) {
+						parport_nr[n] = r;
+					} else {
+						LOG("bad port specifier `%s'\n", parport[n]);
+						return -ENODEV;
+					}
+				}
+			}
+		}
+	}
+#if defined(CONFIG_KMOD) && defined(CONFIG_PNP_PARPORT_MODULE)
+	request_module("parport_probe");
+#endif
+	return cpia_pp_init();
+}
+
+void cleanup_module(void)
+{
+	int i;
+	for (i = 0; camera[i] != NULL && i < PP_MAXCAMS; i++) {
+		while(camera[i]->open_count > 0) {
+			LOG("You forgot to close camera %d, will do it for you\n", camera[i]->camnr);
+			cpia_pp_close(camera[i]);
+		}
+		
+#ifdef CONFIG_VIDEO_CPIA_PP_DMA
+		if (camera[i]->dma_buf) {
+			free_dma(camera[i]->pdev->port->dma);
+			kfree(camera[i]->dma_buf);
+		}
+#endif
+
+		cpia_unregister_camera(camera[i]->camnr);
+		parport_unregister_device(camera[i]->pdev);
+		kfree(camera[i]);
+	}
+	return;
+}
+
+#else /* !MODULE */
+
+__initfunc(void cpia_pp_setup(char *str, int *ints))
+{
+	if (!str) {
+		if (ints[0] == 0 || ints[1] == 0) {
+			/* disable driver on "cpia_pp=" or "cpia_pp=0" */
+			parport_nr[0] = PPCPIA_PARPORT_OFF;
+		}
+	} else if (!strncmp(str, "parport", 7)) {
+		int n = simple_strtoul(str + 7, NULL, 10);
+		if (parport_ptr < PP_MAXCAMS) {
+			parport_nr[parport_ptr++] = n;
+		} else {
+			LOG("too many ports, %s ignored.\n", str);
+		}
+	} else if (!strcmp(str, "auto")) {
+		parport_nr[0] = PPCPIA_PARPORT_AUTO;
+	} else if (!strcmp(str, "none")) {
+		parport_nr[parport_ptr++] = PPCPIA_PARPORT_NONE;
+	}
+}
+
+#endif /* !MODULE */
diff -urN linux-2.2.15-pre5/drivers/char/istallion.c linux/drivers/char/istallion.c
--- linux-2.2.15-pre5/drivers/char/istallion.c	Fri Apr 16 08:20:23 1999
+++ linux/drivers/char/istallion.c	Mon Dec  3 21:58:46 2001
@@ -3,8 +3,8 @@
 /*
  *	istallion.c  -- stallion intelligent multiport serial driver.
  *
- *	Copyright (C) 1996-1999  Stallion Technologies (support@stallion.oz.au).
- *	Copyright (C) 1994-1996  Greg Ungerer (gerg@stallion.oz.au).
+ *	Copyright (C) 1996-2000  Stallion Technologies (support@stallion.oz.au)
+ *	Copyright (C) 1994-1996  Greg Ungerer.
  *
  *	This code is loosely based on the Linux serial driver, written by
  *	Linus Torvalds, Theodore T'so and others.
@@ -167,7 +167,7 @@
  */
 static char	*stli_drvtitle = "Stallion Intelligent Multiport Serial Driver";
 static char	*stli_drvname = "istallion";
-static char	*stli_drvversion = "5.5.1";
+static char	*stli_drvversion = "5.6.0";
 static char	*stli_serialname = "ttyE";
 static char	*stli_calloutname = "cue";
 
@@ -383,7 +383,7 @@
 /*
  *	Define the module agruments.
  */
-MODULE_AUTHOR("Greg Ungerer");
+MODULE_AUTHOR("Stallion Technologies (support@stallion.oz.au)");
 MODULE_DESCRIPTION("Stallion Intelligent Multiport Serial Driver");
 
 MODULE_PARM(board0, "1-3s");
@@ -711,6 +711,8 @@
 static int	stli_getportstats(stliport_t *portp, comstats_t *cp);
 static int	stli_portcmdstats(stliport_t *portp);
 static int	stli_clrportstats(stliport_t *portp, comstats_t *cp);
+static int	stli_geticounters(stliport_t *portp,
+				  struct serial_icounter_struct *cp);
 static int	stli_getportstruct(unsigned long arg);
 static int	stli_getbrdstruct(unsigned long arg);
 static void	*stli_memalloc(int len);
@@ -2170,6 +2172,13 @@
 		    sizeof(comstats_t))) == 0)
 			rc = stli_clrportstats(portp, (comstats_t *) arg);
 		break;
+	case TIOCGICOUNT:
+		if ((rc = verify_area(VERIFY_WRITE, (void *) arg,
+		    sizeof(struct serial_icounter_struct *))) == 0)
+			rc = stli_geticounters(portp,
+				      (struct serial_icounter_struct *) arg);
+		break;
+
 	case TIOCSERCONFIG:
 	case TIOCSERGWILD:
 	case TIOCSERSWILD:
@@ -3375,6 +3384,9 @@
 		portp->closing_wait = 30 * HZ;
 		portp->tqhangup.routine = stli_dohangup;
 		portp->tqhangup.data = portp;
+		portp->open_wait = 0;
+		portp->close_wait = 0;
+		portp->raw_wait = 0;
 		portp->normaltermios = stli_deftermios;
 		portp->callouttermios = stli_deftermios;
 		panelport++;
@@ -5138,6 +5150,33 @@
 
 	copy_to_user(cp, &stli_comstats, sizeof(comstats_t));
 	return(0);
+}
+
+static int stli_geticounters(stliport_t *portp,
+			     struct serial_icounter_struct *icp)
+{
+	struct serial_icounter_struct icount;
+	comstats_t *s	= &stli_comstats;
+	int rc;
+	
+	if ((rc = stli_portcmdstats(portp)) < 0)
+		return(rc);
+	
+	memset(&icount, 0, sizeof(icount));
+	
+	/*
+	 * we only support a subset of the icounters - the others are handled
+	 * by the uart and we don't get interrupted for them
+	 */
+	icount.dcd	= s->modem;
+	icount.rx	= s->rxtotal;
+	icount.tx	= s->txtotal;
+	icount.frame	= s->rxframing;
+	icount.overrun	= s->rxoverrun;
+	icount.parity	= s->rxparity;
+	icount.brk	= s->rxbreaks;
+	
+	return (copy_to_user(icp, &icount, sizeof(icount)));
 }
 
 /*****************************************************************************/
diff -urN linux-2.2.15-pre5/drivers/char/joystick/joy-creative.c linux/drivers/char/joystick/joy-creative.c
--- linux-2.2.15-pre5/drivers/char/joystick/joy-creative.c	Tue Jan  4 10:12:14 2000
+++ linux/drivers/char/joystick/joy-creative.c	Mon Dec  3 21:58:46 2001
@@ -66,7 +66,7 @@
 	int r[2], t[2], p[2];
 	int i, j, ret;
 
-	for (i = 0; i < 2; i++); {
+	for (i = 0; i < 2; i++) {
 		r[i] = buf[i] = 0;
 		p[i] = t[i] = JS_CR_MAX_STROBE;
 		p[i] += JS_CR_MAX_STROBE;
diff -urN linux-2.2.15-pre5/drivers/char/stallion.c linux/drivers/char/stallion.c
--- linux-2.2.15-pre5/drivers/char/stallion.c	Fri Apr 16 08:20:23 1999
+++ linux/drivers/char/stallion.c	Mon Dec  3 21:58:46 2001
@@ -3,8 +3,8 @@
 /*
  *	stallion.c  -- stallion multiport serial driver.
  *
- *	Copyright (C) 1996-1999  Stallion Technologies (support@stallion.oz.au).
- *	Copyright (C) 1994-1996  Greg Ungerer (gerg@stallion.oz.au).
+ *	Copyright (C) 1996-2000  Stallion Technologies (support@stallion.oz.au)
+ *	Copyright (C) 1994-1996  Greg Ungerer.
  *
  *	This code is loosely based on the Linux serial driver, written by
  *	Linus Torvalds, Theodore T'so and others.
@@ -28,6 +28,7 @@
 
 #include <linux/config.h>
 #include <linux/module.h>
+#include <linux/version.h>
 #include <linux/malloc.h>
 #include <linux/interrupt.h>
 #include <linux/tty_flip.h>
@@ -47,6 +48,7 @@
 #include <linux/pci.h>
 #endif
 
+
 /*****************************************************************************/
 
 /*
@@ -127,6 +129,7 @@
 #define	STL_TXBUFLOW		512
 #define	STL_TXBUFSIZE		4096
 
+#define	STL_IRQ_DEVID		(&stl_brds[0])
 /*****************************************************************************/
 
 /*
@@ -135,7 +138,7 @@
  */
 static char	*stl_drvtitle = "Stallion Multiport Serial Driver";
 static char	*stl_drvname = "stallion";
-static char	*stl_drvversion = "5.5.1";
+static char	*stl_drvversion = "5.6.0";
 static char	*stl_serialname = "ttyE";
 static char	*stl_calloutname = "cue";
 
@@ -313,7 +316,7 @@
 /*
  *	Define the module agruments.
  */
-MODULE_AUTHOR("Greg Ungerer");
+MODULE_AUTHOR("Stallion Technologies (support@stallion.oz.au)");
 MODULE_DESCRIPTION("Stallion Multiport Serial Driver");
 
 MODULE_PARM(board0, "1-4s");
@@ -345,6 +348,7 @@
 #define	ID_BRD4		0x10
 #define	ID_BRD8		0x20
 #define	ID_BRD16	0x30
+#define	ID_BRD4_422     0x40    /* 4 port EIO supporting RS422*/
 
 #define	EIO_INTRPEND	0x08
 #define	EIO_INTEDGE	0x00
@@ -523,6 +527,8 @@
 static int	stl_getbrdstats(combrd_t *bp);
 static int	stl_getportstats(stlport_t *portp, comstats_t *cp);
 static int	stl_clrportstats(stlport_t *portp, comstats_t *cp);
+static int	stl_geticounters(stlport_t *portp,
+				 struct serial_icounter_struct *cp);
 static int	stl_getportstruct(unsigned long arg);
 static int	stl_getbrdstruct(unsigned long arg);
 static int	stl_waitcarrier(stlport_t *portp, struct file *filp);
@@ -853,7 +859,7 @@
 	}
 
 	for (i = 0; (i < stl_numintrs); i++)
-		free_irq(stl_gotintrs[i], NULL);
+		free_irq(stl_gotintrs[i], STL_IRQ_DEVID);
 
 	restore_flags(flags);
 }
@@ -1599,7 +1605,8 @@
 		return(-ENODEV);
 
 	if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) &&
- 	    (cmd != COM_GETPORTSTATS) && (cmd != COM_CLRPORTSTATS)) {
+ 	    (cmd != COM_GETPORTSTATS) && (cmd != COM_CLRPORTSTATS) &&
+	    (cmd != TIOCGICOUNT)) {
 		if (tty->flags & (1 << TTY_IO_ERROR))
 			return(-EIO);
 	}
@@ -1671,6 +1678,13 @@
 		    sizeof(comstats_t))) == 0)
 			rc = stl_clrportstats(portp, (comstats_t *) arg);
 		break;
+	case TIOCGICOUNT:
+		if ((rc = verify_area(VERIFY_WRITE, (void *) arg,
+		    sizeof(struct serial_icounter_struct *))) == 0)
+			rc = stl_geticounters(portp,
+				      (struct serial_icounter_struct *) arg);
+		break;
+		
 	case TIOCSERCONFIG:
 	case TIOCSERGWILD:
 	case TIOCSERSWILD:
@@ -2294,7 +2308,8 @@
 			break;
 	}
 	if (i >= stl_numintrs) {
-		if (request_irq(irq, stl_intr, SA_INTERRUPT, name, NULL) != 0) {
+		if (request_irq(irq, stl_intr, SA_SHIRQ, name,
+				STL_IRQ_DEVID) != 0) {
 			printk("STALLION: failed to register interrupt "
 				"routine for %s irq=%d\n", name, irq);
 			rc = -ENODEV;
@@ -2348,6 +2363,8 @@
 		portp->callouttermios = stl_deftermios;
 		portp->tqueue.routine = stl_offintr;
 		portp->tqueue.data = portp;
+		portp->open_wait = 0;
+		portp->close_wait = 0;
 		portp->stats.brd = portp->brdnr;
 		portp->stats.panel = portp->panelnr;
 		portp->stats.port = portp->portnr;
@@ -2445,6 +2462,9 @@
 		case ID_BRD16:
 			brdp->nrports = 16;
 			break;
+		case ID_BRD4_422:
+			brdp->nrports = 4;
+			break;
 		default:
 			return(-ENODEV);
 		}
@@ -2780,7 +2800,7 @@
 
 #if DEBUG
 	printk("stl_initpcibrd(brdtype=%d,busnr=%x,devnr=%x)\n", brdtype,
-		dev->bus->number, dev->devfn);
+		devp->bus->number, devp->devfn);
 #endif
 
 	if ((brdp = stl_allocbrd()) == (stlbrd_t *) NULL)
@@ -3084,6 +3104,28 @@
 	return(0);
 }
 
+static int stl_geticounters(stlport_t *portp,
+			    struct serial_icounter_struct *icp)
+{
+	struct serial_icounter_struct icount;
+	comstats_t *s	= &portp->stats;
+	
+	memset(&icount, 0, sizeof(icount));
+	/*
+	 * we only support a subset of the icounters - the others are handled
+	 * by the uart and we don't get interrupted for them
+	 */
+	icount.dcd	= s->modem;
+	icount.rx	= s->rxtotal;
+	icount.tx	= s->txtotal;
+	icount.frame	= s->rxframing;
+	icount.overrun	= s->rxoverrun;
+	icount.parity	= s->rxparity;
+	icount.brk	= s->rxbreaks;
+	
+	return (copy_to_user(icp, &icount, sizeof(icount)));
+}
+
 /*****************************************************************************/
 
 /*
@@ -4209,6 +4251,7 @@
 		if ((tty == (struct tty_struct *) NULL) ||
 		    (tty->flip.char_buf_ptr == (char *) NULL) ||
 		    ((buflen = TTY_FLIPBUF_SIZE - tty->flip.count) == 0)) {
+			len = MIN(len, sizeof(stl_unwanted));
 			outb((RDSR + portp->uartaddr), ioaddr);
 			insb((ioaddr + EREG_DATA), &stl_unwanted[0], len);
 			portp->stats.rxlost += len;
@@ -5175,6 +5218,7 @@
 		if ((tty == (struct tty_struct *) NULL) ||
 		    (tty->flip.char_buf_ptr == (char *) NULL) ||
 		    ((buflen = TTY_FLIPBUF_SIZE - tty->flip.count) == 0)) {
+			len = MIN(len, sizeof(stl_unwanted));
 			outb(GRXFIFO, (ioaddr + XP_ADDR));
 			insb((ioaddr + XP_DATA), &stl_unwanted[0], len);
 			portp->stats.rxlost += len;
diff -urN linux-2.2.15-pre5/drivers/char/videodev.c linux/drivers/char/videodev.c
--- linux-2.2.15-pre5/drivers/char/videodev.c	Tue Oct 26 17:53:40 1999
+++ linux/drivers/char/videodev.c	Mon Dec  3 21:58:46 2001
@@ -54,6 +54,9 @@
 #ifdef CONFIG_VIDEO_BWQCAM
 extern int init_bw_qcams(struct video_init *);
 #endif
+#ifdef CONFIG_VIDEO_CPIA
+extern int cpia_init(struct video_init *);
+#endif
 #ifdef CONFIG_VIDEO_PLANB
 extern int init_planbs(struct video_init *);
 #endif
@@ -104,6 +107,9 @@
 #endif	
 #ifdef CONFIG_VIDEO_BWQCAM
 	{"bw-qcam", init_bw_qcams},
+#endif        
+#ifdef CONFIG_VIDEO_CPIA
+        {"cpia", cpia_init},
 #endif	
 #ifdef CONFIG_VIDEO_PMS
 	{"PMS", init_pms_cards}, 
diff -urN linux-2.2.15-pre5/drivers/net/smc-mca.c linux/drivers/net/smc-mca.c
--- linux-2.2.15-pre5/drivers/net/smc-mca.c	Mon Dec  3 21:58:41 2001
+++ linux/drivers/net/smc-mca.c	Mon Dec  3 21:58:47 2001
@@ -1,4 +1,4 @@
-/* smc-ultra.c: A SMC Ultra ethernet driver for linux. */
+/* smc-mca.c: A SMC Ultra ethernet driver for linux. */
 /*
     Most of this driver, except for ultramca_probe is nearly
     verbatim from smc-ultra.c by Donald Becker. The rest is
@@ -8,7 +8,7 @@
 
     This driver uses the cards in the 8390-compatible, shared memory mode.
     Most of the run-time complexity is handled by the generic code in
-    8390.c.  The code in this file is responsible for
+    8390.c.
 
     This driver enables the shared memory only when doing the actual data
     transfers to avoid a bug in early version of the card that corrupted
@@ -20,10 +20,21 @@
 
     Changelog:
 
-    Paul Gortmaker  : multiple card support for module users.
-    David Weis      : Micro Channel-ized it.
-    David Monro     : More MCA support 19/01/2000 davidm@amberdata.demon.co.uk
-
+    Paul Gortmaker	 : multiple card support for module users.
+    David Weis		 : Micro Channel-ized it.
+    Tom Sightler	 : Added support for IBM PS/2 Ethernet Adapter/A
+    Christopher Turcksin : Changed MCA-probe so that multiple adapters are
+			   found correctly (Jul 16, 1997)
+    Chris Beauregard	 : Tried to merge the two changes above (Dec 15, 1997)
+    Tom Sightler	 : Fixed minor detection bug caused by above merge
+    Tom Sightler	 : Added support for three more Western Digital
+			   MCA-adapters
+    Tom Sightler	 : Added support for 2.2.x mca_find_unused_adapter
+    Hartmut Schmidt	 : - Modified parameter detection to handle each
+			     card differently depending on a switch-list
+			   - 'card_ver' removed from the adapter list
+			   - Some minor bug fixes
+    David Monro		 : Backport from 2.3 to 2.2, other minor fixes.
 */
 
 
@@ -67,64 +78,183 @@
 #define ULTRA_IO_EXTENT 32
 #define EN0_ERWCNT      0x08  /* Early receive warning count. */
 
+#define _61c8_SMC_Ethercard_PLUS_Elite_A_BNC_AUI_WD8013EP_A            0
+#define _61c9_SMC_Ethercard_PLUS_Elite_A_UTP_AUI_WD8013EP_A            1
+#define _6fc0_WD_Ethercard_PLUS_A_WD8003E_A_OR_WD8003ET_A              2
+#define _6fc1_WD_Starcard_PLUS_A_WD8003ST_A                            3
+#define _6fc2_WD_Ethercard_PLUS_10T_A_WD8003W_A                        4
+#define _efd4_IBM_PS2_Adapter_A_for_Ethernet_UTP_AUI_WD8013WP_A        5
+#define _efd5_IBM_PS2_Adapter_A_for_Ethernet_BNC_AUI_WD8013WP_A        6
+#define _efe5_IBM_PS2_Adapter_A_for_Ethernet                           7
+
+struct smc_mca_adapters_t {
+	unsigned int id;
+	char *name;
+};
+
+const struct smc_mca_adapters_t smc_mca_adapters[] = {
+    { 0x61c8, "SMC Ethercard PLUS Elite/A BNC/AUI (WD8013EP/A)" },
+    { 0x61c9, "SMC Ethercard PLUS Elite/A UTP/AUI (WD8013WP/A)" },
+    { 0x6fc0, "WD Ethercard PLUS/A (WD8003E/A or WD8003ET/A)" },
+    { 0x6fc1, "WD Starcard PLUS/A (WD8003ST/A)" },
+    { 0x6fc2, "WD Ethercard PLUS 10T/A (WD8003W/A)" },
+    { 0xefd4, "IBM PS/2 Adapter/A for Ethernet UTP/AUI (WD8013WP/A)" },
+    { 0xefd5, "IBM PS/2 Adapter/A for Ethernet BNC/AUI (WD8013EP/A)" },
+    { 0xefe5, "IBM PS/2 Adapter/A for Ethernet" },
+    { 0x0000, NULL }
+};
 
-__initfunc(int ultramca_probe(struct device *dev))
+int __init ultramca_probe(struct device *dev)
 {
 	unsigned short ioaddr;
 	unsigned char reg4, num_pages;
-	char slot;
-	unsigned char pos2, pos3, pos4, pos5;
-	int i;
-	char *adapter_name;
-	struct ei_device *ei_local;
-
-	/* Look for five types of adapters - thanks to
-	 * Zunfire@mail.bip.net for the IDs. */
-	if( (slot=mca_find_adapter(0x61c8,0)) != MCA_NOTFOUND)
-	{
-		adapter_name = "SMC/WD EtherCard PLUS Elite/A (8013EP/A)";
-	}
-	else if( (slot=mca_find_adapter(0x61c9,0)) != MCA_NOTFOUND)
-	{
-		adapter_name = "SMC/WD EtherCard PLUS Elite 10T/A (8013WP/A)";
+	char slot = -1;
+	unsigned char pos2 = 0xff, pos3 = 0xff, pos4 = 0xff, pos5 = 0xff;
+	int i, j;
+	int adapter_found = 0;
+	int adapter = 0;
+	int tbase = 0;
+	int tirq = 0;
+	int base_addr = dev ? dev->base_addr : 0;
+	int irq = dev ? dev->irq : 0;
+
+	if (!MCA_bus) {
+		return ENODEV;
+	}
+
+	/* This is a hack, snd should go away when the "0xffe0 means
+	 * don't do ISA probe" code goes away. We aren't ISA, and 0xffe0
+	 * is not a possible port for us.
+	 */
+	if (base_addr == 0xffe0) {
+		base_addr = 0;
 	}
-	else if( (slot=mca_find_adapter(0xefd4,0)) != MCA_NOTFOUND)
-	{
-		adapter_name = "IBM Adapter/A for Ethernet UTP/AUI (8013WP/A)";
+	if (base_addr || irq) {
+		printk(KERN_INFO "Probing for SMC MCA adapter");
+		if (base_addr) {
+			printk(" at I/O address 0x%04x%c",
+			       base_addr, irq ? ' ' : '\n');
+		}
+		if (irq) {
+			printk("using irq %d\n", irq);
+		}
 	}
-	else if( (slot=mca_find_adapter(0xefd5,0)) != MCA_NOTFOUND)
-	{
-		adapter_name = "IBM Adapter/A for Ethernet BNC/AUI (8013EP/A)";
+
+        /* proper multicard detection by ZP Gu (zpg@castle.net) */
+
+	for (j = 0; (smc_mca_adapters[j].name != NULL) && !adapter_found; j++) {
+		slot = mca_find_unused_adapter(smc_mca_adapters[j].id, 0);
+
+		while((slot != MCA_NOTFOUND) && !adapter_found) {
+			tirq = 0;
+			tbase = 0;
+
+                        /* If we're trying to match a specificied irq or
+			 * io address, we'll reject the adapter
+			 * found unless it's the one we're looking for
+			 */
+
+			pos2 = mca_read_stored_pos(slot, 2); /* io_addr */
+			pos3 = mca_read_stored_pos(slot, 3); /* shared mem */
+			pos4 = mca_read_stored_pos(slot, 4); /* ROM bios addr
+							      * range */
+			pos5 = mca_read_stored_pos(slot, 5); /* irq, media
+							      * and RIPL */
+
+			/* Test the following conditions:
+			 * - If an irq parameter is supplied, compare it
+			 *   with the irq of the adapter we found
+			 * - If a base_addr paramater is given, compare it
+			 *   with the base_addr of the adapter we found
+			 * - Check that the irq and the base_addr of the
+			 *   adapter we found is not already in use by
+			 *   this driver
+			 */
+			switch (j) { /* j = card-idx (card array above) [hs] */
+				case _61c8_SMC_Ethercard_PLUS_Elite_A_BNC_AUI_WD8013EP_A:
+		                case _61c9_SMC_Ethercard_PLUS_Elite_A_UTP_AUI_WD8013EP_A:
+				case _efd4_IBM_PS2_Adapter_A_for_Ethernet_UTP_AUI_WD8013WP_A:
+				case _efd5_IBM_PS2_Adapter_A_for_Ethernet_BNC_AUI_WD8013WP_A:
+				{
+					tbase = addr_table[(pos2 & 0xf0) >> 4].base_addr;
+					tirq  = irq_table[(pos5 & 0xc) >> 2].new_irq;
+					break;
+				}
+				case _6fc0_WD_Ethercard_PLUS_A_WD8003E_A_OR_WD8003ET_A:
+				case _6fc1_WD_Starcard_PLUS_A_WD8003ST_A:
+				case _6fc2_WD_Ethercard_PLUS_10T_A_WD8003W_A:
+				case _efe5_IBM_PS2_Adapter_A_for_Ethernet:
+				{
+					tbase = ((pos2 & 0x0fe) * 0x10);
+					tirq  = irq_table[(pos5 & 3)].old_irq;
+					break;
+				}
+			}
+
+			if(!tirq || !tbase || (irq && irq != tirq) || (base_addr && tbase != base_addr)) {
+				slot = mca_find_unused_adapter(smc_mca_adapters[j].id, slot + 1);
+			} else {
+				adapter_found = 1;
+				adapter = j;
+			}
+		}
 	}
-	else if( (slot=mca_find_adapter(0xefe5,0)) != MCA_NOTFOUND)
-	{
-		adapter_name = "IBM Adapter/A for Ethernet";
+	/* Correct j now, before we forget */
+	j--;
+
+	if(!adapter_found) {
+		return ((base_addr || irq) ? ENXIO : ENODEV);
 	}
-	else
-		return -ENODEV;
 
-	mca_set_adapter_name(slot, adapter_name);
+        /* Adapter found. */
+
+	printk(KERN_INFO "%s: %s found in slot %d\n",
+	       dev->name, smc_mca_adapters[adapter].name, slot + 1);
 
-	pos2 = mca_read_stored_pos(slot, 2);     /* IO range */
-	pos3 = mca_read_stored_pos(slot, 3);     /* shared mem */
-	pos4 = mca_read_stored_pos(slot, 4);     /* bios base */
-	pos5 = mca_read_stored_pos(slot, 5);     /* irq and media */
+	mca_set_adapter_name(slot, smc_mca_adapters[adapter].name);
 
-	dev->base_addr = ioaddr = addr_table[pos2 >> 4].base_addr;
-	dev->irq = irq_table[(pos5 & ~IRQ_MASK) >> 2].irq;
 
+	dev->base_addr = ioaddr = tbase;
+	dev->irq       = tirq;
 	dev->mem_start = 0;
-	num_pages = 40;
-	for (i = 0; i < 15; i++)
-	{
-		if (mem_table[i].mem_index == (pos3 & ~MEM_MASK))
+	num_pages      = 40;
+
+	switch (j) {	/* 'j' = card-# in const array above [hs] */
+		case _61c8_SMC_Ethercard_PLUS_Elite_A_BNC_AUI_WD8013EP_A:
+		case _61c9_SMC_Ethercard_PLUS_Elite_A_UTP_AUI_WD8013EP_A:
+		{
+			for (i = 0; i < 16; i++) { /* taking 16 counts
+						    * up to 15 [hs] */
+				if (mem_table[i].mem_index == (pos3 & ~MEM_MASK)) {
+					dev->mem_start = mem_table[i].mem_start;
+					num_pages = mem_table[i].num_pages;
+				}
+			}
+			break;
+		}
+		case _6fc0_WD_Ethercard_PLUS_A_WD8003E_A_OR_WD8003ET_A:
+		case _6fc1_WD_Starcard_PLUS_A_WD8003ST_A:
+		case _6fc2_WD_Ethercard_PLUS_10T_A_WD8003W_A:
+		case _efe5_IBM_PS2_Adapter_A_for_Ethernet:
+		{
+			dev->mem_start = ((pos3 & 0xfc) * 0x1000);
+			num_pages = 0x40;
+			break;
+		}
+		case _efd4_IBM_PS2_Adapter_A_for_Ethernet_UTP_AUI_WD8013WP_A:
+		case _efd5_IBM_PS2_Adapter_A_for_Ethernet_BNC_AUI_WD8013WP_A:
 		{
-			dev->mem_start = mem_table[i].mem_start;
-			num_pages = mem_table[i].num_pages;
+			/* courtesy of gamera@quartz.ocn.ne.jp, pos3 indicates
+			 * the index of the 0x2000 step.
+			 * beware different number of pages [hs]
+			 */
+			dev->mem_start = 0xc0000 + (0x2000 * (pos3 & 0xf));
+			num_pages = 0x20 + (2 * (pos3 & 0x10));
+			break;
 		}
 	}
 
-	if (dev->mem_start == 0)      /* sanity check, shouldn't happen */
+	if (dev->mem_start == 0) /* sanity check, shouldn't happen */
 		return -ENODEV;
 
 	reg4 = inb(ioaddr + 4) & 0x7f;
@@ -133,62 +263,46 @@
 	if (load_8390_module("wd.c"))
 		return -ENOSYS;
 
-	printk("%s: %s at %#3x,", dev->name, adapter_name, ioaddr);
+	printk(KERN_INFO "%s: Parameters: %#3x,", dev->name, ioaddr);
 
 	for (i = 0; i < 6; i++)
 		printk(" %2.2X", dev->dev_addr[i] = inb(ioaddr + 8 + i));
 
-	/*
-	 *	Switch from the station address to the alternate register set and
-	 *	read the useful registers there.
+	/* Switch from the station address to the alternate register set
+	 * and read the useful registers there.
 	 */
 
 	outb(0x80 | reg4, ioaddr + 4);
 
-	/*
-	 *	Enable FINE16 mode to avoid BIOS ROM width mismatches @ reboot.
+	/* Enable FINE16 mode to avoid BIOS ROM width mismatches @ reboot.
 	 */
 
 	outb(0x80 | inb(ioaddr + 0x0c), ioaddr + 0x0c);
 
-	/*
-	 *	Switch back to the station address register set so that the MS-DOS driver
-	 *	can find the card after a warm boot.
+	/* Switch back to the station address register set so that
+	 * the MS-DOS driver can find the card after a warm boot.
 	 */
 
 	outb(reg4, ioaddr + 4);
 
-	/*
-	 *	Allocate dev->priv and fill in 8390 specific dev fields.
+	/* Allocate dev->priv and fill in 8390 specific dev fields.
 	 */
 
-	if (ethdev_init(dev))
-	{
+	if (ethdev_init(dev)) {
 		printk (", no memory for dev->priv.\n");
 		return -ENOMEM;
 	}
 
-	/*
-	 *	OK, we are certain this is going to work.  Setup the device.
+	/* OK, we are certain this is going to work.  Setup the device.
 	 */
 
-	/*
-	 * We store the MCA slot number in the driver-private part of
-	 * the structure 8390.c attaches to dev->priv so we can release
-	 * it later if needed.
-	 */
-	
-	ei_local = (struct ei_device *) dev->priv;
-	ei_local->priv = slot;
 	mca_mark_as_used(slot);
-
 	request_region(ioaddr, ULTRA_IO_EXTENT, "smc-mca");
 
-	/*
-	 *	The 8390 isn't at the base address, so fake the offset
+	/* The 8390 isn't at the base address, so fake the offset
 	 */
 
-	dev->base_addr = ioaddr+ULTRA_NIC_OFFSET;
+	dev->base_addr = ioaddr + ULTRA_NIC_OFFSET;
 
 	ei_status.name = "SMC Ultra MCA";
 	ei_status.word16 = 1;
@@ -196,16 +310,20 @@
 	ei_status.rx_start_page = START_PG + TX_PAGES;
 	ei_status.stop_page = num_pages;
 
-	dev->rmem_start = dev->mem_start + TX_PAGES*256;
+	dev->rmem_start = dev->mem_start + TX_PAGES * 256;
 	dev->mem_end = dev->rmem_end =
-	dev->mem_start + (ei_status.stop_page - START_PG)*256;
+	dev->mem_start + (ei_status.stop_page - START_PG) * 256;
 
-	printk(", IRQ %d memory %#lx-%#lx.\n", dev->irq, dev->mem_start, dev->mem_end-1);
+	printk(", IRQ %d memory %#lx-%#lx.\n",
+	dev->irq, dev->mem_start, dev->mem_end - 1);
 
 	ei_status.reset_8390 = &ultramca_reset_8390;
 	ei_status.block_input = &ultramca_block_input;
 	ei_status.block_output = &ultramca_block_output;
 	ei_status.get_8390_hdr = &ultramca_get_8390_hdr;
+
+	ei_status.priv = slot;
+
 	dev->open = &ultramca_open;
 	dev->stop = &ultramca_close_card;
 	NS8390_init(dev, 0);
@@ -218,21 +336,19 @@
 	int ioaddr = dev->base_addr - ULTRA_NIC_OFFSET; /* ASIC addr */
 
 	if (request_irq(dev->irq, ei_interrupt, 0, ei_status.name, dev))
-	return -EAGAIN;
+		return -EAGAIN;
 
 	outb(ULTRA_MEMENB, ioaddr); /* Enable memory */
 	outb(0x80, ioaddr + 5);     /* ??? */
 	outb(0x01, ioaddr + 6);     /* Enable interrupts and memory. */
 	outb(0x04, ioaddr + 5);     /* ??? */
 
-	/*
-	 *	Set the early receive warning level in window 0 high enough not
-	 *	to receive ERW interrupts.
+	/* Set the early receive warning level in window 0 high enough not
+	 * to receive ERW interrupts.
 	 */
 
-	/*
-	 *	outb_p(E8390_NODMA+E8390_PAGE0, dev->base_addr);
-	 *	outb(0xff, dev->base_addr + EN0_ERWCNT);
+	/* outb_p(E8390_NODMA + E8390_PAGE0, dev->base_addr);
+	 * outb(0xff, dev->base_addr + EN0_ERWCNT);
 	 */
 
 	ei_open(dev);
@@ -245,7 +361,8 @@
 	int ioaddr = dev->base_addr - ULTRA_NIC_OFFSET; /* ASIC addr */
 
 	outb(ULTRA_RESET, ioaddr);
-	if (ei_debug > 1) printk("resetting Ultra, t=%ld...", jiffies);
+	if (ei_debug > 1)
+		printk("resetting Ultra, t=%ld...", jiffies);
 	ei_status.txing = 0;
 
 	outb(0x80, ioaddr + 5);     /* ??? */
@@ -257,12 +374,13 @@
 }
 
 /* Grab the 8390 specific header. Similar to the block_input routine, but
-   we don't need to be concerned with ring wrap as the header will be at
-   the start of a page, so we optimize accordingly. */
+ * we don't need to be concerned with ring wrap as the header will be at
+ * the start of a page, so we optimize accordingly.
+ */
 
 static void ultramca_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr, int ring_page)
 {
-	unsigned long hdr_start = dev->mem_start + ((ring_page - START_PG)<<8);
+	unsigned long hdr_start = dev->mem_start + ((ring_page - START_PG) << 8);
 
 #ifdef notdef
 	/* Officially this is what we are doing, but the readl() is faster */
@@ -273,22 +391,20 @@
 }
 
 /* Block input and output are easy on shared memory ethercards, the only
-   complication is when the ring buffer wraps. */
+ * complication is when the ring buffer wraps.
+ */
 
 static void ultramca_block_input(struct device *dev, int count, struct sk_buff *skb, int ring_offset)
 {
-	unsigned long xfer_start = dev->mem_start + ring_offset - (START_PG<<8);
+	unsigned long xfer_start = dev->mem_start + ring_offset - (START_PG << 8);
 
-	if (xfer_start + count > dev->rmem_end)
-	{
-        	/* We must wrap the input move. */
+	if (xfer_start + count > dev->rmem_end) {
+		/* We must wrap the input move. */
 		int semi_count = dev->rmem_end - xfer_start;
 		memcpy_fromio(skb->data, xfer_start, semi_count);
 		count -= semi_count;
 		memcpy_fromio(skb->data + semi_count, dev->rmem_start, count);
-	}
-	else
-	{
+	} else {
 		/* Packet is in one chunk -- we can copy + cksum. */
 		eth_io_copy_and_sum(skb, xfer_start, count, 0);
 	}
@@ -298,7 +414,7 @@
 static void ultramca_block_output(struct device *dev, int count, const unsigned char *buf,
                 int start_page)
 {
-	unsigned long shmem = dev->mem_start + ((start_page - START_PG)<<8);
+	unsigned long shmem = dev->mem_start + ((start_page - START_PG) << 8);
 
 	memcpy_toio(shmem, buf, count);
 }
@@ -318,7 +434,8 @@
 
 	NS8390_init(dev, 0);
 	/* We should someday disable shared memory and change to 8-bit mode
-       "just in case"... */
+         * "just in case"...
+	 */
 
 	MOD_DEC_USE_COUNT;
 
@@ -329,8 +446,9 @@
 #ifdef MODULE
 #undef MODULE        /* don't want to bother now! */
 
-#define MAX_ULTRAMCA_CARDS  4   /* Max number of Ultra cards per module */
-#define NAMELEN     8   /* # of chars for storing dev->name */
+#define MAX_ULTRAMCA_CARDS 4	/* Max number of Ultra cards per module */
+#define NAMELEN 8		/* # of chars for storing dev->name */
+
 static char namelist[NAMELEN * MAX_ULTRAMCA_CARDS] = { 0, };
 
 static struct device dev_ultra[MAX_ULTRAMCA_CARDS] =
@@ -349,60 +467,42 @@
 MODULE_PARM(io, "1-" __MODULE_STRING(MAX_ULTRAMCA_CARDS) "i");
 MODULE_PARM(irq, "1-" __MODULE_STRING(MAX_ULTRAMCA_CARDS) "i");
 
-/* This is set up so that only a single autoprobe takes place per call.
-ISA device autoprobes on a running machine are not recommended. */
-
 int init_module(void)
 {
 	int this_dev, found = 0;
 
-	for (this_dev = 0; this_dev < MAX_ULTRAMCA_CARDS; this_dev++)
-	{
+	for (this_dev = 0; this_dev < MAX_ULTRAMCA_CARDS; this_dev++) {
 		struct device *dev = &dev_ultra[this_dev];
-		dev->name = namelist+(NAMELEN*this_dev);
+		dev->name = namelist + (NAMELEN * this_dev);
 		dev->irq = irq[this_dev];
 		dev->base_addr = io[this_dev];
 		dev->init = ultramca_probe;
-		if (io[this_dev] == 0)
-		{
-			if (this_dev != 0)
-				break; /* only autoprobe 1st one */
-			printk(KERN_NOTICE "smc-mca.c: Presently autoprobing (not recommended) for a single card.\n");
-		}
-		if (register_netdev(dev) != 0)
-		{
-			printk(KERN_WARNING "smc-mca.c: No SMC Ultra card found (i/o = 0x%x).\n", io[this_dev]);
-			if (found != 0) {	/* Got at least one. */
-				lock_8390_module();
-				return 0;
-			}
-			return -ENXIO;
+
+		if (register_netdev(dev) != 0) {
+			break;
 		}
 		found++;
 	}
-	lock_8390_module();
-	return 0;
+	if (found != 0) {	/* Got at least one. */
+		lock_8390_module();
+		return 0;
+	}
+	printk(KERN_NOTICE "smc-mca.c: No SMC Ultra card found (i/o = 0x%x).\n", io[this_dev]);
+	return -ENXIO;
 }
 
 void cleanup_module(void)
 {
 	int this_dev;
 
-	for (this_dev = 0; this_dev < MAX_ULTRAMCA_CARDS; this_dev++)
-	{
+	for (this_dev = 0; this_dev < MAX_ULTRAMCA_CARDS; this_dev++) {
 		struct device *dev = &dev_ultra[this_dev];
-        	if (dev->priv != NULL)
-        	{
+		if (dev->priv != NULL) {
 			void *priv = dev->priv;
-			/* Need this to find the mca slot number we
-			 * stored in it earlier */
-			struct ei_device *ei_local = (struct ei_device *) dev->priv;
 			/* NB: ultra_close_card() does free_irq */
 			int ioaddr = dev->base_addr - ULTRA_NIC_OFFSET;
+			mca_mark_as_unused(ei_status.priv);
 			release_region(ioaddr, ULTRA_IO_EXTENT);
-			/* Tell the mca subsystem we have finished with
-			 * the card */
-			mca_mark_as_unused(ei_local->priv);
 			unregister_netdev(dev);
 			kfree(priv);
 		}
@@ -410,7 +510,6 @@
 	unlock_8390_module();
 }
 #endif /* MODULE */
-
 
 /*
  * Local variables:
diff -urN linux-2.2.15-pre5/drivers/net/smc-mca.h linux/drivers/net/smc-mca.h
--- linux-2.2.15-pre5/drivers/net/smc-mca.h	Mon Dec  3 21:58:41 2001
+++ linux/drivers/net/smc-mca.h	Mon Dec  3 21:58:47 2001
@@ -1,10 +1,7 @@
-/* 
-   djweis weisd3458@uni.edu 
-   most of this file was taken from ps2esdi.h
-*/
-
-/* Fixed 19/01/2000 davidm@amberdata.demon.co.uk - num_pages should have
- * been in hex */
+/*
+ * djweis weisd3458@uni.edu
+ * most of this file was taken from ps2esdi.h
+ */
 
 struct {
   unsigned int base_addr;
@@ -28,6 +25,7 @@
 };
 
 #define MEM_MASK 64
+
 struct {
   unsigned char mem_index;
   unsigned long mem_start;
@@ -53,13 +51,11 @@
 
 #define IRQ_MASK 243
 struct {
-   unsigned char irq;
+   unsigned char new_irq;
+   unsigned char old_irq;
 } irq_table[] = {
-   {  3 }, 
-   {  4 },
-   { 10 },
-   { 14 }
+   {  3,  3 },
+   {  4,  4 },
+   { 10, 10 },
+   { 14, 15 }
 };
-
- 
-   
diff -urN linux-2.2.15-pre5/drivers/scsi/3w-xxxx.c linux/drivers/scsi/3w-xxxx.c
--- linux-2.2.15-pre5/drivers/scsi/3w-xxxx.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/scsi/3w-xxxx.c	Mon Dec  3 21:58:47 2001
@@ -0,0 +1,2225 @@
+/* 
+   3w-xxxx.c -- 3ware Storage Controller device driver for Linux.
+
+   Written By: Adam Radford <linux@3ware.com>
+   Copyright (C) 1999-2000 3ware Inc.
+
+   Kernel compatablity By: 	Andre Hedrick <andre@suse.com>
+   Non-Copyright (C) 2000	Andre Hedrick <andre@suse.com>
+   
+   Further tiny build fixes and trivial hoovering    Alan Cox
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; version 2 of the License.
+
+   This program is distributed in the hope that it will be useful,           
+   but WITHOUT ANY WARRANTY; without even the implied warranty of            
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the             
+   GNU General Public License for more details.                              
+
+   NO WARRANTY                                                               
+   THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR        
+   CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT      
+   LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,      
+   MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is    
+   solely responsible for determining the appropriateness of using and       
+   distributing the Program and assumes all risks associated with its        
+   exercise of rights under this Agreement, including but not limited to     
+   the risks and costs of program errors, damage to or loss of data,         
+   programs or equipment, and unavailability or interruption of operations.  
+
+   DISCLAIMER OF LIABILITY                                                   
+   NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY   
+   DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL        
+   DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND   
+   ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR     
+   TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE    
+   USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED  
+   HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES             
+
+   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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+
+   Bugs/Comments/Suggestions should be mailed to:                            
+   linux@3ware.com
+
+   For more information, goto:
+   http://www.3ware.com
+*/
+
+#include <linux/module.h>
+
+MODULE_AUTHOR ("3ware Inc.");
+MODULE_DESCRIPTION ("3ware Storage Controller Linux Driver");
+
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/time.h>
+#include <linux/proc_fs.h>
+#include <linux/sched.h>
+#include <linux/ioport.h>
+#include <linux/blk.h>
+#include <linux/hdreg.h>
+#include <linux/string.h>
+#include <linux/delay.h>
+#include <linux/smp.h>
+
+#include <asm/spinlock.h>
+#include <asm/errno.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+
+#define __3W_C			/* let 3w-xxxx.h know it is use */
+
+#include "sd.h"
+#include "scsi.h"
+#include "hosts.h"
+
+#include "3w-xxxx.h"
+
+static int tw_copy_info(TW_Info *info, char *fmt, ...);
+static void tw_copy_mem_info(TW_Info *info, char *data, int len);
+static void tw_interrupt(int irq, void *dev_instance, struct pt_regs *regs);
+
+struct proc_dir_entry tw_scsi_proc_entry = {
+	PROC_SCSI_3W_XXXX,
+	7, "3w-xxxx",
+	S_IFDIR|S_IRUGO|S_IXUGO, 2
+};
+
+/* Globals */
+char *tw_driver_version="0.4.001";
+TW_Device_Extension *tw_device_extension_list[TW_MAX_SLOT];
+int tw_device_extension_count = 0;
+
+/* Functions */
+
+/* This function will complete an aen request from the isr */
+int tw_aen_complete(TW_Device_Extension *tw_dev, int request_id) 
+{
+	TW_Param *param;
+	unsigned short aen, aen_code;
+
+	if (tw_dev->alignment_virtual_address[request_id] == NULL) {
+		printk(KERN_WARNING "3w-xxxx: tw_aen_complete(): Bad alignment virtual address.\n");
+		return 1;
+	}
+	param = (TW_Param *)tw_dev->alignment_virtual_address[request_id];
+	aen = *(unsigned short *)(param->data);
+	aen_code = (aen & 0x0ff);
+	dprintk(KERN_NOTICE "3w-xxxx: tw_aen_complete(): Queue'd code 0x%x\n", aen_code);
+	/* Now queue the code */
+	tw_dev->aen_queue[tw_dev->aen_tail] = aen_code;
+	if (tw_dev->aen_tail == TW_Q_LENGTH - 1) {
+		tw_dev->aen_tail = TW_Q_START;
+	} else {
+		tw_dev->aen_tail = tw_dev->aen_tail + 1;
+	}
+	if (tw_dev->aen_head == tw_dev->aen_tail) {
+		if (tw_dev->aen_head == TW_Q_LENGTH - 1) {
+			tw_dev->aen_head = TW_Q_START;
+		} else {
+			tw_dev->aen_head = tw_dev->aen_head + 1;
+		}
+	}
+	tw_dev->state[request_id] = TW_S_COMPLETED;
+	tw_state_request_finish(tw_dev, request_id);
+
+	return 0;
+} /* End tw_aen_complete() */
+
+/* This function will drain the aen queue after a soft reset */
+int tw_aen_drain_queue(TW_Device_Extension *tw_dev)
+{
+	TW_Command *command_packet;
+	TW_Param *param;
+	int tries = 0;
+	int request_id = 0;
+	u32 command_que_value = 0, command_que_addr;
+	u32 status_reg_value = 0, status_reg_addr;
+	u32 param_value;
+	TW_Response_Queue response_queue;
+	u32 response_que_addr;
+	unsigned short aen;
+	unsigned short aen_code;
+	int finished = 0;
+	int first_reset = 0;
+	int queue = 0;
+	int imax, i;
+	int found = 0;
+
+	dprintk(KERN_NOTICE "3w-xxxx: tw_aen_drain_queue()\n");
+
+	command_que_addr = tw_dev->registers.command_que_addr;
+	status_reg_addr = tw_dev->registers.status_reg_addr;
+	response_que_addr = tw_dev->registers.response_que_addr;
+
+	if (tw_poll_status(tw_dev, TW_STATUS_ATTENTION_INTERRUPT, 15)) {
+		printk(KERN_WARNING "3w-xxxx: tw_aen_drain_queue(): No attention interrupt for card %d\n", tw_dev->host->host_no);
+		return 1;
+	}
+
+	/* Initialize command packet */
+	if (tw_dev->command_packet_virtual_address[request_id] == NULL) {
+		printk(KERN_WARNING "3w-xxxx: tw_aen_drain_queue(): Bad command packet virtual address.\n");
+		return 1;
+	}
+	command_packet = (TW_Command *)tw_dev->command_packet_virtual_address[request_id];
+	memset(command_packet, 0, sizeof(TW_Sector));
+	command_packet->byte0.opcode = TW_OP_GET_PARAM;
+	command_packet->byte0.sgl_offset = 2;
+	command_packet->size = 4;
+	command_packet->request_id = request_id;
+	command_packet->byte3.unit = 0;
+	command_packet->byte3.host_id = 0;
+	command_packet->status = 0;
+	command_packet->flags = 0;
+	command_packet->byte6.parameter_count = 1;
+	command_que_value = tw_dev->command_packet_physical_address[request_id];
+	if (command_que_value == 0) {
+		printk(KERN_WARNING "3w-xxxx: tw_aen_drain_queue(): Bad command packet physical address.\n");
+		return 1;
+	}
+
+	/* Now setup the param */
+	if (tw_dev->alignment_virtual_address[request_id] == NULL) {
+		printk(KERN_WARNING "3w-xxxx: tw_aen_drain_queue(): Bad alignment virtual address.\n");
+		return 1;
+	}
+	param = (TW_Param *)tw_dev->alignment_virtual_address[request_id];
+	memset(param, 0, sizeof(TW_Sector));
+	param->table_id = 0x401; /* AEN table */
+	param->parameter_id = 2; /* Unit code */
+	param->parameter_size_bytes = 2;
+	param_value = tw_dev->alignment_physical_address[request_id];
+	if (param_value == 0) {
+		printk(KERN_WARNING "3w-xxxx: tw_aen_drain_queue(): Bad alignment physical address.\n");
+		return 1;
+	}
+	command_packet->byte8.param.sgl[0].address = param_value;
+	command_packet->byte8.param.sgl[0].length = sizeof(TW_Sector);
+
+	imax = TW_POLL_MAX_RETRIES;
+
+	/* Now drain the controller's aen queue */
+	do {
+		/* Post command packet */
+		outl(command_que_value, command_que_addr);
+    
+		/* Now poll for completion */
+		for (i=0;i<imax;i++) {
+			mdelay(10);
+			status_reg_value = inl(status_reg_addr);
+			if (tw_check_bits(status_reg_value)) {
+				printk(KERN_WARNING "3w-xxxx: tw_aen_drain_queue(): Unexpected bits.\n");
+				return 1;
+			}
+			if ((status_reg_value & TW_STATUS_RESPONSE_QUEUE_EMPTY) == 0) {
+				response_queue.value = inl(response_que_addr);
+				request_id = (unsigned char)response_queue.u.response_id;
+    
+				if (request_id != 0) {
+					/* Unexpected request id */
+					printk(KERN_WARNING "3w-xxxx: tw_aen_drain_queue(): Unexpected request id.\n");
+					return 1;
+				}
+	
+				if (command_packet->status != 0) {
+					if (command_packet->flags != TW_AEN_TABLE_UNDEFINED) {
+						/* Bad response */
+						printk(KERN_WARNING "3w-xxxx: tw_aen_drain_queue(): Bad response, flags = 0x%x.\n", command_packet->flags);
+						return 1;
+					} else {
+						/* We know this is a 3w-1x00, and doesn't support aen's */
+						return 0;
+					}
+				}
+
+				/* Now check the aen */
+				aen = *(unsigned short *)(param->data);
+				aen_code = (aen & 0x0ff);
+				queue = 0;
+				switch (aen_code) {
+					case TW_AEN_QUEUE_EMPTY:
+						dprintk(KERN_NOTICE "3w-xxxx: tw_aen_drain_queue(): Found TW_AEN_QUEUE_EMPTY.\n");
+						if (first_reset != 1) {
+							continue;
+						} else {
+							finished = 1;
+						}
+						break;
+					case TW_AEN_SOFT_RESET:
+						dprintk(KERN_NOTICE "3w-xxxx: tw_aen_drain_queue(): Found TW_AEN_SOFT_RESET.\n");
+						if (first_reset == 0) {
+							first_reset = 1;
+						} else {
+							queue = 1;
+						}
+						break;
+					case TW_AEN_DEGRADED_MIRROR:
+						dprintk(KERN_NOTICE "3w-xxxx: tw_aen_drain_queue(): Found TW_AEN_DEGRADED_MIRROR.\n");
+						queue = 1;
+						break;
+					case TW_AEN_CONTROLLER_ERROR:
+						dprintk(KERN_NOTICE "3w-xxxx: tw_aen_drain_queue(): Found TW_AEN_CONTROLLER_ERROR.\n");
+						queue = 1;
+						break;
+					case TW_AEN_REBUILD_FAIL:
+						dprintk(KERN_NOTICE "3w-xxxx: tw_aen_drain_queue(): Found TW_AEN_REBUILD_FAIL.\n");
+						queue = 1;
+						break;
+					case TW_AEN_REBUILD_DONE:
+						dprintk(KERN_NOTICE "3w-xxxx: tw_aen_drain_queue(): Found TW_AEN_REBUILD_DONE.\n");
+						queue = 1;
+						break;
+					case TW_AEN_QUEUE_FULL:
+						dprintk(KERN_NOTICE "3w-xxxx: tw_aen_drain_queue(): Found TW_AEN_QUEUE_FULL.\n");
+						queue = 1;
+						break;
+					default:
+						dprintk(KERN_WARNING "3w-xxxx: tw_aen_drain_queue(): Unknown AEN code 0x%x.\n", aen_code);
+						queue = 1;
+				}
+
+				/* Now put the aen on the aen_queue */
+				if (queue == 1) {
+					tw_dev->aen_queue[tw_dev->aen_tail] = aen_code;
+					if (tw_dev->aen_tail == TW_Q_LENGTH - 1) {
+						tw_dev->aen_tail = TW_Q_START;
+					} else {
+						tw_dev->aen_tail = tw_dev->aen_tail + 1;
+					}
+					if (tw_dev->aen_head == tw_dev->aen_tail) {
+						if (tw_dev->aen_head == TW_Q_LENGTH - 1) {
+							tw_dev->aen_head = TW_Q_START;
+						} else {
+							tw_dev->aen_head = tw_dev->aen_head + 1;
+						}
+					}
+				}
+				found = 1;
+				break;
+			}
+		}
+		if (found == 0) {
+			printk(KERN_WARNING "3w-xxxx: tw_aen_drain_queue(): Response never received.\n");
+			return 1;
+		}
+		tries++;
+	} while ((tries < TW_MAX_AEN_TRIES) && (finished == 0));
+
+	if (tries >=TW_MAX_AEN_TRIES) {
+		printk(KERN_WARNING "3w-xxxx: tw_aen_drain_queue(): Aen queue error.\n");
+		return 1;
+	}
+
+	return 0;
+} /* End tw_aen_drain_queue() */
+
+/* This function will read the aen queue from the isr */
+int tw_aen_read_queue(TW_Device_Extension *tw_dev, int request_id) 
+{
+	TW_Command *command_packet;
+	TW_Param *param;
+	u32 command_que_value = 0, command_que_addr;
+	u32 status_reg_value = 0, status_reg_addr;
+	u32 param_value = 0;
+
+	dprintk(KERN_NOTICE "3w-xxxx: tw_aen_read_queue()\n");
+	command_que_addr = tw_dev->registers.command_que_addr;
+	status_reg_addr = tw_dev->registers.status_reg_addr;
+
+	status_reg_value = inl(status_reg_addr);
+	if (tw_check_bits(status_reg_value)) {
+		printk(KERN_WARNING "3w-xxxx: tw_aen_read_queue(): Unexpected bits.\n");
+		return 1;
+	}
+	if (tw_dev->command_packet_virtual_address[request_id] == NULL) {
+		printk(KERN_WARNING "3w-xxxx: tw_aen_read_queue(): Bad command packet virtual address.\n");
+		return 1;
+	}
+	command_packet = (TW_Command *)tw_dev->command_packet_virtual_address[request_id];
+	memset(command_packet, 0, sizeof(TW_Sector));
+	command_packet->byte0.opcode = TW_OP_GET_PARAM;
+	command_packet->byte0.sgl_offset = 2;
+	command_packet->size = 4;
+	command_packet->request_id = request_id;
+	command_packet->byte3.unit = 0;
+	command_packet->byte3.host_id = 0;
+	command_packet->status = 0;
+	command_packet->flags = 0;
+	command_packet->byte6.parameter_count = 1;
+	command_que_value = tw_dev->command_packet_physical_address[request_id];
+	if (command_que_value == 0) {
+		printk(KERN_WARNING "3w-xxxx: tw_aen_read_queue(): Bad command packet physical address.\n");
+		return 1;
+	}
+	/* Now setup the param */
+	if (tw_dev->alignment_virtual_address[request_id] == NULL) {
+		printk(KERN_WARNING "3w-xxxx: tw_aen_read_queue(): Bad alignment virtual address.\n");
+		return 1;
+	}
+	param = (TW_Param *)tw_dev->alignment_virtual_address[request_id];
+	memset(param, 0, sizeof(TW_Sector));
+	param->table_id = 0x401; /* AEN table */
+	param->parameter_id = 2; /* Unit code */
+	param->parameter_size_bytes = 2;
+	param_value = tw_dev->alignment_physical_address[request_id];
+	if (param_value == 0) {
+		printk(KERN_WARNING "3w-xxxx: tw_aen_read_queue(): Bad alignment physical address.\n");
+		return 1;
+	}
+	command_packet->byte8.param.sgl[0].address = param_value;
+	command_packet->byte8.param.sgl[0].length = sizeof(TW_Sector);
+
+	/* Now post the command packet */
+	if ((status_reg_value & TW_STATUS_COMMAND_QUEUE_FULL) == 0) {
+		dprintk(KERN_WARNING "3w-xxxx: tw_aen_read_queue(): Post succeeded.\n");
+		tw_dev->srb[request_id] = 0; /* Flag internal command */
+		tw_dev->state[request_id] = TW_S_POSTED;
+		outl(command_que_value, command_que_addr);
+	} else {
+		printk(KERN_WARNING "3w-xxxx: tw_aen_read_queue(): Post failed, will retry.\n");
+		return 1;
+	}
+
+	return 0;
+} /* End tw_aen_read_queue() */
+
+/* This function will allocate memory and check if it is 16 d-word aligned */
+int tw_allocate_memory(TW_Device_Extension *tw_dev, int request_id, int size, int which)
+{
+	u32 *virt_addr;
+
+	dprintk(KERN_NOTICE "3w-xxxx: tw_allocate_memory()\n");
+
+	if (which == 0) {
+		/* Allocate command packet memory */
+		virt_addr = kmalloc(size, GFP_ATOMIC);
+		if (virt_addr == NULL) {
+			printk(KERN_WARNING "3w-xxxx: tw_allocate_memory(): kmalloc() failed.\n");
+			return 1;
+		}
+		if ((u32)virt_addr % TW_ALIGNMENT) {
+			printk(KERN_WARNING "3w-xxxx: tw_allocate_memory(): Found unaligned address.\n");
+			return 1;
+		}
+		tw_dev->command_packet_virtual_address[request_id] = virt_addr;
+		tw_dev->command_packet_physical_address[request_id] = 
+		virt_to_bus(virt_addr);
+	} else {
+		/* Allocate generic buffer */
+		virt_addr = kmalloc(size, GFP_ATOMIC);
+		if (virt_addr == NULL) {
+			printk(KERN_WARNING "3w-xxxx: tw_allocate_memory(): kmalloc() failed.\n");
+			return 1;
+		}
+		if ((u32)virt_addr % TW_ALIGNMENT) {
+			printk(KERN_WARNING "3w-xxxx: tw_allocate_memory(): Found unaligned address.\n");
+			return 1;
+		}
+		tw_dev->alignment_virtual_address[request_id] = virt_addr;
+		tw_dev->alignment_physical_address[request_id] = virt_to_bus(virt_addr);
+	}
+	return 0;
+} /* End tw_allocate_memory() */
+
+/* This function will check the status register for unexpected bits */
+int tw_check_bits(u32 status_reg_value)
+{
+	if ((status_reg_value & TW_STATUS_EXPECTED_BITS) != TW_STATUS_EXPECTED_BITS) {  
+		printk(KERN_WARNING "3w-xxxx: tw_check_bits(): No expected bits (0x%x).\n", status_reg_value);
+		return 1;
+	}
+	if ((status_reg_value & TW_STATUS_UNEXPECTED_BITS) != 0) {
+		printk(KERN_WARNING "3w-xxxx: tw_check_bits(): Found unexpected bits (0x%x).\n", status_reg_value);
+		return 1;
+	}
+
+	return 0;
+} /* End tw_check_bits() */
+
+/* This function will report controller error status */
+int tw_check_errors(TW_Device_Extension *tw_dev) 
+{
+	u32 status_reg_addr, status_reg_value;
+  
+	status_reg_addr = tw_dev->registers.status_reg_addr;
+	status_reg_value = inl(status_reg_addr);
+
+	if (TW_STATUS_ERRORS(status_reg_value) || tw_check_bits(status_reg_value))
+		return 1;
+
+	return 0;
+} /* End tw_check_errors() */
+
+/* This function will clear the attention interrupt */
+void tw_clear_attention_interrupt(TW_Device_Extension *tw_dev)
+{
+	u32 control_reg_addr, control_reg_value;
+  
+	control_reg_addr = tw_dev->registers.control_reg_addr;
+	control_reg_value = TW_CONTROL_CLEAR_ATTENTION_INTERRUPT;
+	outl(control_reg_value, control_reg_addr);
+} /* End tw_clear_attention_interrupt() */
+
+/* This function will clear the host interrupt */
+void tw_clear_host_interrupt(TW_Device_Extension *tw_dev)
+{
+	u32 control_reg_addr, control_reg_value;
+
+	control_reg_addr = tw_dev->registers.control_reg_addr;
+	control_reg_value = TW_CONTROL_CLEAR_HOST_INTERRUPT;
+	outl(control_reg_value, control_reg_addr);
+} /* End tw_clear_host_interrupt() */
+
+/* This function is called by tw_scsi_proc_info */
+static int tw_copy_info(TW_Info *info, char *fmt, ...) 
+{
+	va_list args;
+	char buf[81];
+	int len;
+  
+	va_start(args, fmt);
+	len = vsprintf(buf, fmt, args);
+	va_end(args);
+	tw_copy_mem_info(info, buf, len);
+	return len;
+} /* End tw_copy_info() */
+
+/* This function is called by tw_scsi_proc_info */
+static void tw_copy_mem_info(TW_Info *info, char *data, int len)
+{
+	if (info->position + len > info->length)
+		len = info->length - info->position;
+
+	if (info->position + len < info->offset) {
+		info->position += len;
+		return;
+	}
+	if (info->position < info->offset) {
+		data += (info->offset - info->position);
+		len  -= (info->offset - info->position);
+	}
+	if (len > 0) {
+		memcpy(info->buffer + info->position, data, len);
+		info->position += len;
+	}
+} /* End tw_copy_mem_info() */
+
+/* This function will disable interrupts on the controller */  
+void tw_disable_interrupts(TW_Device_Extension *tw_dev) 
+{
+	u32 control_reg_value, control_reg_addr;
+
+	control_reg_addr = tw_dev->registers.control_reg_addr;
+	control_reg_value = TW_CONTROL_DISABLE_INTERRUPTS;
+	outl(control_reg_value, control_reg_addr);
+} /* End tw_disable_interrupts() */
+
+/* This function will empty the response que */
+int tw_empty_response_que(TW_Device_Extension *tw_dev) 
+{
+	u32 status_reg_addr, status_reg_value;
+	u32 response_que_addr, response_que_value;
+
+	status_reg_addr = tw_dev->registers.status_reg_addr;
+	response_que_addr = tw_dev->registers.response_que_addr;
+  
+	status_reg_value = inl(status_reg_addr);
+
+	if (tw_check_bits(status_reg_value)) {
+		printk(KERN_WARNING "3w-xxxx: tw_empty_response_queue(): Unexpected bits 1.\n");
+		return 1;
+	}
+  
+	while ((status_reg_value & TW_STATUS_RESPONSE_QUEUE_EMPTY) == 0) {
+		response_que_value = inl(response_que_addr);
+		status_reg_value = inl(status_reg_addr);
+		if (tw_check_bits(status_reg_value)) {
+			printk(KERN_WARNING "3w-xxxx: tw_empty_response_queue(): Unexpected bits 2.\n");
+			return 1;
+		}
+	}
+	return 0;
+} /* End tw_empty_response_que() */
+
+/* This function will enable interrupts on the controller */
+void tw_enable_interrupts(TW_Device_Extension *tw_dev)
+{
+	u32 control_reg_value, control_reg_addr;
+
+	control_reg_addr = tw_dev->registers.control_reg_addr;
+	control_reg_value = (TW_CONTROL_CLEAR_ATTENTION_INTERRUPT |
+			     TW_CONTROL_UNMASK_RESPONSE_INTERRUPT |
+			     TW_CONTROL_ENABLE_INTERRUPTS);
+	outl(control_reg_value, control_reg_addr);
+} /* End tw_enable_interrupts() */
+
+/* This function will find and initialize all cards */
+int tw_findcards(Scsi_Host_Template *tw_host) 
+{
+	int numcards = 0, tries = 0, error = 0;
+	struct Scsi_Host *host;
+	TW_Device_Extension *tw_dev;
+	TW_Device_Extension *tw_dev2;
+	struct pci_dev *tw_pci_dev = pci_devices;
+	u32 status_reg_value;
+
+	dprintk(KERN_NOTICE "3w-xxxx: tw_findcards()\n");
+	while ((tw_pci_dev = pci_find_device(TW_VENDOR_ID, TW_DEVICE_ID, tw_pci_dev))) {
+		/* Prepare temporary device extension */
+		tw_dev=(TW_Device_Extension *)kmalloc(sizeof(TW_Device_Extension), GFP_ATOMIC);
+		if (tw_dev == NULL) {
+			printk(KERN_WARNING "3w-xxxx: tw_findcards(): kmalloc() failed for card %d.\n", numcards);
+			continue;
+		}
+		memset(tw_dev, 0, sizeof(TW_Device_Extension));
+
+		error = tw_initialize_device_extension(tw_dev);
+		if (error) {
+			printk(KERN_WARNING "3w-xxxx: tw_findcards(): Couldn't initialize device extension for card %d.\n", numcards);
+			tw_free_device_extension(tw_dev);
+			kfree(tw_dev);
+			continue;
+		}
+
+		/* Calculate the cards register addresses */
+		tw_dev->registers.base_addr = tw_pci_dev->base_address[0];
+		tw_dev->registers.control_reg_addr = (tw_pci_dev->base_address[0] & ~15);
+		tw_dev->registers.status_reg_addr = ((tw_pci_dev->base_address[0] & ~15) + 0x4);
+		tw_dev->registers.command_que_addr = ((tw_pci_dev->base_address[0] & ~15) + 0x8);
+		tw_dev->registers.response_que_addr = ((tw_pci_dev->base_address[0] & ~15) + 0xC);
+		/* Save pci_dev struct to device extension */
+		tw_dev->tw_pci_dev = tw_pci_dev;
+
+		/* Poll status register for 60 secs for 'Controller Ready' flag */
+		if (tw_poll_status(tw_dev, TW_STATUS_MICROCONTROLLER_READY, 60)) {
+			printk(KERN_WARNING "3w-xxxx: tw_findcards(): Microcontroller not ready for card %d.\n", numcards);
+			tw_free_device_extension(tw_dev);
+			kfree(tw_dev);
+			continue;
+		}
+
+		/* Disable interrupts on the card */
+		tw_disable_interrupts(tw_dev);
+
+		while (tries < TW_MAX_RESET_TRIES) {
+			/* Do soft reset */
+			tw_soft_reset(tw_dev);
+
+			error = tw_aen_drain_queue(tw_dev);
+			if (error) {
+				printk(KERN_WARNING "3w-xxxx: tw_findcards(): No attention interrupt for card %d.\n", numcards);
+				tries++;
+				continue;
+			}
+
+			/* Check for controller errors */
+			if (tw_check_errors(tw_dev)) {
+				printk(KERN_WARNING "3w-xxxx: tw_findcards(): Controller errors found, soft resetting card %d.\n", numcards);
+				tries++;
+				continue;
+			}
+
+			/* Empty the response queue */
+			error = tw_empty_response_que(tw_dev);
+			if (error) {
+				printk(KERN_WARNING "3w-xxxx: tw_findcards(): Couldn't empty response queue for card %d.\n", numcards);
+				tries++;
+				continue;
+			}
+
+			/* Now the controller is in a good state */
+			break;
+		}
+
+		if (tries >= TW_MAX_RESET_TRIES) {
+			printk(KERN_WARNING "3w-xxxx: tw_findcards(): Controller error or no attention interrupt: giving up for card %d.\n", numcards);
+			tw_free_device_extension(tw_dev);
+			kfree(tw_dev);
+			continue;
+		}
+
+		/* Make sure that io region isn't already taken */
+		if (check_region((tw_dev->tw_pci_dev->base_address[0]), TW_IO_ADDRESS_RANGE)) {
+			printk(KERN_WARNING "3w-xxxx: tw_findcards(): Couldn't get io range 0x%lx-0x%lx for card %d.\n", 
+				(tw_dev->tw_pci_dev->base_address[0]), 
+				(tw_dev->tw_pci_dev->base_address[0]) + 
+				TW_IO_ADDRESS_RANGE, numcards);
+			tw_free_device_extension(tw_dev);
+			kfree(tw_dev);
+			continue;
+		}
+    
+		/* Reserve the io address space */
+		request_region((tw_dev->tw_pci_dev->base_address[0]), TW_IO_ADDRESS_RANGE, TW_DEVICE_NAME);
+		error = tw_initialize_units(tw_dev);
+		if (error) {
+			printk(KERN_WARNING "3w-xxxx: tw_findcards(): Couldn't initialize units for card %d.\n", numcards);
+			release_region((tw_dev->tw_pci_dev->base_address[0]), TW_IO_ADDRESS_RANGE);
+			tw_free_device_extension(tw_dev);
+			kfree(tw_dev);
+			continue;
+		}
+
+		error = tw_initconnection(tw_dev);
+		if (error) {
+			printk(KERN_WARNING "3w-xxxx: tw_findcards(): Couldn't initconnection for card %d.\n", numcards);
+			release_region((tw_dev->tw_pci_dev->base_address[0]), TW_IO_ADDRESS_RANGE);
+			tw_free_device_extension(tw_dev);
+			kfree(tw_dev);
+			continue;
+		}
+
+		/* Calculate max cmds per lun */
+		if (tw_dev->num_units > 0)
+			tw_host->cmd_per_lun = (TW_Q_LENGTH-2)/tw_dev->num_units;
+
+		/* Register the card with the kernel SCSI layer */
+		host = scsi_register(tw_host, sizeof(TW_Device_Extension));
+		
+		/* FIXME - check for NULL */
+
+		status_reg_value = inl(tw_dev->registers.status_reg_addr);
+
+		dprintk(KERN_NOTICE "scsi%d : Found a 3ware Storage Controller at 0x%x, IRQ: %d P-chip: %d.%d\n", host->host_no,
+				(u32)(tw_pci_dev->base_address[0]), tw_pci_dev->irq, 
+				(status_reg_value & TW_STATUS_MAJOR_VERSION_MASK) >> 28, 
+				(status_reg_value & TW_STATUS_MINOR_VERSION_MASK) >> 24);
+
+		if (host->hostdata) {
+			tw_dev2 = (TW_Device_Extension *)host->hostdata;
+			memcpy(tw_dev2, tw_dev, sizeof(TW_Device_Extension));
+			tw_device_extension_list[tw_device_extension_count] = tw_dev2;
+			numcards++;
+			tw_device_extension_count = numcards;
+			tw_dev2->host = host;
+		} else { 
+			printk(KERN_WARNING "3w-xxxx: tw_findcards(): Bad scsi host data for card %d.\n", numcards-1);
+			scsi_unregister(host);
+			release_region((tw_dev->tw_pci_dev->base_address[0]), TW_IO_ADDRESS_RANGE);
+			tw_free_device_extension(tw_dev);
+			kfree(tw_dev);
+			continue;
+		}
+    
+		/* Re-enable interrupts on the card */
+		tw_enable_interrupts(tw_dev2);
+
+		/* Now setup the interrupt handler */
+		error = tw_setup_irq(tw_dev2);
+		if (error) {
+			printk(KERN_WARNING "3w-xxxx: tw_findcards(): Error requesting irq for card %d.\n", numcards-1);
+			scsi_unregister(host);
+			release_region((tw_dev->tw_pci_dev->base_address[0]), TW_IO_ADDRESS_RANGE);
+
+			tw_free_device_extension(tw_dev);
+			kfree(tw_dev);
+			numcards--;
+			continue;
+		}
+
+		/* Free the temporary device extension */
+		if (tw_dev)
+			kfree(tw_dev);
+	}
+
+	if (numcards == 0) 
+		printk(KERN_WARNING "3w-xxxx: tw_findcards(): No cards found.\n");
+
+	return numcards;
+} /* End tw_findcards() */
+
+/* This function will free up device extension resources */
+void tw_free_device_extension(TW_Device_Extension *tw_dev)
+{
+	int i, imax;
+	imax = TW_Q_LENGTH;
+
+	dprintk(KERN_NOTICE "3w-xxxx: tw_free_device_extension()\n");
+	/* Free command packet and generic buffer memory */
+	for (i=0;i<imax;i++) {
+		if (tw_dev->command_packet_virtual_address[i]) 
+			kfree(tw_dev->command_packet_virtual_address[i]);
+
+		if (tw_dev->alignment_virtual_address[i])
+			kfree(tw_dev->alignment_virtual_address[i]);
+	}
+} /* End tw_free_device_extension() */
+
+/* This function will send an initconnection command to controller */
+int tw_initconnection(TW_Device_Extension *tw_dev) 
+{
+	u32 command_que_addr, command_que_value;
+	u32 status_reg_addr, status_reg_value;
+	u32 response_que_addr;
+	TW_Command  *command_packet;
+	TW_Response_Queue response_queue;
+	int request_id = 0;
+	int i = 0;
+	int imax = 0;
+
+	dprintk(KERN_NOTICE "3w-xxxx: tw_initconnection()\n");
+	command_que_addr = tw_dev->registers.command_que_addr;
+	status_reg_addr = tw_dev->registers.status_reg_addr;
+	response_que_addr = tw_dev->registers.response_que_addr;
+
+	/* Initialize InitConnection command packet */
+	if (tw_dev->command_packet_virtual_address[request_id] == NULL) {
+		printk(KERN_WARNING "3w-xxxx: tw_initconnection(): Bad command packet virtual address.\n");
+		return 1;
+	}
+
+	command_packet = (TW_Command *)tw_dev->command_packet_virtual_address[request_id];
+	memset(command_packet, 0, sizeof(TW_Sector));
+	command_packet->byte0.opcode = TW_OP_INIT_CONNECTION;
+	command_packet->byte0.sgl_offset = 0x0;
+	command_packet->size = TW_INIT_COMMAND_PACKET_SIZE;
+	command_packet->request_id = request_id;
+	command_packet->byte3.unit = 0x0;
+	command_packet->byte3.host_id = 0x0;
+	command_packet->status = 0x0;
+	command_packet->flags = 0x0;
+	command_packet->byte6.message_credits = TW_INIT_MESSAGE_CREDITS; 
+	command_packet->byte8.init_connection.response_queue_pointer = 0x0;
+	command_que_value = tw_dev->command_packet_physical_address[request_id];
+
+	if (command_que_value == 0) {
+		printk(KERN_WARNING "3w-xxxx: tw_initconnection(): Bad command packet physical address.\n");
+		return 1;
+	}
+  
+	/* Send command packet to the board */
+	outl(command_que_value, command_que_addr);
+    
+	/* Poll for completion */
+	imax = TW_POLL_MAX_RETRIES;
+	for (i=0;i<imax;i++) {
+		mdelay(10);
+		status_reg_value = inl(status_reg_addr);
+		if (tw_check_bits(status_reg_value)) {
+			printk(KERN_WARNING "3w-xxxx: tw_initconnection(): Unexpected bits.\n");
+			return 1;
+		}
+		if ((status_reg_value & TW_STATUS_RESPONSE_QUEUE_EMPTY) == 0) {
+			response_queue.value = inl(response_que_addr);
+			request_id = (unsigned char)response_queue.u.response_id;
+			if (request_id != 0) {
+				/* unexpected request id */
+				printk(KERN_WARNING "3w-xxxx: tw_initconnection(): Unexpected request id.\n");
+				return 1;
+			}
+			if (command_packet->status != 0) {
+				/* bad response */
+				printk(KERN_WARNING "3w-xxxx: tw_initconnection(): Bad response, flags = 0x%x.\n", command_packet->flags);
+				return 1;
+			}
+			break;	/* Response was okay, so we exit */
+		}
+	}
+	return 0;
+} /* End tw_initconnection() */
+
+/* This function will initialize the fields of a device extension */
+int tw_initialize_device_extension(TW_Device_Extension *tw_dev)
+{
+	int i, imax;
+
+	dprintk(KERN_NOTICE "3w-xxxx: tw_initialize_device_extension()\n");
+	imax = TW_Q_LENGTH;
+
+	for (i=0; i<imax; i++) {
+		/* Initialize command packet buffers */
+		tw_allocate_memory(tw_dev, i, sizeof(TW_Sector), 0);
+		if (tw_dev->command_packet_virtual_address[i] == NULL) {
+			printk(KERN_WARNING "3w-xxxx: tw_initialize_device_extension(): Bad command packet virtual address.\n");
+			return 1;
+		}
+		memset(tw_dev->command_packet_virtual_address[i], 0, sizeof(TW_Sector));
+    
+		/* Initialize generic buffer */
+		tw_allocate_memory(tw_dev, i, sizeof(TW_Sector), 1);
+		if (tw_dev->alignment_virtual_address[i] == NULL) {
+			printk(KERN_WARNING "3w-xxxx: tw_initialize_device_extension(): Bad alignment virtual address.\n");
+			return 1;
+		}
+		memset(tw_dev->alignment_virtual_address[i], 0, sizeof(TW_Sector));
+
+		tw_dev->free_queue[i] = i;
+		tw_dev->state[i] = TW_S_INITIAL;
+		tw_dev->ioctl_size[i] = 0;
+		tw_dev->aen_queue[i] = 0;
+	}
+
+	for (i=0;i<TW_MAX_UNITS;i++)
+		tw_dev->is_unit_present[i] = 0;
+
+	tw_dev->num_units = 0;
+	tw_dev->num_aborts = 0;
+	tw_dev->num_resets = 0;
+	tw_dev->free_head = TW_Q_START;
+	tw_dev->free_tail = TW_Q_LENGTH - 1;
+	tw_dev->posted_request_count = 0;
+	tw_dev->max_posted_request_count = 0;
+	tw_dev->max_sgl_entries = 0;
+	tw_dev->sgl_entries = 0;
+	tw_dev->host = NULL;
+	tw_dev->pending_head = TW_Q_START;
+	tw_dev->pending_tail = TW_Q_START;
+	tw_dev->aen_head = 0;
+	tw_dev->aen_tail = 0;
+	tw_dev->sector_count = 0;
+	tw_dev->max_sector_count = 0;
+	spin_lock_init(&tw_dev->tw_lock);
+	return 0;
+} /* End tw_initialize_device_extension() */
+
+/* This function will get unit info from the controller */
+int tw_initialize_units(TW_Device_Extension *tw_dev) 
+{
+	int found = 0;
+	unsigned char request_id = 0;
+	TW_Command *command_packet;
+	TW_Param *param;
+	int i, imax, num_units = 0;
+	u32 status_reg_addr, status_reg_value;
+	u32 command_que_addr, command_que_value;
+	u32 response_que_addr;
+	TW_Response_Queue response_queue;
+	u32 param_value;
+	unsigned char *is_unit_present;
+
+	dprintk(KERN_NOTICE "3w-xxxx: tw_initialize_units()\n");
+
+	status_reg_addr = tw_dev->registers.status_reg_addr;
+	command_que_addr = tw_dev->registers.command_que_addr;
+	response_que_addr = tw_dev->registers.response_que_addr;
+  
+	/* Setup the command packet */
+	command_packet = (TW_Command *)tw_dev->command_packet_virtual_address[request_id];
+	if (command_packet == NULL) {
+		printk(KERN_WARNING "3w-xxxx: tw_initialize_units(): Bad command packet virtual address.\n");
+		return 1;
+	}
+	memset(command_packet, 0, sizeof(TW_Sector));
+	command_packet->byte0.opcode      = TW_OP_GET_PARAM;
+	command_packet->byte0.sgl_offset  = 2;
+	command_packet->size              = 4;
+	command_packet->request_id        = request_id;
+	command_packet->byte3.unit        = 0;
+	command_packet->byte3.host_id     = 0;
+	command_packet->status            = 0;
+	command_packet->flags             = 0;
+	command_packet->byte6.block_count = 1;
+
+	/* Now setup the param */
+	if (tw_dev->alignment_virtual_address[request_id] == NULL) {
+		printk(KERN_WARNING "3w-xxxx: tw_initialize_units(): Bad alignment virtual address.\n");
+		return 1;
+	}
+	param = (TW_Param *)tw_dev->alignment_virtual_address[request_id];
+	memset(param, 0, sizeof(TW_Sector));
+	param->table_id = 3;       /* unit summary table */
+	param->parameter_id = 3;   /* unitstatus parameter */
+	param->parameter_size_bytes = TW_MAX_UNITS;
+	param_value = tw_dev->alignment_physical_address[request_id];
+	if (param_value == 0) {
+		printk(KERN_WARNING "3w-xxxx: tw_initialize_units(): Bad alignment physical address.\n");
+		return 1;
+	}
+
+	command_packet->byte8.param.sgl[0].address = param_value;
+	command_packet->byte8.param.sgl[0].length = sizeof(TW_Sector);
+
+	/* Post the command packet to the board */
+	command_que_value = tw_dev->command_packet_physical_address[request_id];
+	if (command_que_value == 0) {
+		printk(KERN_WARNING "3w-xxxx: tw_initialize_units(): Bad command packet physical address.\n");
+		return 1;
+	}
+	outl(command_que_value, command_que_addr);
+
+	/* Poll for completion */
+	imax = TW_POLL_MAX_RETRIES;
+	for(i=0; i<imax; i++) {
+		mdelay(10);
+		status_reg_value = inl(status_reg_addr);
+		if (tw_check_bits(status_reg_value)) {
+			printk(KERN_WARNING "3w-xxxx: tw_initialize_units(): Unexpected bits.\n");
+			return 1;
+		}
+		if ((status_reg_value & TW_STATUS_RESPONSE_QUEUE_EMPTY) == 0) {
+			response_queue.value = inl(response_que_addr);
+			request_id = (unsigned char)response_queue.u.response_id;
+			if (request_id != 0) {
+				/* unexpected request id */
+				printk(KERN_WARNING "3w-xxxx: tw_initialize_units(): Unexpected request id.\n");
+				return 1;
+			}
+			if (command_packet->status != 0) {
+				/* bad response */
+				printk(KERN_WARNING "3w-xxxx: tw_initialize_units(): Bad response, flags = 0x%x.\n", command_packet->flags);
+				return 1;
+			}
+			found = 1;
+			break;
+		}
+	}
+	if (found == 0) {
+		/* response never received */
+		printk(KERN_WARNING "3w-xxxx: tw_initialize_units(): No response.\n");
+		return 1;
+	}
+
+	param = (TW_Param *)tw_dev->alignment_virtual_address[request_id];
+	is_unit_present = (unsigned char *)&(param->data[0]);
+  
+	/* Show all units present */
+	imax = TW_MAX_UNITS;
+	for(i=0; i<imax; i++) {
+		if (is_unit_present[i] == 0) {
+			tw_dev->is_unit_present[i] = FALSE;
+		} else {
+			dprintk(KERN_NOTICE "3w-xxxx: tw_initialize_units(): Unit %d found.\n", i);
+			tw_dev->is_unit_present[i] = TRUE;
+			num_units++;
+		}
+	}
+	tw_dev->num_units = num_units;
+
+	if (num_units == 0) {
+		printk(KERN_NOTICE "3w-xxxx: tw_initialize_units(): No units found.\n");
+		return 1;
+	}
+  
+	return 0;
+} /* End tw_initialize_units() */
+
+/* This function is the interrupt service routine */
+static void tw_interrupt(int irq, void *dev_instance, struct pt_regs *regs) 
+{
+	int request_id;
+	u32 status_reg_addr, status_reg_value;
+	u32 response_que_addr;
+	TW_Device_Extension *tw_dev = (TW_Device_Extension *)dev_instance;
+	TW_Response_Queue response_que;
+	int error = 0;
+	int do_response_interrupt=0;
+	int do_attention_interrupt=0;
+	int do_host_interrupt=0;
+	int do_command_interrupt=0;
+	int flags = 0;
+	int flags2 = 0;
+	TW_Command *command_packet;
+	spin_lock_irqsave(&io_request_lock, flags);
+
+	if (tw_dev->tw_pci_dev->irq == irq) {
+		spin_lock_irqsave(&tw_dev->tw_lock, flags2);
+		dprintk(KERN_NOTICE "3w-xxxx: tw_interrupt()\n");
+
+		/* Read the registers */
+		status_reg_addr = tw_dev->registers.status_reg_addr;
+		response_que_addr = tw_dev->registers.response_que_addr;
+		status_reg_value = inl(status_reg_addr);
+
+		/* Check which interrupt */
+		if (status_reg_value & TW_STATUS_HOST_INTERRUPT)
+			do_host_interrupt=1;
+		if (status_reg_value & TW_STATUS_ATTENTION_INTERRUPT)
+			do_attention_interrupt=1;
+		if (status_reg_value & TW_STATUS_COMMAND_INTERRUPT)
+			do_command_interrupt=1;
+		if (status_reg_value & TW_STATUS_RESPONSE_INTERRUPT)
+			do_response_interrupt=1;
+
+		/* Handle host interrupt */
+		if (do_host_interrupt) {
+			dprintk(KERN_NOTICE "3w-xxxx: tw_interrupt(): Received host interrupt.\n");
+			tw_clear_host_interrupt(tw_dev);
+		}
+
+		/* Handle attention interrupt */
+		if (do_attention_interrupt) {
+			dprintk(KERN_NOTICE "3w-xxxx: tw_interrupt(): Received attention interrupt.\n");
+			tw_state_request_start(tw_dev, &request_id);
+			error = tw_aen_read_queue(tw_dev, request_id);
+			if (error) {
+				printk(KERN_WARNING "3w-xxxx: tw_interrupt(): Error reading aen queue.\n");
+				tw_dev->state[request_id] = TW_S_COMPLETED;
+				tw_state_request_finish(tw_dev, request_id);
+			} else {
+				dprintk(KERN_WARNING "3w-xxxx: tw_interrupt(): Clearing attention interrupt.\n");
+				tw_clear_attention_interrupt(tw_dev);
+			}
+		}
+
+		/* Handle command interrupt */
+		if (do_command_interrupt) {
+			/* Drain as many pending commands as we can */
+			while (tw_dev->pending_request_count > 0) {
+				request_id = tw_dev->pending_queue[tw_dev->pending_head];
+				if (tw_dev->state[request_id] != TW_S_PENDING) {
+					printk(KERN_WARNING "3w-xxxx: tw_interrupt(): Found request id that wasn't pending.\n");
+					break;
+				}
+				if (tw_post_command_packet(tw_dev, request_id)==0) {
+					if (tw_dev->pending_head == TW_Q_LENGTH-1) {
+						tw_dev->pending_head = TW_Q_START;
+					} else {
+						tw_dev->pending_head = tw_dev->pending_head + 1;
+					}
+					tw_dev->pending_request_count--;
+				} else {
+					break;
+				}
+			}
+			/* If there are no more pending requests, we mask command interrupt */
+			if (tw_dev->pending_request_count == 0) 
+				tw_mask_command_interrupt(tw_dev);
+		}
+
+		/* Handle response interrupt */
+		if (do_response_interrupt) {
+			/* Drain the response queue from the board */
+			while ((status_reg_value & TW_STATUS_RESPONSE_QUEUE_EMPTY) == 0) {
+				response_que.value = inl(response_que_addr);
+				request_id = response_que.u.response_id;
+				command_packet = (TW_Command *)tw_dev->command_packet_virtual_address[request_id];
+				if (command_packet->status != 0) {
+					printk(KERN_WARNING "3w-xxxx: tw_interrupt(): Bad response, flags = 0x%x.\n", command_packet->flags);
+				}
+				if (tw_dev->state[request_id] != TW_S_POSTED) {
+					printk(KERN_WARNING "3w-xxxx: tw_interrupt(): Received a request id (%d) (opcode = 0x%x) that wasn't posted.\n", request_id, command_packet->byte0.opcode);
+				}
+				error = 0;
+				dprintk(KERN_NOTICE "3w-xxxx: tw_interrupt(): Response queue request id: %d.\n", request_id);
+				/* Check for internal command */
+				if (tw_dev->srb[request_id] == 0) {
+					dprintk(KERN_WARNING "3w-xxxx: tw_interrupt(): Found internally posted command.\n");
+					error = tw_aen_complete(tw_dev, request_id);
+					if (error) {
+						printk(KERN_WARNING "3w-xxxx: tw_interrupt(): Error completing aen.\n");
+					}
+					status_reg_value = inl(status_reg_addr);
+					if (tw_check_bits(status_reg_value)) {
+						printk(KERN_WARNING "3w-xxxx: tw_interrupt(): Unexpected bits.\n");
+					}
+		} else {
+				switch (tw_dev->srb[request_id]->cmnd[0]) {
+					case READ_10:
+						dprintk(KERN_NOTICE "3w-xxxx: tw_interrupt(): caught READ_10\n");
+					case READ_6:
+						dprintk(KERN_NOTICE "3w-xxxx: tw_interrupt(): caught READ_6\n");
+						break;
+					case WRITE_10:
+						dprintk(KERN_NOTICE "3w-xxxx: tw_interrupt(): caught WRITE_10\n");
+					case WRITE_6:
+						dprintk(KERN_NOTICE "3w-xxxx: tw_interrupt(): caught WRITE_6\n");
+						break;
+					case INQUIRY:
+						dprintk(KERN_NOTICE "3w-xxxx: tw_interrupt(): caught INQUIRY\n");
+						error = tw_scsiop_inquiry_complete(tw_dev, request_id);
+						break;
+					case READ_CAPACITY:
+						dprintk(KERN_NOTICE "3w-xxxx: tw_interrupt(): caught READ_CAPACITY\n");
+						error = tw_scsiop_read_capacity_complete(tw_dev, request_id);
+						break;
+					case TW_IOCTL:
+						dprintk(KERN_NOTICE "3w-xxxx: tw_interrupt(): caught TW_IOCTL\n");
+						error = tw_ioctl_complete(tw_dev, request_id);
+						break;
+					default:
+						printk(KERN_WARNING "3w-xxxx: tw_interrupt(): Unknown scsi opcode: 0x%x.\n", tw_dev->srb[request_id]->cmnd[0]);
+						tw_dev->srb[request_id]->result = (DID_BAD_TARGET << 16);
+						tw_dev->srb[request_id]->scsi_done(tw_dev->srb[request_id]);
+					}
+					if (error) {
+						/* Tell scsi layer there was an error */
+						printk(KERN_WARNING "3w-xxxx: tw_interrupt(): Scsi Error.\n");
+						tw_dev->srb[request_id]->result = (DID_ERROR << 16);
+					} else {
+						/* Tell scsi layer command was a success */
+						tw_dev->srb[request_id]->result = (DID_OK << 16);
+					}
+					tw_dev->state[request_id] = TW_S_COMPLETED;
+					tw_state_request_finish(tw_dev, request_id);
+					tw_dev->posted_request_count--;
+					tw_dev->srb[request_id]->scsi_done(tw_dev->srb[request_id]);
+					status_reg_value = inl(status_reg_addr);
+					if (tw_check_bits(status_reg_value)) {
+						printk(KERN_WARNING "3w-xxxx: tw_interrupt(): Unexpected bits.\n");
+					}
+				}
+			}
+		}
+		spin_unlock_irqrestore(&tw_dev->tw_lock, flags2);
+	}
+	spin_unlock_irqrestore(&io_request_lock, flags);
+}	/* End tw_interrupt() */
+
+/* This function handles ioctls from userspace to the driver */
+int tw_ioctl(TW_Device_Extension *tw_dev, int request_id)
+{
+	unsigned char opcode;
+	int bufflen;
+	TW_Param *param;
+	TW_Command *command_packet;
+	u32 param_value;
+	TW_Ioctl *ioctl = NULL;
+	int tw_aen_code;
+
+	ioctl = (TW_Ioctl *)tw_dev->srb[request_id]->request_buffer;
+	if (ioctl == NULL) {
+		printk(KERN_WARNING "3w-xxxx: tw_ioctl(): Request buffer NULL.\n");
+		tw_dev->state[request_id] = TW_S_COMPLETED;
+		tw_state_request_finish(tw_dev, request_id);
+		tw_dev->srb[request_id]->result = (DID_OK << 16);
+		tw_dev->srb[request_id]->scsi_done(tw_dev->srb[request_id]);
+		return 0;
+	}
+	bufflen = tw_dev->srb[request_id]->request_bufflen;
+
+	/* Initialize command packet */
+	command_packet = (TW_Command *)tw_dev->command_packet_virtual_address[request_id];
+	if (command_packet == NULL) {
+		printk(KERN_WARNING "3w-xxxx: twioctl(): Bad command packet virtual address.\n");
+		tw_dev->state[request_id] = TW_S_COMPLETED;
+		tw_state_request_finish(tw_dev, request_id);
+		tw_dev->srb[request_id]->result = (DID_OK << 16);
+		tw_dev->srb[request_id]->scsi_done(tw_dev->srb[request_id]);
+		return 0;
+	}
+	memset(command_packet, 0, sizeof(TW_Sector));
+
+	/* Initialize param */
+	if (tw_dev->alignment_virtual_address[request_id] == NULL) {
+		printk(KERN_WARNING "3w-xxxx: tw_ioctl(): Bad alignment virtual address.\n");
+		tw_dev->state[request_id] = TW_S_COMPLETED;
+		tw_state_request_finish(tw_dev, request_id);
+		tw_dev->srb[request_id]->result = (DID_OK << 16);
+		tw_dev->srb[request_id]->scsi_done(tw_dev->srb[request_id]);
+		return 0;
+	}
+	param = (TW_Param *)tw_dev->alignment_virtual_address[request_id];
+	memset(param, 0, sizeof(TW_Sector));
+
+	dprintk(KERN_NOTICE "opcode = %d table_id = %d parameter_id = %d parameter_size_bytes = %d\n", ioctl->opcode, ioctl->table_id, ioctl->parameter_id,, ioctl->parameter_size_bytes);
+	opcode = ioctl->opcode;
+
+	switch (opcode) {
+		case TW_OP_NOP:
+			dprintk(KERN_NOTICE "3w-xxxx: tw_ioctl(): caught TW_OP_NOP.\n");
+			command_packet->byte0.opcode = TW_OP_NOP;
+			break;
+		case TW_OP_GET_PARAM:
+			dprintk(KERN_NOTICE "3w-xxxx: tw_ioctl(): caught TW_OP_GET_PARAM.\n");
+			command_packet->byte0.opcode = TW_OP_GET_PARAM;
+			param->table_id = ioctl->table_id;
+			param->parameter_id = ioctl->parameter_id;
+			param->parameter_size_bytes = ioctl->parameter_size_bytes;
+			tw_dev->ioctl_size[request_id] = ioctl->parameter_size_bytes;
+			dprintk(KERN_NOTICE "table_id = %d parameter_id = %d parameter_size_bytes %d\n", param->table_id, param->parameter_id, param->parameter_size_bytes);
+			break;
+		case TW_OP_SET_PARAM:
+			dprintk(KERN_NOTICE "3w-xxxx: tw_ioctl(): caught TW_OP_SET_PARAM: table_id = %d, parameter_id = %d, parameter_size_bytes = %d.\n",
+			ioctl->table_id, ioctl->parameter_id, ioctl->parameter_size_bytes);
+			command_packet->byte0.opcode = TW_OP_SET_PARAM;
+			param->table_id = ioctl->table_id;
+			param->parameter_id = ioctl->parameter_id;
+			param->parameter_size_bytes = ioctl->parameter_size_bytes;
+			memcpy(param->data, ioctl->data, ioctl->parameter_size_bytes);
+			break;
+		case TW_OP_AEN_LISTEN:
+			dprintk(KERN_NOTICE "3w-xxxx: tw_ioctl(): caught TW_OP_AEN_LISTEN.\n");
+			if (tw_dev->aen_head == tw_dev->aen_tail) {
+				/* aen queue empty */
+				dprintk(KERN_NOTICE "3w-xxxx: tw_ioctl(): Aen queue empty.\n");
+				tw_aen_code = TW_AEN_QUEUE_EMPTY;
+				memcpy(tw_dev->srb[request_id]->request_buffer, &tw_aen_code, ioctl->parameter_size_bytes);
+			} else {
+				/* Copy aen queue entry to request buffer */
+				dprintk(KERN_NOTICE "3w-xxxx: tw_ioctl(): Returning aen 0x%x\n", tw_dev->aen_queue[tw_dev->aen_head]);
+				tw_aen_code = tw_dev->aen_queue[tw_dev->aen_head];
+				memcpy(tw_dev->srb[request_id]->request_buffer, &tw_aen_code, ioctl->parameter_size_bytes);
+				if (tw_dev->aen_head == TW_Q_LENGTH - 1) {
+					tw_dev->aen_head = TW_Q_START;
+				} else {
+					tw_dev->aen_head = tw_dev->aen_head + 1;
+				}
+			}
+			tw_dev->state[request_id] = TW_S_COMPLETED;
+			tw_state_request_finish(tw_dev, request_id);
+			tw_dev->srb[request_id]->result = (DID_OK << 16);
+			tw_dev->srb[request_id]->scsi_done(tw_dev->srb[request_id]);
+			return 0;
+		default:
+			printk(KERN_WARNING "3w-xxxx: Unknown ioctl 0x%x.\n", opcode);
+			tw_dev->state[request_id] = TW_S_COMPLETED;
+			tw_state_request_finish(tw_dev, request_id);
+			tw_dev->srb[request_id]->result = (DID_OK << 16);
+			tw_dev->srb[request_id]->scsi_done(tw_dev->srb[request_id]);
+			return 0;
+	}
+
+	param_value = tw_dev->alignment_physical_address[request_id];
+	if (param_value == 0) {
+		printk(KERN_WARNING "3w-xxxx: tw_ioctl(): Bad alignment physical address.\n");
+		tw_dev->state[request_id] = TW_S_COMPLETED;
+		tw_state_request_finish(tw_dev, request_id);
+		tw_dev->srb[request_id]->result = (DID_OK << 16);
+		tw_dev->srb[request_id]->scsi_done(tw_dev->srb[request_id]);
+	}
+
+	command_packet->byte8.param.sgl[0].address = param_value;
+	command_packet->byte8.param.sgl[0].length = sizeof(TW_Sector);
+
+	command_packet->byte0.sgl_offset = 2;
+	command_packet->size = 4;
+	command_packet->request_id = request_id;
+	command_packet->byte3.unit = 0;
+	command_packet->byte3.host_id = 0;
+	command_packet->status = 0;
+	command_packet->flags = 0;
+	command_packet->byte6.parameter_count = 1;
+
+	/* Now try to post the command to the board */
+	tw_post_command_packet(tw_dev, request_id);
+
+	return 0;
+} /* End tw_ioctl() */
+
+/* This function is called by the isr to complete ioctl requests */
+int tw_ioctl_complete(TW_Device_Extension *tw_dev, int request_id)
+{
+	unsigned char *param_data;
+	unsigned char *buff;
+	TW_Param *param;
+
+	dprintk(KERN_NOTICE "3w-xxxx: tw_ioctl_complete()\n");
+	buff = tw_dev->srb[request_id]->request_buffer;
+	if (buff == NULL) {
+		printk(KERN_WARNING "3w-xxxx: tw_ioctl_complete(): Request buffer NULL.\n");
+		return 1;
+	}
+	dprintk(KERN_NOTICE "3w-xxxx: tw_ioctl_complete(): Request_bufflen = %d\n", tw_dev->srb[request_id]->request_bufflen);
+	memset(buff, 0, tw_dev->srb[request_id]->request_bufflen);
+	param = (TW_Param *)tw_dev->alignment_virtual_address[request_id];
+	if (param == NULL) {
+		printk(KERN_WARNING "3w-xxxx: tw_scsiop_read_capacity_complete(): Bad alignment virtual address.\n");
+		return 1;
+	}
+	param_data = &(param->data[0]);
+
+	memcpy(buff, param_data, tw_dev->ioctl_size[request_id]);
+
+	return 0;
+} /* End tw_ioctl_complete() */
+
+/* This function will mask the command interrupt */
+void tw_mask_command_interrupt(TW_Device_Extension *tw_dev)
+{
+	u32 control_reg_addr, control_reg_value;
+	
+	control_reg_addr = tw_dev->registers.control_reg_addr;
+	control_reg_value = TW_CONTROL_MASK_COMMAND_INTERRUPT;
+	outl(control_reg_value, control_reg_addr);
+} /* End tw_mask_command_interrupt() */
+
+/* This function will poll the status register for a flag */
+int tw_poll_status(TW_Device_Extension *tw_dev, u32 flag, int seconds)
+{
+	u32 status_reg_addr, status_reg_value;
+	struct timeval before, timeout;
+
+	status_reg_addr = tw_dev->registers.status_reg_addr;
+	do_gettimeofday(&before);
+	status_reg_value = inl(status_reg_addr);
+
+	while ((status_reg_value & flag) != flag) {
+		status_reg_value = inl(status_reg_addr);
+		do_gettimeofday(&timeout);
+		if (before.tv_sec + seconds < timeout.tv_sec) { 
+			printk(KERN_WARNING "3w-xxxx: tw_poll_status(): Flag 0x%x not found.\n", flag);
+			return 1;
+		}
+		mdelay(1);
+	}
+	return 0;
+} /* End tw_poll_status() */
+
+/* This function will attempt to post a command packet to the board */
+int tw_post_command_packet(TW_Device_Extension *tw_dev, int request_id)
+{
+	u32 status_reg_addr, status_reg_value;
+	u32 command_que_addr, command_que_value;
+
+	dprintk(KERN_NOTICE "3w-xxxx: tw_post_command_packet()\n");
+	command_que_addr = tw_dev->registers.command_que_addr;
+	command_que_value = tw_dev->command_packet_physical_address[request_id];
+	status_reg_addr = tw_dev->registers.status_reg_addr;
+	status_reg_value = inl(status_reg_addr);
+
+	if (tw_check_bits(status_reg_value)) 
+		printk(KERN_WARNING "3w-xxxx: tw_post_command_packet(): Unexpected bits.\n");
+
+	if ((status_reg_value & TW_STATUS_COMMAND_QUEUE_FULL) == 0) {
+		/* We successfully posted the command packet */
+		outl(command_que_value, command_que_addr);
+		tw_dev->state[request_id] = TW_S_POSTED;
+		tw_dev->posted_request_count++;
+		if (tw_dev->posted_request_count > tw_dev->max_posted_request_count) {
+			tw_dev->max_posted_request_count = tw_dev->posted_request_count;
+		}
+	} else {
+		/* Couldn't post the command packet, so we do it in the isr */
+		if (tw_dev->state[request_id] != TW_S_PENDING) {
+			tw_dev->state[request_id] = TW_S_PENDING;
+			tw_dev->pending_request_count++;
+			if (tw_dev->pending_request_count > tw_dev->max_pending_request_count) {
+				tw_dev->max_pending_request_count = tw_dev->pending_request_count;
+			}
+			tw_dev->pending_queue[tw_dev->pending_tail] = request_id;
+			if (tw_dev->pending_tail == TW_Q_LENGTH-1) {
+				tw_dev->pending_tail = TW_Q_START;
+			} else {
+				tw_dev->pending_tail = tw_dev->pending_tail + 1;
+			}
+		} 
+		tw_unmask_command_interrupt(tw_dev);
+		return 1;
+	}
+	return 0;
+} /* End tw_post_command_packet() */
+
+/* This function will reset a device extension */
+int tw_reset_device_extension(TW_Device_Extension *tw_dev) 
+{
+	int imax = 0;
+	int i = 0;
+	Scsi_Cmnd *srb;
+
+	dprintk(KERN_NOTICE "3w-xxxx: tw_reset_device_extension()\n");
+	imax = TW_Q_LENGTH;
+
+	if (tw_reset_sequence(tw_dev)) {
+		printk(KERN_WARNING "3w-xxxx: tw_reset_device_extension(): Reset sequence failed for card %d.\n", tw_dev->host->host_no);
+		return 1;
+	}
+
+	/* Abort all requests that are in progress */
+	for (i=0;i<imax;i++) {
+		if ((tw_dev->state[i] != TW_S_FINISHED) && 
+		    (tw_dev->state[i] != TW_S_INITIAL) &&
+		    (tw_dev->state[i] != TW_S_COMPLETED)) {
+			srb = tw_dev->srb[i];
+			srb->result = (DID_RESET << 16);
+			tw_dev->srb[i]->scsi_done(tw_dev->srb[i]);
+		}
+	}
+
+	/* Reset queues and counts */
+	for (i=0;i<imax;i++) {
+		tw_dev->free_queue[i] = i;
+		tw_dev->state[i] = TW_S_INITIAL;
+	}
+	tw_dev->free_head = TW_Q_START;
+	tw_dev->free_tail = TW_Q_LENGTH - 1;
+	tw_dev->posted_request_count = 0;
+	tw_dev->pending_request_count = 0;
+	tw_dev->pending_head = TW_Q_START;
+	tw_dev->pending_tail = TW_Q_START;
+
+	return 0;
+} /* End tw_reset_device_extension() */
+
+/* This function will reset a controller */
+int tw_reset_sequence(TW_Device_Extension *tw_dev) 
+{
+	int error = 0;
+	int tries = 0;
+
+	/* Disable interrupts */
+	tw_disable_interrupts(tw_dev);
+
+	/* Reset the board */
+	while (tries < TW_MAX_RESET_TRIES) {
+		tw_soft_reset(tw_dev);
+
+		error = tw_aen_drain_queue(tw_dev);
+		if (error) {
+			printk(KERN_WARNING "3w-xxxx: tw_reset_sequence(): No attention interrupt for card %d.\n", tw_dev->host->host_no);
+			tries++;
+			continue;
+		}
+
+		/* Check for controller errors */
+		if (tw_check_errors(tw_dev)) {
+			printk(KERN_WARNING "3w-xxxx: tw_reset_sequence(): Controller errors found, soft resetting card %d.\n", tw_dev->host->host_no);
+			tries++;
+			continue;
+		}
+
+		/* Empty the response queue again */
+		error = tw_empty_response_que(tw_dev);
+		if (error) {
+			printk(KERN_WARNING "3w-xxxx: tw_reset_sequence(): Couldn't empty response queue for card %d.\n", tw_dev->host->host_no);
+			tries++;
+			continue;
+		}
+
+		/* Now the controller is in a good state */
+		break;
+	}
+
+	if (tries >= TW_MAX_RESET_TRIES) {
+		printk(KERN_WARNING "3w-xxxx: tw_reset_sequence(): Controller error or no attention interrupt: giving up for card %d.\n", tw_dev->host->host_no);
+		return 1;
+	}
+
+	error = tw_initconnection(tw_dev);
+	if (error) {
+		printk(KERN_WARNING "3w-xxxx: tw_reset_sequence(): Couldn't initconnection for card %d.\n", tw_dev->host->host_no);
+		return 1;
+	}
+
+	/* Re-enable interrupts */
+	tw_enable_interrupts(tw_dev);
+
+	return 0;
+} /* End tw_reset_sequence() */
+
+/* This funciton returns unit geometry in cylinders/heads/sectors */
+int tw_scsi_biosparam(Disk *disk, kdev_t dev, int geom[]) 
+{
+	int heads, sectors, cylinders;
+	TW_Device_Extension *tw_dev;
+	
+	dprintk(KERN_NOTICE "3w-xxxx: tw_scsi_biosparam()\n");
+	tw_dev = (TW_Device_Extension *)disk->device->host->hostdata;
+
+	heads = 64;
+	sectors = 32;
+	cylinders = disk->capacity / (heads * sectors);
+
+	if (disk->capacity >= 0x200000) {
+		heads = 255;
+		sectors = 63;
+		cylinders = disk->capacity / (heads * sectors);
+	}
+
+	dprintk(KERN_NOTICE "3w-xxxx: tw_scsi_biosparam(): heads = %d, sectors = %d, cylinders = %d\n", heads, sectors, cylinders);
+	geom[0] = heads;			 
+	geom[1] = sectors;
+	geom[2] = cylinders;
+
+	return 0;
+} /* End tw_scsi_biosparam() */
+
+/* This function will find and initialize any cards */
+int tw_scsi_detect(Scsi_Host_Template *tw_host)
+{
+	dprintk(KERN_NOTICE "3w-xxxx: tw_scsi_detect()\n");
+
+	/* Check if the kernel has PCI interface compiled in */
+	if (!pci_present()) {
+		printk(KERN_WARNING "3w-xxxx: tw_scsi_detect(): No pci interface present.\n");
+		return 0;
+	}
+
+	return(tw_findcards(tw_host));
+} /* End tw_scsi_detect() */
+
+/* This is the new scsi eh abort function */
+int tw_scsi_eh_abort(Scsi_Cmnd *SCpnt) 
+{
+	TW_Device_Extension *tw_dev=NULL;
+	int i = 0;
+
+	dprintk(KERN_NOTICE "3w-xxxx: tw_scsi_eh_abort()\n");
+
+	if (!SCpnt) {
+		printk(KERN_WARNING "3w-xxxx: tw_scsi_eh_abort(): Invalid Scsi_Cmnd.\n");
+		return (FAILED);
+	}
+
+	tw_dev = (TW_Device_Extension *)SCpnt->host->hostdata;
+	if (tw_dev == NULL) {
+		printk(KERN_WARNING "3w-xxxx: tw_scsi_eh_abort(): Invalid device extension.\n");
+		return (FAILED);
+	}
+
+	spin_lock(&tw_dev->tw_lock);
+	tw_dev->num_aborts++;
+
+	/* If the command hasn't been posted yet, we can do the abort */
+	for (i=0;i<TW_Q_LENGTH;i++) {
+		if (tw_dev->srb[i] == SCpnt) {
+			if (tw_dev->state[i] == TW_S_STARTED) {
+				printk(KERN_WARNING "3w-xxxx: tw_scsi_eh_abort(): Abort succeeded for started Scsi_Cmnd 0x%x\n", (u32)tw_dev->srb[i]);
+				tw_dev->state[i] = TW_S_COMPLETED;
+				tw_state_request_finish(tw_dev, i);
+				spin_unlock(&tw_dev->tw_lock);
+				return (SUCCESS);
+			}
+			if (tw_dev->state[i] == TW_S_PENDING) {
+				printk(KERN_WARNING "3w-xxxx: tw_scsi_eh_abort(): Abort succeeded for pending Scsi_Cmnd 0x%x\n", (u32)tw_dev->srb[i]);
+				if (tw_dev->pending_head == TW_Q_LENGTH-1) {
+					tw_dev->pending_head = TW_Q_START;
+				} else {
+					tw_dev->pending_head = tw_dev->pending_head + 1;
+				}
+				tw_dev->pending_request_count--;
+				tw_dev->state[i] = TW_S_COMPLETED;
+				tw_state_request_finish(tw_dev, i);
+				spin_unlock(&tw_dev->tw_lock);
+				return (SUCCESS);
+			}
+		}
+	}
+
+	/* If the command has already been posted, we have to reset the card */
+	printk(KERN_WARNING "3w-xxxx: tw_scsi_eh_abort(): Abort failed for unknown Scsi_Cmnd 0x%x, resetting card %d.\n", (u32)SCpnt, tw_dev->host->host_no);
+
+	if (tw_reset_device_extension(tw_dev)) {
+		printk(KERN_WARNING "3w-xxxx: tw_scsi_eh_abort(): Reset failed for card %d.\n", tw_dev->host->host_no);
+		spin_unlock(&tw_dev->tw_lock);
+		return (FAILED);
+	}
+	spin_unlock(&tw_dev->tw_lock);
+
+	return (SUCCESS);
+} /* End tw_scsi_eh_abort() */
+
+/* This is the new scsi eh reset function */
+int tw_scsi_eh_reset(Scsi_Cmnd *SCpnt) 
+{
+	TW_Device_Extension *tw_dev=NULL;
+	int flags = 0;
+
+	dprintk(KERN_NOTICE "3w-xxxx: tw_scsi_eh_reset()\n");
+
+	if (!SCpnt) {
+		printk(KERN_WARNING "3w-xxxx: tw_scsi_eh_reset(): Invalid Scsi_Cmnd.\n");
+		return (FAILED);
+	}
+
+	tw_dev = (TW_Device_Extension *)SCpnt->host->hostdata;
+	if (tw_dev == NULL) {
+		printk(KERN_WARNING "3w-xxxx: tw_scsi_eh_reset(): Invalid device extension.\n");
+		return (FAILED);
+	}
+
+	spin_lock_irqsave(&tw_dev->tw_lock, flags);
+	tw_dev->num_resets++;
+
+	/* Now reset the card and some of the device extension data */
+	if (tw_reset_device_extension(tw_dev)) {
+		printk(KERN_WARNING "3w-xxxx: tw_scsi_eh_reset(): Reset failed for card %d.\n", tw_dev->host->host_no);
+		spin_unlock_irqrestore(&tw_dev->tw_lock, flags);
+		return (FAILED);
+	}
+	printk(KERN_WARNING "3w-xxxx: tw_scsi_eh_reset(): Reset succeeded for card %d.\n", tw_dev->host->host_no);
+	spin_unlock_irqrestore(&tw_dev->tw_lock, flags);
+
+	return (SUCCESS);
+} /* End tw_scsi_eh_reset() */
+
+/* This function handles input and output from /proc/scsi/3w-xxxx/x */
+int tw_scsi_proc_info(char *buffer, char **start, off_t offset, int length, int hostno, int inout) 
+{
+	TW_Device_Extension *tw_dev = NULL;
+	TW_Info info;
+	int i;
+	int j;
+
+	dprintk(KERN_NOTICE "3w-xxxx: tw_scsi_proc_info()\n");
+
+	/* Find the correct device extension */
+	for (i=0;i<tw_device_extension_count;i++) 
+		if (tw_device_extension_list[i]->host->host_no == hostno) 
+			tw_dev = tw_device_extension_list[i];
+	if (tw_dev == NULL) {
+		printk(KERN_WARNING "3w-xxxx: tw_scsi_proc_info(): Couldn't locate device extension.\n");
+		return (-EINVAL);
+	}
+
+	info.buffer = buffer;
+	info.length = length;
+	info.offset = offset;
+	info.position = 0;
+	
+	if (inout) {
+		/* Write */
+		if (strncmp(buffer, "debug", 5) == 0) {
+			printk(KERN_INFO "3w-xxxx: Posted commands:\n");
+			for (j=0;j<TW_Q_LENGTH;j++) {
+				if (tw_dev->state[j] == TW_S_POSTED) {
+					TW_Command *command = (TW_Command *)tw_dev->command_packet_virtual_address[j];
+					printk(KERN_INFO "3w-xxxx: Request_id: %d\n", j);
+					printk(KERN_INFO "Opcode: 0x%x\n", command->byte0.opcode);
+					printk(KERN_INFO "Block_count: 0x%x\n", command->byte6.block_count);
+					printk(KERN_INFO "LBA: 0x%x\n", (u32)command->byte8.io.lba);
+					printk(KERN_INFO "Physical command packet addr: 0x%x\n", tw_dev->command_packet_physical_address[j]);
+					printk(KERN_INFO "Scsi_Cmnd: 0x%x\n", (u32)tw_dev->srb[j]);
+				}
+			}
+			printk(KERN_INFO "3w-xxxx: Free_head: %3d\n", tw_dev->free_head);
+			printk(KERN_INFO "3w-xxxx: Free_tail: %3d\n", tw_dev->free_tail);
+		} 
+		return length;
+	} else {
+		/* Read */
+		if (start) {
+			*start = buffer;
+		}
+		tw_copy_info(&info, "scsi%d: 3ware Storage Controller\n", hostno);
+		tw_copy_info(&info, "Driver version: %s\n", tw_driver_version);
+		tw_copy_info(&info, "Current commands posted:       %3d\n", tw_dev->posted_request_count);
+		tw_copy_info(&info, "Max commands posted:           %3d\n", tw_dev->max_posted_request_count);
+		tw_copy_info(&info, "Current pending commands:      %3d\n", tw_dev->pending_request_count);
+		tw_copy_info(&info, "Max pending commands:          %3d\n", tw_dev->max_pending_request_count);
+		tw_copy_info(&info, "Last sgl length:               %3d\n", tw_dev->sgl_entries);
+		tw_copy_info(&info, "Max sgl length:                %3d\n", tw_dev->max_sgl_entries);
+		tw_copy_info(&info, "Last sector count:             %3d\n", tw_dev->sector_count);
+		tw_copy_info(&info, "Max sector count:              %3d\n", tw_dev->max_sector_count);
+		tw_copy_info(&info, "Resets:                        %3d\n", tw_dev->num_resets);
+		tw_copy_info(&info, "Aborts:                        %3d\n", tw_dev->num_aborts);
+	}
+	if (info.position > info.offset) {
+		return (info.position - info.offset);
+	} else { 
+		return 0;
+	}
+} /* End tw_scsi_proc_info() */
+
+/* This is the main scsi queue function to handle scsi opcodes */
+int tw_scsi_queue(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)) 
+{
+	unsigned char *command = SCpnt->cmnd;
+	int request_id = 0;
+	int error = 0;
+	int flags = 0;
+	TW_Device_Extension *tw_dev = (TW_Device_Extension *)SCpnt->host->hostdata;
+
+	spin_lock_irqsave(&tw_dev->tw_lock, flags);
+	dprintk(KERN_NOTICE "3w-xxxx: tw_scsi_queue()\n");
+
+	/* Skip scsi command if it isn't for us */
+	if ((tw_dev->is_unit_present[SCpnt->target] == FALSE) || (SCpnt->lun != 0)) {
+		SCpnt->result = (DID_BAD_TARGET << 16);
+		done(SCpnt);
+		spin_unlock_irqrestore(&tw_dev->tw_lock, flags);
+		return 0;
+	}
+	if (done == NULL) {
+		printk(KERN_WARNING "3w-xxxx: tw_scsi_queue(): Invalid done function.\n");
+		SCpnt->result = (DID_ERROR << 16);
+		done(SCpnt);
+		spin_unlock_irqrestore(&tw_dev->tw_lock, flags);
+		return 0;
+	}
+	if (tw_dev == NULL) {
+		printk(KERN_WARNING "3w-xxxx: tw_scsi_queue(): Invalid device extension.\n");
+		SCpnt->result = (DID_ERROR << 16);
+		done(SCpnt);
+		spin_unlock_irqrestore(&tw_dev->tw_lock, flags);
+		return 0;
+	}
+	
+	/* Save done function into Scsi_Cmnd struct */
+	SCpnt->scsi_done = done;
+		 
+	/* Queue the command and get a request id */
+	tw_state_request_start(tw_dev, &request_id);
+
+	/* Save the scsi command for use by the ISR */
+	tw_dev->srb[request_id] = SCpnt;
+
+	switch (*command) {
+		case READ_10:
+			dprintk(KERN_NOTICE "3w-xxxx: tw_scsi_queue(): caught READ_10.\n");
+		case READ_6:
+			dprintk(KERN_NOTICE "3w-xxxx: tw_scsi_queue(): caught READ_6.\n");
+		case WRITE_10:
+			dprintk(KERN_NOTICE "3w-xxxx: tw_scsi_queue(): caught WRITE_10.\n");
+		case WRITE_6:
+			dprintk(KERN_NOTICE "3w-xxxx: tw_scsi_queue(): caught WRITE_6.\n");
+			error = tw_scsiop_read_write(tw_dev, request_id);
+			break;
+		case TEST_UNIT_READY:
+			dprintk(KERN_NOTICE "3w-xxxx: tw_scsi_queue(): caught TEST_UNIT_READY.\n");
+			error = tw_scsiop_test_unit_ready(tw_dev, request_id);
+			break;
+		case INQUIRY:
+			dprintk(KERN_NOTICE "3w-xxxx: tw_scsi_queue(): caught INQUIRY.\n");
+			error = tw_scsiop_inquiry(tw_dev, request_id);
+			break;
+		case READ_CAPACITY:
+			dprintk(KERN_NOTICE "3w-xxxx: tw_scsi_queue(): caught READ_CAPACITY.\n");
+			error = tw_scsiop_read_capacity(tw_dev, request_id);
+			break;
+		case TW_IOCTL:
+			dprintk(KERN_NOTICE "3w-xxxx: tw_scsi_queue(): caught TW_SCSI_IOCTL.\n");
+			error = tw_ioctl(tw_dev, request_id);
+			break;
+		default:
+			printk(KERN_NOTICE "3w-xxxx: tw_scsi_queue(): Unknown scsi opcode: 0x%x\n", *command);
+			tw_dev->state[request_id] = TW_S_COMPLETED;
+			tw_state_request_finish(tw_dev, request_id);
+			SCpnt->result = (DID_BAD_TARGET << 16);
+			done(SCpnt);
+	}
+	if (error) {
+		tw_dev->state[request_id] = TW_S_COMPLETED;
+		tw_state_request_finish(tw_dev, request_id);
+		SCpnt->result = (DID_ERROR << 16);
+		done(SCpnt);
+	}
+	spin_unlock_irqrestore(&tw_dev->tw_lock, flags);
+
+	return 0;
+} /* End tw_scsi_queue() */
+
+/* This function will release the resources on an rmmod call */
+int tw_scsi_release(struct Scsi_Host *tw_host) 
+{
+	TW_Device_Extension *tw_dev;
+	tw_dev = (TW_Device_Extension *)tw_host->hostdata;
+
+	dprintk(KERN_NOTICE "3w-xxxx: tw_scsi_release()\n");
+
+	/* Free up the IO region */
+	release_region((tw_dev->tw_pci_dev->base_address[0]), TW_IO_ADDRESS_RANGE);
+
+	/* Free up the IRQ */
+	free_irq(tw_dev->tw_pci_dev->irq, tw_dev);
+
+	/* Free up device extension resources */
+	tw_free_device_extension(tw_dev);
+
+	/* Tell kernel scsi-layer we are gone */
+	scsi_unregister(tw_host);
+
+	return 0;
+} /* End tw_scsi_release() */
+
+/* This function handles scsi inquiry commands */
+int tw_scsiop_inquiry(TW_Device_Extension *tw_dev, int request_id)
+{
+	TW_Param *param;
+	TW_Command *command_packet;
+	u32 command_que_value, command_que_addr;
+	u32 param_value;
+
+	dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_inquiry()\n");
+
+	/* Initialize command packet */
+	command_que_addr = tw_dev->registers.command_que_addr;
+	command_packet = (TW_Command *)tw_dev->command_packet_virtual_address[request_id];
+	if (command_packet == NULL) {
+		printk(KERN_WARNING "3w-xxxx: tw_scsiop_inquiry(): Bad command packet virtual address.\n");
+		return 1;
+	}
+	memset(command_packet, 0, sizeof(TW_Sector));
+	command_packet->byte0.opcode = TW_OP_GET_PARAM;
+	command_packet->byte0.sgl_offset = 2;
+	command_packet->size = 4;
+	command_packet->request_id = request_id;
+	command_packet->byte3.unit = 0;
+	command_packet->byte3.host_id = 0;
+	command_packet->status = 0;
+	command_packet->flags = 0;
+	command_packet->byte6.parameter_count = 1;
+
+	/* Now setup the param */
+	if (tw_dev->alignment_virtual_address[request_id] == NULL) {
+		printk(KERN_WARNING "3w-xxxx: tw_scsiop_inquiry(): Bad alignment virtual address.\n");
+		return 1;
+	}
+	param = (TW_Param *)tw_dev->alignment_virtual_address[request_id];
+	memset(param, 0, sizeof(TW_Sector));
+	param->table_id = 3;	 /* unit summary table */
+	param->parameter_id = 3; /* unitsstatus parameter */
+	param->parameter_size_bytes = TW_MAX_UNITS;
+	param_value = tw_dev->alignment_physical_address[request_id];
+	if (param_value == 0) {
+		printk(KERN_WARNING "3w-xxxx: tw_scsiop_inquiry(): Bad alignment physical address.\n");
+		return 1;
+	}
+
+	command_packet->byte8.param.sgl[0].address = param_value;
+	command_packet->byte8.param.sgl[0].length = sizeof(TW_Sector);
+	command_que_value = tw_dev->command_packet_physical_address[request_id];
+	if (command_que_value == 0) {
+		printk(KERN_WARNING "3w-xxxx: tw_scsiop_inquiry(): Bad command packet physical address.\n");
+		return 1;
+	}
+
+	/* Now try to post the command packet */
+	tw_post_command_packet(tw_dev, request_id);
+
+	return 0;
+} /* End tw_scsiop_inquiry() */
+
+/* This function is called by the isr to complete an inquiry command */
+int tw_scsiop_inquiry_complete(TW_Device_Extension *tw_dev, int request_id)
+{
+	unsigned char *is_unit_present;
+	unsigned char *request_buffer;
+	int i;
+	TW_Param *param;
+
+	dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_inquiry_complete()\n");
+
+	/* Fill request buffer */
+	if (tw_dev->srb[request_id]->request_buffer == NULL) {
+		printk(KERN_WARNING "3w-xxxx: tw_scsiop_inquiry_complete(): Request buffer NULL.\n");
+		return 1;
+	}
+	request_buffer = tw_dev->srb[request_id]->request_buffer;
+	memset(request_buffer, 0, tw_dev->srb[request_id]->request_bufflen);
+	request_buffer[0] = TYPE_DISK;									 /* Peripheral device type */
+	request_buffer[1] = 0;													 /* Device type modifier */
+	request_buffer[2] = 0;													 /* No ansi/iso compliance */
+	request_buffer[4] = 31;													/* Additional length */
+	memcpy(&request_buffer[8], "3ware   ", 8);	 /* Vendor ID */
+	memcpy(&request_buffer[16], "3w-xxxx         ", 16); /* Product ID */
+	memcpy(&request_buffer[32], tw_driver_version, 3);
+
+	param = (TW_Param *)tw_dev->alignment_virtual_address[request_id];
+	if (param == NULL) {
+		printk(KERN_WARNING "3w-xxxx: tw_scsiop_inquiry_complete(): Bad alignment virtual address.\n");
+		return 1;
+	}
+	is_unit_present = &(param->data[0]);
+
+	for (i=0 ; i<TW_MAX_UNITS; i++) {
+		if (is_unit_present[i] == 0) {
+			tw_dev->is_unit_present[i] = FALSE;
+		} else {
+			tw_dev->is_unit_present[i] = TRUE;
+			dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_inquiry_complete: Unit %d found.\n", i);
+		}
+	}
+
+	return 0;
+} /* End tw_scsiop_inquiry_complete() */
+
+/* This function handles scsi read_capacity commands */
+int tw_scsiop_read_capacity(TW_Device_Extension *tw_dev, int request_id) 
+{
+	TW_Param *param;
+	TW_Command *command_packet;
+	u32 command_que_addr, command_que_value;
+	u32 param_value;
+
+	dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_read_capacity()\n");
+
+	/* Initialize command packet */
+	command_que_addr = tw_dev->registers.command_que_addr;
+	command_packet = (TW_Command *)tw_dev->command_packet_virtual_address[request_id];
+
+	if (command_packet == NULL) {
+		dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_read_capacity(): Bad command packet virtual address.\n");
+		return 1;
+	}
+	memset(command_packet, 0, sizeof(TW_Sector));
+	command_packet->byte0.opcode = TW_OP_GET_PARAM;
+	command_packet->byte0.sgl_offset = 2;
+	command_packet->size = 4;
+	command_packet->request_id = request_id;
+	command_packet->byte3.unit = tw_dev->srb[request_id]->target;
+	command_packet->byte3.host_id = 0;
+	command_packet->status = 0;
+	command_packet->flags = 0;
+	command_packet->byte6.block_count = 1;
+
+	/* Now setup the param */
+	if (tw_dev->alignment_virtual_address[request_id] == NULL) {
+		dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_read_capacity(): Bad alignment virtual address.\n");
+		return 1;
+	}
+	param = (TW_Param *)tw_dev->alignment_virtual_address[request_id];
+	memset(param, 0, sizeof(TW_Sector));
+	param->table_id = TW_UNIT_INFORMATION_TABLE_BASE + 
+	tw_dev->srb[request_id]->target;
+	param->parameter_id = 4;	/* unitcapacity parameter */
+	param->parameter_size_bytes = 4;
+	param_value = tw_dev->alignment_physical_address[request_id];
+	if (param_value == 0) {
+		dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_read_capacity(): Bad alignment physical address.\n");
+		return 1;
+	}
+  
+	command_packet->byte8.param.sgl[0].address = param_value;
+	command_packet->byte8.param.sgl[0].length = sizeof(TW_Sector);
+	command_que_value = tw_dev->command_packet_physical_address[request_id];
+	if (command_que_value == 0) {
+		dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_read_capacity(): Bad command packet physical address.\n");
+		return 1;
+	}
+
+	/* Now try to post the command to the board */
+	tw_post_command_packet(tw_dev, request_id);
+  
+	return 0;
+} /* End tw_scsiop_read_capacity() */
+
+/* This function is called by the isr to complete a readcapacity command */
+int tw_scsiop_read_capacity_complete(TW_Device_Extension *tw_dev, int request_id)
+{
+	unsigned char *param_data;
+	u32 capacity;
+	char *buff;
+	TW_Param *param;
+
+	dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_read_capacity_complete()\n");
+
+	buff = tw_dev->srb[request_id]->request_buffer;
+	if (buff == NULL) {
+		printk(KERN_WARNING "3w-xxxx: tw_scsiop_read_capacity_complete(): Request buffer NULL.\n");
+		return 1;
+	}
+	memset(buff, 0, tw_dev->srb[request_id]->request_bufflen);
+	param = (TW_Param *)tw_dev->alignment_virtual_address[request_id];
+	if (param == NULL) {
+		printk(KERN_WARNING "3w-xxxx: tw_scsiop_read_capacity_complete(): Bad alignment virtual address.\n");
+		return 1;
+	}
+	param_data = &(param->data[0]);
+
+	capacity = (param_data[3] << 24) | (param_data[2] << 16) | 
+		   (param_data[1] << 8) | param_data[0];
+
+	dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_read_capacity_complete(): Capacity = 0x%x.\n", capacity);
+
+	/* Number of LBA's */
+	buff[0] = (capacity >> 24);
+	buff[1] = (capacity >> 16) & 0xff;
+	buff[2] = (capacity >> 8) & 0xff;
+	buff[3] = capacity & 0xff;
+
+	/* Block size in bytes (512) */
+	buff[4] = (TW_BLOCK_SIZE >> 24);
+	buff[5] = (TW_BLOCK_SIZE >> 16) & 0xff;
+	buff[6] = (TW_BLOCK_SIZE >> 8) & 0xff;
+	buff[7] = TW_BLOCK_SIZE & 0xff;
+
+	return 0;
+} /* End tw_scsiop_read_capacity_complete() */
+
+/* This function handles scsi read or write commands */
+int tw_scsiop_read_write(TW_Device_Extension *tw_dev, int request_id) 
+{
+	TW_Command *command_packet;
+	u32 command_que_addr, command_que_value = 0;
+	u32 lba = 0x0, num_sectors = 0x0;
+	int i;
+	Scsi_Cmnd *srb;
+	struct scatterlist *sglist;
+
+	dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_read_write()\n");
+
+	if (tw_dev->srb[request_id]->request_buffer == NULL) {
+		printk(KERN_WARNING "3w-xxxx: tw_scsiop_read_write(): Request buffer NULL.\n");
+		return 1;
+	}
+	sglist = (struct scatterlist *)tw_dev->srb[request_id]->request_buffer;
+	srb = tw_dev->srb[request_id];
+
+	/* Initialize command packet */
+	command_que_addr = tw_dev->registers.command_que_addr;
+	command_packet = (TW_Command *)tw_dev->command_packet_virtual_address[request_id];
+	if (command_packet == NULL) {
+		dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_read_write(): Bad command packet virtual address.\n");
+		return 1;
+	}
+
+	if (srb->cmnd[0] == READ_6 || srb->cmnd[0] == READ_10) {
+		command_packet->byte0.opcode = TW_OP_READ;
+	} else {
+		command_packet->byte0.opcode = TW_OP_WRITE;
+	}
+
+	command_packet->byte0.sgl_offset = 3;
+	command_packet->size = 5;
+	command_packet->request_id = request_id;
+	command_packet->byte3.unit = srb->target;
+	command_packet->byte3.host_id = 0;
+	command_packet->status = 0;
+	command_packet->flags = 0;
+
+	if (srb->cmnd[0] == READ_6 || srb->cmnd[0] == WRITE_6) {
+		lba = ((u32)srb->cmnd[1] << 16) | ((u32)srb->cmnd[2] << 8) | (u32)srb->cmnd[3];
+		num_sectors = (u32)srb->cmnd[4];
+	} else {
+		lba = ((u32)srb->cmnd[2] << 24) | ((u32)srb->cmnd[3] << 16) | 
+		       ((u32)srb->cmnd[4] << 8) | (u32)srb->cmnd[5];
+		num_sectors = (u32)srb->cmnd[8] | ((u32)srb->cmnd[7] << 8);
+	}
+  
+	/* Update sector statistic */
+	tw_dev->sector_count = num_sectors;
+	if (tw_dev->sector_count > tw_dev->max_sector_count)
+		tw_dev->max_sector_count = tw_dev->sector_count;
+  
+	dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_read_write(): lba = 0x%x num_sectors = 0x%x\n", lba, num_sectors);
+	command_packet->byte8.io.lba = lba;
+	command_packet->byte6.block_count = num_sectors;
+
+	/* Do this if there are no sg list entries */
+	if (tw_dev->srb[request_id]->use_sg == 0) {    
+		dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_read_write(): SG = 0\n");
+		command_packet->byte8.io.sgl[0].address = virt_to_bus(tw_dev->srb[request_id]->request_buffer);
+		command_packet->byte8.io.sgl[0].length = tw_dev->srb[request_id]->request_bufflen;
+	}
+
+	/* Do this if we have multiple sg list entries */
+	if (tw_dev->srb[request_id]->use_sg > 0) {
+		for (i=0;i<tw_dev->srb[request_id]->use_sg; i++) {
+			command_packet->byte8.io.sgl[i].address = virt_to_bus(sglist[i].address);
+			command_packet->byte8.io.sgl[i].length = sglist[i].length;
+			command_packet->size+=2;
+		}
+		if (tw_dev->srb[request_id]->use_sg > 1)
+			command_packet->size-=2;
+	}
+
+	/* Update SG statistics */
+	tw_dev->sgl_entries = tw_dev->srb[request_id]->use_sg;
+	if (tw_dev->sgl_entries > tw_dev->max_sgl_entries)
+		tw_dev->max_sgl_entries = tw_dev->sgl_entries;
+
+	command_que_value = tw_dev->command_packet_physical_address[request_id];
+	if (command_que_value == 0) {
+		dprintk(KERN_WARNING "3w-xxxx: tw_scsiop_read_write(): Bad command packet physical address.\n");
+		return 1;
+	}
+      
+	/* Now try to post the command to the board */
+	tw_post_command_packet(tw_dev, request_id);
+
+	return 0;
+} /* End tw_scsiop_read_write() */
+
+/* This function will handle test unit ready scsi command */
+int tw_scsiop_test_unit_ready(TW_Device_Extension *tw_dev, int request_id)
+{
+	dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_test_unit_ready()\n");
+
+	/* Tell the scsi layer were done */
+	tw_dev->state[request_id] = TW_S_COMPLETED;
+	tw_state_request_finish(tw_dev, request_id);
+	tw_dev->srb[request_id]->result = (DID_OK << 16);
+	tw_dev->srb[request_id]->scsi_done(tw_dev->srb[request_id]);
+
+	return 0;
+} /* End tw_scsiop_test_unit_ready() */
+
+/* This function will setup the interrupt handler */
+int tw_setup_irq(TW_Device_Extension *tw_dev)
+{
+	char *device = TW_DEVICE_NAME;
+	int error;
+
+	dprintk(KERN_NOTICE "3w-xxxx: tw_setup_irq()\n");
+	error = request_irq(tw_dev->tw_pci_dev->irq, tw_interrupt, SA_SHIRQ, device, tw_dev);
+
+	if (error < 0) {
+		printk(KERN_WARNING "3w-xxxx: tw_setup_irq(): Error requesting IRQ: %d for card %d.\n", tw_dev->tw_pci_dev->irq, tw_dev->host->host_no);
+		return 1;
+	}
+	return 0;
+} /* End tw_setup_irq() */
+
+/* This function will soft reset the controller */
+void tw_soft_reset(TW_Device_Extension *tw_dev) 
+{
+	u32 control_reg_addr, control_reg_value;
+
+	control_reg_addr = tw_dev->registers.control_reg_addr;
+	control_reg_value = (	TW_CONTROL_ISSUE_SOFT_RESET |
+				TW_CONTROL_CLEAR_HOST_INTERRUPT |
+				TW_CONTROL_CLEAR_ATTENTION_INTERRUPT |
+				TW_CONTROL_MASK_COMMAND_INTERRUPT |
+				TW_CONTROL_MASK_RESPONSE_INTERRUPT |
+				TW_CONTROL_CLEAR_ERROR_STATUS | 
+				TW_CONTROL_DISABLE_INTERRUPTS);
+	outl(control_reg_value, control_reg_addr);
+} /* End tw_soft_reset() */
+
+/* This function will free a request_id */
+int tw_state_request_finish(TW_Device_Extension *tw_dev, int request_id)
+{
+	dprintk(KERN_NOTICE "3w-xxxx: tw_state_request_finish()\n");
+  
+	do {    
+		if (tw_dev->free_tail == TW_Q_LENGTH-1) {
+			tw_dev->free_tail = TW_Q_START;
+		} else {
+			tw_dev->free_tail = tw_dev->free_tail + 1;
+		}
+	} while ((tw_dev->state[tw_dev->free_queue[tw_dev->free_tail]] != TW_S_COMPLETED));
+
+	tw_dev->free_queue[tw_dev->free_tail] = request_id;
+
+	tw_dev->state[request_id] = TW_S_FINISHED;
+	dprintk(KERN_NOTICE "3w-xxxx: tw_state_request_finish(): Freeing request_id %d\n", request_id);
+
+	return 0;
+} /* End tw_state_request_finish() */
+
+/* This function will assign an available request_id */
+int tw_state_request_start(TW_Device_Extension *tw_dev, int *request_id)
+{
+	int id = 0;
+
+	dprintk(KERN_NOTICE "3w-xxxx: tw_state_request_start()\n");
+
+	/* Obtain next free request_id */
+	do {
+		if (tw_dev->free_head == TW_Q_LENGTH - 1) {
+			tw_dev->free_head = TW_Q_START;
+		} else {
+			tw_dev->free_head = tw_dev->free_head + 1;
+		}
+	} while ((tw_dev->state[tw_dev->free_queue[tw_dev->free_head]] == TW_S_STARTED) ||
+		 (tw_dev->state[tw_dev->free_queue[tw_dev->free_head]] == TW_S_POSTED) ||
+		 (tw_dev->state[tw_dev->free_queue[tw_dev->free_head]] == TW_S_PENDING) ||
+		 (tw_dev->state[tw_dev->free_queue[tw_dev->free_head]] == TW_S_COMPLETED));
+
+	id = tw_dev->free_queue[tw_dev->free_head];
+
+	if (tw_dev->free_head == TW_Q_LENGTH - 1) {
+		tw_dev->free_head = TW_Q_START;
+	} else {
+		tw_dev->free_head = tw_dev->free_head + 1;
+	}
+
+	dprintk(KERN_NOTICE "3w-xxxx: tw_state_request_start(): id = %d.\n", id);
+	*request_id = id;
+	tw_dev->state[id] = TW_S_STARTED;
+
+	return 0;
+} /* End tw_state_request_start() */
+
+/* This function will unmask the command interrupt on the controller */
+void tw_unmask_command_interrupt(TW_Device_Extension *tw_dev)
+{
+	u32 control_reg_addr, control_reg_value;
+
+	control_reg_addr = tw_dev->registers.control_reg_addr;
+	control_reg_value = TW_CONTROL_UNMASK_COMMAND_INTERRUPT;
+	outl(control_reg_value, control_reg_addr);
+} /* End tw_unmask_command_interrupt() */
+
+/* Now get things going */
+
+#ifdef MODULE
+Scsi_Host_Template driver_template = TWXXXX;
+#include "scsi_module.c"
+#endif
diff -urN linux-2.2.15-pre5/drivers/scsi/3w-xxxx.h linux/drivers/scsi/3w-xxxx.h
--- linux-2.2.15-pre5/drivers/scsi/3w-xxxx.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/scsi/3w-xxxx.h	Mon Dec  3 21:58:47 2001
@@ -0,0 +1,369 @@
+/* 
+   3w-xxxx.h -- 3ware Storage Controller device driver for Linux.
+   
+   Written By: Adam Radford <linux@3ware.com>
+   Copyright (C) 1999 3ware Inc.
+
+   Kernel compatablity By:	Andre Hedrick <andre@suse.com>
+   Non-Copyright (C) 2000	Andre Hedrick <andre@suse.com>
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; version 2 of the License.
+
+   This program is distributed in the hope that it will be useful,           
+   but WITHOUT ANY WARRANTY; without even the implied warranty of            
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the             
+   GNU General Public License for more details.                              
+
+   NO WARRANTY                                                               
+   THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR        
+   CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT      
+   LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,      
+   MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is    
+   solely responsible for determining the appropriateness of using and       
+   distributing the Program and assumes all risks associated with its        
+   exercise of rights under this Agreement, including but not limited to     
+   the risks and costs of program errors, damage to or loss of data,         
+   programs or equipment, and unavailability or interruption of operations.  
+
+   DISCLAIMER OF LIABILITY                                                   
+   NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY   
+   DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL        
+   DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND   
+   ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR     
+   TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE    
+   USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED  
+   HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES             
+
+   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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+
+   Bugs/Comments/Suggestions should be mailed to:                            
+   linux@3ware.com
+   
+   For more information, goto:
+   http://www.3ware.com
+*/
+
+#ifndef _3W_XXXX_H
+#define _3W_XXXX_H
+
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/kdev_t.h>
+
+/* Control register bit definitions */
+#define TW_CONTROL_CLEAR_HOST_INTERRUPT	       0x00080000
+#define TW_CONTROL_CLEAR_ATTENTION_INTERRUPT   0x00040000
+#define TW_CONTROL_MASK_COMMAND_INTERRUPT      0x00020000
+#define TW_CONTROL_MASK_RESPONSE_INTERRUPT     0x00010000
+#define TW_CONTROL_UNMASK_COMMAND_INTERRUPT    0x00008000
+#define TW_CONTROL_UNMASK_RESPONSE_INTERRUPT   0x00004000
+#define TW_CONTROL_CLEAR_ERROR_STATUS	       0x00000200
+#define TW_CONTROL_ISSUE_SOFT_RESET	       0x00000100
+#define TW_CONTROL_ENABLE_INTERRUPTS	       0x00000080
+#define TW_CONTROL_DISABLE_INTERRUPTS	       0x00000040
+#define TW_CONTROL_ISSUE_HOST_INTERRUPT	       0x00000020
+
+/* Status register bit definitions */
+#define TW_STATUS_MAJOR_VERSION_MASK	       0xF0000000
+#define TW_STATUS_MINOR_VERSION_MASK	       0x0F000000
+#define TW_STATUS_PCI_PARITY_ERROR	       0x00800000
+#define TW_STATUS_QUEUE_ERROR		       0x00400000
+#define TW_STATUS_MICROCONTROLLER_ERROR	       0x00200000
+#define TW_STATUS_PCI_ABORT		       0x00100000
+#define TW_STATUS_HOST_INTERRUPT	       0x00080000
+#define TW_STATUS_ATTENTION_INTERRUPT	       0x00040000
+#define TW_STATUS_COMMAND_INTERRUPT	       0x00020000
+#define TW_STATUS_RESPONSE_INTERRUPT	       0x00010000
+#define TW_STATUS_COMMAND_QUEUE_FULL	       0x00008000
+#define TW_STATUS_RESPONSE_QUEUE_EMPTY	       0x00004000
+#define TW_STATUS_MICROCONTROLLER_READY	       0x00002000
+#define TW_STATUS_COMMAND_QUEUE_EMPTY	       0x00001000
+#define TW_STATUS_ALL_INTERRUPTS	       0x000F0000
+#define TW_STATUS_CLEARABLE_BITS	       0x00D00000
+#define TW_STATUS_EXPECTED_BITS		       0x00002000
+#define TW_STATUS_UNEXPECTED_BITS	       0x00F80000
+
+/* RESPONSE QUEUE BIT DEFINITIONS */
+#define TW_RESPONSE_ID_MASK		       0x00000FF0
+
+/* PCI related defines */
+#define TW_IO_ADDRESS_RANGE		       0xD
+#define TW_DEVICE_NAME			       "3ware Storage Controller"
+#define TW_VENDOR_ID (0x13C1)	/* 3ware */
+#define TW_DEVICE_ID (0x1000)	/* Storage Controller */
+
+/* Command packet opcodes */
+#define TW_OP_NOP	      0x0
+#define TW_OP_INIT_CONNECTION 0x1
+#define TW_OP_READ	      0x2
+#define TW_OP_WRITE	      0x3
+#define TW_OP_VERIFY	      0x4
+#define TW_OP_GET_PARAM	      0x12
+#define TW_OP_SET_PARAM	      0x13
+#define TW_OP_SECTOR_INFO     0x1a
+#define TW_OP_AEN_LISTEN      0x1c
+
+/* Asynchronous Event Notification (AEN) Codes */
+#define TW_AEN_QUEUE_EMPTY       0x0000
+#define TW_AEN_SOFT_RESET        0x0001
+#define TW_AEN_DEGRADED_MIRROR   0x0002
+#define TW_AEN_CONTROLLER_ERROR  0x0003
+#define TW_AEN_REBUILD_FAIL      0x0004
+#define TW_AEN_REBUILD_DONE      0x0005
+#define TW_AEN_QUEUE_FULL        0x00ff
+#define TW_AEN_TABLE_UNDEFINED   0x15
+
+/* Misc defines */
+#define TW_ALIGNMENT			      0x200 /* 16 D-WORDS */
+#define TW_MAX_UNITS			      16
+#define TW_COMMAND_ALIGNMENT_MASK	      0x1ff
+#define TW_INIT_MESSAGE_CREDITS		      0x100
+#define TW_INIT_COMMAND_PACKET_SIZE	      0x3
+#define TW_POLL_MAX_RETRIES        	      10000
+#define TW_MAX_SGL_LENGTH		      62
+#define TW_Q_LENGTH			      256
+#define TW_Q_START			      0
+#define TW_MAX_SLOT			      32
+#define TW_MAX_PCI_BUSES		      255
+#define TW_MAX_RESET_TRIES		      3
+#define TW_UNIT_INFORMATION_TABLE_BASE	      0x300
+#define TW_MAX_CMDS_PER_LUN		      (TW_Q_LENGTH-2)/TW_MAX_UNITS
+#define TW_BLOCK_SIZE			      0x200 /* 512-byte blocks */
+#define TW_IOCTL                              0x80
+#define TW_MAX_AEN_TRIES                      100
+
+#define TW_IN_INTR                            1
+
+/* Macros */
+#define TW_STATUS_ERRORS(x) \
+	(((x & TW_STATUS_PCI_ABORT) || \
+	(x & TW_STATUS_PCI_PARITY_ERROR) || \
+	(x & TW_STATUS_QUEUE_ERROR) || \
+	(x & TW_STATUS_MICROCONTROLLER_ERROR)) && \
+	(x & TW_STATUS_MICROCONTROLLER_READY))
+
+#ifdef TW_DEBUG
+#define dprintk(msg...) printk(msg)
+#else
+#define dprintk(msg...) do { } while(0);
+#endif
+
+extern struct proc_dir_entry tw_scsi_proc_entry;
+
+/* Scatter Gather List Entry */
+typedef struct TAG_TW_SG_Entry {
+	unsigned long address;
+	unsigned long length;
+} TW_SG_Entry;
+
+typedef unsigned char TW_Sector[512];
+
+/* Command Packet */
+typedef struct TW_Command {
+	/* First DWORD */
+	struct {
+		unsigned char opcode:5;
+		unsigned char sgl_offset:3;
+	} byte0;
+	unsigned char size;
+	unsigned char request_id;
+	struct {
+		unsigned char unit:4;
+		unsigned char host_id:4;
+	} byte3;
+	/* Second DWORD */
+	unsigned char status;
+	unsigned char flags;
+	union {
+		unsigned short block_count;
+		unsigned short parameter_count;
+		unsigned short message_credits;
+	} byte6;
+	union {
+		struct {
+			unsigned long lba;
+			TW_SG_Entry sgl[TW_MAX_SGL_LENGTH];
+			unsigned long padding;	/* pad to 512 bytes */
+		} io;
+		struct {
+			TW_SG_Entry sgl[TW_MAX_SGL_LENGTH];
+			unsigned long padding[2];
+		} param;
+		struct {
+			unsigned long response_queue_pointer;
+			unsigned long padding[125];
+		} init_connection;
+		struct {
+			char version[504];
+		} ioctl_miniport_version;
+	} byte8;
+} TW_Command;
+
+typedef struct TAG_TW_Ioctl {
+	int buffer;
+	unsigned char opcode;
+	unsigned short table_id;
+	unsigned char parameter_id;
+	unsigned char parameter_size_bytes;
+	unsigned char data[1];
+} TW_Ioctl;
+
+/* GetParam descriptor */
+typedef struct {
+	unsigned short	table_id;
+	unsigned char	parameter_id;
+	unsigned char	parameter_size_bytes;
+	unsigned char	data[1];
+} TW_Param, *PTW_Param;
+
+/* Response queue */
+typedef union TAG_TW_Response_Queue {
+	struct {
+		u32 undefined_1: 4;
+		u32 response_id: 8;
+		u32 undefined_2: 20;
+	} u;
+	u32 value;
+} TW_Response_Queue;
+
+typedef struct TAG_TW_Registers {
+	u32 base_addr;
+	u32 control_reg_addr;
+	u32 status_reg_addr;
+	u32 command_que_addr;
+	u32 response_que_addr;
+} TW_Registers;
+
+typedef struct TAG_TW_Info {
+	char *buffer;
+	int length;
+	int offset;
+	int position;
+} TW_Info;
+
+typedef enum TAG_TW_Cmd_State {
+	TW_S_INITIAL,		/* Initial state */
+	TW_S_STARTED,		/* Id in use */
+	TW_S_POSTED,		/* Posted to the controller */
+	TW_S_PENDING,		/* Waiting to be posted in isr */
+	TW_S_COMPLETED,		/* Completed by isr */
+	TW_S_FINISHED,		/* I/O completely done */
+} TW_Cmd_State;
+
+typedef struct TAG_TW_Device_Extension {
+	TW_Registers		registers;
+	u32			*alignment_virtual_address[TW_Q_LENGTH];
+	u32			alignment_physical_address[TW_Q_LENGTH];
+	int			is_unit_present[TW_MAX_UNITS];
+	int			num_units;
+	u32			*command_packet_virtual_address[TW_Q_LENGTH];
+	u32			command_packet_physical_address[TW_Q_LENGTH];
+	struct pci_dev		*tw_pci_dev;
+	Scsi_Cmnd		*srb[TW_Q_LENGTH];
+	unsigned char		free_queue[TW_Q_LENGTH];
+	unsigned char		free_head;
+	unsigned char		free_tail;
+	unsigned char		pending_queue[TW_Q_LENGTH];
+	unsigned char		pending_head;
+	unsigned char		pending_tail;
+	TW_Cmd_State		state[TW_Q_LENGTH];
+	u32			posted_request_count;
+	u32			max_posted_request_count;
+	u32			request_count_marked_pending;
+	u32			pending_request_count;
+	u32			max_pending_request_count;
+	u32			max_sgl_entries;
+	u32			sgl_entries;
+	u32			num_aborts;
+	u32			num_resets;
+	u32			sector_count;
+	u32			max_sector_count;
+	struct Scsi_Host	*host;
+	spinlock_t		tw_lock;
+	unsigned char		ioctl_size[TW_Q_LENGTH];
+	unsigned short		aen_queue[TW_Q_LENGTH];
+	unsigned char		aen_head;
+	unsigned char		aen_tail;
+	u32			flags;
+} TW_Device_Extension;
+
+/* Function prototypes */
+int tw_aen_complete(TW_Device_Extension *tw_dev, int request_id);
+int tw_aen_drain_queue(TW_Device_Extension *tw_dev);
+int tw_aen_read_queue(TW_Device_Extension *tw_dev, int request_id);
+int tw_allocate_memory(TW_Device_Extension *tw_dev, int request_id, int size, int which);
+int tw_check_bits(u32 status_reg_value);
+int tw_check_errors(TW_Device_Extension *tw_dev);
+void tw_clear_attention_interrupt(TW_Device_Extension *tw_dev);
+void tw_clear_host_interrupt(TW_Device_Extension *tw_dev);
+void tw_disable_interrupts(TW_Device_Extension *tw_dev);
+int tw_empty_response_que(TW_Device_Extension *tw_dev);
+void tw_enable_interrupts(TW_Device_Extension *tw_dev);
+int tw_findcards(Scsi_Host_Template *tw_host);
+void tw_free_device_extension(TW_Device_Extension *tw_dev);
+int tw_initconnection(TW_Device_Extension *tw_dev);
+int tw_initialize_device_extension(TW_Device_Extension *tw_dev);
+int tw_initialize_units(TW_Device_Extension *tw_dev);
+int tw_ioctl(TW_Device_Extension *tw_dev, int request_id);
+int tw_ioctl_complete(TW_Device_Extension *tw_dev, int request_id);
+void tw_mask_command_interrupt(TW_Device_Extension *tw_dev);
+int tw_poll_status(TW_Device_Extension *tw_dev, u32 flag, int seconds);
+int tw_post_command_packet(TW_Device_Extension *tw_dev, int request_id);
+int tw_reset_device_extension(TW_Device_Extension *tw_dev);
+int tw_reset_sequence(TW_Device_Extension *tw_dev);
+int tw_scsi_biosparam(Disk *disk, kdev_t dev, int geom[]);
+int tw_scsi_detect(Scsi_Host_Template *tw_host);
+int tw_scsi_eh_abort(Scsi_Cmnd *SCpnt);
+int tw_scsi_eh_reset(Scsi_Cmnd *SCpnt);
+int tw_scsi_proc_info(char *buffer, char **start, off_t offset, int length, int inode, int inout);
+int tw_scsi_queue(Scsi_Cmnd *cmd, void (*done) (Scsi_Cmnd *));
+int tw_scsi_release(struct Scsi_Host *tw_host);
+int tw_scsiop_inquiry(TW_Device_Extension *tw_dev, int request_id);
+int tw_scsiop_inquiry_complete(TW_Device_Extension *tw_dev, int request_id);
+int tw_scsiop_read_capacity(TW_Device_Extension *tw_dev, int request_id);
+int tw_scsiop_read_capacity_complete(TW_Device_Extension *tw_dev, int request_id);
+int tw_scsiop_read_write(TW_Device_Extension *tw_dev, int request_id);
+int tw_scsiop_test_unit_ready(TW_Device_Extension *tw_dev, int request_id);
+int tw_setup_irq(TW_Device_Extension *tw_dev);
+void tw_soft_reset(TW_Device_Extension *tw_dev);
+int tw_state_request_finish(TW_Device_Extension *tw_dev,int request_id);
+int tw_state_request_start(TW_Device_Extension *tw_dev, int *request_id);
+void tw_unmask_command_interrupt(TW_Device_Extension *tw_dev);
+
+#if defined(HOSTS_C) || defined(MODULE)
+/* Scsi_Host_Template Initializer */
+#define TWXXXX {					\
+	next : NULL,					\
+	module : NULL,					\
+	proc_dir : &tw_scsi_proc_entry,			\
+	proc_info : tw_scsi_proc_info,			\
+	name : "3ware Storage Controller",		\
+	detect : tw_scsi_detect,			\
+	release : tw_scsi_release,			\
+	info : NULL,					\
+	ioctl : NULL,                  			\
+	command : NULL,					\
+	queuecommand : tw_scsi_queue,			\
+	eh_strategy_handler : NULL,			\
+	eh_abort_handler : tw_scsi_eh_abort,		\
+	eh_device_reset_handler : NULL,			\
+	abort : NULL,					\
+	reset : NULL,					\
+	slave_attach : NULL,				\
+	bios_param : tw_scsi_biosparam,			\
+	can_queue : TW_Q_LENGTH,			\
+	this_id: -1,					\
+	sg_tablesize : TW_MAX_SGL_LENGTH,		\
+	cmd_per_lun: TW_MAX_CMDS_PER_LUN,		\
+	present : 0,					\
+	unchecked_isa_dma : 0,				\
+	use_clustering : ENABLE_CLUSTERING,		\
+ 	use_new_eh_code : 1,				\
+	emulated : 1					\
+}
+#endif /* HOSTS_C */
+#endif /* _3W_XXXX_H */
diff -urN linux-2.2.15-pre5/drivers/scsi/Config.in linux/drivers/scsi/Config.in
--- linux-2.2.15-pre5/drivers/scsi/Config.in	Tue Oct 26 17:53:41 1999
+++ linux/drivers/scsi/Config.in	Mon Dec  3 21:58:47 2001
@@ -18,6 +18,9 @@
 mainmenu_option next_comment
 comment 'SCSI low-level drivers'
 
+if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+   dep_tristate '3Ware Hardware ATA-RAID support (EXPERIMENTAL)' CONFIG_BLK_DEV_3W_XXXX_RAID $CONFIG_SCSI
+fi
 dep_tristate '7000FASST SCSI support' CONFIG_SCSI_7000FASST $CONFIG_SCSI
 dep_tristate 'ACARD SCSI support' CONFIG_SCSI_ACARD $CONFIG_SCSI
 dep_tristate 'Adaptec AHA152X/2825 support' CONFIG_SCSI_AHA152X $CONFIG_SCSI
diff -urN linux-2.2.15-pre5/drivers/scsi/Makefile linux/drivers/scsi/Makefile
--- linux-2.2.15-pre5/drivers/scsi/Makefile	Tue Oct 26 17:53:41 1999
+++ linux/drivers/scsi/Makefile	Mon Dec  3 21:58:47 2001
@@ -639,6 +639,14 @@
 L_OBJS += NCR53C9x.o jazz_esp.o
 endif
 
+ifeq ($(CONFIG_BLK_DEV_3W_XXXX_RAID),y)
+L_OBJS += 3w-xxxx.o
+else
+  ifeq ($(CONFIG_BLK_DEV_3W_XXXX_RAID),m)
+  M_OBJS += 3w-xxxx.o
+  endif
+endif
+
 include $(TOPDIR)/Rules.make
 
 53c8xx_d.h: 53c7,8xx.scr script_asm.pl
diff -urN linux-2.2.15-pre5/drivers/scsi/hosts.c linux/drivers/scsi/hosts.c
--- linux-2.2.15-pre5/drivers/scsi/hosts.c	Tue Oct 26 17:53:41 1999
+++ linux/drivers/scsi/hosts.c	Mon Dec  3 21:58:47 2001
@@ -339,6 +339,10 @@
 #include "../net/fc/iph5526_scsi.h"
 #endif
 
+#ifdef CONFIG_BLK_DEV_3W_XXXX_RAID
+#include "3w-xxxx.h"
+#endif
+
 /*
  * Moved ppa driver to the end of the probe list
  * since it is a removable host adapter.
@@ -610,6 +614,10 @@
 #ifdef CONFIG_IPHASE5526
 	IPH5526_SCSI_FC,
 #endif
+#ifdef CONFIG_BLK_DEV_3W_XXXX_RAID
+	TWXXXX,
+#endif
+
 /* "Removable host adapters" below this line (Parallel Port/USB/other) */
 #ifdef CONFIG_SCSI_PPA
     PPA,
diff -urN linux-2.2.15-pre5/drivers/sound/dmabuf.c linux/drivers/sound/dmabuf.c
--- linux-2.2.15-pre5/drivers/sound/dmabuf.c	Tue Oct 26 17:53:41 1999
+++ linux/drivers/sound/dmabuf.c	Mon Dec  3 21:58:47 2001
@@ -70,6 +70,14 @@
 	if (dma_buffsize < 4096)
 		dma_buffsize = 4096;
 	dma_pagesize = (dmap->dma < 4) ? (64 * 1024) : (128 * 1024);
+	
+	/*
+	 *	Now check for the Cyrix problem.
+	 */
+	 
+	if(isa_dma_bridge_buggy==2)
+		dma_pagesize=32768;
+	 
 	dmap->raw_buf = NULL;
 	dmap->buffsize = dma_buffsize;
 	if (dmap->buffsize > dma_pagesize)
diff -urN linux-2.2.15-pre5/include/asm-ppc/gemini_serial.h linux/include/asm-ppc/gemini_serial.h
--- linux-2.2.15-pre5/include/asm-ppc/gemini_serial.h	Tue Jan  4 10:12:24 2000
+++ linux/include/asm-ppc/gemini_serial.h	Mon Dec  3 21:58:47 2001
@@ -7,10 +7,10 @@
 #define BASE_BAUD  (24576000 / 16)
 
 #ifdef CONFIG_SERIAL_DETECT_IRQ
-#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF|ASYNC_SKIP_TEST|ASYNC_AUTO_IRQ)
+#define STD_COM_FLAGS (/*ASYNC_BOOT_AUTOCONF|*/ASYNC_SKIP_TEST|ASYNC_AUTO_IRQ)
 #define STD_COM4_FLAGS (ASYNC_BOOT_AUTOCONF|ASYNC_AUTO_IRQ)
 #else
-#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF|ASYNC_SKIP_TEST)
+#define STD_COM_FLAGS (/*ASYNC_BOOT_AUTOCONF|*/ASYNC_SKIP_TEST)
 #define STD_COM4_FLAGS (ASYNC_BOOT_AUTOCONF)
 #endif
 
diff -urN linux-2.2.15-pre5/include/asm-sparc/ethtool.h linux/include/asm-sparc/ethtool.h
--- linux-2.2.15-pre5/include/asm-sparc/ethtool.h	Wed Mar 10 16:53:37 1999
+++ linux/include/asm-sparc/ethtool.h	Mon Dec  3 21:58:47 2001
@@ -1,7 +1,7 @@
-/* $Id: ethtool.h,v 1.1 1998/12/19 15:09:38 davem Exp $
+/* $Id: ethtool.h,v 1.1.2.1 2000/01/31 05:02:42 davem Exp $
  * ethtool.h: Defines for SparcLinux ethtool.
  *
- * Copyright (C) 1998 David S. Miller (davem@dm.cobaltmicro.com)
+ * Copyright (C) 1998 David S. Miller (davem@redhat.com)
  */
 
 #ifndef _SPARC_ETHTOOL_H
diff -urN linux-2.2.15-pre5/include/asm-sparc64/ethtool.h linux/include/asm-sparc64/ethtool.h
--- linux-2.2.15-pre5/include/asm-sparc64/ethtool.h	Wed Mar 10 16:53:38 1999
+++ linux/include/asm-sparc64/ethtool.h	Mon Dec  3 21:58:47 2001
@@ -1,7 +1,7 @@
-/* $Id: ethtool.h,v 1.1 1998/12/19 15:09:40 davem Exp $
+/* $Id: ethtool.h,v 1.1.2.1 2000/01/31 05:02:43 davem Exp $
  * ethtool.h: Defines for SparcLinux ethtool.
  *
- * Copyright (C) 1998 David S. Miller (davem@dm.cobaltmicro.com)
+ * Copyright (C) 1998 David S. Miller (davem@redhat.com)
  */
 
 #ifndef _SPARC64_ETHTOOL_H
diff -urN linux-2.2.15-pre5/include/linux/cd1400.h linux/include/linux/cd1400.h
--- linux-2.2.15-pre5/include/linux/cd1400.h	Wed Feb  4 14:52:16 1998
+++ linux/include/linux/cd1400.h	Mon Dec  3 21:58:47 2001
@@ -4,7 +4,7 @@
  *	cd1400.h  -- cd1400 UART hardware info.
  *
  *	Copyright (C) 1996-1998  Stallion Technologies (support@stallion.oz.au).
- *	Copyright (C) 1994-1996  Greg Ungerer (gerg@stallion.oz.au).
+ *	Copyright (C) 1994-1996  Greg Ungerer.
  *
  *	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
diff -urN linux-2.2.15-pre5/include/linux/cdk.h linux/include/linux/cdk.h
--- linux-2.2.15-pre5/include/linux/cdk.h	Wed Feb  4 14:52:16 1998
+++ linux/include/linux/cdk.h	Mon Dec  3 21:58:47 2001
@@ -4,7 +4,7 @@
  *	cdk.h  -- CDK interface definitions.
  *
  *	Copyright (C) 1996-1998  Stallion Technologies (support@stallion.oz.au).
- *	Copyright (C) 1994-1996  Greg Ungerer (gerg@stallion.oz.au).
+ *	Copyright (C) 1994-1996  Greg Ungerer.
  *
  *	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
diff -urN linux-2.2.15-pre5/include/linux/comstats.h linux/include/linux/comstats.h
--- linux-2.2.15-pre5/include/linux/comstats.h	Wed Feb  4 14:52:16 1998
+++ linux/include/linux/comstats.h	Mon Dec  3 21:58:47 2001
@@ -4,7 +4,7 @@
  *	comstats.h  -- Serial Port Stats.
  *
  *	Copyright (C) 1996-1998  Stallion Technologies (support@stallion.oz.au).
- *	Copyright (C) 1994-1996  Greg Ungerer (gerg@stallion.oz.au).
+ *	Copyright (C) 1994-1996  Greg Ungerer.
  *
  *	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
diff -urN linux-2.2.15-pre5/include/linux/cpia.h linux/include/linux/cpia.h
--- linux-2.2.15-pre5/include/linux/cpia.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/cpia.h	Mon Dec  3 21:58:47 2001
@@ -0,0 +1,220 @@
+#ifndef cpia_h
+#define cpia_h
+
+/*
+ * CPiA Parallel Port Video4Linux driver
+ *
+ * Supports CPiA based parallel port Video Camera's.
+ *
+ * (C) Copyright 1999 Bas Huisman,
+ *                    Peter Pregler,
+ *                    Scott J. Bertin,
+ *                    VLSI Vision Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#define CPIA_MAJ_VER	0
+#define CPIA_MIN_VER    5
+#define CPIA_PATCH_VER	0
+
+#define CPIA_PP_MAJ_VER       0
+#define CPIA_PP_MIN_VER       5
+#define CPIA_PP_PATCH_VER     0
+
+#define CPIA_MAX_FRAME_SIZE_UNALIGNED	(352 * 288 * 4)   /* CIF at RGB32 */
+#define CPIA_MAX_FRAME_SIZE	((CPIA_MAX_FRAME_SIZE_UNALIGNED + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1)) /* align above to PAGE_SIZE */
+
+#ifdef __KERNEL__
+
+#include <asm/uaccess.h>
+
+struct cpia_camera_ops
+{
+	/* open sets privdata to point to structure for this camera.
+         * Returns negative value on error, otherwise 0.
+	 */
+	int (*open)(int camnr, void **privdata);
+	
+	/* Registers callback function cb to be called with cbdata
+	 * when an image is ready.  If cb is NULL, only single image grabs
+	 * should be used.  cb should immediately call streamRead to read
+	 * the data or data may be lost. Returns negative value on error,
+	 * otherwise 0.
+	 */
+	int (*registerCallback)(void *privdata, void (*cb)(void *cbdata),
+	                        void *cbdata);
+	
+	/* transferCmd sends commands to the camera.  command MUST point to
+	 * an  8 byte buffer in kernel space. data can be NULL if no extra
+	 * data is needed.  The size of the data is given by the last 2
+	 * bytes of comand.  data must also point to memory in kernel space.
+	 * Returns negative value on error, otherwise 0.
+	 */
+	int (*transferCmd)(void *privdata, u8 *command, u8 *data);
+
+	/* streamStart initiates stream capture mode.
+	 * Returns negative value on error, otherwise 0.
+	 */
+	int (*streamStart)(void *privdata);
+	
+	/* streamStop terminates stream capture mode.
+	 * Returns negative value on error, otherwise 0.
+	 */
+	int (*streamStop)(void *privdata);
+        
+	/* streamRead reads a frame from the camera.  buffer points to a
+         * buffer large enough to hold a complete frame in kernel space.
+         * noblock indicates if this should be a non blocking read.
+	 * Returns the number of bytes read, or negative value on error.
+         */
+	int (*streamRead)(void *privdata, u8 *buffer, int noblock);
+	
+	/* close disables the device until open() is called again.
+	 * Returns negative value on error, otherwise 0.
+	 */
+	int (*close)(void *privdata);
+};
+
+/* cpia_register_camera is called by low level driver for each camera.
+ * A unique camera number is returned, or a negative value on error */
+int cpia_register_camera(struct cpia_camera_ops *ops);
+
+/* cpia_unregister_camera is called by low level driver when a camera
+ * is removed.  This must not fail. */
+void cpia_unregister_camera(int camnr);
+
+#define CPIA_MAXCAMS		4
+
+/* raw CIF + 64 byte header + (2 bytes line_length + EOL) per line + 4*EOI +
+ * one byte 16bit DMA alignment
+ */
+#define CPIA_MAX_IMAGE_SIZE ((352*288*2)+64+(288*3)+5)
+
+/* constant value's */
+#define MAGIC_0		0x19
+#define MAGIC_1		0x68
+#define DATA_IN		0xC0
+#define DATA_OUT	0x40
+#define VIDEOSIZE_QCIF	0	/* 176x144 */
+#define VIDEOSIZE_CIF	1	/* 352x288 */
+#define VIDEOSIZE_SIF	2	/* 320x240 */
+#define VIDEOSIZE_QSIF	3	/* 160x120 */
+#define VIDEOSIZE_48_48		4 /* where no one has gone before, iconsize! */
+#define VIDEOSIZE_64_48		5
+#define VIDEOSIZE_128_96	6
+#define VIDEOSIZE_160_120	VIDEOSIZE_QSIF
+#define VIDEOSIZE_176_144	VIDEOSIZE_QCIF
+#define VIDEOSIZE_192_144	7
+#define VIDEOSIZE_224_168	8
+#define VIDEOSIZE_256_192	9
+#define VIDEOSIZE_288_216	10
+#define VIDEOSIZE_320_240	VIDEOSIZE_SIF
+#define VIDEOSIZE_352_288	VIDEOSIZE_CIF
+#define SUBSAMPLE_420	0
+#define SUBSAMPLE_422	1
+#define YUVORDER_YUYV	0
+#define YUVORDER_UYVY	1
+#define NOT_COMPRESSED	0
+#define COMPRESSED	1
+#define NO_DECIMATION	0
+#define DECIMATION_ENAB	1
+#define EOI		0xff	/* End Of Image */
+#define EOL		0xfd	/* End Of Line */
+
+/* Image grab modes */
+#define CPIA_GRAB_SINGLE	0
+#define CPIA_GRAB_CONTINUOUS	1
+
+/* Compression parameters */
+#define CPIA_COMPRESSION_NONE	0
+#define CPIA_COMPRESSION_AUTO	1
+#define CPIA_COMPRESSION_MANUAL	2
+#define CPIA_COMPRESSION_TARGET_QUALITY         0
+#define CPIA_COMPRESSION_TARGET_FRAMERATE       1
+
+/* Return offsets for GetCameraState */
+#define SYSTEMSTATE	0
+#define GRABSTATE	1
+#define STREAMSTATE	2
+#define FATALERROR	3
+#define CMDERROR	4
+#define DEBUGFLAGS	5
+#define VPSTATUS	6
+#define ERRORCODE	7
+
+/* SystemState */
+#define UNINITIALISED_STATE	0
+#define PASS_THROUGH_STATE	1
+#define LO_POWER_STATE		2
+#define HI_POWER_STATE		3
+#define WARM_BOOT_STATE		4
+
+/* GrabState */
+#define GRAB_IDLE		0
+#define GRAB_ACTIVE		1
+#define GRAB_DONE		2
+
+/* StreamState */
+#define STREAM_NOT_READY	0
+#define STREAM_READY		1
+#define STREAM_OPEN		2
+#define STREAM_PAUSED		3
+#define STREAM_FINISHED		4
+
+/* Fatal Error, CmdError, and DebugFlags */
+#define CPIA_FLAG	  1
+#define SYSTEM_FLAG	  2
+#define INT_CTRL_FLAG	  4
+#define PROCESS_FLAG	  8
+#define COM_FLAG	 16
+#define VP_CTRL_FLAG	 32
+#define CAPTURE_FLAG	 64
+#define DEBUG_FLAG	128
+
+/* VPStatus */
+#define VP_STATE_OK			0x00
+
+#define VP_STATE_FAILED_VIDEOINIT	0x01
+#define VP_STATE_FAILED_AECACBINIT	0x02
+#define VP_STATE_AEC_MAX		0x04
+#define VP_STATE_ACB_BMAX		0x08
+
+#define VP_STATE_ACB_RMIN		0x10
+#define VP_STATE_ACB_GMIN		0x20
+#define VP_STATE_ACB_RMAX		0x40
+#define VP_STATE_ACB_GMAX		0x80
+
+/* ErrorCode */
+#define ERROR_FLICKER_BELOW_MIN_EXP     0x01 /*flicker exposure got below minimum exposure */
+
+#define ALOG(lineno,fmt,args...) printk(fmt,lineno,##args)
+#define LOG(fmt,args...) ALOG((__LINE__),KERN_INFO __FILE__":"__FUNCTION__"(%d):"fmt,##args)
+
+#ifdef _CPIA_DEBUG_
+#define ADBG(lineno,fmt,args...) printk(fmt, jiffies, lineno, ##args)
+#define DBG(fmt,args...) ADBG((__LINE__),KERN_DEBUG __FILE__"(%ld):"__FUNCTION__"(%d):"fmt,##args)
+#else
+#define DBG(fmn,args...) {}
+#endif
+
+#define DEB_BYTE(p)\
+  DBG("%1d %1d %1d %1d %1d %1d %1d %1d \n",\
+      (p)&0x80?1:0, (p)&0x40?1:0, (p)&0x20?1:0, (p)&0x10?1:0,\
+        (p)&0x08?1:0, (p)&0x04?1:0, (p)&0x02?1:0, (p)&0x01?1:0);
+
+#endif /* __KERNEL__ */
+
+#endif /* cpia_h */
diff -urN linux-2.2.15-pre5/include/linux/hdreg.h linux/include/linux/hdreg.h
--- linux-2.2.15-pre5/include/linux/hdreg.h	Tue May 11 10:35:44 1999
+++ linux/include/linux/hdreg.h	Mon Dec  3 21:58:47 2001
@@ -49,6 +49,7 @@
 #define WIN_SEEK		0x70
 #define WIN_DIAGNOSE		0x90
 #define WIN_SPECIFY		0x91	/* set drive geometry translation */
+#define WIN_IDLEIMMEDIATE	0xE1	/* force drive to become "ready" */
 #define WIN_SETIDLE1		0xE3
 #define WIN_SETIDLE2		0x97
 
diff -urN linux-2.2.15-pre5/include/linux/istallion.h linux/include/linux/istallion.h
--- linux-2.2.15-pre5/include/linux/istallion.h	Wed Feb  4 14:52:16 1998
+++ linux/include/linux/istallion.h	Mon Dec  3 21:58:47 2001
@@ -4,7 +4,7 @@
  *	istallion.h  -- stallion intelligent multiport serial driver.
  *
  *	Copyright (C) 1996-1998  Stallion Technologies (support@stallion.oz.au).
- *	Copyright (C) 1994-1996  Greg Ungerer (gerg@stallion.oz.au).
+ *	Copyright (C) 1994-1996  Greg Ungerer.
  *
  *	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
diff -urN linux-2.2.15-pre5/include/linux/proc_fs.h linux/include/linux/proc_fs.h
--- linux-2.2.15-pre5/include/linux/proc_fs.h	Tue Jan  4 10:12:25 2000
+++ linux/include/linux/proc_fs.h	Mon Dec  3 21:58:47 2001
@@ -210,6 +210,7 @@
  	PROC_SCSI_IPH5526_FC,
 	PROC_SCSI_FCAL,
 	PROC_SCSI_I2O,
+	PROC_SCSI_3W_XXXX,
 	PROC_SCSI_USB_SCSI,
 	PROC_SCSI_SCSI_DEBUG,	
 	PROC_SCSI_NOT_PRESENT,
diff -urN linux-2.2.15-pre5/include/linux/stallion.h linux/include/linux/stallion.h
--- linux-2.2.15-pre5/include/linux/stallion.h	Wed Feb  4 14:52:16 1998
+++ linux/include/linux/stallion.h	Mon Dec  3 21:58:47 2001
@@ -4,7 +4,7 @@
  *	stallion.h  -- stallion multiport serial driver.
  *
  *	Copyright (C) 1996-1998  Stallion Technologies (support@stallion.oz.au).
- *	Copyright (C) 1994-1996  Greg Ungerer (gerg@stallion.oz.au).
+ *	Copyright (C) 1994-1996  Greg Ungerer.
  *
  *	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
diff -urN linux-2.2.15-pre5/include/net/irda/parameters.h linux/include/net/irda/parameters.h
--- linux-2.2.15-pre5/include/net/irda/parameters.h	Mon Dec  3 21:58:41 2001
+++ linux/include/net/irda/parameters.h	Mon Dec  3 21:58:47 2001
@@ -6,10 +6,10 @@
  * Status:        Experimental.
  * Author:        Dag Brattli <dagb@cs.uit.no>
  * Created at:    Mon Jun  7 08:47:28 1999
- * Modified at:   Mon Dec 13 11:51:59 1999
+ * Modified at:   Sun Jan 30 14:05:14 2000
  * Modified by:   Dag Brattli <dagb@cs.uit.no>
  * 
- *     Copyright (c) 1999 Dag Brattli, All Rights Reserved.
+ *     Copyright (c) 1999-2000 Dag Brattli, All Rights Reserved.
  *     
  *     This program is free software; you can redistribute it and/or 
  *     modify it under the terms of the GNU General Public License as 
@@ -61,15 +61,15 @@
 	__u8  *bp;
 	__u16 *sp;
 	__u32 *ip;
-} pv_t;
+} irda_pv_t;
 
 typedef struct {
 	__u8 pi;
 	__u8 pl;
-	pv_t pv;
-} param_t;
+	irda_pv_t pv;
+} irda_param_t;
 
-typedef int (*PI_HANDLER)(void *self, param_t *param, int get);
+typedef int (*PI_HANDLER)(void *self, irda_param_t *param, int get);
 typedef int (*PV_HANDLER)(void *self, __u8 *buf, int len, __u8 pi,
 			  PV_TYPE type, PI_HANDLER func);
 
diff -urN linux-2.2.15-pre5/init/main.c linux/init/main.c
--- linux-2.2.15-pre5/init/main.c	Mon Dec  3 21:58:41 2001
+++ linux/init/main.c	Mon Dec  3 21:58:47 2001
@@ -111,6 +111,9 @@
 #ifdef CONFIG_PRINTER
 extern void lp_setup(char *str, int *ints);
 #endif
+#ifdef CONFIG_VIDEO_CPIA_PP
+extern void cpia_pp_setup(char *str, int *ints);
+#endif
 #ifdef CONFIG_JOY_AMIGA
 extern void js_am_setup(char *str, int *ints);
 #endif
@@ -492,6 +495,24 @@
 	{ "rd/c0d14p",0x3070 },
 	{ "rd/c0d15p",0x3078 },
 #endif
+#if defined(CONFIG_BLK_CPQ_DA) || defined(CONFIG_BLK_CPQ_DA_MODULE)
+	{ "ida/c0d0p",0x4800 },
+	{ "ida/c0d1p",0x4810 },
+	{ "ida/c0d2p",0x4820 },
+	{ "ida/c0d3p",0x4830 },
+	{ "ida/c0d4p",0x4840 },
+	{ "ida/c0d5p",0x4850 },
+	{ "ida/c0d6p",0x4860 },
+	{ "ida/c0d7p",0x4870 },
+	{ "ida/c0d8p",0x4880 },
+	{ "ida/c0d9p",0x4890 },
+	{ "ida/c0d10p",0x48A0 },
+	{ "ida/c0d11p",0x48B0 },
+	{ "ida/c0d12p",0x48C0 },
+	{ "ida/c0d13p",0x48D0 },
+	{ "ida/c0d14p",0x48E0 },
+	{ "ida/c0d15p",0x48F0 },
+#endif
 #ifdef CONFIG_ATARI_ACSI
 	{ "ada",     0x1c00 },
 	{ "adb",     0x1c10 },
@@ -692,6 +713,9 @@
 #endif
 #ifdef CONFIG_PRINTER
         { "lp=", lp_setup },
+#endif
+#ifdef CONFIG_VIDEO_CPIA_PP
+        { "cpia_pp=", cpia_pp_setup },
 #endif
 #ifdef CONFIG_JOY_AMIGA
 	{ "js_am=", js_am_setup },
diff -urN linux-2.2.15-pre5/ipc/shm.c linux/ipc/shm.c
--- linux-2.2.15-pre5/ipc/shm.c	Tue Jan  4 10:12:25 2000
+++ linux/ipc/shm.c	Mon Dec  3 21:58:47 2001
@@ -630,11 +630,12 @@
 		printk ("shm_nopage: id=%d invalid. Race.\n", id);
 		return 0;
 	}
+#endif
+	/* This can occur on a remap */
+	
 	if (idx >= shp->shm_npages) {
-		printk ("shm_nopage : too large page index. id=%d\n", id);
-		return 0;
+		return NULL;
 	}
-#endif
 
 	pte = __pte(shp->shm_pages[idx]);
 	if (!pte_present(pte)) {
diff -urN linux-2.2.15-pre5/kernel/exit.c linux/kernel/exit.c
--- linux-2.2.15-pre5/kernel/exit.c	Mon Dec  3 21:58:41 2001
+++ linux/kernel/exit.c	Tue Jan  4 10:12:25 2000
@@ -473,7 +473,6 @@
 				if (!(options & WUNTRACED) && !(p->flags & PF_PTRACED))
 					continue;
 				read_unlock(&tasklist_lock);
-				current->state = TASK_RUNNING;	/* We *must* do this before touching userspace! */
 				retval = ru ? getrusage(p, RUSAGE_BOTH, ru) : 0; 
 				if (!retval && stat_addr) 
 					retval = put_user((p->exit_code << 8) | 0x7f, stat_addr);
@@ -486,7 +485,6 @@
 				current->times.tms_cutime += p->times.tms_utime + p->times.tms_cutime;
 				current->times.tms_cstime += p->times.tms_stime + p->times.tms_cstime;
 				read_unlock(&tasklist_lock);
-				current->state = TASK_RUNNING;	/* We *must* do this before touching userspace! */
 				retval = ru ? getrusage(p, RUSAGE_BOTH, ru) : 0;
 				if (!retval && stat_addr)
 					retval = put_user(p->exit_code, stat_addr);
diff -urN linux-2.2.15-pre5/mm/page_alloc.c linux/mm/page_alloc.c
--- linux-2.2.15-pre5/mm/page_alloc.c	Mon Dec  3 21:58:41 2001
+++ linux/mm/page_alloc.c	Mon Dec  3 21:58:47 2001
@@ -245,16 +245,6 @@
 	RMQUEUE_TYPE(order, 1);
 	spin_unlock_irqrestore(&page_alloc_lock, flags);
 
-	/*
-	 * If we can schedule, do so, and make sure to yield.
-	 * We may be a real-time process, and if kswapd is
-	 * waiting for us we need to allow it to run a bit.
-	 */
-	if (gfp_mask & __GFP_WAIT) {
-		current->policy |= SCHED_YIELD;
-		schedule();
-	}
-
 nopage:
 	return 0;
 }
diff -urN linux-2.2.15-pre5/net/ipv4/tcp.c linux/net/ipv4/tcp.c
--- linux-2.2.15-pre5/net/ipv4/tcp.c	Mon Dec  3 21:58:41 2001
+++ linux/net/ipv4/tcp.c	Mon Dec  3 21:58:47 2001
@@ -5,7 +5,7 @@
  *
  *		Implementation of the Transmission Control Protocol(TCP).
  *
- * Version:	$Id: tcp.c,v 1.140.2.7 2000/01/13 23:44:00 davem Exp $
+ * Version:	$Id: tcp.c,v 1.140.2.8 2000/01/27 22:33:35 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -1283,9 +1283,6 @@
 		continue;
 
 	found_ok_skb:
-		/* Below we'll be accessing user memory, which might sleep, so... */
-		current->state = TASK_RUNNING;
-
 		/*	Lock the buffer. We can be fairly relaxed as
 		 *	an interrupt will never steal a buffer we are
 		 *	using unless I've missed something serious in
@@ -1367,15 +1364,15 @@
 		break;
 	}
 
-	remove_wait_queue(sk->sleep, &wait);
-	current->state = TASK_RUNNING;
-
 	if(copied >= 0 && msg->msg_name) {
 		tp->af_specific->addr2sockaddr(sk, (struct sockaddr *)
 					       msg->msg_name);       
 		if(addr_len)
 			*addr_len = tp->af_specific->sockaddr_len;
 	}
+
+	remove_wait_queue(sk->sleep, &wait);
+	current->state = TASK_RUNNING;
 
 	/* Clean up data we have read: This will do ACK frames. */
 	cleanup_rbuf(sk, copied);
diff -urN linux-2.2.15-pre5/net/ipv4/tcp_input.c linux/net/ipv4/tcp_input.c
--- linux-2.2.15-pre5/net/ipv4/tcp_input.c	Mon Dec  3 21:58:41 2001
+++ linux/net/ipv4/tcp_input.c	Mon Dec  3 21:58:47 2001
@@ -5,7 +5,7 @@
  *
  *		Implementation of the Transmission Control Protocol(TCP).
  *
- * Version:	$Id: tcp_input.c,v 1.164.2.11 2000/01/13 04:27:59 davem Exp $
+ * Version:	$Id: tcp_input.c,v 1.164.2.12 2000/01/31 20:43:36 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -2415,7 +2415,8 @@
 		 * BSD 4.4 also does reset.
 		 */
 		if ((sk->shutdown & RCV_SHUTDOWN) && sk->dead) {
-			if (after(TCP_SKB_CB(skb)->end_seq - th->fin, tp->rcv_nxt)) {
+			if (TCP_SKB_CB(skb)->end_seq != TCP_SKB_CB(skb)->seq &&
+			    after(TCP_SKB_CB(skb)->end_seq - th->fin, tp->rcv_nxt)) {
 				tcp_reset(sk);
 				return 1;
 			}
diff -urN linux-2.2.15-pre5/net/ipv4/tcp_output.c linux/net/ipv4/tcp_output.c
--- linux-2.2.15-pre5/net/ipv4/tcp_output.c	Mon Dec  3 21:58:41 2001
+++ linux/net/ipv4/tcp_output.c	Mon Dec  3 21:58:47 2001
@@ -753,12 +753,16 @@
 		}
 	} else {
 		/* Socket is locked, keep trying until memory is available. */
-		do {
+		for (;;) {
 			skb = sock_wmalloc(sk,
 					   (MAX_HEADER +
 					    sk->prot->max_header),
 					   1, GFP_KERNEL);
-		} while (skb == NULL);
+			if (skb)
+				break;
+			current->policy |= SCHED_YIELD;
+			schedule();
+		}
 
 		/* Reserve space for headers and prepare control bits. */
 		skb_reserve(skb, MAX_HEADER + sk->prot->max_header);
diff -urN linux-2.2.15-pre5/net/irda/ircomm/ircomm_param.c linux/net/irda/ircomm/ircomm_param.c
--- linux-2.2.15-pre5/net/irda/ircomm/ircomm_param.c	Mon Dec  3 21:58:41 2001
+++ linux/net/irda/ircomm/ircomm_param.c	Mon Dec  3 21:58:47 2001
@@ -6,10 +6,10 @@
  * Status:        Experimental.
  * Author:        Dag Brattli <dagb@cs.uit.no>
  * Created at:    Mon Jun  7 10:25:11 1999
- * Modified at:   Tue Dec 14 15:26:30 1999
+ * Modified at:   Sun Jan 30 14:32:03 2000
  * Modified by:   Dag Brattli <dagb@cs.uit.no>
  * 
- *     Copyright (c) 1999 Dag Brattli, All Rights Reserved.
+ *     Copyright (c) 1999-2000 Dag Brattli, All Rights Reserved.
  *     
  *     This program is free software; you can redistribute it and/or 
  *     modify it under the terms of the GNU General Public License as 
@@ -40,19 +40,27 @@
 
 #include <net/irda/ircomm_param.h>
 
-static int ircomm_param_service_type(void *instance, param_t *param, int get);
-static int ircomm_param_port_type(void *instance, param_t *param, int get);
-static int ircomm_param_port_name(void *instance, param_t *param, int get);
-static int ircomm_param_service_type(void *instance, param_t *param, int get);
-static int ircomm_param_data_rate(void *instance, param_t *param, int get);
-static int ircomm_param_data_format(void *instance, param_t *param, int get);
-static int ircomm_param_flow_control(void *instance, param_t *param, int get);
-static int ircomm_param_xon_xoff(void *instance, param_t *param, int get);
-static int ircomm_param_enq_ack(void *instance, param_t *param, int get);
-static int ircomm_param_line_status(void *instance, param_t *param, int get);
-static int ircomm_param_dte(void *instance, param_t *param, int get);
-static int ircomm_param_dce(void *instance, param_t *param, int get);
-static int ircomm_param_poll(void *instance, param_t *param, int get);
+static int ircomm_param_service_type(void *instance, irda_param_t *param, 
+				     int get);
+static int ircomm_param_port_type(void *instance, irda_param_t *param, 
+				  int get);
+static int ircomm_param_port_name(void *instance, irda_param_t *param, 
+				  int get);
+static int ircomm_param_service_type(void *instance, irda_param_t *param, 
+				     int get);
+static int ircomm_param_data_rate(void *instance, irda_param_t *param, 
+				  int get);
+static int ircomm_param_data_format(void *instance, irda_param_t *param, 
+				    int get);
+static int ircomm_param_flow_control(void *instance, irda_param_t *param, 
+				     int get);
+static int ircomm_param_xon_xoff(void *instance, irda_param_t *param, int get);
+static int ircomm_param_enq_ack(void *instance, irda_param_t *param, int get);
+static int ircomm_param_line_status(void *instance, irda_param_t *param, 
+				    int get);
+static int ircomm_param_dte(void *instance, irda_param_t *param, int get);
+static int ircomm_param_dce(void *instance, irda_param_t *param, int get);
+static int ircomm_param_poll(void *instance, irda_param_t *param, int get);
 
 static pi_minor_info_t pi_minor_call_table_common[] = {
 	{ ircomm_param_service_type, PV_INT_8_BITS },
@@ -170,7 +178,8 @@
  *    query and then the remote device sends its initial paramters
  *
  */
-static int ircomm_param_service_type(void *instance, param_t *param, int get)
+static int ircomm_param_service_type(void *instance, irda_param_t *param, 
+				     int get)
 {
 	struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
 	__u8 service_type = param->pv.b; /* We know it's a one byte integer */
@@ -229,7 +238,7 @@
  *    Since we only advertise serial service, this parameter should only
  *    be equal to IRCOMM_SERIAL.
  */
-static int ircomm_param_port_type(void *instance, param_t *param, int get)
+static int ircomm_param_port_type(void *instance, irda_param_t *param, int get)
 {
 	struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
 
@@ -253,7 +262,7 @@
  *    Exchange port name
  *
  */
-static int ircomm_param_port_name(void *instance, param_t *param, int get)
+static int ircomm_param_port_name(void *instance, irda_param_t *param, int get)
 {
 	struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
 	
@@ -276,7 +285,7 @@
  *    Exchange data rate to be used in this settings
  *
  */
-static int ircomm_param_data_rate(void *instance, param_t *param, int get)
+static int ircomm_param_data_rate(void *instance, irda_param_t *param, int get)
 {
 	struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
 	
@@ -299,7 +308,8 @@
  *    Exchange data format to be used in this settings
  *
  */
-static int ircomm_param_data_format(void *instance, param_t *param, int get)
+static int ircomm_param_data_format(void *instance, irda_param_t *param, 
+				    int get)
 {
 	struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
 
@@ -320,7 +330,8 @@
  *    Exchange flow control settings to be used in this settings
  *
  */
-static int ircomm_param_flow_control(void *instance, param_t *param, int get)
+static int ircomm_param_flow_control(void *instance, irda_param_t *param, 
+				     int get)
 {
 	struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
 
@@ -343,7 +354,7 @@
  *    Exchange XON/XOFF characters
  *
  */
-static int ircomm_param_xon_xoff(void *instance, param_t *param, int get)
+static int ircomm_param_xon_xoff(void *instance, irda_param_t *param, int get)
 {
 	struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
 
@@ -370,7 +381,7 @@
  *    Exchange ENQ/ACK characters
  *
  */
-static int ircomm_param_enq_ack(void *instance, param_t *param, int get)
+static int ircomm_param_enq_ack(void *instance, irda_param_t *param, int get)
 {
 	struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
 
@@ -397,7 +408,8 @@
  *    
  *
  */
-static int ircomm_param_line_status(void *instance, param_t *param, int get)
+static int ircomm_param_line_status(void *instance, irda_param_t *param, 
+				    int get)
 {
 	IRDA_DEBUG(2, __FUNCTION__ "(), not impl.\n");
 
@@ -410,7 +422,7 @@
  *    If we get here, there must be some sort of null-modem connection, and
  *    we are probably working in server mode as well.
  */
-static int ircomm_param_dte(void *instance, param_t *param, int get)
+static int ircomm_param_dte(void *instance, irda_param_t *param, int get)
 {
 	struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
 	__u8 dte;
@@ -453,7 +465,7 @@
  *    
  *
  */
-static int ircomm_param_dce(void *instance, param_t *param, int get)
+static int ircomm_param_dce(void *instance, irda_param_t *param, int get)
 {
 	struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
 	__u8 dce;
@@ -485,7 +497,7 @@
  *    Called when the peer device is polling for the line settings
  *
  */
-static int ircomm_param_poll(void *instance, param_t *param, int get)
+static int ircomm_param_poll(void *instance, irda_param_t *param, int get)
 {
 	struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
 
diff -urN linux-2.2.15-pre5/net/irda/irttp.c linux/net/irda/irttp.c
--- linux-2.2.15-pre5/net/irda/irttp.c	Mon Dec  3 21:58:41 2001
+++ linux/net/irda/irttp.c	Mon Dec  3 21:58:47 2001
@@ -6,7 +6,7 @@
  * Status:        Stable
  * Author:        Dag Brattli <dagb@cs.uit.no>
  * Created at:    Sun Aug 31 20:14:31 1997
- * Modified at:   Sun Jan 16 22:46:33 2000
+ * Modified at:   Sun Jan 30 14:30:32 2000
  * Modified by:   Dag Brattli <dagb@cs.uit.no>
  * 
  *     Copyright (c) 1998-2000 Dag Brattli <dagb@cs.uit.no>, 
@@ -60,7 +60,8 @@
 static void irttp_fragment_skb(struct tsap_cb *self, struct sk_buff *skb);
 static void irttp_start_todo_timer(struct tsap_cb *self, int timeout);
 static struct sk_buff *irttp_reassemble_skb(struct tsap_cb *self);
-static int irttp_param_max_sdu_size(void *instance, param_t *param, int get);
+static int irttp_param_max_sdu_size(void *instance, irda_param_t *param, 
+				    int get);
 
 /* Information for parsing parameters in IrTTP */
 static pi_minor_info_t pi_minor_call_table[] = {
@@ -1399,7 +1400,8 @@
  *    will be called both when this parameter needs to be inserted into, and
  *    extracted from the connect frames
  */
-static int irttp_param_max_sdu_size(void *instance, param_t *param, int get)
+static int irttp_param_max_sdu_size(void *instance, irda_param_t *param, 
+				    int get)
 {
 	struct tsap_cb *self;
 
diff -urN linux-2.2.15-pre5/net/irda/parameters.c linux/net/irda/parameters.c
--- linux-2.2.15-pre5/net/irda/parameters.c	Mon Dec  3 21:58:41 2001
+++ linux/net/irda/parameters.c	Mon Dec  3 21:58:47 2001
@@ -6,10 +6,10 @@
  * Status:        Experimental.
  * Author:        Dag Brattli <dagb@cs.uit.no>
  * Created at:    Mon Jun  7 10:25:11 1999
- * Modified at:   Tue Dec 14 16:03:57 1999
+ * Modified at:   Sun Jan 30 14:08:39 2000
  * Modified by:   Dag Brattli <dagb@cs.uit.no>
  * 
- *     Copyright (c) 1999 Dag Brattli, All Rights Reserved.
+ *     Copyright (c) 1999-2000 Dag Brattli, All Rights Reserved.
  *     
  *     This program is free software; you can redistribute it and/or 
  *     modify it under the terms of the GNU General Public License as 
@@ -78,7 +78,7 @@
 static int irda_insert_no_value(void *self, __u8 *buf, int len, __u8 pi, 
 				PV_TYPE type, PI_HANDLER func)
 {
-	param_t p;
+	irda_param_t p;
 	int ret;
 
 	p.pi = pi;
@@ -105,7 +105,7 @@
 static int irda_extract_no_value(void *self, __u8 *buf, int len, __u8 pi, 
 				 PV_TYPE type, PI_HANDLER func)
 {
-	param_t p;
+	irda_param_t p;
 	int ret;
 
 	/* Extract values anyway, since handler may need them */
@@ -129,7 +129,7 @@
 static int irda_insert_integer(void *self, __u8 *buf, int len, __u8 pi, 
 			       PV_TYPE type, PI_HANDLER func)
 {
-	param_t p;
+	irda_param_t p;
 	int n = 0;
 	int err;
 
@@ -202,7 +202,7 @@
 static int irda_extract_integer(void *self, __u8 *buf, int len, __u8 pi, 
 				PV_TYPE type, PI_HANDLER func)
 {
-	param_t p;
+	irda_param_t p;
 	int n = 0;
 	int err;
 
@@ -273,7 +273,7 @@
 			       PV_TYPE type, PI_HANDLER func)
 {
 	char str[33];
-	param_t p;
+	irda_param_t p;
 	int err;
 
 	IRDA_DEBUG(2, __FUNCTION__ "()\n");
@@ -319,7 +319,7 @@
 static int irda_extract_octseq(void *self, __u8 *buf, int len, __u8 pi,
 			       PV_TYPE type, PI_HANDLER func)
 {
-	param_t p;
+	irda_param_t p;
 
 	p.pi = pi;     /* In case handler needs to know */
 	p.pl = buf[1]; /* Extract lenght of value */
@@ -346,10 +346,10 @@
  */
 int irda_param_pack(__u8 *buf, char *fmt, ...)
 {
+	irda_pv_t arg;
 	va_list args;
 	char *p;
 	int n = 0;
-	pv_t arg;
 	
 	va_start(args, fmt);
 
@@ -390,10 +390,10 @@
  */
 int irda_param_unpack(__u8 *buf, char *fmt, ...)
 {
+	irda_pv_t arg;
 	va_list args;
 	char *p;
 	int n = 0;
-	pv_t arg;
 
 	va_start(args, fmt);
 
diff -urN linux-2.2.15-pre5/net/irda/qos.c linux/net/irda/qos.c
--- linux-2.2.15-pre5/net/irda/qos.c	Mon Dec  3 21:58:41 2001
+++ linux/net/irda/qos.c	Mon Dec  3 21:58:47 2001
@@ -6,10 +6,10 @@
  * Status:        Stable
  * Author:        Dag Brattli <dagb@cs.uit.no>
  * Created at:    Tue Sep  9 00:00:26 1997
- * Modified at:   Sun Dec 12 13:47:09 1999
+ * Modified at:   Sun Jan 30 14:29:16 2000
  * Modified by:   Dag Brattli <dagb@cs.uit.no>
  * 
- *     Copyright (c) 1998-1999 Dag Brattli <dagb@cs.uit.no>, 
+ *     Copyright (c) 1998-2000 Dag Brattli <dagb@cs.uit.no>, 
  *     All Rights Reserved.
  *     
  *     This program is free software; you can redistribute it and/or 
@@ -43,13 +43,18 @@
 #define CI_BZIP2  27 /* Random pick */
 #endif
 
-static int irlap_param_baud_rate(void *instance, param_t *param, int get);
-static int irlap_param_link_disconnect(void *instance, param_t *parm, int get);
-static int irlap_param_max_turn_time(void *instance, param_t *param, int get);
-static int irlap_param_data_size(void *instance, param_t *param, int get);
-static int irlap_param_window_size(void *instance, param_t *param, int get);
-static int irlap_param_additional_bofs(void *instance, param_t *parm, int get);
-static int irlap_param_min_turn_time(void *instance, param_t *param, int get);
+static int irlap_param_baud_rate(void *instance, irda_param_t *param, int get);
+static int irlap_param_link_disconnect(void *instance, irda_param_t *parm, 
+				       int get);
+static int irlap_param_max_turn_time(void *instance, irda_param_t *param, 
+				     int get);
+static int irlap_param_data_size(void *instance, irda_param_t *param, int get);
+static int irlap_param_window_size(void *instance, irda_param_t *param, 
+				   int get);
+static int irlap_param_additional_bofs(void *instance, irda_param_t *parm, 
+				       int get);
+static int irlap_param_min_turn_time(void *instance, irda_param_t *param, 
+				     int get);
 
 __u32 min_turn_times[]  = { 10000, 5000, 1000, 500, 100, 50, 10, 0 }; /* us */
 __u32 baud_rates[]      = { 2400, 9600, 19200, 38400, 57600, 115200, 576000, 
@@ -345,7 +350,7 @@
  *    Negotiate data-rate
  *
  */
-static int irlap_param_baud_rate(void *instance, param_t *param, int get)
+static int irlap_param_baud_rate(void *instance, irda_param_t *param, int get)
 {
 	__u16 final;
 
@@ -380,7 +385,8 @@
  *    Negotiate link disconnect/threshold time. 
  *
  */
-static int irlap_param_link_disconnect(void *instance, param_t *param, int get)
+static int irlap_param_link_disconnect(void *instance, irda_param_t *param, 
+				       int get)
 {
 	__u16 final;
 	
@@ -413,7 +419,8 @@
  *    will be negotiated independently for each station
  *
  */
-static int irlap_param_max_turn_time(void *instance, param_t *param, int get)
+static int irlap_param_max_turn_time(void *instance, irda_param_t *param, 
+				     int get)
 {
 	struct irlap_cb *self = (struct irlap_cb *) instance;
 	
@@ -435,7 +442,7 @@
  *    will be negotiated independently for each station
  *
  */
-static int irlap_param_data_size(void *instance, param_t *param, int get)
+static int irlap_param_data_size(void *instance, irda_param_t *param, int get)
 {
 	struct irlap_cb *self = (struct irlap_cb *) instance;
 	
@@ -457,7 +464,8 @@
  *    will be negotiated independently for each station
  *
  */
-static int irlap_param_window_size(void *instance, param_t *param, int get)
+static int irlap_param_window_size(void *instance, irda_param_t *param, 
+				   int get)
 {
 	struct irlap_cb *self = (struct irlap_cb *) instance;
 	
@@ -478,7 +486,7 @@
  *    Negotiate additional BOF characters. This is a type 1 parameter and
  *    will be negotiated independently for each station.
  */
-static int irlap_param_additional_bofs(void *instance, param_t *param, int get)
+static int irlap_param_additional_bofs(void *instance, irda_param_t *param, int get)
 {
 	struct irlap_cb *self = (struct irlap_cb *) instance;
 	
@@ -499,7 +507,8 @@
  *    Negotiate the minimum turn around time. This is a type 1 parameter and
  *    will be negotiated independently for each station
  */
-static int irlap_param_min_turn_time(void *instance, param_t *param, int get)
+static int irlap_param_min_turn_time(void *instance, irda_param_t *param, 
+				     int get)
 {
 	struct irlap_cb *self = (struct irlap_cb *) instance;
 	
diff -urN linux-2.2.15-pre5/net/sunrpc/sched.c linux/net/sunrpc/sched.c
--- linux-2.2.15-pre5/net/sunrpc/sched.c	Fri May  7 11:19:11 1999
+++ linux/net/sunrpc/sched.c	Mon Dec  3 21:58:47 2001
@@ -413,18 +413,15 @@
 		}
 
 		/*
-		 * No handler for next step means exit.
-		 */
-		if (!task->tk_action)
-			break;
-
-		/*
 		 * Perform the next FSM step.
 		 * tk_action may be NULL when the task has been killed
 		 * by someone else.
 		 */
-		if (RPC_IS_RUNNING(task) && task->tk_action)
+		if (RPC_IS_RUNNING(task)) {
+			if (!task->tk_action)
+				break;
 			task->tk_action(task);
+		}
 
 		/*
 		 * Check whether task is sleeping.
