diff -u --recursive --new-file v2.1.62/linux/CREDITS linux/CREDITS
--- v2.1.62/linux/CREDITS	Mon Nov  3 13:04:25 1997
+++ linux/CREDITS	Mon Nov 10 16:12:38 1997
@@ -643,12 +643,13 @@
 S: United Kingdom
 
 N: Ron Holt
-E: ron@novell.com
+E: ron@caldera.com
+W: http://www.holt.org/
 D: Kernel development
-D: Contributed to kernel Wabi/Wine support
-S: Novell, Inc.
-S: 1700 South 122 East, Mailstop CP-1
-S: Provo, Utah 84606
+D: Minor kernel modifications to support Wabi and Wine
+S: Caldera, Inc.
+S: 240 West Center St.
+S: Orem, Utah 84059-1920
 S: USA
 
 N: Rob W. W. Hooft
@@ -867,6 +868,12 @@
 S: Am Muehlenweg 38
 S: D53424 Remagen
 S: Germany
+
+N: Achim Leubner
+E: achim@vortex.de
+D: GDT SCSI Disk Array Controller driver
+S: ICP vortex Computersysteme GmbH
+S: Flein, Germany
 
 N: Phil Lewis
 E: beans@bucket.ualr.edu
diff -u --recursive --new-file v2.1.62/linux/Documentation/Configure.help linux/Documentation/Configure.help
--- v2.1.62/linux/Documentation/Configure.help	Mon Nov  3 13:04:25 1997
+++ linux/Documentation/Configure.help	Sat Nov  8 11:39:12 1997
@@ -419,6 +419,33 @@
   here and read Documentation/modules.txt. The module will be called
   raid0.o. If unsure, say Y.
 
+RAID-1 (mirroring) mode
+CONFIG_MD_MIRRORING
+  A RAID-1 set consists of several disk drives which are exact copies
+  of each other. In the event of a mirror failure, the RAID driver
+  will continue to use the operational mirrors in the set, providing
+  an error free MD device to the higher levels of the kernel. In
+  a set with N drives, the available space is the capacity of a single
+  drive, and the set protects against a failure of (N - 1) drives.
+  raidtools, a set of user-space tools which create and maintain
+  RAID1/4/5 sets, is available at:
+  ftp://ftp.kernel.org/pub/linux/daemons/raid
+  http://luthien.nuclecu.unam.mx/~miguel/raid
+
+RAID-4/RAID-5 mode
+CONFIG_MD_RAID5
+  A RAID-5 set of N drives with a capacity of C MB per drive provides
+  the capacity of C * (N - 1) drives, and protects against a failure
+  of a single drive. For a given sector (row) number, (N - 1) drives
+  contain data sectors, and one drive contains the parity protection.
+  For a RAID-4 set, the parity blocks are present on a single drive,
+  while a RAID-5 set distributes the parity accross the drives in one
+  of the available parity distribution methods.
+  raidtools, a set of user-space tools which create and maintain
+  RAID1/4/5 sets, is available at:
+  ftp://ftp.kernel.org/pub/linux/daemons/raid
+  http://luthien.nuclecu.unam.mx/~miguel/raid
+
 Support for Deskstation RPC44 
 CONFIG_DESKSTATION_RPC44
   This is a machine with a R4400 100 MHz CPU. To compile a Linux
@@ -2302,6 +2329,15 @@
   and read Documentation/modules.txt. The module will be called
   AM53C974.o.
 
+GDT SCSI Disk Array Controller support
+CONFIG_SCSI_GDTH
+  This is a driver for all SCSI Disk Array Controllers (EISA/ISA/PCI) 
+  manufactured by ICP vortex. It is documented in the kernel source in
+  drivers/scsi/gdth.c and drivers/scsi/gdth.h. This driver is also
+  available as a module ( = code which can be inserted in and removed
+  from the running kernel whenever you want). If you want to compile 
+  it as a module, say M here and read Documentation/modules.txt. 	
+
 IOMEGA Parallel Port ZIP drive SCSI support
 CONFIG_SCSI_PPA
   This driver supports the parallel port version of IOMEGA's ZIP drive
@@ -5219,6 +5255,26 @@
   removed from the running kernel whenever you want). The module will
   be called joystick.o. If you want to compile it as a module, say M
   here and read Documentation/modules.txt.
+
+Radio support
+CONFIG_MISC_RADIO
+  If you have a radio card (you will probably know if you do!), then
+  you will want to say "y" here and make a character device file 
+  (usually /dev/radio) with major number 10 and minor 129 using mknod
+  ("man mknod").  And then, don't forget to pick up some useful tools
+  to use said device (you _might_ find something at ftp.lmh.ox.ac.uk:
+  /users/weejock/linux/radio/, but I haven't written anything too
+  useful yet...)
+
+AIMSlab RadioTrack card
+CONFIG_RADIO_RTRACK
+  Choose "y" here if you have one of these, and then fill in the port
+  address below.
+
+RadioTrack i/o port
+CONFIG_RADIO_RTRACK_PORT
+  Enter either 0x30f or 0x20f here.  The card default is 0x30f, if you
+  haven't changed the jumper setting on the card.
 
 ARC console time
 CONFIG_RTC_ARC
diff -u --recursive --new-file v2.1.62/linux/Documentation/devices.tex linux/Documentation/devices.tex
--- v2.1.62/linux/Documentation/devices.tex	Tue Sep 23 16:48:46 1997
+++ linux/Documentation/devices.tex	Mon Nov 10 10:56:26 1997
@@ -4,6 +4,8 @@
 % pages to print... :-)  If you're actually putting this in print, you
 % may wish to change these.
 %
+% $Id: devices.tex,v 1.3 1997/11/10 01:29:35 hpa Exp $
+%
 \oddsidemargin=0in
 \textwidth=6.5in
 \topmargin=0in
@@ -43,39 +45,29 @@
  {\end{tabbing}}
 %
 % If you reformat this document, *please* make sure this information
-% gets included!
+% gets included!  This list changes frequently, so it is crucial to
+% know the date of the revision.
 %
 \title{{\bf Linux Allocated Devices}}
 \author{Maintained by H. Peter Anvin $<$hpa@zytor.com$>$}
-\date{Last revised: September 11, 1997}
+\date{Last revised: November 9, 1997}
 \maketitle
 %
 \noindent
-This list is the successor to Rick Miller's Linux Device List, which
-he stopped maintaining when he got busy with other things in 1993.  It
-is a registry of allocated major device numbers, as well as the
-recommended {\file /dev} directory nodes for these devices.
+This list is the Linux Device List, the official registry of allocated
+device numbers and {\file /dev} directory nodes for the Linux
+operating system.
 
 The latest version of this list is included with the Linux kernel
-sources in \LaTeX\ and ASCII form.  It is also available separate from
-{\url ftp://ftp.kernel.org/pub/linux/docs/device-list/}.  In case of
-discrepancy, the \LaTeX\ version is authoritative.
+sources in \LaTeX\ and ASCII form.  It is also available separately
+from {\url ftp://ftp.kernel.org/pub/linux/docs/device-list/}.  In case
+of discrepancy between the text and \LaTeX\ versions, the \LaTeX\
+version is authoritative.
 
 This document is included by reference into the Linux Filesystem
 Standard (FSSTND).  The FSSTND is available from
 {\url ftp://tsx-11.mit.edu/pub/linux/docs/linux-standards/fsstnd/}.
 
-To have a major number allocated, or a minor number in situations
-where that applies (e.g.\ busmice), please contact me with the
-appropriate device information.  I {\em very\/} much appreciate if you
-send me a device description in the same format as the ones already in
-this file.  Also, if you have additional information regarding any of
-the devices listed below, or if I have made a mistake, I would greatly
-appreciate a note.
-
-NOTE: When sending me mail, {\em please\/} include the word ``device''
-in the subject so your mail won't accidentally get buried!
-
 Allocations marked (68k/Amiga) apply to Linux/68k on the Amiga
 platform only.  Allocations marked (68k/Atari) apply to Linux/68k on
 the Atari platform only.
@@ -92,6 +84,28 @@
 on this list.  Any such information requests will be deleted without
 reply.
 
+\section{How to submit a device entry}
+
+To have a major number allocated, or a minor number in situations
+where that applies (e.g. busmice), please contact me with the
+appropriate device information.  Also, if you have additional
+information regarding any of the devices listed below, or if I have
+made a mistake, I would greatly appreciate a note.
+
+I do, however, make two requests about the nature of your report.
+This is necessary for me to be able to keep this list up to date and
+correct in a timely manner.  First of all, {\em please\/} include the
+word ``device'' in the subject so your mail won't accidentally get
+buried!  I receive hundreds of email messages a day, so mail sent with
+other subjects may very well get lost in the avalanche.
+
+Second, please include a description of the device {\em in the same
+format as this list\/}.  The reason for this is that it is the only
+way I have found to ensure I have all the requisite information to
+publish your device and avoid conflicts.
+
+Your cooperation is appreciated.
+
 \section{Major numbers}
 
 \begin{devicelist}
@@ -221,7 +235,12 @@
 \major{88}{}{char }{COMX synchronous serial card}
 \major{89}{}{char }{I$^2$C bus interface}
 \major{90}{}{char }{Memory Technology Device (RAM, ROM, Flash)}
-\major{91}{--119}{}{Unallocated}
+\major{91}{}{char }{CAN-Bus devices}
+\major{92}{}{char }{Reserved for ith Kommunikationstechnik MIC ISDN card}
+\major{93}{}{char }{IBM Smart Capture Card frame grabber}
+\major{94}{}{char }{miroVIDEO DC10/30 capture/playback device}
+\major{95}{}{char }{IP Filter}
+\major{96}{--119}{}{Unallocated}
 \major{120}{--127}{}{Local/experimental use}
 \major{128}{--239}{}{Unallocated}
 \major{240}{--254}{}{Local/experimental use}
@@ -1506,7 +1525,43 @@
 \end{devicelist}
 
 \begin{devicelist}
-\major{91}{--119}{}{Unallocated}
+\major{91}{}{char }{CAN-Bus controller}
+	\minor{0}{/dev/can0}{First CAN-Bus controller}
+	\minor{1}{/dev/can1}{Second CAN-Bus controller}
+	\minordots
+\end{devicelist}
+
+\begin{devicelist}
+\major{92}{}{char }{Reserved for ith Kommunikationstechnik MIC ISDN card}
+\end{devicelist}
+
+\begin{devicelist}
+\major{93}{}{char }{IBM Smart Capture Card frame grabber}
+	\minor{0}{/dev/iscc0}{First Smart Capture Card}
+	\minor{1}{/dev/iscc1}{Second Smart Capture Card}
+	\minordots
+	\minor{128}{/dev/isccctl0}{First Smart Capture Card control}
+	\minor{129}{/dev/isccctl1}{Second Smart Capture Card control}
+	\minordots
+\end{devicelist}
+
+\begin{devicelist}
+\major{94}{}{char }{miroVIDEO DC10/30 capture/playback device}
+	\minor{0}{/dev/dcxx0}{First capture card}
+	\minor{1}{/dev/dcxx1}{Second capture card}
+	\minordots
+\end{devicelist}
+
+\begin{devicelist}
+\major{95}{}{char }{IP filter}
+	\minor{0}{/dev/ipl}{Filter control device/log file}
+	\minor{1}{/dev/ipnat}{NAT control device/log file}
+	\minor{2}{/dev/ipstate}{State information log file}
+	\minor{3}{/dev/ipauth}{Authentication control device/log file}
+\end{devicelist}
+
+\begin{devicelist}
+\major{96}{--119}{}{Unallocated}
 \end{devicelist}
 
 \begin{devicelist}
@@ -1593,11 +1648,11 @@
 point to the ``cooked'' devices ({\file /dev/st*} and {\file
 /dev/sr*}, respectively), whereas {\file /dev/cdwriter} and {\file
 /dev/scanner} should point to the appropriate generic SCSI devices
-({\file /dev/sg*}.)
+({\file /dev/sg*}).
 
 {\file /dev/mouse} may point to a primary serial TTY device, a
 hardware mouse device, or a socket for a mouse driver program
-(e.g. {\file /dev/gpmdata}.)
+(e.g. {\file /dev/gpmdata}).
 
 \subsection{Sockets and pipes}
 
diff -u --recursive --new-file v2.1.62/linux/Documentation/devices.txt linux/Documentation/devices.txt
--- v2.1.62/linux/Documentation/devices.txt	Tue Sep 23 16:48:46 1997
+++ linux/Documentation/devices.txt	Mon Nov 10 10:56:26 1997
@@ -1,38 +1,27 @@
 		       LINUX ALLOCATED DEVICES
 	     Maintained by H. Peter Anvin <hpa@zytor.com>
 
-		   Last revised: September 11, 1997
+		    Last revised: November 9, 1997
 
-This list is the successor to Rick Miller's Linux Device List, which
-he stopped maintaining when he got busy with other things in 1993.  It
-is a registry of allocated major device numbers, as well as the
-recommended /dev directory nodes for these devices.
+This list is the Linux Device List, the official registry of allocated
+device numbers and /dev directory nodes for the Linux operating
+system.
 
 The latest version of this list is included with the Linux kernel
 sources in LaTeX and ASCII form.  It is also available separately from
 ftp://ftp.kernel.org/pub/linux/docs/device-list/.  In case of
-discrepancy, the LaTeX version is authoritative.
+discrepancy between the text and LaTeX versions, the LaTeX version is
+authoritative.
 
 This document is included by reference into the Linux Filesystem
 Standard (FSSTND).  The FSSTND is available from
 ftp://tsx-11.mit.edu/pub/linux/docs/linux-standards/fsstnd/.
 
-To have a major number allocated, or a minor number in situations
-where that applies (e.g. busmice), please contact me with the
-appropriate device information.  I *very* much appreciate if you send
-me a device description in the same format as the ones already in this
-file.  Also, if you have additional information regarding any of the
-devices listed below, or if I have made a mistake, I would greatly
-appreciate a note.
-
-NOTE: When sending me mail, *please* include the word "device" in the
-subject so your mail won't accidentally get buried!
-
 Allocations marked (68k/Amiga) apply to Linux/68k on the Amiga
-platform only.  Allocations marked (68k/Atari) apply to Linux/68k on
+platform only.	Allocations marked (68k/Atari) apply to Linux/68k on
 the Atari platform only.
 
-This document is in the public domain.  The author requests, however,
+This document is in the public domain.	The author requests, however,
 that semantically altered versions are not distributed without
 permission of the author, assuming the author can be contacted without
 an unreasonable effort.
@@ -44,11 +33,34 @@
 on this list.  Any such information requests will be deleted without
 reply.
 
-  0 		Unnamed devices (e.g. non-device mounts)
+     **** PLEASE READ THIS BEFORE SUBMITTING A DEVICE ENTRY ****
+
+To have a major number allocated, or a minor number in situations
+where that applies (e.g. busmice), please contact me with the
+appropriate device information.	 Also, if you have additional
+information regarding any of the devices listed below, or if I have
+made a mistake, I would greatly appreciate a note.
+
+I do, however, make two requests about the nature of your report.
+This is necessary for me to be able to keep this list up to date and
+correct in a timely manner.  First of all, *please* include the word
+"device" in the subject so your mail won't accidentally get buried!  I
+receive hundreds of email messages a day, so mail sent with other
+subjects may very well get lost in the avalanche.
+
+Second, please include a description of the device *in the same format
+as this list*.	The reason for this is that it is the only way I have
+found to ensure I have all the requisite information to publish your
+device and avoid conflicts.
+
+Your cooperation is appreciated.
+
+
+  0		Unnamed devices (e.g. non-device mounts)
 		  0 = reserved as null device number
 
   1 char	Memory devices
-    		  1 = /dev/mem		Physical memory access
+		  1 = /dev/mem		Physical memory access
 		  2 = /dev/kmem		Kernel virtual memory access
 		  3 = /dev/null		Null device
 		  4 = /dev/port		I/O port access
@@ -97,7 +109,7 @@
 		  8 = /dev/fd?h1200	5.25" 1200K in a 1200K drive(1)
 		 40 = /dev/fd?h1440	5.25" 1440K in a 1200K drive(1)
 		 56 = /dev/fd?h1476	5.25" 1476K in a 1200K drive
-	  	 72 = /dev/fd?h1494	5.25" 1494K in a 1200K drive
+		 72 = /dev/fd?h1494	5.25" 1494K in a 1200K drive
 		 92 = /dev/fd?h1600	5.25" 1600K in a 1200K drive(1)
 
 		 12 = /dev/fd?u360	3.5"   360K Double Density
@@ -128,7 +140,7 @@
 		NOTE: The letter in the device name (d, q, h or u)
 		signifies the type of drive: 5.25" Double Density (d),
 		5.25" Quad Density (q), 5.25" High Density (h) or 3.5"
-		(any model, u).  The use of the capital letters D, H
+		(any model, u).	 The use of the capital letters D, H
 		and E for the 3.5" models have been deprecated, since
 		the drive type is insignificant for these devices.
 
@@ -139,7 +151,7 @@
 		256 = /dev/ttyef	256th PTY slave
 
     block	First MFM, RLL and IDE hard disk/CD-ROM interface
-    		  0 = /dev/hda		Master: whole disk (or CD-ROM)
+		  0 = /dev/hda		Master: whole disk (or CD-ROM)
 		 64 = /dev/hdb		Slave: whole disk (or CD-ROM)
 		
 		For partitions, add to the whole disk device number:
@@ -155,7 +167,7 @@
 		appropriate to their respective architectures.
 		
   4 char	TTY devices
-    		  0 = /dev/tty0		Current virtual console
+		  0 = /dev/tty0		Current virtual console
 
 		  1 = /dev/tty1		First virtual console
 		      ...
@@ -178,7 +190,7 @@
 		devices.
 
   5 char	Alternate TTY devices
-    		  0 = /dev/tty		Current TTY device
+		  0 = /dev/tty		Current TTY device
 		  1			Reserved for console device
 		 64 = /dev/cua0		Callout device corresponding to ttyS0
 		      ...
@@ -188,7 +200,7 @@
 		/dev/console in a future version of Linux.
 
   6 char	Parallel printer devices
-    		  0 = /dev/lp0		First parallel printer (0x3bc)
+		  0 = /dev/lp0		First parallel printer (0x3bc)
 		  1 = /dev/lp1		Second parallel printer (0x378)
 		  2 = /dev/lp2		Third parallel printer (0x278)
 
@@ -197,7 +209,7 @@
 		/dev/lp1.
 
   7 char	Virtual console capture devices
-    		  0 = /dev/vcs		Current vc text contents
+		  0 = /dev/vcs		Current vc text contents
 		  1 = /dev/vcs1		tty1 text contents
 		      ...
 		 63 = /dev/vcs63	tty63 text contents
@@ -214,14 +226,14 @@
 		      ...
 
 		The loopback devices are used to mount filesystems not
-		associated with block devices.  The binding to the
+		associated with block devices.	The binding to the
 		loopback devices is handled by mount(8) or losetup(8).
 
   8 block	SCSI disk devices
-    		  0 = /dev/sda		First SCSI disk whole disk
+		  0 = /dev/sda		First SCSI disk whole disk
 		 16 = /dev/sdb		Second SCSI disk whole disk
 		 32 = /dev/sdc		Third SCSI disk whole disk
-  		      ...
+		      ...
 		240 = /dev/sdp		Sixteenth SCSI disk whole disk
 
 		Partitions are handled in the same way as for IDE
@@ -229,16 +241,16 @@
 		partitions is 15.
 
   9 char	SCSI tape devices
-    		  0 = /dev/st0		First SCSI tape, mode 0
+		  0 = /dev/st0		First SCSI tape, mode 0
 		  1 = /dev/st1		Second SCSI tape, mode 0
 		      ...
-    		 32 = /dev/st0l		First SCSI tape, mode 1
+		 32 = /dev/st0l		First SCSI tape, mode 1
 		 33 = /dev/st1l		Second SCSI tape, mode 1
 		      ...
-    		 64 = /dev/st0m		First SCSI tape, mode 2
+		 64 = /dev/st0m		First SCSI tape, mode 2
 		 65 = /dev/st1m		Second SCSI tape, mode 2
 		      ...
-    		 96 = /dev/st0a		First SCSI tape, mode 3
+		 96 = /dev/st0a		First SCSI tape, mode 3
 		 97 = /dev/st1a		Second SCSI tape, mode 3
 		      ...
 		128 = /dev/nst0		First SCSI tape, mode 0, no rewind
@@ -310,7 +322,7 @@
 		The raw keyboard device is used on Linux/SPARC only.
 
     block	SCSI CD-ROM devices
-    		  0 = /dev/sr0		First SCSI CD-ROM
+		  0 = /dev/sr0		First SCSI CD-ROM
 		  1 = /dev/sr1		Second SCSI CD-ROM
 		      ...
 
@@ -438,7 +450,7 @@
 		  1 = /dev/ttyD1	Second Digiboard port
 		      ...
     block	Second IDE hard disk/CD-ROM interface
-    		  0 = /dev/hdc		Master: whole disk (or CD-ROM)
+		  0 = /dev/hdc		Master: whole disk (or CD-ROM)
 		 64 = /dev/hdd		Slave: whole disk (or CD-ROM)
 		
 		Partitions are handled the same way as for the first
@@ -538,7 +550,7 @@
 		  0 = /dev/fb0		First frame buffer
 		  1 = /dev/fb0autodetect
 		 24 = /dev/fb0user0
-	              ...
+		      ...
 		 31 = /dev/fb0user7
 		 32 = /dev/fb1		Second frame buffer
 		 33 = /dev/fb1autodetect
@@ -547,7 +559,7 @@
 		 63 = /dev/fb1user7
 
 		The universal frame buffer device is currently only
-		supported on Linux/68k and Linux/SPARC.  The plain
+		supported on Linux/68k and Linux/SPARC.	 The plain
 		device accesses the frame buffer at current resolution
 		(Linux/68k calls this device "current",
 		e.g. /dev/fb0current); the "autodetect" one at bootup
@@ -614,7 +626,7 @@
 		  1 = /dev/cux1		Callout device corresponding to ttyX1
 		      ...
     block	Third IDE hard disk/CD-ROM interface
-    		  0 = /dev/hde		Master: whole disk (or CD-ROM)
+		  0 = /dev/hde		Master: whole disk (or CD-ROM)
 		 64 = /dev/hdf		Slave: whole disk (or CD-ROM)
 
 		Partitions are handled the same way as for the first
@@ -632,7 +644,7 @@
 		on.
 
     block	Fourth IDE hard disk/CD-ROM interface
-    		  0 = /dev/hdg		Master: whole disk (or CD-ROM)
+		  0 = /dev/hdg		Master: whole disk (or CD-ROM)
 		 64 = /dev/hdh		Slave: whole disk (or CD-ROM)
 		
 		Partitions are handled the same way as for the first
@@ -643,10 +655,10 @@
 		  1 = /dev/midi1	Second MIDI port, kernel timed
 		  2 = /dev/midi2	Third MIDI port, kernel timed
 		  3 = /dev/midi3	Fourth MIDI port, kernel timed
-	         64 = /dev/rmidi0	First MIDI port, untimed
-	         65 = /dev/rmidi1	Second MIDI port, untimed
-	         66 = /dev/rmidi2	Third MIDI port, untimed
-	         67 = /dev/rmidi3	Fourth MIDI port, untimed
+		 64 = /dev/rmidi0	First MIDI port, untimed
+		 65 = /dev/rmidi1	Second MIDI port, untimed
+		 66 = /dev/rmidi2	Third MIDI port, untimed
+		 67 = /dev/rmidi3	Fourth MIDI port, untimed
 		128 = /dev/smpte0	First MIDI port, SMPTE timed
 		129 = /dev/smpte1	Second MIDI port, SMPTE timed
 		130 = /dev/smpte2	Third MIDI port, SMPTE timed
@@ -809,8 +821,8 @@
 
  53 char	BDM interface for remote debugging MC683xx microcontrollers
 		  0 = /dev/pd_bdm0	PD BDM interface on lp0
-          	  1 = /dev/pd_bdm1	PD BDM interface on lp1
-          	  2 = /dev/pd_bdm2	PD BDM interface on lp2
+		  1 = /dev/pd_bdm1	PD BDM interface on lp1
+		  2 = /dev/pd_bdm2	PD BDM interface on lp2
 		  4 = /dev/icd_bdm0	ICD BDM interface on lp0
 		  5 = /dev/icd_bdm1	ICD BDM interface on lp1
 		  6 = /dev/icd_bdm2	ICD BDM interface on lp2
@@ -1014,18 +1026,18 @@
 		      ...
 
 		The driver and documentation may be obtained from
-                http://www.proximity.com.au/~brian/winradio/
+		http://www.proximity.com.au/~brian/winradio/
 
  83 char	Teletext/videotext interfaces
-                  0 = /dev/vtx          Teletext decoder
-                 16 = /dev/vttuner      TV tuner on teletext interface
+		  0 = /dev/vtx		Teletext decoder
+		 16 = /dev/vttuner	TV tuner on teletext interface
 
-                Devices for the driver contained in the VideoteXt package.
-                More information on http://home.pages.de/~videotext/
+		Devices for the driver contained in the VideoteXt package.
+		More information on http://home.pages.de/~videotext/
 
  84 char	Ikon 1011[57] Versatec Greensheet Interface
-                  0 = /dev/ihcp0        First Greensheet port
-                  1 = /dev/ihcp1        Second Greensheet port
+		  0 = /dev/ihcp0	First Greensheet port
+		  1 = /dev/ihcp1	Second Greensheet port
 
  85 char	Linux/SGI shared memory input queue
 		  0 = /dev/shmiq	Master shared input queue
@@ -1060,7 +1072,33 @@
 		 30 = /dev/mtd15	16th MTD (rw)
 		 31 = /dev/mtdr15	16th MTD (ro)
 
- 91-119		UNALLOCATED
+ 91 char	CAN-Bus devices
+		  0 = /dev/can0		First CAN-Bus controller
+		  1 = /dev/can1		Second CAN-Bus controller
+		    ...
+
+ 92 char	Reserved for ith Kommunikationstechnik MIC ISDN card
+
+ 93 char	IBM Smart Capture Card frame grabber
+		  0 = /dev/iscc0	First Smart Capture Card
+		  1 = /dev/iscc1	Second Smart Capture Card
+		    ...
+		128 = /dev/isccctl0	First Smart Capture Card control
+		129 = /dev/isccctl1	Second Smart Capture Card control
+		    ...
+
+ 94 char	miroVIDEO DC10/30 capture/playback device
+		  0 = /dev/dcxx0	First capture card
+		  1 = /dev/dcxx1	Second capture card
+		    ...
+
+ 95 char	IP filter
+		  0 = /dev/ipl		Filter control device/log file
+		  1 = /dev/ipnat	NAT control device/log file
+		  2 = /dev/ipstate	State information log file
+		  3 = /dev/ipauth	Authentication control device/log file
+
+ 96-119		UNALLOCATED
 
 120-127		LOCAL/EXPERIMENTAL USE
 
diff -u --recursive --new-file v2.1.62/linux/MAINTAINERS linux/MAINTAINERS
--- v2.1.62/linux/MAINTAINERS	Sat Nov  1 11:04:27 1997
+++ linux/MAINTAINERS	Tue Nov  4 09:17:30 1997
@@ -204,6 +204,13 @@
 L:	linux-eata@i-connect.net, linux-scsi@vger.rutgers.edu
 S:	Maintained
 
+GDT SCSI DISK ARRAY CONTROLLER DRIVER
+P:	Achim Leubner
+M:	achim@vortex.de
+L:	linux-scsi@vger.rutgers.edu
+W:	http://www.icp-vortex.com/
+S:	Supported
+
 FILE LOCKING (flock() and fcntl()/lockf())
 P:	Andy Walker
 M:	andy@lysaker.kvaerner.no
diff -u --recursive --new-file v2.1.62/linux/Makefile linux/Makefile
--- v2.1.62/linux/Makefile	Mon Nov  3 13:04:25 1997
+++ linux/Makefile	Tue Nov  4 09:18:04 1997
@@ -1,6 +1,6 @@
 VERSION = 2
 PATCHLEVEL = 1
-SUBLEVEL = 62
+SUBLEVEL = 63
 
 ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/)
 
diff -u --recursive --new-file v2.1.62/linux/arch/alpha/kernel/time.c linux/arch/alpha/kernel/time.c
--- v2.1.62/linux/arch/alpha/kernel/time.c	Mon Nov  3 13:04:25 1997
+++ linux/arch/alpha/kernel/time.c	Fri Nov  7 15:24:01 1997
@@ -50,8 +50,6 @@
  */
 #define FIX_SHIFT	48
 
-static unsigned long round_ticks;
-
 /* lump static variables together for more efficient access: */
 static struct {
 	__u32		last_time;		/* cycle counter last time it got invoked */
@@ -81,7 +79,12 @@
 	now = rpcc();
 	delta = now - state.last_time;
 	state.last_time = now;
-	nticks = ((delta * state.scaled_ticks_per_cycle+round_ticks) >> FIX_SHIFT);
+	if(hwrpb->cycle_freq) {
+		nticks = (delta * state.scaled_ticks_per_cycle) >> (FIX_SHIFT-1);
+		nticks = (nticks+1) >> 1;
+    }
+	else nticks=1; /* No way to estimate lost ticks if we don't know
+					  the cycle frequency. */
 	for (i = 0; i < nticks; ++i) {
 		do_timer(regs);
 	}
@@ -147,14 +150,15 @@
 	/* read RTC exactly on falling edge of update flag */
 	/* Wait for rise.... (may take up to 1 second) */
 	
-	while(!(CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP));
+	do {} while(!(CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP));
 	
-    /* Wait for fall.... */
+/* Jay Estabook <jestabro@amt.tay1.dec.com>:
+ * Wait for the Update Done Interrupt bit (0x10) in reg C (12) to be set,
+ * which (hopefully) indicates that the update is really done.
+ */
 	
-	while(CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP);
+	do {} while(!CMOS_READ(RTC_REG_C) & RTC_UIP);
 	
-    __delay(1000000);
-	  
 	sec = CMOS_READ(RTC_SECONDS);
 	min = CMOS_READ(RTC_MINUTES);
 	hour = CMOS_READ(RTC_HOURS);
@@ -189,8 +193,8 @@
 		__you_loose();
 	}
 	state.last_time = rpcc();
+	if(hwrpb->cycle_freq)
 	state.scaled_ticks_per_cycle = ((unsigned long) HZ << FIX_SHIFT) / hwrpb->cycle_freq;
-	round_ticks=(unsigned long) 1 << (FIX_SHIFT-1);
 	state.last_rtc_update = 0;
 
 #ifdef CONFIG_RTC 
diff -u --recursive --new-file v2.1.62/linux/arch/i386/defconfig linux/arch/i386/defconfig
--- v2.1.62/linux/arch/i386/defconfig	Sat Nov  1 11:04:27 1997
+++ linux/arch/i386/defconfig	Wed Nov  5 13:21:12 1997
@@ -137,11 +137,15 @@
 # CONFIG_SCSI_EATA_PIO is not set
 # CONFIG_SCSI_EATA is not set
 # CONFIG_SCSI_FUTURE_DOMAIN is not set
+# CONFIG_SCSI_GDTH is not set
 # CONFIG_SCSI_GENERIC_NCR5380 is not set
 # CONFIG_SCSI_NCR53C406A is not set
 # CONFIG_SCSI_NCR53C7xx is not set
 # CONFIG_SCSI_NCR53C8XX is not set
 # CONFIG_SCSI_PAS16 is not set
+# CONFIG_SCSI_PCI2000 is not set
+# CONFIG_SCSI_PCI2220I is not set
+# CONFIG_SCSI_PSI240I is not set
 # CONFIG_SCSI_QLOGIC_FAS is not set
 # CONFIG_SCSI_QLOGIC_ISP is not set
 # CONFIG_SCSI_SEAGATE is not set
@@ -247,6 +251,7 @@
 # CONFIG_RTC is not set
 # CONFIG_NVRAM is not set
 # CONFIG_JOYSTICK is not set
+# CONFIG_MISC_RADIO is not set
 
 #
 # Sound
diff -u --recursive --new-file v2.1.62/linux/arch/i386/kernel/ioport.c linux/arch/i386/kernel/ioport.c
--- v2.1.62/linux/arch/i386/kernel/ioport.c	Tue Feb  4 06:44:24 1997
+++ linux/arch/i386/kernel/ioport.c	Wed Nov  5 08:57:51 1997
@@ -75,17 +75,15 @@
  * code.
  */
 
-asmlinkage int sys_iopl(long ebx,long ecx,long edx,
-	     long esi, long edi, long ebp, long eax, long ds,
-	     long es, long orig_eax, long eip, long cs,
-	     long eflags, long esp, long ss)
+asmlinkage int sys_iopl(unsigned long unused)
 {
-	unsigned int level = ebx;
+	struct pt_regs * regs = (struct pt_regs *) &unused;
+	unsigned int level = regs->ebx;
 
 	if (level > 3)
 		return -EINVAL;
 	if (!suser())
 		return -EPERM;
-	*(&eflags) = (eflags & 0xffffcfff) | (level << 12);
+	regs->eflags = (regs->eflags & 0xffffcfff) | (level << 12);
 	return 0;
 }
diff -u --recursive --new-file v2.1.62/linux/arch/i386/kernel/setup.c linux/arch/i386/kernel/setup.c
--- v2.1.62/linux/arch/i386/kernel/setup.c	Tue Sep 23 16:48:46 1997
+++ linux/arch/i386/kernel/setup.c	Wed Nov 12 11:09:56 1997
@@ -42,6 +42,7 @@
 char x86_mask = 0;		/* set by kernel/head.S */
 int x86_capability = 0;		/* set by kernel/head.S */
 int fdiv_bug = 0;		/* set if Pentium(TM) with FP bug */
+int pentium_f00f_bug = 0;	/* set if Pentium(TM) with F00F bug */
 int have_cpuid = 0;             /* set if CPUID instruction works */
 
 char x86_vendor_id[13] = "unknown";
@@ -359,6 +360,7 @@
                                        "fdiv_bug\t: %s\n"
                                        "hlt_bug\t\t: %s\n"
 				       "sep_bug\t\t: %s\n"
+				       "pentium_f00f_bug\t\t: %s\n"
                                        "fpu\t\t: %s\n"
                                        "fpu_exception\t: %s\n"
                                        "cpuid\t\t: %s\n"
@@ -367,6 +369,7 @@
                                        CD(fdiv_bug) ? "yes" : "no",
                                        CD(hlt_works_ok) ? "no" : "yes",
 				       sep_bug ? "yes" : "no",
+				       pentium_f00f_bug ? "yes" : "no",
                                        CD(hard_math) ? "yes" : "no",
                                        (CD(hard_math) && ignore_irq13)
                                          ? "yes" : "no",
diff -u --recursive --new-file v2.1.62/linux/arch/i386/kernel/traps.c linux/arch/i386/kernel/traps.c
--- v2.1.62/linux/arch/i386/kernel/traps.c	Sun Sep  7 13:10:42 1997
+++ linux/arch/i386/kernel/traps.c	Wed Nov 12 11:09:56 1997
@@ -413,6 +413,51 @@
 
 #endif /* CONFIG_MATH_EMULATION */
 
+static struct
+{
+	short limit __attribute__((packed));
+	void * addr __attribute__((packed));
+	short __pad __attribute__((packed));
+} idt_d;
+
+void * idt2;
+
+__initfunc(void trap_init_f00f_bug(void))
+{
+	pgd_t * pgd;
+	pmd_t * pmd;
+	pte_t * pte;
+	unsigned long twopage;
+
+	printk("moving IDT ... ");
+
+	twopage = (unsigned long) vmalloc (2*PAGE_SIZE);
+
+	idt2 = (void *)(twopage + 4096-7*8);
+
+	memcpy(idt2,&idt,sizeof(idt));
+
+	idt_d.limit = 256*8-1;
+	idt_d.addr = idt2;
+	idt_d.__pad = 0;
+
+        __asm__ __volatile__("\tlidt %0": "=m" (idt_d));
+
+	/*
+	 * Unmap lower page:
+	 */
+	pgd = pgd_offset(current->mm, twopage);
+	pmd = pmd_offset(pgd, twopage);
+	pte = pte_offset(pmd, twopage);
+
+	pte_clear(pte);
+	flush_tlb_all();
+
+	printk(" ... done\n");
+}
+
+
+
 __initfunc(void trap_init(void))
 {
 	int i;
diff -u --recursive --new-file v2.1.62/linux/arch/i386/mm/fault.c linux/arch/i386/mm/fault.c
--- v2.1.62/linux/arch/i386/mm/fault.c	Wed Oct 15 16:04:23 1997
+++ linux/arch/i386/mm/fault.c	Wed Nov 12 11:09:55 1997
@@ -74,6 +74,25 @@
 	return 0;
 }
 
+asmlinkage void divide_error(void);
+asmlinkage void debug(void);
+asmlinkage void nmi(void);
+asmlinkage void int3(void);
+asmlinkage void overflow(void);
+asmlinkage void bounds(void);
+asmlinkage void invalid_op(void);
+
+asmlinkage void do_divide_error (struct pt_regs *, unsigned long);
+asmlinkage void do_debug (struct pt_regs *, unsigned long);
+asmlinkage void do_nmi (struct pt_regs *, unsigned long);
+asmlinkage void do_int3 (struct pt_regs *, unsigned long);
+asmlinkage void do_overflow (struct pt_regs *, unsigned long);
+asmlinkage void do_bounds (struct pt_regs *, unsigned long);
+asmlinkage void do_invalid_op (struct pt_regs *, unsigned long);
+
+extern int * idt2;
+extern int pentium_f00f_bug;
+
 /*
  * This routine handles page faults.  It determines the address,
  * and the problem, and then passes it off to one of the appropriate
@@ -170,6 +189,46 @@
 		goto out;
 	}
 
+	printk("<%p/%p>\n", idt2, (void *)address);
+	/*
+	 * Pentium F0 0F C7 C8 bug workaround:
+	 */
+	if ( pentium_f00f_bug && (address >= (unsigned long)idt2) &&
+			(address < (unsigned long)idt2+256*8) ) {
+
+		void (*handler) (void);
+		int nr = (address-(unsigned long)idt2)/8;
+		unsigned long low, high;
+
+		low = idt[nr].a;
+		high = idt[nr].b;
+
+		handler = (void (*) (void)) ((low&0x0000ffff) | (high&0xffff0000));
+		printk("<handler %p... ", handler);
+		unlock_kernel();
+
+		if (handler==divide_error)
+			do_divide_error(regs,error_code);
+		else if (handler==debug)
+			do_debug(regs,error_code);
+		else if (handler==nmi)
+			do_nmi(regs,error_code);
+		else if (handler==int3)
+			do_int3(regs,error_code);
+		else if (handler==overflow)
+			do_overflow(regs,error_code);
+		else if (handler==bounds)
+			do_bounds(regs,error_code);
+		else if (handler==invalid_op)
+			do_invalid_op(regs,error_code);
+		else {
+			printk("INVALID HANDLER!\n");
+			for (;;) __cli();
+		}
+		printk("... done>\n");
+		goto out;
+	}
+
 	/* Are we prepared to handle this kernel fault?  */
 	if ((fixup = search_exception_table(regs->eip)) != 0) {
 		printk(KERN_DEBUG "%s: Exception at [<%lx>] cr2=%lx (fixup: %lx)\n",
@@ -193,6 +252,7 @@
 		flush_tlb();
 		goto out;
 	}
+
 	if (address < PAGE_SIZE)
 		printk(KERN_ALERT "Unable to handle kernel NULL pointer dereference");
 	else
diff -u --recursive --new-file v2.1.62/linux/arch/sparc/config.in linux/arch/sparc/config.in
--- v2.1.62/linux/arch/sparc/config.in	Sat Oct 25 02:44:14 1997
+++ linux/arch/sparc/config.in	Sat Nov  8 11:39:12 1997
@@ -68,7 +68,8 @@
 if [ "$CONFIG_BLK_DEV_MD" = "y" ]; then
   tristate '   Linear (append) mode' CONFIG_MD_LINEAR
   tristate '   RAID-0 (striping) mode' CONFIG_MD_STRIPED
-#  tristate '   RAID-1 (mirroring) mode' CONFIG_MD_MIRRORING
+  tristate '   RAID-1 (mirroring) mode' CONFIG_MD_MIRRORING
+  tristate '   RAID-4/RAID-5 mode' CONFIG_MD_RAID5
 fi
 
 tristate 'RAM disk support' CONFIG_BLK_DEV_RAM
diff -u --recursive --new-file v2.1.62/linux/drivers/block/Config.in linux/drivers/block/Config.in
--- v2.1.62/linux/drivers/block/Config.in	Wed Apr 23 19:01:17 1997
+++ linux/drivers/block/Config.in	Sat Nov  8 11:39:12 1997
@@ -53,6 +53,8 @@
 if [ "$CONFIG_BLK_DEV_MD" = "y" ]; then
   tristate '   Linear (append) mode' CONFIG_MD_LINEAR
   tristate '   RAID-0 (striping) mode' CONFIG_MD_STRIPED
+  tristate '   RAID-1 (mirroring) mode' CONFIG_MD_MIRRORING
+  tristate '   RAID-4/RAID-5 mode' CONFIG_MD_RAID5
 fi
 tristate 'RAM disk support' CONFIG_BLK_DEV_RAM
 if [ "$CONFIG_BLK_DEV_RAM" = "y" ]; then
diff -u --recursive --new-file v2.1.62/linux/drivers/block/Makefile linux/drivers/block/Makefile
--- v2.1.62/linux/drivers/block/Makefile	Mon Aug 18 18:19:45 1997
+++ linux/drivers/block/Makefile	Sat Nov  8 11:39:12 1997
@@ -200,25 +200,21 @@
   endif
 endif
 
-#ifeq ($(CONFIG_MD_RAID1),y)
-#L_OBJS += raid1.o
-#else
-#  ifeq ($(CONFIG_MD_SUPPORT_RAID1),y)
-#    ifeq ($(CONFIG_MD_RAID1),m)
-#    M_OBJS += raid1.o
-#    endif
-#  endif
-#endif
-#
-#ifeq ($(CONFIG_MD_RAID5),y)
-#L_OBJS += raid5.o
-#else
-#  ifeq ($(CONFIG_MD_SUPPORT_RAID5),y)
-#    ifeq ($(CONFIG_MD_RAID5),m)
-#    M_OBJS += raid5.o
-#    endif
-#  endif
-#endif
+ifeq ($(CONFIG_MD_MIRRORING),y)
+L_OBJS += raid1.o
+else
+  ifeq ($(CONFIG_MD_MIRRORING),m)
+  M_OBJS += raid1.o
+  endif
+endif
+
+ifeq ($(CONFIG_MD_RAID5),y)
+L_OBJS += raid5.o
+else
+  ifeq ($(CONFIG_MD_RAID5),m)
+  M_OBJS += raid5.o
+  endif
+endif
 
 endif
 
diff -u --recursive --new-file v2.1.62/linux/drivers/block/linear.c linux/drivers/block/linear.c
--- v2.1.62/linux/drivers/block/linear.c	Tue May 13 22:41:05 1997
+++ linux/drivers/block/linear.c	Sat Nov  8 11:39:12 1997
@@ -163,6 +163,7 @@
 
   sz+=sprintf (page+sz, "\n");
 #endif
+  sz+=sprintf (page+sz, " %dk rounding", 1<<FACTOR_SHIFT(FACTOR(mddev)));
   return sz;
 }
 
@@ -171,6 +172,8 @@
 {
   "linear",
   linear_map,
+  NULL,
+  NULL,
   linear_run,
   linear_stop,
   linear_status,
diff -u --recursive --new-file v2.1.62/linux/drivers/block/ll_rw_blk.c linux/drivers/block/ll_rw_blk.c
--- v2.1.62/linux/drivers/block/ll_rw_blk.c	Mon Aug 18 18:19:45 1997
+++ linux/drivers/block/ll_rw_blk.c	Sat Nov  8 11:39:12 1997
@@ -82,6 +82,11 @@
  */
 int * hardsect_size[MAX_BLKDEV] = { NULL, NULL, };
 
+/*
+ * The following tunes the read-ahead algorithm in mm/filemap.c
+ */
+int * max_readahead[MAX_BLKDEV] = { NULL, NULL, };
+
 static inline struct request **get_queue(kdev_t dev)
 {
 	int major = MAJOR(dev);
@@ -295,7 +300,27 @@
 	sti();
 }
 
-static void make_request(int major,int rw, struct buffer_head * bh)
+#define MAX_SECTORS 244
+
+static inline void attempt_merge (struct request *req)
+{
+	struct request *next = req->next;
+
+	if (!next)
+		return;
+	if (req->sector + req->nr_sectors != next->sector)
+		return;
+	if (next->sem || req->cmd != next->cmd || req->rq_dev != next->rq_dev || req->nr_sectors + next->nr_sectors >= MAX_SECTORS)
+		return;
+	req->bhtail->b_reqnext = next->bh;
+	req->bhtail = next->bhtail;
+	req->nr_sectors += next->nr_sectors;
+	next->rq_status = RQ_INACTIVE;
+	req->next = next->next;
+	wake_up (&wait_for_request);
+}
+
+void make_request(int major,int rw, struct buffer_head * bh)
 {
 	unsigned int sector, count;
 	struct request * req;
@@ -313,7 +338,7 @@
 
 	if (blk_size[major])
 		if (blk_size[major][MINOR(bh->b_rdev)] < (sector + count)>>1) {
-			bh->b_state &= (1 << BH_Lock) | (1 << BH_FreeOnIO);
+			bh->b_state &= (1 << BH_Lock);
                         /* This may well happen - the kernel calls bread()
                            without checking the size of the device, e.g.,
                            when mounting a device. */
@@ -323,8 +348,7 @@
                                kdevname(bh->b_rdev), rw,
                                (sector + count)>>1,
                                blk_size[major][MINOR(bh->b_rdev)]);
-			unlock_buffer(bh);
-			return;
+			goto end_io;
 		}
 
 	rw_ahead = 0;	/* normal case; gets changed below for READA/WRITEA */
@@ -333,10 +357,8 @@
 			rw_ahead = 1;
 			rw = READ;	/* drop into READ */
 		case READ:
-			if (buffer_uptodate(bh)) {
-				unlock_buffer(bh); /* Hmmph! Already have it */
-				return;
-			}
+			if (buffer_uptodate(bh)) /* Hmmph! Already have it */
+				goto end_io;
 			kstat.pgpgin++;
 			max_req = NR_REQUEST;	/* reads take precedence */
 			break;
@@ -344,10 +366,8 @@
 			rw_ahead = 1;
 			rw = WRITE;	/* drop into WRITE */
 		case WRITE:
-			if (!buffer_dirty(bh)) {
-				unlock_buffer(bh); /* Hmmph! Nothing to write */
-				return;
-			}
+			if (!buffer_dirty(bh))   /* Hmmph! Nothing to write */
+				goto end_io;
 			/* We don't allow the write-requests to fill up the
 			 * queue completely:  we want some room for reads,
 			 * as they take precedence. The last third of the
@@ -359,8 +379,7 @@
 		default:
 			printk(KERN_ERR "make_request: bad block dev cmd,"
                                " must be R/W/RA/WA\n");
-			unlock_buffer(bh);
-			return;
+			goto end_io;
 	}
 
 /* look for a free request. */
@@ -409,7 +428,7 @@
 				continue;
 			if (req->cmd != rw)
 				continue;
-			if (req->nr_sectors >= 244)
+			if (req->nr_sectors >= MAX_SECTORS)
 				continue;
 			if (req->rq_dev != bh->b_rdev)
 				continue;
@@ -417,6 +436,9 @@
 			if (req->sector + req->nr_sectors == sector) {
 				req->bhtail->b_reqnext = bh;
 				req->bhtail = bh;
+			    	req->nr_sectors += count;
+				/* Can we now merge this req with the next? */
+				attempt_merge(req);
 			/* or to the beginning? */
 			} else if (req->sector - count == sector) {
 			    	bh->b_reqnext = req->bh;
@@ -424,10 +446,10 @@
 			    	req->buffer = bh->b_data;
 			    	req->current_nr_sectors = count;
 			    	req->sector = sector;
+			    	req->nr_sectors += count;
 			} else
 				continue;
 
-		    	req->nr_sectors += count;
 			mark_buffer_clean(bh);
 		    	sti();
 		    	return;
@@ -440,10 +462,8 @@
 
 /* if no request available: if rw_ahead, forget it; otherwise try again blocking.. */
 	if (!req) {
-		if (rw_ahead) {
-			unlock_buffer(bh);
-			return;
-		}
+		if (rw_ahead)
+			goto end_io;
 		req = __get_request_wait(max_req, bh->b_rdev);
 	}
 
@@ -459,6 +479,10 @@
 	req->bhtail = bh;
 	req->next = NULL;
 	add_request(major+blk_dev,req);
+	return;
+
+end_io:
+	bh->b_end_io(bh, test_bit(BH_Uptodate, &bh->b_state));
 }
 
 /* This function can be used to request a number of buffers from a block
@@ -530,6 +554,12 @@
 	for (i = 0; i < nr; i++) {
 		if (bh[i]) {
 			set_bit(BH_Req, &bh[i]->b_state);
+#ifdef CONFIG_BLK_DEV_MD
+			if (MAJOR(bh[i]->b_dev) == MD_MAJOR) {
+				md_make_request(MINOR (bh[i]->b_dev), rw, bh[i]);
+				continue;
+			}
+#endif
 			make_request(MAJOR(bh[i]->b_rdev), rw, bh[i]);
 		}
 	}
@@ -654,6 +684,7 @@
 		req->next = NULL;
 	}
 	memset(ro_bits,0,sizeof(ro_bits));
+	memset(max_readahead, 0, sizeof(max_readahead));
 #ifdef CONFIG_AMIGA_Z2RAM
 	z2_init();
 #endif
diff -u --recursive --new-file v2.1.62/linux/drivers/block/md.c linux/drivers/block/md.c
--- v2.1.62/linux/drivers/block/md.c	Sat Oct 25 02:44:15 1997
+++ linux/drivers/block/md.c	Sat Nov  8 11:39:12 1997
@@ -9,6 +9,9 @@
 
    kerneld support by Boris Tobotras <boris@xtalk.msk.su>
 
+   RAID-1/RAID-5 extensions by:
+        Ingo Molnar, Miguel de Icaza, Gadi Oxman
+   
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2, or (at your option)
@@ -19,6 +22,13 @@
    Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  
 */
 
+/*
+ * Current RAID-1,4,5 parallel reconstruction speed limit is 1024 KB/sec, so
+ * the extra system load does not show up that much. Increase it if your
+ * system can take more.
+ */
+#define SPEED_LIMIT 1024
+
 #include <linux/config.h>
 #include <linux/module.h>
 #include <linux/version.h>
@@ -31,20 +41,31 @@
 #include <linux/proc_fs.h>
 #include <linux/blkdev.h>
 #include <linux/genhd.h>
+#include <linux/smp_lock.h>
 #ifdef CONFIG_KERNELD
 #include <linux/kerneld.h>
 #endif
 #include <linux/errno.h>
 #include <linux/init.h>
 
+#define __KERNEL_SYSCALLS__
+#include <linux/unistd.h>
+
 #define MAJOR_NR MD_MAJOR
 #define MD_DRIVER
 
 #include <linux/blk.h>
 #include <asm/uaccess.h>
+#include <asm/bitops.h>
+#include <asm/atomic.h>
 
 static struct hd_struct md_hd_struct[MAX_MD_DEV];
 static int md_blocksizes[MAX_MD_DEV];
+int md_maxreadahead[MAX_MD_DEV];
+static struct md_thread md_threads[MAX_MD_THREADS];
+#if SUPPORT_RECONSTRUCTION
+static struct md_thread *md_sync_thread = NULL;
+#endif /* SUPPORT_RECONSTRUCTION */
 
 int md_size[MAX_MD_DEV]={0, };
 
@@ -66,7 +87,6 @@
 };
 
 static struct md_personality *pers[MAX_PERSONALITY]={NULL, };
-
 struct md_dev md_dev[MAX_MD_DEV];
 
 static struct gendisk *find_gendisk (kdev_t dev)
@@ -84,7 +104,6 @@
   return (NULL);
 }
 
-
 char *partition_name (kdev_t dev)
 {
   static char name[40];		/* This should be long
@@ -93,49 +112,318 @@
 
   if (!hd)
   {
-    printk ("No gendisk entry for dev %s\n", kdevname(dev));
-    sprintf (name, "dev %s", kdevname(dev));
+    sprintf (name, "[dev %s]", kdevname(dev));
     return (name);
   }
 
   return disk_name (hd, MINOR(dev), name);  /* routine in genhd.c */
 }
 
+static int legacy_raid_sb (int minor, int pnum)
+{
+	int i, factor;
+
+	factor = 1 << FACTOR_SHIFT(FACTOR((md_dev+minor)));
+
+	/*****
+	 * do size and offset calculations.
+	 */
+	for (i=0; i<md_dev[minor].nb_dev; i++) {
+		md_dev[minor].devices[i].size &= ~(factor - 1);
+		md_size[minor] += md_dev[minor].devices[i].size;
+		md_dev[minor].devices[i].offset=i ? (md_dev[minor].devices[i-1].offset + 
+							md_dev[minor].devices[i-1].size) : 0;
+	}
+	if (pnum == RAID0 >> PERSONALITY_SHIFT)
+		md_maxreadahead[minor] = MD_DEFAULT_DISK_READAHEAD * md_dev[minor].nb_dev;
+	return 0;
+}
 
-static void set_ra (void)
+static void free_sb (struct md_dev *mddev)
 {
-  int i, j, minra=INT_MAX;
+	int i;
+	struct real_dev *realdev;
 
-  for (i=0; i<MAX_MD_DEV; i++)
-  {
-    if (!md_dev[i].pers)
-      continue;
-    
-    for (j=0; j<md_dev[i].nb_dev; j++)
-      if (read_ahead[MAJOR(md_dev[i].devices[j].dev)]<minra)
-	minra=read_ahead[MAJOR(md_dev[i].devices[j].dev)];
-  }
-  
-  read_ahead[MD_MAJOR]=minra;
+	if (mddev->sb) {
+		free_page((unsigned long) mddev->sb);
+		mddev->sb = NULL;
+	}
+	for (i = 0; i <mddev->nb_dev; i++) {
+		realdev = mddev->devices + i;
+		if (realdev->sb) {
+			free_page((unsigned long) realdev->sb);
+			realdev->sb = NULL;
+		}
+	}
 }
 
+/*
+ * Check one RAID superblock for generic plausibility
+ */
+
+#define BAD_MAGIC KERN_ERR \
+"md: %s: invalid raid superblock magic (%x) on block %u\n"
+
+#define OUT_OF_MEM KERN_ALERT \
+"md: out of memory.\n"
+
+#define NO_DEVICE KERN_ERR \
+"md: disabled device %s\n"
+
+#define SUCCESS 0
+#define FAILURE -1
+
+static int analyze_one_sb (struct real_dev * rdev)
+{
+	int ret = FAILURE;
+	struct buffer_head *bh;
+	kdev_t dev = rdev->dev;
+	md_superblock_t *sb;
+
+	/*
+	 * Read the superblock, it's at the end of the disk
+	 */
+	rdev->sb_offset = MD_NEW_SIZE_BLOCKS (blk_size[MAJOR(dev)][MINOR(dev)]);
+	set_blocksize (dev, MD_SB_BYTES);
+	bh = bread (dev, rdev->sb_offset / MD_SB_BLOCKS, MD_SB_BYTES);
+
+	if (bh) {
+		sb = (md_superblock_t *) bh->b_data;
+		if (sb->md_magic != MD_SB_MAGIC) {
+			printk (BAD_MAGIC, kdevname(dev),
+					 sb->md_magic, rdev->sb_offset);
+			goto abort;
+		}
+		rdev->sb = (md_superblock_t *) __get_free_page(GFP_KERNEL);
+		if (!rdev->sb) {
+			printk (OUT_OF_MEM);
+			goto abort;
+		}
+		memcpy (rdev->sb, bh->b_data, MD_SB_BYTES);
+
+		rdev->size = sb->size;
+	} else
+		printk (NO_DEVICE,kdevname(rdev->dev));
+	ret = SUCCESS;
+abort:
+	if (bh)
+		brelse (bh);
+	return ret;
+}
+
+#undef SUCCESS
+#undef FAILURE
+
+#undef BAD_MAGIC
+#undef OUT_OF_MEM
+#undef NO_DEVICE
+
+/*
+ * Check a full RAID array for plausibility
+ */
+
+#define INCONSISTENT KERN_ERR \
+"md: superblock inconsistency -- run ckraid\n"
+
+#define OUT_OF_DATE KERN_ERR \
+"md: superblock update time inconsistenty -- using the most recent one\n"
+
+#define OLD_VERSION KERN_ALERT \
+"md: %s: unsupported raid array version %d.%d.%d\n"
+
+#define NOT_CLEAN KERN_ERR \
+"md: %s: raid array is not clean -- run ckraid\n"
+
+#define NOT_CLEAN_IGNORE KERN_ERR \
+"md: %s: raid array is not clean -- reconstructing parity\n"
+
+#define UNKNOWN_LEVEL KERN_ERR \
+"md: %s: unsupported raid level %d\n"
+
+static int analyze_sbs (int minor, int pnum)
+{
+	struct md_dev *mddev = md_dev + minor;
+	int i, N = mddev->nb_dev, out_of_date = 0;
+	struct real_dev * disks = mddev->devices;
+	md_superblock_t *sb, *freshest = NULL;
+
+	/*
+	 * RAID-0 and linear don't use a RAID superblock
+	 */
+	if (pnum == RAID0 >> PERSONALITY_SHIFT ||
+		pnum == LINEAR >> PERSONALITY_SHIFT)
+			return legacy_raid_sb (minor, pnum);
+
+	/*
+	 * Verify the RAID superblock on each real device
+	 */
+	for (i = 0; i < N; i++)
+		if (analyze_one_sb(disks+i))
+			goto abort;
+
+	/*
+	 * The superblock constant part has to be the same
+	 * for all disks in the array.
+	 */
+	sb = NULL;
+	for (i = 0; i < N; i++) {
+		if (!disks[i].sb)
+			continue;
+		if (!sb) {
+			sb = disks[i].sb;
+			continue;
+		}
+		if (memcmp(sb,
+			   disks[i].sb, MD_SB_GENERIC_CONSTANT_WORDS * 4)) {
+			printk (INCONSISTENT);
+			goto abort;
+		}
+	}
+
+	/*
+	 * Ok, we have all disks and the array is ready to run. Lets
+	 * find the freshest superblock, that one will be the superblock
+	 * that represents the whole array.
+	 */
+	if ((sb = mddev->sb = (md_superblock_t *) __get_free_page (GFP_KERNEL)) == NULL)
+		goto abort;
+	freshest = NULL;
+	for (i = 0; i < N; i++) {
+		if (!disks[i].sb)
+			continue;
+		if (!freshest) {
+			freshest = disks[i].sb;
+			continue;
+		}
+		/*
+		 * Find the newest superblock version
+		 */
+		if (disks[i].sb->utime != freshest->utime) {
+			out_of_date = 1;
+			if (disks[i].sb->utime > freshest->utime)
+				freshest = disks[i].sb;
+		}
+	}
+	if (out_of_date)
+		printk(OUT_OF_DATE);
+	memcpy (sb, freshest, sizeof(*freshest));
+
+	/*
+	 * Check if we can support this RAID array
+	 */
+	if (sb->major_version != MD_MAJOR_VERSION ||
+			sb->minor_version > MD_MINOR_VERSION) {
+
+		printk (OLD_VERSION, kdevname(MKDEV(MD_MAJOR, minor)),
+				sb->major_version, sb->minor_version,
+				sb->patch_version);
+		goto abort;
+	}
+
+	/*
+	 * We need to add this as a superblock option.
+	 */
+#if SUPPORT_RECONSTRUCTION
+	if (sb->state != (1 << MD_SB_CLEAN)) {
+		if (sb->level == 1) {
+			printk (NOT_CLEAN, kdevname(MKDEV(MD_MAJOR, minor)));
+			goto abort;
+		} else
+			printk (NOT_CLEAN_IGNORE, kdevname(MKDEV(MD_MAJOR, minor)));
+	}
+#else
+	if (sb->state != (1 << MD_SB_CLEAN)) {
+		printk (NOT_CLEAN, kdevname(MKDEV(MD_MAJOR, minor)));
+		goto abort;
+	}
+#endif /* SUPPORT_RECONSTRUCTION */
+
+	switch (sb->level) {
+		case 1:
+			md_size[minor] = sb->size;
+			md_maxreadahead[minor] = MD_DEFAULT_DISK_READAHEAD;
+			break;
+		case 4:
+		case 5:
+			md_size[minor] = sb->size * (sb->raid_disks - 1);
+			md_maxreadahead[minor] = MD_DEFAULT_DISK_READAHEAD * (sb->raid_disks - 1);
+			break;
+		default:
+			printk (UNKNOWN_LEVEL, kdevname(MKDEV(MD_MAJOR, minor)),
+					sb->level);
+			goto abort;
+	}
+	return 0;
+abort:
+	free_sb(mddev);
+	return 1;
+}
+
+#undef INCONSISTENT
+#undef OUT_OF_DATE
+#undef OLD_VERSION
+#undef NOT_CLEAN
+#undef OLD_LEVEL
+
+int md_update_sb(int minor)
+{
+	struct md_dev *mddev = md_dev + minor;
+	struct buffer_head *bh;
+	md_superblock_t *sb = mddev->sb;
+	struct real_dev *realdev;
+	kdev_t dev;
+	int i;
+	u32 sb_offset;
+
+	sb->utime = CURRENT_TIME;
+	for (i = 0; i < mddev->nb_dev; i++) {
+		realdev = mddev->devices + i;
+		if (!realdev->sb)
+			continue;
+		dev = realdev->dev;
+		sb_offset = realdev->sb_offset;
+		set_blocksize(dev, MD_SB_BYTES);
+		printk("md: updating raid superblock on device %s, sb_offset == %u\n", kdevname(dev), sb_offset);
+		bh = getblk(dev, sb_offset / MD_SB_BLOCKS, MD_SB_BYTES);
+		if (bh) {
+			sb = (md_superblock_t *) bh->b_data;
+			memcpy(sb, mddev->sb, MD_SB_BYTES);
+			memcpy(&sb->descriptor, sb->disks + realdev->sb->descriptor.number, MD_SB_DESCRIPTOR_WORDS * 4);
+			mark_buffer_uptodate(bh, 1);
+			mark_buffer_dirty(bh, 1);
+			ll_rw_block(WRITE, 1, &bh);
+			wait_on_buffer(bh);
+			bforget(bh);
+			fsync_dev(dev);
+			invalidate_buffers(dev);
+		} else
+			printk(KERN_ERR "md: getblk failed for device %s\n", kdevname(dev));
+	}
+	return 0;
+}
 
 static int do_md_run (int minor, int repart)
 {
-  int pnum, i, min, current_ra, err;
-  
+  int pnum, i, min, factor, err;
+
   if (!md_dev[minor].nb_dev)
     return -EINVAL;
   
   if (md_dev[minor].pers)
     return -EBUSY;
-  
+
   md_dev[minor].repartition=repart;
   
-  if ((pnum=PERSONALITY(md_dev+minor) >> (PERSONALITY_SHIFT))
+  if ((pnum=PERSONALITY(&md_dev[minor]) >> (PERSONALITY_SHIFT))
       >= MAX_PERSONALITY)
     return -EINVAL;
-  
+
+  /* Only RAID-1 and RAID-5 can have MD devices as underlying devices */
+  if (pnum != (RAID1 >> PERSONALITY_SHIFT) && pnum != (RAID5 >> PERSONALITY_SHIFT)){
+	  for (i = 0; i < md_dev [minor].nb_dev; i++)
+		  if (MAJOR (md_dev [minor].devices [i].dev) == MD_MAJOR)
+			  return -EINVAL;
+  }
   if (!pers[pnum])
   {
 #ifdef CONFIG_KERNELD
@@ -147,7 +435,7 @@
       return -EINVAL;
   }
   
-  min=1 << FACTOR_SHIFT(FACTOR((md_dev+minor)));
+  factor = min = 1 << FACTOR_SHIFT(FACTOR((md_dev+minor)));
   
   for (i=0; i<md_dev[minor].nb_dev; i++)
     if (md_dev[minor].devices[i].size<min)
@@ -156,118 +444,167 @@
 	      partition_name (md_dev[minor].devices[i].dev), min);
       return -EINVAL;
     }
+
+  for (i=0; i<md_dev[minor].nb_dev; i++) {
+    fsync_dev(md_dev[minor].devices[i].dev);
+    invalidate_buffers(md_dev[minor].devices[i].dev);
+  }
   
   /* Resize devices according to the factor. It is used to align
      partitions size on a given chunk size. */
   md_size[minor]=0;
-  
-  for (i=0; i<md_dev[minor].nb_dev; i++)
-  {
-    md_dev[minor].devices[i].size &= ~(min - 1);
-    md_size[minor] += md_dev[minor].devices[i].size;
-    md_dev[minor].devices[i].offset=i ? (md_dev[minor].devices[i-1].offset + md_dev[minor].devices[i-1].size) : 0;
-  }
+
+  /*
+   * Analyze the raid superblock
+   */ 
+  if (analyze_sbs(minor, pnum))
+    return -EINVAL;
 
   md_dev[minor].pers=pers[pnum];
   
   if ((err=md_dev[minor].pers->run (minor, md_dev+minor)))
   {
     md_dev[minor].pers=NULL;
+    free_sb(md_dev + minor);
     return (err);
   }
-  
+
+  if (pnum != RAID0 >> PERSONALITY_SHIFT && pnum != LINEAR >> PERSONALITY_SHIFT)
+  {
+    md_dev[minor].sb->state &= ~(1 << MD_SB_CLEAN);
+    md_update_sb(minor);
+  }
+
   /* FIXME : We assume here we have blocks
      that are twice as large as sectors.
      THIS MAY NOT BE TRUE !!! */
   md_hd_struct[minor].start_sect=0;
   md_hd_struct[minor].nr_sects=md_size[minor]<<1;
   
-  /* It would be better to have a per-md-dev read_ahead. Currently,
-     we only use the smallest read_ahead among md-attached devices */
-  
-  current_ra=read_ahead[MD_MAJOR];
-  
-  for (i=0; i<md_dev[minor].nb_dev; i++)
-    if (current_ra>read_ahead[MAJOR(md_dev[minor].devices[i].dev)])
-      current_ra=read_ahead[MAJOR(md_dev[minor].devices[i].dev)];
-  
-  read_ahead[MD_MAJOR]=current_ra;
-  
-  printk ("START_DEV md%x %s\n", minor, md_dev[minor].pers->name);
+  read_ahead[MD_MAJOR] = 128;
   return (0);
 }
 
-
 static int do_md_stop (int minor, struct inode *inode)
 {
-  int i;
+	int i;
   
-  if (inode->i_count > 1 || md_dev[minor].busy>1) /* ioctl : one open channel */
-  {
-    printk ("STOP_MD md%x failed : i_count=%d, busy=%d\n", minor,
-	    inode->i_count, md_dev[minor].busy);
-    return -EBUSY;
-  }
-  
-  if (md_dev[minor].pers)
-  {
-    /*  The device won't exist anymore -> flush it now */
-    fsync_dev (inode->i_rdev);
-    invalidate_buffers (inode->i_rdev);
-    md_dev[minor].pers->stop (minor, md_dev+minor);
-  }
-  
-  /* Remove locks. */
-  for (i=0; i<md_dev[minor].nb_dev; i++)
-    clear_inode (md_dev[minor].devices[i].inode);
+	if (inode->i_count>1 || md_dev[minor].busy>1) {
+		/*
+		 * ioctl : one open channel
+		 */
+		printk ("STOP_MD md%x failed : i_count=%d, busy=%d\n",
+				minor, inode->i_count, md_dev[minor].busy);
+		return -EBUSY;
+	}
+  
+	if (md_dev[minor].pers) {
+		/*
+		 * It is safe to call stop here, it only frees private
+		 * data. Also, it tells us if a device is unstoppable
+		 * (eg. resyncing is in progress)
+		 */
+		if (md_dev[minor].pers->stop (minor, md_dev+minor))
+			return -EBUSY;
+		/*
+		 *  The device won't exist anymore -> flush it now
+		 */
+		fsync_dev (inode->i_rdev);
+		invalidate_buffers (inode->i_rdev);
+		if (md_dev[minor].sb) {
+			md_dev[minor].sb->state |= 1 << MD_SB_CLEAN;
+			md_update_sb(minor);
+		}
+	}
+  
+	/* Remove locks. */
+	if (md_dev[minor].sb)
+	free_sb(md_dev + minor);
+	for (i=0; i<md_dev[minor].nb_dev; i++)
+		clear_inode (md_dev[minor].devices[i].inode);
+
+	md_dev[minor].nb_dev=md_size[minor]=0;
+	md_hd_struct[minor].nr_sects=0;
+	md_dev[minor].pers=NULL;
   
-  md_dev[minor].nb_dev=md_size[minor]=0;
-  md_hd_struct[minor].nr_sects=0;
-  md_dev[minor].pers=NULL;
+	read_ahead[MD_MAJOR] = 128;
   
-  set_ra ();			/* calculate new read_ahead */
-  
-  printk ("STOP_DEV md%x\n", minor);
-  return (0);
+	return (0);
 }
 
-
 static int do_md_add (int minor, kdev_t dev)
 {
-  struct gendisk *gen_real;
-  int i;
-  
-  if (MAJOR(dev)==MD_MAJOR || md_dev[minor].nb_dev==MAX_REAL)
-    return -EINVAL;
-  
-  if (!fs_may_mount (dev) || md_dev[minor].pers)
-    return -EBUSY;
-  
-  if (!(gen_real=find_gendisk (dev)))
-    return -ENOENT;
-  
-  i=md_dev[minor].nb_dev++;
-  md_dev[minor].devices[i].dev=dev;
-  
-  /* Lock the device by inserting a dummy inode. This doesn't
-     smell very good, but I need to be consistent with the
-     mount stuff, specially with fs_may_mount. If someone have
-     a better idea, please help ! */
-  
-  md_dev[minor].devices[i].inode=get_empty_inode ();
-  md_dev[minor].devices[i].inode->i_dev=dev; /* don't care about
-						other fields */
-  insert_inode_hash (md_dev[minor].devices[i].inode);
-  
-  /* Sizes are now rounded at run time */
-  
-  md_dev[minor].devices[i].size=gen_real->sizes[MINOR(dev)];
+	int i;
+	int hot_add=0;
+	struct real_dev *realdev;
+
+	if (md_dev[minor].nb_dev==MAX_REAL)
+		return -EINVAL;
+
+	if (blk_size[MAJOR(dev)] == NULL || blk_size[MAJOR(dev)][MINOR(dev)] == 0) {
+		printk("md_add(): zero device size, huh, bailing out.\n");
+		return -EINVAL;
+	}
+
+	if (md_dev[minor].pers) {
+		/*
+		 * The array is already running, hot-add the drive, or
+		 * bail out:
+		 */
+		if (!md_dev[minor].pers->hot_add_disk)
+			return -EBUSY;
+		else
+			hot_add=1;
+	}
+
+	/*
+	 * Careful. We cannot increase nb_dev for a running array.
+	 */
+	i=md_dev[minor].nb_dev;
+	realdev = &md_dev[minor].devices[i];
+	realdev->dev=dev;
+  
+	/* Lock the device by inserting a dummy inode. This doesn't
+	   smell very good, but I need to be consistent with the
+	   mount stuff, specially with fs_may_mount. If someone have
+	   a better idea, please help ! */
+  
+	realdev->inode=get_empty_inode ();
+	realdev->inode->i_dev=dev; 	/* don't care about other fields */
+	insert_inode_hash (realdev->inode);
+  
+	/* Sizes are now rounded at run time */
+  
+/*  md_dev[minor].devices[i].size=gen_real->sizes[MINOR(dev)]; HACKHACK*/
+
+	realdev->size=blk_size[MAJOR(dev)][MINOR(dev)];
+
+	if (hot_add) {
+		/*
+		 * Check the superblock for consistency.
+		 * the personality itself has to check wether it's getting
+		 * added with the proper flags ... also, personality has to
+		 * be checked too ;)
+		 */
+		if (analyze_one_sb (realdev))
+			return -EINVAL;
+		/*
+		 * hot_add has to bump up nb_dev itself
+		 */
+		if (md_dev[minor].pers->hot_add_disk (&md_dev[minor], dev)) {
+			/*
+			 * FIXME: here we should free up the inode and stuff
+			 */
+			printk ("FIXME\n");
+			return -EINVAL;
+		}
+	} else
+		md_dev[minor].nb_dev++;
 
-  printk ("REGISTER_DEV %s to md%x done\n", partition_name(dev), minor);
-  return (0);
+	printk ("REGISTER_DEV %s to md%x done\n", partition_name(dev), minor);
+	return (0);
 }
 
-
 static int md_ioctl (struct inode *inode, struct file *file,
                      unsigned int cmd, unsigned long arg)
 {
@@ -354,7 +691,6 @@
   return (0);
 }
 
-
 static int md_open (struct inode *inode, struct file *file)
 {
   int minor=MINOR(inode->i_rdev);
@@ -427,6 +763,30 @@
   return (md_dev[minor].pers->map(md_dev+minor, rdev, rsector, size));
 }
   
+int md_make_request (int minor, int rw, struct buffer_head * bh)
+{
+	if (md_dev [minor].pers->make_request) {
+		if (buffer_locked(bh))
+			return 0;
+		set_bit(BH_Lock, &bh->b_state);
+		if (rw == WRITE || rw == WRITEA) {
+			if (!buffer_dirty(bh)) {
+				bh->b_end_io(bh, test_bit(BH_Uptodate, &bh->b_state));
+				return 0;
+			}
+		}
+		if (rw == READ || rw == READA) {
+			if (buffer_uptodate(bh)) {
+				bh->b_end_io(bh, test_bit(BH_Uptodate, &bh->b_state));
+				return 0;
+			}
+		}
+		return (md_dev[minor].pers->make_request(md_dev+minor, rw, bh));
+	} else {
+		make_request (MAJOR(bh->b_rdev), rw, bh);
+		return 0;
+	}
+}
 
 static void do_md_request (void)
 {
@@ -434,10 +794,51 @@
   return;
 }  
 
+/*
+ * We run MAX_MD_THREADS from md_init() and arbitrate them in run time.
+ * This is not so elegant, but how can we use kernel_thread() from within
+ * loadable modules?
+ */
+struct md_thread *md_register_thread (void (*run) (void *), void *data)
+{
+	int i;
+	for (i = 0; i < MAX_MD_THREADS; i++) {
+		if (md_threads[i].run == NULL) {
+			md_threads[i].run = run;
+			md_threads[i].data = data;
+			return md_threads + i;
+		}
+	}
+	return NULL;
+}
+
+void md_unregister_thread (struct md_thread *thread)
+{
+	thread->run = NULL;
+	thread->data = NULL;
+	thread->flags = 0;
+}
+
+void md_wakeup_thread(struct md_thread *thread)
+{
+	set_bit(THREAD_WAKEUP, &thread->flags);
+	wake_up(&thread->wqueue);
+}
+
+
 EXPORT_SYMBOL(md_size);
+EXPORT_SYMBOL(md_maxreadahead);
 EXPORT_SYMBOL(register_md_personality);
 EXPORT_SYMBOL(unregister_md_personality);
 EXPORT_SYMBOL(partition_name);
+EXPORT_SYMBOL(md_dev);
+EXPORT_SYMBOL(md_error);
+EXPORT_SYMBOL(md_register_thread);
+EXPORT_SYMBOL(md_unregister_thread);
+EXPORT_SYMBOL(md_update_sb);
+EXPORT_SYMBOL(md_map);
+EXPORT_SYMBOL(md_wakeup_thread);
+EXPORT_SYMBOL(md_do_sync);
 
 static struct proc_dir_entry proc_md = {
 	PROC_MD, 6, "mdstat",
@@ -451,16 +852,36 @@
   for(i=0;i<MAX_MD_DEV;i++)
   {
     md_blocksizes[i] = 1024;
+    md_maxreadahead[i] = MD_DEFAULT_DISK_READAHEAD;
     md_gendisk.part[i].start_sect=-1; /* avoid partition check */
     md_gendisk.part[i].nr_sects=0;
     md_dev[i].pers=NULL;
   }
 
-  blksize_size[MAJOR_NR] = md_blocksizes;
+  blksize_size[MD_MAJOR] = md_blocksizes;
+  max_readahead[MD_MAJOR] = md_maxreadahead;
 
   proc_register(&proc_root, &proc_md);
 }
 
+int md_error (kdev_t mddev, kdev_t rdev)
+{
+    unsigned int minor = MINOR (mddev);
+    int rc;
+
+    if (MAJOR(mddev) != MD_MAJOR || minor > MAX_MD_DEV)
+	panic ("md_error gets unknown device\n");
+    if (!md_dev [minor].pers)
+	panic ("md_error gets an error for an unknown device\n");
+    if (md_dev [minor].pers->error_handler) {
+	rc = md_dev [minor].pers->error_handler (md_dev+minor, rdev);
+#if SUPPORT_RECONSTRUCTION
+	md_wakeup_thread(md_sync_thread);
+#endif /* SUPPORT_RECONSTRUCTION */
+	return rc;
+    }
+    return 0;
+}
 
 int get_md_status (char *page)
 {
@@ -493,9 +914,13 @@
 		   partition_name(md_dev[i].devices[j].dev));
       size+=md_dev[i].devices[j].size;
     }
-    
-    if (md_dev[i].nb_dev)
-      sz+=sprintf (page+sz, " %d blocks", size);
+
+    if (md_dev[i].nb_dev) {
+      if (md_dev[i].pers)
+        sz+=sprintf (page+sz, " %d blocks", md_size[i]);
+      else
+        sz+=sprintf (page+sz, " %d blocks", size);
+    }
 
     if (!md_dev[i].pers)
     {
@@ -506,11 +931,8 @@
     if (md_dev[i].pers->max_invalid_dev)
       sz+=sprintf (page+sz, " maxfault=%ld", MAX_FAULT(md_dev+i));
 
-    sz+=sprintf (page+sz, " %dk %s\n", 1<<FACTOR_SHIFT(FACTOR(md_dev+i)),
-		 md_dev[i].pers == pers[LINEAR>>PERSONALITY_SHIFT] ?
-		 "rounding" : "chunks");
-
     sz+=md_dev[i].pers->status (page+sz, i, md_dev+i);
+    sz+=sprintf (page+sz, "\n");
   }
 
   return (sz);
@@ -543,6 +965,198 @@
   return 0;
 } 
 
+int md_thread(void * arg)
+{
+	struct md_thread *thread = arg;
+
+	current->session = 1;
+	current->pgrp = 1;
+	sprintf(current->comm, "md_thread");
+
+	lock_kernel();
+	for (;;) {
+		sti();
+		clear_bit(THREAD_WAKEUP, &thread->flags);
+		if (thread->run) {
+			thread->run(thread->data);
+			run_task_queue(&tq_disk);
+		}
+		cli();
+		if (!test_bit(THREAD_WAKEUP, &thread->flags)) {
+			do {
+				current->signal = 0;
+				interruptible_sleep_on(&thread->wqueue);
+			} while (current->signal);
+		}
+	}
+}
+
+static md_descriptor_t *get_spare(struct md_dev *mddev)
+{
+	int i;
+	md_superblock_t *sb = mddev->sb;
+	md_descriptor_t *descriptor;
+	struct real_dev *realdev;
+	
+  	for (i = 0; i < mddev->nb_dev; i++) {
+  		realdev = &mddev->devices[i];
+		if (!realdev->sb)
+			continue;
+		descriptor = &sb->disks[realdev->sb->descriptor.number];
+		if (descriptor->state & (1 << MD_FAULTY_DEVICE))
+			continue;
+		if (descriptor->state & (1 << MD_ACTIVE_DEVICE))
+			continue;
+		return descriptor;
+	}
+	return NULL;
+}
+
+/*
+ * parallel resyncing thread. 
+ *
+ * FIXME: - make it abort with a dirty array on mdstop, now it just blocks
+ *        - fix read error handing
+ */
+
+int md_do_sync(struct md_dev *mddev)
+{
+        struct buffer_head *bh;
+	int max_blocks, blocksize, curr_bsize, percent=1, j;
+	kdev_t read_disk = MKDEV(MD_MAJOR, mddev - md_dev);
+	int major = MAJOR(read_disk), minor = MINOR(read_disk);
+	unsigned long starttime;
+
+	blocksize = blksize_size[major][minor];
+	max_blocks = blk_size[major][minor] / (blocksize >> 10);
+
+	printk("... resync log\n");
+	printk(" ....   mddev->nb_dev: %d\n", mddev->nb_dev);
+	printk(" ....   raid array: %s\n", kdevname(read_disk));
+	printk(" ....   max_blocks: %d blocksize: %d\n", max_blocks, blocksize);
+	printk("md: syncing RAID array %s\n", kdevname(read_disk));
+
+	mddev->busy++;
+
+	starttime=jiffies;
+	for (j = 0; j < max_blocks; j++) {
+
+		/*
+		 * B careful. When some1 mounts a non-'blocksize' filesystem
+		 * then we get the blocksize changed right under us. Go deal
+		 * with it transparently, recalculate 'blocksize', 'j' and
+		 * 'max_blocks':
+		 */
+		curr_bsize = blksize_size[major][minor];
+		if (curr_bsize != blocksize) {
+diff_blocksize:
+			if (curr_bsize > blocksize)
+				/*
+				 * this is safe, rounds downwards.
+				 */
+				j /= curr_bsize/blocksize;
+			else
+				j *= blocksize/curr_bsize;
+
+			blocksize = curr_bsize;
+			max_blocks = blk_size[major][minor] / (blocksize >> 10);
+		}
+        	if ((bh = breada (read_disk, j, blocksize, j * blocksize,
+					max_blocks * blocksize)) != NULL) {
+			mark_buffer_dirty(bh, 1);
+			brelse(bh);
+		} else {
+			/*
+			 * FIXME: Ugly, but set_blocksize() isnt safe ...
+			 */
+			curr_bsize = blksize_size[major][minor];
+			if (curr_bsize != blocksize)
+				goto diff_blocksize;
+
+			/*
+			 * It's a real read problem. FIXME, handle this
+			 * a better way.
+			 */
+			printk ( KERN_ALERT
+				 "read error, stopping reconstruction.\n");
+			mddev->busy--;
+			return 1;
+		}
+
+		/*
+		 * Lets sleep some if we are faster than our speed limit:
+		 */
+		while (blocksize*j/(jiffies-starttime+1)*HZ/1024 > SPEED_LIMIT)
+		{
+			current->state = TASK_INTERRUPTIBLE;
+			current->timeout = jiffies+1;
+			schedule();
+		}
+
+		/*
+		 * FIXME: put this status bar thing into /proc
+		 */
+		if (!(j%(max_blocks/100))) {
+			if (!(percent%10))
+				printk (" %03d%% done.\n",percent);
+			else
+				printk (".");
+			percent++;
+		}
+	}
+	fsync_dev(read_disk);
+	printk("md: %s: sync done.\n", kdevname(read_disk));
+	mddev->busy--;
+	return 0;
+}
+
+/*
+ * This is a kernel thread which: syncs a spare disk with the active array
+ *
+ * the amount of foolproofing might seem to be a tad excessive, but an
+ * early (not so error-safe) version of raid1syncd synced the first 0.5 gigs
+ * of my root partition with the first 0.5 gigs of my /home partition ... so
+ * i'm a bit nervous ;)
+ */
+void mdsyncd (void *data)
+{
+	int i;
+	struct md_dev *mddev;
+	md_superblock_t *sb;
+	md_descriptor_t *spare;
+	unsigned long flags;
+
+	for (i = 0, mddev = md_dev; i < MAX_MD_DEV; i++, mddev++) {
+		if ((sb = mddev->sb) == NULL)
+			continue;
+		if (sb->active_disks == sb->raid_disks)
+			continue;
+		if (!sb->spare_disks)
+			continue;
+		if ((spare = get_spare(mddev)) == NULL)
+			continue;
+		if (!mddev->pers->mark_spare)
+			continue;
+		if (mddev->pers->mark_spare(mddev, spare, SPARE_WRITE))
+			continue;
+		if (md_do_sync(mddev) || (spare->state & (1 << MD_FAULTY_DEVICE))) {
+			mddev->pers->mark_spare(mddev, spare, SPARE_INACTIVE);
+			continue;
+		}
+		save_flags(flags);
+		cli();
+		mddev->pers->mark_spare(mddev, spare, SPARE_ACTIVE);
+		spare->state |= (1 << MD_SYNC_DEVICE);
+		spare->state |= (1 << MD_ACTIVE_DEVICE);
+		sb->spare_disks--;
+		sb->active_disks++;
+		mddev->sb_dirty = 1;
+		md_update_sb(mddev - md_dev);
+		restore_flags(flags);
+	}
+	
+}
+
 void linear_init (void);
 void raid0_init (void);
 void raid1_init (void);
@@ -550,7 +1164,11 @@
 
 __initfunc(int md_init (void))
 {
-  printk ("md driver %s MAX_MD_DEV=%d, MAX_REAL=%d\n", MD_VERSION, MAX_MD_DEV, MAX_REAL);
+  int i;
+
+  printk ("md driver %d.%d.%d MAX_MD_DEV=%d, MAX_REAL=%d\n",
+    MD_MAJOR_VERSION, MD_MINOR_VERSION, MD_PATCHLEVEL_VERSION,
+    MAX_MD_DEV, MAX_REAL);
 
   if (register_blkdev (MD_MAJOR, "md", &md_fops))
   {
@@ -558,18 +1176,39 @@
     return (-1);
   }
 
+  memset(md_threads, 0, MAX_MD_THREADS * sizeof(struct md_thread));
+  printk("md: starting %d kernel threads\n", MAX_MD_THREADS);
+  for (i = 0; i < MAX_MD_THREADS; i++) {
+    md_threads[i].run = NULL;
+    init_waitqueue(&md_threads[i].wqueue);
+    md_threads[i].flags = 0;
+    kernel_thread (md_thread, md_threads + i, 0);
+  }
+
   blk_dev[MD_MAJOR].request_fn=DEVICE_REQUEST;
   blk_dev[MD_MAJOR].current_request=NULL;
   read_ahead[MD_MAJOR]=INT_MAX;
+  memset(md_dev, 0, MAX_MD_DEV * sizeof (struct md_dev));
   md_gendisk.next=gendisk_head;
 
   gendisk_head=&md_gendisk;
 
+#if SUPPORT_RECONSTRUCTION
+  if ((md_sync_thread = md_register_thread(mdsyncd, NULL)) == NULL)
+    printk("md: bug: md_sync_thread == NULL\n");
+#endif /* SUPPORT_RECONSTRUCTION */
+
 #ifdef CONFIG_MD_LINEAR
   linear_init ();
 #endif
 #ifdef CONFIG_MD_STRIPED
   raid0_init ();
+#endif
+#ifdef CONFIG_MD_MIRRORING
+  raid1_init ();
+#endif
+#ifdef CONFIG_MD_RAID5
+  raid5_init ();
 #endif
   
   return (0);
diff -u --recursive --new-file v2.1.62/linux/drivers/block/raid0.c linux/drivers/block/raid0.c
--- v2.1.62/linux/drivers/block/raid0.c	Sun Sep  7 13:10:42 1997
+++ linux/drivers/block/raid0.c	Sat Nov  8 11:39:12 1997
@@ -250,6 +250,7 @@
 		 data->strip_zone[j].size);
   }
 #endif
+  sz+=sprintf (page+sz, " %dk chunks", 1<<FACTOR_SHIFT(FACTOR(mddev)));
   return sz;
 }
 
@@ -258,11 +259,17 @@
 {
   "raid0",
   raid0_map,
+  NULL,				/* no special make_request */
+  NULL,				/* no special end_request */
   raid0_run,
   raid0_stop,
   raid0_status,
   NULL,				/* no ioctls */
-  0
+  0,
+  NULL,				/* no error_handler */
+  NULL,				/* hot_add_disk */
+  NULL,				/* hot_remove_disk */
+  NULL				/* mark_spare */
 };
 
 
diff -u --recursive --new-file v2.1.62/linux/drivers/block/raid1.c linux/drivers/block/raid1.c
--- v2.1.62/linux/drivers/block/raid1.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/block/raid1.c	Sat Nov  8 11:39:12 1997
@@ -0,0 +1,870 @@
+/************************************************************************
+ * raid1.c : Multiple Devices driver for Linux
+ *           Copyright (C) 1996 Ingo Molnar, Miguel de Icaza, Gadi Oxman
+ *
+ * RAID-1 management functions.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * You should have received a copy of the GNU General Public License
+ * (for example /usr/src/linux/COPYING); if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/locks.h>
+#include <linux/malloc.h>
+#include <linux/md.h>
+#include <linux/raid1.h>
+#include <asm/bitops.h>
+#include <asm/atomic.h>
+
+#define MAJOR_NR MD_MAJOR
+#define MD_DRIVER
+#define MD_PERSONALITY
+
+/*
+ * The following can be used to debug the driver
+ */
+/*#define RAID1_DEBUG*/
+#ifdef RAID1_DEBUG
+#define PRINTK(x)   do { printk x; } while (0);
+#else
+#define PRINTK(x)   do { ; } while (0);
+#endif
+
+#define MAX(a,b)	((a) > (b) ? (a) : (b))
+#define MIN(a,b)	((a) < (b) ? (a) : (b))
+
+static struct md_personality raid1_personality;
+static struct md_thread *raid1_thread = NULL;
+struct buffer_head *raid1_retry_list = NULL;
+
+static int __raid1_map (struct md_dev *mddev, kdev_t *rdev,
+		        unsigned long *rsector, unsigned long size)
+{
+	struct raid1_data *raid_conf = (struct raid1_data *) mddev->private;
+	int i, n = raid_conf->raid_disks;
+
+	/*
+	 * Later we do read balancing on the read side 
+	 * now we use the first available disk.
+	 */
+
+	PRINTK(("raid1_map().\n"));
+
+	for (i=0; i<n; i++) {
+		if (raid_conf->mirrors[i].operational) {
+			*rdev = raid_conf->mirrors[i].dev;
+			return (0);
+		}
+	}
+
+	printk (KERN_ERR "raid1_map(): huh, no more operational devices?\n");
+	return (-1);
+}
+
+static int raid1_map (struct md_dev *mddev, kdev_t *rdev,
+		      unsigned long *rsector, unsigned long size)
+{
+	return 0;
+}
+
+void raid1_reschedule_retry (struct buffer_head *bh)
+{
+	struct raid1_bh * r1_bh = (struct raid1_bh *)(bh->b_dev_id);
+
+	PRINTK(("raid1_reschedule_retry().\n"));
+
+	r1_bh->next_retry = raid1_retry_list;
+	raid1_retry_list = bh;
+	md_wakeup_thread(raid1_thread);
+}
+
+/*
+ * raid1_end_buffer_io() is called when we have finished servicing a mirrored
+ * operation and are ready to return a success/failure code to the buffer
+ * cache layer.
+ */
+static inline void raid1_end_buffer_io(struct raid1_bh *r1_bh, int uptodate)
+{
+	struct buffer_head *bh = r1_bh->master_bh;
+
+	bh->b_end_io(bh, uptodate);
+	kfree(r1_bh);
+}
+
+int raid1_one_error=0;
+
+void raid1_end_request (struct buffer_head *bh, int uptodate)
+{
+	struct raid1_bh * r1_bh = (struct raid1_bh *)(bh->b_dev_id);
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+	PRINTK(("raid1_end_request().\n"));
+
+	if (raid1_one_error) {
+		raid1_one_error=0;
+		uptodate=0;
+	}
+	/*
+	 * this branch is our 'one mirror IO has finished' event handler:
+	 */
+	if (!uptodate)
+		md_error (bh->b_dev, bh->b_rdev);
+	else {
+		/*
+		 * Set BH_Uptodate in our master buffer_head, so that
+		 * we will return a good error code for to the higher
+		 * levels even if IO on some other mirrored buffer fails.
+		 *
+		 * The 'master' represents the complex operation to 
+		 * user-side. So if something waits for IO, then it will
+		 * wait for the 'master' buffer_head.
+		 */
+		set_bit (BH_Uptodate, &r1_bh->state);
+	}
+
+	/*
+	 * We split up the read and write side, imho they are 
+	 * conceptually different.
+	 */
+
+	if ( (r1_bh->cmd == READ) || (r1_bh->cmd == READA) ) {
+
+		PRINTK(("raid1_end_request(), read branch.\n"));
+
+		/*
+		 * we have only one buffer_head on the read side
+		 */
+		if (uptodate) {
+			PRINTK(("raid1_end_request(), read branch, uptodate.\n"));
+			raid1_end_buffer_io(r1_bh, uptodate);
+			restore_flags(flags);
+			return;
+		}
+		/*
+		 * oops, read error:
+		 */
+		printk(KERN_ERR "raid1: %s: rescheduling block %lu\n", 
+				 kdevname(bh->b_dev), bh->b_blocknr);
+		raid1_reschedule_retry (bh);
+		restore_flags(flags);
+		return;
+	}
+
+	/*
+	 * WRITE or WRITEA.
+	 */
+	PRINTK(("raid1_end_request(), write branch.\n"));
+
+	/*
+	 * lets see if all mirrored write operations have finished 
+	 * already [we have irqs off, so we can decrease]:
+	 */
+
+	if (!--r1_bh->remaining) {
+		struct md_dev *mddev = r1_bh->mddev;
+		struct raid1_data *raid_conf = (struct raid1_data *) mddev->private;
+		int i, n = raid_conf->raid_disks;
+
+		PRINTK(("raid1_end_request(), remaining == 0.\n"));
+
+		for ( i=0; i<n; i++)
+			if (r1_bh->mirror_bh[i]) kfree(r1_bh->mirror_bh[i]);
+
+		raid1_end_buffer_io(r1_bh, test_bit(BH_Uptodate, &r1_bh->state));
+	}
+	else PRINTK(("raid1_end_request(), remaining == %u.\n", r1_bh->remaining));
+	restore_flags(flags);
+}
+
+/* This routine checks if the undelying device is an md device and in that
+ * case it maps the blocks before putting the request on the queue
+ */
+static inline void
+map_and_make_request (int rw, struct buffer_head *bh)
+{
+	if (MAJOR (bh->b_rdev) == MD_MAJOR)
+		md_map (MINOR (bh->b_rdev), &bh->b_rdev, &bh->b_rsector, bh->b_size >> 9);
+	clear_bit(BH_Lock, &bh->b_state);
+	make_request (MAJOR (bh->b_rdev), rw, bh);
+}
+	
+static int
+raid1_make_request (struct md_dev *mddev, int rw, struct buffer_head * bh)
+{
+
+	struct raid1_data *raid_conf = (struct raid1_data *) mddev->private;
+	struct buffer_head *mirror_bh[MD_SB_DISKS], *bh_req;
+	struct raid1_bh * r1_bh;
+	int n = raid_conf->raid_disks, i, sum_bhs = 0, switch_disks = 0, sectors;
+	struct mirror_info *mirror;
+
+	PRINTK(("raid1_make_request().\n"));
+
+	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");
+	memset (r1_bh, 0, sizeof (struct raid1_bh));
+
+/*
+ * make_request() can abort the operation when READA or WRITEA are being
+ * used and no empty request is available.
+ *
+ * Currently, just replace the command with READ/WRITE.
+ */
+	if (rw == READA) rw = READ;
+	if (rw == WRITEA) rw = WRITE;
+
+	if (rw == WRITE || rw == WRITEA)
+		mark_buffer_clean(bh);		/* Too early ? */
+
+/*
+ * i think the read and write branch should be separated completely, since we want
+ * to do read balancing on the read side for example. Comments? :) --mingo
+ */
+
+	r1_bh->master_bh=bh;
+	r1_bh->mddev=mddev;
+	r1_bh->cmd = rw;
+
+	if (rw==READ || rw==READA) {
+		int last_used = raid_conf->last_used;
+		PRINTK(("raid1_make_request(), read branch.\n"));
+		mirror = raid_conf->mirrors + last_used;
+		bh->b_rdev = mirror->dev;
+		sectors = bh->b_size >> 9;
+		if (bh->b_blocknr * sectors == raid_conf->next_sect) {
+			raid_conf->sect_count += sectors;
+			if (raid_conf->sect_count >= mirror->sect_limit)
+				switch_disks = 1;
+		} else
+			switch_disks = 1;
+		raid_conf->next_sect = (bh->b_blocknr + 1) * sectors;
+		if (switch_disks) {
+			PRINTK(("read-balancing: switching %d -> %d (%d sectors)\n", last_used, mirror->next, raid_conf->sect_count));
+			raid_conf->sect_count = 0;
+			last_used = raid_conf->last_used = mirror->next;
+			/*
+			 * Do not switch to write-only disks ... resyncing
+			 * is in progress
+			 */
+			while (raid_conf->mirrors[last_used].write_only)
+				raid_conf->last_used = raid_conf->mirrors[last_used].next;
+		}
+		PRINTK (("raid1 read queue: %d %d\n", MAJOR (bh->b_rdev), MINOR (bh->b_rdev)));
+		bh_req = &r1_bh->bh_req;
+		memcpy(bh_req, bh, sizeof(*bh));
+		bh_req->b_end_io = raid1_end_request;
+		bh_req->b_dev_id = r1_bh;
+		map_and_make_request (rw, bh_req);
+		return 0;
+	}
+
+	/*
+	 * WRITE or WRITEA.
+	 */
+	PRINTK(("raid1_make_request(n=%d), write branch.\n",n));
+
+	for (i = 0; i < n; i++) {
+
+		if (!raid_conf->mirrors [i].operational) {
+			/*
+			 * the r1_bh->mirror_bh[i] pointer remains NULL
+			 */
+			mirror_bh[i] = NULL;
+			continue;
+		}
+
+	/*
+	 * We should use a private pool (size depending on NR_REQUEST),
+	 * to avoid writes filling up the memory with bhs
+	 *
+	 * Such pools are much faster than kmalloc anyways (so we waste almost 
+	 * nothing by not using the master bh when writing and win alot of cleanness)
+	 *
+	 * but for now we are cool enough. --mingo
+	 *
+	 * It's safe to sleep here, buffer heads cannot be used in a shared
+	 * manner in the write branch. Look how we lock the buffer at the beginning
+	 * of this function to grok the difference ;)
+	 */
+		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");
+		memset (mirror_bh[i], 0, sizeof (struct buffer_head));
+
+	/*
+	 * prepare mirrored bh (fields ordered for max mem throughput):
+	 */
+		mirror_bh [i]->b_blocknr    = bh->b_blocknr;
+		mirror_bh [i]->b_dev        = bh->b_dev;
+		mirror_bh [i]->b_rdev 	    = raid_conf->mirrors [i].dev;
+		mirror_bh [i]->b_rsector    = bh->b_rsector;
+		mirror_bh [i]->b_state      =   (1<<BH_Req) | 
+						(1<<BH_Touched) | (1<<BH_Dirty);
+		mirror_bh [i]->b_count      = 1;
+		mirror_bh [i]->b_size       = bh->b_size;
+		mirror_bh [i]->b_data       = bh->b_data;
+		mirror_bh [i]->b_list       = BUF_LOCKED;
+		mirror_bh [i]->b_end_io     = raid1_end_request;
+		mirror_bh [i]->b_dev_id     = r1_bh;
+
+		r1_bh->mirror_bh[i] = mirror_bh[i];
+		sum_bhs++;
+	}
+
+	r1_bh->remaining = sum_bhs;
+
+	PRINTK(("raid1_make_request(), write branch, sum_bhs=%d.\n",sum_bhs));
+
+	/*
+	 * We have to be a bit careful about the semaphore above, thats why we
+	 * start the requests separately. Since kmalloc() could fail, sleep and
+	 * make_request() can sleep too, this is the safer solution. Imagine,
+	 * end_request decreasing the semaphore before we could have set it up ...
+	 * We could play tricks with the semaphore (presetting it and correcting
+	 * at the end if sum_bhs is not 'n' but we have to do end_request by hand
+	 * if all requests finish until we had a chance to set up the semaphore
+	 * correctly ... lots of races).
+	 */
+	for (i = 0; i < n; i++)
+		if (mirror_bh [i] != NULL)
+			map_and_make_request (rw, mirror_bh [i]);
+
+	return (0);
+}
+			   
+static int raid1_status (char *page, int minor, struct md_dev *mddev)
+{
+	struct raid1_data *raid_conf = (struct raid1_data *) mddev->private;
+	int sz = 0, i;
+	
+	sz += sprintf (page+sz, " [%d/%d] [", raid_conf->raid_disks, raid_conf->working_disks);
+	for (i = 0; i < raid_conf->raid_disks; i++)
+		sz += sprintf (page+sz, "%s", raid_conf->mirrors [i].operational ? "U" : "_");
+	sz += sprintf (page+sz, "]");
+	return sz;
+}
+
+static void raid1_fix_links (struct raid1_data *raid_conf, int failed_index)
+{
+	int disks = raid_conf->raid_disks;
+	int j;
+
+	for (j = 0; j < disks; j++)
+		if (raid_conf->mirrors [j].next == failed_index)
+			raid_conf->mirrors [j].next = raid_conf->mirrors [failed_index].next;
+}
+
+#define LAST_DISK KERN_ALERT \
+"raid1: only one disk left and IO error.\n"
+
+#define NO_SPARE_DISK KERN_ALERT \
+"raid1: no spare disk left, degrading mirror level by one.\n"
+
+#define DISK_FAILED KERN_ALERT \
+"raid1: Disk failure on %s, disabling device. \n" \
+"       Operation continuing on %d devices\n"
+
+#define START_SYNCING KERN_ALERT \
+"raid1: start syncing spare disk.\n"
+
+#define ALREADY_SYNCING KERN_INFO \
+"raid1: syncing already in progress.\n"
+
+static int raid1_error (struct md_dev *mddev, kdev_t dev)
+{
+	struct raid1_data *raid_conf = (struct raid1_data *) mddev->private;
+	struct mirror_info *mirror;
+	md_superblock_t *sb = mddev->sb;
+	int disks = raid_conf->raid_disks;
+	int i;
+
+	PRINTK(("raid1_error called\n"));
+
+	if (raid_conf->working_disks == 1) {
+		/*
+		 * Uh oh, we can do nothing if this is our last disk, but
+		 * first check if this is a queued request for a device
+		 * which has just failed.
+		 */
+		for (i = 0, mirror = raid_conf->mirrors; i < disks;
+				 i++, mirror++)
+			if (mirror->dev == dev && !mirror->operational)
+				return 0;
+		printk (LAST_DISK);
+	} else {
+		/* Mark disk as unusable */
+		for (i = 0, mirror = raid_conf->mirrors; i < disks;
+				 i++, mirror++) {
+			if (mirror->dev == dev && mirror->operational){
+				mirror->operational = 0;
+				raid1_fix_links (raid_conf, i);
+				sb->disks[mirror->number].state |=
+						(1 << MD_FAULTY_DEVICE);
+				sb->disks[mirror->number].state &=
+						~(1 << MD_SYNC_DEVICE);
+				sb->disks[mirror->number].state &=
+						~(1 << MD_ACTIVE_DEVICE);
+				sb->active_disks--;
+				sb->working_disks--;
+				sb->failed_disks++;
+				mddev->sb_dirty = 1;
+				md_wakeup_thread(raid1_thread);
+				raid_conf->working_disks--;
+				printk (DISK_FAILED, kdevname (dev),
+						raid_conf->working_disks);
+			}
+		}
+	}
+	return 0;
+}
+
+#undef LAST_DISK
+#undef NO_SPARE_DISK
+#undef DISK_FAILED
+#undef START_SYNCING
+
+/*
+ * This is the personality-specific hot-addition routine
+ */
+
+#define NO_SUPERBLOCK KERN_ERR \
+"raid1: cannot hot-add disk to the array with no RAID superblock\n"
+
+#define WRONG_LEVEL KERN_ERR \
+"raid1: hot-add: level of disk is not RAID-1\n"
+
+#define HOT_ADD_SUCCEEDED KERN_INFO \
+"raid1: device %s hot-added\n"
+
+static int raid1_hot_add_disk (struct md_dev *mddev, kdev_t dev)
+{
+	unsigned long flags;
+	struct raid1_data *raid_conf = (struct raid1_data *) mddev->private;
+	struct mirror_info *mirror;
+	md_superblock_t *sb = mddev->sb;
+	struct real_dev * realdev;
+	int n;
+
+	/*
+	 * The device has it's superblock already read and it was found
+	 * to be consistent for generic RAID usage, now we check wether
+	 * it's usable for RAID-1 hot addition.
+	 */
+
+	n = mddev->nb_dev++;
+	realdev = &mddev->devices[n];
+	if (!realdev->sb) {
+		printk (NO_SUPERBLOCK);
+		return -EINVAL;
+	}
+	if (realdev->sb->level != 1) {
+		printk (WRONG_LEVEL);
+		return -EINVAL;
+	}
+	/* FIXME: are there other things left we could sanity-check? */
+
+	/*
+	 * We have to disable interrupts, as our RAID-1 state is used
+	 * from irq handlers as well.
+	 */
+	save_flags(flags);
+	cli();
+
+	raid_conf->raid_disks++;
+	mirror = raid_conf->mirrors+n;
+
+	mirror->number=n;
+	mirror->raid_disk=n;
+	mirror->dev=dev;
+	mirror->next=0; /* FIXME */
+	mirror->sect_limit=128;
+
+	mirror->operational=0;
+	mirror->spare=1;
+	mirror->write_only=0;
+
+	sb->disks[n].state |= (1 << MD_FAULTY_DEVICE);
+	sb->disks[n].state &= ~(1 << MD_SYNC_DEVICE);
+	sb->disks[n].state &= ~(1 << MD_ACTIVE_DEVICE);
+	sb->nr_disks++;
+	sb->spare_disks++;
+
+	restore_flags(flags);
+
+	md_update_sb(MINOR(dev));
+
+	printk (HOT_ADD_SUCCEEDED, kdevname(realdev->dev));
+
+	return 0;
+}
+
+#undef NO_SUPERBLOCK
+#undef WRONG_LEVEL
+#undef HOT_ADD_SUCCEEDED
+
+/*
+ * Insert the spare disk into the drive-ring
+ */
+static void add_ring(struct raid1_data *raid_conf, struct mirror_info *mirror)
+{
+	int j, next;
+	struct mirror_info *p = raid_conf->mirrors;
+
+	for (j = 0; j < raid_conf->raid_disks; j++, p++)
+		if (p->operational && !p->write_only) {
+			next = p->next;
+			p->next = mirror->raid_disk;
+			mirror->next = next;
+			return;
+		}
+	printk("raid1: bug: no read-operational devices\n");
+}
+
+static int raid1_mark_spare(struct md_dev *mddev, md_descriptor_t *spare,
+				int state)
+{
+	int i = 0, failed_disk = -1;
+	struct raid1_data *raid_conf = mddev->private;
+	struct mirror_info *mirror = raid_conf->mirrors;
+	md_descriptor_t *descriptor;
+	unsigned long flags;
+
+	for (i = 0; i < MD_SB_DISKS; i++, mirror++) {
+		if (mirror->spare && mirror->number == spare->number)
+			goto found;
+	}
+	return 1;
+found:
+	for (i = 0, mirror = raid_conf->mirrors; i < raid_conf->raid_disks;
+								i++, mirror++)
+		if (!mirror->operational)
+			failed_disk = i;
+
+	save_flags(flags);
+	cli();
+	switch (state) {
+		case SPARE_WRITE:
+			mirror->operational = 1;
+			mirror->write_only = 1;
+			raid_conf->raid_disks = MAX(raid_conf->raid_disks,
+							mirror->raid_disk + 1);
+			break;
+		case SPARE_INACTIVE:
+			mirror->operational = 0;
+			mirror->write_only = 0;
+			break;
+		case SPARE_ACTIVE:
+			mirror->spare = 0;
+			mirror->write_only = 0;
+			raid_conf->working_disks++;
+			add_ring(raid_conf, mirror);
+
+			if (failed_disk != -1) {
+				descriptor = &mddev->sb->disks[raid_conf->mirrors[failed_disk].number];
+				i = spare->raid_disk;
+				spare->raid_disk = descriptor->raid_disk;
+				descriptor->raid_disk = i;
+			}
+			break;
+		default:
+			printk("raid1_mark_spare: bug: state == %d\n", state);
+			restore_flags(flags);
+			return 1;
+	}
+	restore_flags(flags);
+	return 0;
+}
+
+/*
+ * This is a kernel thread which:
+ *
+ *	1.	Retries failed read operations on working mirrors.
+ *	2.	Updates the raid superblock when problems encounter.
+ */
+void raid1d (void *data)
+{
+	struct buffer_head *bh;
+	kdev_t dev;
+	unsigned long flags;
+	struct raid1_bh * r1_bh;
+	struct md_dev *mddev;
+
+	PRINTK(("raid1d() active\n"));
+	save_flags(flags);
+	cli();
+	while (raid1_retry_list) {
+		bh = raid1_retry_list;
+		r1_bh = (struct raid1_bh *)(bh->b_dev_id);
+		raid1_retry_list = r1_bh->next_retry;
+		restore_flags(flags);
+
+		mddev = md_dev + MINOR(bh->b_dev);
+		if (mddev->sb_dirty) {
+			printk("dirty sb detected, updating.\n");
+			mddev->sb_dirty = 0;
+			md_update_sb(MINOR(bh->b_dev));
+		}
+		dev = bh->b_rdev;
+		__raid1_map (md_dev + MINOR(bh->b_dev), &bh->b_rdev, &bh->b_rsector, bh->b_size >> 9);
+		if (bh->b_rdev == dev) {
+			printk (KERN_ALERT 
+					"raid1: %s: unrecoverable I/O read error for block %lu\n",
+						kdevname(bh->b_dev), bh->b_blocknr);
+			raid1_end_buffer_io(r1_bh, 0);
+		} else {
+			printk (KERN_ERR "raid1: %s: redirecting sector %lu to another mirror\n", 
+					  kdevname(bh->b_dev), bh->b_blocknr);
+			map_and_make_request (r1_bh->cmd, bh);
+		}
+		cli();
+	}
+	restore_flags(flags);
+}
+
+/*
+ * This will catch the scenario in which one of the mirrors was
+ * mounted as a normal device rather than as a part of a raid set.
+ */
+static int __check_consistency (struct md_dev *mddev, int row)
+{
+	struct raid1_data *raid_conf = mddev->private;
+	kdev_t dev;
+	struct buffer_head *bh = NULL;
+	int i, rc = 0;
+	char *buffer = NULL;
+
+	for (i = 0; i < raid_conf->raid_disks; i++) {
+		if (!raid_conf->mirrors[i].operational)
+			continue;
+		dev = raid_conf->mirrors[i].dev;
+		set_blocksize(dev, 4096);
+		if ((bh = bread(dev, row / 4, 4096)) == NULL)
+			break;
+		if (!buffer) {
+			buffer = (char *) __get_free_page(GFP_KERNEL);
+			if (!buffer)
+				break;
+			memcpy(buffer, bh->b_data, 4096);
+		} else if (memcmp(buffer, bh->b_data, 4096)) {
+			rc = 1;
+			break;
+		}
+		bforget(bh);
+		fsync_dev(dev);
+		invalidate_buffers(dev);
+		bh = NULL;
+	}
+	if (buffer)
+		free_page((unsigned long) buffer);
+	if (bh) {
+		dev = bh->b_dev;
+		bforget(bh);
+		fsync_dev(dev);
+		invalidate_buffers(dev);
+	}
+	return rc;
+}
+
+static int check_consistency (struct md_dev *mddev)
+{
+	int size = mddev->sb->size;
+	int row;
+
+	for (row = 0; row < size; row += size / 8)
+		if (__check_consistency(mddev, row))
+			return 1;
+	return 0;
+}
+
+static int raid1_run (int minor, struct md_dev *mddev)
+{
+	struct raid1_data *raid_conf;
+	int i, j, raid_disk;
+	md_superblock_t *sb = mddev->sb;
+	md_descriptor_t *descriptor;
+	struct real_dev *realdev;
+
+	MOD_INC_USE_COUNT;
+
+	if (sb->level != 1) {
+		printk("raid1: %s: raid level not set to mirroring (%d)\n",
+				kdevname(MKDEV(MD_MAJOR, minor)), sb->level);
+		MOD_DEC_USE_COUNT;
+		return -EIO;
+	}
+	/****
+	 * copy the now verified devices into our private RAID1 bookkeeping
+	 * area. [whatever we allocate in raid1_run(), should be freed in
+	 * raid1_stop()]
+	 */
+
+	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");
+	raid_conf = mddev->private;
+	memset(raid_conf, 0, sizeof(*raid_conf));
+
+	PRINTK(("raid1_run(%d) called.\n", minor));
+
+  	for (i = 0; i < mddev->nb_dev; i++) {
+  		realdev = &mddev->devices[i];
+		if (!realdev->sb) {
+			printk(KERN_ERR "raid1: disabled mirror %s (couldn't access raid superblock)\n", kdevname(realdev->dev));
+			continue;
+		}
+
+		/*
+		 * This is important -- we are using the descriptor on
+		 * the disk only to get a pointer to the descriptor on
+		 * the main superblock, which might be more recent.
+		 */
+		descriptor = &sb->disks[realdev->sb->descriptor.number];
+		if (descriptor->state & (1 << MD_FAULTY_DEVICE)) {
+			printk(KERN_ERR "raid1: disabled mirror %s (errors detected)\n", kdevname(realdev->dev));
+			continue;
+		}
+		if (descriptor->state & (1 << MD_ACTIVE_DEVICE)) {
+			if (!(descriptor->state & (1 << MD_SYNC_DEVICE))) {
+				printk(KERN_ERR "raid1: disabled mirror %s (not in sync)\n", kdevname(realdev->dev));
+				continue;
+			}
+			raid_disk = descriptor->raid_disk;
+			if (descriptor->number > sb->nr_disks || raid_disk > sb->raid_disks) {
+				printk(KERN_ERR "raid1: disabled mirror %s (inconsistent descriptor)\n", kdevname(realdev->dev));
+				continue;
+			}
+			if (raid_conf->mirrors[raid_disk].operational) {
+				printk(KERN_ERR "raid1: disabled mirror %s (mirror %d already operational)\n", kdevname(realdev->dev), raid_disk);
+				continue;
+			}
+			printk(KERN_INFO "raid1: device %s operational as mirror %d\n", kdevname(realdev->dev), raid_disk);
+			raid_conf->mirrors[raid_disk].number = descriptor->number;
+			raid_conf->mirrors[raid_disk].raid_disk = raid_disk;
+			raid_conf->mirrors[raid_disk].dev = mddev->devices [i].dev;
+			raid_conf->mirrors[raid_disk].operational = 1;
+			raid_conf->mirrors[raid_disk].sect_limit = 128;
+			raid_conf->working_disks++;
+		} else {
+		/*
+		 * Must be a spare disk ..
+		 */
+			printk(KERN_INFO "raid1: spare disk %s\n", kdevname(realdev->dev));
+			raid_disk = descriptor->raid_disk;
+			raid_conf->mirrors[raid_disk].number = descriptor->number;
+			raid_conf->mirrors[raid_disk].raid_disk = raid_disk;
+			raid_conf->mirrors[raid_disk].dev = mddev->devices [i].dev;
+			raid_conf->mirrors[raid_disk].sect_limit = 128;
+
+			raid_conf->mirrors[raid_disk].operational = 0;
+			raid_conf->mirrors[raid_disk].write_only = 0;
+			raid_conf->mirrors[raid_disk].spare = 1;
+		}
+	}
+	if (!raid_conf->working_disks) {
+		printk(KERN_ERR "raid1: no operational mirrors for %s\n", kdevname(MKDEV(MD_MAJOR, minor)));
+		kfree(raid_conf);
+		mddev->private = NULL;
+		MOD_DEC_USE_COUNT;
+		return -EIO;
+	}
+
+	raid_conf->raid_disks = sb->raid_disks;
+	raid_conf->mddev = mddev;
+
+	for (j = 0; !raid_conf->mirrors[j].operational; j++);
+	raid_conf->last_used = j;
+	for (i = raid_conf->raid_disks - 1; i >= 0; i--) {
+		if (raid_conf->mirrors[i].operational) {
+			PRINTK(("raid_conf->mirrors[%d].next == %d\n", i, j));
+			raid_conf->mirrors[i].next = j;
+			j = i;
+		}
+	}
+
+	if (check_consistency(mddev)) {
+		printk(KERN_ERR "raid1: detected mirror differences -- run ckraid\n");
+		sb->state |= 1 << MD_SB_ERRORS;
+		kfree(raid_conf);
+		mddev->private = NULL;
+		MOD_DEC_USE_COUNT;
+		return -EIO;
+	}
+
+	/*
+	 * Regenerate the "device is in sync with the raid set" bit for
+	 * each device.
+	 */
+	for (i = 0; i < sb->nr_disks ; i++) {
+		sb->disks[i].state &= ~(1 << MD_SYNC_DEVICE);
+		for (j = 0; j < sb->raid_disks; j++) {
+			if (!raid_conf->mirrors[j].operational)
+				continue;
+			if (sb->disks[i].number == raid_conf->mirrors[j].number)
+				sb->disks[i].state |= 1 << MD_SYNC_DEVICE;
+		}
+	}
+	sb->active_disks = raid_conf->working_disks;
+
+	printk("raid1: raid set %s active with %d out of %d mirrors\n", kdevname(MKDEV(MD_MAJOR, minor)), sb->active_disks, sb->raid_disks);
+	/* Ok, everything is just fine now */
+	return (0);
+}
+
+static int raid1_stop (int minor, struct md_dev *mddev)
+{
+	struct raid1_data *raid_conf = (struct raid1_data *) mddev->private;
+
+	kfree (raid_conf);
+	mddev->private = NULL;
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+static struct md_personality raid1_personality=
+{
+	"raid1",
+	raid1_map,
+	raid1_make_request,
+	raid1_end_request,
+	raid1_run,
+	raid1_stop,
+	raid1_status,
+	NULL,			/* no ioctls */
+	0,
+	raid1_error,
+	raid1_hot_add_disk,
+	/* raid1_hot_remove_drive */ NULL,
+	raid1_mark_spare
+};
+
+int raid1_init (void)
+{
+	if ((raid1_thread = md_register_thread(raid1d, NULL)) == NULL)
+		return -EBUSY;
+	return register_md_personality (RAID1, &raid1_personality);
+}
+
+#ifdef MODULE
+int init_module (void)
+{
+	return raid1_init();
+}
+
+void cleanup_module (void)
+{
+	md_unregister_thread (raid1_thread);
+	unregister_md_personality (RAID1);
+}
+#endif
diff -u --recursive --new-file v2.1.62/linux/drivers/block/raid5.c linux/drivers/block/raid5.c
--- v2.1.62/linux/drivers/block/raid5.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/block/raid5.c	Sat Nov  8 11:39:12 1997
@@ -0,0 +1,1652 @@
+/*****************************************************************************
+ * raid5.c : Multiple Devices driver for Linux
+ *           Copyright (C) 1996, 1997 Ingo Molnar, Miguel de Icaza, Gadi Oxman
+ *
+ * RAID-5 management functions.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * You should have received a copy of the GNU General Public License
+ * (for example /usr/src/linux/COPYING); if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/locks.h>
+#include <linux/malloc.h>
+#include <linux/md.h>
+#include <linux/raid5.h>
+#include <asm/bitops.h>
+#include <asm/atomic.h>
+
+static struct md_personality raid5_personality;
+
+/*
+ * Stripe cache
+ */
+#define NR_STRIPES		128
+#define HASH_PAGES		1
+#define HASH_PAGES_ORDER	0
+#define NR_HASH			(HASH_PAGES * PAGE_SIZE / sizeof(struct stripe_head *))
+#define HASH_MASK		(NR_HASH - 1)
+#define stripe_hash(raid_conf, sect, size)	((raid_conf)->stripe_hashtbl[((sect) / (size >> 9)) & HASH_MASK])
+
+/*
+ * The following can be used to debug the driver
+ */
+#define RAID5_DEBUG	0
+
+#if RAID5_DEBUG
+#define PRINTK(x)   do { printk x; } while (0);
+#else
+#define PRINTK(x)   do { ; } while (0)
+#endif
+
+static inline int stripe_locked(struct stripe_head *sh)
+{
+	return test_bit(STRIPE_LOCKED, &sh->state);
+}
+
+static inline int stripe_error(struct stripe_head *sh)
+{
+	return test_bit(STRIPE_ERROR, &sh->state);
+}
+
+/*
+ * Stripes are locked whenever new buffers can't be added to them.
+ */
+static inline void lock_stripe(struct stripe_head *sh)
+{
+	struct raid5_data *raid_conf = sh->raid_conf;
+	if (!test_and_set_bit(STRIPE_LOCKED, &sh->state)) {
+		PRINTK(("locking stripe %lu\n", sh->sector));
+		raid_conf->nr_locked_stripes++;
+	}
+}
+
+static inline void unlock_stripe(struct stripe_head *sh)
+{
+	struct raid5_data *raid_conf = sh->raid_conf;
+	if (test_and_clear_bit(STRIPE_LOCKED, &sh->state)) {
+		PRINTK(("unlocking stripe %lu\n", sh->sector));
+		raid_conf->nr_locked_stripes--;
+		wake_up(&sh->wait);
+	}
+}
+
+static inline void finish_stripe(struct stripe_head *sh)
+{
+	struct raid5_data *raid_conf = sh->raid_conf;
+	unlock_stripe(sh);
+	sh->cmd = STRIPE_NONE;
+	sh->phase = PHASE_COMPLETE;
+	raid_conf->nr_pending_stripes--;
+	raid_conf->nr_cached_stripes++;
+	wake_up(&raid_conf->wait_for_stripe);
+}
+
+void __wait_on_stripe(struct stripe_head *sh)
+{
+	struct wait_queue wait = { current, NULL };
+
+	PRINTK(("wait_on_stripe %lu\n", sh->sector));
+	sh->count++;
+	add_wait_queue(&sh->wait, &wait);
+repeat:
+	current->state = TASK_UNINTERRUPTIBLE;
+	if (stripe_locked(sh)) {
+		schedule();
+		goto repeat;
+	}
+	PRINTK(("wait_on_stripe %lu done\n", sh->sector));
+	remove_wait_queue(&sh->wait, &wait);
+	sh->count--;
+	current->state = TASK_RUNNING;
+}
+
+static inline void wait_on_stripe(struct stripe_head *sh)
+{
+	if (stripe_locked(sh))
+		__wait_on_stripe(sh);
+}
+
+static inline void remove_hash(struct raid5_data *raid_conf, struct stripe_head *sh)
+{
+	PRINTK(("remove_hash(), stripe %lu\n", sh->sector));
+
+	if (sh->hash_pprev) {
+		if (sh->hash_next)
+			sh->hash_next->hash_pprev = sh->hash_pprev;
+		*sh->hash_pprev = sh->hash_next;
+		sh->hash_pprev = NULL;
+		raid_conf->nr_hashed_stripes--;
+	}
+}
+
+static inline void insert_hash(struct raid5_data *raid_conf, struct stripe_head *sh)
+{
+	struct stripe_head **shp = &stripe_hash(raid_conf, sh->sector, sh->size);
+
+	PRINTK(("insert_hash(), stripe %lu, nr_hashed_stripes %d\n", sh->sector, raid_conf->nr_hashed_stripes));
+
+	if ((sh->hash_next = *shp) != NULL)
+		(*shp)->hash_pprev = &sh->hash_next;
+	*shp = sh;
+	sh->hash_pprev = shp;
+	raid_conf->nr_hashed_stripes++;
+}
+
+static struct buffer_head *get_free_buffer(struct stripe_head *sh, int b_size)
+{
+	struct buffer_head *bh;
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+	if ((bh = sh->buffer_pool) == NULL)
+		return NULL;
+	sh->buffer_pool = bh->b_next;
+	bh->b_size = b_size;
+	restore_flags(flags);
+	return bh;
+}
+
+static struct buffer_head *get_free_bh(struct stripe_head *sh)
+{
+	struct buffer_head *bh;
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+	if ((bh = sh->bh_pool) == NULL)
+		return NULL;
+	sh->bh_pool = bh->b_next;
+	restore_flags(flags);
+	return bh;
+}
+
+static void put_free_buffer(struct stripe_head *sh, struct buffer_head *bh)
+{
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+	bh->b_next = sh->buffer_pool;
+	sh->buffer_pool = bh;
+	restore_flags(flags);
+}
+
+static void put_free_bh(struct stripe_head *sh, struct buffer_head *bh)
+{
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+	bh->b_next = sh->bh_pool;
+	sh->bh_pool = bh;
+	restore_flags(flags);
+}
+
+static struct stripe_head *get_free_stripe(struct raid5_data *raid_conf)
+{
+	struct stripe_head *sh;
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+	if ((sh = raid_conf->free_sh_list) == NULL) {
+		restore_flags(flags);
+		return NULL;
+	}
+	raid_conf->free_sh_list = sh->free_next;
+	raid_conf->nr_free_sh--;
+	if (!raid_conf->nr_free_sh && raid_conf->free_sh_list)
+		printk ("raid5: bug: free_sh_list != NULL, nr_free_sh == 0\n");
+	restore_flags(flags);
+	if (sh->hash_pprev || sh->nr_pending || sh->count)
+		printk("get_free_stripe(): bug\n");
+	return sh;
+}
+
+static void put_free_stripe(struct raid5_data *raid_conf, struct stripe_head *sh)
+{
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+	sh->free_next = raid_conf->free_sh_list;
+	raid_conf->free_sh_list = sh;
+	raid_conf->nr_free_sh++;
+	restore_flags(flags);
+}
+
+static void shrink_buffers(struct stripe_head *sh, int num)
+{
+	struct buffer_head *bh;
+
+	while (num--) {
+		if ((bh = get_free_buffer(sh, -1)) == NULL)
+			return;
+		free_page((unsigned long) bh->b_data);
+		kfree(bh);
+	}
+}
+
+static void shrink_bh(struct stripe_head *sh, int num)
+{
+	struct buffer_head *bh;
+
+	while (num--) {
+		if ((bh = get_free_bh(sh)) == NULL)
+			return;
+		kfree(bh);
+	}
+}
+
+static int grow_buffers(struct stripe_head *sh, int num, int b_size, int priority)
+{
+	struct buffer_head *bh;
+
+	while (num--) {
+		if ((bh = kmalloc(sizeof(struct buffer_head), priority)) == NULL)
+			return 1;
+		memset(bh, 0, sizeof (struct buffer_head));
+		bh->b_data = (char *) __get_free_page(priority);
+		if (!bh->b_data) {
+			kfree(bh);
+			return 1;
+		}
+		bh->b_size = b_size;
+		put_free_buffer(sh, bh);
+	}
+	return 0;
+}
+
+static int grow_bh(struct stripe_head *sh, int num, int priority)
+{
+	struct buffer_head *bh;
+
+	while (num--) {
+		if ((bh = kmalloc(sizeof(struct buffer_head), priority)) == NULL)
+			return 1;
+		memset(bh, 0, sizeof (struct buffer_head));
+		put_free_bh(sh, bh);
+	}
+	return 0;
+}
+
+static void raid5_kfree_buffer(struct stripe_head *sh, struct buffer_head *bh)
+{
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+	put_free_buffer(sh, bh);
+	restore_flags(flags);
+}
+
+static void raid5_kfree_bh(struct stripe_head *sh, struct buffer_head *bh)
+{
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+	put_free_bh(sh, bh);
+	restore_flags(flags);
+}
+
+static void raid5_kfree_old_bh(struct stripe_head *sh, int i)
+{
+	if (!sh->bh_old[i]) {
+		printk("raid5_kfree_old_bh: bug: sector %lu, index %d not present\n", sh->sector, i);
+		return;
+	}
+	raid5_kfree_buffer(sh, sh->bh_old[i]);
+	sh->bh_old[i] = NULL;
+}
+
+static void raid5_update_old_bh(struct stripe_head *sh, int i)
+{
+	PRINTK(("stripe %lu, idx %d, updating cache copy\n", sh->sector, i));
+	if (!sh->bh_copy[i]) {
+		printk("raid5_update_old_bh: bug: sector %lu, index %d not present\n", sh->sector, i);
+		return;
+	}
+	if (sh->bh_old[i])
+		raid5_kfree_old_bh(sh, i);
+	sh->bh_old[i] = sh->bh_copy[i];
+	sh->bh_copy[i] = NULL;
+}
+
+static void kfree_stripe(struct stripe_head *sh)
+{
+	struct raid5_data *raid_conf = sh->raid_conf;
+	int disks = raid_conf->raid_disks, j;
+
+	PRINTK(("kfree_stripe called, stripe %lu\n", sh->sector));
+	if (sh->phase != PHASE_COMPLETE || stripe_locked(sh) || sh->count) {
+		printk("raid5: kfree_stripe(), sector %lu, phase %d, locked %d, count %d\n", sh->sector, sh->phase, stripe_locked(sh), sh->count);
+		return;
+	}
+	for (j = 0; j < disks; j++) {
+		if (sh->bh_old[j])
+			raid5_kfree_old_bh(sh, j);
+		if (sh->bh_new[j] || sh->bh_copy[j])
+			printk("raid5: bug: sector %lu, new %p, copy %p\n", sh->sector, sh->bh_new[j], sh->bh_copy[j]);
+	}
+	remove_hash(raid_conf, sh);
+	put_free_stripe(raid_conf, sh);
+}
+
+static int shrink_stripe_cache(struct raid5_data *raid_conf, int nr)
+{
+	struct stripe_head *sh;
+	int i, count = 0;
+
+	PRINTK(("shrink_stripe_cache called, %d/%d, clock %d\n", nr, raid_conf->nr_hashed_stripes, raid_conf->clock));
+	for (i = 0; i < NR_HASH; i++) {
+repeat:
+		sh = raid_conf->stripe_hashtbl[(i + raid_conf->clock) & HASH_MASK];
+		for (; sh; sh = sh->hash_next) {
+			if (sh->phase != PHASE_COMPLETE)
+				continue;
+			if (stripe_locked(sh))
+				continue;
+			if (sh->count)
+				continue;
+			kfree_stripe(sh);
+			if (++count == nr) {
+				PRINTK(("shrink completed, nr_hashed_stripes %d\n", raid_conf->nr_hashed_stripes));
+				raid_conf->clock = (i + raid_conf->clock) & HASH_MASK;
+				return nr;
+			}
+			goto repeat;
+		}
+	}
+	PRINTK(("shrink completed, nr_hashed_stripes %d\n", raid_conf->nr_hashed_stripes));
+	return count;
+}
+
+static struct stripe_head *find_stripe(struct raid5_data *raid_conf, unsigned long sector, int size)
+{
+	struct stripe_head *sh;
+
+	if (raid_conf->buffer_size != size) {
+		PRINTK(("switching size, %d --> %d\n", raid_conf->buffer_size, size));
+		shrink_stripe_cache(raid_conf, raid_conf->max_nr_stripes);
+		raid_conf->buffer_size = size;
+	}
+
+	PRINTK(("find_stripe, sector %lu\n", sector));
+	for (sh = stripe_hash(raid_conf, sector, size); sh; sh = sh->hash_next)
+		if (sh->sector == sector && sh->raid_conf == raid_conf) {
+			if (sh->size == size) {
+				PRINTK(("found stripe %lu\n", sector));
+				return sh;
+			} else {
+				PRINTK(("switching size for %lu, %d --> %d\n", sector, sh->size, size));
+				kfree_stripe(sh);
+				break;
+			}
+		}
+	PRINTK(("stripe %lu not in cache\n", sector));
+	return NULL;
+}
+
+static int grow_stripes(struct raid5_data *raid_conf, int num, int priority)
+{
+	struct stripe_head *sh;
+
+	while (num--) {
+		if ((sh = kmalloc(sizeof(struct stripe_head), priority)) == NULL)
+			return 1;
+		memset(sh, 0, sizeof(*sh));
+		if (grow_buffers(sh, 2 * raid_conf->raid_disks, PAGE_SIZE, priority)) {
+			shrink_buffers(sh, 2 * raid_conf->raid_disks);
+			kfree(sh);
+			return 1;
+		}
+		if (grow_bh(sh, raid_conf->raid_disks, priority)) {
+			shrink_buffers(sh, 2 * raid_conf->raid_disks);
+			shrink_bh(sh, raid_conf->raid_disks);
+			kfree(sh);
+			return 1;
+		}
+		put_free_stripe(raid_conf, sh);
+		raid_conf->nr_stripes++;
+	}
+	return 0;
+}
+
+static void shrink_stripes(struct raid5_data *raid_conf, int num)
+{
+	struct stripe_head *sh;
+
+	while (num--) {
+		sh = get_free_stripe(raid_conf);
+		if (!sh)
+			break;
+		shrink_buffers(sh, raid_conf->raid_disks * 2);
+		shrink_bh(sh, raid_conf->raid_disks);
+		kfree(sh);
+		raid_conf->nr_stripes--;
+	}
+}
+
+static struct stripe_head *kmalloc_stripe(struct raid5_data *raid_conf, unsigned long sector, int size)
+{
+	struct stripe_head *sh = NULL, *tmp;
+	struct buffer_head *buffer_pool, *bh_pool;
+
+	PRINTK(("kmalloc_stripe called\n"));
+
+	while ((sh = get_free_stripe(raid_conf)) == NULL) {
+		shrink_stripe_cache(raid_conf, raid_conf->max_nr_stripes / 8);
+		if ((sh = get_free_stripe(raid_conf)) != NULL)
+			break;
+		if (!raid_conf->nr_pending_stripes)
+			printk("raid5: bug: nr_free_sh == 0, nr_pending_stripes == 0\n");
+		md_wakeup_thread(raid_conf->thread);
+		PRINTK(("waiting for some stripes to complete\n"));
+		sleep_on(&raid_conf->wait_for_stripe);
+	}
+
+	/*
+	 * The above might have slept, so perhaps another process
+	 * already created the stripe for us..
+	 */
+	if ((tmp = find_stripe(raid_conf, sector, size)) != NULL) { 
+		put_free_stripe(raid_conf, sh);
+		wait_on_stripe(tmp);
+		return tmp;
+	}
+	if (sh) {
+		buffer_pool = sh->buffer_pool;
+		bh_pool = sh->bh_pool;
+		memset(sh, 0, sizeof(*sh));
+		sh->buffer_pool = buffer_pool;
+		sh->bh_pool = bh_pool;
+		sh->phase = PHASE_COMPLETE;
+		sh->cmd = STRIPE_NONE;
+		sh->raid_conf = raid_conf;
+		sh->sector = sector;
+		sh->size = size;
+		raid_conf->nr_cached_stripes++;
+		insert_hash(raid_conf, sh);
+	} else printk("raid5: bug: kmalloc_stripe() == NULL\n");
+	return sh;
+}
+
+static struct stripe_head *get_stripe(struct raid5_data *raid_conf, unsigned long sector, int size)
+{
+	struct stripe_head *sh;
+
+	PRINTK(("get_stripe, sector %lu\n", sector));
+	sh = find_stripe(raid_conf, sector, size);
+	if (sh)
+		wait_on_stripe(sh);
+	else
+		sh = kmalloc_stripe(raid_conf, sector, size);
+	return sh;
+}
+
+
+static struct buffer_head *raid5_kmalloc_buffer(struct stripe_head *sh, int b_size)
+{
+	struct buffer_head *bh;
+
+	if ((bh = get_free_buffer(sh, b_size)) == NULL)
+		printk("raid5: bug: raid5_kmalloc_buffer() == NULL\n");
+	return bh;
+}
+
+static struct buffer_head *raid5_kmalloc_bh(struct stripe_head *sh)
+{
+	struct buffer_head *bh;
+
+	if ((bh = get_free_bh(sh)) == NULL)
+		printk("raid5: bug: raid5_kmalloc_bh() == NULL\n");
+	return bh;
+}
+
+static inline void raid5_end_buffer_io (struct stripe_head *sh, int i, int uptodate)
+{
+	struct buffer_head *bh = sh->bh_new[i];
+
+	sh->bh_new[i] = NULL;
+	raid5_kfree_bh(sh, sh->bh_req[i]);
+	sh->bh_req[i] = NULL;
+	bh->b_end_io(bh, uptodate);
+	if (!uptodate)
+		printk(KERN_ALERT "raid5: %s: unrecoverable I/O error for "
+		       "block %lu\n", kdevname(bh->b_dev), bh->b_blocknr);
+}
+
+static inline void raid5_mark_buffer_uptodate (struct buffer_head *bh, int uptodate)
+{
+	if (uptodate)
+		set_bit(BH_Uptodate, &bh->b_state);
+	else
+		clear_bit(BH_Uptodate, &bh->b_state);
+}
+
+static void raid5_end_request (struct buffer_head * bh, int uptodate)
+{
+	struct stripe_head *sh = bh->b_dev_id;
+	struct raid5_data *raid_conf = sh->raid_conf;
+	int disks = raid_conf->raid_disks, i;
+	unsigned long flags;
+
+	PRINTK(("end_request %lu, nr_pending %d\n", sh->sector, sh->nr_pending));
+	save_flags(flags);
+	cli();
+	raid5_mark_buffer_uptodate(bh, uptodate);
+	--sh->nr_pending;
+	if (!sh->nr_pending) {
+		md_wakeup_thread(raid_conf->thread);
+		atomic_inc(&raid_conf->nr_handle);
+	}
+	if (!uptodate)
+		md_error(bh->b_dev, bh->b_rdev);
+	if (raid_conf->failed_disks) {
+		for (i = 0; i < disks; i++) {
+			if (raid_conf->disks[i].operational)
+				continue;
+			if (bh != sh->bh_old[i] && bh != sh->bh_req[i] && bh != sh->bh_copy[i])
+				continue;
+			if (bh->b_rdev != raid_conf->disks[i].dev)
+				continue;
+			set_bit(STRIPE_ERROR, &sh->state);
+		}
+	}
+	restore_flags(flags);
+}
+
+static int raid5_map (struct md_dev *mddev, kdev_t *rdev,
+		      unsigned long *rsector, unsigned long size)
+{
+	/* No complex mapping used: the core of the work is done in the
+	 * request routine
+	 */
+	return 0;
+}
+
+static void raid5_build_block (struct stripe_head *sh, struct buffer_head *bh, int i)
+{
+	struct raid5_data *raid_conf = sh->raid_conf;
+	struct md_dev *mddev = raid_conf->mddev;
+	int minor = (int) (mddev - md_dev);
+	char *b_data;
+	kdev_t dev = MKDEV(MD_MAJOR, minor);
+	int block = sh->sector / (sh->size >> 9);
+
+	b_data = ((volatile struct buffer_head *) bh)->b_data;
+	memset (bh, 0, sizeof (struct buffer_head));
+	init_buffer(bh, dev, block, raid5_end_request, sh);
+	((volatile struct buffer_head *) bh)->b_data = b_data;
+
+	bh->b_rdev	= raid_conf->disks[i].dev;
+	bh->b_rsector   = sh->sector;
+
+	bh->b_state	= (1 << BH_Req);
+	bh->b_size	= sh->size;
+	bh->b_list	= BUF_LOCKED;
+}
+
+static int raid5_error (struct md_dev *mddev, kdev_t dev)
+{
+	struct raid5_data *raid_conf = (struct raid5_data *) mddev->private;
+	md_superblock_t *sb = mddev->sb;
+	struct disk_info *disk;
+	int i;
+
+	PRINTK(("raid5_error called\n"));
+	raid_conf->resync_parity = 0;
+	for (i = 0, disk = raid_conf->disks; i < raid_conf->raid_disks; i++, disk++)
+		if (disk->dev == dev && disk->operational) {
+			disk->operational = 0;
+			sb->disks[disk->number].state |= (1 << MD_FAULTY_DEVICE);
+			sb->disks[disk->number].state &= ~(1 << MD_SYNC_DEVICE);
+			sb->disks[disk->number].state &= ~(1 << MD_ACTIVE_DEVICE);
+			sb->active_disks--;
+			sb->working_disks--;
+			sb->failed_disks++;
+			mddev->sb_dirty = 1;
+			raid_conf->working_disks--;
+			raid_conf->failed_disks++;
+			md_wakeup_thread(raid_conf->thread);
+			printk (KERN_ALERT
+				"RAID5: Disk failure on %s, disabling device."
+				"Operation continuing on %d devices\n",
+				kdevname (dev), raid_conf->working_disks);
+		}
+	return 0;
+}	
+
+/*
+ * Input: a 'big' sector number, 
+ * Output: index of the data and parity disk, and the sector # in them.
+ */
+static inline unsigned long 
+raid5_compute_sector (int r_sector, unsigned int raid_disks, unsigned int data_disks,
+			unsigned int * dd_idx, unsigned int * pd_idx, 
+			struct raid5_data *raid_conf)
+{
+	unsigned int  stripe;
+	int chunk_number, chunk_offset;
+	unsigned long new_sector;
+	int sectors_per_chunk = raid_conf->chunk_size >> 9;
+
+	/* First compute the information on this sector */
+
+	/*
+	 * Compute the chunk number and the sector offset inside the chunk
+	 */
+	chunk_number = r_sector / sectors_per_chunk;
+	chunk_offset = r_sector % sectors_per_chunk;
+
+	/*
+	 * Compute the stripe number
+	 */
+	stripe = chunk_number / data_disks;
+
+	/*
+	 * Compute the data disk and parity disk indexes inside the stripe
+	 */
+	*dd_idx = chunk_number % data_disks;
+
+	/*
+	 * Select the parity disk based on the user selected algorithm.
+	 */
+	if (raid_conf->level == 4)
+		*pd_idx = data_disks;
+	else switch (raid_conf->algorithm) {
+		case ALGORITHM_LEFT_ASYMMETRIC:
+			*pd_idx = data_disks - stripe % raid_disks;
+			if (*dd_idx >= *pd_idx)
+				(*dd_idx)++;
+			break;
+		case ALGORITHM_RIGHT_ASYMMETRIC:
+			*pd_idx = stripe % raid_disks;
+			if (*dd_idx >= *pd_idx)
+				(*dd_idx)++;
+			break;
+		case ALGORITHM_LEFT_SYMMETRIC:
+			*pd_idx = data_disks - stripe % raid_disks;
+			*dd_idx = (*pd_idx + 1 + *dd_idx) % raid_disks;
+			break;
+		case ALGORITHM_RIGHT_SYMMETRIC:
+			*pd_idx = stripe % raid_disks;
+			*dd_idx = (*pd_idx + 1 + *dd_idx) % raid_disks;
+			break;
+		default:
+			printk ("raid5: unsupported algorithm %d\n", raid_conf->algorithm);
+	}
+
+	/*
+	 * Finally, compute the new sector number
+	 */
+	new_sector = stripe * sectors_per_chunk + chunk_offset;
+
+#if 0
+	if (	*dd_idx > data_disks || *pd_idx > data_disks || 
+		chunk_offset + bh->b_size / 512 > sectors_per_chunk	)
+
+		printk ("raid5: bug: dd_idx == %d, pd_idx == %d, chunk_offset == %d\n", 
+				*dd_idx, *pd_idx, chunk_offset);
+#endif
+
+	return new_sector;
+}
+
+static unsigned long compute_blocknr(struct stripe_head *sh, int i)
+{
+	struct raid5_data *raid_conf = sh->raid_conf;
+	int raid_disks = raid_conf->raid_disks, data_disks = raid_disks - 1;
+	unsigned long new_sector = sh->sector, check;
+	int sectors_per_chunk = raid_conf->chunk_size >> 9;
+	unsigned long stripe = new_sector / sectors_per_chunk;
+	int chunk_offset = new_sector % sectors_per_chunk;
+	int chunk_number, dummy1, dummy2, dd_idx = i;
+	unsigned long r_sector, blocknr;
+
+	switch (raid_conf->algorithm) {
+		case ALGORITHM_LEFT_ASYMMETRIC:
+		case ALGORITHM_RIGHT_ASYMMETRIC:
+			if (i > sh->pd_idx)
+				i--;
+			break;
+		case ALGORITHM_LEFT_SYMMETRIC:
+		case ALGORITHM_RIGHT_SYMMETRIC:
+			if (i < sh->pd_idx)
+				i += raid_disks;
+			i -= (sh->pd_idx + 1);
+			break;
+		default:
+			printk ("raid5: unsupported algorithm %d\n", raid_conf->algorithm);
+	}
+
+	chunk_number = stripe * data_disks + i;
+	r_sector = chunk_number * sectors_per_chunk + chunk_offset;
+	blocknr = r_sector / (sh->size >> 9);
+
+	check = raid5_compute_sector (r_sector, raid_disks, data_disks, &dummy1, &dummy2, raid_conf);
+	if (check != sh->sector || dummy1 != dd_idx || dummy2 != sh->pd_idx) {
+		printk("compute_blocknr: map not correct\n");
+		return 0;
+	}
+	return blocknr;
+}
+
+static void xor_block(struct buffer_head *dest, struct buffer_head *source)
+{
+	int lines = dest->b_size / (sizeof (int)) / 8, i;
+	int *destp = (int *) dest->b_data, *sourcep = (int *) source->b_data;
+
+	for (i = lines; i > 0; i--) {
+		*(destp + 0) ^= *(sourcep + 0);
+		*(destp + 1) ^= *(sourcep + 1);
+		*(destp + 2) ^= *(sourcep + 2);
+		*(destp + 3) ^= *(sourcep + 3);
+		*(destp + 4) ^= *(sourcep + 4);
+		*(destp + 5) ^= *(sourcep + 5);
+		*(destp + 6) ^= *(sourcep + 6);
+		*(destp + 7) ^= *(sourcep + 7);
+		destp += 8;
+		sourcep += 8;
+	}
+}
+
+static void compute_block(struct stripe_head *sh, int dd_idx)
+{
+	struct raid5_data *raid_conf = sh->raid_conf;
+	int i, disks = raid_conf->raid_disks;
+
+	PRINTK(("compute_block, stripe %lu, idx %d\n", sh->sector, dd_idx));
+
+	if (sh->bh_old[dd_idx] == NULL)
+		sh->bh_old[dd_idx] = raid5_kmalloc_buffer(sh, sh->size);
+	raid5_build_block(sh, sh->bh_old[dd_idx], dd_idx);
+
+	memset(sh->bh_old[dd_idx]->b_data, 0, sh->size);
+	for (i = 0; i < disks; i++) {
+		if (i == dd_idx)
+			continue;
+		if (sh->bh_old[i]) {
+			xor_block(sh->bh_old[dd_idx], sh->bh_old[i]);
+			continue;
+		} else
+			printk("compute_block() %d, stripe %lu, %d not present\n", dd_idx, sh->sector, i);
+	}
+	raid5_mark_buffer_uptodate(sh->bh_old[dd_idx], 1);
+}
+
+static void compute_parity(struct stripe_head *sh, int method)
+{
+	struct raid5_data *raid_conf = sh->raid_conf;
+	int i, pd_idx = sh->pd_idx, disks = raid_conf->raid_disks;
+
+	PRINTK(("compute_parity, stripe %lu, method %d\n", sh->sector, method));
+	for (i = 0; i < disks; i++) {
+		if (i == pd_idx || !sh->bh_new[i])
+			continue;
+		if (!sh->bh_copy[i])
+			sh->bh_copy[i] = raid5_kmalloc_buffer(sh, sh->size);
+		raid5_build_block(sh, sh->bh_copy[i], i);
+		mark_buffer_clean(sh->bh_new[i]);
+		memcpy(sh->bh_copy[i]->b_data, sh->bh_new[i]->b_data, sh->size);
+	}
+	if (sh->bh_copy[pd_idx] == NULL)
+		sh->bh_copy[pd_idx] = raid5_kmalloc_buffer(sh, sh->size);
+	raid5_build_block(sh, sh->bh_copy[pd_idx], sh->pd_idx);
+
+	if (method == RECONSTRUCT_WRITE) {
+		memset(sh->bh_copy[pd_idx]->b_data, 0, sh->size);
+		for (i = 0; i < disks; i++) {
+			if (i == sh->pd_idx)
+				continue;
+			if (sh->bh_new[i]) {
+				xor_block(sh->bh_copy[pd_idx], sh->bh_copy[i]);
+				continue;
+			}
+			if (sh->bh_old[i]) {
+				xor_block(sh->bh_copy[pd_idx], sh->bh_old[i]);
+				continue;
+			}
+		}
+	} else if (method == READ_MODIFY_WRITE) {
+		memcpy(sh->bh_copy[pd_idx]->b_data, sh->bh_old[pd_idx]->b_data, sh->size);
+		for (i = 0; i < disks; i++) {
+			if (i == sh->pd_idx)
+				continue;
+			if (sh->bh_new[i] && sh->bh_old[i]) {
+				xor_block(sh->bh_copy[pd_idx], sh->bh_copy[i]);
+				xor_block(sh->bh_copy[pd_idx], sh->bh_old[i]);
+				continue;
+			}
+		}
+	}
+	raid5_mark_buffer_uptodate(sh->bh_copy[pd_idx], 1);
+}
+
+static void add_stripe_bh (struct stripe_head *sh, struct buffer_head *bh, int dd_idx, int rw)
+{
+	struct raid5_data *raid_conf = sh->raid_conf;
+	struct buffer_head *bh_req;
+
+	if (sh->bh_new[dd_idx])
+		printk("raid5: bug: stripe->bh_new[%d], sector %lu exists\n", dd_idx, sh->sector);
+
+	set_bit(BH_Lock, &bh->b_state);
+
+	bh_req = raid5_kmalloc_bh(sh);
+	raid5_build_block(sh, bh_req, dd_idx);
+	bh_req->b_data = bh->b_data;
+
+	if (sh->phase == PHASE_COMPLETE && sh->cmd == STRIPE_NONE) {
+		sh->phase = PHASE_BEGIN;
+		sh->cmd = (rw == READ) ? STRIPE_READ : STRIPE_WRITE;
+		raid_conf->nr_pending_stripes++;
+		atomic_inc(&raid_conf->nr_handle);
+	}
+	sh->bh_new[dd_idx] = bh;
+	sh->bh_req[dd_idx] = bh_req;
+	sh->cmd_new[dd_idx] = rw;
+	sh->new[dd_idx] = 1;
+}
+
+static void complete_stripe(struct stripe_head *sh)
+{
+	struct raid5_data *raid_conf = sh->raid_conf;
+	int disks = raid_conf->raid_disks;
+	int i, new = 0;
+	
+	PRINTK(("complete_stripe %lu\n", sh->sector));
+	for (i = 0; i < disks; i++) {
+		if (sh->cmd == STRIPE_WRITE && i == sh->pd_idx)
+			raid5_update_old_bh(sh, i);
+		if (sh->bh_new[i]) {
+			if (!sh->new[i]) {
+#if 0
+				if (sh->cmd == STRIPE_WRITE) {
+					if (memcmp(sh->bh_new[i]->b_data, sh->bh_copy[i]->b_data, sh->size)) {
+						printk("copy differs, %s, sector %lu ",
+							test_bit(BH_Dirty, &sh->bh_new[i]->b_state) ? "dirty" : "clean",
+							sh->sector);
+					} else if (test_bit(BH_Dirty, &sh->bh_new[i]->b_state))
+						printk("sector %lu dirty\n", sh->sector);
+				}
+#endif
+				if (sh->cmd == STRIPE_WRITE)
+					raid5_update_old_bh(sh, i);
+				raid5_end_buffer_io(sh, i, 1);
+				continue;
+			} else
+				new++;
+		}
+		if (new && sh->cmd == STRIPE_WRITE)
+			printk("raid5: bug, completed STRIPE_WRITE with new == %d\n", new);
+	}
+	if (!new)
+		finish_stripe(sh);
+	else {
+		PRINTK(("stripe %lu, new == %d\n", sh->sector, new));
+		sh->phase = PHASE_BEGIN;
+	}
+}
+
+/*
+ * handle_stripe() is our main logic routine. Note that:
+ *
+ * 1.	lock_stripe() should be used whenever we can't accept additonal
+ *	buffers, either during short sleeping in handle_stripe() or
+ *	during io operations.
+ *
+ * 2.	We should be careful to set sh->nr_pending whenever we sleep,
+ *	to prevent re-entry of handle_stripe() for the same sh.
+ *
+ * 3.	raid_conf->failed_disks and disk->operational can be changed
+ *	from an interrupt. This complicates things a bit, but it allows
+ *	us to stop issuing requests for a failed drive as soon as possible.
+ */
+static void handle_stripe(struct stripe_head *sh)
+{
+	struct raid5_data *raid_conf = sh->raid_conf;
+	struct md_dev *mddev = raid_conf->mddev;
+	int minor = (int) (mddev - md_dev);
+	struct buffer_head *bh;
+	int disks = raid_conf->raid_disks;
+	int i, nr = 0, nr_read = 0, nr_write = 0;
+	int nr_cache = 0, nr_cache_other = 0, nr_cache_overwrite = 0, parity = 0;
+	int nr_failed_other = 0, nr_failed_overwrite = 0, parity_failed = 0;
+	int reading = 0, nr_writing = 0;
+	int method1 = INT_MAX, method2 = INT_MAX;
+	int block;
+	unsigned long flags;
+	int operational[MD_SB_DISKS], failed_disks = raid_conf->failed_disks;
+
+	PRINTK(("handle_stripe(), stripe %lu\n", sh->sector));
+	if (sh->nr_pending) {
+		printk("handle_stripe(), stripe %lu, io still pending\n", sh->sector);
+		return;
+	}
+	if (sh->phase == PHASE_COMPLETE) {
+		printk("handle_stripe(), stripe %lu, already complete\n", sh->sector);
+		return;
+	}
+
+	atomic_dec(&raid_conf->nr_handle);
+
+	if (test_and_clear_bit(STRIPE_ERROR, &sh->state)) {
+		printk("raid5: restarting stripe %lu\n", sh->sector);
+		sh->phase = PHASE_BEGIN;
+	}
+
+	if ((sh->cmd == STRIPE_WRITE && sh->phase == PHASE_WRITE) ||
+	    (sh->cmd == STRIPE_READ && sh->phase == PHASE_READ)) {
+		/*
+		 * Completed
+		 */
+		complete_stripe(sh);
+		if (sh->phase == PHASE_COMPLETE)
+			return;
+	}
+
+	save_flags(flags);
+	cli();
+	for (i = 0; i < disks; i++) {
+		operational[i] = raid_conf->disks[i].operational;
+		if (i == sh->pd_idx && raid_conf->resync_parity)
+			operational[i] = 0;
+	}
+	failed_disks = raid_conf->failed_disks;
+	restore_flags(flags);
+
+	if (failed_disks > 1) {
+		for (i = 0; i < disks; i++) {
+			if (sh->bh_new[i]) {
+				raid5_end_buffer_io(sh, i, 0);
+				continue;
+			}
+		}
+		finish_stripe(sh);
+		return;
+	}
+
+	for (i = 0; i < disks; i++) {
+		if (sh->bh_old[i])
+			nr_cache++;
+		if (i == sh->pd_idx) {
+			if (sh->bh_old[i])
+				parity = 1;
+			else if(!operational[i])
+				parity_failed = 1;
+			continue;
+		}
+		if (!sh->bh_new[i]) {
+			if (sh->bh_old[i])
+				nr_cache_other++;
+			else if (!operational[i])
+				nr_failed_other++;
+			continue;
+		}
+		sh->new[i] = 0;
+		nr++;
+		if (sh->cmd_new[i] == READ)
+			nr_read++;
+		if (sh->cmd_new[i] == WRITE)
+			nr_write++;
+		if (sh->bh_old[i])
+			nr_cache_overwrite++;
+		else if (!operational[i])
+			nr_failed_overwrite++;
+	}
+
+	if (nr_write && nr_read)
+		printk("raid5: bug, nr_write == %d, nr_read == %d, sh->cmd == %d\n", nr_write, nr_read, sh->cmd);
+
+	if (nr_write) {
+		/*
+		 * Attempt to add entries :-)
+		 */
+		if (nr_write != disks - 1) {
+			for (i = 0; i < disks; i++) {
+				if (i == sh->pd_idx)
+					continue;
+				if (sh->bh_new[i])
+					continue;
+				block = (int) compute_blocknr(sh, i);
+				bh = efind_buffer(MKDEV(MD_MAJOR, minor), block, sh->size);
+				if (bh && bh->b_count == 0 && buffer_dirty(bh) && !buffer_locked(bh)) {
+					PRINTK(("Whee.. sector %lu, index %d (%d) found in the buffer cache!\n", sh->sector, i, block));
+					add_stripe_bh(sh, bh, i, WRITE);
+					sh->new[i] = 0;
+					nr++; nr_write++;
+					if (sh->bh_old[i]) {
+						nr_cache_overwrite++;
+						nr_cache_other--;
+					} else if (!operational[i]) {
+						nr_failed_overwrite++;
+						nr_failed_other--;
+					}
+				}
+			}
+		}
+		PRINTK(("handle_stripe() -- begin writing, stripe %lu\n", sh->sector));
+		/*
+		 * Writing, need to update parity buffer.
+		 *
+		 * Compute the number of I/O requests in the "reconstruct
+		 * write" and "read modify write" methods.
+		 */
+		if (!nr_failed_other)
+			method1 = (disks - 1) - (nr_write + nr_cache_other);
+		if (!nr_failed_overwrite && !parity_failed)
+			method2 = nr_write - nr_cache_overwrite + (1 - parity);
+
+		if (method1 == INT_MAX && method2 == INT_MAX)
+			printk("raid5: bug: method1 == method2 == INT_MAX\n");
+		PRINTK(("handle_stripe(), sector %lu, nr_write %d, method1 %d, method2 %d\n", sh->sector, nr_write, method1, method2));
+
+		if (!method1 || !method2) {
+			lock_stripe(sh);
+			sh->nr_pending++;
+			sh->phase = PHASE_WRITE;
+			compute_parity(sh, method1 <= method2 ? RECONSTRUCT_WRITE : READ_MODIFY_WRITE);
+			for (i = 0; i < disks; i++) {
+				if (!operational[i] && !raid_conf->spare && !raid_conf->resync_parity)
+					continue;
+				if (i == sh->pd_idx || sh->bh_new[i])
+					nr_writing++;
+			}
+
+			sh->nr_pending = nr_writing;
+			PRINTK(("handle_stripe() %lu, writing back %d\n", sh->sector, sh->nr_pending));
+
+			for (i = 0; i < disks; i++) {
+				if (!operational[i] && !raid_conf->spare && !raid_conf->resync_parity)
+					continue;
+				bh = sh->bh_copy[i];
+				if (i != sh->pd_idx && ((bh == NULL) ^ (sh->bh_new[i] == NULL)))
+					printk("raid5: bug: bh == %p, bh_new[%d] == %p\n", bh, i, sh->bh_new[i]);
+				if (i == sh->pd_idx && !bh)
+					printk("raid5: bug: bh == NULL, i == pd_idx == %d\n", i);
+				if (bh) {
+					bh->b_state |= (1<<BH_Dirty);
+					PRINTK(("making request for buffer %d\n", i));
+					clear_bit(BH_Lock, &bh->b_state);
+					if (!operational[i] && !raid_conf->resync_parity) {
+						bh->b_rdev = raid_conf->spare->dev;
+						make_request(MAJOR(raid_conf->spare->dev), WRITE, bh);
+					} else
+						make_request(MAJOR(raid_conf->disks[i].dev), WRITE, bh);
+				}
+			}
+			return;
+		}
+
+		lock_stripe(sh);
+		sh->nr_pending++;
+		if (method1 < method2) {
+			sh->write_method = RECONSTRUCT_WRITE;
+			for (i = 0; i < disks; i++) {
+				if (i == sh->pd_idx)
+					continue;
+				if (sh->bh_new[i] || sh->bh_old[i])
+					continue;
+				sh->bh_old[i] = raid5_kmalloc_buffer(sh, sh->size);
+				raid5_build_block(sh, sh->bh_old[i], i);
+				reading++;
+			}
+		} else {
+			sh->write_method = READ_MODIFY_WRITE;
+			for (i = 0; i < disks; i++) {
+				if (sh->bh_old[i])
+					continue;
+				if (!sh->bh_new[i] && i != sh->pd_idx)
+					continue;
+				sh->bh_old[i] = raid5_kmalloc_buffer(sh, sh->size);
+				raid5_build_block(sh, sh->bh_old[i], i);
+				reading++;
+			}
+		}
+		sh->phase = PHASE_READ_OLD;
+		sh->nr_pending = reading;
+		PRINTK(("handle_stripe() %lu, reading %d old buffers\n", sh->sector, sh->nr_pending));
+		for (i = 0; i < disks; i++) {
+			if (!sh->bh_old[i])
+				continue;
+			if (buffer_uptodate(sh->bh_old[i]))
+				continue;
+		 	clear_bit(BH_Lock, &sh->bh_old[i]->b_state);
+			make_request(MAJOR(raid_conf->disks[i].dev), READ, sh->bh_old[i]);
+		}
+	} else {
+		/*
+		 * Reading
+		 */
+		method1 = nr_read - nr_cache_overwrite;
+		lock_stripe(sh);
+		sh->nr_pending++;
+
+		PRINTK(("handle_stripe(), sector %lu, nr_read %d, nr_cache %d, method1 %d\n", sh->sector, nr_read, nr_cache, method1));
+		if (!method1 || (method1 == 1 && nr_cache == disks - 1)) {
+			PRINTK(("read %lu completed from cache\n", sh->sector));
+			for (i = 0; i < disks; i++) {
+				if (!sh->bh_new[i])
+					continue;
+				if (!sh->bh_old[i])
+					compute_block(sh, i);
+				memcpy(sh->bh_new[i]->b_data, sh->bh_old[i]->b_data, sh->size);
+			}
+			sh->nr_pending--;
+			complete_stripe(sh);
+			return;
+		}
+		if (nr_failed_overwrite) {
+			sh->phase = PHASE_READ_OLD;
+			sh->nr_pending = (disks - 1) - nr_cache;
+			PRINTK(("handle_stripe() %lu, phase READ_OLD, pending %d\n", sh->sector, sh->nr_pending));
+			for (i = 0; i < disks; i++) {
+				if (sh->bh_old[i])
+					continue;
+				if (!operational[i])
+					continue;
+				sh->bh_old[i] = raid5_kmalloc_buffer(sh, sh->size);
+				raid5_build_block(sh, sh->bh_old[i], i);
+			 	clear_bit(BH_Lock, &sh->bh_old[i]->b_state);
+				make_request(MAJOR(raid_conf->disks[i].dev), READ, sh->bh_old[i]);
+			}
+		} else {
+			sh->phase = PHASE_READ;
+			sh->nr_pending = nr_read - nr_cache_overwrite;
+			PRINTK(("handle_stripe() %lu, phase READ, pending %d\n", sh->sector, sh->nr_pending));
+			for (i = 0; i < disks; i++) {
+				if (!sh->bh_new[i])
+					continue;
+				if (sh->bh_old[i]) {
+					memcpy(sh->bh_new[i]->b_data, sh->bh_old[i]->b_data, sh->size);
+					continue;
+				}
+				make_request(MAJOR(raid_conf->disks[i].dev), READ, sh->bh_req[i]);
+			}
+		}
+	}
+}
+
+static int raid5_make_request (struct md_dev *mddev, int rw, struct buffer_head * bh)
+{
+	struct raid5_data *raid_conf = (struct raid5_data *) mddev->private;
+	const unsigned int raid_disks = raid_conf->raid_disks;
+	const unsigned int data_disks = raid_disks - 1;
+	unsigned int  dd_idx, pd_idx;
+	unsigned long new_sector;
+
+	struct stripe_head *sh;
+
+	if (rw == READA) rw = READ;
+	if (rw == WRITEA) rw = WRITE;
+
+	new_sector = raid5_compute_sector(bh->b_rsector, raid_disks, data_disks,
+						&dd_idx, &pd_idx, raid_conf);
+
+	PRINTK(("raid5_make_request, sector %lu\n", new_sector));
+repeat:
+	sh = get_stripe(raid_conf, new_sector, bh->b_size);
+	if ((rw == READ && sh->cmd == STRIPE_WRITE) || (rw == WRITE && sh->cmd == STRIPE_READ)) {
+		PRINTK(("raid5: lock contention, rw == %d, sh->cmd == %d\n", rw, sh->cmd));
+		lock_stripe(sh);
+		if (!sh->nr_pending)
+			handle_stripe(sh);
+		goto repeat;
+	}
+	sh->pd_idx = pd_idx;
+	if (sh->phase != PHASE_COMPLETE && sh->phase != PHASE_BEGIN)
+		PRINTK(("stripe %lu catching the bus!\n", sh->sector));
+	add_stripe_bh(sh, bh, dd_idx, rw);
+
+	md_wakeup_thread(raid_conf->thread);
+	return 0;
+}
+
+static void unplug_devices(struct stripe_head *sh)
+{
+#if 0
+	struct raid5_data *raid_conf = sh->raid_conf;
+	int i;
+
+	for (i = 0; i < raid_conf->raid_disks; i++)
+		unplug_device(blk_dev + MAJOR(raid_conf->disks[i].dev));
+#endif
+}
+
+/*
+ * This is our raid5 kernel thread.
+ *
+ * We scan the hash table for stripes which can be handled now.
+ * During the scan, completed stripes are saved for us by the interrupt
+ * handler, so that they will not have to wait for our next wakeup.
+ */
+static void raid5d (void *data)
+{
+	struct stripe_head *sh;
+	struct raid5_data *raid_conf = data;
+	struct md_dev *mddev = raid_conf->mddev;
+	int i, handled = 0, unplug = 0;
+	unsigned long flags;
+
+	PRINTK(("+++ raid5d active\n"));
+
+	if (mddev->sb_dirty) {
+		mddev->sb_dirty = 0;
+		md_update_sb((int) (mddev - md_dev));
+	}
+	for (i = 0; i < NR_HASH; i++) {
+repeat:
+		sh = raid_conf->stripe_hashtbl[i];
+		for (; sh; sh = sh->hash_next) {
+			if (sh->raid_conf != raid_conf)
+				continue;
+			if (sh->phase == PHASE_COMPLETE)
+				continue;
+			if (sh->nr_pending)
+				continue;
+			if (sh->sector == raid_conf->next_sector) {
+				raid_conf->sector_count += (sh->size >> 9);
+				if (raid_conf->sector_count >= 128)
+					unplug = 1;
+			} else
+				unplug = 1;
+			if (unplug) {
+				PRINTK(("unplugging devices, sector == %lu, count == %d\n", sh->sector, raid_conf->sector_count));
+				unplug_devices(sh);
+				unplug = 0;
+				raid_conf->sector_count = 0;
+			}
+			raid_conf->next_sector = sh->sector + (sh->size >> 9);
+			handled++;
+			handle_stripe(sh);
+			goto repeat;
+		}
+	}
+	if (raid_conf) {
+		PRINTK(("%d stripes handled, nr_handle %d\n", handled, atomic_read(&raid_conf->nr_handle)));
+		save_flags(flags);
+		cli();
+		if (!atomic_read(&raid_conf->nr_handle))
+			clear_bit(THREAD_WAKEUP, &raid_conf->thread->flags);
+	}
+	PRINTK(("--- raid5d inactive\n"));
+}
+
+#if SUPPORT_RECONSTRUCTION
+/*
+ * Private kernel thread for parity reconstruction after an unclean
+ * shutdown. Reconstruction on spare drives in case of a failed drive
+ * is done by the generic mdsyncd.
+ */
+static void raid5syncd (void *data)
+{
+	struct raid5_data *raid_conf = data;
+	struct md_dev *mddev = raid_conf->mddev;
+
+	if (!raid_conf->resync_parity)
+		return;
+	md_do_sync(mddev);
+	raid_conf->resync_parity = 0;
+}
+#endif /* SUPPORT_RECONSTRUCTION */
+
+static int __check_consistency (struct md_dev *mddev, int row)
+{
+	struct raid5_data *raid_conf = mddev->private;
+	kdev_t dev;
+	struct buffer_head *bh[MD_SB_DISKS], tmp;
+	int i, rc = 0, nr = 0;
+
+	if (raid_conf->working_disks != raid_conf->raid_disks)
+		return 0;
+	tmp.b_size = 4096;
+	if ((tmp.b_data = (char *) get_free_page(GFP_KERNEL)) == NULL)
+		return 0;
+	memset(bh, 0, MD_SB_DISKS * sizeof(struct buffer_head *));
+	for (i = 0; i < raid_conf->raid_disks; i++) {
+		dev = raid_conf->disks[i].dev;
+		set_blocksize(dev, 4096);
+		if ((bh[i] = bread(dev, row / 4, 4096)) == NULL)
+			break;
+		nr++;
+	}
+	if (nr == raid_conf->raid_disks) {
+		for (i = 1; i < nr; i++)
+			xor_block(&tmp, bh[i]);
+		if (memcmp(tmp.b_data, bh[0]->b_data, 4096))
+			rc = 1;
+	}
+	for (i = 0; i < raid_conf->raid_disks; i++) {
+		dev = raid_conf->disks[i].dev;
+		if (bh[i]) {
+			bforget(bh[i]);
+			bh[i] = NULL;
+		}
+		fsync_dev(dev);
+		invalidate_buffers(dev);
+	}
+	free_page((unsigned long) tmp.b_data);
+	return rc;
+}
+
+static int check_consistency (struct md_dev *mddev)
+{
+	int size = mddev->sb->size;
+	int row;
+
+	for (row = 0; row < size; row += size / 8)
+		if (__check_consistency(mddev, row))
+			return 1;
+	return 0;
+}
+
+static int raid5_run (int minor, struct md_dev *mddev)
+{
+	struct raid5_data *raid_conf;
+	int i, j, raid_disk, memory;
+	md_superblock_t *sb = mddev->sb;
+	md_descriptor_t *descriptor;
+	struct real_dev *realdev;
+
+	MOD_INC_USE_COUNT;
+
+	if (sb->level != 5 && sb->level != 4) {
+		printk("raid5: %s: raid level not set to 4/5 (%d)\n", kdevname(MKDEV(MD_MAJOR, minor)), sb->level);
+		MOD_DEC_USE_COUNT;
+		return -EIO;
+	}
+
+	mddev->private = kmalloc (sizeof (struct raid5_data), GFP_KERNEL);
+	if ((raid_conf = mddev->private) == NULL)
+		goto abort;
+	memset (raid_conf, 0, sizeof (*raid_conf));
+	raid_conf->mddev = mddev;
+
+	if ((raid_conf->stripe_hashtbl = (struct stripe_head **) __get_free_pages(GFP_ATOMIC, HASH_PAGES_ORDER, 0)) == NULL)
+		goto abort;
+	memset(raid_conf->stripe_hashtbl, 0, HASH_PAGES * PAGE_SIZE);
+
+	init_waitqueue(&raid_conf->wait_for_stripe);
+	PRINTK(("raid5_run(%d) called.\n", minor));
+
+  	for (i = 0; i < mddev->nb_dev; i++) {
+  		realdev = &mddev->devices[i];
+		if (!realdev->sb) {
+			printk(KERN_ERR "raid5: disabled device %s (couldn't access raid superblock)\n", kdevname(realdev->dev));
+			continue;
+		}
+
+		/*
+		 * This is important -- we are using the descriptor on
+		 * the disk only to get a pointer to the descriptor on
+		 * the main superblock, which might be more recent.
+		 */
+		descriptor = &sb->disks[realdev->sb->descriptor.number];
+		if (descriptor->state & (1 << MD_FAULTY_DEVICE)) {
+			printk(KERN_ERR "raid5: disabled device %s (errors detected)\n", kdevname(realdev->dev));
+			continue;
+		}
+		if (descriptor->state & (1 << MD_ACTIVE_DEVICE)) {
+			if (!(descriptor->state & (1 << MD_SYNC_DEVICE))) {
+				printk(KERN_ERR "raid5: disabled device %s (not in sync)\n", kdevname(realdev->dev));
+				continue;
+			}
+			raid_disk = descriptor->raid_disk;
+			if (descriptor->number > sb->nr_disks || raid_disk > sb->raid_disks) {
+				printk(KERN_ERR "raid5: disabled device %s (inconsistent descriptor)\n", kdevname(realdev->dev));
+				continue;
+			}
+			if (raid_conf->disks[raid_disk].operational) {
+				printk(KERN_ERR "raid5: disabled device %s (device %d already operational)\n", kdevname(realdev->dev), raid_disk);
+				continue;
+			}
+			printk(KERN_INFO "raid5: device %s operational as raid disk %d\n", kdevname(realdev->dev), raid_disk);
+	
+			raid_conf->disks[raid_disk].number = descriptor->number;
+			raid_conf->disks[raid_disk].raid_disk = raid_disk;
+			raid_conf->disks[raid_disk].dev = mddev->devices[i].dev;
+			raid_conf->disks[raid_disk].operational = 1;
+
+			raid_conf->working_disks++;
+		} else {
+			/*
+			 * Must be a spare disk ..
+			 */
+			printk(KERN_INFO "raid5: spare disk %s\n", kdevname(realdev->dev));
+			raid_disk = descriptor->raid_disk;
+			raid_conf->disks[raid_disk].number = descriptor->number;
+			raid_conf->disks[raid_disk].raid_disk = raid_disk;
+			raid_conf->disks[raid_disk].dev = mddev->devices [i].dev;
+
+			raid_conf->disks[raid_disk].operational = 0;
+			raid_conf->disks[raid_disk].write_only = 0;
+			raid_conf->disks[raid_disk].spare = 1;
+		}
+	}
+	raid_conf->raid_disks = sb->raid_disks;
+	raid_conf->failed_disks = raid_conf->raid_disks - raid_conf->working_disks;
+	raid_conf->mddev = mddev;
+	raid_conf->chunk_size = sb->chunk_size;
+	raid_conf->level = sb->level;
+	raid_conf->algorithm = sb->parity_algorithm;
+	raid_conf->max_nr_stripes = NR_STRIPES;
+
+	if (raid_conf->working_disks != sb->raid_disks && sb->state != (1 << MD_SB_CLEAN)) {
+		printk(KERN_ALERT "raid5: raid set %s not clean and not all disks are operational -- run ckraid\n", kdevname(MKDEV(MD_MAJOR, minor)));
+		goto abort;
+	}
+	if (!raid_conf->chunk_size || raid_conf->chunk_size % 4) {
+		printk(KERN_ERR "raid5: invalid chunk size %d for %s\n", raid_conf->chunk_size, kdevname(MKDEV(MD_MAJOR, minor)));
+		goto abort;
+	}
+	if (raid_conf->algorithm > ALGORITHM_RIGHT_SYMMETRIC) {
+		printk(KERN_ERR "raid5: unsupported parity algorithm %d for %s\n", raid_conf->algorithm, kdevname(MKDEV(MD_MAJOR, minor)));
+		goto abort;
+	}
+	if (raid_conf->failed_disks > 1) {
+		printk(KERN_ERR "raid5: not enough operational devices for %s (%d/%d failed)\n", kdevname(MKDEV(MD_MAJOR, minor)), raid_conf->failed_disks, raid_conf->raid_disks);
+		goto abort;
+	}
+
+	if ((sb->state & (1 << MD_SB_CLEAN)) && check_consistency(mddev)) {
+		printk(KERN_ERR "raid5: detected raid-5 xor inconsistenty -- run ckraid\n");
+		sb->state |= 1 << MD_SB_ERRORS;
+		goto abort;
+	}
+
+	if ((raid_conf->thread = md_register_thread(raid5d, raid_conf)) == NULL) {
+		printk(KERN_ERR "raid5: couldn't allocate thread for %s\n", kdevname(MKDEV(MD_MAJOR, minor)));
+		goto abort;
+	}
+
+#if SUPPORT_RECONSTRUCTION
+	if ((raid_conf->resync_thread = md_register_thread(raid5syncd, raid_conf)) == NULL) {
+		printk(KERN_ERR "raid5: couldn't allocate thread for %s\n", kdevname(MKDEV(MD_MAJOR, minor)));
+		goto abort;
+	}
+#endif /* SUPPORT_RECONSTRUCTION */
+
+	memory = raid_conf->max_nr_stripes * (sizeof(struct stripe_head) +
+		 raid_conf->raid_disks * (sizeof(struct buffer_head) +
+		 2 * (sizeof(struct buffer_head) + PAGE_SIZE))) / 1024;
+	if (grow_stripes(raid_conf, raid_conf->max_nr_stripes, GFP_KERNEL)) {
+		printk(KERN_ERR "raid5: couldn't allocate %dkB for buffers\n", memory);
+		shrink_stripes(raid_conf, raid_conf->max_nr_stripes);
+		goto abort;
+	} else
+		printk(KERN_INFO "raid5: allocated %dkB for %s\n", memory, kdevname(MKDEV(MD_MAJOR, minor)));
+
+	/*
+	 * Regenerate the "device is in sync with the raid set" bit for
+	 * each device.
+	 */
+	for (i = 0; i < sb->nr_disks ; i++) {
+		sb->disks[i].state &= ~(1 << MD_SYNC_DEVICE);
+		for (j = 0; j < sb->raid_disks; j++) {
+			if (!raid_conf->disks[j].operational)
+				continue;
+			if (sb->disks[i].number == raid_conf->disks[j].number)
+				sb->disks[i].state |= 1 << MD_SYNC_DEVICE;
+		}
+	}
+	sb->active_disks = raid_conf->working_disks;
+
+	if (sb->active_disks == sb->raid_disks)
+		printk("raid5: raid level %d set %s active with %d out of %d devices, algorithm %d\n", raid_conf->level, kdevname(MKDEV(MD_MAJOR, minor)), sb->active_disks, sb->raid_disks, raid_conf->algorithm);
+	else
+		printk(KERN_ALERT "raid5: raid level %d set %s active with %d out of %d devices, algorithm %d\n", raid_conf->level, kdevname(MKDEV(MD_MAJOR, minor)), sb->active_disks, sb->raid_disks, raid_conf->algorithm);
+
+	if ((sb->state & (1 << MD_SB_CLEAN)) == 0) {
+		printk("raid5: raid set %s not clean; re-constructing parity\n", kdevname(MKDEV(MD_MAJOR, minor)));
+		raid_conf->resync_parity = 1;
+#if SUPPORT_RECONSTRUCTION
+		md_wakeup_thread(raid_conf->resync_thread);
+#endif /* SUPPORT_RECONSTRUCTION */
+	}
+
+	/* Ok, everything is just fine now */
+	return (0);
+abort:
+	if (raid_conf) {
+		if (raid_conf->stripe_hashtbl)
+			free_pages((unsigned long) raid_conf->stripe_hashtbl, HASH_PAGES_ORDER);
+		kfree(raid_conf);
+	}
+	mddev->private = NULL;
+	printk(KERN_ALERT "raid5: failed to run raid set %s\n", kdevname(MKDEV(MD_MAJOR, minor)));
+	MOD_DEC_USE_COUNT;
+	return -EIO;
+}
+
+static int raid5_stop (int minor, struct md_dev *mddev)
+{
+	struct raid5_data *raid_conf = (struct raid5_data *) mddev->private;
+
+	shrink_stripe_cache(raid_conf, raid_conf->max_nr_stripes);
+	shrink_stripes(raid_conf, raid_conf->max_nr_stripes);
+	md_unregister_thread(raid_conf->thread);
+#if SUPPORT_RECONSTRUCTION
+	md_unregister_thread(raid_conf->resync_thread);
+#endif /* SUPPORT_RECONSTRUCTION */
+	free_pages((unsigned long) raid_conf->stripe_hashtbl, HASH_PAGES_ORDER);
+	kfree(raid_conf);
+	mddev->private = NULL;
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+static int raid5_status (char *page, int minor, struct md_dev *mddev)
+{
+	struct raid5_data *raid_conf = (struct raid5_data *) mddev->private;
+	md_superblock_t *sb = mddev->sb;
+	int sz = 0, i;
+
+	sz += sprintf (page+sz, " level %d, %dk chunk, algorithm %d", sb->level, sb->chunk_size >> 10, sb->parity_algorithm);
+	sz += sprintf (page+sz, " [%d/%d] [", raid_conf->raid_disks, raid_conf->working_disks);
+	for (i = 0; i < raid_conf->raid_disks; i++)
+		sz += sprintf (page+sz, "%s", raid_conf->disks[i].operational ? "U" : "_");
+	sz += sprintf (page+sz, "]");
+	return sz;
+}
+
+static int raid5_mark_spare(struct md_dev *mddev, md_descriptor_t *spare, int state)
+{
+	int i = 0, failed_disk = -1;
+	struct raid5_data *raid_conf = mddev->private;
+	struct disk_info *disk = raid_conf->disks;
+	unsigned long flags;
+	md_superblock_t *sb = mddev->sb;
+	md_descriptor_t *descriptor;
+
+	for (i = 0; i < MD_SB_DISKS; i++, disk++) {
+		if (disk->spare && disk->number == spare->number)
+			goto found;
+	}
+	return 1;
+found:
+	for (i = 0, disk = raid_conf->disks; i < raid_conf->raid_disks; i++, disk++)
+		if (!disk->operational)
+			failed_disk = i;
+	if (failed_disk == -1)
+		return 1;
+	save_flags(flags);
+	cli();
+	switch (state) {
+		case SPARE_WRITE:
+			disk->operational = 1;
+			disk->write_only = 1;
+			raid_conf->spare = disk;
+			break;
+		case SPARE_INACTIVE:
+			disk->operational = 0;
+			disk->write_only = 0;
+			raid_conf->spare = NULL;
+			break;
+		case SPARE_ACTIVE:
+			disk->spare = 0;
+			disk->write_only = 0;
+
+			descriptor = &sb->disks[raid_conf->disks[failed_disk].number];
+			i = spare->raid_disk;
+			disk->raid_disk = spare->raid_disk = descriptor->raid_disk;
+			if (disk->raid_disk != failed_disk)
+				printk("raid5: disk->raid_disk != failed_disk");
+			descriptor->raid_disk = i;
+
+			raid_conf->spare = NULL;
+			raid_conf->working_disks++;
+			raid_conf->failed_disks--;
+			raid_conf->disks[failed_disk] = *disk;
+			break;
+		default:
+			printk("raid5_mark_spare: bug: state == %d\n", state);
+			restore_flags(flags);
+			return 1;
+	}
+	restore_flags(flags);
+	return 0;
+}
+
+static struct md_personality raid5_personality=
+{
+	"raid5",
+	raid5_map,
+	raid5_make_request,
+	raid5_end_request,
+	raid5_run,
+	raid5_stop,
+	raid5_status,
+	NULL,			/* no ioctls */
+	0,
+	raid5_error,
+	/* raid5_hot_add_disk, */ NULL,
+	/* raid1_hot_remove_drive */ NULL,
+	raid5_mark_spare
+};
+
+int raid5_init (void)
+{
+	return register_md_personality (RAID5, &raid5_personality);
+}
+
+#ifdef MODULE
+int init_module (void)
+{
+	return raid5_init();
+}
+
+void cleanup_module (void)
+{
+	unregister_md_personality (RAID5);
+}
+#endif
diff -u --recursive --new-file v2.1.62/linux/drivers/char/Config.in linux/drivers/char/Config.in
--- v2.1.62/linux/drivers/char/Config.in	Thu Sep 11 09:02:23 1997
+++ linux/drivers/char/Config.in	Tue Nov  4 10:23:25 1997
@@ -109,4 +109,11 @@
 fi
 tristate '/dev/nvram support' CONFIG_NVRAM
 tristate 'PC joystick support' CONFIG_JOYSTICK
+bool 'Radio Device Support' CONFIG_MISC_RADIO
+if [ "$CONFIG_MISC_RADIO" != "n" ]; then
+  bool '  AIMSlab RadioTrack (aka RadioReveal) support' CONFIG_RADIO_RTRACK
+  if [ "$CONFIG_RADIO_RTRACK" != "n" ]; then
+    hex '    RadioTrack i/o port (0x20f or 0x30f)' CONFIG_RADIO_RTRACK_PORT 0x20f
+  fi
+fi
 endmenu
diff -u --recursive --new-file v2.1.62/linux/drivers/char/Makefile linux/drivers/char/Makefile
--- v2.1.62/linux/drivers/char/Makefile	Sun Sep  7 13:10:42 1997
+++ linux/drivers/char/Makefile	Tue Nov  4 10:23:25 1997
@@ -281,6 +281,13 @@
   endif
 endif
 
+ifeq ($(CONFIG_MISC_RADIO),y)
+L_OBJS += radio.o
+  ifeq ($(CONFIG_RADIO_RTRACK),y)
+  L_OBJS += rtrack.o
+  endif
+endif
+
 ifeq ($(CONFIG_QIC02_TAPE),y)
 L_OBJS += tpqic02.o
 else
diff -u --recursive --new-file v2.1.62/linux/drivers/char/README.cycladesZ linux/drivers/char/README.cycladesZ
--- v2.1.62/linux/drivers/char/README.cycladesZ	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/README.cycladesZ	Tue Nov 11 11:04:15 1997
@@ -0,0 +1,8 @@
+
+The Cyclades-Z must have firmware loaded onto the card before it will
+operate.  This operation should be performed during system startup,
+
+The firmware, loader program and the latest device driver code are
+available from Cyclades at
+    ftp://ftp.cyclades.com/pub/cyclades/cyclades-z/linux/
+
diff -u --recursive --new-file v2.1.62/linux/drivers/char/busmouse.c linux/drivers/char/busmouse.c
--- v2.1.62/linux/drivers/char/busmouse.c	Tue Sep 23 16:48:47 1997
+++ linux/drivers/char/busmouse.c	Mon Nov  3 15:14:20 1997
@@ -159,8 +159,8 @@
  * writes are disallowed
  */
 
-static long write_mouse(struct inode * inode, struct file * file,
-	const char * buffer, unsigned long count)
+static ssize_t write_mouse(struct file * file,
+	const char * buffer, size_t count, loff_t *ppos)
 {
 	return -EINVAL;
 }
@@ -169,8 +169,8 @@
  * read mouse data.  Currently never blocks.
  */
 
-static long read_mouse(struct inode * inode, struct file * file,
-	char * buffer, unsigned long count)
+static ssize_t read_mouse(struct file * file,
+	char * buffer, size_t count, loff_t *ppos)
 {
 	int r;
 	int dx;
diff -u --recursive --new-file v2.1.62/linux/drivers/char/cyclades.c linux/drivers/char/cyclades.c
--- v2.1.62/linux/drivers/char/cyclades.c	Wed Sep 24 20:05:46 1997
+++ linux/drivers/char/cyclades.c	Tue Nov 11 11:04:15 1997
@@ -1,6 +1,6 @@
 #define BLOCKMOVE
 static char rcsid[] =
-"$Revision: 1.36.4.33 $$Date: 1997/06/27 19:00:00 $";
+"$Revision: 2.1 $$Date: 1997/11/01 17:42:41 $";
 
 /*
  *  linux/drivers/char/cyclades.c
@@ -8,8 +8,9 @@
  * This file contains the driver for the Cyclades Cyclom-Y multiport
  * serial boards.
  *
- * Maintained by Marcio Saito (marcio@cyclades.com) and
- * Randolph Bentson (bentson@grieg.seaslug.org)
+ * Maintained by Ivan Passos (ivan@cyclades.com),
+ * Marcio Saito (marcio@cyclades.com) and 
+ * Randolph Bentson (bentson@grieg.seaslug.org).
  *
  * For Technical support and installation problems, please send e-mail
  * to support@cyclades.com.
@@ -29,6 +30,18 @@
  *   void cleanup_module(void);
  *
  * $Log: cyclades.c,v $
+ * Revision 2.1  1997/11/01 17:42:41 ivan
+ * Changes in the driver to support Alpha systems (except 8Zo V_1);
+ * BREAK fix for the Cyclades-Z boards;
+ * driver inactivity control by FW implemented;
+ * introduction of flag that allows driver to take advantage of 
+ * a special CD1400 feature related to HW flow control;
+ * added support for the CD1400  rev. J (Cyclom-Y boards);
+ * introduction of ioctls to:
+ *  - control the rtsdtr_inv flag (Cyclom-Y);
+ *  - control the rflow flag (Cyclom-Y);
+ *  - adjust the polling interval (Cyclades-Z);
+ *
  * Revision 1.36.4.33  1997/06/27 19:00:00  ivan
  * Fixes related to kernel version conditional 
  * compilation.
@@ -435,7 +448,6 @@
    constant in the definition below. No other change is necessary to
    support more boards/ports. */
 
-//#define NR_PORTS        64
 #define NR_PORTS        128
 
 #define ZE_V1_NPORTS	64
@@ -463,18 +475,24 @@
 
 #define cy_min(a,b) (((a)<(b))?(a):(b))
 
+#if 0
+/******** 
+ * For the next two macros, it is assumed that the buffer size is a 
+ * power of 2 
+ ********/
+
 #define CHARS_IN_BUF(buf_ctrl) \
-    ((buf_ctrl->rx_put - \
-      buf_ctrl->rx_get + \
-      buf_ctrl->rx_bufsize) % \
-		  buf_ctrl->rx_bufsize)
+    ((cy_readl(&buf_ctrl->rx_put) - \
+      cy_readl(&buf_ctrl->rx_get) + \
+      cy_readl(&buf_ctrl->rx_bufsize)) & \
+		  (cy_readl(&buf_ctrl->rx_bufsize) - 1))
 
 #define SPACE_IN_BUF(buf_ctrl) \
-    ((buf_ctrl->tx_get - \
-      buf_ctrl->tx_put + \
-      buf_ctrl->tx_bufsize - 1) % \
-		  buf_ctrl->tx_bufsize)
-
+    ((cy_readl(&buf_ctrl->tx_get) - \
+      cy_readl(&buf_ctrl->tx_put) + \
+      cy_readl(&buf_ctrl->tx_bufsize) - 1) & \
+		  (cy_readl(&buf_ctrl->tx_bufsize) - 1))
+#endif
 
 #include <linux/module.h>
 
@@ -540,13 +558,14 @@
 #define IS_CYC_Z(card) ((card).num_chips == 1)
 
 #define Z_FPGA_CHECK(card) \
-    ((((struct RUNTIME_9060 *)((card).ctl_addr))->init_ctrl&(1<<17))!=0)
+    ((cy_readl(&((struct RUNTIME_9060 *) \
+		 ((card).ctl_addr))->init_ctrl) & (1<<17)) != 0)
 
-#define ISZLOADED(card)	(((ZO_V1==((struct RUNTIME_9060 *) \
-			((card).ctl_addr))->mail_box_0) || \
+#define ISZLOADED(card)	(((ZO_V1==cy_readl(&((struct RUNTIME_9060 *) \
+			((card).ctl_addr))->mail_box_0)) || \
 			Z_FPGA_CHECK(card)) && \
-			(ZFIRM_ID==((struct FIRM_ID *) \
-			((card).base_addr+ID_ADDRESS))->signature))
+			(ZFIRM_ID==cy_readl(&((struct FIRM_ID *) \
+			((card).base_addr+ID_ADDRESS))->signature)))
 
 #define WAKEUP_CHARS (SERIAL_XMIT_SIZE-256)
 
@@ -563,7 +582,7 @@
 static volatile int cy_irq_triggered;
 static volatile int cy_triggered;
 static int cy_wild_int_mask;
-static unsigned char *intr_base_addr;
+static volatile ucchar *intr_base_addr;
 
 
 /* This is the address lookup table. The driver will probe for
@@ -632,26 +651,55 @@
  *      0,     1,     2,     3,     4,     5,     6,     7,     8,     9,
  *     10,    11,    12,    13,    14,    15,    16,    17,    18,    19,
  *                                               HI            VHI
+ *     20
  */
 static int baud_table[] = {
        0,    50,    75,   110,   134,   150,   200,   300,   600,  1200,
     1800,  2400,  4800,  9600, 19200, 38400, 57600, 76800,115200,150000,
-    0};
+  230400,     0};
 
-static char baud_co[] = {  /* 25 MHz clock option table */
+static char baud_co_25[] = {  /* 25 MHz clock option table */
     /* value =>    00    01   02    03    04 */
     /* divide by    8    32   128   512  2048 */
     0x00,  0x04,  0x04,  0x04,  0x04,  0x04,  0x03,  0x03,  0x03,  0x02,
     0x02,  0x02,  0x01,  0x01,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00};
 
-static char baud_bpr[] = {  /* 25 MHz baud rate period table */
+static char baud_bpr_25[] = {  /* 25 MHz baud rate period table */
     0x00,  0xf5,  0xa3,  0x6f,  0x5c,  0x51,  0xf5,  0xa3,  0x51,  0xa3,
     0x6d,  0x51,  0xa3,  0x51,  0xa3,  0x51,  0x36,  0x29,  0x1b,  0x15};
 
+static char baud_co_60[] = {  /* 60 MHz clock option table (CD1400 J) */
+    /* value =>    00    01   02    03    04 */
+    /* divide by    8    32   128   512  2048 */
+    0x00,  0x00,  0x00,  0x04,  0x04,  0x04,  0x04,  0x04,  0x03,  0x03,
+    0x03,  0x02,  0x02,  0x01,  0x01,  0x00,  0x00,  0x00,  0x00,  0x00,
+    0x00};
+
+static char baud_bpr_60[] = {  /* 60 MHz baud rate period table (CD1400 J) */
+    0x00,  0x82,  0x21,  0xff,  0xdb,  0xc3,  0x92,  0x62,  0xc3,  0x62,
+    0x41,  0xc3,  0x62,  0xc3,  0x62,  0xc3,  0x82,  0x62,  0x41,  0x32,
+    0x21};
+
 static char baud_cor3[] = {  /* receive threshold */
     0x0a,  0x0a,  0x0a,  0x0a,  0x0a,  0x0a,  0x0a,  0x0a,  0x0a,  0x0a,
-    0x0a,  0x0a,  0x0a,  0x09,  0x09,  0x08,  0x08,  0x08,  0x08,  0x07};
+    0x0a,  0x0a,  0x0a,  0x09,  0x09,  0x08,  0x08,  0x08,  0x08,  0x07,
+    0x07};
 
+/*
+ * The Cyclades driver implements HW flow control as any serial driver.
+ * The cyclades_port structure member rflow and the vector rflow_thr 
+ * allows us to take advantage of a special feature in the CD1400 to avoid 
+ * data loss even when the system interrupt latency is too high. These flags 
+ * are to be used only with very special applications. Setting these flags 
+ * requires the use of a special cable (DTR and RTS reversed). In the new 
+ * CD1400-based boards (rev. 6.00 or later), there is no need for special 
+ * cables.
+ */
+
+static char rflow_thr[] = {  /* rflow threshold */
+    0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,
+    0x00,  0x00,  0x00,  0x0a,  0x0a,  0x0a,  0x0a,  0x0a,  0x0a,  0x0a,
+    0x0a};
 
 /*  The Cyclom-Ye has placed the sequential chips in non-sequential
  *  address order.  This look-up table overcomes that problem.
@@ -689,6 +737,8 @@
 static void show_status(int);
 #endif
 
+/* The Cyclades-Z polling cycle is defined by this variable */
+static long cyz_polling_cycle = CZ_DEF_POLL;
 
 static int cyz_timeron = 0;
 static struct timer_list
@@ -696,13 +746,12 @@
     NULL, NULL, 0, 0, cyz_poll
 };
 
-
 /**************************************************
 error = verify_area(VERIFY_WRITE, (void *) arg, sizeof(unsigned long));
-memcpy_tofs  (to, from, count);
+copy_to_user  (to, from, count);
 ***************************************************************
 error = verify_area(VERIFY_READ,  (void *) arg, sizeof(unsigned long *));
-memcpy_fromfs(to, from, count);
+copy_from_user(to, from, count);
 **************************************************/
 
 
@@ -873,7 +922,7 @@
    didn't finish within the time limit.
  */
 static int
-cyy_issue_cmd(u_char *base_addr, u_char cmd, int index)
+cyy_issue_cmd(volatile ucchar *base_addr, u_char cmd, int index)
 {
   unsigned long flags;
   volatile int  i;
@@ -881,7 +930,7 @@
     save_flags(flags); cli();
         /* Check to see that the previous command has completed */
         for(i = 0 ; i < 100 ; i++){
-            if (base_addr[CyCCR<<index] == 0){
+            if (cy_readb(base_addr+(CyCCR<<index)) == 0){
                 break;
             }
             udelay(10L);
@@ -894,7 +943,7 @@
         }
 
         /* Issue the new command */
-        base_addr[CyCCR<<index] = cmd;
+        cy_writeb((u_long)base_addr+(CyCCR<<index), cmd);
     restore_flags(flags);
     return(0);
 } /* cyy_issue_cmd */
@@ -932,8 +981,9 @@
   int i;
     
     for (i = 0; i < 16; i++) {
-        if (irq_lines & (1 << i))
+        if (irq_lines & (1 << i)) {
             free_irq(i,NULL);
+	}
     }
 } /* free_all_interrupts */
 
@@ -959,13 +1009,13 @@
          * Delay for 0.1 seconds -- we use a busy loop since this may 
          * occur during the bootup sequence
          */
-        timeout = jiffies+10;
+        timeout = jiffies+(HZ/10);
         while (timeout >= jiffies)
             ;
         
         cy_triggered = 0;       /* Reset after letting things settle */
 
-        timeout = jiffies+10;
+        timeout = jiffies+(HZ/10);
         while (timeout >= jiffies)
                 ;
         
@@ -986,11 +1036,11 @@
  * fool-proof, but it works a large part of the time.
  */
 static int
-get_auto_irq(unsigned char *address)
+get_auto_irq(volatile ucchar *address)
 {
-  unsigned long timeout;
-  unsigned char *base_addr;
-  int           index;
+  unsigned long   	timeout;
+  volatile ucchar 	*base_addr;
+  int           	index;
 
     index = 0;  /* IRQ probing is only for ISA */
     base_addr = address;
@@ -1001,13 +1051,14 @@
      */
     cy_irq_triggered = 0;
     cli();
-        base_addr[CyCAR<<index] = 0;
+        cy_writeb((u_long)base_addr+(CyCAR<<index), 0);
         cyy_issue_cmd(base_addr,CyCHAN_CTL|CyENB_XMTR,index);
-        base_addr[CySRER<<index] |= CyTxMpty;
+        cy_writeb((u_long)base_addr+(CySRER<<index), 
+             cy_readb(base_addr+(CySRER<<index)) | CyTxMpty);
         probe_ready = 1;
     sti();
     
-    timeout = jiffies+2;
+    timeout = jiffies+(HZ/50);
     while (timeout >= jiffies) {
         if (cy_irq_triggered)
             break;
@@ -1021,7 +1072,7 @@
  * faked out by random interrupts
  */
 static int
-do_auto_irq(unsigned char *address)
+do_auto_irq(volatile ucchar *address)
 {
   int                   irq_lines = 0;
   int                   irq_try_1 = 0, irq_try_2 = 0;
@@ -1065,27 +1116,28 @@
   int index = 0;        /* probing interrupts is only for ISA */
 
     if (!probe_ready) {
-        *(intr_base_addr + (Cy_ClrIntr<<index)) = 0;
+        cy_writeb((u_long)intr_base_addr+(Cy_ClrIntr<<index), 0);
         return;
     }
 
     cy_irq_triggered = irq;
     cy_triggered |= 1 << irq;
 
-        if(intr_base_addr[CySVRR<<index] != 0) {
-            save_xir = (u_char) intr_base_addr[CyTIR<<index];
-            save_car = intr_base_addr[CyCAR<<index];
+        if(cy_readb(intr_base_addr+(CySVRR<<index)) != 0) {
+            save_xir = (u_char) cy_readb(intr_base_addr+(CyTIR<<index));
+            save_car = cy_readb(intr_base_addr+(CyCAR<<index));
             if ((save_xir & 0x3) != 0){
                 SP("channel ");
                 CP8(save_xir);
                 SP(" requesting unexpected interrupt\n");
             }
-            intr_base_addr[CyCAR<<index] = (save_xir & 0x3);
-            intr_base_addr[CySRER<<index] &= ~CyTxMpty;
-            intr_base_addr[CyTIR<<index] = (save_xir & 0x3f);
-            intr_base_addr[CyCAR<<index] = (save_car);
+            cy_writeb((u_long)intr_base_addr+(CyCAR<<index), (save_xir & 0x3));
+            cy_writeb((u_long)intr_base_addr+(CySRER<<index),
+                cy_readb(intr_base_addr+(CySRER<<index)) & ~CyTxMpty);
+            cy_writeb((u_long)intr_base_addr+(CyTIR<<index), (save_xir & 0x3f));
+            cy_writeb((u_long)intr_base_addr+(CyCAR<<index), (save_car));
         }
-        *(intr_base_addr + (Cy_ClrIntr<<index)) = 0;
+        cy_writeb((u_long)intr_base_addr+(Cy_ClrIntr<<index), 0);
 	                          /* Cy_ClrIntr is 0x1800 */
     return;
 } /* cy_probe */
@@ -1105,7 +1157,7 @@
   int chip;
   int save_xir, channel, save_car;
   char data;
-  int char_count;
+  volatile int char_count;
   int outch;
   int i,j,index;
   int too_many;
@@ -1114,12 +1166,16 @@
   int mdm_status;
 
     if((cinfo = IRQ_cards[irq]) == 0){
+#ifdef CY_DEBUG_INTERRUPTS
+printk("cy_interrupt: spurious interrupt %d\n\r", irq);
+#endif
         return; /* spurious interrupt */
     }
 
     card_base_addr = (unsigned char *)cinfo->base_addr;
     index = cinfo->bus_index;
 
+
     /* This loop checks all chips in the card.  Make a note whenever
        _any_ chip had some work to do, as this is considered an
        indication that there will be more to do.  Only when no chip
@@ -1131,7 +1187,7 @@
             base_addr = (unsigned char *)
 		       (cinfo->base_addr + (cy_chip_offset[chip]<<index));
             too_many = 0;
-            while ( (status = base_addr[CySVRR<<index]) != 0x00) {
+            while ( (status = cy_readb(base_addr+(CySVRR<<index))) != 0x00) {
                 had_work++;
                 /* The purpose of the following test is to ensure that
                    no chip can monopolize the driver.  This forces the
@@ -1142,31 +1198,34 @@
                     break;
                 }
                 if (status & CySRReceive) { /* reception interrupt */
+#ifdef CY_DEBUG_INTERRUPTS
+printk("cy_interrupt: rcvd intr, chip %d\n\r", chip);
+#endif
                     /* determine the channel & change to that context */
-                    save_xir = (u_char) base_addr[CyRIR<<index];
+                    save_xir = (u_char) cy_readb(base_addr+(CyRIR<<index));
                     channel = (u_short ) (save_xir & CyIRChannel);
                     i = channel + chip * 4 + cinfo->first_line;
                     info = &cy_port[i];
                     info->last_active = jiffies;
-                    save_car = base_addr[CyCAR<<index];
-                    base_addr[CyCAR<<index] = save_xir;
+                    save_car = cy_readb(base_addr+(CyCAR<<index));
+                    cy_writeb((u_long)base_addr+(CyCAR<<index), save_xir);
 
                     /* if there is nowhere to put the data, discard it */
                     if(info->tty == 0){
-                        j = (base_addr[CyRIVR<<index] & CyIVRMask);
+                        j = (cy_readb(base_addr+(CyRIVR<<index)) & CyIVRMask);
                         if ( j == CyIVRRxEx ) { /* exception */
-                            data = base_addr[CyRDSR<<index];
+                            data = cy_readb(base_addr+(CyRDSR<<index));
                         } else { /* normal character reception */
-                            char_count = base_addr[CyRDCR<<index];
+                            char_count = cy_readb(base_addr+(CyRDCR<<index));
                             while(char_count--){
-                                data = base_addr[CyRDSR<<index];
+                                data = cy_readb(base_addr+(CyRDSR<<index));
                             }
                         }
                     }else{ /* there is an open port for this data */
                         tty = info->tty;
-                        j = (base_addr[CyRIVR<<index] & CyIVRMask);
+                        j = (cy_readb(base_addr+(CyRIVR<<index)) & CyIVRMask);
                         if ( j == CyIVRRxEx ) { /* exception */
-                            data = base_addr[CyRDSR<<index];
+                            data = cy_readb(base_addr+(CyRDSR<<index));
                             if(data & info->ignore_status_mask){
                                 continue;
                             }
@@ -1177,7 +1236,7 @@
                                         *tty->flip.flag_buf_ptr++ =
 							    TTY_BREAK;
                                         *tty->flip.char_buf_ptr++ =
-						base_addr[CyRDSR<<index];
+					  cy_readb(base_addr+(CyRDSR<<index));
                                         if (info->flags & ASYNC_SAK){
                                             do_SAK(tty);
                                         }
@@ -1185,12 +1244,12 @@
                                         *tty->flip.flag_buf_ptr++ =
 							    TTY_FRAME;
                                         *tty->flip.char_buf_ptr++ =
-						base_addr[CyRDSR<<index];
+					  cy_readb(base_addr+(CyRDSR<<index));
                                     }else if(data & CyPARITY){
                                         *tty->flip.flag_buf_ptr++ =
 							    TTY_PARITY;
                                         *tty->flip.char_buf_ptr++ =
-						base_addr[CyRDSR<<index];
+					  cy_readb(base_addr+(CyRDSR<<index));
                                     }else if(data & CyOVERRUN){
                                         *tty->flip.flag_buf_ptr++ =
 							    TTY_OVERRUN;
@@ -1204,8 +1263,8 @@
                                             tty->flip.count++;
                                             *tty->flip.flag_buf_ptr++ =
 							     TTY_NORMAL;
-                                            *tty->flip.char_buf_ptr++ =
-						base_addr[CyRDSR<<index];
+                                           *tty->flip.char_buf_ptr++ =
+					    cy_readb(base_addr+(CyRDSR<<index));
                                         }
                                     /* These two conditions may imply */
                                     /* a normal read should be done. */
@@ -1226,7 +1285,7 @@
                             }
                         } else { /* normal character reception */
                             /* load # chars available from the chip */
-                            char_count = base_addr[CyRDCR<<index];
+                            char_count = cy_readb(base_addr+(CyRDCR<<index));
 
 #ifdef CYCLOM_ENABLE_MONITORING
                             ++info->mon.int_count;
@@ -1240,7 +1299,7 @@
                                         break;
                                 }
                                 tty->flip.count++;
-                                data = base_addr[CyRDSR<<index];
+                                data = cy_readb(base_addr+(CyRDSR<<index));
                                 *tty->flip.flag_buf_ptr++ = TTY_NORMAL;
                                 *tty->flip.char_buf_ptr++ = data;
 #ifdef CYCLOM_16Y_HACK
@@ -1251,8 +1310,8 @@
                         queue_task(&tty->flip.tqueue, &tq_timer);
                     }
                     /* end of service */
-                    base_addr[CyRIR<<index] = (save_xir & 0x3f);
-                    base_addr[CyCAR<<index] = (save_car);
+                    cy_writeb((u_long)base_addr+(CyRIR<<index), (save_xir & 0x3f));
+                    cy_writeb((u_long)base_addr+(CyCAR<<index), (save_car));
                 }
 
 
@@ -1260,23 +1319,28 @@
                     /* Since we only get here when the transmit buffer
                        is empty, we know we can always stuff a dozen
                        characters. */
+#ifdef CY_DEBUG_INTERRUPTS
+printk("cy_interrupt: xmit intr, chip %d\n\r", chip);
+#endif
 
                     /* determine the channel & change to that context */
-                    save_xir = (u_char) base_addr[CyTIR<<index];
+                    save_xir = (u_char) cy_readb(base_addr+(CyTIR<<index));
                     channel = (u_short ) (save_xir & CyIRChannel);
                     i = channel + chip * 4 + cinfo->first_line;
-                    save_car = base_addr[CyCAR<<index];
-                    base_addr[CyCAR<<index] = save_xir;
+                    save_car = cy_readb(base_addr+(CyCAR<<index));
+                    cy_writeb((u_long)base_addr+(CyCAR<<index), save_xir);
 
                     /* validate the port# (as configured and open) */
                     if( (i < 0) || (NR_PORTS <= i) ){
-                        base_addr[CySRER<<index] &= ~CyTxMpty;
+                        cy_writeb((u_long)base_addr+(CySRER<<index),
+                             cy_readb(base_addr+(CySRER<<index)) & ~CyTxMpty);
                         goto txend;
                     }
                     info = &cy_port[i];
                     info->last_active = jiffies;
                     if(info->tty == 0){
-                        base_addr[CySRER<<index] &= ~CyTxMpty;
+                        cy_writeb((u_long)base_addr+(CySRER<<index),
+                             cy_readb(base_addr+(CySRER<<index)) & ~CyTxMpty);
                         goto txdone;
                     }
 
@@ -1286,7 +1350,7 @@
 
                     if(info->x_char) { /* send special char */
                         outch = info->x_char;
-                        base_addr[CyTDR<<index] = outch;
+                        cy_writeb((u_long)base_addr+(CyTDR<<index), outch);
                         char_count--;
                         info->x_char = 0;
                     }
@@ -1300,29 +1364,44 @@
 			    PPR runs at 200 Hz, so the delay is
 			    duration * 200/HZ, and thus a break can
 			    run from 1/100 sec to about 5/4 sec.
+			    For CD1400 J or later, replace the 200 Hz
+			    by 500 Hz.
                          */
-                        base_addr[CyTDR<<index] = 0; /* start break */
-                        base_addr[CyTDR<<index] = 0x81;
-                        base_addr[CyTDR<<index] = 0; /* delay a bit */
-                        base_addr[CyTDR<<index] = 0x82;
-                        base_addr[CyTDR<<index] = info->x_break*200/HZ;
-                        base_addr[CyTDR<<index] = 0; /* finish break */
-                        base_addr[CyTDR<<index] = 0x83;
+			/* start break */
+                        cy_writeb((u_long)base_addr + (CyTDR<<index), 0); 
+                        cy_writeb((u_long)base_addr + (CyTDR<<index), 0x81);
+			/* delay a bit */
+                        cy_writeb((u_long)base_addr + (CyTDR<<index), 0); 
+                        cy_writeb((u_long)base_addr + (CyTDR<<index), 0x82);
+                        if (cy_readb(base_addr + (CyGFRCR<<index)) >= 0x48 ) {
+			    /* It is a CD1400 rev. J or later */
+                            cy_writeb((u_long)base_addr + (CyTDR<<index), 
+				      info->x_break*500/HZ);
+			} else {
+                            cy_writeb((u_long)base_addr + (CyTDR<<index), 
+				      info->x_break*200/HZ);
+			}
+			/* finish break */
+                        cy_writeb((u_long)base_addr + (CyTDR<<index), 0); 
+                        cy_writeb((u_long)base_addr + (CyTDR<<index), 0x83);
                         char_count -= 7;
                         info->x_break = 0;
                     }
 
                     while (char_count-- > 0){
                         if (!info->xmit_cnt){
-                            base_addr[CySRER<<index] &= ~CyTxMpty;
+                            cy_writeb((u_long)base_addr+(CySRER<<index),
+                               cy_readb(base_addr+(CySRER<<index)) & ~CyTxMpty);
                             goto txdone;
                         }
                         if (info->xmit_buf == 0){
-                            base_addr[CySRER<<index] &= ~CyTxMpty;
+                            cy_writeb((u_long)base_addr+(CySRER<<index),
+                               cy_readb(base_addr+(CySRER<<index)) & ~CyTxMpty);
                             goto txdone;
                         }
                         if (info->tty->stopped || info->tty->hw_stopped){
-                            base_addr[CySRER<<index] &= ~CyTxMpty;
+                            cy_writeb((u_long)base_addr+(CySRER<<index),
+                               cy_readb(base_addr+(CySRER<<index)) & ~CyTxMpty);
                             goto txdone;
                         }
                         /* Because the Embedded Transmit Commands have
@@ -1340,15 +1419,16 @@
                         if( outch ){
                             info->xmit_cnt--;
                             info->xmit_tail = (info->xmit_tail + 1)
-                                                      & (PAGE_SIZE - 1);
-                            base_addr[CyTDR<<index] = outch;
+                                                      & (SERIAL_XMIT_SIZE - 1);
+                            cy_writeb((u_long)base_addr+(CyTDR<<index), outch);
                         }else{
                             if(char_count > 1){
                                 info->xmit_cnt--;
                                 info->xmit_tail = (info->xmit_tail + 1)
-						      & (PAGE_SIZE - 1);
-                                base_addr[CyTDR<<index] = outch;
-                                base_addr[CyTDR<<index] = 0;
+						      & (SERIAL_XMIT_SIZE - 1);
+                                cy_writeb((u_long)base_addr+(CyTDR<<index), 
+					  outch);
+                                cy_writeb((u_long)base_addr+(CyTDR<<index), 0);
                                 char_count--;
                             }else{
                             }
@@ -1362,23 +1442,24 @@
 
         txend:
                     /* end of service */
-                    base_addr[CyTIR<<index] = (save_xir & 0x3f);
-                    base_addr[CyCAR<<index] = (save_car);
+                    cy_writeb((u_long)base_addr+(CyTIR<<index), 
+			      (save_xir & 0x3f));
+                    cy_writeb((u_long)base_addr+(CyCAR<<index), (save_car));
                 }
 
                 if (status & CySRModem) {        /* modem interrupt */
 
                     /* determine the channel & change to that context */
-                    save_xir = (u_char) base_addr[CyMIR<<index];
+                    save_xir = (u_char) cy_readb(base_addr+(CyMIR<<index));
                     channel = (u_short ) (save_xir & CyIRChannel);
                     info = &cy_port[channel + chip * 4
 		                           + cinfo->first_line];
                     info->last_active = jiffies;
-                    save_car = base_addr[CyCAR<<index];
-                    base_addr[CyCAR<<index] = save_xir;
+                    save_car = cy_readb(base_addr+(CyCAR<<index));
+                    cy_writeb((u_long)base_addr+(CyCAR<<index), save_xir);
 
-                    mdm_change = base_addr[CyMISR<<index];
-                    mdm_status = base_addr[CyMSVR1<<index];
+                    mdm_change = cy_readb(base_addr+(CyMISR<<index));
+                    mdm_status = cy_readb(base_addr+(CyMSVR1<<index));
 
                     if(info->tty == 0){/* no place for data, ignore it*/
                         ;
@@ -1403,7 +1484,9 @@
                                     /* cy_start isn't used
 				         because... !!! */
                                     info->tty->hw_stopped = 0;
-                                    base_addr[CySRER<<index] |= CyTxMpty;
+                                  cy_writeb((u_long)base_addr+(CySRER<<index),
+                                       cy_readb(base_addr+(CySRER<<index)) | 
+                                       CyTxMpty);
                                     cy_sched_event(info,
 				        Cy_EVENT_WRITE_WAKEUP);
                                 }
@@ -1412,8 +1495,9 @@
                                     /* cy_stop isn't used
 				         because ... !!! */
                                     info->tty->hw_stopped = 1;
-                                    base_addr[CySRER<<index] &=
-				                            ~CyTxMpty;
+                                  cy_writeb((u_long)base_addr+(CySRER<<index),
+                                       cy_readb(base_addr+(CySRER<<index)) & 
+                                       ~CyTxMpty);
                                 }
                             }
                         }
@@ -1423,15 +1507,16 @@
                         }
                     }
                     /* end of service */
-                    base_addr[CyMIR<<index] = (save_xir & 0x3f);
-                    base_addr[CyCAR<<index] = save_car;
+                    cy_writeb((u_long)base_addr+(CyMIR<<index), 
+			      (save_xir & 0x3f));
+                    cy_writeb((u_long)base_addr+(CyCAR<<index), save_car);
                 }
             }          /* end while status != 0 */
         }            /* end loop for chips... */
     } while(had_work);
 
    /* clear interrupts */
-   *(card_base_addr + (Cy_ClrIntr<<index)) = 0;
+   cy_writeb((u_long)card_base_addr + (Cy_ClrIntr<<index), 0);
                                 /* Cy_ClrIntr is 0x1800 */
 
 } /* cyy_interrupt */
@@ -1444,7 +1529,7 @@
 
 static int
 cyz_fetch_msg( struct cyclades_card *cinfo,
-	    u_long *channel, u_char *cmd, u_long **param)
+	    uclong *channel, ucchar *cmd, uclong *param)
 {
   struct FIRM_ID *firm_id;
   struct ZFW_CTRL *zfw_ctrl;
@@ -1456,17 +1541,17 @@
 	return (-1);
     }
     zfw_ctrl = (struct ZFW_CTRL *)
-		    (cinfo->base_addr + firm_id->zfwctrl_addr);
+	       (cinfo->base_addr + cy_readl(&firm_id->zfwctrl_addr));
     board_ctrl = &zfw_ctrl->board_ctrl;
 
-    loc_doorbell = ((struct RUNTIME_9060 *)
-                     (cinfo->ctl_addr))->loc_doorbell;
+    loc_doorbell = cy_readl(&((struct RUNTIME_9060 *)
+                     (cinfo->ctl_addr))->loc_doorbell);
     if (loc_doorbell){
 	*cmd = (char)(0xff & loc_doorbell);
-	*channel = board_ctrl->fwcmd_channel;
-	*param = board_ctrl->fwcmd_param;
-	((struct RUNTIME_9060 *)
-	     (cinfo->ctl_addr))->loc_doorbell = 0xffffffff;
+	*channel = cy_readl(&board_ctrl->fwcmd_channel);
+	*param = (uclong)cy_readl(&board_ctrl->fwcmd_param);
+	cy_writel(&((struct RUNTIME_9060 *)(cinfo->ctl_addr))->loc_doorbell, 
+                 0xffffffff);
 	return 1;
     }
     return 0;
@@ -1475,12 +1560,12 @@
 
 static int
 cyz_issue_cmd( struct cyclades_card *cinfo,
-	    u_long channel, u_char cmd, u_long *param)
+	    uclong channel, ucchar cmd, uclong param)
 {
   struct FIRM_ID *firm_id;
   struct ZFW_CTRL *zfw_ctrl;
   struct BOARD_CTRL *board_ctrl;
-  volatile unsigned long *pci_doorbell;
+  volatile uclong *pci_doorbell;
   int index;
 
     firm_id = (struct FIRM_ID *)(cinfo->base_addr + ID_ADDRESS);
@@ -1488,21 +1573,21 @@
 	return (-1);
     }
     zfw_ctrl = (struct ZFW_CTRL *)
-		    (cinfo->base_addr + firm_id->zfwctrl_addr);
+	       (cinfo->base_addr + cy_readl(&firm_id->zfwctrl_addr));
     board_ctrl = &zfw_ctrl->board_ctrl;
 
     index = 0;
-    pci_doorbell = &((struct RUNTIME_9060 *)
-                     (cinfo->ctl_addr))->pci_doorbell;
-    while( (*pci_doorbell & 0xff) != 0){
+    pci_doorbell = (uclong *)(&((struct RUNTIME_9060 *)
+                               (cinfo->ctl_addr))->pci_doorbell);
+    while( (cy_readl(pci_doorbell) & 0xff) != 0){
         if (index++ == 1000){
             return(-1);
         }
 	udelay(50L);
     }
-    board_ctrl->hcmd_channel = channel;
-    board_ctrl->hcmd_param = param;
-    *pci_doorbell = (long)cmd;
+    cy_writel((u_long)&board_ctrl->hcmd_channel, channel);
+    cy_writel((u_long)&board_ctrl->hcmd_param , param);
+    cy_writel((u_long)pci_doorbell, (long)cmd);
 
     return(0);
 } /* cyz_issue_cmd */
@@ -1521,11 +1606,11 @@
     if (!ISZLOADED(*cinfo)){
 	return (-1);
     }
-    zfw_ctrl =
-	(struct ZFW_CTRL *)(cinfo->base_addr + firm_id->zfwctrl_addr);
+    zfw_ctrl = (struct ZFW_CTRL *)
+	       (cinfo->base_addr + cy_readl(&firm_id->zfwctrl_addr));
     ch_ctrl = zfw_ctrl->ch_ctrl;
 
-    ch_ctrl[channel].op_mode = (long)mode;
+    cy_writel(&ch_ctrl[channel].op_mode, (uclong)mode);
 
     return cyz_issue_cmd(cinfo, channel, cmd, 0L);
 
@@ -1542,42 +1627,53 @@
 static void
 cyz_poll(unsigned long arg)
 {
-  struct FIRM_ID *firm_id;
-  struct ZFW_CTRL *zfw_ctrl;
-  struct BOARD_CTRL *board_ctrl;
-  struct CH_CTRL *ch_ctrl;
-  struct BUF_CTRL *buf_ctrl;
+  static volatile struct FIRM_ID *firm_id;
+  static volatile struct ZFW_CTRL *zfw_ctrl;
+  static volatile struct BOARD_CTRL *board_ctrl;
+  static volatile struct CH_CTRL *ch_ctrl;
+  static volatile struct BUF_CTRL *buf_ctrl;
   struct cyclades_card *cinfo;
   struct cyclades_port *info;
   struct tty_struct *tty;
   int card, port;
-  int char_count, small_count;
+  int char_count;
+#ifdef BLOCKMOVE
+  int small_count;
+#endif
   char data;
-  u_long channel;
-  u_char cmd;
-  u_long *param;
-  u_long hw_ver, fw_ver;
-
-    cyz_timerlist.expires = jiffies + 100;
+  uclong channel;
+  ucchar cmd;
+  uclong param;
+  uclong hw_ver, fw_ver;
+  volatile uclong tx_put, tx_get, tx_bufsize;
+  volatile uclong rx_put, rx_get, rx_bufsize;
 
+    cyz_timerlist.expires = jiffies + (HZ);
     for (card = 0 ; card < NR_CARDS ; card++){
 	cinfo = &cy_card[card];
 	if (!IS_CYC_Z(*cinfo)) continue;
 
-	firm_id = (struct FIRM_ID *)
-		    (cinfo->base_addr + ID_ADDRESS);
-        if (!ISZLOADED(*cinfo)){
+
+	firm_id = (struct FIRM_ID *)(cinfo->base_addr + ID_ADDRESS);
+        if (!ISZLOADED(*cinfo)) {
+	    cinfo->inact_ctrl = 0;
 	    continue;
 	}
 
-	zfw_ctrl =
-	    (struct ZFW_CTRL *)
-	    (cinfo->base_addr + firm_id->zfwctrl_addr);
-	board_ctrl = &zfw_ctrl->board_ctrl;
-	fw_ver = board_ctrl->fw_version;
-	hw_ver = ((struct RUNTIME_9060 *)(cinfo->ctl_addr))->mail_box_0;
+	zfw_ctrl = (struct ZFW_CTRL *)
+	           (cinfo->base_addr + cy_readl(&firm_id->zfwctrl_addr));
+	board_ctrl = &(zfw_ctrl->board_ctrl);
+	fw_ver = cy_readl(&board_ctrl->fw_version);
+	hw_ver = cy_readl(&((struct RUNTIME_9060 *)
+                            (cinfo->ctl_addr))->mail_box_0);
+
+	/* Enables the firmware inactivity control */
+	if ((fw_ver > 0x00000310L) && (!cinfo->inact_ctrl)) {
+	    param = cyz_issue_cmd( &cy_card[card], 0L, C_CM_TINACT, 0L);
+	    cinfo->inact_ctrl = 1;
+	}
 
-	while( cyz_fetch_msg( cinfo, &channel, &cmd, &param) == 1){
+	while(cyz_fetch_msg(cinfo, &channel, &cmd, &param) == 1){
 	    char_count = 0;
 	    info = &cy_port[ channel + cinfo->first_line ];
             if((tty = info->tty) == 0) continue;
@@ -1606,9 +1702,9 @@
 	        break;
 	    case C_CM_MDCD:
 		if (info->flags & ASYNC_CHECK_CD){
-		    if (((hw_ver != 0 || fw_ver > 241)
-			? ((u_long)param)
-			: ch_ctrl[channel].rs_status) & C_RS_DCD) {
+		    if ((fw_ver > 241 ? 
+                          ((u_long)param) : 
+                          cy_readl(&ch_ctrl[channel].rs_status)) & C_RS_DCD) {
 			/* SP("Open Wakeup\n"); */
 			cy_sched_event(info,
 			    Cy_EVENT_OPEN_WAKEUP);
@@ -1625,7 +1721,7 @@
 	    case C_CM_MCTS:
 		if (info->flags & ASYNC_CTS_FLOW) {
 		    if(info->tty->hw_stopped){
-			if( ch_ctrl[channel].rs_status & C_RS_DCD){
+			if( cy_readl(&ch_ctrl[channel].rs_status) & C_RS_DCD){
 			    /* cy_start isn't used because... 
 			       HW flow is handled by the board */
 			    /* SP("Write Wakeup\n"); */
@@ -1633,7 +1729,7 @@
 				Cy_EVENT_WRITE_WAKEUP);
 			}
 		    }else{
-			if(!(ch_ctrl[channel].rs_status & C_RS_CTS)){
+			if(!(cy_readl(&ch_ctrl[channel].rs_status) & C_RS_CTS)){
 			    /* cy_stop isn't used because 
 			       HW flow is handled by the board */
 			    /* SP("Write stop\n"); */
@@ -1653,14 +1749,25 @@
 		queue_task(&tty->flip.tqueue, &tq_timer);
 	    }
 	}
-
-	for (port = 0; port < board_ctrl->n_channel; port++){
+	for (port = 0; port < cy_readl(&board_ctrl->n_channel); port++){
 	    info = &cy_port[ port + cinfo->first_line ];
             tty = info->tty;
 	    ch_ctrl = &(zfw_ctrl->ch_ctrl[port]);
 	    buf_ctrl = &(zfw_ctrl->buf_ctrl[port]);
 
-	    if ((char_count = CHARS_IN_BUF(buf_ctrl))){
+/* Removed due to compilation problems in Alpha systems */
+//	    if ((char_count = CHARS_IN_BUF(buf_ctrl))){
+
+	    rx_get = cy_readl(&buf_ctrl->rx_get);
+	    rx_put = cy_readl(&buf_ctrl->rx_put);
+	    rx_bufsize = cy_readl(&buf_ctrl->rx_bufsize);
+	    if (rx_put >= rx_get)
+	    	char_count = rx_put - rx_get;
+	    else
+	    	char_count = rx_put - rx_get + rx_bufsize;
+
+	    if ( char_count ){
+
 		info->last_active = jiffies;
 		info->jiffies[1] = jiffies;
 
@@ -1673,9 +1780,7 @@
 #endif
 		if( tty == 0){
 		    /* flush received characters */
-		    buf_ctrl->rx_get =
-		        (buf_ctrl->rx_get + char_count)
-			    % buf_ctrl->rx_bufsize;
+		    rx_get = (rx_get + char_count) & (rx_bufsize - 1);
 		    /* SP("-"); */
 		    info->rflush_count++;
 		}else{
@@ -1684,22 +1789,22 @@
 		   for performance, but because of buffer boundaries, there
 		   may be several steps to the operation */
 		    while(0 < (small_count
-			= cy_min( (buf_ctrl->rx_bufsize - buf_ctrl->rx_get),
-			    cy_min( (TTY_FLIPBUF_SIZE - tty->flip.count),
-				char_count)))){
-			memcpy(tty->flip.char_buf_ptr,
-			       (char *)(cinfo->base_addr
-				   + buf_ctrl->rx_bufaddr
-				   + buf_ctrl->rx_get),
-			       small_count);
+			= cy_min((rx_bufsize - rx_get),
+			         cy_min((TTY_FLIPBUF_SIZE - tty->flip.count),
+				        char_count)))){
+
+			memcpy_fromio(tty->flip.char_buf_ptr,
+			              (char *)(cinfo->base_addr
+				       + cy_readl(&buf_ctrl->rx_bufaddr)
+				       + rx_get),
+			              small_count);
+
 			tty->flip.char_buf_ptr += small_count;
 			memset(tty->flip.flag_buf_ptr,
 			       TTY_NORMAL,
 			       small_count);
 			tty->flip.flag_buf_ptr += small_count;
-			buf_ctrl->rx_get =
-			    (buf_ctrl->rx_get + small_count)
-				% buf_ctrl->rx_bufsize;
+			rx_get = (rx_get + small_count) & (rx_bufsize - 1);
 			char_count -= small_count;
 			tty->flip.count += small_count;
 		    }
@@ -1708,13 +1813,9 @@
 			if (tty->flip.count >= TTY_FLIPBUF_SIZE){
 				break;
 			}
-			data = *(char *) (cinfo->base_addr +
-			                  buf_ctrl->rx_bufaddr +
-			                  buf_ctrl->rx_get);
-			buf_ctrl->rx_get =
-			    (buf_ctrl->rx_get + 1)
-				% buf_ctrl->rx_bufsize;
-
+			data = cy_readb(cinfo->base_addr +
+			           cy_readl(&buf_ctrl->rx_bufaddr) + rx_get);
+			rx_get = (rx_get + 1) & (rx_bufsize - 1);
 			tty->flip.count++;
 			*tty->flip.flag_buf_ptr++ = TTY_NORMAL;
 			*tty->flip.char_buf_ptr++ = data;
@@ -1722,22 +1823,33 @@
 #endif
 		    queue_task(&tty->flip.tqueue, &tq_timer);
 		}
+		/* Update rx_get */
+		cy_writel(&buf_ctrl->rx_get, rx_get);
 	    }
 
-	    if ((char_count = SPACE_IN_BUF(buf_ctrl))){
-		if( tty == 0){
+/* Removed due to compilation problems in Alpha systems */
+//	    if ((char_count = SPACE_IN_BUF(buf_ctrl))){
+
+	    tx_get = cy_readl(&buf_ctrl->tx_get);
+	    tx_put = cy_readl(&buf_ctrl->tx_put);
+	    tx_bufsize = cy_readl(&buf_ctrl->tx_bufsize);
+	    if (tx_put >= tx_get)
+	    	char_count = tx_get - tx_put - 1 + tx_bufsize;
+	    else
+	    	char_count = tx_get - tx_put - 1;
+
+	    if ( char_count ){
+
+		if( tty == 0 ){
 		    goto ztxdone;
 		}
 
 		if(info->x_char) { /* send special char */
 		    data = info->x_char;
 
-		    *(char *) (cinfo->base_addr +
-		               buf_ctrl->tx_bufaddr + 
-		               buf_ctrl->tx_put) = data;
-		    buf_ctrl->tx_put =
-			(buf_ctrl->tx_put + 1)
-			    % buf_ctrl->tx_bufsize;
+		    cy_writeb((cinfo->base_addr +
+		              cy_readl(&buf_ctrl->tx_bufaddr) + tx_put), data);
+		    tx_put = (tx_put + 1) & (tx_bufsize - 1);
 		    info->x_char = 0;
 		    char_count--;
 		    info->last_active = jiffies;
@@ -1751,21 +1863,20 @@
 		}
 #ifdef BLOCKMOVE
 		while(0 < (small_count
-		    = cy_min( (buf_ctrl->tx_bufsize - buf_ctrl->tx_put),
-			cy_min ( (PAGE_SIZE - info->xmit_tail),
-			  cy_min( info->xmit_cnt, char_count))))){
-		    memcpy((char *)(cinfo->base_addr
-			       + buf_ctrl->tx_bufaddr
-			       + buf_ctrl->tx_put),
-			   &info->xmit_buf[info->xmit_tail],
-			   small_count);
-		    buf_ctrl->tx_put =
-			(buf_ctrl->tx_put + small_count)
-			    % buf_ctrl->tx_bufsize;
+		    = cy_min((tx_bufsize - tx_put),
+			     cy_min ((SERIAL_XMIT_SIZE - info->xmit_tail),
+			             cy_min(info->xmit_cnt, char_count))))){
+
+		    memcpy_toio((char *)(cinfo->base_addr
+			         + cy_readl(&buf_ctrl->tx_bufaddr) + tx_put),
+			        &info->xmit_buf[info->xmit_tail],
+			        small_count);
+
+		    tx_put = (tx_put + small_count) & (tx_bufsize - 1);
 		    char_count -= small_count;
 		    info->xmit_cnt -= small_count;
 		    info->xmit_tail =
-			(info->xmit_tail + small_count) & (PAGE_SIZE - 1);
+		       (info->xmit_tail + small_count) & (SERIAL_XMIT_SIZE - 1);
 		    info->last_active = jiffies;
 		    info->jiffies[2] = jiffies;
 		}
@@ -1774,28 +1885,33 @@
 		    data = info->xmit_buf[info->xmit_tail];
 		    info->xmit_cnt--;
 		    info->xmit_tail =
-			(info->xmit_tail + 1) & (PAGE_SIZE - 1);
+			(info->xmit_tail + 1) & (SERIAL_XMIT_SIZE - 1);
 
-		    *(char *) (cinfo->base_addr +
-		               buf_ctrl->tx_bufaddr +
-		               buf_ctrl->tx_put) = data;
-		    buf_ctrl->tx_put =
-		        (buf_ctrl->tx_put + 1)
-			    % buf_ctrl->tx_bufsize;
+		    cy_writeb(cinfo->base_addr +
+		              cy_readl(&buf_ctrl->tx_bufaddr) + tx_put, 
+			      data);
+		    tx_put = (tx_put + 1) & (tx_bufsize - 1);
 		    char_count--;
 		    info->last_active = jiffies;
 		    info->jiffies[2] = jiffies;
 		}
+
 #endif
 	    ztxdone:
 		if (info->xmit_cnt < WAKEUP_CHARS) {
 		    cy_sched_event(info, Cy_EVENT_WRITE_WAKEUP);
 		}
+		/* Update tx_put */
+		cy_writel(&buf_ctrl->tx_put, tx_put);
 	    }
 	}
-
 	/* poll every 40 ms */
-	cyz_timerlist.expires = jiffies + 4;
+	cyz_timerlist.expires = jiffies + cyz_polling_cycle;
+
+	/* refresh inactivity counter */
+	if (cinfo->inact_ctrl) {
+		cy_writel(&board_ctrl->inactivity, (uclong) ZF_TINACT);
+	}
     }
     add_timer(&cyz_timerlist);
 
@@ -1851,26 +1967,27 @@
 #endif
 
 	save_flags(flags); cli();
-	    base_addr[CyCAR<<index] = (u_char)channel;
+	    cy_writeb((ulong)base_addr+(CyCAR<<index), (u_char)channel);
 
-	    base_addr[CyRTPR<<index] = (info->default_timeout
+	    cy_writeb((ulong)base_addr+(CyRTPR<<index), (info->default_timeout
 				 ? info->default_timeout
-				 : 0x02); /* 10ms rx timeout */
+				 : 0x02)); /* 10ms rx timeout */
 
 	    cyy_issue_cmd(base_addr,CyCHAN_CTL|CyENB_RCVR|CyENB_XMTR,index);
 
-	    base_addr[CyCAR<<index] =
-			 (u_char)channel; /* !!! Is this needed? */
-	    base_addr[CyMSVR1<<index] = CyRTS;
-	    base_addr[CyMSVR2<<index] = CyDTR;
+	    cy_writeb((ulong)base_addr+(CyCAR<<index), (u_char)channel);
+	    cy_writeb((ulong)base_addr+(CyMSVR1<<index), CyRTS);
+	    cy_writeb((ulong)base_addr+(CyMSVR2<<index), CyDTR);
 
 #ifdef SERIAL_DEBUG_DTR
 	    printk("cyc:startup raising DTR\n");
 	    printk("     status: 0x%x, 0x%x\n",
-		   base_addr[CyMSVR1<<index], base_addr[CyMSVR2<<index]);
+		   cy_readb(base_addr+(CyMSVR1<<index)), 
+                   cy_readb(base_addr+(CyMSVR2<<index)));
 #endif
 
-	    base_addr[CySRER<<index] |= CyRxData;
+	    cy_writeb((u_long)base_addr+(CySRER<<index),
+               cy_readb(base_addr+(CySRER<<index)) | CyRxData);
 	    info->flags |= ASYNC_INITIALIZED;
 
 	    if (info->tty){
@@ -1894,7 +2011,7 @@
 
 	zfw_ctrl =
 	    (struct ZFW_CTRL *)
-		(cy_card[card].base_addr + firm_id->zfwctrl_addr);
+		(cy_card[card].base_addr + cy_readl(&firm_id->zfwctrl_addr));
 	board_ctrl = &zfw_ctrl->board_ctrl;
 	ch_ctrl = zfw_ctrl->ch_ctrl;
 
@@ -1903,8 +2020,8 @@
 	     card, channel, (long)base_addr);/**/
 #endif
 
-	ch_ctrl[channel].op_mode = C_CH_ENABLE;
-	ch_ctrl[channel].intr_enable = C_IN_MDCD|C_IN_MCTS;
+	cy_writel(&ch_ctrl[channel].op_mode, C_CH_ENABLE);
+	cy_writel(&ch_ctrl[channel].intr_enable, C_IN_MDCD|C_IN_MCTS);
 	retval = cyz_issue_cmd( &cy_card[card],
 	    channel, C_CM_IOCTL, 0L);	/* was C_CM_RESET */
 	if (retval != 0){
@@ -1913,8 +2030,8 @@
 
 	/* set timeout !!! */
 	/* set RTS and DTR !!! */
-	ch_ctrl[channel].rs_control |=
-		    C_RS_RTS | C_RS_DTR ;
+	cy_writel(&ch_ctrl[channel].rs_control,
+             cy_readl(&ch_ctrl[channel].rs_control) | C_RS_RTS | C_RS_DTR) ;
 	retval = cyz_issue_cmd(&cy_card[info->card],
 	    channel, C_CM_IOCTLM, 0L);
 	if (retval != 0){
@@ -1959,8 +2076,9 @@
 		       + (cy_chip_offset[chip]<<index));
 
 	save_flags(flags); cli();
-	    base_addr[CyCAR<<index] = channel;
-	    base_addr[CySRER<<index] |= CyTxMpty;
+	    cy_writeb((u_long)base_addr+(CyCAR<<index), channel);
+	    cy_writeb((u_long)base_addr+(CySRER<<index), 
+               cy_readb(base_addr+(CySRER<<index)) | CyTxMpty);
 	restore_flags(flags);
     } else {
 	/* Don't have to do anything at this time */
@@ -2012,14 +2130,15 @@
 		free_page((unsigned long) temp);
 	    }
 
-	    base_addr[CyCAR<<index] = (u_char)channel;
+	    cy_writeb((u_long)base_addr+(CyCAR<<index), (u_char)channel);
 	    if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) {
-		base_addr[CyMSVR1<<index] = ~CyRTS;
-		base_addr[CyMSVR2<<index] = ~CyDTR;
+		cy_writeb((u_long)base_addr+(CyMSVR1<<index), ~CyRTS);
+		cy_writeb((u_long)base_addr+(CyMSVR2<<index), ~CyDTR);
 #ifdef SERIAL_DEBUG_DTR
 		printk("cyc shutdown dropping DTR\n");
 		printk("     status: 0x%x, 0x%x\n",
-		    base_addr[CyMSVR1<<index], base_addr[CyMSVR2<<index]);
+		    cy_readb(base_addr+(CyMSVR1<<index)), 
+                    cy_readb(base_addr+(CyMSVR2<<index)));
 #endif
 	    }
 	    cyy_issue_cmd(base_addr,CyCHAN_CTL|CyDIS_RCVR,index);
@@ -2051,10 +2170,11 @@
 
 	zfw_ctrl =
 	    (struct ZFW_CTRL *)
-		(cy_card[card].base_addr + firm_id->zfwctrl_addr);
-	board_ctrl = &zfw_ctrl->board_ctrl;
+		(cy_card[card].base_addr + cy_readl(&firm_id->zfwctrl_addr));
+	board_ctrl = &(zfw_ctrl->board_ctrl);
 	ch_ctrl = zfw_ctrl->ch_ctrl;
 
+
 	save_flags(flags); cli();
 	    if (info->xmit_buf){
 		unsigned char * temp;
@@ -2063,8 +2183,9 @@
 		free_page((unsigned long) temp);
 	    }
 	    if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) {
-		ch_ctrl[channel].rs_control &=
-			    ~(C_RS_RTS | C_RS_DTR );
+		cy_writel((u_long)&ch_ctrl[channel].rs_control,
+                   (uclong)(cy_readl(&ch_ctrl[channel].rs_control) & 
+                   ~(C_RS_RTS | C_RS_DTR)));
 		retval = cyz_issue_cmd(&cy_card[info->card],
 			channel, C_CM_IOCTLM, 0L);
 		if (retval != 0){
@@ -2187,13 +2308,14 @@
 	while (1) {
 	    save_flags(flags); cli();
 		if (!(info->flags & ASYNC_CALLOUT_ACTIVE)){
-		    base_addr[CyCAR<<index] = (u_char)channel;
-		    base_addr[CyMSVR1<<index] = CyRTS;
-		    base_addr[CyMSVR2<<index] = CyDTR;
+		    cy_writeb((u_long)base_addr+(CyCAR<<index), (u_char)channel);
+		    cy_writeb((u_long)base_addr+(CyMSVR1<<index), CyRTS);
+		    cy_writeb((u_long)base_addr+(CyMSVR2<<index), CyDTR);
 #ifdef SERIAL_DEBUG_DTR
 		    printk("cyc:block_til_ready raising DTR\n");
 		    printk("     status: 0x%x, 0x%x\n",
-			base_addr[CyMSVR1<<index], base_addr[CyMSVR2<<index]);
+			cy_readb(base_addr+(CyMSVR1<<index)), 
+                        cy_readb(base_addr+(CyMSVR2<<index)));
 #endif
 		}
 	    restore_flags(flags);
@@ -2208,16 +2330,16 @@
 		break;
 	    }
 	    save_flags(flags); cli();
-		base_addr[CyCAR<<index] = (u_char)channel;
+		cy_writeb((u_long)base_addr+(CyCAR<<index), (u_char)channel);
 		if (!(info->flags & ASYNC_CALLOUT_ACTIVE)
 		&& !(info->flags & ASYNC_CLOSING)
 		&& (C_CLOCAL(tty)
-		    || (base_addr[CyMSVR1<<index] & CyDCD))) {
+		    || (cy_readb(base_addr+(CyMSVR1<<index)) & CyDCD))) {
 			restore_flags(flags);
 			break;
 		}
 	    restore_flags(flags);
-	    if (signal_pending(current)) {
+	    if (current->signal & ~current->blocked) {
 		retval = -ERESTARTSYS;
 		break;
 	    }
@@ -2243,13 +2365,13 @@
 
 	zfw_ctrl =
 	    (struct ZFW_CTRL *)
-		(base_addr + firm_id->zfwctrl_addr);
+		(base_addr + cy_readl(&firm_id->zfwctrl_addr));
 	board_ctrl = &zfw_ctrl->board_ctrl;
 	ch_ctrl = zfw_ctrl->ch_ctrl;
 
 	while (1) {
-	    ch_ctrl[channel].rs_control |=
-			C_RS_RTS | C_RS_DTR ;
+	    cy_writel(&ch_ctrl[channel].rs_control,
+               cy_readl(&ch_ctrl[channel].rs_control) | C_RS_RTS | C_RS_DTR);
 	    retval = cyz_issue_cmd(&cy_card[info->card],
 		    channel, C_CM_IOCTLM, 0L);
 	    if (retval != 0){
@@ -2272,10 +2394,10 @@
 	    if (!(info->flags & ASYNC_CALLOUT_ACTIVE)
 	    && !(info->flags & ASYNC_CLOSING)
 	    && (C_CLOCAL(tty)
-	      || (ch_ctrl[channel].rs_status & C_RS_DCD))) {
+	      || (cy_readl(&ch_ctrl[channel].rs_status) & C_RS_DCD))) {
 		break;
 	    }
-	    if (signal_pending(current)) {
+	    if (current->signal & ~current->blocked) {
 		retval = -ERESTARTSYS;
 		break;
 	    }
@@ -2332,11 +2454,11 @@
     */
     if (IS_CYC_Z(cy_card[info->card])) {
         if (!ISZLOADED(cy_card[info->card])) {
-	    if (((ZE_V1 ==((struct RUNTIME_9060 *)
-		((cy_card[info->card]).ctl_addr))->mail_box_0) &&
+	    if (((ZE_V1 ==cy_readl(&((struct RUNTIME_9060 *)
+		((cy_card[info->card]).ctl_addr))->mail_box_0)) &&
 		Z_FPGA_CHECK(cy_card[info->card])) &&
-		(ZFIRM_HLT==((struct FIRM_ID *)
-		((cy_card[info->card]).base_addr+ID_ADDRESS))->signature))
+		(ZFIRM_HLT==cy_readl(&((struct FIRM_ID *)
+		((cy_card[info->card]).base_addr+ID_ADDRESS))->signature)))
 	    {
 		printk ("Cyclades-Z Error: you need an external power supply for this number of ports.\n\rFirmware halted.\r\n");
 	    } else {
@@ -2403,6 +2525,7 @@
 #ifdef SERIAL_DEBUG_OPEN
     printk(" cyc:cy_open done\n");/**/
 #endif
+
     return 0;
 } /* cy_open */
 
@@ -2603,13 +2726,13 @@
         return;
 
     save_flags(flags); cli();
-        if (info->xmit_cnt >= PAGE_SIZE - 1) {
+        if (info->xmit_cnt >= SERIAL_XMIT_SIZE - 1) {
             restore_flags(flags);
             return;
         }
 
         info->xmit_buf[info->xmit_head++] = ch;
-        info->xmit_head &= PAGE_SIZE - 1;
+        info->xmit_head &= SERIAL_XMIT_SIZE - 1;
         info->xmit_cnt++;
     restore_flags(flags);
 #if 0
@@ -2652,8 +2775,9 @@
 		       + (cy_chip_offset[chip]<<index));
 
 	save_flags(flags); cli();
-	    base_addr[CyCAR<<index] = channel;
-	    base_addr[CySRER<<index] |= CyTxMpty;
+	    cy_writeb((u_long)base_addr+(CyCAR<<index), channel);
+	    cy_writeb((u_long)base_addr+(CySRER<<index),
+               cy_readb(base_addr+(CySRER<<index)) | CyTxMpty);
 	restore_flags(flags);
     } else {
 	/* Since polling is already in place,
@@ -2680,7 +2804,7 @@
 
     if (serial_paranoia_check(info, tty->device, "cy_write_room"))
         return 0;
-    ret = PAGE_SIZE - info->xmit_cnt - 1;
+    ret = SERIAL_XMIT_SIZE - info->xmit_cnt - 1;
     if (ret < 0)
         ret = 0;
     return ret;
@@ -2724,6 +2848,7 @@
   unsigned cflag;
   int   i;
 
+
     if (!info->tty || !info->tty->termios){
         return;
     }
@@ -2736,6 +2861,9 @@
     channel = (info->line) - (cy_card[card].first_line);
 
     if (!IS_CYC_Z(cy_card[card])) {
+
+	index = cy_card[card].bus_index;
+
 	/* baud rate */
 	i = cflag & CBAUD;
      
@@ -2744,6 +2872,11 @@
 		i = 16;
 	    else if(i == B115200) 
 		i = 18;
+	    else if(i == B230400 && 
+                 cy_readb(cy_card[card].base_addr+(CyGFRCR<<index)) >= 0x48) {
+		/* It is a CD1400 rev. J or later */
+		i = 20;
+	    }
 #ifdef B76800
 	    else if(i == B76800) 
 		i = 17;
@@ -2753,15 +2886,35 @@
 	}
 
 	if (i == 15) {
-		if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
-			i += 1;
-		if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
-			i += 3;
-	}
-	info->tbpr = baud_bpr[i]; /* Tx BPR */
-	info->tco = baud_co[i]; /* Tx CO */
-	info->rbpr = baud_bpr[i]; /* Rx BPR */
-	info->rco = baud_co[i]; /* Rx CO */
+	    if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
+		i += 1;
+	    if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
+		i += 3;
+       	    if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST){
+                switch(info->baud) {
+		    case 57600:
+			i += 1; break;
+		    case 115200:
+			i += 3; break;
+		    case 230400:
+			i += 5; break;
+		    default:
+			break;
+	        }
+            }
+	}
+        if(cy_readb(cy_card[card].base_addr+(CyGFRCR<<index)) >= 0x48) {
+	    /* It is a CD1400 rev. J or later */
+	    info->tbpr = baud_bpr_60[i]; /* Tx BPR */
+	    info->tco = baud_co_60[i]; /* Tx CO */
+	    info->rbpr = baud_bpr_60[i]; /* Rx BPR */
+	    info->rco = baud_co_60[i]; /* Rx CO */
+	} else {
+	    info->tbpr = baud_bpr_25[i]; /* Tx BPR */
+	    info->tco = baud_co_25[i]; /* Tx CO */
+	    info->rbpr = baud_bpr_25[i]; /* Rx BPR */
+	    info->rco = baud_co_25[i]; /* Rx CO */
+	}
 	if (baud_table[i] == 134) {
 	    info->timeout = (info->xmit_fifo_size*HZ*30/269) + 2;
 	    /* get it right for 134.5 baud */
@@ -2834,70 +2987,94 @@
 
 	chip = channel>>2;
 	channel &= 0x03;
-	index = cy_card[card].bus_index;
 	base_addr = (unsigned char*)
 		       (cy_card[card].base_addr
 		       + (cy_chip_offset[chip]<<index));
 
 	save_flags(flags); cli();
-	    base_addr[CyCAR<<index] = (u_char)channel;
+	    cy_writeb((u_long)base_addr+(CyCAR<<index), (u_char)channel);
 
 	   /* tx and rx baud rate */
 
-	    base_addr[CyTCOR<<index] = info->tco;
-	    base_addr[CyTBPR<<index] = info->tbpr;
-	    base_addr[CyRCOR<<index] = info->rco;
-	    base_addr[CyRBPR<<index] = info->rbpr;
+	    cy_writeb((u_long)base_addr+(CyTCOR<<index), info->tco);
+	    cy_writeb((u_long)base_addr+(CyTBPR<<index), info->tbpr);
+	    cy_writeb((u_long)base_addr+(CyRCOR<<index), info->rco);
+	    cy_writeb((u_long)base_addr+(CyRBPR<<index), info->rbpr);
 
 	    /* set line characteristics  according configuration */
 
-	    base_addr[CySCHR1<<index] = START_CHAR(info->tty);
-	    base_addr[CySCHR2<<index] = STOP_CHAR(info->tty);
-	    base_addr[CyCOR1<<index] = info->cor1;
-	    base_addr[CyCOR2<<index] = info->cor2;
-	    base_addr[CyCOR3<<index] = info->cor3;
-	    base_addr[CyCOR4<<index] = info->cor4;
-	    base_addr[CyCOR5<<index] = info->cor5;
+	    cy_writeb((u_long)base_addr+(CySCHR1<<index), 
+		      START_CHAR(info->tty));
+	    cy_writeb((u_long)base_addr+(CySCHR2<<index), 
+		      STOP_CHAR(info->tty));
+	    cy_writeb((u_long)base_addr+(CyCOR1<<index), info->cor1);
+	    cy_writeb((u_long)base_addr+(CyCOR2<<index), info->cor2);
+	    cy_writeb((u_long)base_addr+(CyCOR3<<index), info->cor3);
+	    cy_writeb((u_long)base_addr+(CyCOR4<<index), info->cor4);
+	    cy_writeb((u_long)base_addr+(CyCOR5<<index), info->cor5);
 
 	    cyy_issue_cmd(base_addr,
 		     CyCOR_CHANGE|CyCOR1ch|CyCOR2ch|CyCOR3ch,index);
 
-	    base_addr[CyCAR<<index] =
-				(u_char)channel; /* !!! Is this needed? */
-
-	    base_addr[CyRTPR<<index] = (info->default_timeout
-					 ? info->default_timeout
-					 : 0x02); /* 10ms rx timeout */
+	    cy_writeb((u_long)base_addr+(CyCAR<<index), 
+		      (u_char)channel); /* !!! Is this needed? */
+	    cy_writeb((u_long)base_addr+(CyRTPR<<index), (info->default_timeout
+					         ? info->default_timeout
+					         : 0x02)); /* 10ms rx timeout */
 
 	    if (C_CLOCAL(info->tty)) {
-		base_addr[CySRER<<index] |= CyMdmCh; /* without modem intr */
+		/* without modem intr */
+		cy_writeb((u_long)base_addr+(CySRER<<index),
+                   cy_readb(base_addr+(CySRER<<index)) | CyMdmCh); 
 					/* act on 1->0 modem transitions */
-		base_addr[CyMCOR1<<index] = CyCTS;
+                if ((cflag & CRTSCTS) && info->rflow) {
+                        cy_writeb((u_long)base_addr+(CyMCOR1<<index), 
+                                  (CyCTS|rflow_thr[i]));
+                } else {
+                        cy_writeb((u_long)base_addr+(CyMCOR1<<index), CyCTS);
+                }
 					/* act on 0->1 modem transitions */
-		base_addr[CyMCOR2<<index] = CyCTS;
+		cy_writeb((u_long)base_addr+(CyMCOR2<<index), CyCTS);
 	    } else {
-		base_addr[CySRER<<index] |= CyMdmCh; /* with modem intr */
+		/* without modem intr */
+		cy_writeb((u_long)base_addr+(CySRER<<index),
+                   cy_readb(base_addr+(CySRER<<index)) | CyMdmCh); 
 					/* act on 1->0 modem transitions */
-		base_addr[CyMCOR1<<index] = CyDSR|CyCTS|CyRI|CyDCD;
+                if ((cflag & CRTSCTS) && info->rflow) {
+			cy_writeb((u_long)base_addr+(CyMCOR1<<index), 
+        	                  (CyDSR|CyCTS|CyRI|CyDCD|rflow_thr[i]));
+                } else {
+			cy_writeb((u_long)base_addr+(CyMCOR1<<index), 
+                                  CyDSR|CyCTS|CyRI|CyDCD);
+                }
 					/* act on 0->1 modem transitions */
-		base_addr[CyMCOR2<<index] = CyDSR|CyCTS|CyRI|CyDCD;
+		cy_writeb((u_long)base_addr+(CyMCOR2<<index), 
+			  CyDSR|CyCTS|CyRI|CyDCD);
 	    }
 
 	    if(i == 0){ /* baud rate is zero, turn off line */
-		base_addr[CyMSVR2<<index] = ~CyDTR;
+	        if (info->rtsdtr_inv) {
+			cy_writeb((u_long)base_addr+(CyMSVR1<<index), ~CyRTS);
+		} else {
+                        cy_writeb((u_long)base_addr+(CyMSVR2<<index), ~CyDTR);
+		}
 #ifdef SERIAL_DEBUG_DTR
 		printk("cyc:set_line_char dropping DTR\n");
 		printk("     status: 0x%x,
-		    0x%x\n", base_addr[CyMSVR1<<index],
-		    base_addr[CyMSVR2<<index]);
+		    0x%x\n", cy_readb(base_addr+(CyMSVR1<<index)),
+		    cy_readb(base_addr+(CyMSVR2<<index)));
 #endif
 	    }else{
-		base_addr[CyMSVR2<<index] = CyDTR;
+                if (info->rtsdtr_inv) {
+			cy_writeb((u_long)base_addr+(CyMSVR1<<index), CyRTS);
+                } else {
+			cy_writeb((u_long)base_addr+(CyMSVR2<<index), CyDTR);
+                }
 #ifdef SERIAL_DEBUG_DTR
 		printk("cyc:set_line_char raising DTR\n");
 		printk("     status: 0x%x, 0x%x\n",
-		    base_addr[CyMSVR1<<index],
-		    base_addr[CyMSVR2<<index]);
+		    cy_readb(base_addr+(CyMSVR1<<index)),
+		    cy_readb(base_addr+(CyMSVR2<<index)));
 #endif
 	    }
 
@@ -2920,82 +3097,84 @@
 	    return;
 	}
 
-	zfw_ctrl =
-            (struct ZFW_CTRL *)
-                (cy_card[card].base_addr + firm_id->zfwctrl_addr);
+	zfw_ctrl = (struct ZFW_CTRL *)
+                (cy_card[card].base_addr + cy_readl(&firm_id->zfwctrl_addr));
 	board_ctrl = &zfw_ctrl->board_ctrl;
-	ch_ctrl = &zfw_ctrl->ch_ctrl[channel];
+	ch_ctrl = &(zfw_ctrl->ch_ctrl[channel]);
 	buf_ctrl = &zfw_ctrl->buf_ctrl[channel];
 
 	/* baud rate */
 	switch(i = cflag & CBAUD){
 	/*
-        case B0: ch_ctrl->comm_baud = 0; break;
+        case B0: cy_writel(&ch_ctrl->comm_baud , 0); break;
 	*/
-        case B50: ch_ctrl->comm_baud = 50; break;
-        case B75: ch_ctrl->comm_baud = 75; break;
-        case B110: ch_ctrl->comm_baud = 110; break;
-        case B134: ch_ctrl->comm_baud = 134; break;
-        case B150: ch_ctrl->comm_baud = 150; break;
-        case B200: ch_ctrl->comm_baud = 200; break;
-        case B300: ch_ctrl->comm_baud = 300; break;
-        case B600: ch_ctrl->comm_baud = 600; break;
-        case B1200: ch_ctrl->comm_baud = 1200; break;
-        case B1800: ch_ctrl->comm_baud = 1800; break;
-        case B2400: ch_ctrl->comm_baud = 2400; break;
-        case B4800: ch_ctrl->comm_baud = 4800; break;
-        case B9600: ch_ctrl->comm_baud = 9600; break;
-        case B19200: ch_ctrl->comm_baud = 19200; break;
+        case B50: cy_writel(&ch_ctrl->comm_baud , 50); break;
+        case B75: cy_writel(&ch_ctrl->comm_baud , 75); break;
+        case B110: cy_writel(&ch_ctrl->comm_baud , 110); break;
+        case B134: cy_writel(&ch_ctrl->comm_baud , 134); break;
+        case B150: cy_writel(&ch_ctrl->comm_baud , 150); break;
+        case B200: cy_writel(&ch_ctrl->comm_baud , 200); break;
+        case B300: cy_writel(&ch_ctrl->comm_baud , 300); break;
+        case B600: cy_writel(&ch_ctrl->comm_baud , 600); break;
+        case B1200: cy_writel(&ch_ctrl->comm_baud , 1200); break;
+        case B1800: cy_writel(&ch_ctrl->comm_baud , 1800); break;
+        case B2400: cy_writel(&ch_ctrl->comm_baud , 2400); break;
+        case B4800: cy_writel(&ch_ctrl->comm_baud , 4800); break;
+        case B9600: cy_writel(&ch_ctrl->comm_baud , 9600); break;
+        case B19200: cy_writel(&ch_ctrl->comm_baud , 19200); break;
         case B38400:
             if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI){
-                ch_ctrl->comm_baud = 57600;
+                cy_writel(&ch_ctrl->comm_baud , 57600);
             }else if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI){
-                ch_ctrl->comm_baud = 115200;
+                cy_writel(&ch_ctrl->comm_baud , 115200);
+            }else if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST){
+                cy_writel(&ch_ctrl->comm_baud , info->baud);
             }else{
-                ch_ctrl->comm_baud = 38400;
+                cy_writel(&ch_ctrl->comm_baud , 38400);
             }
             break;
-        case B57600: ch_ctrl->comm_baud = 57600; break;
+        case B57600: cy_writel(&ch_ctrl->comm_baud , 57600); break;
 #ifdef B76800
-        case B76800: ch_ctrl->comm_baud = 76800; break;
+        case B76800: cy_writel(&ch_ctrl->comm_baud , 76800); break;
 #endif
-        case B115200: ch_ctrl->comm_baud = 115200; break;
-        case B230400: ch_ctrl->comm_baud = 230400; break;
-        case B460800: ch_ctrl->comm_baud = 460800; break;
+        case B115200: cy_writel(&ch_ctrl->comm_baud , 115200); break;
+        case B230400: cy_writel(&ch_ctrl->comm_baud , 230400); break;
+        case B460800: cy_writel(&ch_ctrl->comm_baud , 460800); break;
 	}
-        if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST){
-                ch_ctrl->comm_baud = info->baud;
-        }
 
 	/* byte size and parity */
 	switch(cflag & CSIZE){
-	case CS5: ch_ctrl->comm_data_l = C_DL_CS5; break;
-	case CS6: ch_ctrl->comm_data_l = C_DL_CS6; break;
-	case CS7: ch_ctrl->comm_data_l = C_DL_CS7; break;
-	case CS8: ch_ctrl->comm_data_l = C_DL_CS8; break;
+	case CS5: cy_writel(&ch_ctrl->comm_data_l , C_DL_CS5); break;
+	case CS6: cy_writel(&ch_ctrl->comm_data_l , C_DL_CS6); break;
+	case CS7: cy_writel(&ch_ctrl->comm_data_l , C_DL_CS7); break;
+	case CS8: cy_writel(&ch_ctrl->comm_data_l , C_DL_CS8); break;
 	}
 	if(cflag & CSTOPB){
-	    ch_ctrl->comm_data_l |= C_DL_2STOP;
+	    cy_writel(&ch_ctrl->comm_data_l,
+               cy_readl(&ch_ctrl->comm_data_l) | C_DL_2STOP);
 	}else{
-	    ch_ctrl->comm_data_l |= C_DL_1STOP;
+	    cy_writel(&ch_ctrl->comm_data_l,
+               cy_readl(&ch_ctrl->comm_data_l) | C_DL_1STOP);
 	}
 	if (cflag & PARENB){
 	    if (cflag & PARODD){
-		ch_ctrl->comm_parity = C_PR_ODD;
+		cy_writel(&ch_ctrl->comm_parity , C_PR_ODD);
 	    }else{
-		ch_ctrl->comm_parity = C_PR_EVEN;
+		cy_writel(&ch_ctrl->comm_parity , C_PR_EVEN);
 	    }
 	}else{
-	    ch_ctrl->comm_parity = C_PR_NONE;
+	    cy_writel(&ch_ctrl->comm_parity , C_PR_NONE);
 	}
 
 	/* CTS flow control flag */
 	if (cflag & CRTSCTS){
 	    info->flags |= ASYNC_CTS_FLOW;
-	    ch_ctrl->hw_flow |= C_RS_CTS | C_RS_RTS;
+	    cy_writel(&ch_ctrl->hw_flow,
+               cy_readl(&ch_ctrl->hw_flow) | C_RS_CTS | C_RS_RTS);
 	}else{
 	    info->flags &= ~ASYNC_CTS_FLOW;
-	    ch_ctrl->hw_flow &= ~(C_RS_CTS | C_RS_RTS);
+	    cy_writel(&ch_ctrl->hw_flow,
+               cy_readl(&ch_ctrl->hw_flow) & ~(C_RS_CTS | C_RS_RTS));
 	}
 
 	retval = cyz_issue_cmd( &cy_card[card], channel, C_CM_IOCTL, 0L);
@@ -3012,12 +3191,14 @@
 	}
 
 	if(i == 0){ /* baud rate is zero, turn off line */
-	    ch_ctrl->rs_control &= ~C_RS_DTR;
+	    cy_writel(&ch_ctrl->rs_control,
+               cy_readl(&ch_ctrl->rs_control) & ~C_RS_DTR);
 #ifdef SERIAL_DEBUG_DTR
 	    printk("cyc:set_line_char dropping Z DTR\n");
 #endif
 	}else{
-	    ch_ctrl->rs_control |= C_RS_DTR;
+	    cy_writel(&ch_ctrl->rs_control,
+               cy_readl(&ch_ctrl->rs_control) | C_RS_DTR);
 #ifdef SERIAL_DEBUG_DTR
 	    printk("cyc:set_line_char raising Z DTR\n");
 #endif
@@ -3028,6 +3209,7 @@
 	    printk("cyc:set_line_char retval at %d was %x\n",
 	        __LINE__, retval);
 	}
+	cy_readl(&ch_ctrl->comm_baud);
 
 	if (info->tty){
 	    clear_bit(TTY_IO_ERROR, &info->tty->flags);
@@ -3131,9 +3313,9 @@
 		       + (cy_chip_offset[chip]<<index));
 
 	save_flags(flags); cli();
-	    base_addr[CyCAR<<index] = (u_char)channel;
-	    status = base_addr[CyMSVR1<<index];
-	    status |= base_addr[CyMSVR2<<index];
+	    cy_writeb((u_long)base_addr+(CyCAR<<index), (u_char)channel);
+	    status = cy_readb(base_addr+(CyMSVR1<<index));
+	    status |= cy_readb(base_addr+(CyMSVR2<<index));
 	restore_flags(flags);
 
 	result =  ((status  & CyRTS) ? TIOCM_RTS : 0)
@@ -3152,12 +3334,11 @@
 	firm_id = (struct FIRM_ID *)
 		    (cy_card[card].base_addr + ID_ADDRESS);
         if (ISZLOADED(cy_card[card])) {
-	    zfw_ctrl =
-		(struct ZFW_CTRL *)
-		(cy_card[card].base_addr + firm_id->zfwctrl_addr);
+	    zfw_ctrl = (struct ZFW_CTRL *)
+		(cy_card[card].base_addr + cy_readl(&firm_id->zfwctrl_addr));
 	    board_ctrl = &zfw_ctrl->board_ctrl;
 	    ch_ctrl = zfw_ctrl->ch_ctrl;
-	    lstatus = ch_ctrl[channel].rs_status;
+	    lstatus = cy_readl(&ch_ctrl[channel].rs_status);
 	    result =  ((lstatus  & C_RS_RTS) ? TIOCM_RTS : 0)
 		    | ((lstatus  & C_RS_DTR) ? TIOCM_DTR : 0)
 	            | ((lstatus  & C_RS_DCD) ? TIOCM_CAR : 0)
@@ -3203,18 +3384,27 @@
 	case TIOCMBIS:
 	    if (arg & TIOCM_RTS){
 		save_flags(flags); cli();
-		    base_addr[CyCAR<<index] = (u_char)channel;
-		    base_addr[CyMSVR1<<index] = CyRTS;
+		cy_writeb((u_long)base_addr+(CyCAR<<index), (u_char)channel);
+                if (info->rtsdtr_inv) {
+		    cy_writeb((u_long)base_addr+(CyMSVR2<<index), CyDTR);
+                } else {
+		    cy_writeb((u_long)base_addr+(CyMSVR1<<index), CyRTS);
+                }
 		restore_flags(flags);
 	    }
 	    if (arg & TIOCM_DTR){
 		save_flags(flags); cli();
-		base_addr[CyCAR<<index] = (u_char)channel;
-		base_addr[CyMSVR2<<index] = CyDTR;
+		cy_writeb((u_long)base_addr+(CyCAR<<index), (u_char)channel);
+                if (info->rtsdtr_inv) {
+		    cy_writeb((u_long)base_addr+(CyMSVR1<<index), CyRTS);
+                } else {
+		    cy_writeb((u_long)base_addr+(CyMSVR2<<index), CyDTR);
+                }
 #ifdef SERIAL_DEBUG_DTR
 		printk("cyc:set_modem_info raising DTR\n");
 		printk("     status: 0x%x, 0x%x\n",
-		    base_addr[CyMSVR1<<index], base_addr[CyMSVR2<<index]);
+		    cy_readb(base_addr+(CyMSVR1<<index)), 
+                    cy_readb(base_addr+(CyMSVR2<<index)));
 #endif
 		restore_flags(flags);
 	    }
@@ -3222,18 +3412,28 @@
 	case TIOCMBIC:
 	    if (arg & TIOCM_RTS){
 		save_flags(flags); cli();
-		    base_addr[CyCAR<<index] = (u_char)channel;
-		    base_addr[CyMSVR1<<index] = ~CyRTS;
+		cy_writeb((u_long)base_addr+(CyCAR<<index), 
+                          (u_char)channel);
+                if (info->rtsdtr_inv) {
+		    	cy_writeb((u_long)base_addr+(CyMSVR2<<index), ~CyDTR);
+                } else {
+		    	cy_writeb((u_long)base_addr+(CyMSVR1<<index), ~CyRTS);
+                }
 		restore_flags(flags);
 	    }
 	    if (arg & TIOCM_DTR){
 		save_flags(flags); cli();
-		base_addr[CyCAR<<index] = (u_char)channel;
-		base_addr[CyMSVR2<<index] = ~CyDTR;
+		cy_writeb((u_long)base_addr+(CyCAR<<index), (u_char)channel);
+                if (info->rtsdtr_inv) {
+			cy_writeb((u_long)base_addr+(CyMSVR1<<index), ~CyRTS);
+                } else {
+			cy_writeb((u_long)base_addr+(CyMSVR2<<index), ~CyDTR);
+                }
 #ifdef SERIAL_DEBUG_DTR
 		printk("cyc:set_modem_info dropping DTR\n");
 		printk("     status: 0x%x, 0x%x\n",
-		    base_addr[CyMSVR1<<index], base_addr[CyMSVR2<<index]);
+		    cy_readb(base_addr+(CyMSVR1<<index)), 
+                    cy_readb(base_addr+(CyMSVR2<<index)));
 #endif
 		restore_flags(flags);
 	    }
@@ -3241,33 +3441,52 @@
 	case TIOCMSET:
 	    if (arg & TIOCM_RTS){
 		save_flags(flags); cli();
-		    base_addr[CyCAR<<index] = (u_char)channel;
-		    base_addr[CyMSVR1<<index] = CyRTS;
+	        cy_writeb((u_long)base_addr+(CyCAR<<index), (u_char)channel);
+                if (info->rtsdtr_inv) {
+			cy_writeb((u_long)base_addr+(CyMSVR2<<index), CyDTR);
+                } else {
+			cy_writeb((u_long)base_addr+(CyMSVR1<<index), CyRTS);
+                }
 		restore_flags(flags);
 	    }else{
 		save_flags(flags); cli();
-		    base_addr[CyCAR<<index] = (u_char)channel;
-		    base_addr[CyMSVR1<<index] = ~CyRTS;
+		cy_writeb((u_long)base_addr+(CyCAR<<index), (u_char)channel);
+                if (info->rtsdtr_inv) {
+			cy_writeb((u_long)base_addr+(CyMSVR2<<index), ~CyDTR);
+                } else {
+			cy_writeb((u_long)base_addr+(CyMSVR1<<index), ~CyRTS);
+                }
 		restore_flags(flags);
 	    }
 	    if (arg & TIOCM_DTR){
 		save_flags(flags); cli();
-		base_addr[CyCAR<<index] = (u_char)channel;
-		base_addr[CyMSVR2<<index] = CyDTR;
+		cy_writeb((u_long)base_addr+(CyCAR<<index), (u_char)channel);
+                if (info->rtsdtr_inv) {
+			cy_writeb((u_long)base_addr+(CyMSVR1<<index), CyRTS);
+                } else {
+			cy_writeb((u_long)base_addr+(CyMSVR2<<index), CyDTR);
+                }
 #ifdef SERIAL_DEBUG_DTR
 		printk("cyc:set_modem_info raising DTR\n");
 		printk("     status: 0x%x, 0x%x\n",
-		    base_addr[CyMSVR1<<index], base_addr[CyMSVR2<<index]);
+		    cy_readb(base_addr+(CyMSVR1<<index)), 
+                    cy_readb(base_addr+(CyMSVR2<<index)));
 #endif
 		restore_flags(flags);
 	    }else{
 		save_flags(flags); cli();
-		base_addr[CyCAR<<index] = (u_char)channel;
-		base_addr[CyMSVR2<<index] = ~CyDTR;
+		cy_writeb((u_long)base_addr+(CyCAR<<index), (u_char)channel);
+                if (info->rtsdtr_inv) {
+			cy_writeb((u_long)base_addr+(CyMSVR1<<index), ~CyRTS);
+                } else {
+			cy_writeb((u_long)base_addr+(CyMSVR2<<index), ~CyDTR);
+                }
+
 #ifdef SERIAL_DEBUG_DTR
 		printk("cyc:set_modem_info dropping DTR\n");
 		printk("     status: 0x%x, 0x%x\n",
-		    base_addr[CyMSVR1<<index], base_addr[CyMSVR2<<index]);
+		    cy_readb(base_addr+(CyMSVR1<<index)), 
+                    cy_readb(base_addr+(CyMSVR2<<index)));
 #endif
 		restore_flags(flags);
 	    }
@@ -3281,19 +3500,20 @@
 	firm_id = (struct FIRM_ID *)
 		    (cy_card[card].base_addr + ID_ADDRESS);
         if (ISZLOADED(cy_card[card])) {
-	    zfw_ctrl =
-		(struct ZFW_CTRL *)
-		(cy_card[card].base_addr + firm_id->zfwctrl_addr);
+	    zfw_ctrl = (struct ZFW_CTRL *)
+		(cy_card[card].base_addr + cy_readl(&firm_id->zfwctrl_addr));
 	    board_ctrl = &zfw_ctrl->board_ctrl;
 	    ch_ctrl = zfw_ctrl->ch_ctrl;
 
 	    switch (cmd) {
 	    case TIOCMBIS:
 		if (arg & TIOCM_RTS){
-		    ch_ctrl[channel].rs_control |= C_RS_RTS;
+		    cy_writel(&ch_ctrl[channel].rs_control,
+                       cy_readl(&ch_ctrl[channel].rs_control) | C_RS_RTS);
 		}
 		if (arg & TIOCM_DTR){
-		    ch_ctrl[channel].rs_control |= C_RS_DTR;
+		    cy_writel(&ch_ctrl[channel].rs_control,
+                       cy_readl(&ch_ctrl[channel].rs_control) | C_RS_DTR);
 #ifdef SERIAL_DEBUG_DTR
 		    printk("cyc:set_modem_info raising Z DTR\n");
 #endif
@@ -3301,10 +3521,12 @@
 		break;
 	    case TIOCMBIC:
 		if (arg & TIOCM_RTS){
-		    ch_ctrl[channel].rs_control &= ~C_RS_RTS;
+		    cy_writel(&ch_ctrl[channel].rs_control,
+                       cy_readl(&ch_ctrl[channel].rs_control) & ~C_RS_RTS);
 		}
 		if (arg & TIOCM_DTR){
-		    ch_ctrl[channel].rs_control &= ~C_RS_DTR;
+		    cy_writel(&ch_ctrl[channel].rs_control,
+                       cy_readl(&ch_ctrl[channel].rs_control) & ~C_RS_DTR);
 #ifdef SERIAL_DEBUG_DTR
 		    printk("cyc:set_modem_info clearing Z DTR\n");
 #endif
@@ -3312,17 +3534,21 @@
 		break;
 	    case TIOCMSET:
 		if (arg & TIOCM_RTS){
-		    ch_ctrl[channel].rs_control |= C_RS_RTS;
+		    cy_writel(&ch_ctrl[channel].rs_control,
+                       cy_readl(&ch_ctrl[channel].rs_control) | C_RS_RTS);
 		}else{
-		    ch_ctrl[channel].rs_control &= ~C_RS_RTS;
+		    cy_writel(&ch_ctrl[channel].rs_control,
+                       cy_readl(&ch_ctrl[channel].rs_control) & ~C_RS_RTS);
 		}
 		if (arg & TIOCM_DTR){
-		    ch_ctrl[channel].rs_control |= C_RS_DTR;
+		    cy_writel(&ch_ctrl[channel].rs_control,
+                       cy_readl(&ch_ctrl[channel].rs_control) | C_RS_DTR);
 #ifdef SERIAL_DEBUG_DTR
 		    printk("cyc:set_modem_info raising Z DTR\n");
 #endif
 		}else{
-		    ch_ctrl[channel].rs_control &= ~C_RS_DTR;
+		    cy_writel(&ch_ctrl[channel].rs_control,
+                       cy_readl(&ch_ctrl[channel].rs_control) & ~C_RS_DTR);
 #ifdef SERIAL_DEBUG_DTR
 		    printk("cyc:set_modem_info clearing Z DTR\n");
 #endif
@@ -3362,8 +3588,8 @@
 	   A better implementation will use C_CM_SET_BREAK
 	   and C_CM_CLR_BREAK with the appropriate delay.
 	 */
-#if 0
-this appears to wedge the output data stream
+#if 1
+// this appears to wedge the output data stream
 int retval;
         retval = cyz_issue_cmd(&cy_card[info->card],
 		(info->line) - (cy_card[info->card].first_line),
@@ -3408,7 +3634,7 @@
 
 	info->cor3 &= ~CyREC_FIFO;
 	info->cor3 |= value & CyREC_FIFO;
-	base_addr[CyCOR3<<index] = info->cor3;
+	cy_writeb((u_long)base_addr+(CyCOR3<<index), info->cor3);
 	cyy_issue_cmd(base_addr,CyCOR_CHANGE|CyCOR3ch,index);
     } else {
 	// Nothing to do!
@@ -3434,7 +3660,7 @@
 		       (cy_card[card].base_addr
 		       + (cy_chip_offset[chip]<<index));
 
-	tmp = base_addr[CyCOR3<<index] & CyREC_FIFO;
+	tmp = cy_readb(base_addr+(CyCOR3<<index)) & CyREC_FIFO;
 	cy_put_user(tmp,value);
     } else {
 	// Nothing to do!
@@ -3475,7 +3701,7 @@
 		       (cy_card[card].base_addr
 		       + (cy_chip_offset[chip]<<index));
 
-	base_addr[CyRTPR<<index] = value & 0xff;
+	cy_writeb((u_long)base_addr+(CyRTPR<<index), value & 0xff);
     } else {
 	// Nothing to do!
     }
@@ -3500,7 +3726,7 @@
 		       (cy_card[card].base_addr
 		       + (cy_chip_offset[chip]<<index));
 
-	tmp = base_addr[CyRTPR<<index];
+	tmp = cy_readb(base_addr+(CyRTPR<<index));
 	cy_put_user(tmp,value);
     } else {
 	// Nothing to do!
@@ -3524,7 +3750,6 @@
     return 0;
 }/* get_default_timeout */
 
-
 /*
  * This routine allows the tty driver to implement device-
  * specific ioctl's.  If the ioctl number passed in cmd is
@@ -3601,6 +3826,26 @@
         case CYSETDEFTIMEOUT:
             ret_val = set_default_timeout(info, (unsigned long)arg);
             break;
+	case CYSETRFLOW:
+    	    info->rflow = 1;
+	    ret_val = 0;
+	    break;
+	case CYRESETRFLOW:
+    	    info->rflow = 0;
+	    ret_val = 0;
+	    break;
+	case CYSETRTSDTR_INV:
+    	    info->rtsdtr_inv = 1;
+	    ret_val = 0;
+	    break;
+	case CYRESETRTSDTR_INV:
+    	    info->rtsdtr_inv = 0;
+	    ret_val = 0;
+	    break;
+	case CYZPOLLCYCLE:
+            cyz_polling_cycle = (HZ * arg) / 1000;
+	    ret_val = 0;
+	    break;
         case TCSBRK:    /* SVID version: non-zero arg --> no break */
             ret_val = tty_check_change(tty);
             if (ret_val)
@@ -3745,7 +3990,7 @@
 #ifdef SERIAL_DEBUG_THROTTLE
   char buf[64];
         
-    printk("cyc:throttle %s: %d....ttyC%d\n", _tty_name(tty, buf),
+    printk("cyc:throttle %s: %d....ttyC%d\n", tty_name(tty, buf),
            tty->ldisc.chars_in_buffer(tty), info->line);
 #endif
 
@@ -3769,8 +4014,12 @@
 		       + (cy_chip_offset[chip]<<index));
 
 	save_flags(flags); cli();
-	    base_addr[CyCAR<<index] = (u_char)channel;
-	    base_addr[CyMSVR1<<index] = ~CyRTS;
+	cy_writeb((u_long)base_addr+(CyCAR<<index), (u_char)channel);
+	if (info->rtsdtr_inv) {
+		cy_writeb((u_long)base_addr+(CyMSVR2<<index), ~CyDTR);
+	} else {
+		cy_writeb((u_long)base_addr+(CyMSVR1<<index), ~CyRTS);
+	}
 	restore_flags(flags);
     } else {
 	// Nothing to do!
@@ -3796,7 +4045,7 @@
 #ifdef SERIAL_DEBUG_THROTTLE
   char buf[64];
         
-    printk("cyc:throttle %s: %d....ttyC%d\n", _tty_name(tty, buf),
+    printk("cyc:throttle %s: %d....ttyC%d\n", tty_name(tty, buf),
            tty->ldisc.chars_in_buffer(tty), info->line);
 #endif
 
@@ -3820,8 +4069,12 @@
 		       + (cy_chip_offset[chip]<<index));
 
 	save_flags(flags); cli();
-	    base_addr[CyCAR<<index] = (u_char)channel;
-	    base_addr[CyMSVR1<<index] = CyRTS;
+	cy_writeb((u_long)base_addr+(CyCAR<<index), (u_char)channel);
+	if (info->rtsdtr_inv) {
+		cy_writeb((u_long)base_addr+(CyMSVR2<<index), CyDTR);
+	} else {
+		cy_writeb((u_long)base_addr+(CyMSVR1<<index), CyRTS);
+	}
 	restore_flags(flags);
     }else{
 	// Nothing to do!
@@ -3861,9 +4114,10 @@
                            + (cy_chip_offset[chip]<<index));
 
         save_flags(flags); cli();
-            base_addr[CyCAR<<index] =
-	        (u_char)(channel & 0x0003); /* index channel */
-            base_addr[CySRER<<index] &= ~CyTxMpty;
+            cy_writeb((u_long)base_addr+(CyCAR<<index),
+	       (u_char)(channel & 0x0003)); /* index channel */
+            cy_writeb((u_long)base_addr+(CySRER<<index), 
+               cy_readb(base_addr+(CySRER<<index)) & ~CyTxMpty);
         restore_flags(flags);
     } else {
 	// Nothing to do!
@@ -3900,8 +4154,10 @@
 		       + (cy_chip_offset[chip]<<index));
 
         save_flags(flags); cli();
-            base_addr[CyCAR<<index] = (u_char)(channel & 0x0003);
-            base_addr[CySRER<<index] |= CyTxMpty;
+            cy_writeb((u_long)base_addr+(CyCAR<<index),
+	       (u_char)(channel & 0x0003)); /* index channel */
+            cy_writeb((u_long)base_addr+(CySRER<<index), 
+               cy_readb(base_addr+(CySRER<<index)) | CyTxMpty);
         restore_flags(flags);
     } else {
 	// Nothing to do!
@@ -3968,24 +4224,25 @@
  * ---------------------------------------------------------------------
  */
 
-
 /* initialize chips on Cyclom-Y card -- return number of valid
    chips (which is number of ports/4) */
-__initfunc(static int
-cyy_init_card(unsigned char *true_base_addr,int index))
+__initfunc(static unsigned short
+cyy_init_card(volatile ucchar *true_base_addr,int index))
 {
   unsigned int chip_number;
-  unsigned char* base_addr;
+  volatile ucchar* base_addr;
 
-    true_base_addr[Cy_HwReset<<index] = 0; /* Cy_HwReset is 0x1400 */
-    true_base_addr[Cy_ClrIntr<<index] = 0; /* Cy_ClrIntr is 0x1800 */
+    cy_writeb((u_long)true_base_addr+(Cy_HwReset<<index), 0); 
+						/* Cy_HwReset is 0x1400 */
+    cy_writeb((u_long)true_base_addr+(Cy_ClrIntr<<index), 0); 
+						/* Cy_ClrIntr is 0x1800 */
     udelay(500L);
 
-    for(chip_number=0; chip_number<CyMaxChipsPerCard; chip_number++){
+    for(chip_number=0; chip_number<CyMAX_CHIPS_PER_CARD; chip_number++){
         base_addr = true_base_addr
 	               + (cy_chip_offset[chip_number]<<index);
         udelay(1000L);
-        if(base_addr[CyCCR<<index] != 0x00){
+        if(cy_readb(base_addr+(CyCCR<<index)) != 0x00){
             /*************
             printk(" chip #%d at %#6lx is never idle (CCR != 0)\n",
                chip_number, (unsigned long)base_addr);
@@ -3993,7 +4250,7 @@
             return chip_number;
         }
 
-        base_addr[CyGFRCR<<index] = 0;
+        cy_writeb((u_long)base_addr+(CyGFRCR<<index), 0);
         udelay(10L);
 
         /* The Cyclom-16Y does not decode address bit 9 and therefore
@@ -4003,16 +4260,16 @@
            and this must be a Cyclom-16Y, not a Cyclom-32Ye.
         */
         if (chip_number == 4
-        && *(true_base_addr
+        && cy_readb(true_base_addr
 	    + (cy_chip_offset[0]<<index)
 	    + (CyGFRCR<<index)) == 0){
             return chip_number;
         }
 
-        base_addr[CyCCR<<index] = CyCHIP_RESET;
+        cy_writeb((u_long)base_addr+(CyCCR<<index), CyCHIP_RESET);
         udelay(1000L);
 
-        if(base_addr[CyGFRCR<<index] == 0x00){
+        if(cy_readb(base_addr+(CyGFRCR<<index)) == 0x00){
             /*
             printk(" chip #%d at %#6lx is not responding ",
                chip_number, (unsigned long)base_addr);
@@ -4020,7 +4277,7 @@
             */
             return chip_number;
         }
-        if((0xf0 & base_addr[CyGFRCR<<index]) != 0x40){
+        if((0xf0 & (cy_readb(base_addr+(CyGFRCR<<index)))) != 0x40){
             /*
             printk(" chip #%d at %#6lx is not valid (GFRCR == %#2x)\n",
                chip_number, (unsigned long)base_addr,
@@ -4028,10 +4285,16 @@
             */
             return chip_number;
         }
-        base_addr[CyGCR<<index] = CyCH0_SERIAL;
-        base_addr[CyPPR<<index] = 244;
-	                       /* better value than CyCLOCK_25_1MS * 5
-					  to run clock at 200 Hz */
+        cy_writeb((u_long)base_addr+(CyGCR<<index), CyCH0_SERIAL);
+        if (cy_readb(base_addr+(CyGFRCR<<index)) >= 0x48){
+	    /* It is a CD1400 rev. J or later */
+	    /* Impossible to reach 5ms with this chip. 
+	       Changed to 2ms instead (f = 500 Hz). */
+	    cy_writeb((u_long)base_addr+(CyPPR<<index), CyCLOCK_60_2MS);
+	} else {
+	    /* f = 200 Hz */
+	    cy_writeb((u_long)base_addr+(CyPPR<<index), CyCLOCK_25_5MS);
+	}
 
         /*
         printk(" chip #%d at %#6lx is rev 0x%2x\n",
@@ -4039,11 +4302,9 @@
 	       base_addr[CyGFRCR<<index]);
         */
     }
-
     return chip_number;
 } /* cyy_init_card */
 
-
 /*
  * ---------------------------------------------------------------------
  * cy_detect_isa() - Probe for Cyclom-Y/ISA boards.
@@ -4053,9 +4314,9 @@
 __initfunc(static int
 cy_detect_isa(void))
 {
-  unsigned int          cy_isa_irq,nboard;
-  unsigned char         *cy_isa_address;
-  unsigned short        i,j,cy_isa_nchan;
+  unsigned short	cy_isa_irq,nboard;
+  volatile ucchar	*cy_isa_address;
+  unsigned short	i,j,cy_isa_nchan;
 
         nboard = 0;
 
@@ -4067,10 +4328,13 @@
                 }
 
                 /* probe for CD1400... */
-#if (LINUX_VERSION_CODE >= 0x020100)
-		cy_isa_address = ioremap((unsigned int)cy_isa_address,0x2000);
+
+#if !defined(__alpha__) && (LINUX_VERSION_CODE >= 0x020100)
+		cy_isa_address = ioremap((unsigned int)cy_isa_address,
+                                                       CyISA_Ywin);
 #endif
-                cy_isa_nchan = 4 * cyy_init_card(cy_isa_address,0);
+                cy_isa_nchan = CyPORTS_PER_CHIP * 
+                     cyy_init_card(cy_isa_address,0);
                 if (cy_isa_nchan == 0) {
                         continue;
                 }
@@ -4078,15 +4342,15 @@
                 /* find out the board's irq by probing */
                 cy_isa_irq = do_auto_irq(cy_isa_address);
                 if (cy_isa_irq == 0) {
-                        printk("Cyclom-Y/ISA found at 0x%x ",
-                                (unsigned int) cy_isa_address);
+                        printk("Cyclom-Y/ISA found at 0x%lx ",
+                                (unsigned long) cy_isa_address);
                         printk("but the IRQ could not be detected.\n");
                         continue;
                 }
 
                 if((cy_next_channel+cy_isa_nchan) > NR_PORTS) {
-                        printk("Cyclom-Y/ISA found at 0x%x ",
-                                (unsigned int) cy_isa_address);
+                        printk("Cyclom-Y/ISA found at 0x%lx ",
+                                (unsigned long) cy_isa_address);
                         printk("but no more channels are available.\n");
                         printk("Change NR_PORTS in cyclades.c and recompile kernel.\n");
                         return(nboard);
@@ -4096,8 +4360,8 @@
                         if (cy_card[j].base_addr == 0)  break;
                 }
                 if (j == NR_CARDS) {    /* no more cy_cards available */
-                        printk("Cyclom-Y/ISA found at 0x%x ",
-                                (unsigned int) cy_isa_address);
+                        printk("Cyclom-Y/ISA found at 0x%lx ",
+                                (unsigned long) cy_isa_address);
                         printk("but no more cards can be used .\n");
                         printk("Change NR_CARDS in cyclades.c and recompile kernel.\n");
                         return(nboard);
@@ -4107,15 +4371,15 @@
                 if(request_irq(cy_isa_irq, cyy_interrupt,
 				   SA_INTERRUPT, "cyclomY", NULL))
                 {
-                        printk("Cyclom-Y/ISA found at 0x%x ",
-                                (unsigned int) cy_isa_address);
+                        printk("Cyclom-Y/ISA found at 0x%lx ",
+                                (unsigned long) cy_isa_address);
                         printk("but could not allocate IRQ#%d.\n",
                                 cy_isa_irq);
                         return(nboard);
                 }
 
                 /* set cy_card */
-                cy_card[j].base_addr = (int) cy_isa_address;
+                cy_card[j].base_addr = (u_long) cy_isa_address;
                 cy_card[j].ctl_addr = 0;
                 cy_card[j].irq = (int) cy_isa_irq;
                 cy_card[j].bus_index = 0;
@@ -4125,10 +4389,10 @@
                 nboard++;
                         
                 /* print message */
-                printk("Cyclom-Y/ISA #%d: 0x%x-0x%x, IRQ%d, ",
-                        j+1, (unsigned int) cy_isa_address,
-                        (unsigned int)(cy_isa_address + 0x1fff),
-			cy_isa_irq);
+                printk("Cyclom-Y/ISA #%d: 0x%lx-0x%lx, IRQ%d, ",
+                    j+1, (unsigned long) cy_isa_address,
+                    (unsigned long)(cy_isa_address + (CyISA_Ywin - 1)),
+		    cy_isa_irq);
                 printk("%d channels starting from port %d.\n",
                         cy_isa_nchan, cy_next_channel);
                 cy_next_channel += cy_isa_nchan;
@@ -4150,11 +4414,11 @@
   unsigned char         cyy_bus, cyy_dev_fn, cyy_rev_id;
   unsigned long         pci_intr_ctrl;
   unsigned char         cy_pci_irq;
-  unsigned int          cy_pci_addr0, cy_pci_addr1, cy_pci_addr2;
+  uclong                cy_pci_addr0, cy_pci_addr1, cy_pci_addr2;
   unsigned short        i,j,cy_pci_nchan;
   unsigned short        device_id,dev_index = 0,board_index = 0;
-  unsigned long         mailbox;
-  unsigned int          Ze_addr0[NR_CARDS], Ze_addr2[NR_CARDS], ZeIndex = 0;
+  uclong		mailbox;
+  uclong		Ze_addr0[NR_CARDS], Ze_addr2[NR_CARDS], ZeIndex = 0;
 
         if(pcibios_present() == 0) {    /* PCI bus not present */
                 return(0);
@@ -4181,13 +4445,17 @@
                 pcibios_read_config_byte(cyy_bus, cyy_dev_fn,
                                  PCI_INTERRUPT_LINE, &cy_pci_irq);
                 pcibios_read_config_dword(cyy_bus, cyy_dev_fn,
-                                  PCI_BASE_ADDRESS_0, &cy_pci_addr0);
+                                  PCI_BASE_ADDRESS_0, 
+				  (unsigned int *) &cy_pci_addr0);
                 pcibios_read_config_dword(cyy_bus, cyy_dev_fn,
-                                  PCI_BASE_ADDRESS_1, &cy_pci_addr1);
+                                  PCI_BASE_ADDRESS_1, 
+				  (unsigned int *) &cy_pci_addr1);
                 pcibios_read_config_dword(cyy_bus, cyy_dev_fn,
-                                  PCI_BASE_ADDRESS_2, &cy_pci_addr2);
+                                  PCI_BASE_ADDRESS_2, 
+				  (unsigned int *) &cy_pci_addr2);
                 pcibios_read_config_byte(cyy_bus, cyy_dev_fn,
                                   PCI_REVISION_ID, &cyy_rev_id);
+
     if ((device_id == PCI_DEVICE_ID_CYCLOM_Y_Lo)
 	   || (device_id == PCI_DEVICE_ID_CYCLOM_Y_Hi)){
 #ifdef CY_PCI_DEBUG
@@ -4195,34 +4463,48 @@
 		cyy_bus, cyy_dev_fn);
             printk("rev_id=%d) IRQ%d\n",
 		cyy_rev_id, (int)cy_pci_irq);
-            printk("Cyclom-Y/PCI:found  winaddr=0x%x ioaddr=0x%x\n",
-		cy_pci_addr2, cy_pci_addr1);
+            printk("Cyclom-Y/PCI:found  winaddr=0x%lx ioaddr=0x%lx\n",
+		(ulong)cy_pci_addr2, (ulong)cy_pci_addr1);
 #endif
                 cy_pci_addr1  &= 0xfffffffc;
                 cy_pci_addr2  &= 0xfffffff0;
 
-#if (LINUX_VERSION_CODE < 0x020100)
+#if defined(__alpha__)
+                if (device_id  == PCI_DEVICE_ID_CYCLOM_Y_Lo) { /* below 1M? */
+		    printk("Cyclom-Y/PCI (bus=0x0%x, pci_id=0x%x, ",
+		        cyy_bus, cyy_dev_fn);
+		    printk("rev_id=%d) IRQ%d\n",
+		        cyy_rev_id, (int)cy_pci_irq);
+                    printk("Cyclom-Y/PCI:found  winaddr=0x%lx ioaddr=0x%lx\n",
+		        (ulong)cy_pci_addr2, (ulong)cy_pci_addr1);
+	            printk("Cyclom-Y/PCI not supported for low addresses in "
+                           "Alpha systems.\n");
+		    i--;
+	            continue;
+                }
+#else
+#if (LINUX_VERSION_CODE < 0x020100) 
                 if ((ulong)cy_pci_addr2 >= 0x100000)  /* above 1M? */
 #endif
-		    cy_pci_addr2 =
-			(unsigned int) ioremap(cy_pci_addr2,CyPCI_Ywin);
+                    cy_pci_addr2 = (ulong) ioremap(cy_pci_addr2, CyPCI_Ywin);
+#endif
 
 #ifdef CY_PCI_DEBUG
-            printk("Cyclom-Y/PCI: relocate winaddr=0x%x ioaddr=0x%x\n",
-		cy_pci_addr2, cy_pci_addr1);
+            printk("Cyclom-Y/PCI: relocate winaddr=0x%lx ioaddr=0x%lx\n",
+		(u_long)cy_pci_addr2, (u_long)cy_pci_addr1);
 #endif
-                cy_pci_nchan = 4 * cyy_init_card((unsigned char *)
-                                                cy_pci_addr2,1);
+                cy_pci_nchan = (unsigned short)(CyPORTS_PER_CHIP * 
+                       cyy_init_card((volatile ucchar *)cy_pci_addr2, 1));
                 if(cy_pci_nchan == 0) {
                         printk("Cyclom-Y PCI host card with ");
-                        printk("no Serial-Modules at 0x%x.\n",
-			    (unsigned int) cy_pci_addr2);
+                        printk("no Serial-Modules at 0x%lx.\n",
+			    (ulong) cy_pci_addr2);
                         i--;
                         continue;
                 }
                 if((cy_next_channel+cy_pci_nchan) > NR_PORTS) {
-                        printk("Cyclom-Y/PCI found at 0x%x ",
-			    (unsigned int) cy_pci_addr2);
+                        printk("Cyclom-Y/PCI found at 0x%lx ",
+			    (ulong) cy_pci_addr2);
                         printk("but no channels are available.\n");
                         printk("Change NR_PORTS in cyclades.c and recompile kernel.\n");
                         return(i);
@@ -4232,8 +4514,8 @@
                         if (cy_card[j].base_addr == 0)  break;
                 }
                 if (j == NR_CARDS) {    /* no more cy_cards available */
-                        printk("Cyclom-Y/PCI found at 0x%x ",
-			    (unsigned int) cy_pci_addr2);
+                        printk("Cyclom-Y/PCI found at 0x%lx ",
+			    (ulong) cy_pci_addr2);
                         printk("but no more cards can be used.\n");
                         printk("Change NR_CARDS in cyclades.c and recompile kernel.\n");
                         return(i);
@@ -4243,15 +4525,15 @@
                 if(request_irq(cy_pci_irq, cyy_interrupt,
 		        SA_INTERRUPT, "cyclomY", NULL))
                 {
-                        printk("Cyclom-Y/PCI found at 0x%x ",
-			    (unsigned int) cy_pci_addr2);
+                        printk("Cyclom-Y/PCI found at 0x%lx ",
+			    (ulong) cy_pci_addr2);
                         printk("but could not allocate IRQ%d.\n",
 			    cy_pci_irq);
                         return(i);
                 }
 
                 /* set cy_card */
-                cy_card[j].base_addr = (int) cy_pci_addr2;
+                cy_card[j].base_addr = (ulong)cy_pci_addr2;
                 cy_card[j].ctl_addr = 0;
                 cy_card[j].irq = (int) cy_pci_irq;
                 cy_card[j].bus_index = 1;
@@ -4266,9 +4548,11 @@
 				| inw(cy_pci_addr1+0x6a)<<16);
 
                 /* print message */
-                printk("Cyclom-Y/PCI #%d: 0x%x-0x%x, IRQ%d, ",
-		    j+1, cy_pci_addr2, (cy_pci_addr2 + CyPCI_Ywin - 1),
-		    (int)cy_pci_irq);
+                printk("Cyclom-Y/PCI #%d: 0x%lx-0x%lx, IRQ%d, ",
+		       j+1, 
+		       (ulong)cy_pci_addr2, 
+		       (ulong)(cy_pci_addr2 + CyPCI_Ywin - 1),
+		       (int)cy_pci_irq);
                 printk("%d channels starting from port %d.\n",
 		    cy_pci_nchan, cy_next_channel);
 
@@ -4279,8 +4563,8 @@
 		    cyy_bus, cyy_dev_fn);
 		printk("rev_id=%d) IRQ%d\n",
 		    cyy_rev_id, (int)cy_pci_irq);
-		printk("Cyclades-Z/PCI: found winaddr=0x%x ctladdr=0x%x\n",
-		    cy_pci_addr2, cy_pci_addr0);
+		printk("Cyclades-Z/PCI: found winaddr=0x%lx ctladdr=0x%lx\n",
+		    (ulong)cy_pci_addr2, (ulong)cy_pci_addr0);
 	    printk("Cyclades-Z/PCI not supported for low addresses\n");
 	    break;
     }else if (device_id == PCI_DEVICE_ID_CYCLOM_Z_Hi){
@@ -4289,25 +4573,29 @@
 		cyy_bus, cyy_dev_fn);
             printk("rev_id=%d) IRQ%d\n",
 		cyy_rev_id, (int)cy_pci_irq);
-            printk("Cyclades-Z/PCI: found winaddr=0x%x ctladdr=0x%x\n",
-                cy_pci_addr2, cy_pci_addr0);
+            printk("Cyclades-Z/PCI: found winaddr=0x%lx ctladdr=0x%lx\n",
+                (ulong)cy_pci_addr2, (ulong)cy_pci_addr0);
 #endif
                 cy_pci_addr0 &= 0xfffffff0;
+#if !defined(__alpha__)
                 cy_pci_addr0 = (unsigned int) ioremap(
                                cy_pci_addr0 & PAGE_MASK,
                                PAGE_ALIGN(CyPCI_Zctl))
                                + (cy_pci_addr0 & (PAGE_SIZE-1));
-
-		mailbox = ((struct RUNTIME_9060 *) cy_pci_addr0)->mail_box_0;
+#endif
+		mailbox = (uclong)cy_readl(&((struct RUNTIME_9060 *) 
+			   cy_pci_addr0)->mail_box_0);
                 cy_pci_addr2 &= 0xfffffff0;
 		if (mailbox == ZE_V1) {
+#if !defined(__alpha__)
                	    cy_pci_addr2 = (unsigned int) ioremap(
 	            	cy_pci_addr2 & PAGE_MASK,
 	            	PAGE_ALIGN(CyPCI_Ze_win))
 	            	+ (cy_pci_addr2 & (PAGE_SIZE-1));
+#endif
 		    if (ZeIndex == NR_CARDS) {
-			printk("Cyclades-Z/PCI found at 0x%x ",
-				(unsigned int) cy_pci_addr2);
+			printk("Cyclades-Ze/PCI found at 0x%lx ",
+				(ulong)cy_pci_addr2);
 			printk("but no more cards can be used.\n");
                         printk("Change NR_CARDS in cyclades.c and recompile kernel.\n");
 		    } else {
@@ -4318,24 +4606,28 @@
 		    i--;
 		    continue;
 		} else {
+#if !defined(__alpha__)
                     cy_pci_addr2 = (unsigned int) ioremap(
 			cy_pci_addr2 & PAGE_MASK,
 			PAGE_ALIGN(CyPCI_Zwin))
 			+ (cy_pci_addr2 & (PAGE_SIZE-1));
+#endif
 		}
 
 #ifdef CY_PCI_DEBUG
-            printk("Cyclades-Z/PCI: relocate winaddr=0x%x ctladdr=0x%x\n",
-                cy_pci_addr2, cy_pci_addr0);
+            printk("Cyclades-Z/PCI: relocate winaddr=0x%lx ctladdr=0x%lx\n",
+                (ulong)cy_pci_addr2, (ulong)cy_pci_addr0);
 	    if (mailbox == ZO_V1) {
-		((struct RUNTIME_9060 *)(cy_pci_addr0))
-					    ->loc_addr_base = WIN_CREG;
+		cy_writel(&((struct RUNTIME_9060 *)
+			  (cy_pci_addr0))->loc_addr_base, WIN_CREG);
 		PAUSE
-		printk("Cyclades-Z/PCI: FPGA id %lx, ver %lx\n",
-		    0xff & ((struct CUSTOM_REG *)(cy_pci_addr2))->fpga_id,
-		    0xff & ((struct CUSTOM_REG *)(cy_pci_addr2))->fpga_version);
-		((struct RUNTIME_9060 *)(cy_pci_addr0))
-						->loc_addr_base = WIN_RAM;
+		printk("Cyclades-8Zo/PCI: FPGA id %lx, ver %lx\n",
+		       (ulong)(0xff & cy_readl(&((struct CUSTOM_REG *)
+		        (cy_pci_addr2))->fpga_id)),
+		       (ulong)(0xff & cy_readl(&((struct CUSTOM_REG *)
+		        (cy_pci_addr2))->fpga_version)));
+		cy_writel(&((struct RUNTIME_9060 *)
+			  (cy_pci_addr0))->loc_addr_base, WIN_RAM);
 	    } else {
 		printk("Cyclades-Z/PCI: New Cyclades-Z board.  FPGA not loaded\n");
 	    }
@@ -4345,8 +4637,8 @@
 	       until it has been properly initialized.
 	     */
 		PAUSE
-		if (mailbox == ZO_V1)
-		    *(unsigned long *)(cy_pci_addr2+ID_ADDRESS) = 0L;
+		if ((mailbox == ZO_V1) || (mailbox == ZO_V2))
+		    cy_writel((ulong)(cy_pci_addr2+ID_ADDRESS), 0L);
 
                 /* This must be a Cyclades-8Zo/PCI.  The extendable
                    version will have a different device_id and will
@@ -4354,8 +4646,8 @@
                 cy_pci_nchan = 8;
 
                 if((cy_next_channel+cy_pci_nchan) > NR_PORTS) {
-                        printk("Cyclades-Z/PCI found at 0x%x ",
-			    (unsigned int) cy_pci_addr2);
+                        printk("Cyclades-8Zo/PCI found at 0x%lx ",
+			    (ulong)cy_pci_addr2);
                         printk("but no channels are available.\n");
                         printk("Change NR_PORTS in cyclades.c and recompile kernel.\n");
                         return(i);
@@ -4366,8 +4658,8 @@
                         if (cy_card[j].base_addr == 0)  break;
                 }
                 if (j == NR_CARDS) {    /* no more cy_cards available */
-		    printk("Cyclades-Z/PCI found at 0x%x ",
-			(unsigned int) cy_pci_addr2);
+		    printk("Cyclades-8Zo/PCI found at 0x%lx ",
+			(ulong)cy_pci_addr2);
 		    printk("but no more cards can be used.\n");
                     printk("Change NR_CARDS in cyclades.c and recompile kernel.\n");
 		    return(i);
@@ -4380,12 +4672,13 @@
 		    {
 			printk("Could not allocate IRQ%d ",
 			    cy_pci_irq);
-			printk("for Cyclades-Z/PCI at 0x%x.\n",
-			    (unsigned int) cy_pci_addr2);
+			printk("for Cyclades-8Zo/PCI at 0x%lx.\n",
+			    (ulong)cy_pci_addr2);
 			return(i);
 		    }
 		}
 
+
                 /* set cy_card */
                 cy_card[j].base_addr = cy_pci_addr2;
                 cy_card[j].ctl_addr = cy_pci_addr0;
@@ -4398,14 +4691,14 @@
                 /* print message */
 		/* don't report IRQ if board is no IRQ */
 		if( (cy_pci_irq < 15) && (cy_pci_irq > 1) ) {
-		    printk("Cyclades-Z/PCI #%d: 0x%x-0x%x, IRQ%d, ",
-			j+1,cy_pci_addr2,
-			(cy_pci_addr2 + CyPCI_Zwin - 1),
+		    printk("Cyclades-8Zo/PCI #%d: 0x%lx-0x%lx, IRQ%d, ",
+			j+1,(ulong)cy_pci_addr2,
+			(ulong)(cy_pci_addr2 + CyPCI_Zwin - 1),
 			(int)cy_pci_irq);
 		}else{
-		    printk("Cyclades-Z/PCI #%d: 0x%x-0x%x, ",
-			j+1,cy_pci_addr2,
-			(cy_pci_addr2 + CyPCI_Zwin - 1));
+		    printk("Cyclades-8Zo/PCI #%d: 0x%lx-0x%lx, ",
+			j+1,(ulong)cy_pci_addr2,
+			(ulong)(cy_pci_addr2 + CyPCI_Zwin - 1));
 		}
                 printk("%d channels starting from port %d.\n",
 		    cy_pci_nchan,cy_next_channel);
@@ -4421,10 +4714,11 @@
 		Ze_addr2[j] = Ze_addr2[j+1];
 	    }
 	    ZeIndex--;
-	    mailbox = ((struct RUNTIME_9060 *) cy_pci_addr0)->mail_box_0;
+		mailbox = (uclong)cy_readl(&((struct RUNTIME_9060 *) 
+					   cy_pci_addr0)->mail_box_0);
 #ifdef CY_PCI_DEBUG
-            printk("Cyclades-Z/PCI: relocate winaddr=0x%x ctladdr=0x%x\n",
-                cy_pci_addr2, cy_pci_addr0);
+            printk("Cyclades-Z/PCI: relocate winaddr=0x%lx ctladdr=0x%lx\n",
+                (ulong)cy_pci_addr2, (ulong)cy_pci_addr0);
 	    printk("Cyclades-Z/PCI: New Cyclades-Z board.  FPGA not loaded\n");
 #endif
 	    /* The following clears the firmware id word.  This ensures
@@ -4436,8 +4730,8 @@
                 cy_pci_nchan = ZE_V1_NPORTS;
 
                 if((cy_next_channel+cy_pci_nchan) > NR_PORTS) {
-                        printk("Cyclades-Z/PCI found at 0x%x ",
-			    (unsigned int) cy_pci_addr2);
+                        printk("Cyclades-Ze/PCI found at 0x%lx ",
+			    (ulong)cy_pci_addr2);
                         printk("but no channels are available.\n");
                         printk("Change NR_PORTS in cyclades.c and recompile kernel.\n");
                         return(i);
@@ -4448,8 +4742,8 @@
                         if (cy_card[j].base_addr == 0)  break;
                 }
                 if (j == NR_CARDS) {    /* no more cy_cards available */
-		    printk("Cyclades-Z/PCI found at 0x%x ",
-			(unsigned int) cy_pci_addr2);
+		    printk("Cyclades-Ze/PCI found at 0x%lx ",
+			(ulong)cy_pci_addr2);
 		    printk("but no more cards can be used.\n");
                     printk("Change NR_CARDS in cyclades.c and recompile kernel.\n");
 		    return(i);
@@ -4462,8 +4756,8 @@
 		    {
 			printk("Could not allocate IRQ%d ",
 			    cy_pci_irq);
-			printk("for Cyclades-Z/PCI at 0x%x.\n",
-			    (unsigned int) cy_pci_addr2);
+			printk("for Cyclades-Ze/PCI at 0x%lx.\n",
+			    (ulong) cy_pci_addr2);
 			return(i);
 		    }
 		}
@@ -4480,21 +4774,21 @@
                 /* print message */
 		/* don't report IRQ if board is no IRQ */
 		if( (cy_pci_irq < 15) && (cy_pci_irq > 1) ) {
-		    printk("Cyclades-Z/PCI #%d: 0x%x-0x%x, IRQ%d, ",
-			j+1,cy_pci_addr2,
-			(cy_pci_addr2 + CyPCI_Ze_win - 1),
+		    printk("Cyclades-Ze/PCI #%d: 0x%lx-0x%lx, IRQ%d, ",
+			j+1,(ulong)cy_pci_addr2,
+			(ulong)(cy_pci_addr2 + CyPCI_Ze_win - 1),
 			(int)cy_pci_irq);
 		}else{
-		    printk("Cyclades-Z/PCI #%d: 0x%x-0x%x, ",
-			j+1,cy_pci_addr2,
-			(cy_pci_addr2 + CyPCI_Ze_win - 1));
+		    printk("Cyclades-Ze/PCI #%d: 0x%lx-0x%lx, ",
+			j+1,(ulong)cy_pci_addr2,
+			(ulong)(cy_pci_addr2 + CyPCI_Ze_win - 1));
 		}
                 printk("%d channels starting from port %d.\n",
 		    cy_pci_nchan,cy_next_channel);
                 cy_next_channel += cy_pci_nchan;
         }
 	if (ZeIndex != 0) {
-	    printk("Cyclades-Z/PCI found at 0x%x ",
+	    printk("Cyclades-Ze/PCI found at 0x%x ",
 		(unsigned int) Ze_addr2[0]);
 	    printk("but no more cards can be used.\n");
             printk("Change NR_CARDS in cyclades.c and recompile kernel.\n");
@@ -4520,7 +4814,7 @@
     tmp = strrchr(rcsdate, ' '); *tmp = '\0';
     printk("Cyclom driver %s %s\n",
         rcsvers, rcsdate);
-    printk("\tbuilt %s %s\n",
+    printk("        built %s %s\n",
 	__DATE__, __TIME__);
 } /* show_version */
 
@@ -4549,7 +4843,7 @@
   struct cyclades_port  *info;
   struct cyclades_card *cinfo;
   int number_z_boards = 0;
-  int board,port,i;
+  int board,port,i,index;
   unsigned long mailbox;
   int nports;
 
@@ -4623,7 +4917,7 @@
 
     /* look for isa boards */
     cy_isa_nboard = cy_detect_isa();
-
+    
     /* look for pci boards */
     cy_pci_nboard = cy_detect_pci();
 
@@ -4649,10 +4943,10 @@
     /* initialize per-port data structures for each valid board found */
     for (board = 0 ; board < cy_nboard ; board++) {
             cinfo = &cy_card[board];
-            if (cinfo->num_chips == 1){ /* Cyclades-8Zo/PCI */
+            if (cinfo->num_chips == 1){ /* Cyclades-Z */
 		number_z_boards++;
-		mailbox = ((struct RUNTIME_9060 *)
-			cy_card[board].ctl_addr)->mail_box_0;
+		mailbox = cy_readl(&((struct RUNTIME_9060 *)
+			     cy_card[board].ctl_addr)->mail_box_0);
 		nports = (mailbox == ZE_V1) ? ZE_V1_NPORTS : 8;
                 for (port = cinfo->first_line ;
                      port < cinfo->first_line + nports;
@@ -4680,7 +4974,7 @@
                     info->event = 0;
                     info->count = 0;
 #ifdef SERIAL_DEBUG_COUNT
-        printk("cyc:cy_init(1) setting Z count to 0\n");
+//        printk("cyc:cy_init(1) setting Z count to 0\n");
 #endif
                     info->blocked_open = 0;
                     info->default_threshold = 0;
@@ -4705,6 +4999,7 @@
                 }
                 continue;
             }else{ /* Cyclom-Y of some kind*/
+                index = cinfo->bus_index;
                 for (port = cinfo->first_line ;
                      port < cinfo->first_line + 4*cinfo->num_chips ;
                      port++)
@@ -4722,16 +5017,28 @@
                     info->cor3 = 0x08; /* _very_ small rcv threshold */
                     info->cor4 = 0;
                     info->cor5 = 0;
-                    info->tbpr = baud_bpr[13]; /* Tx BPR */
-                    info->tco = baud_co[13]; /* Tx CO */
-                    info->rbpr = baud_bpr[13]; /* Rx BPR */
-                    info->rco = baud_co[13]; /* Rx CO */
                     info->close_delay = 0;
+                    if (cy_readb(cinfo->base_addr+(CyGFRCR<<index)) >= 0x48) {
+                        /* It is a CD1400 rev. J or later */
+                        info->tbpr = baud_bpr_60[13]; /* Tx BPR */
+                        info->tco = baud_co_60[13]; /* Tx CO */
+                        info->rbpr = baud_bpr_60[13]; /* Rx BPR */
+                        info->rco = baud_co_60[13]; /* Rx CO */
+                        info->rflow = 0;
+                        info->rtsdtr_inv = 1;
+                    } else {
+                        info->tbpr = baud_bpr_25[13]; /* Tx BPR */
+                        info->tco = baud_co_25[13]; /* Tx CO */
+                        info->rbpr = baud_bpr_25[13]; /* Rx BPR */
+                        info->rco = baud_co_25[13]; /* Rx CO */
+                        info->rflow = 0;
+                        info->rtsdtr_inv = 0;
+                    }
                     info->x_char = 0;
                     info->event = 0;
                     info->count = 0;
 #ifdef SERIAL_DEBUG_COUNT
-        printk("cyc:cy_init(2) setting Y count to 0\n");
+//        printk("cyc:cy_init(2) setting Y count to 0\n");
 #endif
                     info->blocked_open = 0;
                     info->default_threshold = 0;
@@ -4874,54 +5181,54 @@
 
 /* Global Registers */
 
-        printk(" CyGFRCR %x\n", base_addr[CyGFRCR<<index]);
-        printk(" CyCAR %x\n", base_addr[CyCAR<<index]);
-        printk(" CyGCR %x\n", base_addr[CyGCR<<index]);
-        printk(" CySVRR %x\n", base_addr[CySVRR<<index]);
-        printk(" CyRICR %x\n", base_addr[CyRICR<<index]);
-        printk(" CyTICR %x\n", base_addr[CyTICR<<index]);
-        printk(" CyMICR %x\n", base_addr[CyMICR<<index]);
-        printk(" CyRIR %x\n", base_addr[CyRIR<<index]);
-        printk(" CyTIR %x\n", base_addr[CyTIR<<index]);
-        printk(" CyMIR %x\n", base_addr[CyMIR<<index]);
-        printk(" CyPPR %x\n", base_addr[CyPPR<<index]);
+        printk(" CyGFRCR %x\n", cy_readb(base_addr + CyGFRCR<<index));
+        printk(" CyCAR %x\n", cy_readb(base_addr + CyCAR<<index));
+        printk(" CyGCR %x\n", cy_readb(base_addr + CyGCR<<index));
+        printk(" CySVRR %x\n", cy_readb(base_addr + CySVRR<<index));
+        printk(" CyRICR %x\n", cy_readb(base_addr + CyRICR<<index));
+        printk(" CyTICR %x\n", cy_readb(base_addr + CyTICR<<index));
+        printk(" CyMICR %x\n", cy_readb(base_addr + CyMICR<<index));
+        printk(" CyRIR %x\n", cy_readb(base_addr + CyRIR<<index));
+        printk(" CyTIR %x\n", cy_readb(base_addr + CyTIR<<index));
+        printk(" CyMIR %x\n", cy_readb(base_addr + CyMIR<<index));
+        printk(" CyPPR %x\n", cy_readb(base_addr + CyPPR<<index));
 
-        base_addr[CyCAR<<index] = (u_char)channel;
+        cy_writeb(base_addr + CyCAR<<index, (u_char)channel);
 
 /* Virtual Registers */
 
-        printk(" CyRIVR %x\n", base_addr[CyRIVR<<index]);
-        printk(" CyTIVR %x\n", base_addr[CyTIVR<<index]);
-        printk(" CyMIVR %x\n", base_addr[CyMIVR<<index]);
-        printk(" CyMISR %x\n", base_addr[CyMISR<<index]);
+        printk(" CyRIVR %x\n", cy_readb(base_addr + CyRIVR<<index));
+        printk(" CyTIVR %x\n", cy_readb(base_addr + CyTIVR<<index));
+        printk(" CyMIVR %x\n", cy_readb(base_addr + CyMIVR<<index));
+        printk(" CyMISR %x\n", cy_readb(base_addr + CyMISR<<index));
 
 /* Channel Registers */
 
-        printk(" CyCCR %x\n", base_addr[CyCCR<<index]);
-        printk(" CySRER %x\n", base_addr[CySRER<<index]);
-        printk(" CyCOR1 %x\n", base_addr[CyCOR1<<index]);
-        printk(" CyCOR2 %x\n", base_addr[CyCOR2<<index]);
-        printk(" CyCOR3 %x\n", base_addr[CyCOR3<<index]);
-        printk(" CyCOR4 %x\n", base_addr[CyCOR4<<index]);
-        printk(" CyCOR5 %x\n", base_addr[CyCOR5<<index]);
-        printk(" CyCCSR %x\n", base_addr[CyCCSR<<index]);
-        printk(" CyRDCR %x\n", base_addr[CyRDCR<<index]);
-        printk(" CySCHR1 %x\n", base_addr[CySCHR1<<index]);
-        printk(" CySCHR2 %x\n", base_addr[CySCHR2<<index]);
-        printk(" CySCHR3 %x\n", base_addr[CySCHR3<<index]);
-        printk(" CySCHR4 %x\n", base_addr[CySCHR4<<index]);
-        printk(" CySCRL %x\n", base_addr[CySCRL<<index]);
-        printk(" CySCRH %x\n", base_addr[CySCRH<<index]);
-        printk(" CyLNC %x\n", base_addr[CyLNC<<index]);
-        printk(" CyMCOR1 %x\n", base_addr[CyMCOR1<<index]);
-        printk(" CyMCOR2 %x\n", base_addr[CyMCOR2<<index]);
-        printk(" CyRTPR %x\n", base_addr[CyRTPR<<index]);
-        printk(" CyMSVR1 %x\n", base_addr[CyMSVR1<<index]);
-        printk(" CyMSVR2 %x\n", base_addr[CyMSVR2<<index]);
-        printk(" CyRBPR %x\n", base_addr[CyRBPR<<index]);
-        printk(" CyRCOR %x\n", base_addr[CyRCOR<<index]);
-        printk(" CyTBPR %x\n", base_addr[CyTBPR<<index]);
-        printk(" CyTCOR %x\n", base_addr[CyTCOR<<index]);
+        printk(" CyCCR %x\n", cy_readb(base_addr + CyCCR<<index));
+        printk(" CySRER %x\n", cy_readb(base_addr + CySRER<<index));
+        printk(" CyCOR1 %x\n", cy_readb(base_addr + CyCOR1<<index));
+        printk(" CyCOR2 %x\n", cy_readb(base_addr + CyCOR2<<index));
+        printk(" CyCOR3 %x\n", cy_readb(base_addr + CyCOR3<<index));
+        printk(" CyCOR4 %x\n", cy_readb(base_addr + CyCOR4<<index));
+        printk(" CyCOR5 %x\n", cy_readb(base_addr + CyCOR5<<index));
+        printk(" CyCCSR %x\n", cy_readb(base_addr + CyCCSR<<index));
+        printk(" CyRDCR %x\n", cy_readb(base_addr + CyRDCR<<index));
+        printk(" CySCHR1 %x\n", cy_readb(base_addr + CySCHR1<<index));
+        printk(" CySCHR2 %x\n", cy_readb(base_addr + CySCHR2<<index));
+        printk(" CySCHR3 %x\n", cy_readb(base_addr + CySCHR3<<index));
+        printk(" CySCHR4 %x\n", cy_readb(base_addr + CySCHR4<<index));
+        printk(" CySCRL %x\n", cy_readb(base_addr + CySCRL<<index));
+        printk(" CySCRH %x\n", cy_readb(base_addr + CySCRH<<index));
+        printk(" CyLNC %x\n", cy_readb(base_addr + CyLNC<<index));
+        printk(" CyMCOR1 %x\n", cy_readb(base_addr + CyMCOR1<<index));
+        printk(" CyMCOR2 %x\n", cy_readb(base_addr + CyMCOR2<<index));
+        printk(" CyRTPR %x\n", cy_readb(base_addr + CyRTPR<<index));
+        printk(" CyMSVR1 %x\n", cy_readb(base_addr + CyMSVR1<<index));
+        printk(" CyMSVR2 %x\n", cy_readb(base_addr + CyMSVR2<<index));
+        printk(" CyRBPR %x\n", cy_readb(base_addr + CyRBPR<<index));
+        printk(" CyRCOR %x\n", cy_readb(base_addr + CyRCOR<<index));
+        printk(" CyTBPR %x\n", cy_readb(base_addr + CyTBPR<<index));
+        printk(" CyTCOR %x\n", cy_readb(base_addr + CyTCOR<<index));
 
     restore_flags(flags);
 } /* show_status */
diff -u --recursive --new-file v2.1.62/linux/drivers/char/joystick.c linux/drivers/char/joystick.c
--- v2.1.62/linux/drivers/char/joystick.c	Thu Jul 17 10:06:04 1997
+++ linux/drivers/char/joystick.c	Tue Nov  4 08:04:24 1997
@@ -47,6 +47,9 @@
 			     	compatibility.
 			     Better ioctl names. Kept binary compatibility.
 			     Removed 'save_busy'. Just set busy to 1.
+11/03/97  Brian Gerst 0.9.1: Fixed bug which caused driver to always time out 
+                             but never report a timeout (broken while loop).
+                             Fixed js_read for new VFS code.
 */
 
 #include <linux/module.h>
@@ -227,20 +230,21 @@
  *	one shots to clear.
  */
 
-static long js_read (struct inode *inode, struct file *file, char *buf, unsigned long count)
+static ssize_t js_read (struct file *file, char *buf, 
+		     size_t count, loff_t *ppos)
 {
 	int j, chk, jsmask;
 	int t0, t_x0, t_y0, t_x1, t_y1;
-	unsigned int minor, minor2;
+	unsigned int minor;
 	int buttons;
-
+	struct inode *inode=file->f_dentry->d_inode;
+	
 	if (count != JS_RETURN)
 		return -EINVAL;
-	minor = MINOR (inode->i_rdev);
+	minor = MINOR (inode->i_rdev); 
 	inode->i_atime = CURRENT_TIME;
 	if (jiffies >= js_data[minor].js_expiretime) 
 	{
-		minor2 = minor << 1;
 		j = js_data[minor].js_timeout;
 		for (; (js_exist & inb (JS_PORT)) && j; j--);
 		if (j == 0)
@@ -262,8 +266,8 @@
 		/*get init timestamp*/
 		t_x0 = t_y0 = t_x1 = t_y1 = t0 = get_timer0 ();
 		/*wait for an axis' bit to clear or timeout*/
-		while (j-- && (chk = (inb (JS_PORT) & js_exist ) | jsmask)) 
-		{
+		do {
+			chk = (inb (JS_PORT) & js_exist) | jsmask;
 			if (!(chk & JS_X_0)) {
 				t_x0 = get_timer0();
 				jsmask |= JS_X_0;
@@ -280,7 +284,7 @@
 				t_y1 = get_timer0();
 				jsmask |= JS_Y_1;
 			}
-		}
+		} while (--j && jsmask != js_exist);
 		sti();					/* allow interrupts */
 
 		js_read_semaphore = 0;	/* allow other reads to progress */
diff -u --recursive --new-file v2.1.62/linux/drivers/char/misc.c linux/drivers/char/misc.c
--- v2.1.62/linux/drivers/char/misc.c	Sun Sep  7 13:10:42 1997
+++ linux/drivers/char/misc.c	Tue Nov  4 10:23:25 1997
@@ -76,6 +76,7 @@
 extern int rtc_init(void);
 extern int dsp56k_init(void);
 extern int nvram_init(void);
+extern int radio_init(void);
 extern void hfmodem_init(void);
 
 #ifdef CONFIG_PROC_FS
@@ -256,6 +257,9 @@
 #endif
 #ifdef CONFIG_NVRAM
 	nvram_init();
+#endif
+#ifdef CONFIG_MISC_RADIO
+	radio_init();
 #endif
 #ifdef CONFIG_HFMODEM
 	hfmodem_init();
diff -u --recursive --new-file v2.1.62/linux/drivers/char/nvram.c linux/drivers/char/nvram.c
--- v2.1.62/linux/drivers/char/nvram.c	Tue Sep 23 16:48:47 1997
+++ linux/drivers/char/nvram.c	Tue Nov  4 08:34:39 1997
@@ -227,11 +227,11 @@
 	return( (offset >= 0) ? (file->f_pos = offset) : -EINVAL );
 }
 
-static long nvram_read( struct inode * inode, struct file * file,
-						char * buf, unsigned long count )
+static ssize_t nvram_read( struct file * file,
+						char * buf, size_t count, loff_t *ppos )
 {
 	unsigned long flags;
-	unsigned i = file->f_pos;
+	unsigned i = *ppos;
 	char *tmp = buf;
 
 	save_flags(flags);
@@ -244,17 +244,16 @@
 
 	for( ; count-- > 0 && i < NVRAM_BYTES; ++i, ++tmp )
 		put_user( nvram_read_int(i), tmp );
-	file->f_pos = i;
+	*ppos = i;
 
 	restore_flags(flags);
 	return( tmp - buf );
 }
 
-static long nvram_write( struct inode * inode, struct file * file,
-						 const char * buf, unsigned long count )
+static ssize_t nvram_write( struct file * file, const char * buf, size_t count, loff_t *ppos )
 {
 	unsigned long flags;
-	unsigned i = file->f_pos;
+	unsigned i = *ppos;
 	const char *tmp = buf;
 	char c;
 
@@ -271,7 +270,7 @@
 		nvram_write_int( c, i );
 	}
 	nvram_set_checksum_int();
-	file->f_pos = i;
+	*ppos = i;
 
 	restore_flags(flags);
 	return( tmp - buf );
diff -u --recursive --new-file v2.1.62/linux/drivers/char/pcwd.c linux/drivers/char/pcwd.c
--- v2.1.62/linux/drivers/char/pcwd.c	Tue Sep 23 16:48:47 1997
+++ linux/drivers/char/pcwd.c	Thu Nov  6 15:29:47 1997
@@ -29,6 +29,7 @@
  * 961118	Changed some verbiage on some of the output, tidied up
  *		code bits, and added compatibility to 2.1.x.
  * 970912       Enabled board on open and disable on close.
+ * 971107       Took account of recent VFS changes (broke read).
  */
 
 #include <linux/module.h>
@@ -222,7 +223,7 @@
 }
 
 static int pcwd_ioctl(struct inode *inode, struct file *file,
-	unsigned int cmd, unsigned long arg)
+		      unsigned int cmd, unsigned long arg)
 {
 	int i, cdat, rv;
 	static struct watchdog_info ident=
@@ -359,8 +360,12 @@
 	return 0;
 }
 
-static long pcwd_write(struct inode *inode, struct file *file, const char *buf, unsigned long len)
+static ssize_t pcwd_write(struct file *file, const char *buf, size_t len,
+			  loff_t *ppos)
 {
+	/*  Can't seek (pwrite) on this device  */
+	if (ppos != &file->f_pos)
+		return -ESPIPE;
 	if (len)
 	{
 		pcwd_send_heartbeat();
@@ -381,13 +386,16 @@
 	return(0);
 }
 
-static long pcwd_read(struct inode *inode, struct file *file, char *buf,
-	unsigned long count)
+static ssize_t pcwd_read(struct file *file, char *buf, size_t count,
+			 loff_t *ppos)
 {
 	unsigned short c = inb(current_readport);
 	unsigned char cp;
 
-	switch(MINOR(inode->i_rdev)) 
+	/*  Can't seek (pread) on this device  */
+	if (ppos != &file->f_pos)
+		return -ESPIPE;
+	switch(MINOR(file->f_dentry->d_inode->i_rdev)) 
 	{
 		case TEMP_MINOR:
 			cp = c;
@@ -489,11 +497,16 @@
 	pcwd_read,	/* Read */
 	pcwd_write,	/* Write */
 	NULL,		/* Readdir */
-	NULL,		/* Select */
+	NULL,		/* Poll */
 	pcwd_ioctl,	/* IOctl */
 	NULL,		/* MMAP */
 	pcwd_open,	/* Open */
-	pcwd_close	/* Close */
+	pcwd_close,	/* Release */
+	NULL,           /* Fsync */
+	NULL,           /* Fasync */
+	NULL,           /* CheckMediaChange */
+	NULL,           /* Revalidate */
+	NULL,           /* Lock */
 };
 
 static struct miscdevice pcwd_miscdev = {
diff -u --recursive --new-file v2.1.62/linux/drivers/char/radio.c linux/drivers/char/radio.c
--- v2.1.62/linux/drivers/char/radio.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/radio.c	Tue Nov  4 10:23:25 1997
@@ -0,0 +1,234 @@
+/*
+ * Radio Card Device Driver for Linux
+ *
+ * (c) 1997 Matthew Kirkwood <weejock@ferret.lmh.ox.ac.uk>
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+
+#include <linux/miscdevice.h>
+
+#include <asm/uaccess.h>
+
+#include <linux/config.h>
+
+#include <linux/radio.h>
+#ifdef CONFIG_RADIO_RTRACK
+#include "rtrack.h"
+#endif
+#ifdef CONFIG_RADIO_WINRADIO
+#include "winradio.h"
+#endif
+
+int radio_open(struct inode *inode, struct file *file);
+int radio_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg);
+
+/* /dev/radio interface */
+static struct file_operations radio_fops = {
+	NULL,			/* seek */
+	NULL,			/* read */
+	NULL,			/* write */
+	NULL,			/* readdir */
+	NULL,			/* select */
+	&radio_ioctl,		/* ioctl */
+	NULL,			/* mmap */
+	&radio_open,		/* we're not allowed NULL, it seems... */
+	NULL			/* release */
+};
+
+static struct miscdevice radio_miscdevice = {
+	RADIO_MINOR,		/* minor device number */
+	"radio",		/* title */
+	&radio_fops,		/* file operations */
+	NULL, NULL		/* previous and next (not our business) */
+};
+
+
+static struct radio_device *firstdevice, *lastdevice;
+static int numdevices;
+
+__initfunc(void radio_init(void))
+{
+	/* register the handler for the device number... */
+	if(misc_register(&radio_miscdevice)) {
+		printk("radio: couldn't register misc device\n");
+		return;
+	}
+
+	/* do some general initialisation stuff */
+	lastdevice = firstdevice = NULL;
+	numdevices = 0;
+
+#ifdef	CONFIG_RADIO_RTRACK
+	radiotrack_init();
+#endif
+#ifdef	CONFIG_RADIO_WINRADIO
+	printk("oooops.  no winradio support yet... :-(\n");
+#endif
+/* etc.... */
+
+	printk("radio: registered %d devices\n", numdevices);
+}
+
+
+/* according to drivers/char/misc.c, the "open" call must not be NULL.
+ * I'm not sure if I approve, but I didn't write it, so...
+ */
+int radio_open(struct inode *inode, struct file *file)
+{
+	return 0;
+}
+
+
+/* append a device to the linked list... */
+int radio_add_device(struct radio_device *newdev)
+{
+	if(firstdevice == NULL) {
+		firstdevice = newdev;
+	} else {
+		lastdevice->next = newdev;
+	}
+	lastdevice = newdev; numdevices++;
+	newdev->cap->dev_num=numdevices;
+	newdev->next = NULL;			/* don't need, but... */
+	return(numdevices);
+}
+
+struct radio_device *getdev(int index)
+{
+struct radio_device *retval;
+
+	if(index > numdevices)
+		return NULL;		/* let's have a bit less of that */
+
+	retval = firstdevice;
+	for(;index;index--)
+		retval = retval->next;
+
+	return retval;
+}
+
+
+/* interface routine */
+int radio_ioctl(struct inode *inode, struct file *file,
+		unsigned int cmd, unsigned long arg)
+{
+struct radio_device *dev;
+struct radio_ctl *ctl_arg = (struct radio_ctl*)arg;
+int nowrite;
+int val, ret;
+
+	if((void*)arg == NULL)
+		return -EFAULT;		/* XXX - check errnos are OK */
+
+
+	switch(cmd) {
+	case RADIO_NUMDEVS:
+		return (put_user(numdevices,(int*)arg) ? -EFAULT : 0);
+
+
+	case RADIO_GETCAPS:
+	/* p'raps I should verify for read then write ?? */
+		if(verify_area(VERIFY_WRITE, (void*)arg, sizeof(struct radio_cap)))
+			return -EFAULT;
+		if((dev = getdev(((struct radio_cap*)arg)->dev_num)) == NULL)
+			return -EINVAL;
+		copy_to_user((void*)arg, dev->cap, sizeof(struct radio_cap));
+		return 0;
+
+
+	case RADIO_GETBNDCAP:
+		if(verify_area(VERIFY_WRITE, (void*)arg, sizeof(struct radio_band)))
+			return -EFAULT;
+
+		if((dev = getdev(((struct radio_band*)arg)->dev_num)) == NULL)
+			return -EINVAL;
+
+		val = ((struct radio_band*)arg)->index;
+		if(val >= dev->cap->num_bwidths)
+			return -EINVAL;			/* XXX errno */
+
+		copy_to_user((void*)arg, (dev->bands)+(val*sizeof(struct radio_band)),
+			sizeof(struct radio_band));
+		return 0;
+	}
+
+
+/* now, we know that arg points to a struct radio_ctl */
+	/* get the requested device */
+	if(verify_area(VERIFY_READ, ctl_arg, sizeof(struct radio_ctl)))
+		return -EFAULT;
+
+	if((dev = getdev(ctl_arg->dev_num)) == NULL)
+		return -EINVAL;
+
+	nowrite = verify_area(VERIFY_WRITE, ctl_arg, sizeof(struct radio_ctl));
+
+	val = ctl_arg->value;
+
+	switch(cmd) {
+	case RADIO_SETVOL:
+		if((val < dev->cap->volmin) || (val > dev->cap->volmax))
+			return -EINVAL;
+		if((ret = (*(dev->setvol))(dev, val)))
+			return ret;
+		dev->curvol = val;
+		return 0;
+
+	case RADIO_GETVOL:
+		if(nowrite)
+			return -EFAULT;
+		ctl_arg->value = dev->curvol;
+		return 0;
+
+
+	case RADIO_SETBAND:
+		if(val >= dev->cap->num_bwidths)
+			return -EINVAL;
+		if((ret = (*(dev->setband))(dev, val)))
+			return ret;
+		dev->curband = val;
+		return 0;
+
+	case RADIO_GETBAND:
+		if(nowrite)
+			return -EFAULT;
+		ctl_arg->value = dev->curband;
+		return 0;
+
+
+	case RADIO_SETFREQ: {
+	struct radio_band *bp;
+
+		bp = (dev->bands) + ((dev->curband) * sizeof(struct radio_band));
+		if((val < bp->freqmin) || (val > bp->freqmax))
+			return -EINVAL;
+		if((ret = (*(dev->setfreq))(dev, val)))
+			return ret;
+		dev->curfreq = val;
+		return 0;
+	}
+
+	case RADIO_GETFREQ:
+		if(nowrite)
+			return -EFAULT;
+		ctl_arg->value = dev->curfreq;
+		return 0;
+
+
+	case RADIO_GETSIGSTR:
+		if(nowrite)
+			return -EFAULT;
+		ctl_arg->value = (*(dev->getsigstr))(dev);
+		return 0;
+
+
+	default:
+		return -ENOSYS;
+	}
+}
diff -u --recursive --new-file v2.1.62/linux/drivers/char/rtrack.c linux/drivers/char/rtrack.c
--- v2.1.62/linux/drivers/char/rtrack.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/rtrack.c	Tue Nov  4 10:23:25 1997
@@ -0,0 +1,213 @@
+/* radiotrack (radioreveal) driver for Linux radio support
+ * (c) 1997 M. Kirkwood
+ */
+/* TODO: Allow for more than one of these foolish entities :-) */
+
+/* Notes on the hardware (reverse engineered from other peoples'
+ * reverse engineering of AIMS' code :-)
+ *
+ *  Frequency control is done digitally -- ie out(port,encodefreq(95.8));
+ *
+ *  The signal strength query is unsurprisingly inaccurate.  And it seems
+ *  to indicate that (on my card, at least) the frequency setting isn't
+ *  too great.  (I have to tune up .025MHz from what the freq should be
+ *  to get a report that the thing is tuned.)
+ *
+ *  Volume control is (ugh) analogue:
+ *   out(port, start_increasing_volume);
+ *   wait(a_wee_while);
+ *   out(port, stop_changing_the_volume);
+ *  
+ */
+
+#include <linux/config.h>
+#include <linux/radio.h>
+#include <linux/ioport.h>
+
+#include <linux/delay.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+
+#include "rtrack.h"
+
+/* Let's just be a bit careful here, shall we? */
+#if (CONFIG_RADIO_RTRACK_PORT != 0x20f) && (CONFIG_RADIO_RTRACK_PORT != 0x30f)
+#error Invalid port specified for RadioTrack
+#endif
+
+/* local prototypes */
+void outbits(int bits, int data, int port);
+void decvol(int port);
+void incvol(int port);
+void mute(int port);
+void unmute(int port);
+
+/* public structurey-type things */
+int rt_port = CONFIG_RADIO_RTRACK_PORT;
+
+struct radio_cap rt_cap = {
+	0,			/* device index (not dealt with here) */
+	RADIO_TYPE_RTRACK,	/* type */
+	1,			/* number of bandwidths */
+	0, 10			/* volmin, volmax */
+};
+
+/* we only get one band/protocol with radiotrack */
+struct radio_band rt_band = {
+	0,			/* device index */
+	0,			/* bandwidth "index" */
+	RADIO_PROTOCOL_FM,
+	RADIO_BAND_FM_STD,
+	RADIO_FM_FRTOINT(88.0),	/* freq range */
+	RADIO_FM_FRTOINT(108.0),
+	0,1			/* sig strength range */
+};
+
+/* p'raps these should become struct radio_ops and struct radio_status? */
+struct radio_device rt_dev = {
+	&rt_cap,
+	&rt_band,
+	&rt_setvol,
+	0,				/* curvol */
+	&rt_setband,
+	0,				/* curband */
+	&rt_setfreq,
+	0,				/* curfreq */
+	&rt_getsigstr,
+	NULL,				/* next (to be filled in later) */
+	&rt_port			/* misc */
+};
+
+
+void radiotrack_init()
+{
+int dev_num, i;
+
+/* XXX - probe here?? - XXX */
+
+/* try to grab the i/o port */
+	if(check_region(rt_port,2)) {
+		printk("rtrack.c: port 0x%x already used\n", rt_port);
+		return;
+	}
+
+	request_region(rt_port,2,"rtrack");
+
+	dev_num = radio_add_device(&rt_dev);
+/* initialise the card */
+	/* set the volume very low */
+	for(i=rt_cap.volmax; i>rt_cap.volmin; i--)
+		decvol(rt_port);
+	rt_dev.curvol = rt_cap.volmin;
+}
+
+
+int rt_setvol(struct radio_device *dev, int vol)
+{
+int port, i;
+
+	if(vol == dev->curvol)
+		return 0;
+
+	port = *(int*)(dev->misc);
+	if(vol == 0)
+		mute(port);
+
+	if(vol > dev->curvol)
+		for(i = dev->curvol; i < vol; i++)
+			incvol(port);
+	else
+		for(i = dev->curvol; i > vol; i--)
+			decvol(port);
+
+	if(dev->curvol == 0)
+		unmute(port);
+
+	return 0;
+}
+
+
+int rt_setband(struct radio_device *dev, int band)
+{
+/* we know in advance that we only have one band, and
+ * the wrapper checks the validity of all the inputs
+ */
+	return 0;
+}
+
+int rt_setfreq(struct radio_device *dev, int freq)
+{
+int myport = *(int*)(dev->misc);
+
+	outbits(16, RTRACK_ENCODE(freq), myport);
+	outbits(8, 0xa0, myport);
+/* XXX - get rid of this once setvol is implemented properly - XXX */
+/* these insist on turning the thing on.  not sure I approve... */
+	udelay(1000);
+	outb(0, myport);
+	outb(0xc8, myport);
+
+	return 0;
+}
+
+int rt_getsigstr(struct radio_device *dev)
+{
+int res;
+int myport = *(int*)(dev->misc);
+
+	outb(0xf8, myport);
+	udelay(200000);
+	res = (int)inb(myport);
+	udelay(10000);
+	outb(0xe8, myport);
+	if(res == 0xfd)
+		return 1;
+	else
+		return 0;
+}
+
+
+/* local things */
+void outbits(int bits, int data, int port)
+{
+	while(bits--) {
+		if(data & 1) {
+			outw(5, port);
+			outw(5, port);
+			outw(7, port);
+			outw(7, port);
+		} else {
+			outw(1, port);
+			outw(1, port);
+			outw(3, port);
+			outw(3, port);
+		}
+		data>>=1;
+	}
+}
+
+void decvol(int port)
+{
+	outb(0x48, port);
+	udelay(100000);
+	outb(0xc8, port);
+}
+
+void incvol(int port)
+{
+	outb(0x88, port);
+	udelay(100000);
+	outb(0xc8, port);
+}
+
+void mute(int port)
+{
+	outb(0, port);
+	outb(0xc0, port);
+}
+
+void unmute(int port)
+{
+	outb(0, port);
+	outb(0xc8, port);
+}
diff -u --recursive --new-file v2.1.62/linux/drivers/char/rtrack.h linux/drivers/char/rtrack.h
--- v2.1.62/linux/drivers/char/rtrack.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/rtrack.h	Tue Nov  4 10:23:25 1997
@@ -0,0 +1,25 @@
+/* RadioTrack (RadioReveal) include file.
+ * (c) 1997 M. Kirkwood
+ *
+ * Not in include/linux/ because there's no need for anyone
+ * to know about these details, I reckon.
+ */
+
+#ifndef __RTRACK_H
+#define __RTRACK_H
+
+#include <linux/radio.h>
+
+void radiotrack_init(void);
+int rt_setvol(struct radio_device *dev, int vol);
+int rt_setband(struct radio_device *dev, int vol);
+int rt_setfreq(struct radio_device *dev, int vol);
+int rt_getsigstr(struct radio_device *dev);
+
+/* frequency encoding stuff... */
+/* we have to careful not to introduce fp stuff here */
+#define	RTRACK_ENCODE(x)	(((((x)*2)/5)-(40*88))+0xf6c)
+#define	RTRACK_DECODE(x)	(((((x)-0xf6c)+(40*88))*5)/2)
+/* we shouldn't actually need the decode macro (or the excessive bracketing :-) */
+
+#endif /* __RTRACK_H */
diff -u --recursive --new-file v2.1.62/linux/drivers/net/3c507.c linux/drivers/net/3c507.c
--- v2.1.62/linux/drivers/net/3c507.c	Mon Nov  3 13:04:26 1997
+++ linux/drivers/net/3c507.c	Mon Nov 10 16:17:17 1997
@@ -447,7 +447,7 @@
 {
 	struct net_local *lp = (struct net_local *)dev->priv;
 	int ioaddr = dev->base_addr;
-	short *shmem = (short*)dev->mem_start;
+	unsigned long shmem = dev->mem_start;
 
 	if (dev->tbusy) 
 	{
@@ -458,7 +458,7 @@
 			return 1;
 		if (net_debug > 1)
 			printk("%s: transmit timed out, %s?  ", dev->name,
-				   shmem[iSCB_STATUS>>1] & 0x8000 ? "IRQ conflict" :
+				   readw(shmem+iSCB_STATUS) & 0x8000 ? "IRQ conflict" :
 				   "network cable problem");
 		/* Try to restart the adaptor. */
 		if (lp->last_restart == lp->stats.tx_packets) {
@@ -468,7 +468,7 @@
 		} else {
 			/* Issue the channel attention signal and hope it "gets better". */
 			if (net_debug > 1) printk("Kicking board.\n");
-			shmem[iSCB_CMD>>1] = 0xf000|CUC_START|RX_START;
+			writew(0xf000|CUC_START|RX_START,shmem+iSCB_CMD);
 			outb(0, ioaddr + SIGNAL_CA);			/* Issue channel-attn. */
 			lp->last_restart = lp->stats.tx_packets;
 		}
@@ -508,7 +508,7 @@
 	struct net_local *lp;
 	int ioaddr, status, boguscount = 0;
 	ushort ack_cmd = 0;
-	ushort *shmem;
+	unsigned long shmem;
 
 	if (dev == NULL) {
 		printk ("net_interrupt(): irq %d for unknown device.\n", irq);
@@ -518,9 +518,9 @@
 
 	ioaddr = dev->base_addr;
 	lp = (struct net_local *)dev->priv;
-	shmem = ((ushort*)dev->mem_start);
+	shmem = dev->mem_start;
 
-	status = shmem[iSCB_STATUS>>1];
+	status = readw(shmem+iSCB_STATUS);
 
 	if (net_debug > 4) {
 		printk("%s: 3c507 interrupt, status %4.4x.\n", dev->name, status);
@@ -531,7 +531,7 @@
 
 	/* Reap the Tx packet buffers. */
 	while (lp->tx_reap != lp->tx_head) {
-	  unsigned short tx_status = shmem[lp->tx_reap>>1];
+	  unsigned short tx_status = readw(shmem+lp->tx_reap);
 
 	  if (tx_status == 0) {
 		if (net_debug > 5)  printk("Couldn't reap %#x.\n", lp->tx_reap);
@@ -586,11 +586,11 @@
 			printk("%s: Rx unit stopped, status %04x, restarting.\n",
 				   dev->name, status);
 		init_rx_bufs(dev);
-		shmem[iSCB_RFA >> 1] = RX_BUF_START;
+		writew(RX_BUF_START,shmem+iSCB_RFA);
 		ack_cmd |= RX_START;
 	}
 
-	shmem[iSCB_CMD>>1] = ack_cmd;
+	writew(ack_cmd,shmem+iSCB_CMD);
 	outb(0, ioaddr + SIGNAL_CA);			/* Issue channel-attn. */
 
 	/* Clear the latched interrupt. */
@@ -605,13 +605,13 @@
 static int el16_close(struct device *dev)
 {
 	int ioaddr = dev->base_addr;
-	ushort *shmem = (short*)dev->mem_start;
+	unsigned long shmem = dev->mem_start;
 
 	dev->tbusy = 1;
 	dev->start = 0;
 
 	/* Flush the Tx and disable Rx. */
-	shmem[iSCB_CMD >> 1] = RX_SUSPEND | CUC_SUSPEND;
+	writew(RX_SUSPEND | CUC_SUSPEND,shmem+iSCB_CMD);
 	outb(0, ioaddr + SIGNAL_CA);
 
 	/* Disable the 82586's input to the interrupt line. */
@@ -641,7 +641,7 @@
 static void init_rx_bufs(struct device *dev)
 {
 	struct net_local *lp = (struct net_local *)dev->priv;
-	unsigned short *write_ptr;
+	unsigned long write_ptr;
 	unsigned short SCB_base = SCB_BASE;
 
 	int cur_rxbuf = lp->rx_head = RX_BUF_START;
@@ -649,26 +649,26 @@
 	/* Initialize each Rx frame + data buffer. */
 	do {	/* While there is room for one more. */
 
-	  write_ptr = (unsigned short *)(dev->mem_start + cur_rxbuf);
+	  write_ptr = dev->mem_start + cur_rxbuf;
 
-		*write_ptr++ = 0x0000; 				/* Status */
-		*write_ptr++ = 0x0000;				/* Command */
-		*write_ptr++ = cur_rxbuf + RX_BUF_SIZE; /* Link */
-		*write_ptr++ = cur_rxbuf + 22;		/* Buffer offset */
-		*write_ptr++ = 0x0000; 				/* Pad for dest addr. */
-		*write_ptr++ = 0x0000;
-		*write_ptr++ = 0x0000;
-		*write_ptr++ = 0x0000; 				/* Pad for source addr. */
-		*write_ptr++ = 0x0000;
-		*write_ptr++ = 0x0000;
-		*write_ptr++ = 0x0000;				/* Pad for protocol. */
-
-		*write_ptr++ = 0x0000;				/* Buffer: Actual count */
-		*write_ptr++ = -1;					/* Buffer: Next (none). */
-		*write_ptr++ = cur_rxbuf + 0x20 + SCB_base;	/* Buffer: Address low */
-		*write_ptr++ = 0x0000;
+		writew(0x0000,write_ptr);			/* Status */
+		writew(0x0000,write_ptr+=2);			/* Command */
+		writew(cur_rxbuf + RX_BUF_SIZE,write_ptr+=2);	/* Link */
+		writew(cur_rxbuf + 22,write_ptr+=2);		/* Buffer offset */
+		writew(0x0000,write_ptr+=2);			/* Pad for dest addr. */
+		writew(0x0000,write_ptr+=2);
+		writew(0x0000,write_ptr+=2);
+		writew(0x0000,write_ptr+=2);			/* Pad for source addr. */
+		writew(0x0000,write_ptr+=2);
+		writew(0x0000,write_ptr+=2);
+		writew(0x0000,write_ptr+=2);			/* Pad for protocol. */
+
+		writew(0x0000,write_ptr+=2);			/* Buffer: Actual count */
+		writew(-1,write_ptr+=2);			/* Buffer: Next (none). */
+		writew(cur_rxbuf + 0x20 + SCB_base,write_ptr+=2);/* Buffer: Address low */
+		writew(0x0000,write_ptr+=2);
 		/* Finally, the number of bytes in the buffer. */
-		*write_ptr++ = 0x8000 + RX_BUF_SIZE-0x20;
+		writew(0x8000 + RX_BUF_SIZE-0x20,write_ptr+=2);
 
 		lp->rx_tail = cur_rxbuf;
 		cur_rxbuf += RX_BUF_SIZE;
@@ -676,18 +676,16 @@
 
 	/* Terminate the list by setting the EOL bit, and wrap the pointer to make
 	   the list a ring. */
-	write_ptr = (unsigned short *)
-	  (dev->mem_start + lp->rx_tail + 2);
-	*write_ptr++ = 0xC000;					/* Command, mark as last. */
-	*write_ptr++ = lp->rx_head;				/* Link */
-
+	write_ptr = dev->mem_start + lp->rx_tail + 2;
+	writew(0xC000,write_ptr);				/* Command, mark as last. */
+	writew(lp->rx_head,write_ptr+2);			/* Link */
 }
 
 static void init_82586_mem(struct device *dev)
 {
 	struct net_local *lp = (struct net_local *)dev->priv;
 	short ioaddr = dev->base_addr;
-	ushort *shmem = (short*)dev->mem_start;
+	unsigned long shmem = dev->mem_start;
 
 	/* Enable loopback to protect the wire while starting up,
 	   and hold the 586 in reset during the memory initialization. */
@@ -698,13 +696,13 @@
 	init_words[7] = SCB_BASE;
 
 	/* Write the words at 0xfff6 (address-aliased to 0xfffff6). */
-	memcpy((void*)dev->mem_end-10, init_words, 10);
+	memcpy_toio(dev->mem_end-10, init_words, 10);
 
 	/* Write the words at 0x0000. */
-	memcpy((char*)dev->mem_start, init_words + 5, sizeof(init_words) - 10);
+	memcpy_toio(dev->mem_start, init_words + 5, sizeof(init_words) - 10);
 
 	/* Fill in the station address. */
-	memcpy((char*)dev->mem_start+SA_OFFSET, dev->dev_addr,
+	memcpy_toio(dev->mem_start+SA_OFFSET, dev->dev_addr,
 		   sizeof(dev->dev_addr));
 
 	/* The Tx-block list is written as needed.  We just set up the values. */
@@ -722,11 +720,11 @@
 
 	{
 		int boguscnt = 50;
-		while (shmem[iSCB_STATUS>>1] == 0)
+		while (readw(shmem+iSCB_STATUS) == 0)
 			if (--boguscnt == 0) {
 				printk("%s: i82586 initialization timed out with status %04x,"
 					   "cmd %04x.\n", dev->name,
-					   shmem[iSCB_STATUS>>1], shmem[iSCB_CMD>>1]);
+					   readw(shmem+iSCB_STATUS), readw(shmem+iSCB_CMD));
 				break;
 			}
 		/* Issue channel-attn -- the 82586 won't start. */
@@ -737,7 +735,7 @@
 	outb(0x84, ioaddr + MISC_CTRL);
 	if (net_debug > 4)
 		printk("%s: Initialized 82586, status %04x.\n", dev->name,
-			   shmem[iSCB_STATUS>>1]);
+			   readw(shmem+iSCB_STATUS));
 	return;
 }
 
@@ -746,30 +744,30 @@
 	struct net_local *lp = (struct net_local *)dev->priv;
 	short ioaddr = dev->base_addr;
 	ushort tx_block = lp->tx_head;
-	ushort *write_ptr =	  (ushort *)(dev->mem_start + tx_block);
+	unsigned long write_ptr = dev->mem_start + tx_block;
 
 	/* Set the write pointer to the Tx block, and put out the header. */
-	*write_ptr++ = 0x0000;				/* Tx status */
-	*write_ptr++ = CMD_INTR|CmdTx;		/* Tx command */
-	*write_ptr++ = tx_block+16;			/* Next command is a NoOp. */
-	*write_ptr++ = tx_block+8;			/* Data Buffer offset. */
+	writew(0x0000,write_ptr);			/* Tx status */
+	writew(CMD_INTR|CmdTx,write_ptr+=2);		/* Tx command */
+	writew(tx_block+16,write_ptr+=2);		/* Next command is a NoOp. */
+	writew(tx_block+8,write_ptr+=2);			/* Data Buffer offset. */
 
 	/* Output the data buffer descriptor. */
-	*write_ptr++ = length | 0x8000;		/* Byte count parameter. */
-	*write_ptr++ = -1;					/* No next data buffer. */
-	*write_ptr++ = tx_block+22+SCB_BASE;/* Buffer follows the NoOp command. */
-	*write_ptr++ = 0x0000;				/* Buffer address high bits (always zero). */
+	writew(length | 0x8000,write_ptr+=2);		/* Byte count parameter. */
+	writew(-1,write_ptr+=2);			/* No next data buffer. */
+	writew(tx_block+22+SCB_BASE,write_ptr+=2);	/* Buffer follows the NoOp command. */
+	writew(0x0000,write_ptr+=2);			/* Buffer address high bits (always zero). */
 
 	/* Output the Loop-back NoOp command. */
-	*write_ptr++ = 0x0000;				/* Tx status */
-	*write_ptr++ = CmdNOp;				/* Tx command */
-	*write_ptr++ = tx_block+16;			/* Next is myself. */
+	writew(0x0000,write_ptr+=2);			/* Tx status */
+	writew(CmdNOp,write_ptr+=2);			/* Tx command */
+	writew(tx_block+16,write_ptr+=2);		/* Next is myself. */
 
 	/* Output the packet at the write pointer. */
-	memcpy(write_ptr, buf, length);
+	memcpy_toio(write_ptr+2, buf, length);
 
 	/* Set the old command link pointing to this send packet. */
-	*(ushort*)(dev->mem_start + lp->tx_cmd_link) = tx_block;
+	writew(tx_block,dev->mem_start + lp->tx_cmd_link);
 	lp->tx_cmd_link = tx_block + 20;
 
 	/* Set the next free tx region. */
@@ -789,19 +787,19 @@
 static void el16_rx(struct device *dev)
 {
 	struct net_local *lp = (struct net_local *)dev->priv;
-	short *shmem = (short*)dev->mem_start;
+	unsigned long shmem = dev->mem_start;
 	ushort rx_head = lp->rx_head;
 	ushort rx_tail = lp->rx_tail;
 	ushort boguscount = 10;
 	short frame_status;
 
-	while ((frame_status = shmem[rx_head>>1]) < 0) {   /* Command complete */
-		ushort *read_frame =  (short *)(dev->mem_start + rx_head);
-		ushort rfd_cmd = read_frame[1];
-		ushort next_rx_frame = read_frame[2];
-		ushort data_buffer_addr = read_frame[3];
-		ushort *data_frame = (short *)(dev->mem_start + data_buffer_addr);
-		ushort pkt_len = data_frame[0];
+	while ((frame_status = readw(shmem+rx_head)) < 0) {   /* Command complete */
+		unsigned long read_frame = dev->mem_start + rx_head;
+		ushort rfd_cmd = readw(read_frame+2);
+		ushort next_rx_frame = readw(read_frame+4);
+		ushort data_buffer_addr = readw(read_frame+6);
+		unsigned long data_frame = dev->mem_start + data_buffer_addr;
+		ushort pkt_len = readw(data_frame);
 
 		if (rfd_cmd != 0 || data_buffer_addr != rx_head + 22
 			|| (pkt_len & 0xC000) != 0xC000) {
@@ -833,7 +831,7 @@
 			skb->dev = dev;
 
 			/* 'skb->data' points to the start of sk_buff data area. */
-			memcpy(skb_put(skb,pkt_len), data_frame + 5, pkt_len);
+			memcpy_fromio(skb_put(skb,pkt_len), data_frame + 10, pkt_len);
 
 			skb->protocol=eth_type_trans(skb,dev);
 			netif_rx(skb);
@@ -841,10 +839,10 @@
 		}
 
 		/* Clear the status word and set End-of-List on the rx frame. */
-		read_frame[0] = 0;
-		read_frame[1] = 0xC000;
+		writew(0,read_frame);
+		writew(0xC000,read_frame+2);
 		/* Clear the end-of-list on the prev. RFD. */
-		*(short*)(dev->mem_start + rx_tail + 2) = 0x0000;
+		writew(0x0000,dev->mem_start + rx_tail + 2);
 
 		rx_tail = rx_head;
 		rx_head = next_rx_frame;
diff -u --recursive --new-file v2.1.62/linux/drivers/net/dgrs.c linux/drivers/net/dgrs.c
--- v2.1.62/linux/drivers/net/dgrs.c	Thu May 29 21:53:07 1997
+++ linux/drivers/net/dgrs.c	Sun Nov  9 10:13:48 1997
@@ -176,7 +176,7 @@
 int	dgrs_hashexpire = -1;
 uchar	dgrs_ipaddr[4] = { 0xff, 0xff, 0xff, 0xff};
 uchar	dgrs_iptrap[4] = { 0xff, 0xff, 0xff, 0xff};
-long	dgrs_ipxnet = -1;
+__u32	dgrs_ipxnet = -1;
 int	dgrs_nicmode = 0;
 
 /*
@@ -1507,7 +1507,7 @@
 static int	spantree = -1;
 static int	ipaddr[4] = { -1 };
 static int	iptrap[4] = { -1 };
-static long	ipxnet = -1;
+static __u32	ipxnet = -1;
 static int	nicmode = -1;
 
 MODULE_PARM(debug, "i");
diff -u --recursive --new-file v2.1.62/linux/drivers/net/ne.c linux/drivers/net/ne.c
--- v2.1.62/linux/drivers/net/ne.c	Mon Nov  3 13:04:26 1997
+++ linux/drivers/net/ne.c	Tue Nov 11 08:17:49 1997
@@ -78,6 +78,8 @@
 	{PCI_VENDOR_ID_WINBOND2,	PCI_DEVICE_ID_WINBOND2_89C940},
 	{PCI_VENDOR_ID_COMPEX,		PCI_DEVICE_ID_COMPEX_RL2000},
 	{PCI_VENDOR_ID_KTI,		PCI_DEVICE_ID_KTI_ET32P2},
+	{PCI_VENDOR_ID_NETVIN,		PCI_DEVICE_ID_NETVIN_NV5000SC},
+	{PCI_VENDOR_ID_VIA,		PCI_DEVICE_ID_VIA_82C926},
 	{0,}
 };
 #endif
diff -u --recursive --new-file v2.1.62/linux/drivers/scsi/Config.in linux/drivers/scsi/Config.in
--- v2.1.62/linux/drivers/scsi/Config.in	Mon Oct 20 10:36:52 1997
+++ linux/drivers/scsi/Config.in	Wed Nov  5 13:13:02 1997
@@ -49,6 +49,7 @@
     int  '  maximum number of queued commands' CONFIG_SCSI_EATA_MAX_TAGS 16
   fi
 dep_tristate 'Future Domain 16xx SCSI/AHA 2920 support' CONFIG_SCSI_FUTURE_DOMAIN $CONFIG_SCSI
+dep_tristate 'GDT SCSI Disk Array Controller support' CONFIG_SCSI_GDTH $CONFIG_SCSI
 dep_tristate 'Generic NCR5380/53c400 SCSI support' CONFIG_SCSI_GENERIC_NCR5380 $CONFIG_SCSI
 if [ "$CONFIG_SCSI_GENERIC_NCR5380" != "n" ]; then
 	bool '   Enable NCR53c400 extensions' CONFIG_SCSI_GENERIC_NCR53C400
@@ -95,6 +96,9 @@
   fi
 fi
 dep_tristate 'PAS16 SCSI support' CONFIG_SCSI_PAS16 $CONFIG_SCSI
+dep_tristate 'PCI2000 support' CONFIG_SCSI_PCI2000 $CONFIG_SCSI
+dep_tristate 'PCI2220i support' CONFIG_SCSI_PCI2220I $CONFIG_SCSI
+dep_tristate 'PSI240i support' CONFIG_SCSI_PSI240I $CONFIG_SCSI
 dep_tristate 'Qlogic FAS SCSI support' CONFIG_SCSI_QLOGIC_FAS $CONFIG_SCSI
 if [ "$CONFIG_PCI" = "y" ]; then
   dep_tristate 'Qlogic ISP SCSI support' CONFIG_SCSI_QLOGIC_ISP $CONFIG_SCSI
diff -u --recursive --new-file v2.1.62/linux/drivers/scsi/Makefile linux/drivers/scsi/Makefile
--- v2.1.62/linux/drivers/scsi/Makefile	Mon Nov  3 13:04:26 1997
+++ linux/drivers/scsi/Makefile	Wed Nov  5 13:11:54 1997
@@ -15,6 +15,7 @@
 SCSI_SRCS = $(wildcard $(L_OBJS:%.o=%.c))
 
 AHA152X	= -DDEBUG_AHA152X -DAUTOCONF
+GDTH = #-DDEBUG_GDTH=2 -D__SERIAL__ -D__COM2__ -DGDTH_STATISTICS
 
 .SUFFIXES:
 .SUFFIXES: .c .o .h .a
@@ -91,6 +92,30 @@
   endif
 endif
 
+ifeq ($(CONFIG_SCSI_PCI2000),y)
+L_OBJS += pci2000.o
+else
+    ifeq ($(CONFIG_SCSI_PCI2000),m)
+    M_OBJS += pci2000.o
+    endif
+endif
+
+ifeq ($(CONFIG_SCSI_PCI2220I),y)
+L_OBJS += pci2220i.o
+else
+    ifeq ($(CONFIG_SCSI_PCI2220I),m)
+    M_OBJS += pci2220i.o
+    endif
+endif
+
+ifeq ($(CONFIG_SCSI_PSI240I),y)
+L_OBJS += psi240i.o
+else
+    ifeq ($(CONFIG_SCSI_PSI240I),m)
+    M_OBJS += psi240i.o
+    endif
+endif
+
 ifeq ($(CONFIG_A4000T_SCSI),y)
 L_OBJS += amiga7xx.o 53c7xx.o
 else
@@ -285,6 +310,14 @@
   endif
 endif
 
+ifeq ($(CONFIG_SCSI_GDTH),y)
+L_OBJS += gdth.o
+else
+  ifeq ($(CONFIG_SCSI_GDTH),m)
+  M_OBJS += gdth.o
+  endif
+endif
+
 ifeq ($(CONFIG_SCSI_DEBUG),y)
 L_OBJS += scsi_debug.o
 else
@@ -432,6 +465,9 @@
 
 aha152x.o: aha152x.c
 	$(CC) $(CFLAGS) $(AHA152X) -c aha152x.c 
+
+gdth.o: gdth.c gdth.h gdth_proc.c gdth_proc.h
+	$(CC) $(CFLAGS) $(GDTH) -c gdth.c 
 
 aic7xxx.o: aic7xxx.c aic7xxx_seq.h aic7xxx_reg.h
 	$(CC) $(CFLAGS) -c -o $@ aic7xxx.c
diff -u --recursive --new-file v2.1.62/linux/drivers/scsi/fdomain.c linux/drivers/scsi/fdomain.c
--- v2.1.62/linux/drivers/scsi/fdomain.c	Fri Dec 27 02:03:23 1996
+++ linux/drivers/scsi/fdomain.c	Fri Nov  7 10:16:36 1997
@@ -470,6 +470,7 @@
    { "Future Domain Corp. V1.0008/18/93",                   5, 33,  3,  4, 0 },
    { "Future Domain Corp. V1.0008/18/93",                  26, 33,  3,  4, 1 },
    { "Adaptec AHA-2920 PCI-SCSI Card",                     42, 31,  3, -1, 1 },
+   { "IBM F1 P264/32",                                      5, 14,  3, -1, 1 },
 				/* This next signature may not be a 3.5 bios */
    { "Future Domain Corp. V2.0108/18/93",                   5, 33,  3,  5, 0 },
    { "FUTURE DOMAIN CORP.  V3.5008/18/93",                  5, 34,  3,  5, 0 },
diff -u --recursive --new-file v2.1.62/linux/drivers/scsi/gdth.c linux/drivers/scsi/gdth.c
--- v2.1.62/linux/drivers/scsi/gdth.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/scsi/gdth.c	Tue Nov  4 09:17:30 1997
@@ -0,0 +1,3387 @@
+/************************************************************************
+ * GDT ISA/EISA/PCI Disk Array Controller driver for Linux              *
+ *                                                                      *
+ * gdth.c                                                               *
+ * Copyright (C) 1995-97 ICP vortex Computersysteme GmbH, Achim Leubner *
+ *                                                                      *
+ * <achim@vortex.de>                                                    *
+ *                                                                      *
+ * 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 kernel; if not, write to the Free Software           *
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.            *
+ *                                                                      *
+ * Tested with Linux 1.2.13, ..., 2.1.61                                *
+ *                                                                      *
+ * $Log: gdth.c,v $
+ * Revision 1.10  1997/10/31 12:29:57  achim
+ * Read heads/sectors from host drive
+ *
+ * Revision 1.9  1997/09/04 10:07:25  achim
+ * IO-mapping with virt_to_bus(), readb(), writeb(), ...
+ * register_reboot_notifier() to get a notify on shutdown used
+ *
+ * Revision 1.8  1997/04/02 12:14:30  achim
+ * Version 1.00 (see gdth.h), tested with kernel 2.0.29
+ *
+ * Revision 1.7  1997/03/12 13:33:37  achim
+ * gdth_reset() changed, new async. events
+ *
+ * Revision 1.6  1997/03/04 14:01:11  achim
+ * Shutdown routine gdth_halt() implemented
+ *
+ * Revision 1.5  1997/02/21 09:08:36  achim
+ * New controller included (RP, RP1, RP2 series)
+ * IOCTL interface implemented
+ *
+ * Revision 1.4  1996/07/05 12:48:55  achim
+ * Function gdth_bios_param() implemented
+ * New constant GDTH_MAXC_P_L inserted
+ * GDT_WRITE_THR, GDT_EXT_INFO implemented
+ * Function gdth_reset() changed
+ *
+ * Revision 1.3  1996/05/10 09:04:41  achim
+ * Small changes for Linux 1.2.13
+ *
+ * Revision 1.2  1996/05/09 12:45:27  achim
+ * Loadable module support implemented
+ * /proc support corrections made
+ *
+ * Revision 1.1  1996/04/11 07:35:57  achim
+ * Initial revision
+ *
+ *
+ * $Id: gdth.c,v 1.10 1997/10/31 12:29:57 achim Exp $ 
+ ************************************************************************/
+
+#ifdef MODULE
+#include <linux/module.h>
+#endif
+
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/head.h>
+#include <linux/types.h>
+#include <linux/bios32.h>
+#include <linux/pci.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/in.h>
+#include <linux/proc_fs.h>
+#include <linux/time.h>
+#include <linux/timer.h>
+#if LINUX_VERSION_CODE >= 0x020100
+#include <linux/reboot.h>
+#endif
+
+#include <asm/dma.h>
+#include <asm/system.h>
+#include <asm/io.h>
+
+#if LINUX_VERSION_CODE >= 0x010300
+#include <linux/blk.h>
+#else
+#include "../block/blk.h"
+#endif
+#include "scsi.h"
+#include "hosts.h"
+#include "sd.h"
+
+#include "gdth.h"
+
+#if LINUX_VERSION_CODE >= 0x010346
+static void gdth_interrupt(int irq,void *dev_id,struct pt_regs *regs);
+#else
+static void gdth_interrupt(int irq,struct pt_regs *regs);
+#endif
+static int gdth_sync_event(int hanum,int service,unchar index,Scsi_Cmnd *scp);
+static int gdth_async_event(int hanum,int service);
+
+static void gdth_putq(int hanum,Scsi_Cmnd *scp,unchar priority);
+static void gdth_next(int hanum);
+static int gdth_fill_raw_cmd(int hanum,Scsi_Cmnd *scp,unchar b);
+static int gdth_special_cmd(int hanum,Scsi_Cmnd *scp,unchar b);
+static gdth_evt_str *gdth_store_event(ushort source, ushort idx,
+                                      gdth_evt_data *evt);
+static int gdth_read_event(int handle, gdth_evt_str *estr);
+static void gdth_readapp_event(unchar application, gdth_evt_str *estr);
+static void gdth_clear_events(void);
+
+static void gdth_copy_internal_data(Scsi_Cmnd *scp,char *buffer,ushort count);
+static int gdth_internal_cache_cmd(int hanum,Scsi_Cmnd *scp,
+                                   unchar b,ulong *flags);
+static int gdth_fill_cache_cmd(int hanum,Scsi_Cmnd *scp,ushort hdrive);
+
+static int gdth_search_eisa(ushort eisa_adr);
+static int gdth_search_isa(ulong bios_adr);
+static int gdth_search_pci(ushort device_id,ushort index,gdth_pci_str *pcistr);
+static int gdth_init_eisa(ushort eisa_adr,gdth_ha_str *ha);
+static int gdth_init_isa(ulong bios_adr,gdth_ha_str *ha);
+static int gdth_init_pci(gdth_pci_str *pcistr,gdth_ha_str *ha);
+
+static void gdth_enable_int(int hanum);
+static int gdth_get_status(unchar *pIStatus,int irq);
+static int gdth_test_busy(int hanum);
+static int gdth_get_cmd_index(int hanum);
+static void gdth_release_event(int hanum);
+static int gdth_wait(int hanum,int index,ulong time);
+static int gdth_internal_cmd(int hanum,unchar service,ushort opcode,ulong p1,
+                             ulong p2,ulong p3);
+static int gdth_search_drives(int hanum);
+
+static void *gdth_mmap(ulong paddr, ulong size);
+static void gdth_munmap(void *addr);
+
+static const char *gdth_ctr_name(int hanum);
+#if LINUX_VERSION_CODE >= 0x020100
+static int gdth_halt(struct notifier_block *nb, ulong event, void *buf);
+#else
+void gdth_halt(void);
+#endif
+
+#ifdef DEBUG_GDTH
+static unchar   DebugState = DEBUG_GDTH;
+extern int sys_syslog(int,char*,int);
+#define LOGEN           sys_syslog(7,NULL,0);
+#define WAITSEC(a)      {ulong idx; for(idx=0;idx<a*1000L;++idx) udelay(1000);}
+
+#ifdef SLOWMOTION_GDTH
+#define SLOWM   WAITSEC(2)  
+#undef  INIT_RETRIES
+#undef  INIT_TIMEOUT
+#undef  POLL_TIMEOUT
+#define INIT_RETRIES    15
+#define INIT_TIMEOUT    150
+#define POLL_TIMEOUT    150
+#else
+#define SLOWM
+#endif
+
+#ifdef __SERIAL__
+#define MAX_SERBUF 160
+static void ser_init(void);
+static void ser_puts(char *str);
+static void ser_putc(char c);
+static int  ser_printk(const char *fmt, ...);
+static char strbuf[MAX_SERBUF+1];
+#ifdef __COM2__
+#define COM_BASE 0x2f8
+#else
+#define COM_BASE 0x3f8
+#endif
+static void ser_init()
+{
+    unsigned port=COM_BASE;
+
+    outb(0x80,port+3);
+    outb(0,port+1);
+    /* 19200 Baud, if 9600: outb(12,port) */
+    outb(6, port);
+    outb(3,port+3);
+    outb(0,port+1);
+    /*
+    ser_putc('I');
+    ser_putc(' ');
+    */
+}
+
+static void ser_puts(char *str)
+{
+    char *ptr;
+
+    ser_init();
+    for (ptr=str;*ptr;++ptr)
+        ser_putc(*ptr);
+}
+
+static void ser_putc(char c)
+{
+    unsigned port=COM_BASE;
+
+    while ((inb(port+5) & 0x20)==0);
+    outb(c,port);
+    if (c==0x0a)
+    {
+        while ((inb(port+5) & 0x20)==0);
+        outb(0x0d,port);
+    }
+}
+
+static int ser_printk(const char *fmt, ...)
+{
+    va_list args;
+    int i;
+
+    va_start(args,fmt);
+    i = vsprintf(strbuf,fmt,args);
+    ser_puts(strbuf);
+    va_end(args);
+    return i;
+}
+
+#define TRACE(a)    {if (DebugState==1) {ser_printk a; SLOWM}}
+#define TRACE2(a)   {if (DebugState==1 || DebugState==2) {ser_printk a; SLOWM}}
+#define TRACE3(a)   {if (DebugState!=0) {ser_printk a; SLOWM}}
+
+#else /* !__SERIAL__ */
+#define TRACE(a)    {if (DebugState==1) {LOGEN;printk a; SLOWM}}
+#define TRACE2(a)   {if (DebugState==1 || DebugState==2) {LOGEN;printk a; SLOWM}}
+#define TRACE3(a)   {if (DebugState!=0) {LOGEN;printk a; SLOWM}}
+#endif
+
+#else /* !DEBUG */
+#define TRACE(a)
+#define TRACE2(a)
+#define TRACE3(a)
+#endif
+
+#ifdef GDTH_STATISTICS
+static ulong max_rq=0, max_index=0, max_sg=0;
+static ulong act_ints=0, act_ios=0, act_stats=0, act_rq=0;
+#define GDTH_TIMER      31                      /* see linux/timer.h ! */
+#endif
+
+#define PTR2USHORT(a)   (ushort)(ulong)(a)
+#define JIFFYWAIT(a)    {ulong gdtjf;gdtjf=jiffies+(a);while(gdtjf>jiffies);}
+#define GDTOFFSOF(a,b)  (size_t)&(((a*)0)->b)   
+#define INDEX_OK(i,t)   ((i)<sizeof(t)/sizeof((t)[0]))
+
+#define NUMDATA(a)      ( (gdth_num_str  *)((a)->hostdata))
+#define HADATA(a)       (&((gdth_ext_str *)((a)->hostdata))->haext)
+#define CMDDATA(a)      (&((gdth_ext_str *)((a)->hostdata))->cmdext)
+#define DMADATA(a)      (&((gdth_ext_str *)((a)->hostdata))->dmaext)
+
+
+#if LINUX_VERSION_CODE < 0x010300
+static void *gdth_mmap(ulong paddr, ulong size) 
+{
+    if (paddr >= high_memory)
+	return NULL; 
+    else
+	return (void *)paddr;
+}
+static void gdth_munmap(void *addr) 
+{
+}
+inline ulong virt_to_phys(volatile void *addr)
+{
+    return (ulong)addr;
+}
+inline void *phys_to_virt(ulong addr)
+{
+    return (void *)addr;
+}
+#define virt_to_bus		virt_to_phys
+#define bus_to_virt		phys_to_virt
+#define readb(addr)		(*(volatile unchar *)(addr))
+#define readw(addr)		(*(volatile ushort *)(addr))
+#define readl(addr)		(*(volatile ulong *)(addr))
+#define writeb(b,addr)		(*(volatile unchar *)(addr) = (b))
+#define writew(b,addr)		(*(volatile ushort *)(addr) = (b))
+#define writel(b,addr)		(*(volatile ulong *)(addr) = (b))
+#define memset_io(a,b,c)	memset((void *)(a),(b),(c))
+#define memcpy_fromio(a,b,c)	memcpy((a),(void *)(b),(c))
+#define memcpy_toio(a,b,c)	memcpy((void *)(a),(b),(c))
+
+#elif LINUX_VERSION_CODE < 0x020100
+static int remapped = FALSE;
+static void *gdth_mmap(ulong paddr, ulong size) 
+{
+    if ( paddr >= high_memory) {
+	remapped = TRUE;
+	return vremap(paddr, size);
+    } else {
+	return (void *)paddr; 
+    }
+}
+static void gdth_munmap(void *addr) 
+{
+    if (remapped)
+	vfree(addr);
+    remapped = FALSE;
+}
+#else
+static void *gdth_mmap(ulong paddr, ulong size) 
+{ 
+    return ioremap(paddr, size); 
+}
+static void gdth_munmap(void *addr) 
+{
+    return iounmap(addr);
+}
+#endif
+
+
+static unchar   gdth_drq_tab[4] = {5,6,7,7};            /* DRQ table */
+static unchar   gdth_irq_tab[6] = {0,10,11,12,14,0};    /* IRQ table */
+static unchar   gdth_polling;                           /* polling if TRUE */
+static unchar   gdth_from_wait  = FALSE;                /* gdth_wait() */
+static int      wait_index,wait_hanum;                  /* gdth_wait() */
+static int      gdth_ctr_count  = 0;                    /* controller count */
+static int      gdth_ctr_vcount = 0;                    /* virt. ctr. count */
+static struct Scsi_Host *gdth_ctr_tab[MAXHA];           /* controller table */
+static struct Scsi_Host *gdth_ctr_vtab[MAXHA*MAXBUS];   /* virt. ctr. table */
+static unchar   gdth_write_through = FALSE;             /* write through */
+static char *gdth_ioctl_tab[4][MAXHA];                  /* ioctl buffer */
+static gdth_evt_str ebuffer[MAX_EVENTS];                /* event buffer */
+static int elastidx;
+static int eoldidx;
+
+static struct {
+    Scsi_Cmnd   *cmnd;                          /* pending request */
+    ushort      service;                        /* service */
+} gdth_cmd_tab[GDTH_MAXCMDS][MAXHA];            /* table of pend. requests */
+
+#define DIN     1                               /* IN data direction */
+#define DOU     2                               /* OUT data direction */
+#define DNO     DIN                             /* no data transfer */
+#define DUN     DIN                             /* unknown data direction */
+static unchar gdth_direction_tab[0x100] = {
+    DNO,DNO,DIN,DIN,DOU,DIN,DIN,DOU,DIN,DUN,DOU,DOU,DUN,DUN,DUN,DIN,
+    DNO,DIN,DIN,DOU,DIN,DOU,DNO,DNO,DOU,DNO,DIN,DNO,DIN,DOU,DNO,DUN,
+    DIN,DUN,DIN,DUN,DOU,DIN,DUN,DUN,DIN,DIN,DIN,DUN,DUN,DIN,DIN,DIN,
+    DIN,DIN,DIN,DNO,DIN,DNO,DNO,DIN,DIN,DIN,DIN,DIN,DIN,DIN,DIN,DIN,
+    DIN,DIN,DIN,DIN,DIN,DNO,DUN,DNO,DNO,DNO,DUN,DNO,DIN,DIN,DUN,DUN,
+    DUN,DUN,DUN,DUN,DUN,DIN,DUN,DUN,DUN,DUN,DIN,DUN,DUN,DUN,DUN,DUN,
+    DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,
+    DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,
+    DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,
+    DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,
+    DUN,DUN,DUN,DUN,DUN,DNO,DNO,DUN,DIN,DNO,DIN,DUN,DNO,DUN,DIN,DIN,
+    DIN,DIN,DIN,DNO,DUN,DIN,DIN,DIN,DIN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,
+    DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,
+    DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,
+    DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DOU,DUN,DUN,DUN,DUN,DUN,
+    DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN
+};
+
+/* LILO params: gdth=<IRQ>
+ *
+ * Where: <IRQ> is any of the valid IRQs for EISA controllers (10,11,12,14)
+ * Sets the IRQ of the GDT3000/3020 EISA controller to this value,
+ * if the IRQ can not automat. detect (controller BIOS disabled)
+ * See gdth_init_eisa() 
+ *
+ * You can use the command line gdth=0 to disable the driver 
+ */
+static unchar irqs[MAXHA] = {0xff};
+static unchar disable_gdth_scan = FALSE;
+
+/* /proc support */
+#if LINUX_VERSION_CODE >= 0x010300
+#include <linux/stat.h> 
+struct proc_dir_entry proc_scsi_gdth = {
+    PROC_SCSI_GDTH, 4, "gdth",
+    S_IFDIR | S_IRUGO | S_IXUGO, 2
+};
+#include "gdth_proc.h"
+#include "gdth_proc.c"
+#endif
+
+#if LINUX_VERSION_CODE >= 0x020100
+/* notifier block to get a notify on system shutdown/halt/reboot */
+static struct notifier_block gdth_notifier = {
+    gdth_halt, NULL, 0
+};
+#endif
+
+/* controller search and initialization functions */
+
+static int gdth_search_eisa(ushort eisa_adr)
+{
+    ulong id;
+    
+    TRACE(("gdth_search_eisa() adr. %x\n",eisa_adr));
+    id = inl(eisa_adr+ID0REG);
+    if (id == GDT3A_ID || id == GDT3B_ID) {     /* GDT3000A or GDT3000B */
+        if ((inb(eisa_adr+EISAREG) & 8) == 0)   
+            return 0;                           /* not EISA configured */
+        return 1;
+    }
+    if (id == GDT3_ID)                          /* GDT3000 */
+        return 1;
+
+    return 0;                                   
+}
+
+
+static int gdth_search_isa(ulong bios_adr)
+{
+    void *addr;
+    ulong id;
+
+    TRACE(("gdth_search_isa() bios adr. %lx\n",bios_adr));
+    if ((addr = gdth_mmap(bios_adr+BIOS_ID_OFFS, sizeof(ulong))) != NULL) {
+	id = readl(addr);
+	gdth_munmap(addr);
+	if (id == GDT2_ID)                          /* GDT2000 */
+	    return 1;
+    }
+    return 0;
+}
+
+
+static int gdth_search_pci(ushort device_id,ushort index,gdth_pci_str *pcistr)
+{
+    int error;
+    ulong base0,base1,base2;
+
+    TRACE(("gdth_search_pci() device_id %d, index %d\n",
+                 device_id,index));
+
+    if (!pcibios_present())
+        return 0;
+
+    if (pcibios_find_device(PCI_VENDOR_ID_VORTEX,device_id,index,
+                             &pcistr->bus,&pcistr->device_fn))
+        return 0;
+
+    /* GDT PCI controller found, now read resources from config space */
+#if LINUX_VERSION_CODE >= 0x010300
+#define GDTH_BASEP      (int *)
+#else
+#define GDTH_BASEP
+#endif
+    if ((error = pcibios_read_config_dword(pcistr->bus,pcistr->device_fn,
+                                           PCI_BASE_ADDRESS_0,
+                                           GDTH_BASEP&base0)) ||
+        (error = pcibios_read_config_dword(pcistr->bus,pcistr->device_fn,
+                                           PCI_BASE_ADDRESS_1,
+                                           GDTH_BASEP&base1)) ||
+        (error = pcibios_read_config_dword(pcistr->bus,pcistr->device_fn,
+                                           PCI_BASE_ADDRESS_2,
+                                           GDTH_BASEP&base2)) ||
+        (error = pcibios_read_config_dword(pcistr->bus,pcistr->device_fn,
+                                           PCI_ROM_ADDRESS,
+                                           GDTH_BASEP&pcistr->bios)) ||
+        (error = pcibios_read_config_byte(pcistr->bus,pcistr->device_fn,
+                                          PCI_INTERRUPT_LINE,&pcistr->irq))) {
+        printk("GDT-PCI: error %s reading configuration space",
+               pcibios_strerror(error));
+        return -1;
+    }
+
+    pcistr->device_id = device_id;
+    if (device_id <= PCI_DEVICE_ID_VORTEX_GDT6000B ||   /* GDT6000 or GDT6000B */
+        device_id >= PCI_DEVICE_ID_VORTEX_GDT6x17RP) {  /* MPR */
+        if ((base0 & PCI_BASE_ADDRESS_SPACE)!=PCI_BASE_ADDRESS_SPACE_MEMORY)
+            return -1;
+        pcistr->dpmem = base0 & PCI_BASE_ADDRESS_MEM_MASK;
+    } else {                                    /* GDT6110, GDT6120, .. */
+        if ((base0 & PCI_BASE_ADDRESS_SPACE)!=PCI_BASE_ADDRESS_SPACE_MEMORY ||
+            (base2 & PCI_BASE_ADDRESS_SPACE)!=PCI_BASE_ADDRESS_SPACE_MEMORY ||
+            (base1 & PCI_BASE_ADDRESS_SPACE)!=PCI_BASE_ADDRESS_SPACE_IO)
+            return -1;
+        pcistr->dpmem = base2 & PCI_BASE_ADDRESS_MEM_MASK;
+        pcistr->io_mm = base0 & PCI_BASE_ADDRESS_MEM_MASK;
+        pcistr->io    = base1 & PCI_BASE_ADDRESS_IO_MASK;
+    }
+    return 1;
+}
+
+
+static int gdth_init_eisa(ushort eisa_adr,gdth_ha_str *ha)
+{
+    ulong retries,id;
+    unchar prot_ver,eisacf,i,irq_found;
+
+    TRACE(("gdth_init_eisa() adr. %x\n",eisa_adr));
+    
+    /* disable board interrupts, deinitialize services */
+    outb(0xff,eisa_adr+EDOORREG);
+    outb(0x00,eisa_adr+EDENABREG);
+    outb(0x00,eisa_adr+EINTENABREG);
+    
+    outb(0xff,eisa_adr+LDOORREG);
+    retries = INIT_RETRIES;
+    JIFFYWAIT(2);
+    while (inb(eisa_adr+EDOORREG) != 0xff) {
+        if (--retries == 0) {
+            printk("GDT-EISA: Initialization error (DEINIT failed)\n");
+            return 0;
+        }
+        udelay(1000);
+        TRACE2(("wait for DEINIT: retries=%ld\n",retries));
+    }
+    prot_ver = inb(eisa_adr+MAILBOXREG);
+    outb(0xff,eisa_adr+EDOORREG);
+    if (prot_ver != PROTOCOL_VERSION) {
+        printk("GDT-EISA: Illegal protocol version\n");
+        return 0;
+    }
+    ha->bmic = eisa_adr;
+    ha->brd_phys = (ulong)eisa_adr >> 12;
+
+    outl(0,eisa_adr+MAILBOXREG);
+    outl(0,eisa_adr+MAILBOXREG+4);
+    outl(0,eisa_adr+MAILBOXREG+8);
+    outl(0,eisa_adr+MAILBOXREG+12);
+
+    /* detect IRQ */ 
+    if ((id = inl(eisa_adr+ID0REG)) == GDT3_ID) {
+        ha->type = GDT_EISA;
+        ha->stype = id;
+        outl(1,eisa_adr+MAILBOXREG+8);
+        outb(0xfe,eisa_adr+LDOORREG);
+        retries = INIT_RETRIES;
+        JIFFYWAIT(2);
+        while (inb(eisa_adr+EDOORREG) != 0xfe) {
+            if (--retries == 0) {
+                printk("GDT-EISA: Initialization error (get IRQ failed)\n");
+                return 0;
+            }
+            udelay(1000);
+        }
+        ha->irq = inb(eisa_adr+MAILBOXREG);
+        outb(0xff,eisa_adr+EDOORREG);
+        TRACE2(("GDT3000/3020: IRQ=%d\n",ha->irq));
+	/* check the result */
+	if (ha->irq == 0) {
+	    TRACE2(("Unknown IRQ, check IRQ table from cmd line !\n"));
+	    for (i=0,irq_found=FALSE; i<MAXHA && irqs[i]!=0xff; ++i) {
+		if (irqs[i]!=0) {
+		    irq_found=TRUE;
+		    break;
+		}
+	    }
+	    if (irq_found) {
+		ha->irq = irqs[i];
+		irqs[i] = 0;
+		printk("GDT-EISA: Can not detect controller IRQ,\n");
+		printk("Use IRQ setting from command line (IRQ = %d)\n",
+		       ha->irq);
+	    } else {
+		printk("GDT-EISA: Initialization error (unknown IRQ), Enable\n");
+		printk("the controller BIOS or use command line parameters\n");
+		return 0;
+	    }
+	}
+    } else {
+        eisacf = inb(eisa_adr+EISAREG) & 7;
+        if (eisacf > 4)                         /* level triggered */
+            eisacf -= 4;
+        ha->irq = gdth_irq_tab[eisacf];
+        ha->type = GDT_EISA;
+        ha->stype= id;
+    }
+    return 1;
+}
+
+       
+static int gdth_init_isa(ulong bios_adr,gdth_ha_str *ha)
+{
+    register gdt2_dpram_str *dp2_ptr;
+    int i;
+    unchar irq_drq,prot_ver;
+    ulong retries;
+
+    TRACE(("gdth_init_isa() bios adr. %lx\n",bios_adr));
+
+    ha->brd = gdth_mmap(bios_adr, sizeof(gdt2_dpram_str));
+    if (ha->brd == NULL) {
+	printk("GDT-ISA: Initialization error (DPMEM remap error)\n");
+	return 0;
+    }
+    dp2_ptr = (gdt2_dpram_str *)ha->brd;
+    writeb(1, &dp2_ptr->io.memlock);			/* switch off write protection */
+    /* reset interface area */
+    memset_io((char *)&dp2_ptr->u,0,sizeof(dp2_ptr->u));
+
+    /* disable board interrupts, read DRQ and IRQ */
+    writeb(0xff, &dp2_ptr->io.irqdel);
+    writeb(0x00, &dp2_ptr->io.irqen);
+    writeb(0x00, &dp2_ptr->u.ic.S_Status);
+    writeb(0x00, &dp2_ptr->u.ic.Cmd_Index);
+
+    irq_drq = readb(&dp2_ptr->io.rq);
+    for (i=0; i<3; ++i) {
+        if ((irq_drq & 1)==0)
+            break;
+        irq_drq >>= 1;
+    }
+    ha->drq = gdth_drq_tab[i];
+
+    irq_drq = readb(&dp2_ptr->io.rq) >> 3;
+    for (i=1; i<5; ++i) {
+        if ((irq_drq & 1)==0)
+            break;
+        irq_drq >>= 1;
+    }
+    ha->irq = gdth_irq_tab[i];
+
+    /* deinitialize services */
+    writel(bios_adr, &dp2_ptr->u.ic.S_Info[0]);
+    writeb(0xff, &dp2_ptr->u.ic.S_Cmd_Indx);
+    writeb(0, &dp2_ptr->io.event);
+    retries = INIT_RETRIES;
+    JIFFYWAIT(2);
+    while (readb(&dp2_ptr->u.ic.S_Status) != 0xff) {
+        if (--retries == 0) {
+            printk("GDT-ISA: Initialization error (DEINIT failed)\n");
+	    gdth_munmap(ha->brd);
+            return 0;
+        }
+        udelay(1000);
+    }
+    prot_ver = (unchar)readl(&dp2_ptr->u.ic.S_Info[0]);
+    writeb(0, &dp2_ptr->u.ic.Status);
+    writeb(0xff, &dp2_ptr->io.irqdel);
+    if (prot_ver != PROTOCOL_VERSION) {
+        printk("GDT-ISA: Illegal protocol version\n");
+	gdth_munmap(ha->brd);
+        return 0;
+    }
+
+    ha->type = GDT_ISA;
+    ha->ic_all_size = sizeof(dp2_ptr->u);
+    ha->stype= GDT2_ID;
+    ha->brd_phys = bios_adr >> 4;
+
+    /* special request to controller BIOS */
+    writel(0x00, &dp2_ptr->u.ic.S_Info[0]);
+    writel(0x00, &dp2_ptr->u.ic.S_Info[1]);
+    writel(0x01, &dp2_ptr->u.ic.S_Info[2]);
+    writel(0x00, &dp2_ptr->u.ic.S_Info[3]);
+    writeb(0xfe, &dp2_ptr->u.ic.S_Cmd_Indx);
+    writeb(0, &dp2_ptr->io.event);
+    retries = INIT_RETRIES;
+    JIFFYWAIT(2);
+    while (readb(&dp2_ptr->u.ic.S_Status) != 0xfe) {
+        if (--retries == 0) {
+            printk("GDT-ISA: Initialization error\n");
+	    gdth_munmap(ha->brd);
+            return 0;
+        }
+        udelay(1000);
+    }
+    writeb(0, &dp2_ptr->u.ic.Status);
+    writeb(0xff, &dp2_ptr->io.irqdel);
+    return 1;
+}
+
+
+static int gdth_init_pci(gdth_pci_str *pcistr,gdth_ha_str *ha)
+{
+    register gdt6_dpram_str *dp6_ptr;
+    register gdt6c_dpram_str *dp6c_ptr;
+    register gdt6m_dpram_str *dp6m_ptr;
+    ulong retries;
+    unchar prot_ver;
+
+    TRACE(("gdth_init_pci()\n"));
+
+    ha->brd_phys = (pcistr->bus << 8) | (pcistr->device_fn & 0xf8);
+    ha->stype    = (ulong)pcistr->device_id;
+    ha->irq      = pcistr->irq;
+    
+    if (ha->stype <= PCI_DEVICE_ID_VORTEX_GDT6000B) {   /* GDT6000 or GDT6000B */
+        TRACE2(("init_pci() dpmem %lx irq %d\n",pcistr->dpmem,ha->irq));
+	ha->brd = gdth_mmap(pcistr->dpmem, sizeof(gdt6_dpram_str));
+	if (ha->brd == NULL) {
+	    printk("GDT-PCI: Initialization error (DPMEM remap error)\n");
+	    return 0;
+	}
+        dp6_ptr = (gdt6_dpram_str *)ha->brd;
+        /* reset interface area */
+        memset_io((char *)&dp6_ptr->u,0,sizeof(dp6_ptr->u));
+        if (readl(&dp6_ptr->u) != 0) {
+            printk("GDT-PCI: Initialization error (DPMEM write error)\n");
+	    gdth_munmap(ha->brd);
+            return 0;
+        }
+        
+        /* disable board interrupts, deinit services */
+        writeb(0xff, &dp6_ptr->io.irqdel);
+        writeb(0x00, &dp6_ptr->io.irqen);;
+        writeb(0x00, &dp6_ptr->u.ic.S_Status);
+        writeb(0x00, &dp6_ptr->u.ic.Cmd_Index);
+
+        writel(pcistr->dpmem, &dp6_ptr->u.ic.S_Info[0]);
+        writeb(0xff, &dp6_ptr->u.ic.S_Cmd_Indx);
+        writeb(0, &dp6_ptr->io.event);
+        retries = INIT_RETRIES;
+        JIFFYWAIT(2);
+        while (readb(&dp6_ptr->u.ic.S_Status) != 0xff) {
+            if (--retries == 0) {
+                printk("GDT-PCI: Initialization error (DEINIT failed)\n");
+		gdth_munmap(ha->brd);
+                return 0;
+            }
+            udelay(1000);
+        }
+        prot_ver = (unchar)readl(&dp6_ptr->u.ic.S_Info[0]);
+        writeb(0, &dp6_ptr->u.ic.S_Status);
+        writeb(0xff, &dp6_ptr->io.irqdel);
+        if (prot_ver != PROTOCOL_VERSION) {
+            printk("GDT-PCI: Illegal protocol version\n");
+	    gdth_munmap(ha->brd);
+            return 0;
+        }
+
+        ha->type = GDT_PCI;
+        ha->ic_all_size = sizeof(dp6_ptr->u);
+        
+        /* special command to controller BIOS */
+        writel(0x00, &dp6_ptr->u.ic.S_Info[0]);
+        writel(0x00, &dp6_ptr->u.ic.S_Info[1]);
+        writel(0x01, &dp6_ptr->u.ic.S_Info[2]);
+        writel(0x00, &dp6_ptr->u.ic.S_Info[3]);
+        writeb(0xfe, &dp6_ptr->u.ic.S_Cmd_Indx);
+        writeb(0, &dp6_ptr->io.event);
+        retries = INIT_RETRIES;
+        JIFFYWAIT(2);
+        while (readb(&dp6_ptr->u.ic.S_Status) != 0xfe) {
+            if (--retries == 0) {
+                printk("GDT-PCI: Initialization error\n");
+		gdth_munmap(ha->brd);
+                return 0;
+            }
+            udelay(1000);
+        }
+        writeb(0, &dp6_ptr->u.ic.S_Status);
+        writeb(0xff, &dp6_ptr->io.irqdel);
+
+    } else if (ha->stype <= PCI_DEVICE_ID_VORTEX_GDT6555) { /* GDT6110, GDT6120, .. */
+	ha->plx = (gdt6c_plx_regs *)pcistr->io;
+	TRACE2(("init_pci_new() dpmem %lx io %lx irq %d\n",
+		pcistr->dpmem,(ulong)ha->plx,ha->irq));
+	ha->brd = gdth_mmap(pcistr->dpmem, sizeof(gdt6c_dpram_str));
+	if (ha->brd == NULL) {
+	    printk("GDT-PCI: Initialization error (DPMEM remap error)\n");
+	    gdth_munmap(ha->brd);
+	    return 0;
+	}
+        dp6c_ptr = (gdt6c_dpram_str *)ha->brd;
+        /* reset interface area */
+        memset_io((char *)&dp6c_ptr->u,0,sizeof(dp6c_ptr->u));
+        if (readl(&dp6c_ptr->u) != 0) {
+            printk("GDT-PCI: Initialization error (DPMEM write error)\n");
+	    gdth_munmap(ha->brd);
+            return 0;
+        }
+        
+        /* disable board interrupts, deinit services */
+        outb(0x00,PTR2USHORT(&ha->plx->control1));
+        outb(0xff,PTR2USHORT(&ha->plx->edoor_reg));
+        
+        writeb(0x00, &dp6c_ptr->u.ic.S_Status);
+        writeb(0x00, &dp6c_ptr->u.ic.Cmd_Index);
+
+        writel(pcistr->dpmem, &dp6c_ptr->u.ic.S_Info[0]);
+        writeb(0xff, &dp6c_ptr->u.ic.S_Cmd_Indx);
+
+        outb(1,PTR2USHORT(&ha->plx->ldoor_reg));
+
+        retries = INIT_RETRIES;
+        JIFFYWAIT(2);
+        while (readb(&dp6c_ptr->u.ic.S_Status) != 0xff) {
+            if (--retries == 0) {
+                printk("GDT-PCI: Initialization error (DEINIT failed)\n");
+		gdth_munmap(ha->brd);
+                return 0;
+            }
+            udelay(1000);
+        }
+        prot_ver = (unchar)readl(&dp6c_ptr->u.ic.S_Info[0]);
+        writeb(0, &dp6c_ptr->u.ic.Status);
+        if (prot_ver != PROTOCOL_VERSION) {
+            printk("GDT-PCI: Illegal protocol version\n");
+	    gdth_munmap(ha->brd);
+            return 0;
+        }
+
+        ha->type = GDT_PCINEW;
+        ha->ic_all_size = sizeof(dp6c_ptr->u);
+
+        /* special command to controller BIOS */
+        writel(0x00, &dp6c_ptr->u.ic.S_Info[0]);
+        writel(0x00, &dp6c_ptr->u.ic.S_Info[1]);
+        writel(0x01, &dp6c_ptr->u.ic.S_Info[2]);
+        writel(0x00, &dp6c_ptr->u.ic.S_Info[3]);
+        writeb(0xfe, &dp6c_ptr->u.ic.S_Cmd_Indx);
+        
+        outb(1,PTR2USHORT(&ha->plx->ldoor_reg));
+
+        retries = INIT_RETRIES;
+        JIFFYWAIT(2);
+        while (readb(&dp6c_ptr->u.ic.S_Status) != 0xfe) {
+            if (--retries == 0) {
+                printk("GDT-PCI: Initialization error\n");
+		gdth_munmap(ha->brd);
+                return 0;
+            }
+            udelay(1000);
+        }
+        writeb(0, &dp6c_ptr->u.ic.S_Status);
+
+    } else {                                            /* MPR */
+	TRACE2(("init_pci_mpr() dpmem %lx irq %d\n",pcistr->dpmem,ha->irq));
+	ha->brd = gdth_mmap(pcistr->dpmem, sizeof(gdt6m_dpram_str));
+	if (ha->brd == NULL) {
+	    printk("GDT-PCI: Initialization error (DPMEM remap error)\n");
+	    return 0;
+	}
+
+        dp6m_ptr = (gdt6m_dpram_str *)ha->brd;
+        /* reset interface area */
+        memset_io((char *)&dp6m_ptr->u,0,sizeof(dp6m_ptr->u));
+        if (readl(&dp6m_ptr->u) != 0) {
+            printk("GDT-PCI: Initialization error (DPMEM write error)\n");
+	    gdth_munmap(ha->brd);
+            return 0;
+        }
+        
+        /* disable board interrupts, deinit services */
+        writeb(readb(&dp6m_ptr->i960r.edoor_en_reg) | 4,
+	       &dp6m_ptr->i960r.edoor_en_reg);
+        writeb(0xff, &dp6m_ptr->i960r.edoor_reg);
+        writeb(0x00, &dp6m_ptr->u.ic.S_Status);
+        writeb(0x00, &dp6m_ptr->u.ic.Cmd_Index);
+
+        writel(pcistr->dpmem, &dp6m_ptr->u.ic.S_Info[0]);
+        writeb(0xff, &dp6m_ptr->u.ic.S_Cmd_Indx);
+        writeb(1, &dp6m_ptr->i960r.ldoor_reg);
+        retries = INIT_RETRIES;
+        JIFFYWAIT(2);
+        while (readb(&dp6m_ptr->u.ic.S_Status) != 0xff) {
+            if (--retries == 0) {
+                printk("GDT-PCI: Initialization error (DEINIT failed)\n");
+		gdth_munmap(ha->brd);
+                return 0;
+            }
+            udelay(1000);
+        }
+        prot_ver = (unchar)readl(&dp6m_ptr->u.ic.S_Info[0]);
+        writeb(0, &dp6m_ptr->u.ic.S_Status);
+        if (prot_ver != PROTOCOL_VERSION) {
+            printk("GDT-PCI: Illegal protocol version\n");
+	    gdth_munmap(ha->brd);
+            return 0;
+        }
+
+        ha->type = GDT_PCIMPR;
+        ha->ic_all_size = sizeof(dp6m_ptr->u);
+        
+        /* special command to controller BIOS */
+        writel(0x00, &dp6m_ptr->u.ic.S_Info[0]);
+        writel(0x00, &dp6m_ptr->u.ic.S_Info[1]);
+        writel(0x01, &dp6m_ptr->u.ic.S_Info[2]);
+        writel(0x00, &dp6m_ptr->u.ic.S_Info[3]);
+        writeb(0xfe, &dp6m_ptr->u.ic.S_Cmd_Indx);
+        writeb(1, &dp6m_ptr->i960r.ldoor_reg);
+        retries = INIT_RETRIES;
+        JIFFYWAIT(2);
+        while (readb(&dp6m_ptr->u.ic.S_Status) != 0xfe) {
+            if (--retries == 0) {
+                printk("GDT-PCI: Initialization error\n");
+		gdth_munmap(ha->brd);
+                return 0;
+            }
+            udelay(1000);
+        }
+        writeb(0, &dp6m_ptr->u.ic.S_Status);
+    }
+
+    return 1;
+}
+
+
+/* controller protocol functions */
+
+static void gdth_enable_int(int hanum)
+{
+    gdth_ha_str *ha;
+    ulong flags;
+    gdt2_dpram_str *dp2_ptr;
+    gdt6_dpram_str *dp6_ptr;
+    gdt6m_dpram_str *dp6m_ptr;
+
+    TRACE(("gdth_enable_int() hanum %d\n",hanum));
+    ha = HADATA(gdth_ctr_tab[hanum]);
+
+    save_flags(flags);
+    cli();
+
+    if (ha->type == GDT_EISA) {
+        outb(0xff, ha->bmic + EDOORREG);
+        outb(0xff, ha->bmic + EDENABREG);
+        outb(0x01, ha->bmic + EINTENABREG);
+    } else if (ha->type == GDT_ISA) {
+        dp2_ptr = (gdt2_dpram_str *)ha->brd;
+        writeb(1, &dp2_ptr->io.irqdel);
+        writeb(0, &dp2_ptr->u.ic.Cmd_Index);
+        writeb(1, &dp2_ptr->io.irqen);
+    } else if (ha->type == GDT_PCI) {
+        dp6_ptr = (gdt6_dpram_str *)ha->brd;
+        writeb(1, &dp6_ptr->io.irqdel);
+        writeb(0, &dp6_ptr->u.ic.Cmd_Index);
+        writeb(1, &dp6_ptr->io.irqen);
+    } else if (ha->type == GDT_PCINEW) {
+        outb(0xff, PTR2USHORT(&ha->plx->edoor_reg));
+        outb(0x03, PTR2USHORT(&ha->plx->control1));
+    } else if (ha->type == GDT_PCIMPR) {
+        dp6m_ptr = (gdt6m_dpram_str *)ha->brd;
+        writeb(0xff, &dp6m_ptr->i960r.edoor_reg);
+        writeb(readb(&dp6m_ptr->i960r.edoor_en_reg) & ~4,
+	       &dp6m_ptr->i960r.edoor_en_reg);
+    }
+    restore_flags(flags);
+}
+
+
+static int gdth_get_status(unchar *pIStatus,int irq)
+{
+    register gdth_ha_str *ha;
+    int i;
+
+    TRACE(("gdth_get_status() irq %d ctr_count %d\n",
+                 irq,gdth_ctr_count));
+    
+    *pIStatus = 0;
+    for (i=0; i<gdth_ctr_count; ++i) {
+        ha = HADATA(gdth_ctr_tab[i]);
+        if (ha->irq != (unchar)irq)             /* check IRQ */
+            continue;
+        if (ha->type == GDT_EISA)
+            *pIStatus = inb((ushort)ha->bmic + EDOORREG);
+        else if (ha->type == GDT_ISA)
+            *pIStatus = readb(&((gdt2_dpram_str *)ha->brd)->u.ic.Cmd_Index);
+        else if (ha->type == GDT_PCI)
+            *pIStatus = readb(&((gdt6_dpram_str *)ha->brd)->u.ic.Cmd_Index);
+        else if (ha->type == GDT_PCINEW) 
+            *pIStatus = inb(PTR2USHORT(&ha->plx->edoor_reg));
+        else if (ha->type == GDT_PCIMPR)
+            *pIStatus = readb(&((gdt6m_dpram_str *)ha->brd)->i960r.edoor_reg);
+   
+        if (*pIStatus)                                  
+            return i;                           /* board found */
+    }
+    return -1;
+}
+                 
+    
+static int gdth_test_busy(int hanum)
+{
+    register gdth_ha_str *ha;
+    register int gdtsema0 = 0;
+
+    TRACE(("gdth_test_busy() hanum %d\n",hanum));
+    
+    ha = HADATA(gdth_ctr_tab[hanum]);
+    if (ha->type == GDT_EISA)
+        gdtsema0 = (int)inb(ha->bmic + SEMA0REG);
+    else if (ha->type == GDT_ISA)
+        gdtsema0 = (int)readb(&((gdt2_dpram_str *)ha->brd)->u.ic.Sema0);
+    else if (ha->type == GDT_PCI)
+        gdtsema0 = (int)readb(&((gdt6_dpram_str *)ha->brd)->u.ic.Sema0);
+    else if (ha->type == GDT_PCINEW) 
+        gdtsema0 = (int)inb(PTR2USHORT(&ha->plx->sema0_reg));
+    else if (ha->type == GDT_PCIMPR)
+        gdtsema0 = (int)readb(&((gdt6m_dpram_str *)ha->brd)->i960r.sema0_reg);
+
+    return (gdtsema0 & 1);
+}
+
+
+static int gdth_get_cmd_index(int hanum)
+{
+    register gdth_ha_str *ha;
+    int i;
+
+    TRACE(("gdth_get_cmd_index() hanum %d\n",hanum));
+
+    ha = HADATA(gdth_ctr_tab[hanum]);
+    for (i=0; i<GDTH_MAXCMDS; ++i) {
+        if (gdth_cmd_tab[i][hanum].cmnd == UNUSED_CMND) {
+            gdth_cmd_tab[i][hanum].cmnd = ha->pccb->RequestBuffer;
+            gdth_cmd_tab[i][hanum].service = ha->pccb->Service;
+            ha->pccb->CommandIndex = (ulong)i+2;
+            return (i+2);
+        }
+    }
+    return 0;
+}
+
+
+static void gdth_set_sema0(int hanum)
+{
+    register gdth_ha_str *ha;
+
+    TRACE(("gdth_set_sema0() hanum %d\n",hanum));
+
+    ha = HADATA(gdth_ctr_tab[hanum]);
+    if (ha->type == GDT_EISA)
+        outb(1, ha->bmic + SEMA0REG);
+    else if (ha->type == GDT_ISA)
+        writeb(1, &((gdt2_dpram_str *)ha->brd)->u.ic.Sema0);
+    else if (ha->type == GDT_PCI)
+        writeb(1, &((gdt6_dpram_str *)ha->brd)->u.ic.Sema0);
+    else if (ha->type == GDT_PCINEW)  
+        outb(1, PTR2USHORT(&ha->plx->sema0_reg));
+    else if (ha->type == GDT_PCIMPR)
+        writeb(1, &((gdt6m_dpram_str *)ha->brd)->i960r.sema0_reg);
+    
+}
+
+
+static void gdth_copy_command(int hanum)
+{
+    register gdth_ha_str *ha;
+    register gdth_cmd_str *cmd_ptr;
+    register gdt6m_dpram_str *dp6m_ptr;
+    register gdt6c_dpram_str *dp6c_ptr;
+    gdt6_dpram_str *dp6_ptr;
+    gdt2_dpram_str *dp2_ptr;
+    ushort cp_count,dp_offset,cmd_no;
+    
+    TRACE(("gdth_copy_command() hanum %d\n",hanum));
+
+    ha = HADATA(gdth_ctr_tab[hanum]);
+    cp_count = ha->cmd_len;
+    dp_offset= ha->cmd_offs_dpmem;
+    cmd_no   = ha->cmd_cnt;
+    cmd_ptr  = ha->pccb;
+
+    ++ha->cmd_cnt;                                                      
+    if (ha->type == GDT_EISA)
+        return;                                 /* no DPMEM, no copy */
+
+    /* set cpcount dword aligned */
+    if (cp_count & 3)
+        cp_count += (4 - (cp_count & 3));
+
+    ha->cmd_offs_dpmem += cp_count;
+    
+    /* set offset and service, copy command to DPMEM */
+    if (ha->type == GDT_ISA) {
+        dp2_ptr = (gdt2_dpram_str *)ha->brd;
+        writew(dp_offset + DPMEM_COMMAND_OFFSET, 
+	       &dp2_ptr->u.ic.comm_queue[cmd_no].offset);
+        writew((ushort)cmd_ptr->Service, 
+	       &dp2_ptr->u.ic.comm_queue[cmd_no].serv_id);
+	memcpy_toio(&dp2_ptr->u.ic.gdt_dpr_cmd[dp_offset],cmd_ptr,cp_count);
+    } else if (ha->type == GDT_PCI) {
+        dp6_ptr = (gdt6_dpram_str *)ha->brd;
+        writew(dp_offset + DPMEM_COMMAND_OFFSET, 
+	       &dp6_ptr->u.ic.comm_queue[cmd_no].offset);
+        writew((ushort)cmd_ptr->Service, 
+	       &dp6_ptr->u.ic.comm_queue[cmd_no].serv_id);
+        memcpy_toio(&dp6_ptr->u.ic.gdt_dpr_cmd[dp_offset],cmd_ptr,cp_count);
+    } else if (ha->type == GDT_PCINEW) {
+        dp6c_ptr = (gdt6c_dpram_str *)ha->brd;
+        writew(dp_offset + DPMEM_COMMAND_OFFSET, 
+	       &dp6c_ptr->u.ic.comm_queue[cmd_no].offset);
+	writew((ushort)cmd_ptr->Service, 
+	       &dp6c_ptr->u.ic.comm_queue[cmd_no].serv_id);
+	memcpy_toio(&dp6c_ptr->u.ic.gdt_dpr_cmd[dp_offset],cmd_ptr,cp_count);
+    } else if (ha->type == GDT_PCIMPR) {
+        dp6m_ptr = (gdt6m_dpram_str *)ha->brd;
+        writew(dp_offset + DPMEM_COMMAND_OFFSET, 
+	       &dp6m_ptr->u.ic.comm_queue[cmd_no].offset);
+        writew((ushort)cmd_ptr->Service, 
+	       &dp6m_ptr->u.ic.comm_queue[cmd_no].serv_id);
+        memcpy_toio(&dp6m_ptr->u.ic.gdt_dpr_cmd[dp_offset],cmd_ptr,cp_count);
+    }
+}
+
+
+static void gdth_release_event(int hanum)
+{
+    register gdth_ha_str *ha;
+
+#ifdef GDTH_STATISTICS
+    ulong i,j;
+    for (i=0,j=0; j<GDTH_MAXCMDS; ++j) {
+        if (gdth_cmd_tab[j][hanum].cmnd != UNUSED_CMND)
+            ++i;
+    }
+    if (max_index < i) {
+        max_index = i;
+        TRACE3(("GDT: max_index = %d\n",(ushort)i));
+    }
+#endif
+
+    TRACE(("gdth_release_event() hanum %d\n",hanum));
+    ha = HADATA(gdth_ctr_tab[hanum]);
+
+    if (ha->pccb->OpCode == GDT_INIT)
+        ha->pccb->Service |= 0x80;
+
+    if (ha->type == GDT_EISA) {
+        outb(ha->pccb->Service, ha->bmic + LDOORREG);
+        if (ha->pccb->OpCode == GDT_INIT)               /* store DMA buffer */
+            outl((ulong)ha->pccb, ha->bmic + MAILBOXREG);
+    } else if (ha->type == GDT_ISA)
+        writeb(0, &((gdt2_dpram_str *)ha->brd)->io.event);
+    else if (ha->type == GDT_PCI)
+        writeb(0, &((gdt6_dpram_str *)ha->brd)->io.event);
+    else if (ha->type == GDT_PCINEW) 
+        outb(1, PTR2USHORT(&ha->plx->ldoor_reg));
+    else if (ha->type == GDT_PCIMPR)
+        writeb(1, &((gdt6m_dpram_str *)ha->brd)->i960r.ldoor_reg);
+}
+
+    
+static int gdth_wait(int hanum,int index,ulong time)
+{
+    gdth_ha_str *ha;
+    int answer_found = FALSE;
+
+    TRACE(("gdth_wait() hanum %d index %d time %ld\n",hanum,index,time));
+
+    ha = HADATA(gdth_ctr_tab[hanum]);
+    if (index == 0)
+        return 1;                               /* no wait required */
+
+    gdth_from_wait = TRUE;
+    do {
+#if LINUX_VERSION_CODE >= 0x010346
+        gdth_interrupt((int)ha->irq,NULL,NULL);
+#else
+        gdth_interrupt((int)ha->irq,NULL);
+#endif
+        if (wait_hanum==hanum && wait_index==index) {
+            answer_found = TRUE;
+            break;
+        }
+        udelay(1000);
+    } while (--time);
+    gdth_from_wait = FALSE;
+    
+    while (gdth_test_busy(hanum))
+        udelay(1);
+
+    return (answer_found);
+}
+
+
+static int gdth_internal_cmd(int hanum,unchar service,ushort opcode,ulong p1,
+                             ulong p2,ulong p3)
+{
+    register gdth_ha_str *ha;
+    register gdth_cmd_str *cmd_ptr;
+    int retries,index;
+
+    TRACE2(("gdth_internal_cmd() service %d opcode %d\n",service,opcode));
+
+    ha = HADATA(gdth_ctr_tab[hanum]);
+    cmd_ptr = ha->pccb;
+    memset((char*)cmd_ptr,0,sizeof(gdth_cmd_str));
+
+    /* make command  */
+    for (retries = INIT_RETRIES;;) {
+        cmd_ptr->Service          = service;
+        cmd_ptr->RequestBuffer    = INTERNAL_CMND;
+        if (!(index=gdth_get_cmd_index(hanum))) {
+            TRACE(("GDT: No free command index found\n"));
+            return 0;
+        }
+        gdth_set_sema0(hanum);
+        cmd_ptr->OpCode           = opcode;
+        cmd_ptr->BoardNode        = LOCALBOARD;
+        if (service == CACHESERVICE) {
+            if (opcode == GDT_IOCTL) {
+                cmd_ptr->u.ioctl.subfunc = p1;
+                cmd_ptr->u.ioctl.channel = p2;
+                cmd_ptr->u.ioctl.param_size = (ushort)p3;
+                cmd_ptr->u.ioctl.p_param = virt_to_bus(ha->pscratch);
+            } else {
+                cmd_ptr->u.cache.DeviceNo = (ushort)p1;
+                cmd_ptr->u.cache.BlockNo  = p2;
+            }
+        } else if (service == SCSIRAWSERVICE) {
+            cmd_ptr->u.raw.direction  = p1;
+            cmd_ptr->u.raw.bus        = (unchar)p2;
+            cmd_ptr->u.raw.target     = (unchar)p3;
+            cmd_ptr->u.raw.lun        = 0;
+        }
+        ha->cmd_len          = sizeof(gdth_cmd_str);
+        ha->cmd_offs_dpmem   = 0;
+        ha->cmd_cnt          = 0;
+        gdth_copy_command(hanum);
+        gdth_release_event(hanum);
+        JIFFYWAIT(2);
+        if (!gdth_wait(hanum,index,INIT_TIMEOUT)) {
+            printk("GDT: Initialization error (timeout service %d)\n",service);
+            return 0;
+        }
+        if (ha->status != S_BSY || --retries == 0)
+            break;
+        udelay(1000);   
+    }   
+    
+    return (ha->status != S_OK ? 0:1);
+}
+    
+
+/* search for devices */
+
+static int gdth_search_drives(int hanum)
+{
+    register gdth_ha_str *ha;
+    ushort cdev_cnt,i;
+    unchar b,t,pos_found;
+    ulong drv_cyls, drv_hds, drv_secs;
+    ulong bus_no;
+    gdth_getch_str *chn;
+    
+    TRACE(("gdth_search_drives() hanum %d\n",hanum));
+    ha = HADATA(gdth_ctr_tab[hanum]);
+
+    /* initialize controller services, at first: screen service */
+    if (!gdth_internal_cmd(hanum,SCREENSERVICE,GDT_INIT,0,0,0)) {
+        printk("GDT: Initialization error screen service (code %d)\n",
+               ha->status);
+        return 0;
+    }
+    TRACE2(("gdth_search_drives(): SCREENSERVICE initialized\n"));
+    
+    /* initialize cache service */
+    if (!gdth_internal_cmd(hanum,CACHESERVICE,GDT_INIT,LINUX_OS,0,0)) {
+        printk("GDT: Initialization error cache service (code %d)\n",
+               ha->status);
+        return 0;
+    }
+    TRACE2(("gdth_search_drives(): CACHESERVICE initialized\n"));
+    cdev_cnt = (ushort)ha->info;
+
+    /* mount all cache devices */
+    gdth_internal_cmd(hanum,CACHESERVICE,GDT_MOUNT,0xffff,1,0);
+    TRACE2(("gdth_search_drives(): mountall CACHESERVICE OK\n"));
+
+    /* initialize cache service after mountall */
+    if (!gdth_internal_cmd(hanum,CACHESERVICE,GDT_INIT,LINUX_OS,0,0)) {
+	printk("GDT: Initialization error cache service (code %d)\n",
+	       ha->status);
+	return 0;
+    }
+    TRACE2(("gdth_search_drives() CACHES. init. after mountall\n"));
+    cdev_cnt = (ushort)ha->info;
+
+    /* detect number of SCSI buses */
+    chn = (gdth_getch_str *)DMADATA(gdth_ctr_tab[hanum]);
+    for (bus_no=0; bus_no<MAXBUS; ++bus_no) {
+	chn->channel_no = bus_no;
+	if (!gdth_internal_cmd(hanum,CACHESERVICE,GDT_IOCTL,
+			       SCSI_CHAN_CNT | L_CTRL_PATTERN,
+			       IO_CHANNEL | INVALID_CHANNEL,
+			       sizeof(gdth_getch_str))) {
+	    if (bus_no == 0) {
+		printk("GDT: Error detecting SCSI channel count (0x%x)\n",
+		       ha->status);
+		return 0;
+	    }
+	    break;
+	}
+	if (chn->siop_id < MAXID)
+	    ha->id[bus_no][chn->siop_id].type = SIOP_DTYP;
+    }       
+    ha->bus_cnt = (unchar)bus_no;
+    TRACE2(("gdth_search_drives() %d SCSI channels\n",ha->bus_cnt));
+
+    /* read cache configuration */
+    if (!gdth_internal_cmd(hanum,CACHESERVICE,GDT_IOCTL,CACHE_INFO,
+			   INVALID_CHANNEL,sizeof(gdth_cinfo_str))) {
+	printk("GDT: Initialization error cache service (code %d)\n",
+	       ha->status);
+	return 0;
+    }
+    ha->cpar = ((gdth_cinfo_str *)DMADATA(gdth_ctr_tab[hanum]))->cpar;
+    TRACE2(("gdth_search_drives() cinfo: vs %lx sta %d str %d dw %d b %d\n",
+	    ha->cpar.version,ha->cpar.state,ha->cpar.strategy,
+	    ha->cpar.write_back,ha->cpar.block_size));
+
+    /* initialize raw service */
+    if (!gdth_internal_cmd(hanum,SCSIRAWSERVICE,GDT_INIT,0,0,0)) {
+        printk("GDT: Initialization error raw service (code %d)\n",
+               ha->status);
+        return 0;
+    }
+    TRACE2(("gdth_search_drives(): RAWSERVICE initialized\n"));
+
+    /* set/get features raw service (scatter/gather) */
+    ha->raw_feat = 0;
+    if (gdth_internal_cmd(hanum,SCSIRAWSERVICE,GDT_SET_FEAT,SCATTER_GATHER,
+                          0,0)) {
+        TRACE2(("gdth_search_drives(): set features RAWSERVICE OK\n"));
+        if (gdth_internal_cmd(hanum,SCSIRAWSERVICE,GDT_GET_FEAT,0,0,0))
+        {
+            TRACE2(("gdth_search_dr(): get feat RAWSERVICE %ld\n",
+                          ha->info));
+            ha->raw_feat = (ushort)ha->info;
+        }
+    } 
+
+    /* set/get features cache service (equal to raw service) */
+    if (gdth_internal_cmd(hanum,CACHESERVICE,GDT_SET_FEAT,0,
+                          SCATTER_GATHER,0)) {
+        TRACE2(("gdth_search_drives(): set features CACHESERVICE OK\n"));
+        if (gdth_internal_cmd(hanum,CACHESERVICE,GDT_GET_FEAT,0,0,0)) {
+            TRACE2(("gdth_search_dr(): get feat CACHESERV. %ld\n",
+                          ha->info));
+            ha->cache_feat = (ushort)ha->info;
+        }
+    }
+
+    /* scanning for raw devices */
+    for (b=0; b<ha->bus_cnt; ++b) {
+        for (t=0; t<MAXID; ++t) {
+            TRACE(("gdth_search_drives() rawd. bus %d id %d\n",b,t));
+            if (ha->id[b][t].type != SIOP_DTYP && 
+                gdth_internal_cmd(hanum,SCSIRAWSERVICE,GDT_INFO,0,b,t)) {
+                ha->id[b][t].type = RAW_DTYP;
+            }
+        }
+    }
+
+    /* scanning for cache devices */
+    for (i=0; i<cdev_cnt && i<MAX_HDRIVES; ++i) {
+        TRACE(("gdth_search_drives() cachedev. %d\n",i));
+        if (gdth_internal_cmd(hanum,CACHESERVICE,GDT_INFO,i,0,0)) {
+            /* dynamic relation between host drive number and Bus/ID */
+            /* search free position */
+            pos_found = FALSE;
+            for (b=0,t=0; b<ha->bus_cnt; ++b) {
+                for (t=0; t<MAXID; ++t) {
+                    if (ha->id[b][t].type == EMPTY_DTYP) {
+                        pos_found = TRUE;
+                        break;
+                    }
+                }
+                if (pos_found)
+                    break;
+            }
+            TRACE(("gdth_search_dr() drive %d free pos at bus/id %d/%d\n",
+                         i,b,t));
+
+            ha->id[b][t].type      = CACHE_DTYP;
+            ha->id[b][t].devtype   = 0;
+            ha->id[b][t].size      = ha->info;
+            ha->id[b][t].hostdrive = i;
+
+            /* evaluate mapping (sectors per head, heads per cylinder) */
+	    ha->id[b][t].size &= ~SECS32;
+	    if (ha->info2 == 0) {
+		drv_cyls = ha->id[b][t].size /HEADS/SECS;
+		if (drv_cyls <= MAXCYLS) {
+		    drv_hds = HEADS;
+		    drv_secs= SECS;
+		} else {                            /* too high for 64*32 */
+		    drv_cyls = ha->id[b][t].size /MEDHEADS/MEDSECS;
+		    if (drv_cyls <= MAXCYLS) {
+			drv_hds = MEDHEADS;
+			drv_secs= MEDSECS;
+		    } else {                        /* too high for 127*63 */
+			drv_cyls = ha->id[b][t].size /BIGHEADS/BIGSECS;
+			drv_hds = BIGHEADS;
+			drv_secs= BIGSECS;
+		    }
+		}
+            } else {
+		drv_hds = ha->info2 & 0xff;
+		drv_secs = (ha->info2 >> 8) & 0xff;
+		drv_cyls = ha->id[b][t].size /drv_hds/drv_secs;
+	    }
+            ha->id[b][t].heads = (unchar)drv_hds;
+            ha->id[b][t].secs  = (unchar)drv_secs;
+            /* round size */
+            ha->id[b][t].size  = drv_cyls * drv_hds * drv_secs;
+            TRACE2(("gdth_search_dr() cdr. %d size %ld hds %ld scs %ld\n",
+                   i,ha->id[b][t].size,drv_hds,drv_secs));
+            
+            /* get informations about device */
+            if (gdth_internal_cmd(hanum,CACHESERVICE,GDT_DEVTYPE,i,
+                                  0,0)) {
+                TRACE(("gdth_search_dr() cache drive %d devtype %ld\n",
+                       i,ha->info));
+                ha->id[b][t].devtype = (ushort)ha->info;
+            }
+        }
+    }
+
+    TRACE(("gdth_search_drives() OK\n"));
+    return 1;
+}
+
+
+/* command queueing/sending functions */
+
+static void gdth_putq(int hanum,Scsi_Cmnd *scp,unchar priority)
+{
+    register gdth_ha_str *ha;
+    register Scsi_Cmnd *pscp;
+    register Scsi_Cmnd *nscp;
+    ulong flags;
+    unchar b, t;
+
+    TRACE(("gdth_putq() priority %d\n",priority));
+    save_flags(flags);
+    cli();
+
+    ha = HADATA(gdth_ctr_tab[hanum]);
+    scp->SCp.this_residual = (int)priority;
+    gdth_update_timeout(scp, scp->timeout * 6);
+#if LINUX_VERSION_CODE >= 0x020000
+    b = scp->channel;
+#else
+    b = NUMDATA(nscp->host)->busnum;
+#endif
+    t = scp->target;
+#if LINUX_VERSION_CODE >= 0x010300
+    if (priority >= DEFAULT_PRI && ha->id[b][t].lock) {
+        TRACE2(("gdth_putq(): locked IO -> update_timeout()\n"));
+        scp->SCp.buffers_residual = gdth_update_timeout(scp, 0);
+    }
+#endif
+
+    if (ha->req_first==NULL) {
+        ha->req_first = scp;                    /* queue was empty */
+        scp->SCp.ptr = NULL;
+    } else {                                    /* queue not empty */
+        pscp = ha->req_first;
+        nscp = (Scsi_Cmnd *)pscp->SCp.ptr;
+        /* priority: 0-highest,..,0xff-lowest */
+        while (nscp && (unchar)nscp->SCp.this_residual <= priority) {
+            pscp = nscp;
+            nscp = (Scsi_Cmnd *)pscp->SCp.ptr;
+        }
+        pscp->SCp.ptr = (char *)scp;
+        scp->SCp.ptr  = (char *)nscp;
+    }
+    restore_flags(flags);
+
+#ifdef GDTH_STATISTICS
+    flags = 0;
+    for (nscp=ha->req_first; nscp; nscp=(Scsi_Cmnd*)nscp->SCp.ptr)
+        ++flags;
+    if (max_rq < flags) {
+        max_rq = flags;
+        TRACE3(("GDT: max_rq = %d\n",(ushort)max_rq));
+    }
+#endif
+}
+
+static void gdth_next(int hanum)
+{
+    register gdth_ha_str *ha;
+    register Scsi_Cmnd *pscp;
+    register Scsi_Cmnd *nscp;
+    unchar b, t, next_cmd, firsttime;
+    ushort hdrive;
+    ulong flags;
+    int cmd_index;
+
+    TRACE(("gdth_next() hanum %d\n",hanum));
+    save_flags(flags);
+    cli();
+
+    ha = HADATA(gdth_ctr_tab[hanum]);
+    ha->cmd_cnt = ha->cmd_offs_dpmem = 0;
+    next_cmd = firsttime = TRUE;
+    cmd_index = 0;
+
+    for (nscp = pscp = ha->req_first; nscp; nscp = (Scsi_Cmnd *)nscp->SCp.ptr) {
+        if (nscp != pscp && nscp != (Scsi_Cmnd *)pscp->SCp.ptr)
+            pscp = (Scsi_Cmnd *)pscp->SCp.ptr;
+#if LINUX_VERSION_CODE >= 0x020000
+        b = nscp->channel;
+#else
+        b = NUMDATA(nscp->host)->busnum;
+#endif
+        t = nscp->target;
+        if (nscp->SCp.this_residual < DEFAULT_PRI || !ha->id[b][t].lock) {
+
+            if (firsttime) {
+                if (gdth_test_busy(hanum)) {        /* controller busy ? */
+                    TRACE(("gdth_next() controller %d busy !\n",hanum));
+                    if (!gdth_polling) {
+                        restore_flags(flags);
+                        return;
+                    }
+                    while (gdth_test_busy(hanum))
+                        udelay(1000);
+                }
+                firsttime = FALSE;
+            }
+
+#if LINUX_VERSION_CODE >= 0x010300
+            if (nscp->done == gdth_scsi_done) {
+                if (!(cmd_index=gdth_special_cmd(hanum,nscp,b)))
+                    next_cmd = FALSE;
+            } else
+#endif
+            if (ha->id[b][t].type != CACHE_DTYP) {
+                if (!(cmd_index=gdth_fill_raw_cmd(hanum,nscp,b)))
+                    next_cmd = FALSE;
+            } else {
+                hdrive = ha->id[b][t].hostdrive;
+                switch (nscp->cmnd[0]) {
+                  case TEST_UNIT_READY:
+                  case INQUIRY:
+                  case REQUEST_SENSE:
+                  case READ_CAPACITY:
+                  case VERIFY:
+                  case START_STOP:
+                  case MODE_SENSE:
+                    TRACE2(("cache cmd %x/%x/%x/%x/%x/%x\n",nscp->cmnd[0],
+                        nscp->cmnd[1],nscp->cmnd[2],nscp->cmnd[3],
+                        nscp->cmnd[4],nscp->cmnd[5]));
+                        gdth_internal_cache_cmd(hanum,nscp,b,&flags);
+                    break;
+
+                  case ALLOW_MEDIUM_REMOVAL:
+                    TRACE2(("cache cmd %x/%x/%x/%x/%x/%x\n",nscp->cmnd[0],
+                        nscp->cmnd[1],nscp->cmnd[2],nscp->cmnd[3],
+                        nscp->cmnd[4],nscp->cmnd[5]));
+                    if ( (nscp->cmnd[4]&1) && !(ha->id[b][t].devtype&1) ) {
+                        TRACE2(("Prevent r. nonremov. drive->do nothing\n"));
+                        nscp->result = DID_OK << 16;
+                        restore_flags( flags );
+                        nscp->scsi_done(nscp);
+                        save_flags( flags );
+                        cli();
+                    } else {
+                        nscp->cmnd[3] = (ha->id[b][t].devtype&1) ? 1:0;
+                        TRACE2(("Prevent/allow r. %d rem. drive %d\n",
+                            nscp->cmnd[4],nscp->cmnd[3]));
+                        if (!(cmd_index=gdth_fill_cache_cmd(hanum,nscp,hdrive)))
+                            next_cmd = FALSE;
+                    }
+                    break;
+
+                  case READ_6:
+                  case WRITE_6:
+                  case READ_10:
+                  case WRITE_10:
+                    if (!(cmd_index=gdth_fill_cache_cmd(hanum,nscp,hdrive)))
+                        next_cmd = FALSE;
+                    break;
+
+                  default:
+                    TRACE2(("cache cmd %x/%x/%x/%x/%x/%x\n",nscp->cmnd[0],
+                        nscp->cmnd[1],nscp->cmnd[2],nscp->cmnd[3],
+                        nscp->cmnd[4],nscp->cmnd[5]));
+                    printk("GDT: Unknown SCSI command 0x%x to cache service !\n",
+                       nscp->cmnd[0]);
+                    nscp->result = DID_ABORT << 16;
+                    restore_flags( flags );
+                    nscp->scsi_done( nscp );
+                    save_flags( flags );
+                    cli();
+                    break;
+                }
+            }
+
+            if (!next_cmd)
+                break;
+            if (nscp == ha->req_first)
+                ha->req_first = pscp = (Scsi_Cmnd *)nscp->SCp.ptr;
+            else
+                pscp->SCp.ptr = nscp->SCp.ptr;
+            if (gdth_polling)
+                break;
+        }
+    }
+
+    if (ha->cmd_cnt > 0) {
+        gdth_release_event(hanum);
+    }
+
+    restore_flags(flags);
+
+    if (gdth_polling && ha->cmd_cnt > 0) {
+        if (!gdth_wait(hanum,cmd_index,POLL_TIMEOUT))
+            printk("GDT: Controller %d: Command %d timed out !\n",
+                   hanum,cmd_index);
+    }
+}
+    
+static void gdth_copy_internal_data(Scsi_Cmnd *scp,char *buffer,ushort count)
+{
+    ushort cpcount,i;
+    ushort cpsum,cpnow;
+    struct scatterlist *sl;
+
+    cpcount = count<=(ushort)scp->bufflen ? count:(ushort)scp->bufflen;
+    if (scp->use_sg) {
+        sl = (struct scatterlist *)scp->request_buffer;
+        for (i=0,cpsum=0; i<scp->use_sg; ++i,++sl) {
+            cpnow = (ushort)sl->length;
+            TRACE(("copy_internal() now %d sum %d count %d %d\n",
+                          cpnow,cpsum,cpcount,(ushort)scp->bufflen));
+            if (cpsum+cpnow > cpcount) 
+                cpnow = cpcount - cpsum;
+            cpsum += cpnow;
+            memcpy((char*)sl->address,buffer,cpnow);
+            if (cpsum == cpcount)
+                break;
+            buffer += cpnow;
+        }
+    } else {
+        TRACE(("copy_internal() count %d\n",cpcount));
+        memcpy((char*)scp->request_buffer,buffer,cpcount);
+    }
+}
+
+static int gdth_internal_cache_cmd(int hanum,Scsi_Cmnd *scp,
+                                   unchar b,ulong *flags)
+{
+    register gdth_ha_str *ha;
+    ushort hdrive;
+    unchar t;
+    gdth_inq_data inq;
+    gdth_rdcap_data rdc;
+    gdth_sense_data sd;
+    gdth_modep_data mpd;
+
+    ha = HADATA(gdth_ctr_tab[hanum]);
+    t  = scp->target;
+    hdrive = ha->id[b][t].hostdrive;
+    TRACE(("gdth_internal_cache_cmd() cmd 0x%x hdrive %d\n",
+                 scp->cmnd[0],hdrive));
+
+    if (scp->lun !=0)
+        scp->result = DID_BAD_TARGET << 16;
+    else {
+        switch (scp->cmnd[0]) {
+          case TEST_UNIT_READY:
+          case VERIFY:
+          case START_STOP:
+            TRACE2(("Test/Verify/Start hdrive %d\n",hdrive));
+            break;
+
+          case INQUIRY:
+            TRACE2(("Inquiry hdrive %d devtype %d\n",
+                          hdrive,ha->id[b][t].devtype));
+            inq.type_qual = (ha->id[b][t].devtype&4) ? TYPE_ROM:TYPE_DISK;
+            /* you can here set all disks to removable, if you want to do
+               a flush using the ALLOW_MEDIUM_REMOVAL command */
+            inq.modif_rmb = ha->id[b][t].devtype&1 ? 0x80:0x00;
+            inq.version   = 2;
+            inq.resp_aenc = 2;
+            inq.add_length= 32;
+            strcpy(inq.vendor,"ICP    ");
+            sprintf(inq.product,"Host Drive  #%02d",hdrive);
+            strcpy(inq.revision,"   ");
+            gdth_copy_internal_data(scp,(char*)&inq,sizeof(gdth_inq_data));
+            break;
+
+          case REQUEST_SENSE:
+            TRACE2(("Request sense hdrive %d\n",hdrive));
+            sd.errorcode = 0x70;
+            sd.segno     = 0x00;
+            sd.key       = NO_SENSE;
+            sd.info      = 0;
+            sd.add_length= 0;
+            gdth_copy_internal_data(scp,(char*)&sd,sizeof(gdth_sense_data));
+            break;
+
+          case MODE_SENSE:
+            TRACE2(("Mode sense hdrive %d\n",hdrive));
+            memset((char*)&mpd,0,sizeof(gdth_modep_data));
+            mpd.hd.data_length = sizeof(gdth_modep_data);
+            mpd.hd.dev_par     = (ha->id[b][t].devtype&2) ? 0x80:0;
+            mpd.hd.bd_length   = sizeof(mpd.bd);
+            mpd.bd.block_length[0] = (SECTOR_SIZE & 0x00ff0000) >> 16;
+            mpd.bd.block_length[1] = (SECTOR_SIZE & 0x0000ff00) >> 8;
+            mpd.bd.block_length[2] = (SECTOR_SIZE & 0x000000ff);
+            gdth_copy_internal_data(scp,(char*)&mpd,sizeof(gdth_modep_data));
+            break;
+
+          case READ_CAPACITY:
+            TRACE2(("Read capacity hdrive %d\n",hdrive));
+            rdc.last_block_no = ntohl(ha->id[b][t].size-1);
+            rdc.block_length  = ntohl(SECTOR_SIZE);
+            gdth_copy_internal_data(scp,(char*)&rdc,sizeof(gdth_rdcap_data));
+            break;
+
+          default:
+            TRACE2(("Internal cache cmd 0x%x unknown\n",scp->cmnd[0]));
+            break;
+        }
+        scp->result = DID_OK << 16;
+    }
+
+    restore_flags(*flags);
+    scp->scsi_done(scp);
+    save_flags(*flags);
+    cli();
+    return 1;
+}
+    
+static int gdth_fill_cache_cmd(int hanum,Scsi_Cmnd *scp,ushort hdrive)
+{
+    register gdth_ha_str *ha;
+    register gdth_cmd_str *cmdp;
+    struct scatterlist *sl;
+    ushort i;
+    int cmd_index;
+
+    ha = HADATA(gdth_ctr_tab[hanum]);
+    cmdp = ha->pccb;
+    TRACE(("gdth_fill_cache_cmd() cmd 0x%x cmdsize %d hdrive %d\n",
+                 scp->cmnd[0],scp->cmd_len,hdrive));
+
+    if (ha->type==GDT_EISA && ha->cmd_cnt>0) 
+        return 0;
+
+    cmdp->Service = CACHESERVICE;
+    cmdp->RequestBuffer = scp;
+    /* search free command index */
+    if (!(cmd_index=gdth_get_cmd_index(hanum))) {
+        TRACE(("GDT: No free command index found\n"));
+        return 0;
+    }
+    /* if it's the first command, set command semaphore */
+    if (ha->cmd_cnt == 0)
+        gdth_set_sema0(hanum);
+
+    /* fill command */
+    if (scp->cmnd[0]==ALLOW_MEDIUM_REMOVAL) {
+        if (scp->cmnd[4] & 1)                   /* prevent ? */
+            cmdp->OpCode      = GDT_MOUNT;
+        else if (scp->cmnd[3] & 1)              /* removable drive ? */
+            cmdp->OpCode      = GDT_UNMOUNT;
+        else
+            cmdp->OpCode      = GDT_FLUSH;
+    } else {
+        if (scp->cmnd[0]==WRITE_6 || scp->cmnd[0]==WRITE_10) {
+            if (gdth_write_through)
+                cmdp->OpCode  = GDT_WRITE_THR;
+            else
+                cmdp->OpCode  = GDT_WRITE;
+        } else {
+            cmdp->OpCode      = GDT_READ;
+        }
+    }
+
+    cmdp->BoardNode           = LOCALBOARD;
+    cmdp->u.cache.DeviceNo    = hdrive;
+
+    if (scp->cmnd[0]==ALLOW_MEDIUM_REMOVAL) {
+        cmdp->u.cache.BlockNo = 1;
+        cmdp->u.cache.sg_canz = 0;
+    } else {
+        if (scp->cmd_len != 6) {
+            cmdp->u.cache.BlockNo = ntohl(*(ulong*)&scp->cmnd[2]);
+            cmdp->u.cache.BlockCnt= (ulong)ntohs(*(ushort*)&scp->cmnd[7]);
+        } else {
+            cmdp->u.cache.BlockNo = ntohl(*(ulong*)&scp->cmnd[0]) & 0x001fffffUL;
+            cmdp->u.cache.BlockCnt= scp->cmnd[4]==0 ? 0x100 : scp->cmnd[4];
+        }
+
+        if (scp->use_sg) {
+            cmdp->u.cache.DestAddr= -1UL;
+            sl = (struct scatterlist *)scp->request_buffer;
+            for (i=0; i<scp->use_sg; ++i,++sl) {
+                cmdp->u.cache.sg_lst[i].sg_ptr = virt_to_bus(sl->address);
+                cmdp->u.cache.sg_lst[i].sg_len = (ulong)sl->length;
+            }
+            cmdp->u.cache.sg_canz = (ulong)i;
+
+#ifdef GDTH_STATISTICS
+            if (max_sg < (ulong)i) {
+                max_sg = (ulong)i;
+                TRACE3(("GDT: max_sg = %d\n",i));
+            }
+#endif
+            if (i<GDTH_MAXSG)
+                cmdp->u.cache.sg_lst[i].sg_len = 0;
+        } else {
+            if (ha->cache_feat & SCATTER_GATHER) {
+                cmdp->u.cache.DestAddr = -1UL;
+                cmdp->u.cache.sg_canz = 1;
+                cmdp->u.cache.sg_lst[0].sg_ptr = virt_to_bus(scp->request_buffer);
+                cmdp->u.cache.sg_lst[0].sg_len = scp->request_bufflen;
+                cmdp->u.cache.sg_lst[1].sg_len = 0;
+            } else {
+                cmdp->u.cache.DestAddr  = virt_to_bus(scp->request_buffer);
+                cmdp->u.cache.sg_canz= 0;
+            }
+        }
+    }
+    TRACE(("cache cmd: addr. %lx sganz %lx sgptr0 %lx sglen0 %lx\n",
+                  cmdp->u.cache.DestAddr,cmdp->u.cache.sg_canz,
+                  cmdp->u.cache.sg_lst[0].sg_ptr,
+                  cmdp->u.cache.sg_lst[0].sg_len));
+    TRACE(("cache cmd: cmd %d blockno. %ld, blockcnt %ld\n",
+                  cmdp->OpCode,cmdp->u.cache.BlockNo,cmdp->u.cache.BlockCnt));
+
+    /* evaluate command size, check space */
+    ha->cmd_len = GDTOFFSOF(gdth_cmd_str,u.cache.sg_lst) +
+        (ushort)cmdp->u.cache.sg_canz * sizeof(gdth_sg_str);
+    if (ha->cmd_len & 3)
+        ha->cmd_len += (4 - (ha->cmd_len & 3));
+
+    if (ha->cmd_cnt > 0) {
+        if ((ha->cmd_offs_dpmem + ha->cmd_len + DPMEM_COMMAND_OFFSET) >
+            ha->ic_all_size) {
+            TRACE2(("gdth_fill_cache() DPMEM overflow\n"));
+            gdth_cmd_tab[cmd_index-2][hanum].cmnd = UNUSED_CMND;
+            return 0;
+        }
+    }
+
+    /* copy command */
+    gdth_copy_command(hanum);
+    return cmd_index;
+}
+
+static int gdth_fill_raw_cmd(int hanum,Scsi_Cmnd *scp,unchar b)
+{
+    register gdth_ha_str *ha;
+    register gdth_cmd_str *cmdp;
+    struct scatterlist *sl;
+    ushort i;
+    int cmd_index;
+    unchar t,l;
+
+    ha = HADATA(gdth_ctr_tab[hanum]);
+    t = scp->target;
+    l = scp->lun;
+    cmdp = ha->pccb;
+    TRACE(("gdth_fill_raw_cmd() cmd 0x%x bus %d ID %d LUN %d\n",
+                 scp->cmnd[0],b,t,l));
+
+    if (ha->type==GDT_EISA && ha->cmd_cnt>0) 
+        return 0;
+
+    cmdp->Service = SCSIRAWSERVICE;
+    cmdp->RequestBuffer = scp;
+    /* search free command index */
+    if (!(cmd_index=gdth_get_cmd_index(hanum))) {
+        TRACE(("GDT: No free command index found\n"));
+        return 0;
+    }
+    /* if it's the first command, set command semaphore */
+    if (ha->cmd_cnt == 0)
+        gdth_set_sema0(hanum);
+
+    /* fill command */  
+    cmdp->OpCode           = GDT_WRITE;         /* always */
+    cmdp->BoardNode        = LOCALBOARD;
+    cmdp->u.raw.reserved   = 0;
+    cmdp->u.raw.mdisc_time = 0;
+    cmdp->u.raw.mcon_time  = 0;
+    cmdp->u.raw.clen       = scp->cmd_len;
+    cmdp->u.raw.target     = t;
+    cmdp->u.raw.lun        = l;
+    cmdp->u.raw.bus        = b;
+    cmdp->u.raw.priority   = 0;
+    cmdp->u.raw.link_p     = NULL;
+    cmdp->u.raw.sdlen      = scp->request_bufflen;
+    cmdp->u.raw.sense_len  = 16;
+    cmdp->u.raw.sense_data = virt_to_bus(scp->sense_buffer);
+    cmdp->u.raw.direction  = 
+        gdth_direction_tab[scp->cmnd[0]]==DOU ? DATA_OUT : DATA_IN;
+    memcpy(cmdp->u.raw.cmd,scp->cmnd,12);
+
+    if (scp->use_sg) {
+        cmdp->u.raw.sdata  = -1UL;
+        sl = (struct scatterlist *)scp->request_buffer;
+        for (i=0; i<scp->use_sg; ++i,++sl) {
+            cmdp->u.raw.sg_lst[i].sg_ptr = virt_to_bus(sl->address);
+            cmdp->u.raw.sg_lst[i].sg_len = (ulong)sl->length;
+        }
+        cmdp->u.raw.sg_ranz = (ulong)i;
+
+#ifdef GDTH_STATISTICS
+        if (max_sg < (ulong)i) {
+            max_sg = (ulong)i;
+            TRACE3(("GDT: max_sg = %d\n",i));
+        }
+#endif
+        if (i<GDTH_MAXSG)
+            cmdp->u.raw.sg_lst[i].sg_len = 0;
+    } else {
+        if (ha->raw_feat & SCATTER_GATHER) {
+            cmdp->u.raw.sdata  = -1UL;
+            cmdp->u.raw.sg_ranz= 1;
+            cmdp->u.raw.sg_lst[0].sg_ptr = virt_to_bus(scp->request_buffer);
+            cmdp->u.raw.sg_lst[0].sg_len = scp->request_bufflen;
+            cmdp->u.raw.sg_lst[1].sg_len = 0;
+        } else {
+            cmdp->u.raw.sdata  = virt_to_bus(scp->request_buffer);
+            cmdp->u.raw.sg_ranz= 0;
+        }
+    }
+    TRACE(("raw cmd: addr. %lx sganz %lx sgptr0 %lx sglen0 %lx\n",
+                  cmdp->u.raw.sdata,cmdp->u.raw.sg_ranz,
+                  cmdp->u.raw.sg_lst[0].sg_ptr,
+                  cmdp->u.raw.sg_lst[0].sg_len));
+
+    /* evaluate command size, check space */
+    ha->cmd_len = GDTOFFSOF(gdth_cmd_str,u.raw.sg_lst) +
+        (ushort)cmdp->u.raw.sg_ranz * sizeof(gdth_sg_str);
+    if (ha->cmd_len & 3)
+        ha->cmd_len += (4 - (ha->cmd_len & 3));
+
+    if (ha->cmd_cnt > 0) {
+        if ((ha->cmd_offs_dpmem + ha->cmd_len + DPMEM_COMMAND_OFFSET) >
+            ha->ic_all_size) {
+            TRACE2(("gdth_fill_raw() DPMEM overflow\n"));
+            gdth_cmd_tab[cmd_index-2][hanum].cmnd = UNUSED_CMND;
+            return 0;
+        }
+    }
+
+    /* copy command */
+    gdth_copy_command(hanum);
+    return cmd_index;
+}
+
+static int gdth_special_cmd(int hanum,Scsi_Cmnd *scp,unchar b)
+{
+    register gdth_ha_str *ha;
+    register gdth_cmd_str *cmdp;
+    int cmd_index;
+
+    ha  = HADATA(gdth_ctr_tab[hanum]);
+    cmdp= ha->pccb;
+    TRACE2(("gdth_special_cmd(): "));
+
+    if (ha->type==GDT_EISA && ha->cmd_cnt>0) 
+        return 0;
+
+    memcpy( cmdp, scp->request_buffer, sizeof(gdth_cmd_str));
+    cmdp->RequestBuffer = scp;
+
+    /* search free command index */
+    if (!(cmd_index=gdth_get_cmd_index(hanum))) {
+        TRACE(("GDT: No free command index found\n"));
+        return 0;
+    }
+
+    /* if it's the first command, set command semaphore */
+    if (ha->cmd_cnt == 0)
+       gdth_set_sema0(hanum);
+
+    /* evaluate command size, check space */
+    if (cmdp->OpCode == GDT_IOCTL) {
+        TRACE2(("IOCTL\n"));
+        ha->cmd_len = GDTOFFSOF(gdth_cmd_str,u.ioctl.p_param) + sizeof(ulong);
+    } else if (cmdp->Service == CACHESERVICE) {
+        TRACE2(("cache command %d\n",cmdp->OpCode));
+        ha->cmd_len = GDTOFFSOF(gdth_cmd_str,u.cache.sg_lst) + sizeof(gdth_sg_str);
+    } else if (cmdp->Service == SCSIRAWSERVICE) {
+        TRACE2(("raw command %d/%d\n",cmdp->OpCode,cmdp->u.raw.cmd[0]));
+        ha->cmd_len = GDTOFFSOF(gdth_cmd_str,u.raw.sg_lst) + sizeof(gdth_sg_str);
+    }
+
+    if (ha->cmd_len & 3)
+        ha->cmd_len += (4 - (ha->cmd_len & 3));
+
+    if (ha->cmd_cnt > 0) {
+        if ((ha->cmd_offs_dpmem + ha->cmd_len + DPMEM_COMMAND_OFFSET) >
+            ha->ic_all_size) {
+            TRACE2(("gdth_special_cmd() DPMEM overflow\n"));
+            gdth_cmd_tab[cmd_index-2][hanum].cmnd = UNUSED_CMND;
+            return 0;
+        }
+    }
+
+    /* copy command */
+    gdth_copy_command(hanum);
+    return cmd_index;
+}    
+
+
+/* Controller event handling functions */
+static gdth_evt_str *gdth_store_event(ushort source, ushort idx,
+                                      gdth_evt_data *evt)
+{
+    gdth_evt_str *e;
+    ulong flags;
+    struct timeval tv;
+
+    TRACE2(("gdth_store_event() source %d idx %d\n", source, idx));
+    if (source == 0)                        /* no source -> no event */
+        return 0;
+
+    save_flags(flags);
+    cli();
+    if (ebuffer[elastidx].event_source == source &&
+        ebuffer[elastidx].event_idx == idx &&
+        !memcmp((char *)&ebuffer[elastidx].event_data.eu,
+            (char *)&evt->eu, evt->size)) {
+        e = &ebuffer[elastidx];
+        do_gettimeofday(&tv);
+        e->last_stamp = tv.tv_sec;
+        ++e->same_count;
+    } else {
+        if (ebuffer[elastidx].event_source != 0) {  /* entry not free ? */
+            ++elastidx;
+            if (elastidx == MAX_EVENTS)
+                elastidx = 0;
+            if (elastidx == eoldidx) {              /* reached mark ? */
+                ++eoldidx;
+                if (eoldidx == MAX_EVENTS)
+                    eoldidx = 0;
+            }
+        }
+        e = &ebuffer[elastidx];
+        e->event_source = source;
+        e->event_idx = idx;
+        do_gettimeofday(&tv);
+        e->first_stamp = e->last_stamp = tv.tv_sec;
+        e->same_count = 1;
+        e->event_data = *evt;
+    }
+    restore_flags(flags);
+    return e;
+}
+
+static int gdth_read_event(int handle, gdth_evt_str *estr)
+{
+    gdth_evt_str *e;
+    int eindex;
+    ulong flags;
+
+    TRACE2(("gdth_read_event() handle %d\n", handle));
+    save_flags(flags);
+    cli();
+    if (handle == -1)
+        eindex = eoldidx;
+    else
+        eindex = handle;
+    estr->event_source = 0;
+
+    if (eindex >= MAX_EVENTS) {
+        restore_flags(flags);
+        return eindex;
+    }
+    e = &ebuffer[eindex];
+    if (e->event_source != 0) {
+        if (eindex != elastidx) {
+            if (++eindex == MAX_EVENTS)
+                eindex = 0;
+        } else {
+            eindex = -1;
+        }
+        memcpy(estr, e, sizeof(gdth_evt_str));
+    }
+    restore_flags(flags);
+    return eindex;
+}
+
+static void gdth_readapp_event(unchar application, gdth_evt_str *estr)
+{
+    gdth_evt_str *e;
+    int eindex;
+    ulong flags;
+    unchar found = FALSE;
+
+    TRACE2(("gdth_readapp_event() app. %d\n", application));
+    save_flags(flags);
+    cli();
+    eindex = eoldidx;
+    for (;;) {
+        e = &ebuffer[eindex];
+        if (e->event_source == 0)
+            break;
+        if ((e->application & application) == 0) {
+            e->application |= application;
+            found = TRUE;
+            break;
+        }
+        if (eindex == elastidx)
+            break;
+        if (++eindex == MAX_EVENTS)
+            eindex = 0;
+    }
+    if (found)
+        memcpy(estr, e, sizeof(gdth_evt_str));
+    else
+        estr->event_source = 0;
+    restore_flags(flags);
+}
+
+static void gdth_clear_events()
+{
+    ulong flags;
+
+    TRACE(("gdth_clear_events()"));
+    save_flags(flags);
+    cli();
+
+    eoldidx = elastidx = 0;
+    ebuffer[0].event_source = 0;
+    restore_flags(flags);
+}
+
+
+/* SCSI interface functions */
+
+#if LINUX_VERSION_CODE >= 0x010346
+static void gdth_interrupt(int irq,void *dev_id,struct pt_regs *regs)
+#else
+static void gdth_interrupt(int irq,struct pt_regs *regs)
+#endif
+{
+    register gdth_ha_str *ha;
+    gdt6m_dpram_str *dp6m_ptr;
+    gdt6_dpram_str *dp6_ptr;
+    gdt2_dpram_str *dp2_ptr;
+    Scsi_Cmnd *scp;
+    int hanum;
+    unchar IStatus;
+    ushort CmdStatus, Service = 0;
+    ulong InfoBytes, InfoBytes2 = 0;
+    gdth_evt_data dvr;
+
+    TRACE(("gdth_interrupt() IRQ %d\n",irq));
+
+    /* if polling and not from gdth_wait() -> return */
+    if (gdth_polling) {
+        if (!gdth_from_wait) {
+            return;
+        }
+    }
+
+    wait_index = 0;
+
+    /* search controller */
+    if ((hanum = gdth_get_status(&IStatus,irq)) == -1) {
+        /*
+        TRACE2(("gdth_interrupt(): Spurious interrupt received\n"));
+        */
+        return;
+    }
+
+#ifdef GDTH_STATISTICS
+    ++act_ints;
+#endif
+    
+    ha = HADATA(gdth_ctr_tab[hanum]);
+    if (ha->type == GDT_EISA) {
+        if (IStatus & 0x80) {                   /* error flag */
+            IStatus &= ~0x80;
+            CmdStatus = inw(ha->bmic + MAILBOXREG+8);
+            TRACE2(("gdth_interrupt() error %d/%d\n",IStatus,CmdStatus));
+            if (IStatus == ASYNCINDEX) {        /* async. event ? */
+                Service = inw(ha->bmic + MAILBOXREG+10);
+                InfoBytes2 = inl(ha->bmic + MAILBOXREG+4);
+            }
+        } else                                  /* no error */
+            CmdStatus = S_OK;
+        InfoBytes = inl(ha->bmic + MAILBOXREG+12);
+	if (gdth_polling)			/* init. -> more info */
+	    InfoBytes2 = inl(ha->bmic + MAILBOXREG+4);
+        outb(0xff, ha->bmic + EDOORREG);    /* acknowledge interrupt */
+        outb(0x00, ha->bmic + SEMA1REG);    /* reset status semaphore */
+    } else if (ha->type == GDT_ISA) {
+        dp2_ptr = (gdt2_dpram_str *)ha->brd;
+        if (IStatus & 0x80) {                   /* error flag */
+            IStatus &= ~0x80;
+            CmdStatus = readw(&dp2_ptr->u.ic.Status);
+            TRACE2(("gdth_interrupt() error %d/%d\n",IStatus,CmdStatus));
+            if (IStatus == ASYNCINDEX) {        /* async. event ? */
+                Service = readw(&dp2_ptr->u.ic.Service);
+                InfoBytes2 = readl(&dp2_ptr->u.ic.Info[1]);
+            }
+        } else                                  /* no error */
+            CmdStatus = S_OK;
+        InfoBytes = readl(&dp2_ptr->u.ic.Info[0]);
+	if (gdth_polling)			/* init. -> more info */
+	    InfoBytes2 = readl(&dp2_ptr->u.ic.Info[1]);
+        writeb(0xff, &dp2_ptr->io.irqdel);              /* acknowledge interrupt */
+        writeb(0, &dp2_ptr->u.ic.Cmd_Index);            /* reset command index */
+        writeb(0, &dp2_ptr->io.Sema1);                 /* reset status semaphore */
+    } else if (ha->type == GDT_PCI) {
+        dp6_ptr = (gdt6_dpram_str *)ha->brd;
+        if (IStatus & 0x80) {                   /* error flag */
+            IStatus &= ~0x80;
+            CmdStatus = readw(&dp6_ptr->u.ic.Status);
+            TRACE2(("gdth_interrupt() error %d/%d\n",IStatus,CmdStatus));
+            if (IStatus == ASYNCINDEX) {        /* async. event ? */
+                Service = readw(&dp6_ptr->u.ic.Service);
+                InfoBytes2 = readl(&dp6_ptr->u.ic.Info[1]);
+            }
+        } else                                  /* no error */
+            CmdStatus = S_OK;
+        InfoBytes = readl(&dp6_ptr->u.ic.Info[0]);
+	if (gdth_polling)			/* init. -> more info */
+	    InfoBytes2 = readl(&dp6_ptr->u.ic.Info[1]);
+        writeb(0xff, &dp6_ptr->io.irqdel);              /* acknowledge interrupt */
+        writeb(0, &dp6_ptr->u.ic.Cmd_Index);            /* reset command index */
+        writeb(0, &dp6_ptr->io.Sema1);                 /* reset status semaphore */
+    } else if (ha->type == GDT_PCINEW) {
+        if (IStatus & 0x80) {                   /* error flag */
+            IStatus &= ~0x80;
+            CmdStatus = inw(PTR2USHORT(&ha->plx->status));
+            TRACE2(("gdth_interrupt() error %d/%d\n",IStatus,CmdStatus));
+            if (IStatus == ASYNCINDEX) {        /* async. event ? */
+                Service = inw(PTR2USHORT(&ha->plx->service));
+                InfoBytes2 = inl(PTR2USHORT(&ha->plx->info[1]));
+            }
+        } else
+            CmdStatus = S_OK;
+
+        InfoBytes = inl(PTR2USHORT(&ha->plx->info[0]));
+	if (gdth_polling)			/* init. -> more info */
+	    InfoBytes2 = inl(PTR2USHORT(&ha->plx->info[1]));
+        outb(0xff, PTR2USHORT(&ha->plx->edoor_reg)); 
+        outb(0x00, PTR2USHORT(&ha->plx->sema1_reg)); 
+    } else if (ha->type == GDT_PCIMPR) {
+        dp6m_ptr = (gdt6m_dpram_str *)ha->brd;
+        if (IStatus & 0x80) {                   /* error flag */
+            IStatus &= ~0x80;
+            CmdStatus = readw(&dp6m_ptr->i960r.status);
+            TRACE2(("gdth_interrupt() error %d/%d\n",IStatus,CmdStatus));
+            if (IStatus == ASYNCINDEX) {        /* async. event ? */
+                Service = readw(&dp6m_ptr->i960r.service);
+                InfoBytes2 = readl(&dp6m_ptr->i960r.info[1]);
+            }
+        } else                                  /* no error */
+            CmdStatus = S_OK;
+        InfoBytes = readl(&dp6m_ptr->i960r.info[0]);
+	if (gdth_polling)			/* init. -> more info */
+	    InfoBytes2 = readl(&dp6m_ptr->i960r.info[1]);
+        writeb(0xff, &dp6m_ptr->i960r.edoor_reg);
+        writeb(0, &dp6m_ptr->i960r.sema1_reg);
+    } else {
+        TRACE2(("gdth_interrupt() unknown controller type\n"));
+        return;
+    }
+
+    TRACE(("gdth_interrupt() index %d stat %d info %ld\n",
+                 IStatus,CmdStatus,InfoBytes));
+    ha->status = CmdStatus;
+    ha->info   = InfoBytes;
+    ha->info2  = InfoBytes2;
+
+    if (gdth_from_wait) {
+        wait_hanum = hanum;
+        wait_index = (int)IStatus;
+    }
+
+    if (IStatus == ASYNCINDEX) {
+        TRACE2(("gdth_interrupt() async. event\n"));
+        gdth_async_event(hanum,Service);
+    } else {
+        if (IStatus == SPEZINDEX) {
+            TRACE2(("Service unknown or not initialized !\n"));
+            dvr.size = sizeof(dvr.eu.driver);
+            dvr.eu.driver.ionode = hanum;
+            gdth_store_event(ES_DRIVER, 4, &dvr);
+            return;
+        }
+        scp     = gdth_cmd_tab[IStatus-2][hanum].cmnd;
+        Service = gdth_cmd_tab[IStatus-2][hanum].service;
+        gdth_cmd_tab[IStatus-2][hanum].cmnd = UNUSED_CMND;
+        if (scp == UNUSED_CMND) {
+            TRACE2(("gdth_interrupt() index to unused command (%d)\n",IStatus));
+            dvr.size = sizeof(dvr.eu.driver);
+            dvr.eu.driver.ionode = hanum;
+            dvr.eu.driver.index = IStatus;
+            gdth_store_event(ES_DRIVER, 1, &dvr);
+            return;
+        }
+        if (scp == INTERNAL_CMND) {
+            TRACE(("gdth_interrupt() answer to internal command\n"));
+            return;
+        }
+        TRACE(("gdth_interrupt() sync. status\n"));
+        gdth_sync_event(hanum,Service,IStatus,scp);
+    }
+    gdth_next(hanum);
+}
+
+static int gdth_sync_event(int hanum,int service,unchar index,Scsi_Cmnd *scp)
+{
+    register gdth_ha_str *ha;
+    gdth_msg_str *msg;
+    gdth_cmd_str *cmdp;
+    char c='\r';
+    ushort i;
+    gdth_evt_data dvr;
+
+    ha   = HADATA(gdth_ctr_tab[hanum]);
+    cmdp = ha->pccb;
+    TRACE(("gdth_sync_event() scp %lx serv %d status %d\n",
+                 (ulong)scp,service,ha->status));
+
+    if (service == SCREENSERVICE) {
+        msg  = (gdth_msg_str *)ha->pscratch;
+        TRACE(("len: %ld, answer: %d, ext: %d, alen: %ld\n",
+                     msg->msg_len,msg->msg_answer,msg->msg_ext,msg->msg_alen));
+        if (msg->msg_len)
+            if (!(msg->msg_answer && msg->msg_ext)) {
+                msg->msg_text[msg->msg_len] = '\0';
+                printk("%s",msg->msg_text);
+            }
+
+        if (msg->msg_ext && !msg->msg_answer) {
+            while (gdth_test_busy(hanum))
+                udelay(1);
+            cmdp->Service       = SCREENSERVICE;
+            cmdp->RequestBuffer = SCREEN_CMND;
+            gdth_get_cmd_index(hanum);
+            gdth_set_sema0(hanum);
+            cmdp->OpCode        = GDT_READ;
+            cmdp->BoardNode     = LOCALBOARD;
+            cmdp->u.screen.reserved  = 0;
+            cmdp->u.screen.msg_handle= msg->msg_handle;
+            cmdp->u.screen.msg_addr  = (ulong)msg;
+            ha->cmd_offs_dpmem = 0;
+            ha->cmd_len = GDTOFFSOF(gdth_cmd_str,u.screen.msg_addr) 
+                + sizeof(ulong);
+            ha->cmd_cnt = 0;
+            gdth_copy_command(hanum);
+            gdth_release_event(hanum);
+            return 1;
+        }
+
+        if (msg->msg_answer && msg->msg_alen) {
+            for (i=0; i<msg->msg_alen && i<MSGLEN; ++i) {
+                /* getchar() ?? */           
+                /* .. */
+                if (c == '\r')
+                    break;
+                msg->msg_text[i] = c; 
+            }
+            msg->msg_alen -= i;
+            if (c!='\r' && msg->msg_alen!=0) {
+                msg->msg_answer = 1;
+                msg->msg_ext    = 1;
+            } else {
+                msg->msg_ext    = 0;
+                msg->msg_answer = 0;
+            }
+            msg->msg_len = i;
+            while (gdth_test_busy(hanum))
+                udelay(1);
+            cmdp->Service       = SCREENSERVICE;
+            cmdp->RequestBuffer = SCREEN_CMND;
+            gdth_get_cmd_index(hanum);
+            gdth_set_sema0(hanum);
+            cmdp->OpCode        = GDT_WRITE;
+            cmdp->BoardNode     = LOCALBOARD;
+            cmdp->u.screen.reserved  = 0;
+            cmdp->u.screen.msg_handle= msg->msg_handle;
+            cmdp->u.screen.msg_addr  = (ulong)msg;
+            ha->cmd_offs_dpmem = 0;
+            ha->cmd_len = GDTOFFSOF(gdth_cmd_str,u.screen.msg_addr) 
+                + sizeof(ulong);
+            ha->cmd_cnt = 0;
+            gdth_copy_command(hanum);
+            gdth_release_event(hanum);
+            return 1;
+        }
+        printk("\n");
+
+    } else {
+        scp->SCp.Message = (int)ha->status;
+        /* cache or raw service */
+        if (ha->status == S_OK) {
+            scp->result = DID_OK << 16;
+        } else if (ha->status == S_BSY) {
+            TRACE2(("Controller busy -> retry !\n"));
+            gdth_putq(hanum,scp,DEFAULT_PRI);
+            return 1;
+        } else {
+            if (service == CACHESERVICE) {
+                memset((char*)scp->sense_buffer,0,16);
+                scp->sense_buffer[0] = 0x70;
+                scp->sense_buffer[2] = NOT_READY;
+                scp->result = (DID_OK << 16) | (CHECK_CONDITION << 1);
+
+                if (scp->done != gdth_scsi_done) {
+                    dvr.size = sizeof(dvr.eu.sync);
+                    dvr.eu.sync.ionode  = hanum;
+                    dvr.eu.sync.service = service;
+                    dvr.eu.sync.status  = ha->status;
+                    dvr.eu.sync.info    = ha->info;
+                    dvr.eu.sync.hostdrive =
+#if LINUX_VERSION_CODE >= 0x020000
+                        ha->id[scp->channel][scp->target].hostdrive;
+#else
+                        ha->id[NUMDATA(scp->host)->busnum][scp->target].hostdrive;
+#endif
+                    if (ha->status >= 0x8000)
+                        gdth_store_event(ES_SYNC, 0, &dvr);
+                    else
+                        gdth_store_event(ES_SYNC, service, &dvr);
+                }
+            } else {
+                if (ha->status!=S_RAW_SCSI || ha->status==S_RAW_ILL) {
+                    scp->result = DID_BAD_TARGET << 16;
+                } else {
+                    scp->result = (DID_OK << 16) | ha->info;
+                }
+            }
+        }
+        scp->SCp.have_data_in++;
+        scp->scsi_done(scp);
+    }
+
+    return 1;
+}
+
+static char *async_cache_tab[] = {
+/* 0*/  "\011\000\002\002\002\004\002\006\004"
+        "GDT HA %u, service %u, async. status %u/%lu unknown",
+/* 1*/  "\011\000\002\002\002\004\002\006\004"
+        "GDT HA %u, service %u, async. status %u/%lu unknown",
+/* 2*/  "\005\000\002\006\004"
+        "GDT HA %u, Host Drive %lu not ready",
+/* 3*/  "\005\000\002\006\004"
+        "GDT HA %u, Host Drive %lu: REASSIGN not successful and/or data error on reassigned blocks. Drive may crash in the future and should be replaced",
+/* 4*/  "\005\000\002\006\004"
+        "GDT HA %u, mirror update on Host Drive %lu failed",
+/* 5*/  "\005\000\002\006\004"
+        "GDT HA %u, Mirror Drive %lu failed",
+/* 6*/  "\005\000\002\006\004"
+        "GDT HA %u, Mirror Drive %lu: REASSIGN not successful and/or data error on reassigned blocks. Drive may crash in the future and should be replaced",
+/* 7*/  "\005\000\002\006\004"
+        "GDT HA %u, Host Drive %lu write protected",
+/* 8*/  "\005\000\002\006\004"
+        "GDT HA %u, media changed in Host Drive %lu",
+/* 9*/  "\005\000\002\006\004"
+        "GDT HA %u, Host Drive %lu is offline",
+/*10*/  "\005\000\002\006\004"
+        "GDT HA %u, media change of Mirror Drive %lu",
+/*11*/  "\005\000\002\006\004"
+        "GDT HA %u, Mirror Drive %lu is write protected",
+/*12*/  "\005\000\002\006\004"
+        "GDT HA %u, general error on Host Drive %lu. Please check the devices of this drive!",
+/*13*/  "\007\000\002\006\002\010\002"
+        "GDT HA %u, Array Drive %u: Cache Drive %u failed",
+/*14*/  "\005\000\002\006\002"
+        "GDT HA %u, Array Drive %u: FAIL state entered",
+/*15*/  "\005\000\002\006\002"
+        "GDT HA %u, Array Drive %u: error",
+/*16*/  "\007\000\002\006\002\010\002"
+        "GDT HA %u, Array Drive %u: failed drive replaced by Cache Drive %u",
+/*17*/  "\005\000\002\006\002"
+        "GDT HA %u, Array Drive %u: parity build failed",
+/*18*/  "\005\000\002\006\002"
+        "GDT HA %u, Array Drive %u: drive rebuild failed",
+/*19*/  "\007\000\002\010\002"
+        "GDT HA %u, Test of Hot Fix %u failed",
+/*20*/  "\005\000\002\006\002"
+        "GDT HA %u, Array Drive %u: drive build finished successfully",
+/*21*/  "\005\000\002\006\002"
+        "GDT HA %u, Array Drive %u: drive rebuild finished successfully",
+/*22*/  "\007\000\002\006\002\010\002"
+        "GDT HA %u, Array Drive %u: Hot Fix %u activated",
+/*23*/  "\005\000\002\006\002"
+        "GDT HA %u, Host Drive %u: processing of i/o aborted due to serious drive error",
+/*24*/  "\005\000\002\010\002"
+        "GDT HA %u, mirror update on Cache Drive %u completed",
+/*25*/  "\005\000\002\010\002"
+        "GDT HA %u, mirror update on Cache Drive %lu failed",
+/*26*/  "\005\000\002\006\002"
+        "GDT HA %u, Array Drive %u: drive rebuild started",
+/*27*/  "\005\000\002\012\001"
+        "GDT HA %u, Fault bus %u: SHELF OK detected",
+/*28*/  "\005\000\002\012\001"
+        "GDT HA %u, Fault bus %u: SHELF not OK detected",
+/*29*/  "\007\000\002\012\001\013\001"
+        "GDT HA %u, Fault bus %u, ID %u: Auto Hot Plug started",
+/*30*/  "\007\000\002\012\001\013\001"
+        "GDT HA %u, Fault bus %u, ID %u: new disk detected",
+/*31*/  "\007\000\002\012\001\013\001"
+        "GDT HA %u, Fault bus %u, ID %u: old disk detected",
+/*32*/  "\007\000\002\012\001\013\001"
+        "GDT HA %u, Fault bus %u, ID %u: plugging an active disk is illegal",
+/*33*/  "\007\000\002\012\001\013\001"
+        "GDT HA %u, Fault bus %u, ID %u: illegal device detected",
+/*34*/  "\011\000\002\012\001\013\001\006\004"
+        "GDT HA %u, Fault bus %u, ID %u: insufficient disk capacity (%lu MB required)",
+/*35*/  "\007\000\002\012\001\013\001"
+        "GDT HA %u, Fault bus %u, ID %u: disk write protected",
+/*36*/  "\007\000\002\012\001\013\001"
+        "GDT HA %u, Fault bus %u, ID %u: disk not available",
+/*37*/  "\007\000\002\012\001\006\004"
+        "GDT HA %u, Fault bus %u: swap detected (%lu)",
+/*38*/  "\007\000\002\012\001\013\001"
+        "GDT HA %u, Fault bus %u, ID %u: Auto Hot Plug finished successfully",
+/*39*/  "\007\000\002\012\001\013\001"
+        "GDT HA %u, Fault bus %u, ID %u: Auto Hot Plug aborted due to user Hot Plug",
+/*40*/  "\007\000\002\012\001\013\001"
+        "GDT HA %u, Fault bus %u, ID %u: Auto Hot Plug aborted",
+/*41*/  "\007\000\002\012\001\013\001"
+        "GDT HA %u, Fault bus %u, ID %u: Auto Hot Plug for Hot Fix started",
+/*42*/  "\005\000\002\006\002"
+        "GDT HA %u, Array Drive %u: drive build started",
+/*43*/  "\003\000\002"
+        "GDT HA %u, DRAM parity error detected",
+/*44*/  "\005\000\002\006\002"
+        "GDT HA %u, Mirror Drive %u: update started",
+/*45*/  "\007\000\002\006\002\010\002"
+        "GDT HA %u, Mirror Drive %u: Hot Fix %u activated",
+/*46*/  "\005\000\002\006\002"
+        "GDT HA %u, Array Drive %u: no matching Pool Hot Fix Drive available",
+/*47*/  "\005\000\002\006\002"
+        "GDT HA %u, Array Drive %u: Pool Hot Fix Drive available",
+/*48*/  "\005\000\002\006\002"
+        "GDT HA %u, Mirror Drive %u: no matching Pool Hot Fix Drive available",
+/*49*/  "\005\000\002\006\002"
+        "GDT HA %u, Mirror Drive %u: Pool Hot Fix Drive available",
+/*50*/  "\007\000\002\012\001\013\001"
+        "GDT HA %u, SCSI bus %u, ID %u: IGNORE_WIDE_RESIDUE message received",
+/*51*/  "\005\000\002\006\002"
+        "GDT HA %u, Array Drive %u: expand started",
+/*52*/  "\005\000\002\006\002"
+        "GDT HA %u, Array Drive %u: expand finished successfully",
+/*53*/  "\005\000\002\006\002"
+        "GDT HA %u, Array Drive %u: expand failed",
+/*54*/  "\003\000\002"
+        "GDT HA %u, CPU temperature critical",
+/*55*/  "\003\000\002"
+        "GDT HA %u, CPU temperature OK",
+/*56*/  "\005\000\002\006\004"
+        "GDT HA %u, Host drive %lu created",
+/*57*/  "\005\000\002\006\002"
+        "GDT HA %u, Array Drive %u: expand restarted",
+/*58*/  "\005\000\002\006\002"
+        "GDT HA %u, Array Drive %u: expand stopped",
+};
+
+
+static int gdth_async_event(int hanum,int service)
+{
+    gdth_stackframe stack;
+    gdth_evt_data dvr;
+    char *f = NULL;
+    int i,j;
+    gdth_ha_str *ha;
+    gdth_msg_str *msg;
+    gdth_cmd_str *cmdp;
+    int cmd_index;
+
+    ha  = HADATA(gdth_ctr_tab[hanum]);
+    cmdp= ha->pccb;
+    msg = (gdth_msg_str *)ha->pscratch;
+    TRACE2(("gdth_async_event() ha %d serv %d\n",
+                 hanum,service));
+
+    if (service == SCREENSERVICE) {
+        if (ha->status == MSG_REQUEST) {
+            while (gdth_test_busy(hanum))
+                udelay(1);
+            cmdp->Service       = SCREENSERVICE;
+            cmdp->RequestBuffer = SCREEN_CMND;
+            cmd_index = gdth_get_cmd_index(hanum);
+            gdth_set_sema0(hanum);
+            cmdp->OpCode        = GDT_READ;
+            cmdp->BoardNode     = LOCALBOARD;
+            cmdp->u.screen.reserved  = 0;
+            cmdp->u.screen.msg_handle= MSG_INV_HANDLE;
+            cmdp->u.screen.msg_addr  = (ulong)msg;
+            ha->cmd_offs_dpmem = 0;
+            ha->cmd_len = GDTOFFSOF(gdth_cmd_str,u.screen.msg_addr) 
+                + sizeof(ulong);
+            ha->cmd_cnt = 0;
+            gdth_copy_command(hanum);
+            if (ha->type == GDT_EISA)
+                printk("[EISA slot %d] ",(ushort)ha->brd_phys);
+            else if (ha->type == GDT_ISA)
+                printk("[DPMEM 0x%4X] ",(ushort)ha->brd_phys);
+            else 
+                printk("[PCI %d/%d] ",(ushort)(ha->brd_phys>>8),
+                       (ushort)((ha->brd_phys>>3)&0x1f));
+            gdth_release_event(hanum);
+        }
+
+    } else {
+        dvr.size = sizeof(dvr.eu.async);
+        dvr.eu.async.ionode   = hanum;
+        dvr.eu.async.service = service;
+        dvr.eu.async.status  = ha->status;
+        dvr.eu.async.info    = ha->info;
+        *(ulong *)dvr.eu.async.scsi_coord  = ha->info2;
+        gdth_store_event(ES_ASYNC, service, &dvr);
+
+        if (service==CACHESERVICE && INDEX_OK(ha->status,async_cache_tab)) {
+            TRACE2(("GDT: Async. event cache service, event no.: %d\n",
+                ha->status));
+        
+            f = async_cache_tab[ha->status];
+
+            /* i: parameter to push, j: stack element to fill */
+            for (j=0,i=1; i < f[0]; i+=2) {
+                switch (f[i+1]) {
+                  case 4:
+                    stack.b[j++] = *(ulong*)&dvr.eu.stream[(int)f[i]];
+                    break;
+                  case 2:
+                    stack.b[j++] = *(ushort*)&dvr.eu.stream[(int)f[i]];
+                    break;
+                  case 1:
+                    stack.b[j++] = *(unchar*)&dvr.eu.stream[(int)f[i]];
+                    break;
+                  default:
+                    break;
+                }
+            }
+
+            printk(&f[f[0]],stack); printk("\n");
+
+        } else {
+            printk("GDT: Unknown async. event service %d event no. %d\n",
+                service,ha->status);
+        }
+    }
+    return 1;
+}
+
+#ifdef GDTH_STATISTICS
+void gdth_timeout(void)
+{
+    ulong flags,i;
+    Scsi_Cmnd *nscp;
+    gdth_ha_str *ha;
+    int hanum = 0;
+
+    save_flags(flags);
+    cli();
+
+    for (act_stats=0,i=0; i<GDTH_MAXCMDS; ++i) 
+        if (gdth_cmd_tab[i][hanum].cmnd != UNUSED_CMND)
+            ++act_stats;
+
+    ha = HADATA(gdth_ctr_tab[hanum]);
+    for (act_rq=0,nscp=ha->req_first; nscp; nscp=(Scsi_Cmnd*)nscp->SCp.ptr)
+        ++act_rq;
+
+    TRACE2(("gdth_to(): ints %ld, ios %ld, act_stats %ld, act_rq %ld\n",
+            act_ints, act_ios, act_stats, act_rq));
+    act_ints = act_ios = 0;
+
+    timer_table[GDTH_TIMER].expires = jiffies + 30*HZ;
+    timer_active |= 1<<GDTH_TIMER;
+    sti();
+}
+#endif
+
+int gdth_detect(Scsi_Host_Template *shtp)
+{
+    struct Scsi_Host *shp;
+    gdth_ha_str *ha;
+    unsigned long flags;
+    ulong isa_bios;
+    ushort eisa_slot,device_id,index;
+    gdth_pci_str pcistr;
+    int i,j,hanum;
+    unchar b;
+    
+ 
+#ifdef DEBUG_GDTH
+    printk("GDT: This driver contains debugging information !! Trace level = %d\n",
+        DebugState);
+    printk("     Destination of debugging information: ");
+#ifdef __SERIAL__
+#ifdef __COM2__
+    printk("Serial port COM2\n");
+#else
+    printk("Serial port COM1\n");
+#endif
+#else
+    printk("Console\n");
+#endif
+    WAITSEC(3);
+#endif
+
+    TRACE(("gdth_detect()\n"));
+
+    if (disable_gdth_scan) {
+        printk("GDT: Controller driver disabled from command line !\n");
+        return 0;
+    }
+
+    /* initializations */
+    gdth_polling = TRUE; b = 0;
+    for (i=0; i<GDTH_MAXCMDS; ++i)
+        for (j=0; j<MAXHA; ++j)
+            gdth_cmd_tab[i][j].cmnd = UNUSED_CMND;
+    for (i=0; i<4; ++i)
+        for (j=0; j<MAXHA; ++j)
+            gdth_ioctl_tab[i][j] = NULL;
+    gdth_clear_events();
+
+    /* scanning for controllers, at first: ISA controller */
+    for (isa_bios=0xc8000UL; isa_bios<=0xd8000UL; isa_bios+=0x8000UL) {
+        if (gdth_search_isa(isa_bios)) {        /* controller found */
+            shp = scsi_register(shtp,sizeof(gdth_ext_str));
+            ha = HADATA(shp);
+            if (!gdth_init_isa(isa_bios,ha)) {
+                scsi_unregister(shp);
+                continue;
+            }
+            /* controller found and initialized */
+            printk("Configuring GDT-ISA HA at BIOS 0x%05lX IRQ %u DRQ %u\n",
+                   isa_bios,ha->irq,ha->drq);
+
+            save_flags(flags);
+            cli();
+#if LINUX_VERSION_CODE >= 0x010346 
+            if (request_irq(ha->irq,gdth_interrupt,SA_INTERRUPT,"gdth",NULL))
+#else
+            if (request_irq(ha->irq,gdth_interrupt,SA_INTERRUPT,"gdth")) 
+#endif
+            {
+                printk("GDT-ISA: Unable to allocate IRQ\n");
+                restore_flags(flags);
+                scsi_unregister(shp);
+                continue;
+            }
+            if (request_dma(ha->drq,"gdth")) {
+                printk("GDT-ISA: Unable to allocate DMA channel\n");
+#if LINUX_VERSION_CODE >= 0x010346 
+                free_irq(ha->irq,NULL);
+#else
+                free_irq(ha->irq);
+#endif
+                restore_flags(flags);
+                scsi_unregister(shp);
+                continue;
+            }
+            set_dma_mode(ha->drq,DMA_MODE_CASCADE);
+            enable_dma(ha->drq);
+            shp->unchecked_isa_dma = 1;
+            shp->irq = ha->irq;
+            shp->dma_channel = ha->drq;
+            for (i=0; i<MAXID; ++i) {
+                if (ha->id[0][i].type==SIOP_DTYP) {
+                    shp->this_id = i;
+                    break;
+                }
+            }
+            hanum = gdth_ctr_count;         
+            gdth_ctr_tab[gdth_ctr_count++] = shp;
+            gdth_ctr_vtab[gdth_ctr_vcount++] = shp;
+
+            NUMDATA(shp)->hanum = (ushort)hanum;
+            NUMDATA(shp)->busnum= 0;
+
+            ha->pccb = CMDDATA(shp);
+            ha->pscratch = DMADATA(shp);
+            ha->req_first = NULL;
+            for (i=0; i<MAXBUS; ++i) {
+                for (j=0; j<MAXID; ++j) {
+                    ha->id[i][j].type = EMPTY_DTYP;
+                    ha->id[i][j].lock = 0;
+                }
+            }
+            restore_flags(flags);
+
+            if (!gdth_search_drives(hanum)) {
+                printk("GDT-ISA: Error during device scan\n");
+                --gdth_ctr_count;
+		--gdth_ctr_vcount;
+                save_flags(flags);
+                cli();
+#if LINUX_VERSION_CODE >= 0x010346 
+                free_irq(ha->irq,NULL);
+#else
+                free_irq(ha->irq);
+#endif
+                restore_flags(flags);
+                scsi_unregister(shp);
+                continue;
+            }
+
+#if LINUX_VERSION_CODE >= 0x020000
+            shp->max_id      = 8;
+            shp->max_lun     = 8;
+            shp->max_channel = ha->bus_cnt - 1;
+#else
+            /* register addit. SCSI channels as virtual controllers */
+            for (b=1; b<ha->bus_cnt; ++b) {
+                shp = scsi_register(shtp,sizeof(gdth_num_str));
+                shp->unchecked_isa_dma = 1;
+                shp->irq = ha->irq;
+                shp->dma_channel = ha->drq;
+                for (i=0; i<MAXID; ++i) {
+                    if (ha->id[b][i].type==SIOP_DTYP) {
+                        shp->this_id = i;
+                        break;
+                    }
+                }
+                gdth_ctr_vtab[gdth_ctr_vcount++] = shp;
+                NUMDATA(shp)->hanum = (ushort)hanum;
+                NUMDATA(shp)->busnum = b;
+            }
+#endif
+
+            gdth_enable_int(hanum);
+        }
+    }
+
+    /* scanning for EISA controllers */
+    for (eisa_slot=0x1000; eisa_slot<=0x8000; eisa_slot+=0x1000) {
+        if (gdth_search_eisa(eisa_slot)) {      /* controller found */
+            shp = scsi_register(shtp,sizeof(gdth_ext_str));
+            ha = HADATA(shp);
+            if (!gdth_init_eisa(eisa_slot,ha)) {
+                scsi_unregister(shp);
+                continue;
+            }
+            /* controller found and initialized */
+            printk("Configuring GDT-EISA HA at Slot %d IRQ %u\n",
+                   eisa_slot>>12,ha->irq);
+
+            save_flags(flags);
+            cli();
+#if LINUX_VERSION_CODE >= 0x010346 
+            if (request_irq(ha->irq,gdth_interrupt,SA_INTERRUPT,"gdth",NULL))
+#else
+            if (request_irq(ha->irq,gdth_interrupt,SA_INTERRUPT,"gdth")) 
+#endif
+            {
+                printk("GDT-EISA: Unable to allocate IRQ\n");
+                restore_flags(flags);
+                scsi_unregister(shp);
+                continue;
+            }
+            shp->unchecked_isa_dma = 0;
+            shp->irq = ha->irq;
+            shp->dma_channel = 0xff;
+            for (i=0; i<MAXID; ++i) {
+                if (ha->id[0][i].type==SIOP_DTYP) {
+                    shp->this_id = i;
+                    break;
+                }
+            }
+            hanum = gdth_ctr_count;
+            gdth_ctr_tab[gdth_ctr_count++] = shp;
+            gdth_ctr_vtab[gdth_ctr_vcount++] = shp;
+
+            NUMDATA(shp)->hanum = (ushort)hanum;
+            NUMDATA(shp)->busnum= 0;
+            TRACE2(("EISA detect Bus 0: shp %lx hanum %d\n",
+                          (ulong)shp,NUMDATA(shp)->hanum));
+
+            ha->pccb = CMDDATA(shp);
+            ha->pscratch = DMADATA(shp);
+            ha->req_first = NULL;
+            for (i=0; i<MAXBUS; ++i) {
+                for (j=0; j<MAXID; ++j) {
+                    ha->id[i][j].type = EMPTY_DTYP;
+                    ha->id[i][j].lock = 0;
+                }
+            }
+            restore_flags(flags);
+
+            if (!gdth_search_drives(hanum)) {
+                printk("GDT-EISA: Error during device scan\n");
+                --gdth_ctr_count;
+		--gdth_ctr_vcount;
+                save_flags(flags);
+                cli();
+#if LINUX_VERSION_CODE >= 0x010346 
+                free_irq(ha->irq,NULL);
+#else
+                free_irq(ha->irq);
+#endif
+                restore_flags(flags);
+                scsi_unregister(shp);
+                continue;
+            }
+
+#if LINUX_VERSION_CODE >= 0x020000
+            shp->max_id      = 8;
+            shp->max_lun     = 8;
+            shp->max_channel = ha->bus_cnt - 1;
+#else
+            /* register addit. SCSI channels as virtual controllers */
+            for (b=1; b<ha->bus_cnt; ++b) {
+                shp = scsi_register(shtp,sizeof(gdth_num_str));
+                shp->unchecked_isa_dma = 0;
+                shp->irq = ha->irq;
+                shp->dma_channel = 0xff;
+                for (i=0; i<MAXID; ++i) {
+                    if (ha->id[b][i].type==SIOP_DTYP) {
+                        shp->this_id = i;
+                        break;
+                    }
+                }
+                gdth_ctr_vtab[gdth_ctr_vcount++] = shp;
+                NUMDATA(shp)->hanum = (ushort)hanum;
+                NUMDATA(shp)->busnum = b;
+                TRACE2(("EISA detect Bus %d: shp %lx hanum %d\n",
+                              NUMDATA(shp)->busnum,(ulong)shp,
+                              NUMDATA(shp)->hanum));
+            }
+#endif
+
+            gdth_enable_int(hanum);
+        }
+    }
+
+    /* scanning for PCI controllers */
+    for (device_id = 0; device_id <= PCI_DEVICE_ID_VORTEX_GDT6x21RP2; ++device_id) {
+        if (device_id > PCI_DEVICE_ID_VORTEX_GDT6555 &&
+            device_id < PCI_DEVICE_ID_VORTEX_GDT6x17RP)
+            continue;
+        for (index = 0; ; ++index) {
+            if (!gdth_search_pci(device_id,index,&pcistr)) 
+                break;                          /* next device_id */
+            shp = scsi_register(shtp,sizeof(gdth_ext_str));
+            ha = HADATA(shp);
+            if (!gdth_init_pci(&pcistr,ha)) {
+                scsi_unregister(shp);
+                continue;
+            }
+            /* controller found and initialized */
+            printk("Configuring GDT-PCI HA at %d/%d IRQ %u\n",
+                   pcistr.bus,pcistr.device_fn>>3,ha->irq);
+
+            save_flags(flags);
+            cli();
+#if LINUX_VERSION_CODE >= 0x010346 
+            if (request_irq(ha->irq,gdth_interrupt,SA_INTERRUPT,"gdth",NULL))
+#else
+            if (request_irq(ha->irq,gdth_interrupt,SA_INTERRUPT,"gdth")) 
+#endif
+            {
+                printk("GDT-PCI: Unable to allocate IRQ\n");
+                restore_flags(flags);
+                scsi_unregister(shp);
+                continue;
+            }
+            shp->unchecked_isa_dma = 0;
+            shp->irq = ha->irq;
+            shp->dma_channel = 0xff;
+            for (i=0; i<MAXID; ++i) {
+                if (ha->id[0][i].type==SIOP_DTYP) {
+                    shp->this_id = i;
+                    break;
+                }
+            }
+            hanum = gdth_ctr_count;
+            gdth_ctr_tab[gdth_ctr_count++] = shp;
+            gdth_ctr_vtab[gdth_ctr_vcount++] = shp;
+
+            NUMDATA(shp)->hanum = (ushort)hanum;
+            NUMDATA(shp)->busnum= 0;
+
+            ha->pccb = CMDDATA(shp);
+            ha->pscratch = DMADATA(shp);
+            ha->req_first = NULL;
+            for (i=0; i<MAXBUS; ++i) {
+                for (j=0; j<MAXID; ++j) {
+                    ha->id[i][j].type = EMPTY_DTYP;
+                    ha->id[i][j].lock = 0;
+                }
+            }
+            restore_flags(flags);
+
+            if (!gdth_search_drives(hanum)) {
+                printk("GDT-PCI: Error during device scan\n");
+                --gdth_ctr_count;
+		--gdth_ctr_vcount;
+                save_flags(flags);
+                cli();
+#if LINUX_VERSION_CODE >= 0x010346 
+                free_irq(ha->irq,NULL);
+#else
+                free_irq(ha->irq);
+#endif
+                restore_flags(flags);
+                scsi_unregister(shp);
+                continue;
+            }
+
+#if LINUX_VERSION_CODE >= 0x020000
+            shp->max_id      = 8;
+            shp->max_lun     = 8;
+            shp->max_channel = ha->bus_cnt - 1;
+#else
+            /* register addit. SCSI channels as virtual controllers */
+            for (b=1; b<ha->bus_cnt; ++b) {
+                shp = scsi_register(shtp,sizeof(gdth_num_str));
+                shp->unchecked_isa_dma = 0;
+                shp->irq = ha->irq;
+                shp->dma_channel = 0xff;
+                for (i=0; i<MAXID; ++i) {
+                    if (ha->id[b][i].type==SIOP_DTYP) {
+                        shp->this_id = i;
+                        break;
+                    }
+                }
+                gdth_ctr_vtab[gdth_ctr_vcount++] = shp;
+                NUMDATA(shp)->hanum = (ushort)hanum;
+                NUMDATA(shp)->busnum = b;
+            }
+#endif
+
+            gdth_enable_int(hanum);
+        }
+    }
+
+    TRACE2(("gdth_detect() %d controller detected\n",gdth_ctr_count));
+    if (gdth_ctr_count > 0) {
+#ifdef GDTH_STATISTICS
+	TRACE2(("gdth_detect(): Initializing timer !\n"));
+	timer_table[GDTH_TIMER].fn = gdth_timeout;
+	timer_table[GDTH_TIMER].expires = jiffies + HZ;
+	timer_active |= 1<<GDTH_TIMER;
+#endif
+#if LINUX_VERSION_CODE >= 0x020100
+	register_reboot_notifier(&gdth_notifier);
+#endif
+    }
+    gdth_polling = FALSE;
+    return gdth_ctr_vcount;
+}
+
+
+int gdth_release(struct Scsi_Host *shp)
+{
+    unsigned long flags;
+
+    TRACE2(("gdth_release()\n"));
+
+    save_flags(flags);
+    cli();
+    if (NUMDATA(shp)->busnum == 0) {
+        if (shp->irq) {
+#if LINUX_VERSION_CODE >= 0x010346
+            free_irq(shp->irq,NULL);
+#else
+            free_irq(shp->irq);
+#endif
+        }
+        if (shp->dma_channel != 0xff) {
+            free_dma(shp->dma_channel);
+        }
+    }
+
+    restore_flags(flags);
+    scsi_unregister(shp);
+    return 0;
+}
+            
+
+static const char *gdth_ctr_name(int hanum)
+{
+    gdth_ha_str *ha;
+
+    TRACE2(("gdth_ctr_name()\n"));
+
+    ha    = HADATA(gdth_ctr_tab[hanum]);
+
+    if (ha->type == GDT_EISA) {
+        switch (ha->stype) {
+          case GDT3_ID:
+            return("GDT3000/3020 (EISA)");
+          case GDT3A_ID:
+            return("GDT3000A/3020A/3050A (EISA)");
+          case GDT3B_ID:
+            return("GDT3000B/3010A (EISA)");
+        }
+    } else if (ha->type == GDT_ISA) {
+        return("GDT2000/2020 (ISA)");
+    } else if (ha->type == GDT_PCI) {
+        switch (ha->stype) {
+          case PCI_DEVICE_ID_VORTEX_GDT60x0:
+            return("GDT6000/6020/6050 (PCI)");
+          case PCI_DEVICE_ID_VORTEX_GDT6000B:
+            return("GDT6000B/6010 (PCI)");
+        }
+    } else if (ha->type == GDT_PCINEW) {
+        switch (ha->stype) {
+          case PCI_DEVICE_ID_VORTEX_GDT6x10:
+            return("GDT6110/6510 (PCI)");
+          case PCI_DEVICE_ID_VORTEX_GDT6x20:
+            return("GDT6120/6520 (PCI)");
+          case PCI_DEVICE_ID_VORTEX_GDT6530:
+            return("GDT6530 (PCI)");
+          case PCI_DEVICE_ID_VORTEX_GDT6550:
+            return("GDT6550 (PCI)");
+          case PCI_DEVICE_ID_VORTEX_GDT6x17:
+            return("GDT6117/6517 (PCI)");
+          case PCI_DEVICE_ID_VORTEX_GDT6x27:
+            return("GDT6127/6527 (PCI)");
+          case PCI_DEVICE_ID_VORTEX_GDT6537:
+            return("GDT6537 (PCI)");
+          case PCI_DEVICE_ID_VORTEX_GDT6557:
+            return("GDT6557/6557-ECC (PCI)");
+          case PCI_DEVICE_ID_VORTEX_GDT6x15:
+            return("GDT6115/6515 (PCI)");
+          case PCI_DEVICE_ID_VORTEX_GDT6x25:
+            return("GDT6125/6525 (PCI)");
+          case PCI_DEVICE_ID_VORTEX_GDT6535:
+            return("GDT6535 (PCI)");
+          case PCI_DEVICE_ID_VORTEX_GDT6555:
+            return("GDT6555/6555-ECC (PCI)");
+        }
+    } else if (ha->type == GDT_PCIMPR) {
+        switch (ha->stype) {
+          case PCI_DEVICE_ID_VORTEX_GDT6x17RP:
+            return("GDT6117RP/GDT6517RP (PCI)");
+          case PCI_DEVICE_ID_VORTEX_GDT6x27RP:
+            return("GDT6127RP/GDT6527RP (PCI)");
+          case PCI_DEVICE_ID_VORTEX_GDT6537RP:
+            return("GDT6537RP (PCI)");
+          case PCI_DEVICE_ID_VORTEX_GDT6557RP:
+            return("GDT6557RP (PCI)");
+          case PCI_DEVICE_ID_VORTEX_GDT6x11RP:
+            return("GDT6111RP/GDT6511RP (PCI)");
+          case PCI_DEVICE_ID_VORTEX_GDT6x21RP:
+            return("GDT6121RP/GDT6521RP (PCI)");
+          case PCI_DEVICE_ID_VORTEX_GDT6x17RP1:
+            return("GDT6117RP1/GDT6517RP1 (PCI)");
+          case PCI_DEVICE_ID_VORTEX_GDT6x27RP1:
+            return("GDT6127RP1/GDT6527RP1 (PCI)");
+          case PCI_DEVICE_ID_VORTEX_GDT6537RP1:
+            return("GDT6537RP1 (PCI)");
+          case PCI_DEVICE_ID_VORTEX_GDT6557RP1:
+            return("GDT6557RP1 (PCI)");
+          case PCI_DEVICE_ID_VORTEX_GDT6x11RP1:
+            return("GDT6111RP1/GDT6511RP1 (PCI)");
+          case PCI_DEVICE_ID_VORTEX_GDT6x21RP1:
+            return("GDT6121RP1/GDT6521RP1 (PCI)");
+          case PCI_DEVICE_ID_VORTEX_GDT6x17RP2:
+            return("GDT6117RP2/GDT6517RP2 (PCI)");
+          case PCI_DEVICE_ID_VORTEX_GDT6x27RP2:
+            return("GDT6127RP2/GDT6527RP2 (PCI)");
+          case PCI_DEVICE_ID_VORTEX_GDT6537RP2:
+            return("GDT6537RP2 (PCI)");
+          case PCI_DEVICE_ID_VORTEX_GDT6557RP2:
+            return("GDT6557RP2 (PCI)");
+          case PCI_DEVICE_ID_VORTEX_GDT6x11RP2:
+            return("GDT6111RP2/GDT6511RP2 (PCI)");
+          case PCI_DEVICE_ID_VORTEX_GDT6x21RP2:
+            return("GDT6121RP2/GDT6521RP2 (PCI)");
+        }
+    }
+    return("");
+}
+
+const char *gdth_info(struct Scsi_Host *shp)
+{
+    int hanum;
+    
+    TRACE2(("gdth_info()\n"));
+    hanum = NUMDATA(shp)->hanum;
+
+    return (gdth_ctr_name(hanum));
+}
+
+
+int gdth_abort(Scsi_Cmnd *scp)
+{
+    TRACE2(("gdth_abort() reason %d\n",scp->abort_reason));
+    return SCSI_ABORT_SNOOZE;
+}
+
+#if LINUX_VERSION_CODE >= 0x010346
+int gdth_reset(Scsi_Cmnd *scp, unsigned int reset_flags)
+#else
+int gdth_reset(Scsi_Cmnd *scp)
+#endif
+{
+    TRACE2(("gdth_reset()\n"));
+    return SCSI_RESET_PUNT;
+}
+
+
+#if LINUX_VERSION_CODE >= 0x010300
+int gdth_bios_param(Disk *disk,kdev_t dev,int *ip)
+#else
+int gdth_bios_param(Disk *disk,int dev,int *ip)
+#endif
+{
+    unchar b, t;
+    int hanum;
+    gdth_ha_str *ha;
+
+    hanum = NUMDATA(disk->device->host)->hanum;
+    b = disk->device->channel;
+    t = disk->device->id;
+    TRACE2(("gdth_bios_param() ha %d bus %d target %d\n", hanum, b, t));
+    ha = HADATA(gdth_ctr_tab[hanum]);
+
+    ip[0] = ha->id[b][t].heads;
+    ip[1] = ha->id[b][t].secs;
+    ip[2] = disk->capacity / ip[0] / ip[1];
+
+    TRACE2(("gdth_bios_param(): %d heads, %d secs, %d cyls\n",
+            ip[0],ip[1],ip[2]));
+    return 0;
+}
+
+
+static void internal_done(Scsi_Cmnd *scp)
+{
+    scp->SCp.sent_command++;
+}
+
+int gdth_command(Scsi_Cmnd *scp)
+{
+    TRACE2(("gdth_command()\n"));
+
+    scp->SCp.sent_command = 0;
+    gdth_queuecommand(scp,internal_done);
+
+    while (!scp->SCp.sent_command)
+        barrier();
+    return scp->result;
+}
+
+
+int gdth_queuecommand(Scsi_Cmnd *scp,void (*done)(Scsi_Cmnd *))
+{
+    int hanum;
+    int priority;
+
+    TRACE(("gdth_queuecommand() cmd 0x%x id %d lun %d\n",
+                  scp->cmnd[0],scp->target,scp->lun));
+    
+    scp->scsi_done = (void *)done;
+    scp->SCp.have_data_in = 0;
+    hanum = NUMDATA(scp->host)->hanum;
+#ifdef GDTH_STATISTICS
+    ++act_ios;
+#endif
+
+    priority = DEFAULT_PRI;
+#if LINUX_VERSION_CODE >= 0x010300
+    if (scp->done == gdth_scsi_done)
+        priority = scp->SCp.this_residual;
+#endif
+    gdth_putq( hanum, scp, priority );
+    gdth_next( hanum );
+    return 0;
+}
+
+
+/* shutdown routine */
+#if LINUX_VERSION_CODE >= 0x020100
+static int gdth_halt(struct notifier_block *nb, ulong event, void *buf)
+#else
+void gdth_halt(void)
+#endif
+{
+    int             hanum, i, j;
+    gdth_ha_str     *ha;
+    Scsi_Cmnd       scp;
+    Scsi_Device     sdev;
+    gdth_cmd_str    gdtcmd;
+    char            cmnd[12];
+
+#if LINUX_VERSION_CODE >= 0x020100
+    TRACE2(("gdth_halt() event %d\n",event));
+    if (event != SYS_RESTART && event != SYS_HALT && event != SYS_POWER_OFF)
+	return NOTIFY_DONE;
+#else
+    TRACE2(("gdth_halt()\n"));
+#endif
+    printk("GDT: Flushing all host drives .. ");
+    for (hanum = 0; hanum < gdth_ctr_count; ++hanum) {
+        ha = HADATA(gdth_ctr_tab[hanum]);
+        memset(&sdev,0,sizeof(Scsi_Device));
+        memset(&scp, 0,sizeof(Scsi_Cmnd));
+        sdev.host = gdth_ctr_tab[hanum];
+        sdev.id = sdev.host->this_id;
+        scp.cmd_len = 12;
+        scp.host = gdth_ctr_tab[hanum];
+        scp.target = sdev.host->this_id;
+        scp.device = &sdev;
+        scp.use_sg = 0;
+
+        /* flush */
+        for (i = 0; i < MAXBUS; ++i) {
+            for (j = 0; j < MAXID; ++j) {
+                if (ha->id[i][j].type == CACHE_DTYP) {
+                    gdtcmd.BoardNode = LOCALBOARD;
+                    gdtcmd.Service = CACHESERVICE;
+                    gdtcmd.OpCode = GDT_FLUSH;
+                    gdtcmd.u.cache.DeviceNo = ha->id[i][j].hostdrive;
+                    gdtcmd.u.cache.BlockNo = 1;
+                    gdtcmd.u.cache.sg_canz = 0;
+                    TRACE2(("gdth_halt(): flush ha %d drive %d\n",
+                        hanum, ha->id[i][j].hostdrive));
+                    {
+                        struct semaphore sem = MUTEX_LOCKED;
+                        scp.request.rq_status = RQ_SCSI_BUSY;
+                        scp.request.sem = &sem;
+                        scsi_do_cmd(&scp, cmnd, &gdtcmd,
+                                    sizeof(gdth_cmd_str), gdth_scsi_done,
+                                    30*HZ, 1);
+                        down(&sem);
+                    }
+                }
+            }
+        }
+
+        /* controller reset */
+        gdtcmd.BoardNode = LOCALBOARD;
+        gdtcmd.Service = CACHESERVICE;
+        gdtcmd.OpCode = GDT_RESET;
+        TRACE2(("gdth_halt(): reset controller %d\n", hanum));
+        {
+            struct semaphore sem = MUTEX_LOCKED;
+            scp.request.rq_status = RQ_SCSI_BUSY;
+            scp.request.sem = &sem;
+            scsi_do_cmd(&scp, cmnd, &gdtcmd,
+                sizeof(gdth_cmd_str), gdth_scsi_done,
+                10*HZ, 1);
+            down(&sem);
+        }
+    }
+    printk("Done.\n");
+
+#ifdef GDTH_STATISTICS
+    timer_active &= ~(1<<GDTH_TIMER);
+#endif
+#if LINUX_VERSION_CODE >= 0x020100
+    unregister_reboot_notifier(&gdth_notifier);
+    return NOTIFY_OK;
+#endif
+}
+
+
+/* called from init/main.c */
+void gdth_setup(char *str,int *ints)
+{
+    static size_t setup_idx = 0;
+
+    TRACE2(("gdth_setup() str %s ints[0] %d ints[1] %d\n",
+                  str ? str:"NULL", ints[0],
+                  ints[0] ? ints[1]:0));
+
+    if (setup_idx >= MAXHA) {
+        printk("GDT: gdth_setup() called too many times. Bad LILO params ?\n");
+        return;
+    }
+    if (ints[0] != 1) {
+        printk("GDT: Illegal command line !\n");
+        printk("Usage: gdth=<IRQ>\n");
+        printk("Where: <IRQ>: valid EISA controller IRQ (10,11,12,14)\n");
+        printk("              or 0 to disable controller driver\n");
+        return;
+    }
+    if (ints[1] == 10 || ints[1] == 11 || ints[1] == 12 || ints[1] == 14) {
+        irqs[setup_idx++] = ints[1];
+        irqs[setup_idx]   = 0xff;
+        return;
+    }
+    if (ints[1] == 0) {
+        disable_gdth_scan = TRUE;
+        return;
+    }
+    printk("GDT: Invalid IRQ (%d) specified\n",ints[1]);
+}
+
+
+#ifdef MODULE
+Scsi_Host_Template driver_template = GDTH;
+#include "scsi_module.c"
+#endif
+
diff -u --recursive --new-file v2.1.62/linux/drivers/scsi/gdth.h linux/drivers/scsi/gdth.h
--- v2.1.62/linux/drivers/scsi/gdth.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/scsi/gdth.h	Wed Nov  5 13:16:29 1997
@@ -0,0 +1,720 @@
+#ifndef _GDTH_H
+#define _GDTH_H
+
+/*
+ * Header file for the GDT ISA/EISA/PCI Disk Array Controller driver for Linux
+ * 
+ * gdth.h Copyright (C) 1995-97 ICP vortex Computersysteme GmbH, Achim Leubner
+ * See gdth.c for further informations and 
+ * below for supported controller types
+ *
+ * <achim@vortex.de>
+ *
+ * $Id: gdth.h,v 1.9 1997/11/04 09:55:42 achim Exp $
+ */
+
+#include <linux/version.h>
+#include <linux/types.h>
+
+#ifndef NULL
+#define NULL 0
+#endif
+#ifndef TRUE
+#define TRUE 1
+#endif
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+/* defines, macros */
+
+/* driver version */
+#define GDTH_VERSION_STR        "1.02"
+#define GDTH_VERSION            1
+#define GDTH_SUBVERSION         2
+
+/* protocol version */
+#define PROTOCOL_VERSION        1
+
+/* controller classes */
+#define GDT_ISA         0x01                    /* ISA controller */
+#define GDT_EISA        0x02                    /* EISA controller */
+#define GDT_PCI         0x03                    /* PCI controller */
+#define GDT_PCINEW      0x04                    /* new PCI controller */
+#define GDT_PCIMPR      0x05                    /* PCI MPR controller */
+/* GDT_EISA, controller subtypes EISA */
+#define GDT3_ID         0x0130941c              /* GDT3000/3020 */
+#define GDT3A_ID        0x0230941c              /* GDT3000A/3020A/3050A */
+#define GDT3B_ID        0x0330941c              /* GDT3000B/3010A */
+/* GDT_ISA */
+#define GDT2_ID         0x0120941c              /* GDT2000/2020 */
+/* vendor ID, device IDs (PCI) */
+/* these defines should already exist in <linux/pci.h> */
+#ifndef PCI_VENDOR_ID_VORTEX
+#define PCI_VENDOR_ID_VORTEX            0x1119  /* PCI controller vendor ID */
+#endif
+#ifndef PCI_DEVICE_ID_VORTEX_GDT60x0
+/* GDT_PCI */
+#define PCI_DEVICE_ID_VORTEX_GDT60x0    0       /* GDT6000/6020/6050 */
+#define PCI_DEVICE_ID_VORTEX_GDT6000B   1       /* GDT6000B/6010 */
+/* GDT_PCINEW */
+#define PCI_DEVICE_ID_VORTEX_GDT6x10    2       /* GDT6110/6510 */
+#define PCI_DEVICE_ID_VORTEX_GDT6x20    3       /* GDT6120/6520 */
+#define PCI_DEVICE_ID_VORTEX_GDT6530    4       /* GDT6530 */
+#define PCI_DEVICE_ID_VORTEX_GDT6550    5       /* GDT6550 */
+/* GDT_PCINEW, wide/ultra SCSI controllers */
+#define PCI_DEVICE_ID_VORTEX_GDT6x17    6       /* GDT6117/6517 */
+#define PCI_DEVICE_ID_VORTEX_GDT6x27    7       /* GDT6127/6527 */
+#define PCI_DEVICE_ID_VORTEX_GDT6537    8       /* GDT6537 */
+#define PCI_DEVICE_ID_VORTEX_GDT6557    9       /* GDT6557/6557-ECC */
+/* GDT_PCINEW, wide SCSI controllers */
+#define PCI_DEVICE_ID_VORTEX_GDT6x15    10      /* GDT6115/6515 */
+#define PCI_DEVICE_ID_VORTEX_GDT6x25    11      /* GDT6125/6525 */
+#define PCI_DEVICE_ID_VORTEX_GDT6535    12      /* GDT6535 */
+#define PCI_DEVICE_ID_VORTEX_GDT6555    13      /* GDT6555/6555-ECC */
+#endif
+
+#ifndef PCI_DEVICE_ID_VORTEX_GDT6x17RP
+/* GDT_MPR, RP series, wide/ultra SCSI */
+#define PCI_DEVICE_ID_VORTEX_GDT6x17RP  0x100   /* GDT6117RP/GDT6517RP */
+#define PCI_DEVICE_ID_VORTEX_GDT6x27RP  0x101   /* GDT6127RP/GDT6527RP */
+#define PCI_DEVICE_ID_VORTEX_GDT6537RP  0x102   /* GDT6537RP */
+#define PCI_DEVICE_ID_VORTEX_GDT6557RP  0x103   /* GDT6557RP */
+/* GDT_MPR, RP series, narrow/ultra SCSI */
+#define PCI_DEVICE_ID_VORTEX_GDT6x11RP  0x104   /* GDT6111RP/GDT6511RP */
+#define PCI_DEVICE_ID_VORTEX_GDT6x21RP  0x105   /* GDT6121RP/GDT6521RP */
+/* GDT_MPR, RP1 series, wide/ultra SCSI */
+#define PCI_DEVICE_ID_VORTEX_GDT6x17RP1 0x110   /* GDT6117RP1/GDT6517RP1 */
+#define PCI_DEVICE_ID_VORTEX_GDT6x27RP1 0x111   /* GDT6127RP1/GDT6527RP1 */
+#define PCI_DEVICE_ID_VORTEX_GDT6537RP1 0x112   /* GDT6537RP1 */
+#define PCI_DEVICE_ID_VORTEX_GDT6557RP1 0x113   /* GDT6557RP1 */
+/* GDT_MPR, RP1 series, narrow/ultra SCSI */
+#define PCI_DEVICE_ID_VORTEX_GDT6x11RP1 0x114   /* GDT6111RP1/GDT6511RP1 */
+#define PCI_DEVICE_ID_VORTEX_GDT6x21RP1 0x115   /* GDT6121RP1/GDT6521RP1 */
+/* GDT_MPR, RP2 series, wide/ultra SCSI */
+#define PCI_DEVICE_ID_VORTEX_GDT6x17RP2 0x120   /* GDT6117RP2/GDT6517RP2 */
+#define PCI_DEVICE_ID_VORTEX_GDT6x27RP2 0x121   /* GDT6127RP2/GDT6527RP2 */
+#define PCI_DEVICE_ID_VORTEX_GDT6537RP2 0x122   /* GDT6537RP2 */
+#define PCI_DEVICE_ID_VORTEX_GDT6557RP2 0x123   /* GDT6557RP2 */
+/* GDT_MPR, RP2 series, narrow/ultra SCSI */
+#define PCI_DEVICE_ID_VORTEX_GDT6x11RP2 0x124   /* GDT6111RP2/GDT6511RP2 */
+#define PCI_DEVICE_ID_VORTEX_GDT6x21RP2 0x125   /* GDT6121RP2/GDT6521RP2 */
+#endif
+
+/* limits */
+#define GDTH_SCRATCH    4096                    /* 4KB scratch buffer */
+#define GDTH_MAXCMDS    124
+#define GDTH_MAXC_P_L   16                      /* max. cmds per lun */
+#define MAXOFFSETS      128
+#define MAXHA           8
+#define MAXID           8
+#define MAXLUN          8
+#define MAXBUS          5
+#define MAX_HDRIVES     35                      /* max. host drive count */
+#define MAX_EVENTS      100                     /* event buffer count */
+#define MAXCYLS         1024
+#define HEADS           64
+#define SECS            32                      /* mapping 64*32 */
+#define MEDHEADS        127
+#define MEDSECS         63                      /* mapping 127*63 */
+#define BIGHEADS        255
+#define BIGSECS         63                      /* mapping 255*63 */
+
+/* special command ptr. */
+#define UNUSED_CMND     ((Scsi_Cmnd *)-1)
+#define INTERNAL_CMND   ((Scsi_Cmnd *)-2)
+#define SCREEN_CMND     ((Scsi_Cmnd *)-3)
+#define SPECIAL_SCP(p)  (p==UNUSED_CMND || p==INTERNAL_CMND || p==SCREEN_CMND)
+
+/* device types */
+#define EMPTY_DTYP      0
+#define CACHE_DTYP      1
+#define RAW_DTYP        2
+#define SIOP_DTYP       3                       /* the SCSI processor */
+
+/* controller services */
+#define SCSIRAWSERVICE  3
+#define CACHESERVICE    9
+#define SCREENSERVICE   11
+
+/* screenservice defines */
+#define MSG_INV_HANDLE  -1                      /* special message handle */
+#define MSGLEN          16                      /* size of message text */
+#define MSG_SIZE        34                      /* size of message structure */
+#define MSG_REQUEST     0                       /* async. event: message */
+
+/* cacheservice defines */
+#define SECTOR_SIZE     0x200                   /* always 512 bytes per sector */
+
+/* DPMEM constants */
+#define IC_HEADER_BYTES 48
+#define IC_QUEUE_BYTES  4
+#define DPMEM_COMMAND_OFFSET    IC_HEADER_BYTES+IC_QUEUE_BYTES*MAXOFFSETS
+
+/* service commands */
+#define GDT_INIT        0                       /* service initialization */
+#define GDT_READ        1                       /* read command */
+#define GDT_WRITE       2                       /* write command */
+#define GDT_INFO        3                       /* information about devices */
+#define GDT_FLUSH       4                       /* flush dirty cache buffers */
+#define GDT_IOCTL       5                       /* ioctl command */
+#define GDT_DEVTYPE     9                       /* additional information */
+#define GDT_MOUNT       10                      /* mount cache device */
+#define GDT_UNMOUNT     11                      /* unmount cache device */
+#define GDT_SET_FEAT    12                      /* set feat. (scatter/gather) */
+#define GDT_GET_FEAT    13                      /* get features */
+#define GDT_RESERVE     14                      /* reserve dev. to raw service */
+#define GDT_WRITE_THR   16                      /* write through */
+#define GDT_EXT_INFO    18                      /* extended info */
+#define GDT_RESET       19                      /* controller reset */
+
+/* IOCTL command defines */
+#define SCSI_CHAN_CNT   5                       /* subfunctions */
+#define L_CTRL_PATTERN  0x20000000L
+#define CACHE_INFO      4
+#define CACHE_CONFIG    5
+#define IO_CHANNEL      0x00020000L             /* channels */
+#define INVALID_CHANNEL 0x0000ffffL     
+
+/* IOCTLs */
+#define GDTIOCTL_MASK       ('J'<<8)
+#define GDTIOCTL_GENERAL    (GDTIOCTL_MASK | 0) /* general IOCTL */
+#define GDTIOCTL_DRVERS     (GDTIOCTL_MASK | 1) /* get driver version */
+#define GDTIOCTL_CTRTYPE    (GDTIOCTL_MASK | 2) /* get controller type */
+#define GDTIOCTL_CTRCNT     (GDTIOCTL_MASK | 5) /* get controller count */
+#define GDTIOCTL_LOCKDRV    (GDTIOCTL_MASK | 6) /* lock host drive */
+#define GDTIOCTL_LOCKCHN    (GDTIOCTL_MASK | 7) /* lock channel */
+#define GDTIOCTL_EVENT      (GDTIOCTL_MASK | 8) /* read controller events */
+
+/* service errors */
+#define S_OK            1                       /* no error */
+#define S_BSY           7                       /* controller busy */
+#define S_RAW_SCSI      12                      /* raw serv.: target error */
+#define S_RAW_ILL       0xff                    /* raw serv.: illegal */
+
+/* timeout values */
+#define INIT_RETRIES    10000                   /* 10000 * 1ms = 10s */
+#define INIT_TIMEOUT    100000                  /* 1000 * 1ms = 1s */
+#define POLL_TIMEOUT    10000                   /* 10000 * 1ms = 10s */
+
+/* priorities */
+#define DEFAULT_PRI     0x20
+#define IOCTL_PRI       0x10
+
+/* data directions */
+#define DATA_IN         0x01000000L             /* data from target */
+#define DATA_OUT        0x00000000L             /* data to target */
+
+/* BMIC registers (EISA controllers) */
+#define ID0REG          0x0c80                  /* board ID */
+#define EINTENABREG     0x0c89                  /* interrupt enable */
+#define SEMA0REG        0x0c8a                  /* command semaphore */
+#define SEMA1REG        0x0c8b                  /* status semaphore */
+#define LDOORREG        0x0c8d                  /* local doorbell */
+#define EDENABREG       0x0c8e                  /* EISA system doorbell enable */
+#define EDOORREG        0x0c8f                  /* EISA system doorbell */
+#define MAILBOXREG      0x0c90                  /* mailbox reg. (16 bytes) */
+#define EISAREG         0x0cc0                  /* EISA configuration */
+
+/* other defines */
+#define LINUX_OS        8                       /* used for cache optim. */
+#define SCATTER_GATHER  1                       /* s/g feature */
+#define GDTH_MAXSG      32                      /* max. s/g elements */
+#define SECS32          0x1f                    /* round capacity */
+#define BIOS_ID_OFFS    0x10                    /* offset contr. ID in ISABIOS */
+#define LOCALBOARD      0                       /* board node always 0 */
+#define ASYNCINDEX      0                       /* cmd index async. event */
+#define SPEZINDEX       1                       /* cmd index unknown service */
+#define GDT_WR_THROUGH  0x100                   /* WRITE_THROUGH supported */
+
+/* typedefs */
+
+#pragma pack(1)
+
+typedef struct {
+    char        buffer[GDTH_SCRATCH];           /* scratch buffer */
+} gdth_scratch_str;
+
+/* screenservice message */
+typedef struct {                               
+    ulong       msg_handle;                     /* message handle */
+    ulong       msg_len;                        /* size of message */
+    ulong       msg_alen;                       /* answer length */
+    unchar      msg_answer;                     /* answer flag */
+    unchar      msg_ext;                        /* more messages */
+    unchar      msg_reserved[2];
+    char        msg_text[MSGLEN+2];             /* the message text */
+} gdth_msg_str;
+
+/* get channel count IOCTL */
+typedef struct {
+    ulong       channel_no;                     /* number of channel */
+    ulong       drive_cnt;                      /* number of drives */
+    unchar      siop_id;                        /* SCSI processor ID */
+    unchar      siop_state;                     /* SCSI processor state */ 
+} gdth_getch_str;
+
+/* cache info/config IOCTL */
+typedef struct {
+    ulong       version;                        /* firmware version */
+    ushort      state;                          /* cache state (on/off) */
+    ushort      strategy;                       /* cache strategy */
+    ushort      write_back;                     /* write back state (on/off) */
+    ushort      block_size;                     /* cache block size */
+} gdth_cpar_str;
+
+typedef struct {
+    ulong       csize;                          /* cache size */
+    ulong       read_cnt;                       /* read/write counter */
+    ulong       write_cnt;
+    ulong       tr_hits;                        /* hits */
+    ulong       sec_hits;
+    ulong       sec_miss;                       /* misses */
+} gdth_cstat_str;
+
+typedef struct {
+    gdth_cpar_str   cpar;
+    gdth_cstat_str  cstat;
+} gdth_cinfo_str;
+
+/* scatter/gather element */
+typedef struct {
+    ulong       sg_ptr;                         /* address */
+    ulong       sg_len;                         /* length */
+} gdth_sg_str;
+
+/* command structure */
+typedef struct {
+    ulong       BoardNode;                      /* board node (always 0) */
+    ulong       CommandIndex;                   /* command number */
+    ushort      OpCode;                         /* the command (READ,..) */
+    union {
+        struct {
+            ushort      DeviceNo;               /* number of cache drive */
+            ulong       BlockNo;                /* block number */
+            ulong       BlockCnt;               /* block count */
+            ulong       DestAddr;               /* dest. addr. (if s/g: -1) */
+            ulong       sg_canz;                /* s/g element count */
+            gdth_sg_str sg_lst[GDTH_MAXSG];     /* s/g list */
+        } cache;                                /* cache service cmd. str. */
+        struct {
+            ushort      param_size;             /* size of p_param buffer */
+            ulong       subfunc;                /* IOCTL function */
+            ulong       channel;                /* device */
+            ulong       p_param;                /* buffer */
+        } ioctl;                                /* IOCTL command structure */
+        struct {
+            ushort      reserved;
+            ulong       msg_handle;             /* message handle */
+            ulong       msg_addr;               /* message buffer address */
+        } screen;                               /* screen service cmd. str. */
+        struct {
+            ushort      reserved;
+            ulong       direction;              /* data direction */
+            ulong       mdisc_time;             /* disc. time (0: no timeout)*/
+            ulong       mcon_time;              /* connect time(0: no to.) */
+            ulong       sdata;                  /* dest. addr. (if s/g: -1) */
+            ulong       sdlen;                  /* data length (bytes) */
+            ulong       clen;                   /* SCSI cmd. length(6,10,12) */
+            unchar      cmd[12];                /* SCSI command */
+            unchar      target;                 /* target ID */
+            unchar      lun;                    /* LUN */
+            unchar      bus;                    /* SCSI bus number */
+            unchar      priority;               /* only 0 used */
+            ulong       sense_len;              /* sense data length */
+            ulong       sense_data;             /* sense data addr. */
+            struct raw  *link_p;                /* linked cmds (not supp.) */
+            ulong       sg_ranz;                /* s/g element count */
+            gdth_sg_str sg_lst[GDTH_MAXSG];     /* s/g list */
+        } raw;                                  /* raw service cmd. struct. */
+    } u;
+    /* additional variables */
+    unchar      Service;                        /* controller service */
+    ushort      Status;                         /* command result */
+    ulong       Info;                           /* additional information */
+    Scsi_Cmnd   *RequestBuffer;                 /* request buffer */
+} gdth_cmd_str;
+
+/* controller event structure */
+#define ES_ASYNC    1
+#define ES_DRIVER   2
+#define ES_TEST     3
+#define ES_SYNC     4
+typedef struct {
+    ushort                  size;               /* size of structure */
+    union {
+        char                stream[16];
+        struct {
+            ushort          ionode;
+            ushort          service;
+            ulong           index;
+        } driver;
+        struct {
+            ushort          ionode;
+            ushort          service;
+            ushort          status;
+            ulong           info;
+            unchar          scsi_coord[3];
+        } async;
+        struct {
+            ushort          ionode;
+            ushort          service;
+            ushort          status;
+            ulong           info;
+            ushort          hostdrive;
+            unchar          scsi_coord[3];
+            unchar          sense_key;
+        } sync;
+        struct {
+            ulong           l1, l2, l3, l4;
+        } test;
+    } eu;
+} gdth_evt_data;
+
+typedef struct {
+    ulong           first_stamp;
+    ulong           last_stamp;
+    ushort          same_count;
+    ushort          event_source;
+    ushort          event_idx;
+    unchar          application;
+    unchar          reserved;
+    gdth_evt_data   event_data;
+} gdth_evt_str;
+
+
+/* DPRAM structures */
+
+/* interface area ISA/PCI */
+typedef struct {
+    unchar              S_Cmd_Indx;             /* special command */
+    unchar volatile     S_Status;               /* status special command */
+    ushort              reserved1;
+    ulong               S_Info[4];              /* add. info special command */
+    unchar volatile     Sema0;                  /* command semaphore */
+    unchar              reserved2[3];
+    unchar              Cmd_Index;              /* command number */
+    unchar              reserved3[3];
+    ushort volatile     Status;                 /* command status */
+    ushort              Service;                /* service(for async.events) */
+    ulong               Info[2];                /* additional info */
+    struct {
+        ushort          offset;                 /* command offs. in the DPRAM*/
+        ushort          serv_id;                /* service */
+    } comm_queue[MAXOFFSETS];                   /* command queue */
+    ulong               bios_reserved[2];
+    unchar              gdt_dpr_cmd[1];         /* commands */
+} gdt_dpr_if;
+
+/* SRAM structure PCI controllers */
+typedef struct {
+    ulong       magic;                          /* controller ID from BIOS */
+    ushort      need_deinit;                    /* switch betw. BIOS/driver */
+    unchar      switch_support;                 /* see need_deinit */
+    unchar      padding[9];
+    unchar      os_used[16];                    /* OS code per service */
+    unchar      unused[28];
+    unchar      fw_magic;                       /* contr. ID from firmware */
+} gdt_pci_sram;
+
+/* SRAM structure EISA controllers (but NOT GDT3000/3020) */
+typedef struct {
+    unchar      os_used[16];                    /* OS code per service */
+    ushort      need_deinit;                    /* switch betw. BIOS/driver */
+    unchar      switch_support;                 /* see need_deinit */
+    unchar      padding;
+} gdt_eisa_sram;
+
+
+/* DPRAM ISA controllers */
+typedef struct {
+    union {
+        struct {
+            unchar      bios_used[0x3c00-32];   /* 15KB - 32Bytes BIOS */
+            ulong       magic;                  /* controller (EISA) ID */
+            ushort      need_deinit;            /* switch betw. BIOS/driver */
+            unchar      switch_support;         /* see need_deinit */
+            unchar      padding[9];
+            unchar      os_used[16];            /* OS code per service */
+        } dp_sram;
+        unchar          bios_area[0x4000];      /* 16KB reserved for BIOS */
+    } bu;
+    union {
+        gdt_dpr_if      ic;                     /* interface area */
+        unchar          if_area[0x3000];        /* 12KB for interface */
+    } u;
+    struct {
+        unchar          memlock;                /* write protection DPRAM */
+        unchar          event;                  /* release event */
+        unchar          irqen;                  /* board interrupts enable */
+        unchar          irqdel;                 /* acknowledge board int. */
+        unchar volatile Sema1;                  /* status semaphore */
+        unchar          rq;                     /* IRQ/DRQ configuration */
+    } io;
+} gdt2_dpram_str;
+
+/* DPRAM PCI controllers */
+typedef struct {
+    union {
+        gdt_dpr_if      ic;                     /* interface area */
+        unchar          if_area[0xff0-sizeof(gdt_pci_sram)];
+    } u;
+    gdt_pci_sram        gdt6sr;                 /* SRAM structure */
+    struct {
+        unchar          unused0[1];
+        unchar volatile Sema1;                  /* command semaphore */
+        unchar          unused1[3];
+        unchar          irqen;                  /* board interrupts enable */
+        unchar          unused2[2];
+        unchar          event;                  /* release event */
+        unchar          unused3[3];
+        unchar          irqdel;                 /* acknowledge board int. */
+        unchar          unused4[3];
+    } io;
+} gdt6_dpram_str;
+
+/* PLX register structure (new PCI controllers) */
+typedef struct {
+    unchar              cfg_reg;        /* DPRAM cfg.(2:below 1MB,0:anywhere)*/
+    unchar              unused1[0x3f];
+    unchar volatile     sema0_reg;              /* command semaphore */
+    unchar volatile     sema1_reg;              /* status semaphore */
+    unchar              unused2[2];
+    ushort volatile     status;                 /* command status */
+    ushort              service;                /* service */
+    ulong               info[2];                /* additional info */
+    unchar              unused3[0x10];
+    unchar              ldoor_reg;              /* PCI to local doorbell */
+    unchar              unused4[3];
+    unchar volatile     edoor_reg;              /* local to PCI doorbell */
+    unchar              unused5[3];
+    unchar              control0;               /* control0 register(unused) */
+    unchar              control1;               /* board interrupts enable */
+    unchar              unused6[0x16];
+} gdt6c_plx_regs;
+
+/* DPRAM new PCI controllers */
+typedef struct {
+    union {
+        gdt_dpr_if      ic;                     /* interface area */
+        unchar          if_area[0x4000-sizeof(gdt_pci_sram)];
+    } u;
+    gdt_pci_sram        gdt6sr;                 /* SRAM structure */
+} gdt6c_dpram_str;
+
+/* i960 register structure (PCI MPR controllers) */
+typedef struct {
+    unchar              unused1[16];
+    unchar volatile     sema0_reg;              /* command semaphore */
+    unchar              unused2;
+    unchar volatile     sema1_reg;              /* status semaphore */
+    unchar              unused3;
+    ushort volatile     status;                 /* command status */
+    ushort              service;                /* service */
+    ulong               info[2];                /* additional info */
+    unchar              ldoor_reg;              /* PCI to local doorbell */
+    unchar              unused4[11];
+    unchar volatile     edoor_reg;              /* local to PCI doorbell */
+    unchar              unused5[7];
+    unchar              edoor_en_reg;           /* board interrupts enable */
+    unchar              unused6[27];
+    ulong               unused7[1004];          /* size: 4 KB */
+} gdt6m_i960_regs;
+
+/* DPRAM PCI MPR controllers */
+typedef struct {
+    gdt6m_i960_regs     i960r;                  /* 4KB i960 registers */
+    union {
+        gdt_dpr_if      ic;                     /* interface area */
+        unchar          if_area[0x3000-sizeof(gdt_pci_sram)];
+    } u;
+    gdt_pci_sram        gdt6sr;                 /* SRAM structure */
+} gdt6m_dpram_str;
+
+
+/* PCI resources */
+typedef struct {
+    ushort      device_id;                      /* device ID (0,..,9) */
+    unchar      bus;                            /* PCI bus */
+    unchar      device_fn;                      /* PCI device/function no. */
+    ulong       dpmem;                          /* DPRAM address */
+    ulong       io;                             /* IO address */
+    ulong       io_mm;                          /* IO address mem. mapped */
+    ulong       bios;                           /* BIOS address */
+    unchar      irq;                            /* IRQ */
+} gdth_pci_str;
+
+
+/* controller information structure */
+typedef struct {
+    unchar              bus_cnt;                /* SCSI bus count */
+    unchar              type;                   /* controller class */
+    ushort              raw_feat;               /* feat. raw service (s/g,..) */
+    ulong               stype;                  /* controller subtype */
+    ushort              cache_feat;             /* feat. cache serv. (s/g,..) */
+    ushort		bmic;			/* BMIC address (EISA) */
+    void               	*brd;	                /* DPRAM address */
+    ulong               brd_phys;               /* slot number/BIOS address */
+    gdt6c_plx_regs      *plx;                   /* PLX regs (new PCI contr.) */
+    gdth_cmd_str        *pccb;                  /* address command structure */
+    gdth_scratch_str    *pscratch;
+    unchar              irq;                    /* IRQ */
+    unchar              drq;                    /* DRQ (ISA controllers) */
+    ushort              status;                 /* command status */
+    ulong               info;
+    ulong               info2;                  /* additional info */
+    Scsi_Cmnd           *req_first;             /* top of request queue */
+    struct {
+        unchar          type;                   /* device type */
+        unchar          heads;                  /* mapping */
+        unchar          secs;
+        unchar          lock;                   /* drive locked ? (hot plug) */
+        ushort          hostdrive;              /* host drive number */
+        ushort          devtype;                /* further information */
+        ulong           size;                   /* capacity */
+    } id[MAXBUS][MAXID];         
+    ushort              cmd_cnt;                /* command count in DPRAM */
+    ushort              cmd_len;                /* length of actual command */
+    ushort              cmd_offs_dpmem;         /* actual offset in DPRAM */
+    ushort              ic_all_size;            /* sizeof DPRAM interf. area */
+    unchar              reserved;
+    unchar              mode;                   /* information from /proc */
+    ushort              param_size;
+    gdth_cpar_str       cpar;                   /* controller cache par. */
+} gdth_ha_str;
+
+/* structure for scsi_register(), SCSI bus != 0 */
+typedef struct {
+    ushort      hanum;
+    ushort      busnum;
+} gdth_num_str;
+
+/* structure for scsi_register() */
+typedef struct {
+    gdth_num_str        numext;                 /* must be the first element */
+    gdth_ha_str         haext;
+    gdth_cmd_str        cmdext;
+    gdth_scratch_str    dmaext;
+} gdth_ext_str;
+
+
+/* INQUIRY data format */
+typedef struct {
+    unchar      type_qual;
+    unchar      modif_rmb;
+    unchar      version;
+    unchar      resp_aenc;
+    unchar      add_length;
+    unchar      reserved1;
+    unchar      reserved2;
+    unchar      misc;
+    unchar      vendor[8];
+    unchar      product[16];
+    unchar      revision[4];
+} gdth_inq_data;
+
+/* READ_CAPACITY data format */
+typedef struct {
+    ulong       last_block_no;
+    ulong       block_length;
+} gdth_rdcap_data;
+
+/* REQUEST_SENSE data format */
+typedef struct {
+    unchar      errorcode;
+    unchar      segno;
+    unchar      key;
+    ulong       info;
+    unchar      add_length;
+    ulong       cmd_info;
+    unchar      adsc;
+    unchar      adsq;
+    unchar      fruc;
+    unchar      key_spec[3];
+} gdth_sense_data;
+
+/* MODE_SENSE data format */
+typedef struct {
+    struct {
+        unchar  data_length;
+        unchar  med_type;
+        unchar  dev_par;
+        unchar  bd_length;
+    } hd;
+    struct {
+        unchar  dens_code;
+        unchar  block_count[3];
+        unchar  reserved;
+        unchar  block_length[3];
+    } bd;
+} gdth_modep_data;
+
+typedef struct {
+    ulong       b[10];                          /* 32 bit compiler ! */
+} gdth_stackframe;
+
+#pragma pack()
+
+/* function prototyping */
+
+int gdth_detect(Scsi_Host_Template *);
+int gdth_release(struct Scsi_Host *);
+int gdth_command(Scsi_Cmnd *);
+int gdth_queuecommand(Scsi_Cmnd *,void (*done)(Scsi_Cmnd *));
+int gdth_abort(Scsi_Cmnd *);
+#if LINUX_VERSION_CODE >= 0x010346
+int gdth_reset(Scsi_Cmnd *, unsigned int reset_flags);
+#else
+int gdth_reset(Scsi_Cmnd *);
+#endif
+const char *gdth_info(struct Scsi_Host *);
+
+
+#if LINUX_VERSION_CODE >= 0x010300
+int gdth_bios_param(Disk *,kdev_t,int *);
+extern struct proc_dir_entry proc_scsi_gdth;
+int gdth_proc_info(char *,char **,off_t,int,int,int);
+#define GDTH { NULL, NULL,                              \
+                   &proc_scsi_gdth,                     \
+                   gdth_proc_info,                      \
+                   "GDT SCSI Disk Array Controller",    \
+                   gdth_detect,                         \
+                   gdth_release,                        \
+                   gdth_info,                           \
+                   gdth_command,                        \
+                   gdth_queuecommand,                   \
+                   gdth_abort,                          \
+                   gdth_reset,                          \
+                   NULL,                                \
+                   gdth_bios_param,                     \
+                   GDTH_MAXCMDS,                        \
+                   -1,                                  \
+                   GDTH_MAXSG,                          \
+                   GDTH_MAXC_P_L,                       \
+                   0,                                   \
+                   1,                                   \
+                   ENABLE_CLUSTERING}
+#else
+int gdth_bios_param(Disk *,int,int *);
+#define GDTH { NULL, NULL,                              \
+                   "GDT SCSI Disk Array Controller",    \
+                   gdth_detect,                         \
+                   gdth_release,                        \
+                   gdth_info,                           \
+                   gdth_command,                        \
+                   gdth_queuecommand,                   \
+                   gdth_abort,                          \
+                   gdth_reset,                          \
+                   NULL,                                \
+                   gdth_bios_param,                     \
+                   GDTH_MAXCMDS,                        \
+                   -1,                                  \
+                   GDTH_MAXSG,                          \
+                   GDTH_MAXC_P_L,                       \
+                   0,                                   \
+                   1,                                   \
+                   ENABLE_CLUSTERING}
+#endif
+
+#endif
+
diff -u --recursive --new-file v2.1.62/linux/drivers/scsi/gdth_ioctl.h linux/drivers/scsi/gdth_ioctl.h
--- v2.1.62/linux/drivers/scsi/gdth_ioctl.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/scsi/gdth_ioctl.h	Tue Nov  4 09:17:30 1997
@@ -0,0 +1,86 @@
+#ifndef _GDTH_IOCTL_H
+#define _GDTH_IOCTL_H
+
+/* gdth_ioctl.h
+ * $Id: gdth_ioctl.h,v 1.1 1997/02/21 08:07:27 achim Exp $
+ */
+
+/* IOCTLs */
+#define GDTIOCTL_MASK       ('J'<<8)
+#define GDTIOCTL_GENERAL    (GDTIOCTL_MASK | 0) /* general IOCTL */
+#define GDTIOCTL_DRVERS     (GDTIOCTL_MASK | 1) /* get driver version */
+#define GDTIOCTL_CTRTYPE    (GDTIOCTL_MASK | 2) /* get controller type */
+#define GDTIOCTL_OSVERS     (GDTIOCTL_MASK | 3) /* get OS version */
+#define GDTIOCTL_CTRCNT     (GDTIOCTL_MASK | 5) /* get controller count */
+#define GDTIOCTL_LOCKDRV    (GDTIOCTL_MASK | 6) /* lock host drive */
+#define GDTIOCTL_LOCKCHN    (GDTIOCTL_MASK | 7) /* lock channel */
+#define GDTIOCTL_EVENT      (GDTIOCTL_MASK | 8) /* read controller events */
+
+#define GDTIOCTL_MAGIC      0x06030f07UL
+
+
+/* IOCTL structure (write) */
+typedef struct {
+    ulong                   magic;              /* IOCTL magic */
+    ushort                  ioctl;              /* IOCTL */
+    ushort                  ionode;             /* controller number */
+    ushort                  service;            /* controller service */
+    ushort                  timeout;            /* timeout */
+    union {
+        struct {
+            unchar          command[512];       /* controller command */
+            unchar          data[1];            /* add. data */
+        } general;
+        struct {
+            unchar          lock;               /* lock/unlock */
+            unchar          drive_cnt;          /* drive count */
+            ushort          drives[35];         /* drives */
+        } lockdrv;
+        struct {
+            unchar          lock;               /* lock/unlock */
+            unchar          channel;            /* channel */
+        } lockchn;
+        struct {
+            int             erase;              /* erase event ? */
+            int             handle;
+        } event;
+    } iu;
+} gdth_iowr_str;
+
+/* IOCTL structure (read) */
+typedef struct {
+    ulong                   size;               /* buffer size */
+    ulong                   status;             /* IOCTL error code */
+    union {
+        struct {
+            unchar          data[1];            /* data */
+        } general;
+        struct {
+            ushort          version;            /* driver version */
+        } drvers;
+        struct {
+            unchar          type;               /* controller type */
+            ushort          info;               /* slot etc. */
+            ushort          oem_id;             /* OEM ID */
+            ushort          bios_ver;           /* not used */
+            ushort          access;             /* not used */
+            ushort          ext_type;           /* extended type */
+        } ctrtype;
+        struct {
+            unchar          version;            /* OS version */
+            unchar          subversion;         /* OS subversion */
+            ushort          revision;           /* revision */
+        } osvers;
+        struct {
+            ushort          count;              /* controller count */
+        } ctrcnt;
+        struct {
+            int             handle;
+            unchar          evt[32];            /* event structure */
+        } event;
+    } iu;
+} gdth_iord_str;
+
+
+#endif
+
diff -u --recursive --new-file v2.1.62/linux/drivers/scsi/gdth_proc.c linux/drivers/scsi/gdth_proc.c
--- v2.1.62/linux/drivers/scsi/gdth_proc.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/scsi/gdth_proc.c	Tue Nov  4 09:17:30 1997
@@ -0,0 +1,635 @@
+/* gdth_proc.c 
+ * $Id: gdth_proc.c,v 1.6 1997/10/31 10:36:24 achim Exp $
+ */
+
+#include "gdth_ioctl.h"
+
+int gdth_proc_info(char *buffer,char **start,off_t offset,int length,   
+                   int hostno,int inout)
+{
+    int hanum,busnum,i;
+
+    TRACE2(("gdth_proc_info() length %d ha %d offs %d inout %d\n",
+            length,hostno,offset,inout));
+
+    for (i=0; i<gdth_ctr_vcount; ++i) {
+        if (gdth_ctr_vtab[i]->host_no == hostno)
+            break;
+    }
+    if (i==gdth_ctr_vcount)
+        return(-EINVAL);
+
+    hanum = NUMDATA(gdth_ctr_vtab[i])->hanum;
+    busnum= NUMDATA(gdth_ctr_vtab[i])->busnum;
+
+    if (inout)
+        return(gdth_set_info(buffer,length,i,hanum,busnum));
+    else
+        return(gdth_get_info(buffer,start,offset,length,i,hanum,busnum));
+}
+
+static int gdth_set_info(char *buffer,int length,int vh,int hanum,int busnum)
+{
+    int             ret_val;
+    Scsi_Cmnd       scp;
+    Scsi_Device     sdev;
+    gdth_iowr_str   *piowr;
+
+    TRACE2(("gdth_set_info() ha %d bus %d\n",hanum,busnum));
+    piowr = (gdth_iowr_str *)buffer;
+
+    memset(&sdev,0,sizeof(Scsi_Device));
+    memset(&scp, 0,sizeof(Scsi_Cmnd));
+    sdev.host = gdth_ctr_vtab[vh];
+    sdev.id = sdev.host->this_id;
+    scp.cmd_len = 12;
+    scp.host = gdth_ctr_vtab[vh];
+    scp.target = sdev.host->this_id;
+    scp.device = &sdev;
+    scp.use_sg = 0;
+
+    if (length >= 4) {
+        if (strncmp(buffer,"gdth",4) == 0) {
+            buffer += 5;
+            length -= 5;
+            ret_val = gdth_set_asc_info( buffer, length, hanum, scp );
+        } else if (piowr->magic == GDTIOCTL_MAGIC) {
+            ret_val = gdth_set_bin_info( buffer, length, hanum, scp );
+        } else {
+            printk("GDT: Wrong signature: %6s\n",buffer);
+            ret_val = -EINVAL;
+        }
+    } else {
+        ret_val = -EINVAL;
+    }
+    return ret_val;
+}
+         
+static int gdth_set_asc_info(char *buffer,int length,int hanum,Scsi_Cmnd scp)
+{
+    int             orig_length, drive, wb_mode;
+    char            cmnd[12];
+    int             i, j, found;
+    gdth_ha_str     *ha;
+    gdth_cmd_str    gdtcmd;
+    gdth_cpar_str   *pcpar;
+
+    TRACE2(("gdth_set_asc_info() ha %d\n",hanum));
+    ha = HADATA(gdth_ctr_tab[hanum]);
+    memset(cmnd, 0,10);
+    orig_length = length + 5;
+    drive = -1;
+    wb_mode = 0;
+    found = FALSE;
+
+    if (length >= 5 && strncmp(buffer,"flush",5)==0) {
+        buffer += 6;
+        length -= 6;
+        if (length && *buffer>='0' && *buffer<='9') {
+            drive = (int)(*buffer-'0');
+            ++buffer; --length;
+            if (length && *buffer>='0' && *buffer<='9') {
+                drive = drive*10 + (int)(*buffer-'0');
+                ++buffer; --length;
+            }
+            printk("GDT: Flushing host drive %d .. ",drive);
+        } else {
+            printk("GDT: Flushing all host drives .. ");
+        }
+        for (i = 0; i < MAXBUS; ++i) {
+            for (j = 0; j < MAXID; ++j) {
+                if (ha->id[i][j].type == CACHE_DTYP) {
+                    if (drive != -1 &&
+                        ha->id[i][j].hostdrive != (ushort)drive)
+                        continue;
+                    found = TRUE;
+                    gdtcmd.BoardNode = LOCALBOARD;
+                    gdtcmd.Service = CACHESERVICE;
+                    gdtcmd.OpCode = GDT_FLUSH;
+                    gdtcmd.u.cache.DeviceNo = ha->id[i][j].hostdrive;
+                    gdtcmd.u.cache.BlockNo = 1;
+                    gdtcmd.u.cache.sg_canz = 0;
+                    {
+                        struct semaphore sem = MUTEX_LOCKED;
+                        scp.request.rq_status = RQ_SCSI_BUSY;
+                        scp.request.sem = &sem;
+                        scsi_do_cmd(&scp, cmnd, &gdtcmd,
+                                    sizeof(gdth_cmd_str), gdth_scsi_done,
+                                    30*HZ, 1);
+                        down(&sem);
+                    }
+                }
+            }
+        }
+        if (!found)
+            printk("\nNo host drive found !\n");
+        else
+            printk("Done.\n");
+        return(orig_length);
+    }
+
+    if (length >= 7 && strncmp(buffer,"wbp_off",7)==0) {
+        buffer += 8;
+        length -= 8;
+        printk("GDT: Disabling write back permanently .. ");
+        wb_mode = 1;
+    } else if (length >= 6 && strncmp(buffer,"wbp_on",6)==0) {
+        buffer += 7;
+        length -= 7;
+        printk("GDT: Enabling write back permanently .. ");
+        wb_mode = 2;
+    } else if (length >= 6 && strncmp(buffer,"wb_off",6)==0) {
+        buffer += 7;
+        length -= 7;
+        printk("GDT: Disabling write back commands .. ");
+        if (ha->cache_feat & GDT_WR_THROUGH) {
+            gdth_write_through = TRUE;
+            printk("Done.\n");
+        } else {
+            printk("Not supported !\n");
+        }
+        return(orig_length);
+    } else if (length >= 5 && strncmp(buffer,"wb_on",5)==0) {
+        buffer += 6;
+        length -= 6;
+        printk("GDT: Enabling write back commands .. ");
+        gdth_write_through = FALSE;
+        printk("Done.\n");
+        return(orig_length);
+    }
+
+    if (wb_mode) {
+        pcpar = (gdth_cpar_str *)kmalloc( sizeof(gdth_cpar_str),
+            GFP_ATOMIC | GFP_DMA );
+        if (pcpar == NULL) {
+            TRACE2(("gdth_set_info(): Unable to allocate memory.\n"));
+            printk("Unable to allocate memory.\n");
+            return(-EINVAL);
+        }
+        memcpy( pcpar, &ha->cpar, sizeof(gdth_cpar_str) );
+        gdtcmd.BoardNode = LOCALBOARD;
+        gdtcmd.Service = CACHESERVICE;
+        gdtcmd.OpCode = GDT_IOCTL;
+        gdtcmd.u.ioctl.p_param = virt_to_bus(pcpar);
+        gdtcmd.u.ioctl.param_size = sizeof(gdth_cpar_str);
+        gdtcmd.u.ioctl.subfunc = CACHE_CONFIG;
+        gdtcmd.u.ioctl.channel = INVALID_CHANNEL;
+        pcpar->write_back = wb_mode==1 ? 0:1;
+        {
+            struct semaphore sem = MUTEX_LOCKED;
+            scp.request.rq_status = RQ_SCSI_BUSY;
+            scp.request.sem = &sem;
+            scsi_do_cmd(&scp, cmnd, &gdtcmd, sizeof(gdth_cmd_str),
+                        gdth_scsi_done, 30*HZ, 1);
+            down(&sem);
+        }
+        kfree( pcpar );
+        printk("Done.\n");
+        return(orig_length);
+    }
+
+    printk("GDT: Unknown command: %s  Length: %d\n",buffer,length);
+    return(-EINVAL);
+}
+
+static int gdth_set_bin_info(char *buffer,int length,int hanum,Scsi_Cmnd scp)
+{
+    char            cmnd[12];
+    int             id;
+    unchar          i, j, k, found;
+    gdth_ha_str     *ha;
+    gdth_iowr_str   *piowr;
+    gdth_iord_str   *piord;
+    gdth_cmd_str    *pcmd;
+    ulong           *ppadd;
+    ulong           add_size, flags;
+
+
+    TRACE2(("gdth_set_bin_info() ha %d\n",hanum));
+    ha = HADATA(gdth_ctr_tab[hanum]);
+    memset(cmnd, 0,10);
+    piowr = (gdth_iowr_str *)buffer;
+    piord = NULL;
+    pcmd = NULL;
+
+    if (length < GDTOFFSOF(gdth_iowr_str,iu))
+        return(-EINVAL);
+
+    switch (piowr->ioctl) {
+      case GDTIOCTL_GENERAL:
+        if (length < GDTOFFSOF(gdth_iowr_str,iu.general.data[0]))
+            return(-EINVAL);
+        pcmd = (gdth_cmd_str *)piowr->iu.general.command;
+        pcmd->Service = piowr->service;
+        if (pcmd->OpCode == GDT_IOCTL) {
+            ppadd = &pcmd->u.ioctl.p_param;
+            add_size = pcmd->u.ioctl.param_size;
+        } else if (piowr->service == CACHESERVICE) {
+            add_size = pcmd->u.cache.BlockCnt * SECTOR_SIZE;
+            if (ha->cache_feat & SCATTER_GATHER) {
+                ppadd = &pcmd->u.cache.sg_lst[0].sg_ptr;
+                pcmd->u.cache.DestAddr = -1UL;
+                pcmd->u.cache.sg_lst[0].sg_len = add_size;
+                pcmd->u.cache.sg_canz = 1;
+            } else {
+                ppadd = &pcmd->u.cache.DestAddr;
+                pcmd->u.cache.sg_canz = 0;
+            }
+        } else if (piowr->service == SCSIRAWSERVICE) {
+            add_size = pcmd->u.raw.sdlen;
+            if (ha->raw_feat & SCATTER_GATHER) {
+                ppadd = &pcmd->u.raw.sg_lst[0].sg_ptr;
+                pcmd->u.raw.sdata = -1UL;
+                pcmd->u.raw.sg_lst[0].sg_len = add_size;
+                pcmd->u.raw.sg_ranz = 1;
+            } else {
+                ppadd = &pcmd->u.raw.sdata;
+                pcmd->u.raw.sg_ranz = 0;
+            }
+        } else {
+            return(-EINVAL);
+        }
+        id = gdth_ioctl_alloc( hanum, sizeof(gdth_iord_str) + add_size );
+        if (id == -1)
+            return(-EBUSY);
+        piord = (gdth_iord_str *)gdth_ioctl_tab[id-1][hanum];
+
+        piord->size = sizeof(gdth_iord_str) + add_size;
+        if (add_size > 0) {
+            memcpy(piord->iu.general.data, piowr->iu.general.data, add_size);
+            *ppadd = virt_to_bus(piord->iu.general.data);
+        }
+        /* do IOCTL */
+        {
+            struct semaphore sem = MUTEX_LOCKED;
+            scp.request.rq_status = RQ_SCSI_BUSY;
+            scp.request.sem = &sem;
+            scp.SCp.this_residual = IOCTL_PRI;
+            scsi_do_cmd(&scp, cmnd, pcmd,
+                        sizeof(gdth_cmd_str), gdth_scsi_done,
+                        piowr->timeout*HZ, 1);
+            down(&sem);
+            piord->status = (ulong)scp.SCp.Message;
+        }
+        break;
+
+      case GDTIOCTL_DRVERS:
+        id = gdth_ioctl_alloc( hanum, sizeof(gdth_iord_str) );
+        if (id == -1)
+            return(-EBUSY);
+        piord = (gdth_iord_str *)gdth_ioctl_tab[id-1][hanum];
+        piord->size = sizeof(gdth_iord_str);
+        piord->status = S_OK;
+        piord->iu.drvers.version = (GDTH_VERSION<<8) | GDTH_SUBVERSION;
+        break;
+
+      case GDTIOCTL_CTRTYPE:
+        id = gdth_ioctl_alloc( hanum, sizeof(gdth_iord_str) );
+        if (id == -1)
+            return(-EBUSY);
+        piord = (gdth_iord_str *)gdth_ioctl_tab[id-1][hanum];
+        piord->size = sizeof(gdth_iord_str);
+        piord->status = S_OK;
+        if (ha->type == GDT_ISA || ha->type == GDT_EISA) {
+            piord->iu.ctrtype.type = (unchar)((ha->stype>>20) - 10);
+        } else if (ha->type != GDT_PCIMPR) {
+            piord->iu.ctrtype.type = (unchar)((ha->stype<<8) + 6);
+        } else {
+            piord->iu.ctrtype.type = 0xfe;
+            piord->iu.ctrtype.ext_type = 0x6000 | ha->stype;
+        }
+        piord->iu.ctrtype.info = ha->brd_phys;
+        piord->iu.ctrtype.oem_id = (ushort)GDT3_ID;
+        break;
+
+      case GDTIOCTL_CTRCNT:
+        id = gdth_ioctl_alloc( hanum, sizeof(gdth_iord_str) );
+        if (id == -1)
+            return(-EBUSY);
+        piord = (gdth_iord_str *)gdth_ioctl_tab[id-1][hanum];
+        piord->size = sizeof(gdth_iord_str);
+        piord->status = S_OK;
+        piord->iu.ctrcnt.count = (ushort)gdth_ctr_count;
+        break;
+
+      case GDTIOCTL_OSVERS:
+        id = gdth_ioctl_alloc( hanum, sizeof(gdth_iord_str) );
+        if (id == -1)
+            return(-EBUSY);
+        piord = (gdth_iord_str *)gdth_ioctl_tab[id-1][hanum];
+        piord->size = sizeof(gdth_iord_str);
+        piord->status = S_OK;
+        piord->iu.osvers.version = (unchar)(LINUX_VERSION_CODE >> 16);
+        piord->iu.osvers.subversion = (unchar)(LINUX_VERSION_CODE >> 8);
+        piord->iu.osvers.revision = (ushort)(LINUX_VERSION_CODE & 0xff);
+        break;
+
+      case GDTIOCTL_LOCKDRV:
+        id = gdth_ioctl_alloc( hanum, sizeof(gdth_iord_str) );
+        if (id == -1)
+            return(-EBUSY);
+        piord = (gdth_iord_str *)gdth_ioctl_tab[id-1][hanum];
+        for (i = k = 0; i < piowr->iu.lockdrv.drive_cnt; ++i) {
+            found = FALSE;
+            for (j = 0; j < ha->bus_cnt; ++j) {
+                for (k = 0; k < MAXID; ++k) {
+                    if (ha->id[j][k].type == CACHE_DTYP &&
+                        ha->id[j][k].hostdrive == piowr->iu.lockdrv.drives[i]) {
+                        found = TRUE;
+                        break;
+                    }
+                }
+                if (found)
+                    break;
+            }
+            if (!found)
+                continue;
+
+            if (piowr->iu.lockdrv.lock) {
+                save_flags( flags );
+                cli();
+                ha->id[j][k].lock = 1;
+                restore_flags( flags );
+                gdth_wait_completion( hanum, j, k );
+                gdth_stop_timeout( hanum, j, k );
+            } else {
+                save_flags( flags );
+                cli();
+                ha->id[j][k].lock = 0;
+                restore_flags( flags );
+                gdth_start_timeout( hanum, j, k );
+                gdth_next( hanum );
+            }
+        }
+        piord->size = sizeof(gdth_iord_str);
+        piord->status = S_OK;
+        break;
+
+      case GDTIOCTL_LOCKCHN:
+        id = gdth_ioctl_alloc( hanum, sizeof(gdth_iord_str) );
+        if (id == -1)
+            return(-EBUSY);
+        for (k = 0, j = piowr->iu.lockchn.channel; k < MAXID; ++k) {
+            if (ha->id[j][k].type != RAW_DTYP)
+                continue;
+
+            if (piowr->iu.lockchn.lock) {
+                save_flags( flags );
+                cli();
+                ha->id[j][k].lock = 1;
+                restore_flags( flags );
+                gdth_wait_completion( hanum, j, k );
+                gdth_stop_timeout( hanum, j, k );
+            } else {
+                save_flags( flags );
+                cli();
+                ha->id[j][k].lock = 0;
+                restore_flags( flags );
+                gdth_start_timeout( hanum, j, k );
+                gdth_next( hanum );
+            }
+        }
+        piord = (gdth_iord_str *)gdth_ioctl_tab[id-1][hanum];
+        piord->size = sizeof(gdth_iord_str);
+        piord->status = S_OK;
+        break;
+
+      case GDTIOCTL_EVENT:
+        id = gdth_ioctl_alloc( hanum, sizeof(gdth_iord_str) );
+        if (id == -1)
+            return(-EBUSY);
+        piord = (gdth_iord_str *)gdth_ioctl_tab[id-1][hanum];
+        if (piowr->iu.event.erase == 0) {
+            piord->iu.event.handle = gdth_read_event( piowr->iu.event.handle,
+                (gdth_evt_str *)piord->iu.event.evt );
+        } else {
+            piord->iu.event.handle = piowr->iu.event.handle;
+            gdth_readapp_event( (unchar)piowr->iu.event.erase,
+                (gdth_evt_str *)piord->iu.event.evt );
+        }
+        piord->size = sizeof(gdth_iord_str);
+        piord->status = S_OK;
+        break;
+
+      default:
+        return(-EINVAL);
+    }
+    /* we return a buffer ID to detect the right buffer during READ-IOCTL */
+    return id;
+}
+
+static int gdth_get_info(char *buffer,char **start,off_t offset,
+                         int length,int vh,int hanum,int busnum)
+{
+    int size = 0,len = 0;
+    off_t begin = 0,pos = 0;
+    gdth_ha_str *ha;
+    gdth_iord_str *piord;
+    int id;
+
+    TRACE2(("gdth_get_info() ha %d bus %d\n",hanum,busnum));
+    ha = HADATA(gdth_ctr_tab[hanum]);
+    id = length;
+
+    /* look for buffer ID in length */
+    if (id > 4) {
+#if LINUX_VERSION_CODE >= 0x020000
+        size = sprintf(buffer+len,
+                       "%s SCSI Disk Array Controller\n",
+                       gdth_ctr_name(hanum));
+#else
+        size = sprintf(buffer+len,
+                       "%s SCSI Disk Array Controller (SCSI Bus %d)\n",
+                       gdth_ctr_name(hanum),busnum);
+#endif
+        len += size;  pos = begin + len;
+        size = sprintf(buffer+len,
+                       "Firmware Version: %d.%2d\tDriver Version: %s\n",
+                       (unchar)(ha->cpar.version>>8),
+                       (unchar)(ha->cpar.version),GDTH_VERSION_STR);
+        len += size;  pos = begin + len;
+ 
+        if (pos < offset) {
+            len = 0;
+            begin = pos;
+        }
+        if (pos > offset + length)
+            goto stop_output;
+
+    } else {
+        piord = (gdth_iord_str *)gdth_ioctl_tab[id-1][hanum];
+        if (piord == NULL)
+            goto stop_output;
+        length = piord->size;
+        memcpy(buffer+len, (char *)piord, length);
+        gdth_ioctl_free(hanum, id);
+        len += length; pos = begin + len;
+
+        if (pos < offset) {
+            len = 0;
+            begin = pos;
+        }
+        if (pos > offset + length)
+            goto stop_output;
+    }
+
+stop_output:
+    *start = buffer +(offset-begin);
+    len -= (offset-begin);
+    if (len > length)
+        len = length;
+    TRACE2(("get_info() len %d pos %d begin %d offset %d length %d size %d\n",
+            len,pos,begin,offset,length,size));
+    return(len);
+}
+
+
+void gdth_scsi_done(Scsi_Cmnd *scp)
+{
+    TRACE2(("gdth_scsi_done()\n"));
+
+    scp->request.rq_status = RQ_SCSI_DONE;
+
+    if (scp->request.sem != NULL)
+        up(scp->request.sem);
+}
+
+static int gdth_ioctl_alloc(int hanum, ushort size)
+{
+    ulong flags;
+    int i;
+
+    if (size == 0)
+        return -1;
+
+    save_flags(flags);
+    cli();
+
+    for (i = 0; i < 4; ++i) {
+        if (gdth_ioctl_tab[i][hanum] == NULL) {
+            gdth_ioctl_tab[i][hanum] = kmalloc( size, GFP_ATOMIC | GFP_DMA );
+            break;
+        }
+    }
+
+    restore_flags(flags);
+    if (i == 4 || gdth_ioctl_tab[i][hanum] == NULL)
+        return -1;
+    return (i+1);
+}
+
+static void gdth_ioctl_free(int hanum, int idx)
+{
+    ulong flags;
+
+    save_flags(flags);
+    cli();
+
+    kfree( gdth_ioctl_tab[idx-1][hanum] );
+    gdth_ioctl_tab[idx-1][hanum] = NULL;
+
+    restore_flags(flags);
+}
+
+static void gdth_wait_completion(int hanum, int busnum, int id)
+{
+    ulong flags;
+    int i;
+    Scsi_Cmnd *scp;
+
+    save_flags(flags);
+    cli();
+
+    for (i = 0; i < GDTH_MAXCMDS; ++i) {
+        scp = gdth_cmd_tab[i][hanum].cmnd;
+        if (!SPECIAL_SCP(scp) && scp->target == (unchar)id &&
+#if LINUX_VERSION_CODE >= 0x020000
+            scp->channel == (unchar)busnum)
+#else
+            NUMDATA(scp->host)->busnum == (unchar)busnum)
+#endif
+        {
+            restore_flags(flags);
+            while (!scp->SCp.have_data_in)
+                barrier();
+            save_flags(flags);
+            cli();
+        }
+    }
+    restore_flags(flags);
+}
+
+static void gdth_stop_timeout(int hanum, int busnum, int id)
+{
+    ulong flags;
+    Scsi_Cmnd *scp;
+    gdth_ha_str *ha;
+
+    save_flags(flags);
+    cli();
+    ha = HADATA(gdth_ctr_tab[hanum]);
+
+    for (scp = ha->req_first; scp; scp = (Scsi_Cmnd *)scp->SCp.ptr) {
+        if (scp->target == (unchar)id &&
+#if LINUX_VERSION_CODE >= 0x020000
+            scp->channel == (unchar)busnum)
+#else
+            NUMDATA(scp->host)->busnum == (unchar)busnum)
+#endif
+        {
+            TRACE2(("gdth_stop_timeout(): update_timeout()\n"));
+            scp->SCp.buffers_residual = gdth_update_timeout(scp, 0);
+        }
+    }
+    restore_flags(flags);
+}
+
+static void gdth_start_timeout(int hanum, int busnum, int id)
+{
+    ulong flags;
+    Scsi_Cmnd *scp;
+    gdth_ha_str *ha;
+
+    save_flags(flags);
+    cli();
+    ha = HADATA(gdth_ctr_tab[hanum]);
+
+    for (scp = ha->req_first; scp; scp = (Scsi_Cmnd *)scp->SCp.ptr) {
+        if (scp->target == (unchar)id &&
+#if LINUX_VERSION_CODE >= 0x020000
+            scp->channel == (unchar)busnum)
+#else
+            NUMDATA(scp->host)->busnum == (unchar)busnum)
+#endif
+        {
+            TRACE2(("gdth_start_timeout(): update_timeout()\n"));
+            gdth_update_timeout(scp, scp->SCp.buffers_residual);
+        }
+    }
+    restore_flags(flags);
+}
+
+static int gdth_update_timeout(Scsi_Cmnd *scp, int timeout)
+{
+    ulong flags;
+    int oldto;
+
+    save_flags(flags);
+    cli();
+
+    oldto = scp->timeout;
+    scp->timeout = timeout;
+    if (timeout > 0) {
+        if (timer_table[SCSI_TIMER].expires == 0) {
+            timer_table[SCSI_TIMER].expires = jiffies + timeout;
+            timer_active |= 1 << SCSI_TIMER;
+        } else {
+            if (jiffies + timeout < timer_table[SCSI_TIMER].expires)
+                timer_table[SCSI_TIMER].expires = jiffies + timeout;
+        }
+    }
+
+    restore_flags(flags);
+    return oldto;
+}
+
diff -u --recursive --new-file v2.1.62/linux/drivers/scsi/gdth_proc.h linux/drivers/scsi/gdth_proc.h
--- v2.1.62/linux/drivers/scsi/gdth_proc.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/scsi/gdth_proc.h	Tue Nov  4 09:17:30 1997
@@ -0,0 +1,24 @@
+#ifndef _GDTH_PROC_H
+#define _GDTH_PROC_H
+
+/* gdth_proc.h 
+ * $Id: gdth_proc.h,v 1.2 1997/02/21 08:08:51 achim Exp $
+ */
+
+static int gdth_set_info(char *buffer,int length,int vh,int hanum,int busnum);
+static int gdth_set_asc_info(char *buffer,int length,int hanum,Scsi_Cmnd scp);
+static int gdth_set_bin_info(char *buffer,int length,int hanum,Scsi_Cmnd scp);
+static int gdth_get_info(char *buffer,char **start,off_t offset,
+                         int length,int vh,int hanum,int busnum);
+
+static int gdth_ioctl_alloc(int hanum, ushort size);
+static void gdth_ioctl_free(int hanum, int id);
+static void gdth_wait_completion(int hanum, int busnum, int id);
+static void gdth_stop_timeout(int hanum, int busnum, int id);
+static void gdth_start_timeout(int hanum, int busnum, int id);
+static int gdth_update_timeout(Scsi_Cmnd *scp, int timeout);
+
+void gdth_scsi_done(Scsi_Cmnd *scp);
+
+#endif
+
diff -u --recursive --new-file v2.1.62/linux/drivers/scsi/hosts.c linux/drivers/scsi/hosts.c
--- v2.1.62/linux/drivers/scsi/hosts.c	Mon Aug 18 18:19:46 1997
+++ linux/drivers/scsi/hosts.c	Wed Nov  5 13:17:26 1997
@@ -194,6 +194,22 @@
 #include "mac53c94.h"
 #endif
 
+#ifdef CONFIG_SCSI_GDTH
+#include "gdth.h"
+#endif
+
+#ifdef CONFIG_SCSI_PCI2000
+#include "pci2000.h"
+#endif
+
+#ifdef CONFIG_SCSI_PCI2220I
+#include "pci2220i.h"
+#endif
+
+#ifdef CONFIG_SCSI_PSI240I
+#include "psi240i.h"
+#endif
+
 #ifdef CONFIG_SCSI_DEBUG
 #include "scsi_debug.h"
 #endif
@@ -256,6 +272,17 @@
 #ifdef CONFIG_SCSI_ADVANSYS
 	ADVANSYS,
 #endif
+
+#ifdef CONFIG_SCSI_PCI2000
+        PCI2000,
+#endif
+#ifdef CONFIG_SCSI_PCI2220I
+        PCI2220I,
+#endif
+#ifdef CONFIG_SCSI_PSI240I
+        PSI240I,
+#endif
+
 /* BusLogic must come before aha1542.c */
 #ifdef CONFIG_SCSI_BUSLOGIC
     BUSLOGIC,
@@ -340,6 +367,9 @@
 #endif
 #ifdef CONFIG_SCSI_SUNESP
     SCSI_SPARC_ESP,
+#endif
+#ifdef CONFIG_SCSI_GDTH
+    GDTH,
 #endif
 #ifdef CONFIG_SCSI_QLOGICPTI
     QLOGICPTI,
diff -u --recursive --new-file v2.1.62/linux/drivers/scsi/pci2000.c linux/drivers/scsi/pci2000.c
--- v2.1.62/linux/drivers/scsi/pci2000.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/scsi/pci2000.c	Wed Nov  5 12:59:49 1997
@@ -0,0 +1,660 @@
+/*+M*************************************************************************
+ * Perceptive Solutions, Inc. PCI-2000 device driver proc support for Linux.
+ *
+ * Copyright (c) 1997 Perceptive Solutions, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING.  If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *
+ *	File Name:		pci2000i.c
+ *
+ *-M*************************************************************************/
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/head.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/bios32.h>
+#include <linux/pci.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/proc_fs.h>
+#include <asm/dma.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#include <linux/blk.h>
+#include "scsi.h"
+#include "hosts.h"
+
+#include "pci2000.h"
+#include "psi_roy.h"
+
+#include<linux/stat.h>
+
+struct proc_dir_entry Proc_Scsi_Pci2000 =
+	{ PROC_SCSI_PCI2000, 7, "pci2000", S_IFDIR | S_IRUGO | S_IXUGO, 2 };
+
+//#define DEBUG 1
+
+#ifdef DEBUG
+#define DEB(x) x
+#define STOP_HERE	{int st;for(st=0;st<100;st++){st=1;}}
+#else
+#define DEB(x)
+#define STOP_HERE
+#endif
+
+typedef struct
+	{
+	ULONG		address;
+	ULONG		length;
+	}	SCATGATH, *PSCATGATH;
+
+typedef struct
+	{
+	Scsi_Cmnd	*SCpnt;
+	SCATGATH	 scatGath[16];
+	UCHAR		 tag;
+	}	DEV2000, *PDEV2000;
+
+typedef struct
+	{
+	USHORT		 basePort;
+	USHORT		 mb0;
+	USHORT		 mb1;
+	USHORT		 mb2;
+	USHORT		 mb3;
+	USHORT		 mb4;
+	USHORT		 cmd;
+	USHORT		 tag;
+	DEV2000	 	 dev[MAX_BUS][MAX_UNITS];
+	}	ADAPTER2000, *PADAPTER2000;
+
+#define HOSTDATA(host) ((PADAPTER2000)&host->hostdata)
+
+
+static struct	Scsi_Host 	   *PsiHost[MAXADAPTER] = {NULL,};  // One for each adapter
+static			int				NumAdapters = 0;
+
+/****************************************************************
+ *	Name:			WaitReady	:LOCAL
+ *
+ *	Description:	Wait for controller ready.
+ *
+ *	Parameters:		padapter - Pointer adapter data structure.
+ *
+ *	Returns:		TRUE on not ready.
+ *
+ ****************************************************************/
+static int WaitReady (PADAPTER2000 padapter)
+	{
+	ULONG	timer;
+
+	timer = jiffies + TIMEOUT_COMMAND;								// calculate the timeout value
+	do	{
+		if ( !inb_p (padapter->cmd) )
+			return FALSE;
+		}	while ( timer > jiffies );									// test for timeout
+	return TRUE;
+	}
+/****************************************************************
+ *	Name:	OpDone	:LOCAL
+ *
+ *	Description:	Clean up operation and issue done to caller.
+ *
+ *	Parameters:		SCpnt	- Pointer to SCSI command structure.
+ *					status	- Caller status.
+ *
+ *	Returns:		Nothing.
+ *
+ ****************************************************************/
+static void OpDone (Scsi_Cmnd *SCpnt, ULONG status)
+	{
+	SCpnt->result = status;
+	SCpnt->scsi_done (SCpnt);
+	}
+/****************************************************************
+ *	Name:	Command		:LOCAL
+ *
+ *	Description:	Issue queued command to the PCI-2000.
+ *
+ *	Parameters:		padapter - Pointer to adapter information structure.
+ *					cmd		 - PCI-2000 command byte.
+ *
+ *	Returns:		Non-zero command tag if operation is accepted.
+ *
+ ****************************************************************/
+static UCHAR Command (PADAPTER2000 padapter, UCHAR cmd)
+	{
+	outb_p (cmd, padapter->cmd);
+	if ( WaitReady (padapter) )
+		return 0;
+
+	if ( inw_p (padapter->mb0) )
+		return 0;
+
+	return inb_p (padapter->mb1);
+	}
+/****************************************************************
+ *	Name:	BuildSgList		:LOCAL
+ *
+ *	Description:	Build the scatter gather list for controller.
+ *
+ *	Parameters:		SCpnt	 - Pointer to SCSI command structure.
+ *					padapter - Pointer to adapter information structure.
+ *					pdev	 - Pointer to adapter device structure.
+ *
+ *	Returns:		Non-zero in not scatter gather.
+ *
+ ****************************************************************/
+static int BuildSgList (Scsi_Cmnd *SCpnt, PADAPTER2000 padapter, PDEV2000 pdev)
+	{
+	int	z;
+
+	if ( SCpnt->use_sg )
+		{
+		for ( z = 0;  z < SCpnt->use_sg;  z++ )
+			{
+			pdev->scatGath[z].address = virt_to_bus (((struct scatterlist *)SCpnt->request_buffer)[z].address);
+			pdev->scatGath[z].length = ((struct scatterlist *)SCpnt->request_buffer)[z].length;
+			}
+		outl (virt_to_bus (pdev->scatGath), padapter->mb2);
+		outl ((SCpnt->use_sg << 24) | SCpnt->request_bufflen, padapter->mb3);
+		return FALSE;
+		}
+	outl (virt_to_bus (SCpnt->request_buffer), padapter->mb2);
+	outl (SCpnt->request_bufflen, padapter->mb3);
+	return TRUE;
+	}
+/****************************************************************
+ *	Name:	Irq_Handler	:LOCAL
+ *
+ *	Description:	Interrupt handler.
+ *
+ *	Parameters:		irq		- Hardware IRQ number.
+ *					dev_id	-
+ *					regs	-
+ *
+ *	Returns:		TRUE if drive is not ready in time.
+ *
+ ****************************************************************/
+static void Irq_Handler (int irq, void *dev_id, struct pt_regs *regs)
+	{
+	struct Scsi_Host   *shost = NULL;	// Pointer to host data block
+	PADAPTER2000		padapter;		// Pointer to adapter control structure
+	PDEV2000			pdev;
+	Scsi_Cmnd		   *SCpnt;
+	UCHAR				tag = 0;
+	UCHAR				tag0;
+	ULONG				error;
+	int					pun;
+	int					bus;
+	int					z;
+
+	DEB(printk ("\npci2000 recieved interrupt "));
+	for ( z = 0; z < NumAdapters;  z++ )										// scan for interrupt to process
+		{
+		if ( PsiHost[z]->irq == (UCHAR)(irq & 0xFF) )
+			{
+			tag = inb_p (HOSTDATA(PsiHost[z])->tag);
+			if (  tag )
+				{
+				shost = PsiHost[z];
+				break;
+				}
+			}
+		}
+
+	if ( !shost )
+		{
+		DEB (printk ("\npci2000: not my interrupt"));
+		return;
+		}
+
+	padapter = HOSTDATA(shost);
+
+	tag0 = tag & 0x7F;															// mask off the error bit
+	for ( bus = 0;  bus < MAX_BUS;  bus++ )										// scan the busses
+    	{
+		for ( pun = 0;  pun < MAX_UNITS;  pun++ )								// scan the targets
+    		{
+			pdev = &padapter->dev[bus][pun];
+			if ( !pdev->tag )
+    			continue;
+			if ( pdev->tag == tag0 )											// is this it?
+				{
+				pdev->tag = 0;
+				SCpnt = pdev->SCpnt;
+				goto irqProceed;
+    			}
+			}
+    	}
+
+	outb_p (0xFF, padapter->tag);												// clear the op interrupt
+	outb_p (CMD_DONE, padapter->cmd);											// complete the op
+	return;																		// done, but, with what?
+
+irqProceed:;
+	if ( tag & ERR08_TAGGED )												// is there an error here?
+		{
+		if ( WaitReady (padapter) )
+			{
+			OpDone (SCpnt, DID_TIME_OUT << 16);
+			return;
+			}
+
+		outb_p (tag0, padapter->mb0);										// get real error code
+		outb_p (CMD_ERROR, padapter->cmd);
+		if ( WaitReady (padapter) )											// wait for controller to suck up the op
+			{
+			OpDone (SCpnt, DID_TIME_OUT << 16);
+			return;
+			}
+
+		error = inl (padapter->mb0);										// get error data
+		outb_p (0xFF, padapter->tag);										// clear the op interrupt
+		outb_p (CMD_DONE, padapter->cmd);									// complete the op
+
+		DEB (printk ("status: %lX ", error));
+		if ( error == 0x00020002 )											// is this error a check condition?
+			{
+			if ( bus )														// are we doint SCSI commands?
+				{
+				OpDone (SCpnt, (DID_OK << 16) | 2);
+				return;
+				}
+			if ( *SCpnt->cmnd == SCSIOP_TEST_UNIT_READY )
+				OpDone (SCpnt, (DRIVER_SENSE << 24) | (DID_OK << 16) | 2);	// test caller we have sense data too
+			else
+				OpDone (SCpnt, DID_ERROR << 16);
+			return;
+			}
+		OpDone (SCpnt, DID_ERROR << 16);
+		return;
+		}
+
+	outb_p (0xFF, padapter->tag);											// clear the op interrupt
+	outb_p (CMD_DONE, padapter->cmd);										// complete the op
+	OpDone (SCpnt, DID_OK << 16);
+	}
+/****************************************************************
+ *	Name:	Pci2220i_QueueCommand
+ *
+ *	Description:	Process a queued command from the SCSI manager.
+ *
+ *	Parameters:		SCpnt - Pointer to SCSI command structure.
+ *					done  - Pointer to done function to call.
+ *
+ *	Returns:		Status code.
+ *
+ ****************************************************************/
+int Pci2000_QueueCommand (Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *))
+	{
+	UCHAR		   *cdb = (UCHAR *)SCpnt->cmnd;					// Pointer to SCSI CDB
+	PADAPTER2000	padapter = HOSTDATA(SCpnt->host);			// Pointer to adapter control structure
+	int				rc		 = -1;								// command return code
+	UCHAR			bus		 = SCpnt->channel;
+	UCHAR			pun		 = SCpnt->target;
+	UCHAR			lun		 = SCpnt->lun;
+	UCHAR			cmd;
+	PDEV2000		pdev	 = &padapter->dev[bus][pun];
+
+	if ( !done )
+		{
+		printk("pci2000_queuecommand: %02X: done can't be NULL\n", *cdb);
+		return 0;
+		}
+
+	SCpnt->scsi_done = done;
+	pdev->SCpnt = SCpnt;  									// Save this command data
+
+	if ( WaitReady (padapter) )
+		{
+		rc = DID_ERROR;
+		goto finished;
+		}
+
+	outw_p (pun | (lun << 8), padapter->mb0);
+
+	if ( bus )
+		{
+		DEB (if(*cdb) printk ("\nCDB: %X-  %X %X %X %X %X %X %X %X %X %X ", SCpnt->cmd_len, cdb[0], cdb[1], cdb[2], cdb[3], cdb[4], cdb[5], cdb[6], cdb[7], cdb[8], cdb[9]));
+		DEB (if(*cdb) printk ("\ntimeout_per_command: %d, timeout_total: %d, timeout: %d, internal_timout: %d", SCpnt->timeout_per_command,
+							  SCpnt->timeout_total, SCpnt->timeout, SCpnt->internal_timeout));
+		outl (SCpnt->timeout_per_command, padapter->mb1);
+		outb_p (CMD_SCSI_TIMEOUT, padapter->cmd);
+		if ( WaitReady (padapter) )
+			{
+			rc = DID_ERROR;
+			goto finished;
+			}
+
+		outw_p (pun | (lun << 8), padapter->mb0);
+		outw_p (SCpnt->cmd_len << 8, padapter->mb0 + 2);
+		outl (virt_to_bus (cdb), padapter->mb1);
+		if ( BuildSgList (SCpnt, padapter, pdev) )
+			cmd = CMD_SCSI_THRU;
+		else
+			cmd = CMD_SCSI_THRU_SG;
+		if ( (pdev->tag = Command (padapter, cmd)) == 0 )
+			rc = DID_TIME_OUT;
+		goto finished;
+		}
+	else
+		{
+		if ( lun )
+			{
+			rc = DID_BAD_TARGET;
+			goto finished;
+			}
+		}
+
+	switch ( *cdb )
+		{
+		case SCSIOP_INQUIRY:   					// inquiry CDB
+			{
+			if ( SCpnt->use_sg )
+				{
+				outl (virt_to_bus (((struct scatterlist *)(SCpnt->request_buffer))->address), padapter->mb2);
+				}
+			else
+				{
+				outl (virt_to_bus (SCpnt->request_buffer), padapter->mb2);
+				}
+			outl (SCpnt->request_bufflen, padapter->mb3);
+			cmd = CMD_DASD_SCSI_INQ;
+			break;
+			}
+
+		case SCSIOP_TEST_UNIT_READY:			// test unit ready CDB
+			outl (virt_to_bus (SCpnt->sense_buffer), padapter->mb2);
+			outl (sizeof (SCpnt->sense_buffer), padapter->mb3);
+			cmd = CMD_TEST_READY;
+			break;
+
+		case SCSIOP_READ_CAPACITY:			  	// read capctiy CDB
+			if ( SCpnt->use_sg )
+				{
+				outl (virt_to_bus (((struct scatterlist *)(SCpnt->request_buffer))->address), padapter->mb2);
+				}
+			else
+				{
+				outl (virt_to_bus (SCpnt->request_buffer), padapter->mb2);
+				}
+			outl (8, padapter->mb3);
+			cmd = CMD_DASD_CAP;
+			break;
+		case SCSIOP_VERIFY:						// verify CDB
+			outw_p ((USHORT)cdb[8] | ((USHORT)cdb[7] << 8), padapter->mb0 + 2);
+			outl (XSCSI2LONG (&cdb[2]), padapter->mb1);
+			cmd = CMD_READ_SG;
+			break;
+		case SCSIOP_READ:						// read10 CDB
+			outw_p ((USHORT)cdb[8] | ((USHORT)cdb[7] << 8), padapter->mb0 + 2);
+			outl (XSCSI2LONG (&cdb[2]), padapter->mb1);
+			if ( BuildSgList (SCpnt, padapter, pdev) )
+				cmd = CMD_READ;
+			else
+				cmd = CMD_READ_SG;
+			break;
+		case SCSIOP_READ6:						// read6  CDB
+			outw_p (cdb[4], padapter->mb0 + 2);
+			outl ((SCSI2LONG (&cdb[1])) & 0x001FFFFF, padapter->mb1);
+			if ( BuildSgList (SCpnt, padapter, pdev) )
+				cmd = CMD_READ;
+			else
+				cmd = CMD_READ_SG;
+			break;
+		case SCSIOP_WRITE:						// write10 CDB
+			outw_p ((USHORT)cdb[8] | ((USHORT)cdb[7] << 8), padapter->mb0 + 2);
+			outl (XSCSI2LONG (&cdb[2]), padapter->mb1);
+			if ( BuildSgList (SCpnt, padapter, pdev) )
+				cmd = CMD_WRITE;
+			else
+				cmd = CMD_WRITE_SG;
+			break;
+		case SCSIOP_WRITE6:						// write6  CDB
+			outw_p (cdb[4], padapter->mb0 + 2);
+			outl ((SCSI2LONG (&cdb[1])) & 0x001FFFFF, padapter->mb1);
+			if ( BuildSgList (SCpnt, padapter, pdev) )
+				cmd = CMD_WRITE;
+			else
+				cmd = CMD_WRITE_SG;
+			break;
+		case SCSIOP_START_STOP_UNIT:
+			cmd = CMD_EJECT_MEDIA;
+			break;
+		case SCSIOP_MEDIUM_REMOVAL:
+			switch ( cdb[4] )
+				{
+				case 0:
+					cmd = CMD_UNLOCK_DOOR;
+					break;
+				case 1:
+					cmd = CMD_LOCK_DOOR;
+					break;
+				default:
+					cmd = 0;
+					break;
+				}
+			if ( cmd )
+				break;
+		default:
+			DEB (printk ("pci2220i_queuecommand: Unsupported command %02X\n", *cdb));
+			OpDone (SCpnt, DID_ERROR << 16);
+			return 0;
+		}
+
+	if ( (pdev->tag = Command (padapter, cmd)) == 0 )
+		rc = DID_TIME_OUT;
+finished:;
+	if ( rc != -1 )
+		OpDone (SCpnt, rc << 16);
+	return 0;
+	}
+/****************************************************************
+ *	Name:	internal_done :LOCAL
+ *
+ *	Description:	Done handler for non-queued commands
+ *
+ *	Parameters:		SCpnt - Pointer to SCSI command structure.
+ *
+ *	Returns:		Nothing.
+ *
+ ****************************************************************/
+static void internal_done (Scsi_Cmnd * SCpnt)
+	{
+	SCpnt->SCp.Status++;
+	}
+/****************************************************************
+ *	Name:	Pci2220i_Command
+ *
+ *	Description:	Process a command from the SCSI manager.
+ *
+ *	Parameters:		SCpnt - Pointer to SCSI command structure.
+ *
+ *	Returns:		Status code.
+ *
+ ****************************************************************/
+int Pci2000_Command (Scsi_Cmnd *SCpnt)
+	{
+	DEB(printk("pci2000_command: ..calling pci2000_queuecommand\n"));
+
+	Pci2000_QueueCommand (SCpnt, internal_done);
+
+    SCpnt->SCp.Status = 0;
+	while (!SCpnt->SCp.Status)
+		barrier ();
+	return SCpnt->result;
+	}
+/****************************************************************
+ *	Name:	Pci2220i_Detect
+ *
+ *	Description:	Detect and initialize our boards.
+ *
+ *	Parameters:		tpnt - Pointer to SCSI host template structure.
+ *
+ *	Returns:		Number of adapters found.
+ *
+ ****************************************************************/
+int Pci2000_Detect (Scsi_Host_Template *tpnt)
+	{
+	int					pci_index = 0;
+	struct Scsi_Host   *pshost;
+	PADAPTER2000	    padapter;
+	int					z;
+	int					setirq;
+
+	if ( pcibios_present () )
+		{
+		for ( pci_index = 0;  pci_index <= MAXADAPTER;  ++pci_index )
+			{
+			UCHAR	pci_bus, pci_device_fn;
+
+			if ( pcibios_find_device (VENDOR_PSI, DEVICE_ROY_1, pci_index, &pci_bus, &pci_device_fn) != 0 )
+				break;
+
+			pshost = scsi_register (tpnt, sizeof(ADAPTER2000));
+			padapter = HOSTDATA(pshost);
+
+			pcibios_read_config_word (pci_bus, pci_device_fn, PCI_BASE_ADDRESS_1, &padapter->basePort);
+			padapter->basePort &= 0xFFFE;
+			DEB (printk ("\nBase Regs = %#04X", padapter->basePort));			// get the base I/O port address
+			padapter->mb0	= padapter->basePort + RTR_MAILBOX;		   			// get the 32 bit mail boxes
+			padapter->mb1	= padapter->basePort + RTR_MAILBOX + 4;
+			padapter->mb2	= padapter->basePort + RTR_MAILBOX + 8;
+			padapter->mb3	= padapter->basePort + RTR_MAILBOX + 12;
+			padapter->mb4	= padapter->basePort + RTR_MAILBOX + 16;
+			padapter->cmd	= padapter->basePort + RTR_LOCAL_DOORBELL;			// command register
+			padapter->tag	= padapter->basePort + RTR_PCI_DOORBELL;			// tag/response register
+
+			if ( WaitReady (padapter) )
+				goto unregister;
+			outb_p (0x84, padapter->mb0);
+			outb_p (CMD_SPECIFY, padapter->cmd);
+			if ( WaitReady (padapter) )
+				goto unregister;
+
+			pcibios_read_config_byte (pci_bus, pci_device_fn, PCI_INTERRUPT_LINE, &pshost->irq);
+			setirq = 1;
+			for ( z = 0;  z < pci_index;  z++ )											// scan for shared interrupts
+				{
+				if ( PsiHost[z]->irq == pshost->irq )						// if shared then, don't posses
+					setirq = 0;
+				}
+			if ( setirq )																// if not shared, posses
+				{
+				if ( request_irq (pshost->irq, Irq_Handler, 0, "pci2000", NULL) )
+					{
+					printk ("Unable to allocate IRQ for PSI-2000 controller.\n");
+					goto unregister;
+					}
+				}
+			PsiHost[pci_index]	= pshost;												// save SCSI_HOST pointer
+
+			pshost->unique_id	= padapter->basePort;
+			pshost->max_id		= 16;
+			pshost->max_channel	= 1;
+
+			printk("\nPSI-2000 EIDE CONTROLLER: at I/O = %X  IRQ = %d\n", padapter->basePort, pshost->irq);
+			printk("(C) 1997 Perceptive Solutions, Inc. All rights reserved\n\n");
+			continue;
+unregister:;
+			scsi_unregister (pshost);
+			}
+		}
+	NumAdapters = pci_index;
+	return pci_index;
+	}
+/****************************************************************
+ *	Name:	Pci2220i_Abort
+ *
+ *	Description:	Process the Abort command from the SCSI manager.
+ *
+ *	Parameters:		SCpnt - Pointer to SCSI command structure.
+ *
+ *	Returns:		Allways snooze.
+ *
+ ****************************************************************/
+int Pci2000_Abort (Scsi_Cmnd *SCpnt)
+	{
+	DEB (printk ("pci2000_abort\n"));
+	return SCSI_ABORT_SNOOZE;
+	}
+/****************************************************************
+ *	Name:	Pci2220i_Reset
+ *
+ *	Description:	Process the Reset command from the SCSI manager.
+ *
+ *	Parameters:		SCpnt - Pointer to SCSI command structure.
+ *					flags - Flags about the reset command
+ *
+ *	Returns:		No active command at this time, so this means
+ *					that each time we got some kind of response the
+ *					last time through.  Tell the mid-level code to
+ *					request sense information in order to decide what
+ *					to do next.
+ *
+ ****************************************************************/
+int Pci2000_Reset (Scsi_Cmnd *SCpnt, unsigned int reset_flags)
+	{
+	return SCSI_RESET_PUNT;
+	}
+
+#include "sd.h"
+
+/****************************************************************
+ *	Name:	Pci2220i_BiosParam
+ *
+ *	Description:	Process the biosparam request from the SCSI manager to
+ *					return C/H/S data.
+ *
+ *	Parameters:		disk - Pointer to SCSI disk structure.
+ *					dev	 - Major/minor number from kernel.
+ *					geom - Pointer to integer array to place geometry data.
+ *
+ *	Returns:		zero.
+ *
+ ****************************************************************/
+int Pci2000_BiosParam (Scsi_Disk *disk, kdev_t dev, int geom[])
+	{
+	PADAPTER2000	    padapter;
+
+	padapter = HOSTDATA(disk->device->host);
+
+	if ( WaitReady (padapter) )
+		return 0;
+	outb_p (disk->device->id, padapter->mb0);
+	outb_p (CMD_GET_PARMS, padapter->cmd);
+	if ( WaitReady (padapter) )
+		return 0;
+
+	geom[0] = inb_p (padapter->mb2 + 3);
+	geom[1] = inb_p (padapter->mb2 + 2);
+	geom[2] = inw_p (padapter->mb2);
+	return 0;
+	}
+
+
+#ifdef MODULE
+/* Eventually this will go into an include file, but this will be later */
+Scsi_Host_Template driver_template = PCI2220I;
+
+#include "scsi_module.c"
+#endif
+
diff -u --recursive --new-file v2.1.62/linux/drivers/scsi/pci2000.h linux/drivers/scsi/pci2000.h
--- v2.1.62/linux/drivers/scsi/pci2000.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/scsi/pci2000.h	Wed Nov  5 13:00:23 1997
@@ -0,0 +1,226 @@
+/*+M*************************************************************************
+ * Perceptive Solutions, Inc. PCI-2000 device driver proc support for Linux.
+ *
+ * Copyright (c) 1997 Perceptive Solutions, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING.  If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *
+ *	File Name:		pci2000.h
+ *
+ *	Description:	Header file for the SCSI driver for the PCI-2000
+ *					interface card.
+ *
+ *-M*************************************************************************/
+#ifndef _PCI2000_H
+#define _PCI2000_H
+
+#include <linux/types.h>
+#include <linux/kdev_t.h>
+
+#ifndef	PSI_EIDE_SCSIOP
+#define	PSI_EIDE_SCSIOP	1
+
+/************************************************/
+/*		definition of standard data types		*/
+/************************************************/
+#define	CHAR	char
+#define	UCHAR	unsigned char
+#define	SHORT	short
+#define	USHORT	unsigned short
+#define	BOOL	long
+#define	LONG	long
+#define	ULONG	unsigned long
+#define	VOID	void
+
+typedef	CHAR	*PCHAR;
+typedef	UCHAR	*PUCHAR;
+typedef	SHORT	*PSHORT;
+typedef	USHORT	*PUSHORT;
+typedef	BOOL	*PBOOL;
+typedef	LONG	*PLONG;
+typedef	ULONG	*PULONG;
+typedef	VOID	*PVOID;
+
+
+/************************************************/
+/*		Misc. macros			 				*/
+/************************************************/
+#define ANY2SCSI(up, p)					\
+((UCHAR *)up)[0] = (((ULONG)(p)) >> 8);	\
+((UCHAR *)up)[1] = ((ULONG)(p));
+
+#define SCSI2LONG(up)					\
+( (((long)*(((UCHAR *)up))) << 16)		\
++ (((long)(((UCHAR *)up)[1])) << 8)		\
++ ((long)(((UCHAR *)up)[2])) )
+
+#define XANY2SCSI(up, p)				\
+((UCHAR *)up)[0] = ((long)(p)) >> 24;	\
+((UCHAR *)up)[1] = ((long)(p)) >> 16;	\
+((UCHAR *)up)[2] = ((long)(p)) >> 8;	\
+((UCHAR *)up)[3] = ((long)(p));
+
+#define XSCSI2LONG(up)					\
+( (((long)(((UCHAR *)up)[0])) << 24)	\
++ (((long)(((UCHAR *)up)[1])) << 16)	\
++ (((long)(((UCHAR *)up)[2])) <<  8)	\
++ ((long)(((UCHAR *)up)[3])) )
+
+/************************************************/
+/*		SCSI CDB operation codes 				*/
+/************************************************/
+#define SCSIOP_TEST_UNIT_READY		0x00
+#define SCSIOP_REZERO_UNIT			0x01
+#define SCSIOP_REWIND				0x01
+#define SCSIOP_REQUEST_BLOCK_ADDR	0x02
+#define SCSIOP_REQUEST_SENSE		0x03
+#define SCSIOP_FORMAT_UNIT			0x04
+#define SCSIOP_READ_BLOCK_LIMITS	0x05
+#define SCSIOP_REASSIGN_BLOCKS		0x07
+#define SCSIOP_READ6				0x08
+#define SCSIOP_RECEIVE				0x08
+#define SCSIOP_WRITE6				0x0A
+#define SCSIOP_PRINT				0x0A
+#define SCSIOP_SEND					0x0A
+#define SCSIOP_SEEK6				0x0B
+#define SCSIOP_TRACK_SELECT			0x0B
+#define SCSIOP_SLEW_PRINT			0x0B
+#define SCSIOP_SEEK_BLOCK			0x0C
+#define SCSIOP_PARTITION			0x0D
+#define SCSIOP_READ_REVERSE			0x0F
+#define SCSIOP_WRITE_FILEMARKS		0x10
+#define SCSIOP_FLUSH_BUFFER			0x10
+#define SCSIOP_SPACE				0x11
+#define SCSIOP_INQUIRY				0x12
+#define SCSIOP_VERIFY6				0x13
+#define SCSIOP_RECOVER_BUF_DATA		0x14
+#define SCSIOP_MODE_SELECT			0x15
+#define SCSIOP_RESERVE_UNIT			0x16
+#define SCSIOP_RELEASE_UNIT			0x17
+#define SCSIOP_COPY					0x18
+#define SCSIOP_ERASE				0x19
+#define SCSIOP_MODE_SENSE			0x1A
+#define SCSIOP_START_STOP_UNIT		0x1B
+#define SCSIOP_STOP_PRINT			0x1B
+#define SCSIOP_LOAD_UNLOAD			0x1B
+#define SCSIOP_RECEIVE_DIAGNOSTIC	0x1C
+#define SCSIOP_SEND_DIAGNOSTIC		0x1D
+#define SCSIOP_MEDIUM_REMOVAL		0x1E
+#define SCSIOP_READ_CAPACITY		0x25
+#define SCSIOP_READ					0x28
+#define SCSIOP_WRITE				0x2A
+#define SCSIOP_SEEK					0x2B
+#define SCSIOP_LOCATE				0x2B
+#define SCSIOP_WRITE_VERIFY			0x2E
+#define SCSIOP_VERIFY				0x2F
+#define SCSIOP_SEARCH_DATA_HIGH		0x30
+#define SCSIOP_SEARCH_DATA_EQUAL	0x31
+#define SCSIOP_SEARCH_DATA_LOW		0x32
+#define SCSIOP_SET_LIMITS			0x33
+#define SCSIOP_READ_POSITION		0x34
+#define SCSIOP_SYNCHRONIZE_CACHE	0x35
+#define SCSIOP_COMPARE				0x39
+#define SCSIOP_COPY_COMPARE			0x3A
+#define SCSIOP_WRITE_DATA_BUFF		0x3B
+#define SCSIOP_READ_DATA_BUFF		0x3C
+#define SCSIOP_CHANGE_DEFINITION	0x40
+#define SCSIOP_READ_SUB_CHANNEL		0x42
+#define SCSIOP_READ_TOC				0x43
+#define SCSIOP_READ_HEADER			0x44
+#define SCSIOP_PLAY_AUDIO			0x45
+#define SCSIOP_PLAY_AUDIO_MSF		0x47
+#define SCSIOP_PLAY_TRACK_INDEX		0x48
+#define SCSIOP_PLAY_TRACK_RELATIVE	0x49
+#define SCSIOP_PAUSE_RESUME			0x4B
+#define SCSIOP_LOG_SELECT			0x4C
+#define SCSIOP_LOG_SENSE			0x4D
+#define SCSIOP_MODE_SELECT10		0x55
+#define SCSIOP_MODE_SENSE10			0x5A
+#define SCSIOP_LOAD_UNLOAD_SLOT		0xA6
+#define SCSIOP_MECHANISM_STATUS		0xBD
+#define SCSIOP_READ_CD				0xBE
+
+// SCSI read capacity structure
+typedef	struct _READ_CAPACITY_DATA
+	{
+	ULONG blks;				/* total blocks (converted to little endian) */
+	ULONG blksiz;			/* size of each (converted to little endian) */
+	}	READ_CAPACITY_DATA, *PREAD_CAPACITY_DATA;
+
+// SCSI inquiry data
+typedef struct _INQUIRYDATA
+	{
+	UCHAR DeviceType			:5;
+	UCHAR DeviceTypeQualifier	:3;
+	UCHAR DeviceTypeModifier	:7;
+	UCHAR RemovableMedia		:1;
+    UCHAR Versions;
+    UCHAR ResponseDataFormat;
+    UCHAR AdditionalLength;
+    UCHAR Reserved[2];
+	UCHAR SoftReset				:1;
+	UCHAR CommandQueue			:1;
+	UCHAR Reserved2				:1;
+	UCHAR LinkedCommands		:1;
+	UCHAR Synchronous			:1;
+	UCHAR Wide16Bit				:1;
+	UCHAR Wide32Bit				:1;
+	UCHAR RelativeAddressing	:1;
+    UCHAR VendorId[8];
+    UCHAR ProductId[16];
+    UCHAR ProductRevisionLevel[4];
+    UCHAR VendorSpecific[20];
+    UCHAR Reserved3[40];
+	}	INQUIRYDATA, *PINQUIRYDATA;
+
+#endif
+
+// function prototypes
+int Pci2000_Detect			(Scsi_Host_Template *tpnt);
+int Pci2000_Command			(Scsi_Cmnd *SCpnt);
+int Pci2000_QueueCommand	(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *));
+int Pci2000_Abort			(Scsi_Cmnd *SCpnt);
+int Pci2000_Reset			(Scsi_Cmnd *SCpnt, unsigned int flags);
+int Pci2000_BiosParam		(Disk *disk, kdev_t dev, int geom[]);
+
+#ifndef NULL
+	#define NULL 0
+#endif
+
+extern struct proc_dir_entry Proc_Scsi_Pci2000;
+
+#define PCI2000 { NULL, NULL,							\
+			&Proc_Scsi_Pci2000,/* proc_dir_entry */		\
+			NULL,		                				\
+			"PCI-2000 SCSI Intelligent Disk Controller",\
+			Pci2000_Detect,								\
+			NULL,										\
+			NULL,	 									\
+			Pci2000_Command,							\
+			Pci2000_QueueCommand,						\
+			Pci2000_Abort,								\
+			Pci2000_Reset,								\
+			NULL,										\
+			Pci2000_BiosParam,                 			\
+			16, 										\
+			-1, 										\
+			16,		 									\
+			1, 											\
+			0, 											\
+			0, 											\
+			DISABLE_CLUSTERING }
+
+#endif
diff -u --recursive --new-file v2.1.62/linux/drivers/scsi/pci2220i.c linux/drivers/scsi/pci2220i.c
--- v2.1.62/linux/drivers/scsi/pci2220i.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/scsi/pci2220i.c	Wed Nov  5 13:18:17 1997
@@ -0,0 +1,817 @@
+/*+M*************************************************************************
+ * Perceptive Solutions, Inc. PCI-2000 device driver proc support for Linux.
+ *
+ * Copyright (c) 1997 Perceptive Solutions, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING.  If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *
+ *	File Name:		pci2220i.c
+ *
+ *	Description:	SCSI driver for the PCI2220I EIDE interface card.
+ *
+ *-M*************************************************************************/
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/head.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/bios32.h>
+#include <linux/pci.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/proc_fs.h>
+#include <asm/dma.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#include <linux/blk.h>
+#include "scsi.h"
+#include "hosts.h"
+
+#include "pci2220i.h"
+#include "psi_dale.h"
+
+#include<linux/stat.h>
+
+struct proc_dir_entry Proc_Scsi_Pci2220i =
+	{ PROC_SCSI_PCI2220I, 7, "pci2220i", S_IFDIR | S_IRUGO | S_IXUGO, 2 };
+
+//#define DEBUG 1
+
+#ifdef DEBUG
+#define DEB(x) x
+#define STOP_HERE	{int st;for(st=0;st<100;st++){st=1;}}
+#else
+#define DEB(x)
+#define STOP_HERE
+#endif
+
+#define MAXADAPTER 4	/* Increase this and the sizes of the arrays below, if you need more. */
+
+#define	MAX_BUS_MASTER_BLOCKS	1		// This is the maximum we can bus master for (1024 bytes)
+
+#define	PORT_DATA				0
+#define	PORT_ERROR				1
+#define	PORT_SECTOR_COUNT		2
+#define	PORT_LBA_0				3
+#define	PORT_LBA_8				4
+#define	PORT_LBA_16				5
+#define	PORT_LBA_24				6
+#define	PORT_STAT_CMD			7
+#define	PORT_STAT_SEL			8
+#define	PORT_FAIL				9
+#define	PORT_ALT_STAT		   	10
+
+typedef struct
+	{
+	UCHAR		   	device;				// device code
+	UCHAR			byte6;				// device select register image
+	UCHAR			spigot;				// spigot number
+	UCHAR			sparebyte;			// placeholder
+	USHORT			sectors;			// number of sectors per track
+	USHORT			heads;				// number of heads
+	USHORT			cylinders;			// number of cylinders for this device
+	USHORT			spareword;			// placeholder
+	ULONG			blocks;				// number of blocks on device
+	}	OUR_DEVICE, *POUR_DEVICE;
+
+typedef struct
+	{
+	USHORT		 ports[12];
+	USHORT		 regDmaDesc;					// address of the DMA discriptor register for direction of transfer
+	USHORT		 regDmaCmdStat;					// Byte #1 of DMA command status register
+	USHORT		 regDmaAddrPci;					// 32 bit register for PCI address of DMA
+	USHORT		 regDmaAddrLoc;					// 32 bit register for local bus address of DMA
+	USHORT		 regDmaCount;					// 32 bit register for DMA transfer count
+	USHORT		 regDmaMode;						// 32 bit register for DMA mode control
+	USHORT		 regRemap;						// 32 bit local space remap
+	USHORT		 regDesc;						// 32 bit local region descriptor
+	USHORT		 regRange;						// 32 bit local range
+	USHORT		 regIrqControl;					// 16 bit Interrupt enable/disable and status
+	USHORT		 regScratchPad;					// scratch pad I/O base address
+	USHORT		 regBase;						// Base I/O register for data space
+	USHORT		 basePort;						// PLX base I/O port
+	USHORT		 timingMode;					// timing mode currently set for adapter
+	ULONG		 timingAddress;					// address to use on adapter for current timing mode
+	OUR_DEVICE	 device[4];
+	IDE_STRUCT	 ide;
+	ULONG		 startSector;
+	USHORT		 sectorCount;
+	Scsi_Cmnd	*SCpnt;
+	VOID		*buffer;
+	USHORT		 expectingIRQ;
+	USHORT		 readPhase;
+	}	ADAPTER2220I, *PADAPTER2220I;
+
+#define HOSTDATA(host) ((PADAPTER2220I)&host->hostdata)
+
+
+static struct	Scsi_Host 	   *PsiHost[MAXADAPTER] = {NULL,};  // One for each adapter
+static			int				NumAdapters = 0;
+static			IDENTIFY_DATA	identifyData;
+static			SETUP			DaleSetup;
+
+/****************************************************************
+ *	Name:	WriteData	:LOCAL
+ *
+ *	Description:	Write data to device.
+ *
+ *	Parameters:		padapter - Pointer adapter data structure.
+ *
+ *	Returns:		TRUE if drive does not assert DRQ in time.
+ *
+ ****************************************************************/
+static int WriteData (PADAPTER2220I padapter)
+	{
+	ULONG	timer;
+	USHORT *pports = padapter->ports;
+
+	timer = jiffies + TIMEOUT_DRQ;								// calculate the timeout value
+	do  {
+		if ( inb_p (pports[PORT_STAT_CMD]) & IDE_STATUS_DRQ )
+			{
+			outb_p (0, padapter->regDmaDesc);							// write operation
+			outl (padapter->timingAddress, padapter->regDmaAddrLoc);
+			outl (virt_to_bus (padapter->buffer), padapter->regDmaAddrPci);
+			outl ((ULONG)padapter->ide.ide.ide[2] * (ULONG)512, padapter->regDmaCount);
+			outb_p (1, padapter->regDmaMode);							// interrupts off
+			outb_p (0x03, padapter->regDmaCmdStat);						// kick the DMA engine in gear
+			return 0;
+			}
+		}	while ( timer > jiffies );									// test for timeout
+
+	padapter->ide.ide.ides.cmd = 0;									// null out the command byte
+	return 1;
+	}
+/****************************************************************
+ *	Name:	IdeCmd	:LOCAL
+ *
+ *	Description:	Process a queued command from the SCSI manager.
+ *
+ *	Parameters:		padapter - Pointer adapter data structure.
+ *
+ *	Returns:		Zero if no error or status register contents on error.
+ *
+ ****************************************************************/
+static UCHAR IdeCmd (PADAPTER2220I padapter)
+	{
+	ULONG	timer;
+	USHORT *pports = padapter->ports;
+	UCHAR	status;
+
+	outb_p (padapter->ide.ide.ides.spigot, pports[PORT_STAT_SEL]);	// select the spigot
+	outb_p (padapter->ide.ide.ide[6], pports[PORT_LBA_24]);			// select the drive
+	timer = jiffies + TIMEOUT_READY;							// calculate the timeout value
+	DEB(printk ("\npci2220i Issueing new command: 0x%X",padapter->ide.ide.ides.cmd));
+	do  {
+		status = inb_p (padapter->ports[PORT_STAT_CMD]);
+		if ( status & IDE_STATUS_DRDY )
+			{
+			outb_p (padapter->ide.ide.ide[2], pports[PORT_SECTOR_COUNT]);
+			outb_p (padapter->ide.ide.ide[3], pports[PORT_LBA_0]);
+			outb_p (padapter->ide.ide.ide[4], pports[PORT_LBA_8]);
+			outb_p (padapter->ide.ide.ide[5], pports[PORT_LBA_16]);
+			padapter->expectingIRQ = 1;
+			outb_p (padapter->ide.ide.ide[7], pports[PORT_STAT_CMD]);
+
+			if ( padapter->ide.ide.ides.cmd == IDE_CMD_WRITE_MULTIPLE )
+				return (WriteData (padapter));
+			return 0;
+			}
+		}	while ( timer > jiffies );									// test for timeout
+
+	padapter->ide.ide.ides.cmd = 0;									// null out the command byte
+	return status;
+	}
+/****************************************************************
+ *	Name:	SetupTransfer	:LOCAL
+ *
+ *	Description:	Setup a data transfer command.
+ *
+ *	Parameters:		padapter - Pointer adapter data structure.
+ *					drive	 - Drive/head register upper nibble only.
+ *
+ *	Returns:		TRUE if no data to transfer.
+ *
+ ****************************************************************/
+static int SetupTransfer (PADAPTER2220I padapter, UCHAR drive)
+	{
+	if ( padapter->sectorCount )
+		{
+		*(ULONG *)padapter->ide.ide.ides.lba = padapter->startSector;
+		padapter->ide.ide.ide[6] |= drive;
+//		padapter->ide.ide.ides.sectors = ( padapter->sectorCount > SECTORSXFER ) ? SECTORSXFER : padapter->sectorCount;
+		padapter->ide.ide.ides.sectors = ( padapter->sectorCount > MAX_BUS_MASTER_BLOCKS ) ? MAX_BUS_MASTER_BLOCKS : padapter->sectorCount;
+		padapter->sectorCount -= padapter->ide.ide.ides.sectors;	// bump the start and count for next xfer
+		padapter->startSector += padapter->ide.ide.ides.sectors;
+		return 0;
+		}
+	else
+		{
+		padapter->ide.ide.ides.cmd = 0;								// null out the command byte
+		padapter->SCpnt = NULL;
+		return 1;
+		}
+	}
+/****************************************************************
+ *	Name:	DecodeError	:LOCAL
+ *
+ *	Description:	Decode and process device errors.
+ *
+ *	Parameters:		pshost - Pointer to host data block.
+ *					status - Status register code.
+ *
+ *	Returns:		The driver status code.
+ *
+ ****************************************************************/
+static ULONG DecodeError (struct Scsi_Host *pshost, UCHAR status)
+	{
+	PADAPTER2220I	padapter = HOSTDATA(pshost);
+	UCHAR			error;
+
+	padapter->expectingIRQ = 0;
+	padapter->SCpnt = NULL;
+	if ( status & IDE_STATUS_WRITE_FAULT )
+		{
+		return DID_PARITY << 16;
+		}
+	if ( status & IDE_STATUS_BUSY )
+		return DID_BUS_BUSY << 16;
+
+	error = inb_p (padapter->ports[PORT_ERROR]);
+	DEB(printk ("\npci2220i error register: %x", error));
+	switch ( error )
+		{
+		case IDE_ERROR_AMNF:
+		case IDE_ERROR_TKONF:
+		case IDE_ERROR_ABRT:
+		case IDE_ERROR_IDFN:
+		case IDE_ERROR_UNC:
+		case IDE_ERROR_BBK:
+		default:
+			return DID_ERROR << 16;
+		}
+	return DID_ERROR << 16;
+	}
+/****************************************************************
+ *	Name:	Irq_Handler	:LOCAL
+ *
+ *	Description:	Interrupt handler.
+ *
+ *	Parameters:		irq		- Hardware IRQ number.
+ *					dev_id	-
+ *					regs	-
+ *
+ *	Returns:		TRUE if drive is not ready in time.
+ *
+ ****************************************************************/
+static void Irq_Handler (int irq, void *dev_id, struct pt_regs *regs)
+	{
+	struct Scsi_Host   *shost = NULL;	// Pointer to host data block
+	PADAPTER2220I		padapter;		// Pointer to adapter control structure
+	USHORT		 	   *pports;			// I/O port array
+	Scsi_Cmnd		   *SCpnt;
+	UCHAR				status;
+	int					z;
+
+//	DEB(printk ("\npci2220i recieved interrupt\n"));
+
+	for ( z = 0; z < NumAdapters;  z++ )								// scan for interrupt to process
+		{
+		if ( PsiHost[z]->irq == (UCHAR)(irq & 0xFF) )
+			{
+			if ( inw_p (HOSTDATA(PsiHost[z])->regIrqControl) & 0x8000 )
+				{
+				shost = PsiHost[z];
+				break;
+				}
+			}
+		}
+
+	if ( !shost )
+		{
+		DEB (printk ("\npci2220i: not my interrupt"));
+		return;
+		}
+
+	padapter = HOSTDATA(shost);
+	pports = padapter->ports;
+	SCpnt = padapter->SCpnt;
+
+	if ( !padapter->expectingIRQ )
+		{
+		DEB(printk ("\npci2220i Unsolicited interrupt\n"));
+		return;
+		}
+	padapter->expectingIRQ = 0;
+
+	status = inb_p (padapter->ports[PORT_STAT_CMD]);					// read the device status
+	if ( status & (IDE_STATUS_ERROR | IDE_STATUS_WRITE_FAULT) )
+		goto irqerror;
+
+	switch ( padapter->ide.ide.ides.cmd )								// decide how to handle the interrupt
+		{
+		case IDE_CMD_READ_MULTIPLE:
+			if ( padapter->readPhase == 1 )								// is this a bus master channel complete?
+				{
+				DEB(printk ("\npci2220i processing read interrupt cleanup"));
+				outb_p (0x08, padapter->regDmaCmdStat);					// cancel interrupt from DMA engine
+				padapter->buffer += padapter->ide.ide.ides.sectors * 512;
+				if ( SetupTransfer (padapter, padapter->ide.ide.ide[6] & 0xF0) )
+					{
+					SCpnt->result = DID_OK << 16;
+					padapter->SCpnt = NULL;
+					SCpnt->scsi_done (SCpnt);
+					return;
+					}
+				padapter->readPhase = 0;
+				if ( !(status = IdeCmd (padapter)) )
+					{
+					DEB (printk ("\npci2220i interrupt complete, waiting for another"));
+					return;
+					}
+				}
+			if ( status & IDE_STATUS_DRQ )
+				{
+				DEB(printk ("\npci2220i processing read interrupt start bus master cycle"));
+				outb_p (8, padapter->regDmaDesc); 				   		// read operation
+				padapter->readPhase = 1;
+				padapter->expectingIRQ = 1;
+				outl   (padapter->timingAddress, padapter->regDmaAddrLoc);
+				outl   (virt_to_bus (padapter->buffer), padapter->regDmaAddrPci);
+				outl   ((ULONG)padapter->ide.ide.ides.sectors * (ULONG)512, padapter->regDmaCount);
+				outb_p (5, padapter->regDmaMode);				   		// interrupt enable/disable
+				outb_p (0x03, padapter->regDmaCmdStat);			   		// kick the DMA engine in gear
+				return;
+				}
+			break;
+
+		case IDE_CMD_WRITE_MULTIPLE:
+			DEB(printk ("\npci2220i processing write interrupt cleanup"));
+			padapter->buffer += padapter->ide.ide.ides.sectors * 512;
+			if ( SetupTransfer (padapter, padapter->ide.ide.ide[6] & 0xF0) )
+				{
+				SCpnt->result = DID_OK << 16;
+				padapter->SCpnt = NULL;
+				SCpnt->scsi_done (SCpnt);
+				return;
+				}
+			if ( !(status = IdeCmd (padapter)) )
+				{
+				DEB (printk ("\npci2220i interrupt complete, waiting for another"));
+				return;
+				}
+			break;
+
+		case IDE_COMMAND_IDENTIFY:
+			{
+			PINQUIRYDATA	pinquiryData  = SCpnt->request_buffer;
+
+			DEB(printk ("\npci2220i processing verify interrupt cleanup"));
+			if ( status & IDE_STATUS_DRQ )
+				{
+				insw (pports[PORT_DATA], &identifyData, sizeof (identifyData) >> 1);
+
+				memset (pinquiryData, 0, SCpnt->request_bufflen);		// Zero INQUIRY data structure.
+				pinquiryData->DeviceType = 0;
+				pinquiryData->Versions = 2;
+				pinquiryData->AdditionalLength = 35 - 4;
+
+				// Fill in vendor identification fields.
+				for ( z = 0;  z < 20;  z += 2 )
+					{
+					pinquiryData->VendorId[z]	  = ((UCHAR *)identifyData.ModelNumber)[z + 1];
+					pinquiryData->VendorId[z + 1] = ((UCHAR *)identifyData.ModelNumber)[z];
+					}
+
+				// Initialize unused portion of product id.
+				for ( z = 0;  z < 4;  z++ )
+					pinquiryData->ProductId[12 + z] = ' ';
+
+				// Move firmware revision from IDENTIFY data to
+				// product revision in INQUIRY data.
+				for ( z = 0;  z < 4;  z += 2 )
+					{
+					pinquiryData->ProductRevisionLevel[z]	 = ((UCHAR *)identifyData.FirmwareRevision)[z + 1];
+					pinquiryData->ProductRevisionLevel[z + 1] = ((UCHAR *)identifyData.FirmwareRevision)[z];
+					}
+
+				SCpnt->result = DID_OK << 16;
+				padapter->SCpnt = NULL;
+				SCpnt->scsi_done (SCpnt);
+				return;
+				}
+			break;
+			}
+
+		default:
+			DEB(printk ("\npci2220i no real process here!"));
+			SCpnt->result = DID_OK << 16;
+			padapter->SCpnt = NULL;
+			SCpnt->scsi_done (SCpnt);
+			return;
+		}
+
+irqerror:;
+	DEB(printk ("\npci2220i error  Device Status: %X\n", status));
+	SCpnt->result = DecodeError (shost, status);
+	SCpnt->scsi_done (SCpnt);
+	}
+/****************************************************************
+ *	Name:	Pci2220i_QueueCommand
+ *
+ *	Description:	Process a queued command from the SCSI manager.
+ *
+ *	Parameters:		SCpnt - Pointer to SCSI command structure.
+ *					done  - Pointer to done function to call.
+ *
+ *	Returns:		Status code.
+ *
+ ****************************************************************/
+int Pci2220i_QueueCommand (Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *))
+	{
+	UCHAR		   *cdb = (UCHAR *)SCpnt->cmnd;					// Pointer to SCSI CDB
+	PADAPTER2220I	padapter = HOSTDATA(SCpnt->host);			// Pointer to adapter control structure
+	POUR_DEVICE		pdev	 = &padapter->device[SCpnt->target];// Pointer to device information
+	UCHAR			rc;											// command return code
+
+	SCpnt->scsi_done = done;
+	padapter->ide.ide.ides.spigot = pdev->spigot;
+	padapter->buffer = SCpnt->request_buffer;
+	if (done)
+		{
+		if ( !pdev->device )
+			{
+			SCpnt->result = DID_BAD_TARGET << 16;
+			done (SCpnt);
+			return 0;
+			}
+		}
+	else
+		{
+		printk("pci2220i_queuecommand: %02X: done can't be NULL\n", *cdb);
+		return 0;
+		}
+
+	DEB (if(*cdb) printk ("\nCDB: %X-  %X %X %X %X %X %X %X %X %X %X ", SCpnt->cmd_len, cdb[0], cdb[1], cdb[2], cdb[3], cdb[4], cdb[5], cdb[6], cdb[7], cdb[8], cdb[9]));
+	switch ( *cdb )
+		{
+		case SCSIOP_INQUIRY:   					// inquiry CDB
+			{
+			padapter->ide.ide.ide[6] = pdev->byte6;
+			padapter->ide.ide.ides.cmd = IDE_COMMAND_IDENTIFY;
+			break;
+			}
+
+		case SCSIOP_TEST_UNIT_READY:			// test unit ready CDB
+			SCpnt->result = DID_OK << 16;
+			done (SCpnt);
+			return 0;
+
+		case SCSIOP_READ_CAPACITY:			  	// read capctiy CDB
+			{
+			PREAD_CAPACITY_DATA	pdata = (PREAD_CAPACITY_DATA)SCpnt->request_buffer;
+
+			pdata->blksiz = 0x20000;
+			XANY2SCSI ((UCHAR *)&pdata->blks, pdev->blocks);
+			SCpnt->result = DID_OK << 16;
+			done (SCpnt);
+			return 0;
+			}
+
+		case SCSIOP_VERIFY:						// verify CDB
+			*(ULONG *)padapter->ide.ide.ides.lba = XSCSI2LONG (&cdb[2]);
+			padapter->ide.ide.ide[6] |= pdev->byte6;
+			padapter->ide.ide.ide[2] = (UCHAR)((USHORT)cdb[8] | ((USHORT)cdb[7] << 8));
+			padapter->ide.ide.ides.cmd = IDE_COMMAND_VERIFY;
+			break;
+
+		case SCSIOP_READ:						// read10 CDB
+			padapter->startSector = XSCSI2LONG (&cdb[2]);
+			padapter->sectorCount = (USHORT)cdb[8] | ((USHORT)cdb[7] << 8);
+			SetupTransfer (padapter, pdev->byte6);
+			padapter->ide.ide.ides.cmd = IDE_CMD_READ_MULTIPLE;
+			padapter->readPhase = 0;
+			break;
+
+		case SCSIOP_READ6:						// read6  CDB
+			padapter->startSector = SCSI2LONG (&cdb[1]);
+			padapter->sectorCount = cdb[4];
+			SetupTransfer (padapter, pdev->byte6);
+			padapter->ide.ide.ides.cmd = IDE_CMD_READ_MULTIPLE;
+			padapter->readPhase = 0;
+			break;
+
+		case SCSIOP_WRITE:						// write10 CDB
+			padapter->startSector = XSCSI2LONG (&cdb[2]);
+			padapter->sectorCount = (USHORT)cdb[8] | ((USHORT)cdb[7] << 8);
+			SetupTransfer (padapter, pdev->byte6);
+			padapter->ide.ide.ides.cmd = IDE_CMD_WRITE_MULTIPLE;
+			break;
+		case SCSIOP_WRITE6:						// write6  CDB
+			padapter->startSector = SCSI2LONG (&cdb[1]);
+			padapter->sectorCount = cdb[4];
+			SetupTransfer (padapter, pdev->byte6);
+			padapter->ide.ide.ides.cmd = IDE_CMD_WRITE_MULTIPLE;
+			break;
+
+		default:
+			DEB (printk ("pci2220i_queuecommand: Unsupported command %02X\n", *cdb));
+			SCpnt->result = DID_ERROR << 16;
+			done (SCpnt);
+			return 0;
+		}
+
+	padapter->SCpnt = SCpnt;  									// Save this command data
+
+	rc = IdeCmd (padapter);
+	if ( rc )
+		{
+		padapter->expectingIRQ = 0;
+		DEB (printk ("pci2220i_queuecommand: %02X, %02X: Device failed to respond for command\n", *cdb, padapter->ide.ide.ides.cmd));
+		SCpnt->result = DID_ERROR << 16;
+		done (SCpnt);
+		return 0;
+		}
+	if ( padapter->ide.ide.ides.cmd == IDE_CMD_WRITE_MULTIPLE )
+		{
+		if ( WriteData (padapter) )
+			{
+			padapter->expectingIRQ = 0;
+			DEB (printk ("pci2220i_queuecommand: %02X, %02X: Device failed to accept data\n", *cdb, padapter->ide.ide.ides.cmd));
+			SCpnt->result = DID_ERROR << 16;
+			done (SCpnt);
+			return 0;
+			}
+		}
+	DEB (printk("  now waiting for initial interrupt "));
+	return 0;
+	}
+
+static void internal_done(Scsi_Cmnd * SCpnt)
+	{
+	SCpnt->SCp.Status++;
+	}
+/****************************************************************
+ *	Name:	Pci2220i_Command
+ *
+ *	Description:	Process a command from the SCSI manager.
+ *
+ *	Parameters:		SCpnt - Pointer to SCSI command structure.
+ *
+ *	Returns:		Status code.
+ *
+ ****************************************************************/
+int Pci2220i_Command (Scsi_Cmnd *SCpnt)
+	{
+	DEB(printk("pci2220i_command: ..calling pci2220i_queuecommand\n"));
+
+	Pci2220i_QueueCommand (SCpnt, internal_done);
+
+    SCpnt->SCp.Status = 0;
+	while (!SCpnt->SCp.Status)
+		barrier ();
+	return SCpnt->result;
+	}
+/****************************************************************
+ *	Name:			ReadFlash
+ *
+ *	Description:	Read information from controller Flash memory.
+ *
+ *	Parameters:		hostdata - Pointer to host interface data structure.
+ *					pdata	 - Pointer to data structures.
+ *					base	 - base address in Flash.
+ *					length	 - lenght of data space in bytes.
+ *
+ *	Returns:		Nothing.
+ *
+ ****************************************************************/
+VOID ReadFlash (PADAPTER2220I hostdata, VOID *pdata, ULONG base, ULONG length)
+	{
+	ULONG	 oldremap;
+	UCHAR	 olddesc;
+	ULONG	 z;
+	UCHAR	*pd = (UCHAR *)pdata;
+
+	oldremap = inl (hostdata->regRemap);									// save values to restore later
+	olddesc  = inb_p (hostdata->regDesc);
+
+	outl (base | 1, hostdata->regRemap);									// remap to Flash space as specified
+	outb_p (0x40, hostdata->regDesc);										// describe remap region as 8 bit
+	for ( z = 0;  z < length;  z++)											// get "length" data count
+		*pd++ = inb_p (hostdata->regBase + z);								// read in the data
+
+	outl (oldremap, hostdata->regRemap);									// restore remap register values
+	outb_p (olddesc, hostdata->regDesc);
+	}
+
+/****************************************************************
+ *	Name:	Pci2220i_Detect
+ *
+ *	Description:	Detect and initialize our boards.
+ *
+ *	Parameters:		tpnt - Pointer to SCSI host template structure.
+ *
+ *	Returns:		Number of adapters found.
+ *
+ ****************************************************************/
+int Pci2220i_Detect (Scsi_Host_Template *tpnt)
+	{
+	int					pci_index = 0;
+	struct Scsi_Host   *pshost;
+	PADAPTER2220I	    hostdata;
+	ULONG				modearray[] = {DALE_DATA_MODE2, DALE_DATA_MODE3, DALE_DATA_MODE4, DALE_DATA_MODE4P};
+	int					unit;
+	int					z;
+	int					setirq;
+
+	if ( pcibios_present () )
+		{
+		for ( pci_index = 0;  pci_index <= MAXADAPTER;  ++pci_index )
+			{
+			UCHAR	pci_bus, pci_device_fn;
+
+			if ( pcibios_find_device (VENDOR_PSI, DEVICE_DALE_1, pci_index, &pci_bus, &pci_device_fn) != 0 )
+				break;
+
+			pshost = scsi_register (tpnt, sizeof(ADAPTER2220I));
+			hostdata = HOSTDATA(pshost);
+
+			pcibios_read_config_word (pci_bus, pci_device_fn, PCI_BASE_ADDRESS_1, &hostdata->basePort);
+			hostdata->basePort &= 0xFFFE;
+			DEB (printk ("\nBase Regs = %#04X", hostdata->basePort));
+			hostdata->regRemap		= hostdata->basePort + RTR_LOCAL_REMAP;				// 32 bit local space remap
+			DEB (printk (" %#04X", hostdata->regRemap));
+			hostdata->regDesc		= hostdata->basePort + RTR_REGIONS;	  				// 32 bit local region descriptor
+			DEB (printk (" %#04X", hostdata->regDesc));
+			hostdata->regRange		= hostdata->basePort + RTR_LOCAL_RANGE;				// 32 bit local range
+			DEB (printk (" %#04X", hostdata->regRange));
+			hostdata->regIrqControl	= hostdata->basePort + RTR_INT_CONTROL_STATUS;		// 16 bit interupt control and status
+			DEB (printk (" %#04X", hostdata->regIrqControl));
+			hostdata->regScratchPad	= hostdata->basePort + RTR_MAILBOX;	  				// 16 byte scratchpad I/O base address
+			DEB (printk (" %#04X", hostdata->regScratchPad));
+
+			pcibios_read_config_word (pci_bus, pci_device_fn, PCI_BASE_ADDRESS_2, &hostdata->regBase);
+			hostdata->regBase &= 0xFFFE;
+			for ( z = 0;  z < 9;  z++ )													// build regester address array
+				hostdata->ports[z] = hostdata->regBase + 0x80 + (z * 4);
+			hostdata->ports[PORT_FAIL] = hostdata->regBase + REG_FAIL;
+			hostdata->ports[PORT_ALT_STAT] = hostdata->regBase + REG_ALT_STAT;
+			DEB (printk ("\nPorts ="));
+			DEB (for (z=0;z<11;z++) printk(" %#04X", hostdata->ports[z]););
+
+			hostdata->regDmaDesc	= hostdata->regBase + RTL_DMA1_DESC_PTR;			// address of the DMA discriptor register for direction of transfer
+			DEB (printk ("\nDMA Regs = %#04X", hostdata->regDmaDesc));
+			hostdata->regDmaCmdStat	= hostdata->regBase + RTL_DMA_COMMAND_STATUS + 1;	// Byte #1 of DMA command status register
+			DEB (printk (" %#04X", hostdata->regDmaCmdStat));
+			hostdata->regDmaAddrPci	= hostdata->regBase + RTL_DMA1_PCI_ADDR;			// 32 bit register for PCI address of DMA
+			DEB (printk (" %#04X", hostdata->regDmaAddrPci));
+			hostdata->regDmaAddrLoc	= hostdata->regBase + RTL_DMA1_LOCAL_ADDR;			// 32 bit register for local bus address of DMA
+			DEB (printk (" %#04X", hostdata->regDmaAddrLoc));
+			hostdata->regDmaCount	= hostdata->regBase + RTL_DMA1_COUNT;				// 32 bit register for DMA transfer count
+			DEB (printk (" %#04X", hostdata->regDmaCount));
+			hostdata->regDmaMode	= hostdata->regBase + RTL_DMA1_MODE + 1;			// 32 bit register for DMA mode control
+			DEB (printk (" %#04X", hostdata->regDmaMode));
+
+			if ( !inb_p (hostdata->regScratchPad + DALE_NUM_DRIVES) )					// if no devices on this board
+				goto unregister;
+
+			pcibios_read_config_byte (pci_bus, pci_device_fn, PCI_INTERRUPT_LINE, &pshost->irq);
+			setirq = 1;
+			for ( z = 0;  z < pci_index;  z++ )											// scan for shared interrupts
+				{
+				if ( PsiHost[z]->irq == pshost->irq )						// if shared then, don't posses
+					setirq = 0;
+				}
+			if ( setirq )																// if not shared, posses
+				{
+				if ( request_irq (pshost->irq, Irq_Handler, 0, "pci2220i", NULL) )
+					{
+					printk ("Unable to allocate IRQ for PSI-2220I controller.\n");
+					goto unregister;
+					}
+				}
+			PsiHost[pci_index]	= pshost;												// save SCSI_HOST pointer
+
+			pshost->unique_id	= hostdata->regBase;
+			pshost->max_id		= 4;
+
+			outb_p (0x01, hostdata->regRange);											// fix our range register because other drivers want to tromp on it
+
+			hostdata->timingMode	= inb_p (hostdata->regScratchPad + DALE_TIMING_MODE);
+			hostdata->timingAddress	= modearray[hostdata->timingMode - 2];
+			ReadFlash (hostdata, &DaleSetup, DALE_FLASH_SETUP, sizeof (SETUP));
+
+			for ( z = 0;  z < inb_p (hostdata->regScratchPad + DALE_NUM_DRIVES);  ++z )
+				{
+				unit = inb_p (hostdata->regScratchPad + DALE_CHANNEL_DEVICE_0 + z) & 0x0F;
+				hostdata->device[unit].device	 = inb_p (hostdata->regScratchPad + DALE_SCRATH_DEVICE_0 + unit);
+				hostdata->device[unit].byte6	 = (UCHAR)(((unit & 1) << 4) | 0xE0);
+				hostdata->device[unit].spigot	 = (UCHAR)(1 << (unit >> 1));
+				hostdata->device[unit].sectors	 = DaleSetup.setupDevice[unit].sectors;
+				hostdata->device[unit].heads	 = DaleSetup.setupDevice[unit].heads;
+				hostdata->device[unit].cylinders = DaleSetup.setupDevice[unit].cylinders;
+				hostdata->device[unit].blocks	 = DaleSetup.setupDevice[unit].blocks;
+				DEB (printk ("\nHOSTDATA->device    = %X", hostdata->device[unit].device));
+				DEB (printk ("\n          byte6     = %X", hostdata->device[unit].byte6));
+				DEB (printk ("\n          spigot    = %X", hostdata->device[unit].spigot));
+				DEB (printk ("\n          sectors   = %X", hostdata->device[unit].sectors));
+				DEB (printk ("\n          heads     = %X", hostdata->device[unit].heads));
+				DEB (printk ("\n          cylinders = %X", hostdata->device[unit].cylinders));
+				DEB (printk ("\n          blocks    = %lX", hostdata->device[unit].blocks));
+				}
+
+			printk("\nPSI-2220I EIDE CONTROLLER: at I/O = %X/%X  IRQ = %d\n", hostdata->basePort, hostdata->regBase, pshost->irq);
+			printk("(C) 1997 Perceptive Solutions, Inc. All rights reserved\n\n");
+			continue;
+unregister:
+			scsi_unregister (pshost);
+			NumAdapters++;
+			}
+		}
+	return NumAdapters;
+	}
+/****************************************************************
+ *	Name:	Pci2220i_Abort
+ *
+ *	Description:	Process the Abort command from the SCSI manager.
+ *
+ *	Parameters:		SCpnt - Pointer to SCSI command structure.
+ *
+ *	Returns:		Allways snooze.
+ *
+ ****************************************************************/
+int Pci2220i_Abort (Scsi_Cmnd *SCpnt)
+	{
+	DEB (printk ("pci2220i_abort\n"));
+	return SCSI_ABORT_SNOOZE;
+	}
+/****************************************************************
+ *	Name:	Pci2220i_Reset
+ *
+ *	Description:	Process the Reset command from the SCSI manager.
+ *
+ *	Parameters:		SCpnt - Pointer to SCSI command structure.
+ *					flags - Flags about the reset command
+ *
+ *	Returns:		No active command at this time, so this means
+ *					that each time we got some kind of response the
+ *					last time through.  Tell the mid-level code to
+ *					request sense information in order to decide what
+ *					to do next.
+ *
+ ****************************************************************/
+int Pci2220i_Reset (Scsi_Cmnd *SCpnt, unsigned int reset_flags)
+	{
+	return SCSI_RESET_PUNT;
+	}
+
+#include "sd.h"
+
+/****************************************************************
+ *	Name:	Pci2220i_BiosParam
+ *
+ *	Description:	Process the biosparam request from the SCSI manager to
+ *					return C/H/S data.
+ *
+ *	Parameters:		disk - Pointer to SCSI disk structure.
+ *					dev	 - Major/minor number from kernel.
+ *					geom - Pointer to integer array to place geometry data.
+ *
+ *	Returns:		zero.
+ *
+ ****************************************************************/
+int Pci2220i_BiosParam (Scsi_Disk *disk, kdev_t dev, int geom[])
+	{
+	POUR_DEVICE	pdev;
+
+	pdev = &(HOSTDATA(disk->device->host)->device[disk->device->id]);
+
+	geom[0] = pdev->heads;
+	geom[1] = pdev->sectors;
+	geom[2] = pdev->cylinders;
+	return 0;
+	}
+
+
+#ifdef MODULE
+/* Eventually this will go into an include file, but this will be later */
+Scsi_Host_Template driver_template = PCI2220I;
+
+#include "scsi_module.c"
+#endif
diff -u --recursive --new-file v2.1.62/linux/drivers/scsi/pci2220i.h linux/drivers/scsi/pci2220i.h
--- v2.1.62/linux/drivers/scsi/pci2220i.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/scsi/pci2220i.h	Wed Nov  5 13:06:34 1997
@@ -0,0 +1,345 @@
+/*+M*************************************************************************
+ * Perceptive Solutions, Inc. PCI-2000 device driver proc support for Linux.
+ *
+ * Copyright (c) 1997 Perceptive Solutions, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING.  If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *
+ *	File Name:		pci2220i.h
+ *
+ *	Description:	Header file for the SCSI driver for the PCI2220I
+ *					EIDE interface card.
+ *
+ *-M*************************************************************************/
+
+#ifndef _PCI2220I_H
+#define _PCI2220I_H
+
+#include <linux/types.h>
+#include <linux/kdev_t.h>
+
+#ifndef	PSI_EIDE_SCSIOP
+#define	PSI_EIDE_SCSIOP	1
+
+/************************************************/
+/*		Some defines that we like 				*/
+/************************************************/
+#define	CHAR		char
+#define	UCHAR		unsigned char
+#define	SHORT		short
+#define	USHORT		unsigned short
+#define	BOOL		unsigned short
+#define	LONG		long
+#define	ULONG		unsigned long
+#define	VOID		void
+
+/************************************************/
+/*		Timeout konstants		 				*/
+/************************************************/
+#define	TIMEOUT_READY				10		// 100 mSec
+#define	TIMEOUT_DRQ					40		// 400 mSec
+
+/************************************************/
+/*		Misc. macros			 				*/
+/************************************************/
+#define ANY2SCSI(up, p)					\
+((UCHAR *)up)[0] = (((ULONG)(p)) >> 8);	\
+((UCHAR *)up)[1] = ((ULONG)(p));
+
+#define SCSI2LONG(up)					\
+( (((long)*(((UCHAR *)up))) << 16)		\
++ (((long)(((UCHAR *)up)[1])) << 8)		\
++ ((long)(((UCHAR *)up)[2])) )
+
+#define XANY2SCSI(up, p)				\
+((UCHAR *)up)[0] = ((long)(p)) >> 24;	\
+((UCHAR *)up)[1] = ((long)(p)) >> 16;	\
+((UCHAR *)up)[2] = ((long)(p)) >> 8;	\
+((UCHAR *)up)[3] = ((long)(p));
+
+#define XSCSI2LONG(up)					\
+( (((long)(((UCHAR *)up)[0])) << 24)	\
++ (((long)(((UCHAR *)up)[1])) << 16)	\
++ (((long)(((UCHAR *)up)[2])) <<  8)	\
++ ((long)(((UCHAR *)up)[3])) )
+
+/************************************************/
+/*		SCSI CDB operation codes 				*/
+/************************************************/
+#define SCSIOP_TEST_UNIT_READY		0x00
+#define SCSIOP_REZERO_UNIT			0x01
+#define SCSIOP_REWIND				0x01
+#define SCSIOP_REQUEST_BLOCK_ADDR	0x02
+#define SCSIOP_REQUEST_SENSE		0x03
+#define SCSIOP_FORMAT_UNIT			0x04
+#define SCSIOP_READ_BLOCK_LIMITS	0x05
+#define SCSIOP_REASSIGN_BLOCKS		0x07
+#define SCSIOP_READ6				0x08
+#define SCSIOP_RECEIVE				0x08
+#define SCSIOP_WRITE6				0x0A
+#define SCSIOP_PRINT				0x0A
+#define SCSIOP_SEND					0x0A
+#define SCSIOP_SEEK6				0x0B
+#define SCSIOP_TRACK_SELECT			0x0B
+#define SCSIOP_SLEW_PRINT			0x0B
+#define SCSIOP_SEEK_BLOCK			0x0C
+#define SCSIOP_PARTITION			0x0D
+#define SCSIOP_READ_REVERSE			0x0F
+#define SCSIOP_WRITE_FILEMARKS		0x10
+#define SCSIOP_FLUSH_BUFFER			0x10
+#define SCSIOP_SPACE				0x11
+#define SCSIOP_INQUIRY				0x12
+#define SCSIOP_VERIFY6				0x13
+#define SCSIOP_RECOVER_BUF_DATA		0x14
+#define SCSIOP_MODE_SELECT			0x15
+#define SCSIOP_RESERVE_UNIT			0x16
+#define SCSIOP_RELEASE_UNIT			0x17
+#define SCSIOP_COPY					0x18
+#define SCSIOP_ERASE				0x19
+#define SCSIOP_MODE_SENSE			0x1A
+#define SCSIOP_START_STOP_UNIT		0x1B
+#define SCSIOP_STOP_PRINT			0x1B
+#define SCSIOP_LOAD_UNLOAD			0x1B
+#define SCSIOP_RECEIVE_DIAGNOSTIC	0x1C
+#define SCSIOP_SEND_DIAGNOSTIC		0x1D
+#define SCSIOP_MEDIUM_REMOVAL		0x1E
+#define SCSIOP_READ_CAPACITY		0x25
+#define SCSIOP_READ					0x28
+#define SCSIOP_WRITE				0x2A
+#define SCSIOP_SEEK					0x2B
+#define SCSIOP_LOCATE				0x2B
+#define SCSIOP_WRITE_VERIFY			0x2E
+#define SCSIOP_VERIFY				0x2F
+#define SCSIOP_SEARCH_DATA_HIGH		0x30
+#define SCSIOP_SEARCH_DATA_EQUAL	0x31
+#define SCSIOP_SEARCH_DATA_LOW		0x32
+#define SCSIOP_SET_LIMITS			0x33
+#define SCSIOP_READ_POSITION		0x34
+#define SCSIOP_SYNCHRONIZE_CACHE	0x35
+#define SCSIOP_COMPARE				0x39
+#define SCSIOP_COPY_COMPARE			0x3A
+#define SCSIOP_WRITE_DATA_BUFF		0x3B
+#define SCSIOP_READ_DATA_BUFF		0x3C
+#define SCSIOP_CHANGE_DEFINITION	0x40
+#define SCSIOP_READ_SUB_CHANNEL		0x42
+#define SCSIOP_READ_TOC				0x43
+#define SCSIOP_READ_HEADER			0x44
+#define SCSIOP_PLAY_AUDIO			0x45
+#define SCSIOP_PLAY_AUDIO_MSF		0x47
+#define SCSIOP_PLAY_TRACK_INDEX		0x48
+#define SCSIOP_PLAY_TRACK_RELATIVE	0x49
+#define SCSIOP_PAUSE_RESUME			0x4B
+#define SCSIOP_LOG_SELECT			0x4C
+#define SCSIOP_LOG_SENSE			0x4D
+#define SCSIOP_MODE_SELECT10		0x55
+#define SCSIOP_MODE_SENSE10			0x5A
+#define SCSIOP_LOAD_UNLOAD_SLOT		0xA6
+#define SCSIOP_MECHANISM_STATUS		0xBD
+#define SCSIOP_READ_CD				0xBE
+
+// IDE command definitions
+#define IDE_COMMAND_ATAPI_RESET		0x08
+#define IDE_COMMAND_READ			0x20
+#define IDE_COMMAND_WRITE			0x30
+#define IDE_COMMAND_RECALIBRATE		0x10
+#define IDE_COMMAND_SEEK			0x70
+#define IDE_COMMAND_SET_PARAMETERS	0x91
+#define IDE_COMMAND_VERIFY			0x40
+#define IDE_COMMAND_ATAPI_PACKET	0xA0
+#define IDE_COMMAND_ATAPI_IDENTIFY	0xA1
+#define	IDE_CMD_READ_MULTIPLE		0xC4
+#define	IDE_CMD_WRITE_MULTIPLE		0xC5
+#define	IDE_CMD_SET_MULTIPLE		0xC6
+#define IDE_COMMAND_WRITE_DMA		0xCA
+#define IDE_COMMAND_READ_DMA		0xC8
+#define IDE_COMMAND_IDENTIFY		0xEC
+
+// IDE status definitions
+#define IDE_STATUS_ERROR			0x01
+#define IDE_STATUS_INDEX			0x02
+#define IDE_STATUS_CORRECTED_ERROR	0x04
+#define IDE_STATUS_DRQ				0x08
+#define IDE_STATUS_DSC				0x10
+#define	IDE_STATUS_WRITE_FAULT		0x20
+#define IDE_STATUS_DRDY				0x40
+#define IDE_STATUS_BUSY				0x80
+
+// IDE error definitions
+#define	IDE_ERROR_AMNF				0x01
+#define	IDE_ERROR_TKONF				0x02
+#define	IDE_ERROR_ABRT				0x04
+#define	IDE_ERROR_MCR				0x08
+#define	IDE_ERROR_IDFN				0x10
+#define	IDE_ERROR_MC				0x20
+#define	IDE_ERROR_UNC				0x40
+#define	IDE_ERROR_BBK				0x80
+
+//	IDE interface structure
+typedef struct _IDE_STRUCT
+	{
+	union
+		{
+		UCHAR	ide[9];
+		struct
+			{
+			USHORT	data;
+			UCHAR	sectors;
+			UCHAR	lba[4];
+			UCHAR	cmd;
+			UCHAR	spigot;
+			}	ides;
+		} ide;
+	}	IDE_STRUCT;
+
+// SCSI read capacity structure
+typedef	struct _READ_CAPACITY_DATA
+	{
+	ULONG blks;				/* total blocks (converted to little endian) */
+	ULONG blksiz;			/* size of each (converted to little endian) */
+	}	READ_CAPACITY_DATA, *PREAD_CAPACITY_DATA;
+
+// SCSI inquiry data
+typedef struct _INQUIRYDATA
+	{
+	UCHAR DeviceType			:5;
+	UCHAR DeviceTypeQualifier	:3;
+	UCHAR DeviceTypeModifier	:7;
+	UCHAR RemovableMedia		:1;
+    UCHAR Versions;
+    UCHAR ResponseDataFormat;
+    UCHAR AdditionalLength;
+    UCHAR Reserved[2];
+	UCHAR SoftReset				:1;
+	UCHAR CommandQueue			:1;
+	UCHAR Reserved2				:1;
+	UCHAR LinkedCommands		:1;
+	UCHAR Synchronous			:1;
+	UCHAR Wide16Bit				:1;
+	UCHAR Wide32Bit				:1;
+	UCHAR RelativeAddressing	:1;
+    UCHAR VendorId[8];
+    UCHAR ProductId[16];
+    UCHAR ProductRevisionLevel[4];
+    UCHAR VendorSpecific[20];
+    UCHAR Reserved3[40];
+	}	INQUIRYDATA, *PINQUIRYDATA;
+
+// IDE IDENTIFY data
+typedef struct _IDENTIFY_DATA
+	{
+    USHORT GeneralConfiguration;            // 00
+    USHORT NumberOfCylinders;               // 02
+    USHORT Reserved1;                       // 04
+    USHORT NumberOfHeads;                   // 06
+    USHORT UnformattedBytesPerTrack;        // 08
+    USHORT UnformattedBytesPerSector;       // 0A
+    USHORT SectorsPerTrack;                 // 0C
+    USHORT VendorUnique1[3];                // 0E
+    USHORT SerialNumber[10];                // 14
+    USHORT BufferType;                      // 28
+    USHORT BufferSectorSize;                // 2A
+    USHORT NumberOfEccBytes;                // 2C
+    USHORT FirmwareRevision[4];             // 2E
+    USHORT ModelNumber[20];                 // 36
+    UCHAR  MaximumBlockTransfer;            // 5E
+    UCHAR  VendorUnique2;                   // 5F
+    USHORT DoubleWordIo;                    // 60
+    USHORT Capabilities;                    // 62
+    USHORT Reserved2;                       // 64
+    UCHAR  VendorUnique3;                   // 66
+    UCHAR  PioCycleTimingMode;              // 67
+    UCHAR  VendorUnique4;                   // 68
+    UCHAR  DmaCycleTimingMode;              // 69
+    USHORT TranslationFieldsValid:1;        // 6A
+    USHORT Reserved3:15;
+    USHORT NumberOfCurrentCylinders;        // 6C
+    USHORT NumberOfCurrentHeads;            // 6E
+    USHORT CurrentSectorsPerTrack;          // 70
+    ULONG  CurrentSectorCapacity;           // 72
+    USHORT Reserved4[197];                  // 76
+	}	IDENTIFY_DATA, *PIDENTIFY_DATA;
+
+// Identify data without the Reserved4.
+typedef struct _IDENTIFY_DATA2 {
+    USHORT GeneralConfiguration;            // 00
+    USHORT NumberOfCylinders;               // 02
+    USHORT Reserved1;                       // 04
+    USHORT NumberOfHeads;                   // 06
+    USHORT UnformattedBytesPerTrack;        // 08
+    USHORT UnformattedBytesPerSector;       // 0A
+    USHORT SectorsPerTrack;                 // 0C
+    USHORT VendorUnique1[3];                // 0E
+    USHORT SerialNumber[10];                // 14
+    USHORT BufferType;                      // 28
+    USHORT BufferSectorSize;                // 2A
+    USHORT NumberOfEccBytes;                // 2C
+    USHORT FirmwareRevision[4];             // 2E
+    USHORT ModelNumber[20];                 // 36
+    UCHAR  MaximumBlockTransfer;            // 5E
+    UCHAR  VendorUnique2;                   // 5F
+    USHORT DoubleWordIo;                    // 60
+    USHORT Capabilities;                    // 62
+    USHORT Reserved2;                       // 64
+    UCHAR  VendorUnique3;                   // 66
+    UCHAR  PioCycleTimingMode;              // 67
+    UCHAR  VendorUnique4;                   // 68
+    UCHAR  DmaCycleTimingMode;              // 69
+	USHORT TranslationFieldsValid:1;     	// 6A
+    USHORT Reserved3:15;
+    USHORT NumberOfCurrentCylinders;        // 6C
+    USHORT NumberOfCurrentHeads;            // 6E
+    USHORT CurrentSectorsPerTrack;          // 70
+    ULONG  CurrentSectorCapacity;           // 72
+	}	IDENTIFY_DATA2, *PIDENTIFY_DATA2;
+
+#endif	// PSI_EIDE_SCSIOP
+
+// function prototypes
+int Pci2220i_Detect			(Scsi_Host_Template *tpnt);
+int Pci2220i_Command			(Scsi_Cmnd *SCpnt);
+int Pci2220i_QueueCommand	(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *));
+int Pci2220i_Abort			(Scsi_Cmnd *SCpnt);
+int Pci2220i_Reset			(Scsi_Cmnd *SCpnt, unsigned int flags);
+int Pci2220i_BiosParam		(Disk *disk, kdev_t dev, int geom[]);
+
+#ifndef NULL
+	#define NULL 0
+#endif
+
+extern struct proc_dir_entry Proc_Scsi_Pci2220i;
+
+#define PCI2220I { NULL, NULL,						\
+			&Proc_Scsi_Pci2220i,/* proc_dir_entry */	\
+			NULL,		                			\
+			"PCI-2220I EIDE Disk Controller",		\
+			Pci2220i_Detect,							\
+			NULL,									\
+			NULL,	 								\
+			Pci2220i_Command,						\
+			Pci2220i_QueueCommand,					\
+			Pci2220i_Abort,							\
+			Pci2220i_Reset,							\
+			NULL,									\
+			Pci2220i_BiosParam,                 		\
+			1, 										\
+			-1, 									\
+			SG_NONE,		 						\
+			1, 										\
+			0, 										\
+			0, 										\
+			DISABLE_CLUSTERING }
+
+#endif
diff -u --recursive --new-file v2.1.62/linux/drivers/scsi/psi240i.c linux/drivers/scsi/psi240i.c
--- v2.1.62/linux/drivers/scsi/psi240i.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/scsi/psi240i.c	Wed Nov  5 13:09:56 1997
@@ -0,0 +1,717 @@
+/*+M*************************************************************************
+ * Perceptive Solutions, Inc. PSI-240I device driver proc support for Linux.
+ *
+ * Copyright (c) 1997 Perceptive Solutions, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING.  If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *
+ *	File Name:		psi240i.c
+ *
+ *	Description:	SCSI driver for the PSI240I EIDE interface card.
+ *
+ *-M*************************************************************************/
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/head.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/proc_fs.h>
+#include <asm/dma.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#include <linux/blk.h>
+#include "scsi.h"
+#include "hosts.h"
+
+#include "psi240i.h"
+#include "psi_chip.h"
+
+#include<linux/stat.h>
+
+struct proc_dir_entry Proc_Scsi_Psi240i =
+	{ PROC_SCSI_PSI240I, 7, "psi240i", S_IFDIR | S_IRUGO | S_IXUGO, 2 };
+
+//#define DEBUG 1
+
+#ifdef DEBUG
+#define DEB(x) x
+#else
+#define DEB(x)
+#endif
+
+#define MAXBOARDS 2	/* Increase this and the sizes of the arrays below, if you need more. */
+
+#define	PORT_DATA				0
+#define	PORT_ERROR				1
+#define	PORT_SECTOR_COUNT		2
+#define	PORT_LBA_0				3
+#define	PORT_LBA_8				4
+#define	PORT_LBA_16				5
+#define	PORT_LBA_24				6
+#define	PORT_STAT_CMD			7
+#define	PORT_SEL_FAIL			8
+#define	PORT_IRQ_STATUS			9
+#define	PORT_ADDRESS			10
+#define	PORT_FAIL				11
+#define	PORT_ALT_STAT		   	12
+
+typedef struct
+	{
+	UCHAR		   	device;				// device code
+	UCHAR			byte6;				// device select register image
+	UCHAR			spigot;				// spigot number
+	UCHAR			expectingIRQ;		// flag for expecting and interrupt
+	USHORT			sectors;			// number of sectors per track
+	USHORT			heads;				// number of heads
+	USHORT			cylinders;			// number of cylinders for this device
+	USHORT			spareword;			// placeholder
+	ULONG			blocks;				// number of blocks on device
+	}	OUR_DEVICE, *POUR_DEVICE;
+
+typedef struct
+	{
+	USHORT		 ports[13];
+	OUR_DEVICE	 device[8];
+	Scsi_Cmnd	*pSCmnd;
+	IDE_STRUCT	 ide;
+	ULONG		 startSector;
+	USHORT		 sectorCount;
+	Scsi_Cmnd	*SCpnt;
+	VOID		*buffer;
+	USHORT		 expectingIRQ;
+	}	ADAPTER240I, *PADAPTER240I;
+
+#define HOSTDATA(host) ((PADAPTER240I)&host->hostdata)
+
+static struct	Scsi_Host *PsiHost[6] = {NULL,};  /* One for each IRQ level (10-15) */
+static			IDENTIFY_DATA	identifyData;
+static			SETUP			ChipSetup;
+
+static	USHORT	portAddr[6] = {CHIP_ADRS_0, CHIP_ADRS_1, CHIP_ADRS_2, CHIP_ADRS_3, CHIP_ADRS_4, CHIP_ADRS_5};
+
+/****************************************************************
+ *	Name:	WriteData	:LOCAL
+ *
+ *	Description:	Write data to device.
+ *
+ *	Parameters:		padapter - Pointer adapter data structure.
+ *
+ *	Returns:		TRUE if drive does not assert DRQ in time.
+ *
+ ****************************************************************/
+static int WriteData (PADAPTER240I padapter)
+	{
+	ULONG	timer;
+	USHORT *pports = padapter->ports;
+
+	timer = jiffies + TIMEOUT_DRQ;								// calculate the timeout value
+	do  {
+		if ( inb_p (pports[PORT_STAT_CMD]) & IDE_STATUS_DRQ )
+			{
+			outsw (pports[PORT_DATA], padapter->buffer, (USHORT)padapter->ide.ide.ide[2] * 256);
+			return 0;
+			}
+		}	while ( timer > jiffies );									// test for timeout
+
+	padapter->ide.ide.ides.cmd = 0;									// null out the command byte
+	return 1;
+	}
+/****************************************************************
+ *	Name:	IdeCmd	:LOCAL
+ *
+ *	Description:	Process a queued command from the SCSI manager.
+ *
+ *	Parameters:		padapter - Pointer adapter data structure.
+ *
+ *	Returns:		Zero if no error or status register contents on error.
+ *
+ ****************************************************************/
+static UCHAR IdeCmd (PADAPTER240I padapter)
+	{
+	ULONG	timer;
+	USHORT *pports = padapter->ports;
+	UCHAR	status;
+
+	outb_p (padapter->ide.ide.ides.spigot, pports[PORT_SEL_FAIL]);	// select the spigot
+	outb_p (padapter->ide.ide.ide[6], pports[PORT_LBA_24]);			// select the drive
+	timer = jiffies + TIMEOUT_READY;							// calculate the timeout value
+	do  {
+		status = inb_p (padapter->ports[PORT_STAT_CMD]);
+		if ( status & IDE_STATUS_DRDY )
+			{
+			outb_p (padapter->ide.ide.ide[2], pports[PORT_SECTOR_COUNT]);
+			outb_p (padapter->ide.ide.ide[3], pports[PORT_LBA_0]);
+			outb_p (padapter->ide.ide.ide[4], pports[PORT_LBA_8]);
+			outb_p (padapter->ide.ide.ide[5], pports[PORT_LBA_16]);
+			padapter->expectingIRQ = 1;
+			outb_p (padapter->ide.ide.ide[7], pports[PORT_STAT_CMD]);
+
+			if ( padapter->ide.ide.ides.cmd == IDE_CMD_WRITE_MULTIPLE )
+				return (WriteData (padapter));
+
+			return 0;
+			}
+		}	while ( timer > jiffies );									// test for timeout
+
+	padapter->ide.ide.ides.cmd = 0;									// null out the command byte
+	return status;
+	}
+/****************************************************************
+ *	Name:	SetupTransfer	:LOCAL
+ *
+ *	Description:	Setup a data transfer command.
+ *
+ *	Parameters:		padapter - Pointer adapter data structure.
+ *					drive	 - Drive/head register upper nibble only.
+ *
+ *	Returns:		TRUE if no data to transfer.
+ *
+ ****************************************************************/
+static int SetupTransfer (PADAPTER240I padapter, UCHAR drive)
+	{
+	if ( padapter->sectorCount )
+		{
+		*(ULONG *)padapter->ide.ide.ides.lba = padapter->startSector;
+		padapter->ide.ide.ide[6] |= drive;
+		padapter->ide.ide.ides.sectors = ( padapter->sectorCount > SECTORSXFER ) ? SECTORSXFER : padapter->sectorCount;
+		padapter->sectorCount -= padapter->ide.ide.ides.sectors;	// bump the start and count for next xfer
+		padapter->startSector += padapter->ide.ide.ides.sectors;
+		return 0;
+		}
+	else
+		{
+		padapter->ide.ide.ides.cmd = 0;								// null out the command byte
+		padapter->SCpnt = NULL;
+		return 1;
+		}
+	}
+/****************************************************************
+ *	Name:	DecodeError	:LOCAL
+ *
+ *	Description:	Decode and process device errors.
+ *
+ *	Parameters:		pshost - Pointer to host data block.
+ *					status - Status register code.
+ *
+ *	Returns:		The driver status code.
+ *
+ ****************************************************************/
+static ULONG DecodeError (struct Scsi_Host *pshost, UCHAR status)
+	{
+	PADAPTER240I	padapter = HOSTDATA(pshost);
+	UCHAR			error;
+
+	padapter->expectingIRQ = 0;
+	padapter->SCpnt = NULL;
+	if ( status & IDE_STATUS_WRITE_FAULT )
+		{
+		return DID_PARITY << 16;
+		}
+	if ( status & IDE_STATUS_BUSY )
+		return DID_BUS_BUSY << 16;
+
+	error = inb_p (padapter->ports[PORT_ERROR]);
+	DEB(printk ("\npsi240i error register: %x", error));
+	switch ( error )
+		{
+		case IDE_ERROR_AMNF:
+		case IDE_ERROR_TKONF:
+		case IDE_ERROR_ABRT:
+		case IDE_ERROR_IDFN:
+		case IDE_ERROR_UNC:
+		case IDE_ERROR_BBK:
+		default:
+			return DID_ERROR << 16;
+		}
+	return DID_ERROR << 16;
+	}
+/****************************************************************
+ *	Name:	Irq_Handler	:LOCAL
+ *
+ *	Description:	Interrupt handler.
+ *
+ *	Parameters:		irq		- Hardware IRQ number.
+ *					dev_id	-
+ *					regs	-
+ *
+ *	Returns:		TRUE if drive is not ready in time.
+ *
+ ****************************************************************/
+static void Irq_Handler (int irq, void *dev_id, struct pt_regs *regs)
+	{
+	struct Scsi_Host   *shost;			// Pointer to host data block
+	PADAPTER240I		padapter;		// Pointer to adapter control structure
+	USHORT		 	   *pports;			// I/O port array
+	Scsi_Cmnd		   *SCpnt;
+	UCHAR				status;
+	int					z;
+
+	DEB(printk ("\npsi240i recieved interrupt\n"));
+
+	shost = PsiHost[irq - 10];
+	if ( !shost )
+		panic ("Splunge!");
+
+	padapter = HOSTDATA(shost);
+	pports = padapter->ports;
+	SCpnt = padapter->SCpnt;
+
+	if ( !padapter->expectingIRQ )
+		{
+		DEB(printk ("\npsi240i Unsolicited interrupt\n"));
+		return;
+		}
+	padapter->expectingIRQ = 0;
+
+	status = inb_p (padapter->ports[PORT_STAT_CMD]);			// read the device status
+	if ( status & (IDE_STATUS_ERROR | IDE_STATUS_WRITE_FAULT) )
+		goto irqerror;
+
+	DEB(printk ("\npsi240i processing interrupt"));
+	switch ( padapter->ide.ide.ides.cmd )							// decide how to handle the interrupt
+		{
+		case IDE_CMD_READ_MULTIPLE:
+			if ( status & IDE_STATUS_DRQ )
+				{
+				insw (pports[PORT_DATA], padapter->buffer, (USHORT)padapter->ide.ide.ides.sectors * 256);
+				padapter->buffer += padapter->ide.ide.ides.sectors * 512;
+				if ( SetupTransfer (padapter, padapter->ide.ide.ide[6] & 0xF0) )
+					{
+					SCpnt->result = DID_OK << 16;
+					padapter->SCpnt = NULL;
+					SCpnt->scsi_done (SCpnt);
+					return;
+					}
+				if ( !(status = IdeCmd (padapter)) )
+					return;
+				}
+			break;
+
+		case IDE_CMD_WRITE_MULTIPLE:
+			padapter->buffer += padapter->ide.ide.ides.sectors * 512;
+			if ( SetupTransfer (padapter, padapter->ide.ide.ide[6] & 0xF0) )
+				{
+				SCpnt->result = DID_OK << 16;
+				padapter->SCpnt = NULL;
+				SCpnt->scsi_done (SCpnt);
+				return;
+				}
+			if ( !(status = IdeCmd (padapter)) )
+				return;
+			break;
+
+		case IDE_COMMAND_IDENTIFY:
+			{
+			PINQUIRYDATA	pinquiryData  = SCpnt->request_buffer;
+
+			if ( status & IDE_STATUS_DRQ )
+				{
+				insw (pports[PORT_DATA], &identifyData, sizeof (identifyData) >> 1);
+
+				memset (pinquiryData, 0, SCpnt->request_bufflen);		// Zero INQUIRY data structure.
+				pinquiryData->DeviceType = 0;
+				pinquiryData->Versions = 2;
+				pinquiryData->AdditionalLength = 35 - 4;
+
+				// Fill in vendor identification fields.
+				for ( z = 0;  z < 20;  z += 2 )
+					{
+					pinquiryData->VendorId[z]	  = ((UCHAR *)identifyData.ModelNumber)[z + 1];
+					pinquiryData->VendorId[z + 1] = ((UCHAR *)identifyData.ModelNumber)[z];
+					}
+
+				// Initialize unused portion of product id.
+				for ( z = 0;  z < 4;  z++ )
+					pinquiryData->ProductId[12 + z] = ' ';
+
+				// Move firmware revision from IDENTIFY data to
+				// product revision in INQUIRY data.
+				for ( z = 0;  z < 4;  z += 2 )
+					{
+					pinquiryData->ProductRevisionLevel[z]	 = ((UCHAR *)identifyData.FirmwareRevision)[z + 1];
+					pinquiryData->ProductRevisionLevel[z + 1] = ((UCHAR *)identifyData.FirmwareRevision)[z];
+					}
+
+				SCpnt->result = DID_OK << 16;
+				padapter->SCpnt = NULL;
+				SCpnt->scsi_done (SCpnt);
+				return;
+				}
+			break;
+			}
+
+		default:
+			SCpnt->result = DID_OK << 16;
+			padapter->SCpnt = NULL;
+			SCpnt->scsi_done (SCpnt);
+			return;
+		}
+
+irqerror:;
+	DEB(printk ("\npsi240i error  Device Status: %X\n", status));
+	SCpnt->result = DecodeError (shost, status);
+	SCpnt->scsi_done (SCpnt);
+	}
+/****************************************************************
+ *	Name:	Psi240i_QueueCommand
+ *
+ *	Description:	Process a queued command from the SCSI manager.
+ *
+ *	Parameters:		SCpnt - Pointer to SCSI command structure.
+ *					done  - Pointer to done function to call.
+ *
+ *	Returns:		Status code.
+ *
+ ****************************************************************/
+int Psi240i_QueueCommand (Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *))
+	{
+	UCHAR		   *cdb = (UCHAR *)SCpnt->cmnd;					// Pointer to SCSI CDB
+	PADAPTER240I	padapter = HOSTDATA(SCpnt->host);			// Pointer to adapter control structure
+	POUR_DEVICE		pdev	 = &padapter->device[SCpnt->target];// Pointer to device information
+	UCHAR			rc;											// command return code
+
+	SCpnt->scsi_done = done;
+	padapter->ide.ide.ides.spigot = pdev->spigot;
+	padapter->buffer = SCpnt->request_buffer;
+	if (done)
+		{
+		if ( !pdev->device )
+			{
+			SCpnt->result = DID_BAD_TARGET << 16;
+			done (SCpnt);
+			return 0;
+			}
+		}
+	else
+		{
+		printk("psi240i_queuecommand: %02X: done can't be NULL\n", *cdb);
+		return 0;
+		}
+
+	switch ( *cdb )
+		{
+		case SCSIOP_INQUIRY:   					// inquiry CDB
+			{
+			padapter->ide.ide.ide[6] = pdev->byte6;
+			padapter->ide.ide.ides.cmd = IDE_COMMAND_IDENTIFY;
+			break;
+			}
+
+		case SCSIOP_TEST_UNIT_READY:			// test unit ready CDB
+			SCpnt->result = DID_OK << 16;
+			done (SCpnt);
+			return 0;
+
+		case SCSIOP_READ_CAPACITY:			  	// read capctiy CDB
+			{
+			PREAD_CAPACITY_DATA	pdata = (PREAD_CAPACITY_DATA)SCpnt->request_buffer;
+
+			pdata->blksiz = 0x20000;
+			XANY2SCSI ((UCHAR *)&pdata->blks, pdev->blocks);
+			SCpnt->result = DID_OK << 16;
+			done (SCpnt);
+			return 0;
+			}
+
+		case SCSIOP_VERIFY:						// verify CDB
+			*(ULONG *)padapter->ide.ide.ides.lba = XSCSI2LONG (&cdb[2]);
+			padapter->ide.ide.ide[6] |= pdev->byte6;
+			padapter->ide.ide.ide[2] = (UCHAR)((USHORT)cdb[8] | ((USHORT)cdb[7] << 8));
+			padapter->ide.ide.ides.cmd = IDE_COMMAND_VERIFY;
+			break;
+
+		case SCSIOP_READ:						// read10 CDB
+			padapter->startSector = XSCSI2LONG (&cdb[2]);
+			padapter->sectorCount = (USHORT)cdb[8] | ((USHORT)cdb[7] << 8);
+			SetupTransfer (padapter, pdev->byte6);
+			padapter->ide.ide.ides.cmd = IDE_CMD_READ_MULTIPLE;
+			break;
+
+		case SCSIOP_READ6:						// read6  CDB
+			padapter->startSector = SCSI2LONG (&cdb[1]);
+			padapter->sectorCount = cdb[4];
+			SetupTransfer (padapter, pdev->byte6);
+			padapter->ide.ide.ides.cmd = IDE_CMD_READ_MULTIPLE;
+			break;
+
+		case SCSIOP_WRITE:						// write10 CDB
+			padapter->startSector = XSCSI2LONG (&cdb[2]);
+			padapter->sectorCount = (USHORT)cdb[8] | ((USHORT)cdb[7] << 8);
+			SetupTransfer (padapter, pdev->byte6);
+			padapter->ide.ide.ides.cmd = IDE_CMD_WRITE_MULTIPLE;
+			break;
+		case SCSIOP_WRITE6:						// write6  CDB
+			padapter->startSector = SCSI2LONG (&cdb[1]);
+			padapter->sectorCount = cdb[4];
+			SetupTransfer (padapter, pdev->byte6);
+			padapter->ide.ide.ides.cmd = IDE_CMD_WRITE_MULTIPLE;
+			break;
+
+		default:
+			DEB (printk ("psi240i_queuecommand: Unsupported command %02X\n", *cdb));
+			SCpnt->result = DID_ERROR << 16;
+			done (SCpnt);
+			return 0;
+		}
+
+	padapter->SCpnt = SCpnt;  									// Save this command data
+
+	rc = IdeCmd (padapter);
+	if ( rc )
+		{
+		padapter->expectingIRQ = 0;
+		DEB (printk ("psi240i_queuecommand: %02X, %02X: Device failed to respond for command\n", *cdb, padapter->ide.ide.ides.cmd));
+		SCpnt->result = DID_ERROR << 16;
+		done (SCpnt);
+		return 0;
+		}
+	DEB (printk("psi240i_queuecommand: %02X, %02X now waiting for interrupt ", *cdb, padapter->ide.ide.ides.cmd));
+	return 0;
+	}
+
+static void internal_done(Scsi_Cmnd * SCpnt)
+	{
+	SCpnt->SCp.Status++;
+	}
+/****************************************************************
+ *	Name:	Psi240i_Command
+ *
+ *	Description:	Process a command from the SCSI manager.
+ *
+ *	Parameters:		SCpnt - Pointer to SCSI command structure.
+ *
+ *	Returns:		Status code.
+ *
+ ****************************************************************/
+int Psi240i_Command (Scsi_Cmnd *SCpnt)
+	{
+	DEB(printk("psi240i_command: ..calling psi240i_queuecommand\n"));
+
+	Psi240i_QueueCommand (SCpnt, internal_done);
+
+    SCpnt->SCp.Status = 0;
+	while (!SCpnt->SCp.Status)
+		barrier ();
+	return SCpnt->result;
+	}
+/***************************************************************************
+ *	Name:			ReadChipMemory
+ *
+ *	Description:	Read information from controller memory.
+ *
+ *	Parameters:		psetup	- Pointer to memory image of setup information.
+ *					base	- base address of memory.
+ *					length	- lenght of data space in bytes.
+ *					port	- I/O address of data port.
+ *
+ *	Returns:		Nothing.
+ *
+ **************************************************************************/
+void ReadChipMemory (void *pdata, USHORT base, USHORT length, USHORT port)
+	{
+	USHORT	z, zz;
+	UCHAR	*pd = (UCHAR *)pdata;
+	outb_p (SEL_NONE, port + REG_SEL_FAIL);				// setup data port
+	zz = 0;
+	while ( zz < length )
+		{
+		outw_p (base, port + REG_ADDRESS);				// setup address
+
+		for ( z = 0;  z < 8;  z++ )
+			{
+			if ( (zz + z) < length )
+			*pd++ = inb_p (port + z);	// read data byte
+			}
+		zz += 8;
+		base += 8;
+		}
+	}
+/****************************************************************
+ *	Name:	Psi240i_Detect
+ *
+ *	Description:	Detect and initialize our boards.
+ *
+ *	Parameters:		tpnt - Pointer to SCSI host template structure.
+ *
+ *	Returns:		Number of adapters found.
+ *
+ ****************************************************************/
+int Psi240i_Detect (Scsi_Host_Template *tpnt)
+	{
+	int					board;
+	int					count = 0;
+	int					unit;
+	int					z;
+	USHORT				port;
+	CHIP_CONFIG_N		chipConfig;
+	CHIP_DEVICE_N		chipDevice[8];
+	struct Scsi_Host   *pshost;
+	ULONG				flags;
+
+	for ( board = 0;  board < 6;  board++ )					// scan for I/O ports
+		{
+		port = portAddr[board];								// get base address to test
+		if ( check_region (port, 16) )						// test for I/O addresses available
+			continue;										//   nope
+		if ( inb_p (port + REG_FAIL) != CHIP_ID )			// do the first test for likley hood that it is us
+			continue;
+		outb_p (SEL_NONE, port + REG_SEL_FAIL);				// setup EEPROM/RAM access
+		outw (0, port + REG_ADDRESS);						// setup EEPROM address zero
+		if ( inb_p (port) != 0x55 )							// test 1st byte
+			continue;										//   nope
+		if ( inb_p (port + 1) != 0xAA )						// test 2nd byte
+			continue;										//   nope
+
+		// at this point our board is found and can be accessed.  Now we need to initialize
+		// our informatation and register with the kernel.
+
+
+		ReadChipMemory (&chipConfig, CHIP_CONFIG, sizeof (chipConfig), port);
+		ReadChipMemory (&chipDevice, CHIP_DEVICE, sizeof (chipDevice), port);
+		ReadChipMemory (&ChipSetup, CHIP_EEPROM_DATA, sizeof (ChipSetup), port);
+
+		if ( !chipConfig.numDrives )						// if no devices on this board
+			continue;
+
+		pshost = scsi_register (tpnt, sizeof(ADAPTER240I));
+
+		save_flags (flags);
+		cli ();
+		if ( request_irq (chipConfig.irq, Irq_Handler, 0, "psi240i", NULL) )
+			{
+			printk ("Unable to allocate IRQ for PSI-240I controller.\n");
+			restore_flags (flags);
+			goto unregister;
+			}
+
+		PsiHost[chipConfig.irq - 10] = pshost;
+		pshost->unique_id = port;
+		pshost->io_port = port;
+		pshost->n_io_port = 16;  /* Number of bytes of I/O space used */
+		pshost->irq = chipConfig.irq;
+
+		for ( z = 0;  z < 11;  z++ )						// build regester address array
+			HOSTDATA(pshost)->ports[z] = port + z;
+		HOSTDATA(pshost)->ports[11] = port + REG_FAIL;
+		HOSTDATA(pshost)->ports[12] = port + REG_ALT_STAT;
+		DEB (printk ("\nPorts ="));
+		DEB (for (z=0;z<13;z++) printk(" %#04X",HOSTDATA(pshost)->ports[z]););
+
+		for ( z = 0;  z < chipConfig.numDrives;  ++z )
+			{
+			unit = chipDevice[z].channel & 0x0F;
+			HOSTDATA(pshost)->device[unit].device	 = ChipSetup.setupDevice[unit].device;
+			HOSTDATA(pshost)->device[unit].byte6	 = (UCHAR)(((unit & 1) << 4) | 0xE0);
+			HOSTDATA(pshost)->device[unit].spigot	 = (UCHAR)(1 << (unit >> 1));
+			HOSTDATA(pshost)->device[unit].sectors	 = ChipSetup.setupDevice[unit].sectors;
+			HOSTDATA(pshost)->device[unit].heads	 = ChipSetup.setupDevice[unit].heads;
+			HOSTDATA(pshost)->device[unit].cylinders = ChipSetup.setupDevice[unit].cylinders;
+			HOSTDATA(pshost)->device[unit].blocks	 = ChipSetup.setupDevice[unit].blocks;
+			DEB (printk ("\nHOSTDATA->device    = %X", HOSTDATA(pshost)->device[unit].device));
+			DEB (printk ("\n          byte6     = %X", HOSTDATA(pshost)->device[unit].byte6));
+			DEB (printk ("\n          spigot    = %X", HOSTDATA(pshost)->device[unit].spigot));
+			DEB (printk ("\n          sectors   = %X", HOSTDATA(pshost)->device[unit].sectors));
+			DEB (printk ("\n          heads     = %X", HOSTDATA(pshost)->device[unit].heads));
+			DEB (printk ("\n          cylinders = %X", HOSTDATA(pshost)->device[unit].cylinders));
+			DEB (printk ("\n          blocks    = %lX", HOSTDATA(pshost)->device[unit].blocks));
+			}
+
+		restore_flags (flags);
+		printk("\nPSI-240I EIDE CONTROLLER: at I/O = %x  IRQ = %d\n", port, chipConfig.irq);
+		printk("(C) 1997 Perceptive Solutions, Inc. All rights reserved\n\n");
+		count++;
+		continue;
+
+unregister:;
+		scsi_unregister (pshost);
+		}
+	return count;
+	}
+/****************************************************************
+ *	Name:	Psi240i_Abort
+ *
+ *	Description:	Process the Abort command from the SCSI manager.
+ *
+ *	Parameters:		SCpnt - Pointer to SCSI command structure.
+ *
+ *	Returns:		Allways snooze.
+ *
+ ****************************************************************/
+int Psi240i_Abort (Scsi_Cmnd *SCpnt)
+	{
+	DEB (printk ("psi240i_abort\n"));
+	return SCSI_ABORT_SNOOZE;
+	}
+/****************************************************************
+ *	Name:	Psi240i_Reset
+ *
+ *	Description:	Process the Reset command from the SCSI manager.
+ *
+ *	Parameters:		SCpnt - Pointer to SCSI command structure.
+ *					flags - Flags about the reset command
+ *
+ *	Returns:		No active command at this time, so this means
+ *					that each time we got some kind of response the
+ *					last time through.  Tell the mid-level code to
+ *					request sense information in order to decide what
+ *					to do next.
+ *
+ ****************************************************************/
+int Psi240i_Reset (Scsi_Cmnd *SCpnt, unsigned int reset_flags)
+	{
+	return SCSI_RESET_PUNT;
+	}
+
+#include "sd.h"
+
+/****************************************************************
+ *	Name:	Psi240i_BiosParam
+ *
+ *	Description:	Process the biosparam request from the SCSI manager to
+ *					return C/H/S data.
+ *
+ *	Parameters:		disk - Pointer to SCSI disk structure.
+ *					dev	 - Major/minor number from kernel.
+ *					geom - Pointer to integer array to place geometry data.
+ *
+ *	Returns:		zero.
+ *
+ ****************************************************************/
+int Psi240i_BiosParam (Scsi_Disk *disk, kdev_t dev, int geom[])
+	{
+	POUR_DEVICE	pdev;
+
+	pdev = &(HOSTDATA(disk->device->host)->device[disk->device->id]);
+
+	geom[0] = pdev->heads;
+	geom[1] = pdev->sectors;
+	geom[2] = pdev->cylinders;
+	return 0;
+	}
+
+
+#ifdef MODULE
+/* Eventually this will go into an include file, but this will be later */
+Scsi_Host_Template driver_template = PSI240I;
+
+#include "scsi_module.c"
+#endif
+
diff -u --recursive --new-file v2.1.62/linux/drivers/scsi/psi240i.h linux/drivers/scsi/psi240i.h
--- v2.1.62/linux/drivers/scsi/psi240i.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/scsi/psi240i.h	Wed Nov  5 13:10:00 1997
@@ -0,0 +1,344 @@
+/*+M*************************************************************************
+ * Perceptive Solutions, Inc. PSI-240I device driver proc support for Linux.
+ *
+ * Copyright (c) 1997 Perceptive Solutions, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING.  If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *
+ *	File Name:		psi240i.h
+ *
+ *	Description:	Header file for the SCSI driver for the PSI240I
+ *					EIDE interface card.
+ *
+ *-M*************************************************************************/
+#ifndef _PSI240I_H
+#define _PSI240I_H
+
+#include <linux/types.h>
+#include <linux/kdev_t.h>
+
+#ifndef	PSI_EIDE_SCSIOP
+#define	PSI_EIDE_SCSIOP	1
+
+/************************************************/
+/*		Some defines that we like 				*/
+/************************************************/
+#define	CHAR		char
+#define	UCHAR		unsigned char
+#define	SHORT		short
+#define	USHORT		unsigned short
+#define	BOOL		unsigned short
+#define	LONG		long
+#define	ULONG		unsigned long
+#define	VOID		void
+
+/************************************************/
+/*		Timeout konstants		 				*/
+/************************************************/
+#define	TIMEOUT_READY				10		// 100 mSec
+#define	TIMEOUT_DRQ					40		// 400 mSec
+
+/************************************************/
+/*		Misc. macros			 				*/
+/************************************************/
+#define ANY2SCSI(up, p)					\
+((UCHAR *)up)[0] = (((ULONG)(p)) >> 8);	\
+((UCHAR *)up)[1] = ((ULONG)(p));
+
+#define SCSI2LONG(up)					\
+( (((long)*(((UCHAR *)up))) << 16)		\
++ (((long)(((UCHAR *)up)[1])) << 8)		\
++ ((long)(((UCHAR *)up)[2])) )
+
+#define XANY2SCSI(up, p)				\
+((UCHAR *)up)[0] = ((long)(p)) >> 24;	\
+((UCHAR *)up)[1] = ((long)(p)) >> 16;	\
+((UCHAR *)up)[2] = ((long)(p)) >> 8;	\
+((UCHAR *)up)[3] = ((long)(p));
+
+#define XSCSI2LONG(up)					\
+( (((long)(((UCHAR *)up)[0])) << 24)	\
++ (((long)(((UCHAR *)up)[1])) << 16)	\
++ (((long)(((UCHAR *)up)[2])) <<  8)	\
++ ((long)(((UCHAR *)up)[3])) )
+
+/************************************************/
+/*		SCSI CDB operation codes 				*/
+/************************************************/
+#define SCSIOP_TEST_UNIT_READY		0x00
+#define SCSIOP_REZERO_UNIT			0x01
+#define SCSIOP_REWIND				0x01
+#define SCSIOP_REQUEST_BLOCK_ADDR	0x02
+#define SCSIOP_REQUEST_SENSE		0x03
+#define SCSIOP_FORMAT_UNIT			0x04
+#define SCSIOP_READ_BLOCK_LIMITS	0x05
+#define SCSIOP_REASSIGN_BLOCKS		0x07
+#define SCSIOP_READ6				0x08
+#define SCSIOP_RECEIVE				0x08
+#define SCSIOP_WRITE6				0x0A
+#define SCSIOP_PRINT				0x0A
+#define SCSIOP_SEND					0x0A
+#define SCSIOP_SEEK6				0x0B
+#define SCSIOP_TRACK_SELECT			0x0B
+#define SCSIOP_SLEW_PRINT			0x0B
+#define SCSIOP_SEEK_BLOCK			0x0C
+#define SCSIOP_PARTITION			0x0D
+#define SCSIOP_READ_REVERSE			0x0F
+#define SCSIOP_WRITE_FILEMARKS		0x10
+#define SCSIOP_FLUSH_BUFFER			0x10
+#define SCSIOP_SPACE				0x11
+#define SCSIOP_INQUIRY				0x12
+#define SCSIOP_VERIFY6				0x13
+#define SCSIOP_RECOVER_BUF_DATA		0x14
+#define SCSIOP_MODE_SELECT			0x15
+#define SCSIOP_RESERVE_UNIT			0x16
+#define SCSIOP_RELEASE_UNIT			0x17
+#define SCSIOP_COPY					0x18
+#define SCSIOP_ERASE				0x19
+#define SCSIOP_MODE_SENSE			0x1A
+#define SCSIOP_START_STOP_UNIT		0x1B
+#define SCSIOP_STOP_PRINT			0x1B
+#define SCSIOP_LOAD_UNLOAD			0x1B
+#define SCSIOP_RECEIVE_DIAGNOSTIC	0x1C
+#define SCSIOP_SEND_DIAGNOSTIC		0x1D
+#define SCSIOP_MEDIUM_REMOVAL		0x1E
+#define SCSIOP_READ_CAPACITY		0x25
+#define SCSIOP_READ					0x28
+#define SCSIOP_WRITE				0x2A
+#define SCSIOP_SEEK					0x2B
+#define SCSIOP_LOCATE				0x2B
+#define SCSIOP_WRITE_VERIFY			0x2E
+#define SCSIOP_VERIFY				0x2F
+#define SCSIOP_SEARCH_DATA_HIGH		0x30
+#define SCSIOP_SEARCH_DATA_EQUAL	0x31
+#define SCSIOP_SEARCH_DATA_LOW		0x32
+#define SCSIOP_SET_LIMITS			0x33
+#define SCSIOP_READ_POSITION		0x34
+#define SCSIOP_SYNCHRONIZE_CACHE	0x35
+#define SCSIOP_COMPARE				0x39
+#define SCSIOP_COPY_COMPARE			0x3A
+#define SCSIOP_WRITE_DATA_BUFF		0x3B
+#define SCSIOP_READ_DATA_BUFF		0x3C
+#define SCSIOP_CHANGE_DEFINITION	0x40
+#define SCSIOP_READ_SUB_CHANNEL		0x42
+#define SCSIOP_READ_TOC				0x43
+#define SCSIOP_READ_HEADER			0x44
+#define SCSIOP_PLAY_AUDIO			0x45
+#define SCSIOP_PLAY_AUDIO_MSF		0x47
+#define SCSIOP_PLAY_TRACK_INDEX		0x48
+#define SCSIOP_PLAY_TRACK_RELATIVE	0x49
+#define SCSIOP_PAUSE_RESUME			0x4B
+#define SCSIOP_LOG_SELECT			0x4C
+#define SCSIOP_LOG_SENSE			0x4D
+#define SCSIOP_MODE_SELECT10		0x55
+#define SCSIOP_MODE_SENSE10			0x5A
+#define SCSIOP_LOAD_UNLOAD_SLOT		0xA6
+#define SCSIOP_MECHANISM_STATUS		0xBD
+#define SCSIOP_READ_CD				0xBE
+
+// IDE command definitions
+#define IDE_COMMAND_ATAPI_RESET		0x08
+#define IDE_COMMAND_READ			0x20
+#define IDE_COMMAND_WRITE			0x30
+#define IDE_COMMAND_RECALIBRATE		0x10
+#define IDE_COMMAND_SEEK			0x70
+#define IDE_COMMAND_SET_PARAMETERS	0x91
+#define IDE_COMMAND_VERIFY			0x40
+#define IDE_COMMAND_ATAPI_PACKET	0xA0
+#define IDE_COMMAND_ATAPI_IDENTIFY	0xA1
+#define	IDE_CMD_READ_MULTIPLE		0xC4
+#define	IDE_CMD_WRITE_MULTIPLE		0xC5
+#define	IDE_CMD_SET_MULTIPLE		0xC6
+#define IDE_COMMAND_WRITE_DMA		0xCA
+#define IDE_COMMAND_READ_DMA		0xC8
+#define IDE_COMMAND_IDENTIFY		0xEC
+
+// IDE status definitions
+#define IDE_STATUS_ERROR			0x01
+#define IDE_STATUS_INDEX			0x02
+#define IDE_STATUS_CORRECTED_ERROR	0x04
+#define IDE_STATUS_DRQ				0x08
+#define IDE_STATUS_DSC				0x10
+#define	IDE_STATUS_WRITE_FAULT		0x20
+#define IDE_STATUS_DRDY				0x40
+#define IDE_STATUS_BUSY				0x80
+
+// IDE error definitions
+#define	IDE_ERROR_AMNF				0x01
+#define	IDE_ERROR_TKONF				0x02
+#define	IDE_ERROR_ABRT				0x04
+#define	IDE_ERROR_MCR				0x08
+#define	IDE_ERROR_IDFN				0x10
+#define	IDE_ERROR_MC				0x20
+#define	IDE_ERROR_UNC				0x40
+#define	IDE_ERROR_BBK				0x80
+
+//	IDE interface structure
+typedef struct _IDE_STRUCT
+	{
+	union
+		{
+		UCHAR	ide[9];
+		struct
+			{
+			USHORT	data;
+			UCHAR	sectors;
+			UCHAR	lba[4];
+			UCHAR	cmd;
+			UCHAR	spigot;
+			}	ides;
+		} ide;
+	}	IDE_STRUCT;
+
+// SCSI read capacity structure
+typedef	struct _READ_CAPACITY_DATA
+	{
+	ULONG blks;				/* total blocks (converted to little endian) */
+	ULONG blksiz;			/* size of each (converted to little endian) */
+	}	READ_CAPACITY_DATA, *PREAD_CAPACITY_DATA;
+
+// SCSI inquiry data
+typedef struct _INQUIRYDATA
+	{
+	UCHAR DeviceType			:5;
+	UCHAR DeviceTypeQualifier	:3;
+	UCHAR DeviceTypeModifier	:7;
+	UCHAR RemovableMedia		:1;
+    UCHAR Versions;
+    UCHAR ResponseDataFormat;
+    UCHAR AdditionalLength;
+    UCHAR Reserved[2];
+	UCHAR SoftReset				:1;
+	UCHAR CommandQueue			:1;
+	UCHAR Reserved2				:1;
+	UCHAR LinkedCommands		:1;
+	UCHAR Synchronous			:1;
+	UCHAR Wide16Bit				:1;
+	UCHAR Wide32Bit				:1;
+	UCHAR RelativeAddressing	:1;
+    UCHAR VendorId[8];
+    UCHAR ProductId[16];
+    UCHAR ProductRevisionLevel[4];
+    UCHAR VendorSpecific[20];
+    UCHAR Reserved3[40];
+	}	INQUIRYDATA, *PINQUIRYDATA;
+
+// IDE IDENTIFY data
+typedef struct _IDENTIFY_DATA
+	{
+    USHORT GeneralConfiguration;            // 00
+    USHORT NumberOfCylinders;               // 02
+    USHORT Reserved1;                       // 04
+    USHORT NumberOfHeads;                   // 06
+    USHORT UnformattedBytesPerTrack;        // 08
+    USHORT UnformattedBytesPerSector;       // 0A
+    USHORT SectorsPerTrack;                 // 0C
+    USHORT VendorUnique1[3];                // 0E
+    USHORT SerialNumber[10];                // 14
+    USHORT BufferType;                      // 28
+    USHORT BufferSectorSize;                // 2A
+    USHORT NumberOfEccBytes;                // 2C
+    USHORT FirmwareRevision[4];             // 2E
+    USHORT ModelNumber[20];                 // 36
+    UCHAR  MaximumBlockTransfer;            // 5E
+    UCHAR  VendorUnique2;                   // 5F
+    USHORT DoubleWordIo;                    // 60
+    USHORT Capabilities;                    // 62
+    USHORT Reserved2;                       // 64
+    UCHAR  VendorUnique3;                   // 66
+    UCHAR  PioCycleTimingMode;              // 67
+    UCHAR  VendorUnique4;                   // 68
+    UCHAR  DmaCycleTimingMode;              // 69
+    USHORT TranslationFieldsValid:1;        // 6A
+    USHORT Reserved3:15;
+    USHORT NumberOfCurrentCylinders;        // 6C
+    USHORT NumberOfCurrentHeads;            // 6E
+    USHORT CurrentSectorsPerTrack;          // 70
+    ULONG  CurrentSectorCapacity;           // 72
+    USHORT Reserved4[197];                  // 76
+	}	IDENTIFY_DATA, *PIDENTIFY_DATA;
+
+// Identify data without the Reserved4.
+typedef struct _IDENTIFY_DATA2 {
+    USHORT GeneralConfiguration;            // 00
+    USHORT NumberOfCylinders;               // 02
+    USHORT Reserved1;                       // 04
+    USHORT NumberOfHeads;                   // 06
+    USHORT UnformattedBytesPerTrack;        // 08
+    USHORT UnformattedBytesPerSector;       // 0A
+    USHORT SectorsPerTrack;                 // 0C
+    USHORT VendorUnique1[3];                // 0E
+    USHORT SerialNumber[10];                // 14
+    USHORT BufferType;                      // 28
+    USHORT BufferSectorSize;                // 2A
+    USHORT NumberOfEccBytes;                // 2C
+    USHORT FirmwareRevision[4];             // 2E
+    USHORT ModelNumber[20];                 // 36
+    UCHAR  MaximumBlockTransfer;            // 5E
+    UCHAR  VendorUnique2;                   // 5F
+    USHORT DoubleWordIo;                    // 60
+    USHORT Capabilities;                    // 62
+    USHORT Reserved2;                       // 64
+    UCHAR  VendorUnique3;                   // 66
+    UCHAR  PioCycleTimingMode;              // 67
+    UCHAR  VendorUnique4;                   // 68
+    UCHAR  DmaCycleTimingMode;              // 69
+	USHORT TranslationFieldsValid:1;     	// 6A
+    USHORT Reserved3:15;
+    USHORT NumberOfCurrentCylinders;        // 6C
+    USHORT NumberOfCurrentHeads;            // 6E
+    USHORT CurrentSectorsPerTrack;          // 70
+    ULONG  CurrentSectorCapacity;           // 72
+	}	IDENTIFY_DATA2, *PIDENTIFY_DATA2;
+
+#endif	// PSI_EIDE_SCSIOP
+
+// function prototypes
+int Psi240i_Detect			(Scsi_Host_Template *tpnt);
+int Psi240i_Command			(Scsi_Cmnd *SCpnt);
+int Psi240i_QueueCommand	(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *));
+int Psi240i_Abort			(Scsi_Cmnd *SCpnt);
+int Psi240i_Reset			(Scsi_Cmnd *SCpnt, unsigned int flags);
+int Psi240i_BiosParam		(Disk *disk, kdev_t dev, int geom[]);
+
+#ifndef NULL
+	#define NULL 0
+#endif
+
+extern struct proc_dir_entry Proc_Scsi_Psi240i;
+
+#define PSI240I { NULL, NULL,						\
+			&Proc_Scsi_Psi240i,/* proc_dir_entry */	\
+			NULL,		                			\
+			"PSI-240I EIDE Disk Controller",		\
+			Psi240i_Detect,							\
+			NULL,									\
+			NULL,	 								\
+			Psi240i_Command,						\
+			Psi240i_QueueCommand,					\
+			Psi240i_Abort,							\
+			Psi240i_Reset,							\
+			NULL,									\
+			Psi240i_BiosParam,                 		\
+			1, 										\
+			-1, 									\
+			SG_NONE,		 						\
+			1, 										\
+			0, 										\
+			0, 										\
+			DISABLE_CLUSTERING }
+
+#endif
diff -u --recursive --new-file v2.1.62/linux/drivers/scsi/psi_chip.h linux/drivers/scsi/psi_chip.h
--- v2.1.62/linux/drivers/scsi/psi_chip.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/scsi/psi_chip.h	Wed Nov  5 13:10:06 1997
@@ -0,0 +1,194 @@
+/*+M*************************************************************************
+ * Perceptive Solutions, Inc. PSI-240I device driver proc support for Linux.
+ *
+ * Copyright (c) 1997 Perceptive Solutions, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING.  If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *
+ *	File Name:	psi_chip.h
+ *
+ *	Description:	This file contains the interface defines and
+ *					error codes.
+ *
+ *-M*************************************************************************/
+#ifndef PSI_CHIP
+#define PSI_CHIP
+
+/************************************************/
+/*		Misc konstants							*/
+/************************************************/
+#define	CHIP_MAXDRIVES			8
+
+/************************************************/
+/*		Chip I/O addresses						*/
+/************************************************/
+#define	CHIP_ADRS_0				0x0130
+#define	CHIP_ADRS_1				0x0150
+#define	CHIP_ADRS_2				0x0190
+#define	CHIP_ADRS_3				0x0210
+#define	CHIP_ADRS_4				0x0230
+#define	CHIP_ADRS_5				0x0250
+
+/************************************************/
+/*		EEPROM locations		*/
+/************************************************/
+#define	CHIP_EEPROM_BIOS		0x0000		// BIOS base address
+#define	CHIP_EEPROM_DATA		0x2000	   	// SETUP data base address
+#define	CHIP_EEPROM_FACTORY		0x2400	   	// FACTORY data base address
+#define	CHIP_EEPROM_SETUP		0x3000	   	// SETUP PROGRAM base address
+
+#define	CHIP_EEPROM_SIZE		32768U	   	// size of the entire EEPROM
+#define	CHIP_EEPROM_BIOS_SIZE	8192	   	// size of the BIOS in bytes
+#define	CHIP_EEPROM_DATA_SIZE	4096	   	// size of factory, setup, log data block in bytes
+#define	CHIP_EEPROM_SETUP_SIZE	20480U	   	// size of the setup program in bytes
+
+/************************************************/
+/*		Chip Interrupts							*/
+/************************************************/
+#define	CHIP_IRQ_10				0x72
+#define	CHIP_IRQ_11				0x73
+#define	CHIP_IRQ_12				0x74
+
+/************************************************/
+/*		Chip Setup addresses		*/
+/************************************************/
+#define	CHIP_SETUP_BASE			0x0000C000L
+
+/************************************************/
+/*		Chip Register address offsets	*/
+/************************************************/
+#define	REG_DATA				0x00
+#define	REG_ERROR				0x01
+#define	REG_SECTOR_COUNT		0x02
+#define	REG_LBA_0				0x03
+#define	REG_LBA_8				0x04
+#define	REG_LBA_16				0x05
+#define	REG_LBA_24				0x06
+#define	REG_STAT_CMD			0x07
+#define	REG_SEL_FAIL			0x08
+#define	REG_IRQ_STATUS			0x09
+#define	REG_ADDRESS				0x0A
+#define	REG_FAIL				0x0C
+#define	REG_ALT_STAT		   	0x0E
+#define	REG_DRIVE_ADRS			0x0F
+
+/************************************************/
+/*		Chip RAM locations		*/
+/************************************************/
+#define	CHIP_DEVICE				0x8000
+#define	CHIP_DEVICE_0			0x8000
+#define CHIP_DEVICE_1			0x8008
+#define	CHIP_DEVICE_2			0x8010
+#define	CHIP_DEVICE_3			0x8018
+#define	CHIP_DEVICE_4			0x8020
+#define	CHIP_DEVICE_5			0x8028
+#define	CHIP_DEVICE_6			0x8030
+#define	CHIP_DEVICE_7			0x8038
+typedef struct
+	{
+	UCHAR	channel;		// channel of this device (0-8).
+	UCHAR	spt;			// Sectors Per Track.
+	ULONG	spc;			// Sectors Per Cylinder.
+	}	CHIP_DEVICE_N;
+
+#define	CHIP_CONFIG				0x8100		// address of boards configuration.
+typedef struct
+	{
+	UCHAR		irq;			// interrupt request channel number
+	UCHAR		numDrives;		// Number of accessable drives
+	UCHAR		fastFormat;	 	// Boolean for fast format enable
+	}	CHIP_CONFIG_N;
+
+#define	CHIP_MAP				0x8108 		// eight byte device type map.
+
+
+#define	CHIP_RAID				0x8120 		// array of RAID signature structures and LBA
+#define	CHIP_RAID_1				0x8120
+#define CHIP_RAID_2				0x8130
+#define	CHIP_RAID_3				0x8140
+#define	CHIP_RAID_4				0x8150
+
+/************************************************/
+/*		Chip Register Masks		*/
+/************************************************/
+#define	CHIP_ID					0x7B
+#define	SEL_RAM					0x8000
+#define	MASK_FAIL				0x80
+
+/************************************************/
+/*		Chip cable select bits		*/
+/************************************************/
+#define	SECTORSXFER				8
+
+/************************************************/
+/*		Chip cable select bits		*/
+/************************************************/
+#define	SEL_NONE				0x00
+#define	SEL_1					0x01
+#define	SEL_2					0x02
+#define	SEL_3					0x04
+#define	SEL_4					0x08
+
+/************************************************/
+/*		Programmable Interrupt Controller*/
+/************************************************/
+#define	PIC1					0x20		// first 8259 base port address
+#define	PIC2					0xA0		// second 8259 base port address
+#define	INT_OCW1				1			// Operation Control Word 1: IRQ mask
+#define	EOI						0x20		// non-specific end-of-interrupt
+
+/************************************************/
+/*		Device/Geometry controls				*/
+/************************************************/
+#define GEOMETRY_NONE		 	0x0			// No device
+#define GEOMETRY_AUTO			0x1			// Geometry set automatically
+#define GEOMETRY_USER		 	0x2			// User supplied geometry
+
+#define	DEVICE_NONE				0x0			// No device present
+#define	DEVICE_INACTIVE			0x1			// device present but not registered active
+#define	DEVICE_ATAPI			0x2			// ATAPI device (CD_ROM, Tape, Etc...)
+#define	DEVICE_DASD_NONLBA		0x3			// Non LBA incompatible device
+#define	DEVICE_DASD_LBA			0x4			// LBA compatible device
+
+/************************************************/
+/*		Setup Structure Definitions	*/
+/************************************************/
+typedef struct							// device setup parameters
+	{
+	UCHAR			geometryControl;	// geometry control flags
+	UCHAR		   	device;				// device code
+	USHORT			sectors;			// number of sectors per track
+	USHORT			heads;				// number of heads
+	USHORT			cylinders;			// number of cylinders for this device
+	ULONG			blocks;				// number of blocks on device
+	USHORT			spare1;
+	USHORT			spare2;
+	} SETUP_DEVICE, *PSETUP_DEVICE;
+
+typedef struct		// master setup structure
+	{
+	USHORT 			startupDelay;
+	USHORT 			promptBIOS;
+	USHORT 			fastFormat;
+	USHORT			spare2;
+	USHORT			spare3;
+	USHORT			spare4;
+	USHORT			spare5;
+	USHORT			spare6;
+	SETUP_DEVICE	setupDevice[8];
+	}	SETUP, *PSETUP;
+
+#endif
\ No newline at end of file
diff -u --recursive --new-file v2.1.62/linux/drivers/scsi/psi_dale.h linux/drivers/scsi/psi_dale.h
--- v2.1.62/linux/drivers/scsi/psi_dale.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/scsi/psi_dale.h	Wed Nov  5 13:06:38 1997
@@ -0,0 +1,187 @@
+/*+M*************************************************************************
+ * Perceptive Solutions, Inc. PCI-2000 device driver proc support for Linux.
+ *
+ * Copyright (c) 1997 Perceptive Solutions, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING.  If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *
+ *	File Name:		psi_dale.h
+ *
+ *	Description:	This file contains the interface defines and
+ *					error codes.
+ *
+ *-M*************************************************************************/
+
+#ifndef PSI_DALE
+#define PSI_DALE
+
+/************************************************/
+/*		Dale PCI setup							*/
+/************************************************/
+#define	VENDOR_PSI			0x1256
+#define	DEVICE_DALE_1		0x4401		/* 'D1' */
+
+/************************************************/
+/*		Misc konstants							*/
+/************************************************/
+#define	DALE_MAXDRIVES			4
+#define	SECTORSXFER				8
+#define	BYTES_PER_SECTOR		512
+#define	DEFAULT_TIMING_MODE		5
+
+/************************************************/
+/*		EEPROM locations						*/
+/************************************************/
+#define	DALE_FLASH_PAGE_SIZE	128				// number of bytes per page
+#define	DALE_FLASH_SIZE			65536L
+
+#define	DALE_FLASH_BIOS			0x00080000L		// BIOS base address
+#define	DALE_FLASH_SETUP		0x00088000L		// SETUP PROGRAM base address offset from BIOS
+#define	DALE_FLASH_RAID			0x00088400L		// RAID signature storage
+#define	DALE_FLASH_FACTORY		0x00089000L		// FACTORY data base address offset from BIOS
+
+#define	DALE_FLASH_BIOS_SIZE	32768U			// size of FLASH BIOS REGION
+
+/************************************************/
+/*		DALE Register address offsets			*/
+/************************************************/
+#define	REG_DATA				0x80
+#define	REG_ERROR				0x84
+#define	REG_SECTOR_COUNT		0x88
+#define	REG_LBA_0				0x8C
+#define	REG_LBA_8				0x90
+#define	REG_LBA_16				0x94
+#define	REG_LBA_24				0x98
+#define	REG_STAT_CMD			0x9C
+#define	REG_STAT_SEL			0xA0
+#define	REG_FAIL				0xB0
+#define	REG_ALT_STAT			0xB8
+#define	REG_DRIVE_ADRS			0xBC
+
+#define	DALE_DATA_SLOW			0x00040000L
+#define	DALE_DATA_MODE2			0x00040000L
+#define	DALE_DATA_MODE3			0x00050000L
+#define	DALE_DATA_MODE4			0x00060000L
+#define	DALE_DATA_MODE4P		0x00070000L
+
+#define RTR_LOCAL_RANGE					0x000
+#define RTR_LOCAL_REMAP					0x004
+#define RTR_EXP_RANGE					0x010
+#define RTR_EXP_REMAP					0x014
+#define RTR_REGIONS						0x018
+#define RTR_DM_MASK						0x01C
+#define RTR_DM_LOCAL_BASE				0x020
+#define RTR_DM_IO_BASE					0x024
+#define RTR_DM_PCI_REMAP				0x028
+#define RTR_DM_IO_CONFIG				0x02C
+#define RTR_MAILBOX						0x040
+#define RTR_LOCAL_DOORBELL				0x060
+#define RTR_PCI_DOORBELL				0x064
+#define RTR_INT_CONTROL_STATUS 			0x068
+#define RTR_EEPROM_CONTROL_STATUS		0x06C
+
+#define RTL_DMA0_MODE					0x00
+#define RTL_DMA0_PCI_ADDR				0x04
+#define RTL_DMA0_LOCAL_ADDR				0x08
+#define RTL_DMA0_COUNT					0x0C
+#define RTL_DMA0_DESC_PTR				0x10
+#define RTL_DMA1_MODE					0x14
+#define RTL_DMA1_PCI_ADDR				0x18
+#define RTL_DMA1_LOCAL_ADDR				0x1C
+#define RTL_DMA1_COUNT					0x20
+#define RTL_DMA1_DESC_PTR				0x24
+#define RTL_DMA_COMMAND_STATUS			0x28
+#define RTL_DMA_ARB0					0x2C
+#define RTL_DMA_ARB1					0x30
+
+/************************************************/
+/*		Dale Scratchpad locations				*/
+/************************************************/
+#define	DALE_CHANNEL_DEVICE_0	0		// device channel locations
+#define	DALE_CHANNEL_DEVICE_1	1
+#define	DALE_CHANNEL_DEVICE_2	2
+#define	DALE_CHANNEL_DEVICE_3	3
+
+#define	DALE_SCRATH_DEVICE_0	4		// device type codes
+#define	DALE_SCRATH_DEVICE_1	5
+#define DALE_SCRATH_DEVICE_2	6
+#define	DALE_SCRATH_DEVICE_3	7
+
+#define	DALE_RAID_0_STATUS		8
+#define DALE_RAID_1_STATUS		9
+
+#define	DALE_TIMING_MODE		12		// bus master timing mode (2, 3, 4, 5)
+#define	DALE_NUM_DRIVES			13		// number of addressable drives on this board
+#define	DALE_RAID_ON			14 		// RAID status On
+#define	DALE_LAST_ERROR			15		// Last error code from BIOS
+
+/************************************************/
+/*		Dale cable select bits					*/
+/************************************************/
+#define	SEL_NONE				0x00
+#define	SEL_1					0x01
+#define	SEL_2					0x02
+
+/************************************************/
+/*		Programmable Interrupt Controller		*/
+/************************************************/
+#define	PIC1					0x20				// first 8259 base port address
+#define	PIC2					0xA0				// second 8259 base port address
+#define	INT_OCW1				1					// Operation Control Word 1: IRQ mask
+#define	EOI						0x20				// non-specific end-of-interrupt
+
+/************************************************/
+/*		Device/Geometry controls				*/
+/************************************************/
+#define GEOMETRY_NONE	 	0x0			// No device
+#define GEOMETRY_SET		0x1			// Geometry set
+#define	GEOMETRY_LBA		0x2			// Geometry set in default LBA mode
+#define	GEOMETRY_PHOENIX	0x3			// Geometry set in Pheonix BIOS compatibility mode
+
+#define	DEVICE_NONE			0x0			// No device present
+#define	DEVICE_INACTIVE		0x1			// device present but not registered active
+#define	DEVICE_ATAPI		0x2			// ATAPI device (CD_ROM, Tape, Etc...)
+#define	DEVICE_DASD_NONLBA	0x3			// Non LBA incompatible device
+#define	DEVICE_DASD_LBA		0x4			// LBA compatible device
+
+/************************************************/
+/*		Setup Structure Definitions				*/
+/************************************************/
+typedef struct		// device setup parameters
+	{
+	UCHAR	geometryControl;	// geometry control flags
+	UCHAR	device;				// device code
+	USHORT	sectors;			// number of sectors per track
+	USHORT	heads;				// number of heads
+	USHORT	cylinders;			// number of cylinders for this device
+	ULONG	blocks;				// number of blocks on device
+	ULONG	realCapacity;		// number of real blocks on this device for drive changed testing
+	} SETUP_DEVICE, *PSETUP_DEVICE;
+
+typedef struct		// master setup structure
+	{
+	USHORT			startupDelay;
+	BOOL			promptBIOS;
+	BOOL			fastFormat;
+	BOOL			shareInterrupt;
+	BOOL			rebootRebuil;
+	USHORT			timingMode;
+	USHORT			spare5;
+	USHORT			spare6;
+	SETUP_DEVICE	setupDevice[4];
+	}	SETUP, *PSETUP;
+
+#endif
diff -u --recursive --new-file v2.1.62/linux/drivers/scsi/psi_roy.h linux/drivers/scsi/psi_roy.h
--- v2.1.62/linux/drivers/scsi/psi_roy.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/scsi/psi_roy.h	Wed Nov  5 13:00:30 1997
@@ -0,0 +1,314 @@
+/*+M*************************************************************************
+ * Perceptive Solutions, Inc. PCI-2000 device driver proc support for Linux.
+ *
+ * Copyright (c) 1997 Perceptive Solutions, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING.  If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *
+ *	File Name:	psi_roy.h
+ *
+ *	Description:	This file contains the host interface command and
+ *					error codes.
+ *
+ *-M*************************************************************************/
+
+#ifndef	ROY_HOST
+#define	ROY_HOST
+
+/************************************************/
+/*		PCI setup								*/
+/************************************************/
+#define	VENDOR_PSI			0x1256
+#define	DEVICE_ROY_1		0x5201		/* 'R1' */
+
+/************************************************/
+/*		controller constants					*/
+/************************************************/
+#define MAXADAPTER			4			// Increase this and the sizes of the arrays below, if you need more.
+#define	MAX_BUS				2
+#define	MAX_UNITS			16
+#define	TIMEOUT_COMMAND		30 			// number of jiffies for command busy timeout
+
+/************************************************/
+/*		I/O address offsets						*/
+/************************************************/
+#define RTR_MAILBOX						0x040
+#define RTR_LOCAL_DOORBELL				0x060
+#define RTR_PCI_DOORBELL				0x064
+
+/************************************************/
+/*												*/
+/*			Host command codes					*/
+/*												*/
+/************************************************/
+#define	CMD_READ_CHS		0x01		/* read sectors as specified (CHS mode) */
+#define	CMD_READ			0x02		/* read sectors as specified (RBA mode) */
+#define	CMD_READ_SG			0x03		/* read sectors using scatter/gather list */
+#define	CMD_WRITE_CHS		0x04		/* write sectors as specified (CHS mode) */
+#define	CMD_WRITE			0x05		/* write sectors as specified (RBA mode) */
+#define	CMD_WRITE_SG		0x06		/* write sectors using scatter/gather list (LBA mode) */
+#define	CMD_READ_CHS_SG		0x07		/* read sectors using scatter/gather list (CHS mode) */
+#define	CMD_WRITE_CHS_SG	0x08		/* write sectors using scatter/gather list (CHS mode) */
+#define	CMD_VERIFY_CHS		0x09		/* verify data on sectors as specified (CHS mode) */
+#define	CMD_VERIFY			0x0A		/* verify data on sectors as specified (RBA mode) */
+
+#define	CMD_READ_ABS		0x10		/* read absolute disk */
+#define	CMD_WRITE_ABS		0x11		/* write absolute disk */
+#define	CMD_VERIFY_ABS		0x12		/* verify absolute disk */
+#define	CMD_TEST_READY		0x13		/* test unit ready and return status code */
+#define	CMD_LOCK_DOOR		0x14		/* lock device door */
+#define	CMD_UNLOCK_DOOR		0x15		/* unlock device door */
+#define	CMD_EJECT_MEDIA		0x16		/* eject the media */
+#define	CMD_UPDATE_CAP		0x17		/* update capacity information */
+#define	CMD_TEST_PRIV		0x18		/* test and setup private format media */
+
+
+#define	CMD_SCSI_THRU		0x30		/* SCSI pass through CDB */
+#define	CMD_SCSI_THRU_SG	0x31		/* SCSI pass through CDB with scatter/gather */
+#define	CMD_SCSI_REQ_SENSE	0x32		/* SCSI pass through request sense after check condition */
+
+#define	CMD_DASD_SCSI_INQ	0x36		/* to DASD inquire for DASD info in SCSI inquiry format */
+#define	CMD_DASD_CAP		0x37		/* read DASD capacity */
+#define	CMD_DASD_INQ		0x38		/* do DASD inquire for type data */
+#define	CMD_SCSI_INQ		0x39		/* do SCSI inquire */
+#define	CMD_READ_SETUP		0x3A		/* Get setup structures from controller */
+#define	CMD_WRITE_SETUP		0x3B		/* Put setup structures in controller and burn in flash */
+#define	CMD_READ_CONFIG		0x3C		/* Get the entire configuration and setup structures */
+#define	CMD_WRITE_CONFIG	0x3D		/* Put the entire configuration and setup structures in flash */
+
+#define	CMD_TEXT_DEVICE		0x3E		/* obtain device text */
+#define	CMD_TEXT_SIGNON		0x3F		/* get sign on banner */
+
+#define	CMD_QUEUE			0x40		/* any command below this generates a queue tag interrupt to host*/
+
+#define	CMD_PREFETCH		0x40		/* prefetch sectors as specified */
+#define	CMD_TEST_WRITE		0x41		/* Test a device for write protect */
+#define	CMD_LAST_STATUS		0x42		/* get last command status and error data*/
+#define	CMD_ABORT			0x43		/* abort command as specified */
+#define	CMD_ERROR			0x44		/* fetch error code from a tagged op */
+#define	CMD_DONE			0x45		/* done with operation */
+#define	CMD_DIAGNOSTICS		0x46		/* execute controller diagnostics and wait for results */
+#define	CMD_FEATURE_MODE	0x47		/* feature mode control word */
+#define	CMD_DASD_INQUIRE	0x48		/* inquire as to DASD SCSI device (32 possible) */
+#define	CMD_FEATURE_QUERY	0x49		/* query the feature control word */
+#define	CMD_DASD_EJECT		0x4A		/* Eject removable media for DASD type */
+#define	CMD_DASD_LOCK		0x4B		/* Lock removable media for DASD type */
+#define	CMD_DASD_TYPE		0x4C		/* obtain DASD device type */
+#define	CMD_NUM_DEV			0x4D		/* obtain the number of devices connected to the controller */
+#define	CMD_GET_PARMS		0x4E		/* obtain device parameters */
+#define	CMD_SPECIFY			0x4F		/* specify operating system for scatter/gather operations */
+
+#define	CMD_RAID_GET_DEV	0x50		/* read RAID device geometry */
+#define CMD_RAID_READ		0x51		/* read RAID 1 parameter block */
+#define	CMD_RAID_WRITE		0x52		/* write RAID 1 parameter block */
+#define	CMD_RAID_LITEUP		0x53		/* Light up the drive light for identification */
+#define	CMD_RAID_REBUILD	0x54		/* issue a RAID 1 pair rebuild */
+#define	CMD_RAID_MUTE		0x55		/* mute RAID failure alarm */
+#define	CMD_RAID_FAIL		0x56		/* induce a RAID failure */
+#define	CMD_RAID_STATUS		0x57		/* get status of RAID pair */
+#define	CMD_RAID_STOP		0x58		/* stop any reconstruct in progress */
+#define CMD_RAID_START		0x59		/* start reconstruct */
+
+#define	CMD_SCSI_GET		0x60		/* get SCSI pass through devices */
+#define	CMD_SCSI_TIMEOUT	0x61		/* set SCSI pass through timeout */
+#define	CMD_SCSI_ERROR		0x62		/* get SCSI pass through request sense length and residual data count */
+#define	CMD_GET_SPARMS		0x63		/* get SCSI bus and user parms */
+#define	CMD_SCSI_ABORT		0x64		/* abort by setting time-out to zero */
+
+#define	CMD_CHIRP_CHIRP		0x77		/* make a chirp chirp sound */
+#define	CMD_GET_LAST_DONE	0x78		/* get tag of last done in progress */
+#define	CMD_GET_FEATURES	0x79		/* get feature code and ESN */
+#define CMD_CLEAR_CACHE		0x7A		/* Clear cache on specified device */
+#define	CMD_BIOS_TEST		0x7B		/* Test whether or not to load BIOS */
+#define	CMD_WAIT_FLUSH		0x7C		/* wait for cache flushed and invalidate read cache */
+#define	CMD_RESET_BUS		0x7D		/* reset the SCSI bus */
+#define	CMD_STARTUP_QRY		0x7E		/* startup in progress query */
+#define	CMD_RESET			0x7F		/* reset the controller */
+
+#define	CMD_RESTART_RESET	0x80		/* reload and restart the controller at any reset issued */
+#define	CMD_SOFT_RESET		0x81		/* do a soft reset NOW! */
+
+/************************************************/
+/*												*/
+/*				Host return errors				*/
+/*												*/
+/************************************************/
+#define	ERR08_TAGGED		0x80		/* doorbell error ored with tag */
+
+#define	ERR16_NONE			0x0000		/* no errors */
+#define	ERR16_SC_COND_MET	0x0004		/* SCSI status - Condition Met */
+#define	ERR16_CMD			0x0101		/* command error */
+#define	ERR16_SC_CHECK_COND	0x0002		/* SCSI status - Check Condition */
+#define	ERR16_CMD_NOT		0x0201		/* command not supported */
+#define ERR16_NO_DEVICE     0x0301		/* invalid device selection */
+#define	ERR16_SECTOR		0x0202		/* bad sector */
+#define	ERR16_PROTECT		0x0303		/* write protected */
+#define	ERR16_NOSECTOR		0x0404		/* sector not found */
+#define	ERR16_MEDIA			0x0C0C		/* invalid media */
+#define	ERR16_CONTROL		0x2020		/* controller error */
+#define	ERR16_CONTROL_DMA	0x2120		/* controller DMA engine error */
+#define	ERR16_NO_ALARM		0x2220		/* alarm is not active */
+#define	ERR16_OP_BUSY		0x2320		/* operation busy */
+#define	ERR16_SEEK			0x4040		/* seek failure */
+#define	ERR16_DEVICE_FAIL	0x4140		/* device has failed */
+#define ERR16_TIMEOUT		0x8080		/* timeout error */
+#define	ERR16_DEV_NOT_READY	0xAAAA		/* drive not ready */
+#define	ERR16_UNDEFINED		0xBBBB		/* undefined error */
+#define	ERR16_WRITE_FAULT	0xCCCC		/* write fault */
+#define ERR16_INVALID_DEV	0x4001		/* invalid device access */
+#define	ERR16_DEVICE_BUSY	0x4002		/* device is busy */
+#define	ERR16_MEMORY		0x4003		/* device pass thru requires too much memory */
+#define	ERR16_NO_FEATURE	0x40FA		/* feature no implemented */
+#define	ERR16_NOTAG			0x40FD		/* no tag space available */
+#define	ERR16_NOT_READY		0x40FE		/* controller not ready error */
+#define	ERR16_SETUP_FLASH	0x5050		/* error when writing setup to flash memory */
+#define	ERR16_SETUP_SIZE	0x5051		/* setup block size error */
+#define	ERR16_SENSE			0xFFFF		/* sense opereration failed */
+#define	ERR16_SC_BUSY		0x0008		/* SCSI status - Busy */
+#define	ERR16_SC_RES_CONFL	0x0018		/* SCSI status - Reservation Conflict */
+#define	ERR16_SC_CMD_TERM	0x0022		/* SCSI status - Command Terminated */
+#define	ERR16_SC_OTHER		0x00FF		/* SCSI status - not recognized (any value masked) */
+#define	ERR16_MEDIA_CHANGED	0x8001		/* devices media has been changed */
+
+#define	ERR32_NONE			0x00000000	/* no errors */
+#define	ERR32_SC_COND_MET	0x00000004	/* SCSI status - Condition Met */
+#define	ERR32_CMD			0x00010101	/* command error */
+#define	ERR32_SC_CHECK_COND	0x00020002	/* SCSI status - Check Condition */
+#define	ERR32_CMD_NOT		0x00030201	/* command not supported */
+#define ERR32_NO_DEVICE     0x00040301	/* invalid device selection */
+#define	ERR32_SECTOR		0x00050202	/* bad sector */
+#define	ERR32_PROTECT		0x00060303	/* write protected */
+#define	ERR32_NOSECTOR		0x00070404	/* sector not found */
+#define	ERR32_MEDIA			0x00080C0C	/* invalid media */
+#define	ERR32_CONTROL		0x00092020	/* controller error */
+#define	ERR32_CONTROL_DMA	0x000A2120	/* Controller DMA error */
+#define	ERR32_NO_ALARM		0x000B2220 	/* alarm is not active */
+#define	ERR32_OP_BUSY		0x000C2320	/* operation busy */
+#define	ERR32_SEEK			0x000D4040	/* seek failure */
+#define	ERR32_DEVICE_FAIL	0x000E4140	/* device has failed */
+#define ERR32_TIMEOUT		0x000F8080	/* timeout error */
+#define	ERR32_DEV_NOT_READY	0x0010AAAA	/* drive not ready */
+#define	ERR32_UNDEFINED		0x0011BBBB	/* undefined error */
+#define	ERR32_WRITE_FAULT	0x0012CCCC	/* write fault */
+#define ERR32_INVALID_DEV	0x00134001	/* invalid device access */
+#define	ERR32_DEVICE_BUSY	0x00144002	/* device is busy */
+#define	ERR32_MEMORY		0x00154003	/* device pass thru requires too much memory */
+#define	ERR32_NO_FEATURE	0x001640FA	/* feature no implemented */
+#define	ERR32_NOTAG			0x001740FD	/* no tag space available */
+#define	ERR32_NOT_READY		0x001840FE	/* controller not ready error */
+#define	ERR32_SETUP_FLASH	0x00195050	/* error when writing setup to flash memory */
+#define	ERR32_SETUP_SIZE	0x001A5051	/* setup block size error */
+#define	ERR32_SENSE			0x001BFFFF	/* sense opereration failed */
+#define	ERR32_SC_BUSY		0x001C0008	/* SCSI status - Busy */
+#define	ERR32_SC_RES_CONFL	0x001D0018	/* SCSI status - Reservation Conflict */
+#define	ERR32_SC_CMD_TERM	0x001E0022	/* SCSI status - Command Terminated */
+#define	ERR32_SC_OTHER		0x001F00FF	/* SCSI status - not recognized (any value masked) */
+#define	ERR32_MEDIA_CHANGED	0x00208001	/* devices media has been changed */
+
+/************************************************/
+/*												*/
+/*	Host Operating System specification codes	*/
+/*												*/
+/************************************************/
+
+#define	SPEC_INTERRUPT		0x80		/* specification requires host interrupt */
+#define	SPEC_BACKWARD_SG	0x40		/* specification requires scatter/gather items reversed */
+#define	SPEC_DOS_BLOCK		0x01		/* DOS DASD blocking on pass through */
+#define	SPEC_OS2_V3			0x02		/* OS/2 Warp */
+#define	SPCE_SCO_3242		0x04		/* SCO 3.4.2.2 */
+
+
+/************************************************/
+/*												*/
+/*	Inquire structures							*/
+/*												*/
+/************************************************/
+typedef	struct	_CNT_SCSI_INQ
+	{
+	UCHAR	devt;						/* 00: device type */
+	UCHAR	devtm;						/* 01: device type modifier */
+	UCHAR	svers;						/* 02: SCSI version */
+	UCHAR	rfmt;						/* 03: response data format */
+	UCHAR	adlen;						/* 04: additional length of data */
+	UCHAR	res1;						/* 05: */
+	UCHAR	res2;						/* 06: */
+	UCHAR	fncs;						/* 07: functional capabilities */
+	UCHAR	vid[8];						/* 08: vendor ID */
+	UCHAR	pid[16];					/* 10: product ID */
+	UCHAR	rev[4];						/* 20: product revision */
+	}	CNT_SCSI_INQ;
+
+typedef	struct	_CNT_IDE_INQ
+	{
+	USHORT	GeneralConfiguration;		/* 00 */
+	USHORT	NumberOfCylinders;			/* 02 */
+	USHORT	Reserved1;					/* 04 */
+	USHORT	NumberOfHeads;				/* 06 */
+	USHORT	UnformattedBytesPerTrack;	/* 08 */
+	USHORT	UnformattedBytesPerSector;	/* 0A */
+	USHORT	SectorsPerTrack;			/* 0C */
+	USHORT	VendorUnique1[3];			/* 0E */
+	USHORT	SerialNumber[10];			/* 14 */
+	USHORT	BufferType;					/* 28 */
+	USHORT	BufferSectorSize;			/* 2A */
+	USHORT	NumberOfEccBytes;			/* 2C */
+	USHORT	FirmwareRevision[4];		/* 2E */
+	USHORT	ModelNumber[20];			/* 36 */
+	UCHAR	MaximumBlockTransfer;		/* 5E */
+	UCHAR	VendorUnique2;				/* 5F */
+	USHORT	DoubleWordIo;				/* 60 */
+	USHORT	Capabilities;				/* 62 */
+	USHORT	Reserved2;					/* 64 */
+	UCHAR	VendorUnique3;				/* 66 */
+	UCHAR	PioCycleTimingMode;			/* 67 */
+	UCHAR	VendorUnique4;				/* 68 */
+	UCHAR	DmaCycleTimingMode;			/* 69 */
+	USHORT	TranslationFieldsValid;		/* 6A */
+	USHORT	NumberOfCurrentCylinders;	/* 6C */
+	USHORT	NumberOfCurrentHeads;		/* 6E */
+	USHORT	CurrentSectorsPerTrack;		/* 70 */
+	ULONG	CurrentSectorCapacity;		/* 72 */
+	}	CNT_IDE_INQ;
+
+typedef struct	_DASD_INQUIRE
+	{
+	ULONG	type;						/* 0 = SCSI, 1 = IDE */
+	union
+		{
+		CNT_SCSI_INQ	scsi;			/* SCSI inquire data */
+		CNT_IDE_INQ		ide;			/* IDE inquire data */
+		}	inq;
+	}	DASD_INQUIRE;
+
+/************************************************/
+/*												*/
+/*	Device Codes								*/
+/*												*/
+/************************************************/
+#define DEVC_DASD			0x00		/* Direct-access Storage Device */
+#define DEVC_SEQACESS		0x01		/* Sequential-access device */
+#define DEVC_PRINTER		0x02		/* Printer device */
+#define DEVC_PROCESSOR		0x03		/* Processor device */
+#define DEVC_WRITEONCE		0x04		/* Write-once device */
+#define DEVC_CDROM			0x05		/* CD-ROM device */
+#define DEVC_SCANNER		0x06		/* Scanner device */
+#define DEVC_OPTICAL		0x07		/* Optical memory device */
+#define DEVC_MEDCHGR		0x08		/* Medium changer device */
+#define	DEVC_DASD_REMOVABLE	0x80		/* Direct-access storage device, Removable */
+#define	DEVC_NONE			0xFF		/* no device */
+
+#endif
+
diff -u --recursive --new-file v2.1.62/linux/drivers/scsi/scsi.h linux/drivers/scsi/scsi.h
--- v2.1.62/linux/drivers/scsi/scsi.h	Sun Sep  7 13:10:43 1997
+++ linux/drivers/scsi/scsi.h	Sat Nov  8 11:39:12 1997
@@ -498,8 +498,7 @@
 	    req->nr_sectors -= bh->b_size >> 9;
 	    req->sector += bh->b_size >> 9;
 	    bh->b_reqnext = NULL;
-	    mark_buffer_uptodate(bh, uptodate);
-	    unlock_buffer(bh);
+	    bh->b_end_io(bh, uptodate);
 	    sectors -= bh->b_size >> 9;
 	    if ((bh = req->bh) != NULL) {
 		req->current_nr_sectors = bh->b_size >> 9;
diff -u --recursive --new-file v2.1.62/linux/drivers/scsi/scsi_ioctl.c linux/drivers/scsi/scsi_ioctl.c
--- v2.1.62/linux/drivers/scsi/scsi_ioctl.c	Wed Sep  3 20:52:43 1997
+++ linux/drivers/scsi/scsi_ioctl.c	Fri Nov  7 10:14:49 1997
@@ -341,6 +341,11 @@
 		    &((Scsi_Idlun *) arg)->dev_id);
         put_user(dev->host->unique_id, &((Scsi_Idlun *) arg)->host_unique_id);
 	return 0;
+    case SCSI_IOCTL_GET_BUS_NUMBER:
+        result = verify_area(VERIFY_WRITE, (void *) arg, sizeof(int));
+        if (result) return result;
+        put_user( dev->host->host_no, (int *) arg);
+	return 0;
     case SCSI_IOCTL_TAGGED_ENABLE:
 	if(!suser())  return -EACCES;
 	if(!dev->tagged_supported) return -EINVAL;
diff -u --recursive --new-file v2.1.62/linux/drivers/sound/.objects linux/drivers/sound/.objects
--- v2.1.62/linux/drivers/sound/.objects	Tue Mar  4 10:25:24 1997
+++ linux/drivers/sound/.objects	Tue Sep 30 08:46:46 1997
@@ -89,3 +89,11 @@
 	OBJS := $(OBJS)  uart401.o
 endif
 
+ifdef CONFIG_OPL3SA1
+	OBJS := $(OBJS)  opl3sa.o
+endif
+
+ifdef CONFIG_SOFTOSS
+	OBJS := $(OBJS)  softoss.o softoss_rs.o
+endif
+
diff -u --recursive --new-file v2.1.62/linux/drivers/sound/.version linux/drivers/sound/.version
--- v2.1.62/linux/drivers/sound/.version	Wed May 28 10:51:32 1997
+++ linux/drivers/sound/.version	Sun Nov  9 23:01:54 1997
@@ -1,2 +1,2 @@
-3.8a
+3.8s
 0x030804
diff -u --recursive --new-file v2.1.62/linux/drivers/sound/CHANGELOG linux/drivers/sound/CHANGELOG
--- v2.1.62/linux/drivers/sound/CHANGELOG	Wed May 28 10:51:32 1997
+++ linux/drivers/sound/CHANGELOG	Tue Sep 30 08:46:46 1997
@@ -1,5 +1,12 @@
-Changelog for version 3.8
+Changelog for version 3.8o
 --------------------------
+
+Since 3.8h
+- Included support for OPL3-SA1 and SoftOSS
+
+Since 3.8
+- Fixed SNDCTL_DSP_GETOSPACE
+- Compatibility fixes for Linux 2.1.47
 
 Since 3.8-beta21
 - Fixed all known bugs (I think).
diff -u --recursive --new-file v2.1.62/linux/drivers/sound/Config.in linux/drivers/sound/Config.in
--- v2.1.62/linux/drivers/sound/Config.in	Thu Jul 17 10:06:06 1997
+++ linux/drivers/sound/Config.in	Sun Nov  9 22:59:31 1997
@@ -1,277 +1,15 @@
-bool 'ProAudioSpectrum 16 support' CONFIG_PAS
-bool '100%% Sound Blaster compatibles (SB16/32/64, ESS, Jazz16) support' CONFIG_SB
-bool 'Generic OPL2/OPL3 FM synthesizer support' CONFIG_ADLIB
-bool 'Gravis Ultrasound support' CONFIG_GUS
-bool 'MPU-401 support (NOT for SB16)' CONFIG_MPU401
-bool 'PSS (ECHO-ADI2111) support' CONFIG_PSS
-bool '16 bit sampling option of GUS (_NOT_ GUS MAX)' CONFIG_GUS16
-bool 'GUS MAX support' CONFIG_GUSMAX
-bool 'Microsoft Sound System support' CONFIG_MSS
-bool 'Ensoniq SoundScape support' CONFIG_SSCAPE
-bool 'MediaTrix AudioTrix Pro support' CONFIG_TRIX
-bool 'Support for MAD16 and/or Mozart based cards' CONFIG_MAD16
-bool 'Support for Crystal CS4232 based (PnP) cards' CONFIG_CS4232
-bool 'Support for Turtle Beach Wave Front (Maui, Tropez) synthesizers' CONFIG_MAUI
-bool 'FM synthesizer (YM3812/OPL-3) support' CONFIG_YM3812
-
-if [ "$CONFIG_AEDSP16" = "y" ]; then
-hex 'I/O base for Audio Excel DSP 16 220 or 240' AEDSP16_BASE 220
-fi
-
-if [ "$CONFIG_SB" = "y" ]; then
-hex 'I/O base for SB Check from manual of the card' SBC_BASE 220
-fi
-
-if [ "$CONFIG_SB" = "y" ]; then
-int 'Sound Blaster IRQ Check from manual of the card' SBC_IRQ 7
-fi
-
-if [ "$CONFIG_SB" = "y" ]; then
-int 'Sound Blaster DMA 0, 1 or 3' SBC_DMA 1
-fi
-
-if [ "$CONFIG_SB" = "y" ]; then
-int 'Sound Blaster 16 bit DMA (SB16, Jazz16, SMW) 5, 6 or 7 (use 1 for 8 bit cards)' SB_DMA2 5
-fi
-
-if [ "$CONFIG_SB" = "y" ]; then
-hex 'MPU401 I/O base of SB16, Jazz16 and ES1688 Check from manual of the card' SB_MPU_BASE 330
-fi
-
-
-if [ "$CONFIG_SB" = "y" ]; then
-comment 'MPU401 IRQ is only required with Jazz16, SM Wave and ESS1688.'
-fi
-
-
-if [ "$CONFIG_SB" = "y" ]; then
-comment 'Enter -1 to the following question if you have something else such as SB16/32.'
-fi
-
-if [ "$CONFIG_SB" = "y" ]; then
-int 'SB MPU401 IRQ (Jazz16, SM Wave and ES1688) Check from manual of the card' SB_MPU_IRQ -1
-fi
-
-if [ "$CONFIG_PAS" = "y" ]; then
-int 'PAS16 IRQ 3, 4, 5, 7, 9, 10, 11, 12, 14 or 15' PAS_IRQ 10
-fi
-
-if [ "$CONFIG_PAS" = "y" ]; then
-int 'PAS16 DMA 0, 1, 3, 5, 6 or 7' PAS_DMA 3
-fi
-
-if [ "$CONFIG_GUS" = "y" ]; then
-hex 'I/O base for GUS 210, 220, 230, 240, 250 or 260' GUS_BASE 220
-fi
-
-if [ "$CONFIG_GUS" = "y" ]; then
-int 'GUS IRQ 3, 5, 7, 9, 11, 12 or 15' GUS_IRQ 15
-fi
-
-if [ "$CONFIG_GUS" = "y" ]; then
-int 'GUS DMA 1, 3, 5, 6 or 7' GUS_DMA 6
-fi
-
-if [ "$CONFIG_GUS" = "y" ]; then
-int 'Second DMA channel for GUS 1, 3, 5, 6 or 7' GUS_DMA2 -1
-fi
-
-if [ "$CONFIG_GUS16" = "y" ]; then
-hex 'I/O base for the 16 bit daughtercard of GUS 530, 604, E80 or F40' GUS16_BASE 530
-fi
-
-if [ "$CONFIG_GUS16" = "y" ]; then
-int 'GUS 16 bit daughtercard IRQ 3, 4, 5, 7, or 9' GUS16_IRQ 7
-fi
-
-if [ "$CONFIG_GUS16" = "y" ]; then
-int 'GUS DMA 0, 1 or 3' GUS16_DMA 3
-fi
-
-if [ "$CONFIG_MPU401" = "y" ]; then
-hex 'I/O base for MPU401 Check from manual of the card' MPU_BASE 330
-fi
-
-if [ "$CONFIG_MPU401" = "y" ]; then
-int 'MPU401 IRQ Check from manual of the card' MPU_IRQ 9
-fi
-
-
-if [ "$CONFIG_MAUI" = "y" ]; then
-comment 'ERROR! You have to use old sound configuration method with Maui.'
-fi
-
-if [ "$CONFIG_MAUI" = "y" ]; then
-hex 'I/O base for Maui 210, 230, 260, 290, 300, 320, 338 or 330' MAUI_BASE 330
-fi
-
-if [ "$CONFIG_MAUI" = "y" ]; then
-int 'Maui IRQ 5, 9, 12 or 15' MAUI_IRQ 9
-fi
-
-if [ "$CONFIG_UART6850" = "y" ]; then
-hex 'I/O base for UART 6850 MIDI port (Unknown)' U6850_BASE 0
-fi
-
-if [ "$CONFIG_UART6850" = "y" ]; then
-int 'UART6850 IRQ (Unknown)' U6850_IRQ -1
-fi
-
-
-if [ "$CONFIG_PSS" = "y" ]; then
-comment 'ERROR! You have to use old sound configuration method with PSS cards.'
-fi
-
-if [ "$CONFIG_PSS" = "y" ]; then
-hex 'PSS I/O base 220 or 240' PSS_BASE 220
-fi
-
-if [ "$CONFIG_PSS" = "y" ]; then
-hex 'PSS audio I/O base 530, 604, E80 or F40' PSS_MSS_BASE 530
-fi
-
-if [ "$CONFIG_PSS" = "y" ]; then
-int 'PSS audio IRQ 7, 9, 10 or 11' PSS_MSS_IRQ 11
-fi
-
-if [ "$CONFIG_PSS" = "y" ]; then
-int 'PSS audio DMA 0, 1 or 3' PSS_MSS_DMA 3
-fi
-
-if [ "$CONFIG_PSS" = "y" ]; then
-hex 'PSS MIDI I/O base ' PSS_MPU_BASE 330
-fi
-
-if [ "$CONFIG_PSS" = "y" ]; then
-int 'PSS MIDI IRQ 3, 4, 5, 7 or 9' PSS_MPU_IRQ 9
-fi
-
-if [ "$CONFIG_MSS" = "y" ]; then
-hex 'MSS/WSS I/O base 530, 604, E80 or F40' MSS_BASE 530
-fi
-
-if [ "$CONFIG_MSS" = "y" ]; then
-int 'MSS/WSS IRQ 7, 9, 10 or 11' MSS_IRQ 11
-fi
-
-if [ "$CONFIG_MSS" = "y" ]; then
-int 'MSS/WSS DMA 0, 1 or 3' MSS_DMA 3
-fi
-
-if [ "$CONFIG_MSS" = "y" ]; then
-int 'MSS/WSS second DMA (if possible) 0, 1 or 3' MSS_DMA2 -1
-fi
-
-if [ "$CONFIG_SSCAPE" = "y" ]; then
-hex 'SoundScape MIDI I/O base 320, 330, 340 or 350' SSCAPE_BASE 330
-fi
-
-if [ "$CONFIG_SSCAPE" = "y" ]; then
-int 'SoundScape MIDI IRQ ' SSCAPE_IRQ 9
-fi
-
-if [ "$CONFIG_SSCAPE" = "y" ]; then
-int 'SoundScape initialization DMA 0, 1 or 3' SSCAPE_DMA 3
-fi
-
-if [ "$CONFIG_SSCAPE" = "y" ]; then
-hex 'SoundScape audio I/O base 534, 608, E84 or F44' SSCAPE_MSS_BASE 534
-fi
-
-if [ "$CONFIG_SSCAPE" = "y" ]; then
-int 'SoundScape audio IRQ 7, 9, 10 or 11' SSCAPE_MSS_IRQ 11
-fi
-
-
-if [ "$CONFIG_TRIX" = "y" ]; then
-comment 'ERROR! You have to use old sound configuration method with AudioTrix.'
-fi
-
-if [ "$CONFIG_TRIX" = "y" ]; then
-hex 'AudioTrix audio I/O base 530, 604, E80 or F40' TRIX_BASE 530
-fi
-
-if [ "$CONFIG_TRIX" = "y" ]; then
-int 'AudioTrix audio IRQ 7, 9, 10 or 11' TRIX_IRQ 11
-fi
-
-if [ "$CONFIG_TRIX" = "y" ]; then
-int 'AudioTrix audio DMA 0, 1 or 3' TRIX_DMA 0
-fi
-
-if [ "$CONFIG_TRIX" = "y" ]; then
-int 'AudioTrix second (duplex) DMA 0, 1 or 3' TRIX_DMA2 3
-fi
-
-if [ "$CONFIG_TRIX" = "y" ]; then
-hex 'AudioTrix MIDI I/O base 330, 370, 3B0 or 3F0' TRIX_MPU_BASE 330
-fi
-
-if [ "$CONFIG_TRIX" = "y" ]; then
-int 'AudioTrix MIDI IRQ 3, 4, 5, 7 or 9' TRIX_MPU_IRQ 9
-fi
-
-if [ "$CONFIG_TRIX" = "y" ]; then
-hex 'AudioTrix SB I/O base 220, 210, 230, 240, 250, 260 or 270' TRIX_SB_BASE 220
-fi
-
-if [ "$CONFIG_TRIX" = "y" ]; then
-int 'AudioTrix SB IRQ 3, 4, 5 or 7' TRIX_SB_IRQ 7
-fi
-
-if [ "$CONFIG_TRIX" = "y" ]; then
-int 'AudioTrix SB DMA 1 or 3' TRIX_SB_DMA 1
-fi
-
-if [ "$CONFIG_CS4232" = "y" ]; then
-hex 'CS4232 audio I/O base 530, 604, E80 or F40' CS4232_BASE 530
-fi
-
-if [ "$CONFIG_CS4232" = "y" ]; then
-int 'CS4232 audio IRQ 5, 7, 9, 11, 12 or 15' CS4232_IRQ 11
-fi
-
-if [ "$CONFIG_CS4232" = "y" ]; then
-int 'CS4232 audio DMA 0, 1 or 3' CS4232_DMA 0
-fi
-
-if [ "$CONFIG_CS4232" = "y" ]; then
-int 'CS4232 second (duplex) DMA 0, 1 or 3' CS4232_DMA2 3
-fi
-
-if [ "$CONFIG_CS4232" = "y" ]; then
-hex 'CS4232 MIDI I/O base 330, 370, 3B0 or 3F0' CS4232_MPU_BASE 330
-fi
-
-if [ "$CONFIG_CS4232" = "y" ]; then
-int 'CS4232 MIDI IRQ 5, 7, 9, 11, 12 or 15' CS4232_MPU_IRQ 9
-fi
-
-if [ "$CONFIG_MAD16" = "y" ]; then
-hex 'MAD16 audio I/O base 530, 604, E80 or F40' MAD16_BASE 530
-fi
-
-if [ "$CONFIG_MAD16" = "y" ]; then
-int 'MAD16 audio IRQ 7, 9, 10 or 11' MAD16_IRQ 11
-fi
-
-if [ "$CONFIG_MAD16" = "y" ]; then
-int 'MAD16 audio DMA 0, 1 or 3' MAD16_DMA 3
-fi
-
-if [ "$CONFIG_MAD16" = "y" ]; then
-int 'MAD16 second (duplex) DMA 0, 1 or 3' MAD16_DMA2 0
-fi
-
-if [ "$CONFIG_MAD16" = "y" ]; then
-hex 'MAD16 MIDI I/O base 300, 310, 320 or 330 (0 disables)' MAD16_MPU_BASE 330
-fi
-
-if [ "$CONFIG_MAD16" = "y" ]; then
-int 'MAD16 MIDI IRQ 5, 7, 9 or 10' MAD16_MPU_IRQ 9
-fi
 #
-$MAKE -C drivers/sound kernelconfig || exit 1
+# Sound driver configuration
+#
+#--------
+# There is another confic script which is compatible with rest of
+# the kernel. It can be activated by running 'make mkscript' in this
+# directory. Please note that this is an _experimental_ feature which
+# doesn't work with all cards (PSS, SM Wave, AudioTriX Pro, Maui).
+#--------
+#
+$MAKE -C drivers/sound config || exit 1
+
 bool 'Additional low level drivers' CONFIG_LOWLEVEL_SOUND
 
 if [ "$CONFIG_LOWLEVEL_SOUND" = "y" ]; then
diff -u --recursive --new-file v2.1.62/linux/drivers/sound/Makefile linux/drivers/sound/Makefile
--- v2.1.62/linux/drivers/sound/Makefile	Wed May 28 10:51:32 1997
+++ linux/drivers/sound/Makefile	Sun Nov  9 22:59:28 1997
@@ -1,3 +1,4 @@
+BUILDCODE=o
 # Makefile for the Linux sound card driver
 #
 # Note 2! The CFLAGS definitions are now inherited from the
@@ -155,6 +156,7 @@
 
 clrconf:
 	rm -f local.h .depend synth-ld.h trix_boot.h smw-midi0001.h maui_boot.h .defines
+	rm Config.in;cp Config.std Config.in
 
 configure: configure.c
 	$(HOSTCC) -o configure configure.c
@@ -162,6 +164,7 @@
 
 dep:
 	$(CPP) -M $(CFLAGS) -I. *.c > .depend
+	cd lowlevel;make dep
 
 setup:
 	@echo Compiling Sound Driver v $(VERSION) for Linux
diff -u --recursive --new-file v2.1.62/linux/drivers/sound/Readme linux/drivers/sound/Readme
--- v2.1.62/linux/drivers/sound/Readme	Wed May 28 10:51:32 1997
+++ linux/drivers/sound/Readme	Tue Sep 30 08:46:46 1997
@@ -1,22 +1,21 @@
-OSS Lite version 3.8 release notes
+OSS/Free version 3.8 release notes
 ----------------------------------
 
 Most up to date information about this driver is available from 
-http://www.4front-tech.com/ossfree or http://personal.eunet.fi/pp/voxware
-(European mirror).
+http://www.4front-tech.com/ossfree.
 
 
 
 Please read the SOUND-HOWTO (available from sunsite.unc.edu and other Linux FTP 
 sites). It gives instructions about using sound with Linux. It's bit out of
 date but still very useful. Information about bug fixes and such things
-is available from the web page (see below).
+is available from the web page (see above).
 
 Please check http://www.4front-tech.com/pguide for more info about programming
-with OSS.
+with OSS API.
 
    ====================================================
--  THIS VERSION ____REQUIRES____ Linux 2.1.36 OR LATER.
+-  THIS VERSION ____REQUIRES____ Linux 2.1.57 OR LATER.
    ====================================================
 
 Packages "snd-util-3.8.tar.gz" and "snd-data-0.1.tar.Z"
@@ -78,6 +77,18 @@
 There are probably many other names missing. If you have sent me some
 patches and your name is not in the above list, please inform me.
 
+Sending your contributions or patches
+-------------------------------------
+
+First of all it's highly recommended to contact me before sending anything
+or before even starting to do any work. Tell me what you suggest to be
+changed or what you have planned to do. Also ensure you are using the
+very latest (development) version of OSS/Free since the change may already be
+implemented there. In general it's major waste of time to try to improve
+several months old version. Information about the latest version can be found
+from http://www.4front-tech.com/ossfree. In general there is no point in
+sending me patches relative to production kernels.
+
 Sponsors etc.
 -------------
 
@@ -166,7 +177,7 @@
 Hannu
 
 Hannu Savolainen
-hannu@voxware.pp.fi, hannu@4front-tech.com	
+hannu@4front-tech.com	
 (Please check http://www.4front-tech.com/ossfree before mailing me).
 
 Snail mail:	Hannu Savolainen
diff -u --recursive --new-file v2.1.62/linux/drivers/sound/Readme.cards linux/drivers/sound/Readme.cards
--- v2.1.62/linux/drivers/sound/Readme.cards	Wed May 28 10:51:32 1997
+++ linux/drivers/sound/Readme.cards	Tue Sep 30 08:46:46 1997
@@ -5,7 +5,13 @@
 Open Sound Systems (OSS/Free). Information about the commercial version
 (OSS/Linux) and it's configuration is available from 
 http://www.4front-tech.com/linux.html. Information presented here is
-not valid for OSS/Linux.
+not valid for OSS/Linux. 
+
+If you are unsure about how to configure OSS/Free
+you can download the free evaluation version of OSS/Linux from the above
+address. There is a chance that it can autodetect your soundcard. In this case
+you can use the information included in soundon.log when configuring OSS/Free.
+
 
 IMPORTANT!	This document covers only cards that were "known" when
 		this driver version was released. Please look at
@@ -25,7 +31,7 @@
 		method to use. After you have used the "new" method once
 		it will always be used when you use any of the config
 		programs. To return back to the "old" method you should
-		reinstall the kernel sources.
+		execute "cp Config.std Config.in" in linux/drivers/sound.
 
 		The /etc/soundconf file (forget it if you don't know what
 		this file does) contains settings that are used only by
@@ -33,8 +39,8 @@
 		are stored there (they really are _NOT_ stored
 		there). Don't try to edit /etc/soundconf or any other
 		kernel or sound driver config files manually. The _only_
-		proper ways to change the settings are make config,
-		make menuconfig or make xconfig.
+		proper ways to change the settings are make config or
+		make menuconfig (the "old" method).
 
 		When using make xconfig and/or make menuconfig, you should
 		carefully check each sound configuration option (particularly
@@ -42,7 +48,6 @@
 		offered by these programs are not necessarily valid.
 
 
-
 THE BIGGEST MISTAKES YOU CAN DO
 ===============================
 
@@ -211,7 +216,8 @@
 		is compatible just with SB Pro but there is also a non-SB-
 		compatible 16 bit mode. Usually it's MSS/WSS but it could also
 		be a proprietary one like MV Jazz16 or ESS ES688. OPTi
-		MAD16 chips are very common in so called "SB 16 bit cards".
+		MAD16 chips are very common in so called "SB 16 bit cards"
+		(try with the MAD16 driver).
 
 	======================================================================
 	"Supposed to be SB compatible" cards.
@@ -292,10 +298,67 @@
 	----------------------------------------------------------------
 	NOTE! OPL3-SA is different chip than the ordinary OPL3. In addition
 	to the FM synth this chip has also digital audio (WSS) and
-	MIDI (MPU401) capabilities. OPL3-SA is not supported by OSS/Free.
-	Support for it is included in OSS/Linux v3.8 and later.
+	MIDI (MPU401) capabilities. Support for OPL3-SA is described below.
 	----------------------------------------------------------------
 
+Yamaha OPL3-SA1
+
+	Yamaha OPL3-SA1 (YMF701) is an audio controller chip used on some
+	(Intel) motherboards and on cheap soundcards. It should not be
+	confused with the original OPL3 chip (YMF278) which is entirely
+        different chip. OPL3-SA1 has support for MSS, MPU401 and SB Pro
+	(not used in OSS/Free) in addition to the OPL3 FM synth.
+
+	There are also chips called OPL3-SA2, OPL3-SA3, ..., OPL3SA-N. They
+	are PnP chips and will not work with the OPL3-SA1 driver. You should 
+	use the standard MSS, MPU401 and OPL3 options with thses chips and to
+	activate the card using isapnptools.
+
+4Front Technologies SoftOSS
+
+	SoftOSS is a software based wave table emulation which works with
+	any 16 bit stereo soundcard. Due to it's nature a fast CPU is
+	required (P133 is minumum). Althoug SoftOSS doesn _not_ use MMX
+	instructions it has proven out that recent processors (which appear
+	to have MMX) perform significantly better with SoftOSS than earlier
+	ones. For example a P166MMX beats a PPro200. SoftOSS should not be used
+	on 486 or 386 machines.
+
+	The amount of CPU load caused by SoftOSS can be controlled by 
+	selecting the SOFTOSS_RATE and SOFTOSS_VOICES parameters properly
+	(they will be prompted by make config). It's recommended to set
+	SOFTOSS_VOICES to 32. If you have a P166MMX or faster (PPro200 is
+	not faster) you can set SOFTOSS_RATE to 44100 (kHz). However with
+	slower systems it recommended to use sampling rates around 22050
+	or even 16000 kHz. Selecting too high values for these parameters
+        may hang your system when playing MIDI files with hight degree of
+	polyphony (number of concurrently playing notes). It's also possible to
+	decrease SOFTOSS_VOICES. This makes it possible to use higher sampling
+	rates. However using fewer voices decreases playback quality more than
+	decreasing the sampling rate.
+
+	SoftOSS keeps the samples loaded on system's RAM so large RAM is
+	required. SoftOSS should never be used on machines with less than 16M
+	of RAM since this is potentially dangerous (you may accidently run out
+	of memory which probably crashes the machine). 
+
+	SoftOSS implements the wave table API originally designed for GUS. For
+	this reason all applications designed for GUS should work (at least
+	after minor modifications). For example gmod/xgmod and playmidi -g are
+	known to work.
+
+	To work SoftOSS will require GUS compatible
+	patch files to be installed on the system (in /dos/ultrasnd/midi). You
+	can use the public domain MIDIA patchset available from several ftp
+	sites.
+
+        *********************************************************************
+	IMPORTANT NOTICE! The original patch set distributed with Gravis 
+	Ultrasound card is not in public domain (even it's available from
+	some ftp sites). You should contact Voice Crystal (www.voicecrystal.com)
+	if you like to use these patches with SoftOSS included in OSS/Free.
+        *********************************************************************
+
 PSS based cards (AD1848 + ADSP-2115 + Echo ESC614 ASIC)
 	Analog Devices and Echo Speech have together defined a soundcard
 	architecture based on the above chips. The DSP chip is used
@@ -327,7 +390,7 @@
 	VIVO90 cards are not compatible with Soundscapes so the Soundscape driver
 	will not work with them. You may want to use OSS/Linux with these cards.
 
-MAD16 and Mozart based cards
+OPTi MAD16 and Mozart based cards
 	The Mozart (OAK OTI-601), MAD16 (OPTi 82C928), MAD16 Pro (OPTi 82C929),
 	OPTi 82C924/82C925 (in _non_ PnP mode) and OPTi 82C930 interface
 	chips are used in many different soundcards, including some
@@ -644,8 +707,12 @@
 Sound Blasters (the original ones by Creative)
 ---------------------------------------------
 
+NOTE! Check if you have a PnP Sound Blaster (cards sold after summer 1995
+      are almost certainly PnP ones). With PnP cards you should use isapnptools
+      to activate them (see above).
+
 It's possible to configure these cards to use different I/O, IRQ and
-DMA settings. Since the available settings have changed between various
+DMA settings. Since the possible/default settings have changed between various
 models, you have to consult manual of your card for the proper ones. It's
 a good idea to use the same values than with DOS/Windows. With SB and SB Pro
 it's the only choice. SB16 has software selectable IRQ and DMA channels but
@@ -662,6 +729,15 @@
 one when the configuration program asks for the 16 bit one. This may work
 in some systems but is likely to cause terrible noise on some other systems.
 
+It's possible to use two SB16/32/64 at the same time. To do this you should
+first configure OSS/Free for one card. Then edit local.h manually and define
+SB2_BASE, SB2_IRQ, SB2_DMA and SB2_DMA2 for the second one. You can't get
+the OPL3, MIDI and EMU8000 devices of the second card to work. If you are
+going to use two PnP Sound Blasters, ensure that they are of different model
+and have different PnP ID's. There is no way to get two cards with the same
+card ID and serial number to work. The easiest way to check this is trying 
+if isapnptools can see both cards or just one.
+
 NOTE!	Don't enable the SM Games option (asked by the configuration program)
 	if you are not 101% sure that your card is a Logitech Soundman Games
 	(not a SM Wave or SM16).
@@ -1185,12 +1261,10 @@
 If you have any corrections and/or comments, please contact me.
 
 Hannu Savolainen
-hannu@voxware.pp.fi
+hannu@4front-tech.com
 
 Personal home page:	   http://personal.eunet.fi/pp/voxware/hannu.html
 www home page of OSS/Free: http://www.4front-tech.com/ossfree
-  European/Finnish mirror: http://personal.eunet.fi/pp/voxware
 
 www home page of commercial OSS
 (Open Sound System) drivers: http://www.4front-tech.com/oss.html
-
diff -u --recursive --new-file v2.1.62/linux/drivers/sound/Readme.linux linux/drivers/sound/Readme.linux
--- v2.1.62/linux/drivers/sound/Readme.linux	Wed May 28 10:51:32 1997
+++ linux/drivers/sound/Readme.linux	Tue Sep 30 08:46:46 1997
@@ -67,10 +67,10 @@
 check for possible boot (insmod) time error messages in /var/adm/messages.
 
 - Other messages or problems
-Please check http://www.4front-tech.com/osslite for more info.
+Please check http://www.4front-tech.com/ossfree for more info.
 
 Hannu Savolainen
-hannu@voxware.pp.fi
+hannu@4front-tech.com
 
 ----------------- cut here ------------------------------
 SURPRISE SURPRISE!!!
diff -u --recursive --new-file v2.1.62/linux/drivers/sound/ad1848.c linux/drivers/sound/ad1848.c
--- v2.1.62/linux/drivers/sound/ad1848.c	Wed May 28 10:51:32 1997
+++ linux/drivers/sound/ad1848.c	Tue Sep 30 08:46:49 1997
@@ -28,7 +28,7 @@
 #define DEB1(x)
 #include "sound_config.h"
 
-#if defined(CONFIG_AD1848)
+#ifdef CONFIG_AD1848
 
 #include "ad1848_mixer.h"
 
@@ -49,7 +49,7 @@
     int             audio_mode;
     int             open_mode;
     int             intr_active;
-    char           *chip_name;
+    char           *chip_name, *name;
     int             model;
 #define MD_1848		1
 #define MD_4231		2
@@ -1423,7 +1423,7 @@
   devc->MCE_bit = 0x40;
   devc->irq = 0;
   devc->open_mode = 0;
-  devc->chip_name = "AD1848";
+  devc->chip_name = devc->name = "AD1848";
   devc->model = MD_1848;	/* AD1848 or CS4248 */
   devc->levels = NULL;
   devc->c930_password_port = 0;
@@ -1439,6 +1439,11 @@
      * If the I/O address is unused, it typically returns 0xff.
    */
 
+  if (inb (devc->base) == 0xff)
+    {
+      DDB (printk ("ad1848_detect: The base I/O address appears to be dead\n"));
+    }
+
 /*
  * Wait for the device to stop initialization
  */
@@ -1459,8 +1464,7 @@
 
   if ((inb (devc->base) & 0x80) != 0x00)	/* Not a AD1848 */
     {
-      DDB (printk ("ad1848 detect error - step A (%02x)\n",
-		   (int) inb (devc->base)));
+      DDB (printk ("ad1848 detect error - step A (%02x)\n", (int) inb (devc->base)));
       return 0;
     }
 
@@ -1480,7 +1484,7 @@
     else
       {
 	DDB (printk ("ad1848 detect error - step B (%x/%x)\n", tmp1, tmp2));
-	/* return 0; */
+	return 0;
       }
 
   DDB (printk ("ad1848_detect() - step C\n"));
@@ -1493,7 +1497,7 @@
     else
       {
 	DDB (printk ("ad1848 detect error - step C (%x/%x)\n", tmp1, tmp2));
-	/* return 0; */
+	return 0;
       }
 
   /*
@@ -1618,8 +1622,7 @@
 		id = ad_read (devc, 25) & 0xe7;
 	      if (id == 0x80)	/* Device still busy??? */
 		id = ad_read (devc, 25) & 0xe7;
-	      DDB (printk ("ad1848_detect() - step J (%02x/%02x)\n", id,
-			   ad_read (devc, 25)));
+	      DDB (printk ("ad1848_detect() - step J (%02x/%02x)\n", id, ad_read (devc, 25)));
 
 	      switch (id)
 		{
@@ -1687,9 +1690,7 @@
 		  break;
 
 		default:	/* Assume CS4231 or OPTi 82C930 */
-		  DDB (printk ("ad1848: I25 = %02x/%02x\n",
-			       ad_read (devc, 25),
-			       ad_read (devc, 25) & 0xe7));
+		  DDB (printk ("ad1848: I25 = %02x/%02x\n", ad_read (devc, 25), ad_read (devc, 25) & 0xe7));
 		  if (optiC930)
 		    {
 		      devc->chip_name = "82C930";
@@ -1741,8 +1742,6 @@
 
   ad1848_port_info *portc = NULL;
 
-  request_region (devc->base, 4, devc->chip_name);
-
   devc->irq = (irq > 0) ? irq : 0;
   devc->open_mode = 0;
   devc->timer_ticks = 0;
@@ -1750,6 +1749,8 @@
   devc->dma2 = dma_capture;
   devc->audio_flags = DMA_AUTOMODE;
   devc->playback_dev = devc->record_dev = 0;
+  if (name != NULL)
+    devc->name = name;
 
   if (name != NULL && name[0] != 0)
     sprintf (dev_name,
@@ -1758,6 +1759,8 @@
     sprintf (dev_name,
 	     "Generic audio codec (%s)", devc->chip_name);
 
+  request_region (devc->base, 4, devc->name);
+
   conf_printf2 (dev_name,
 		devc->base, devc->irq, dma_playback, dma_capture);
 
@@ -1801,7 +1804,7 @@
     {
       irq2dev[irq] = devc->dev_no = my_dev;
       if (snd_set_irq_handler (devc->irq, adintr,
-			       "SoundPort",
+			       devc->name,
 			       NULL) < 0)
 	{
 	  printk ("ad1848: IRQ in use\n");
@@ -1843,11 +1846,11 @@
 
   if (!share_dma)
     {
-      if (sound_alloc_dma (dma_playback, "Sound System"))
+      if (sound_alloc_dma (dma_playback, devc->name))
 	printk ("ad1848.c: Can't allocate DMA%d\n", dma_playback);
 
       if (dma_capture != dma_playback)
-	if (sound_alloc_dma (dma_capture, "Sound System (capture)"))
+	if (sound_alloc_dma (dma_capture, devc->name))
 	  printk ("ad1848.c: Can't allocate DMA%d\n", dma_capture);
     }
 
@@ -1944,8 +1947,7 @@
 	}
     }
   else
-    printk ("ad1848: Can't find device to be unloaded. Base=%x\n",
-	    io_base);
+    printk ("ad1848: Can't find device to be unloaded. Base=%x\n", io_base);
 }
 
 void
@@ -1973,7 +1975,7 @@
 
       if (irq > 15)
 	{
-	  /* printk ("ad1848.c: Bogus interrupt %d\n", irq); */
+	  /* printk("ad1848.c: Bogus interrupt %d\n", irq); */
 	  return;
 	}
 
@@ -2149,8 +2151,7 @@
       tmp |= 0x03;
       break;
     default:
-      DDB (printk ("init_deskpro: Invalid MSS port %x\n",
-		   hw_config->io_base));
+      DDB (printk ("init_deskpro: Invalid MSS port %x\n", hw_config->io_base));
       return 0;
     }
   outb ((tmp & ~0x04), 0xc44);	/* Write to bank=0 */
@@ -2325,8 +2326,7 @@
     {
       int             ret;
 
-      DDB (printk ("No MSS signature detected on port 0x%x (0x%x)\n",
-		   hw_config->io_base, (int) inb (hw_config->io_base + 3)));
+      DDB (printk ("No MSS signature detected on port 0x%x (0x%x)\n", hw_config->io_base, (int) inb (hw_config->io_base + 3)));
       DDB (printk ("Trying to detect codec anyway but IRQ/DMA may not work\n"));
       if (!(ret = ad1848_detect (hw_config->io_base + 4, NULL, hw_config->osp)))
 	return 0;
diff -u --recursive --new-file v2.1.62/linux/drivers/sound/adlib_card.c linux/drivers/sound/adlib_card.c
--- v2.1.62/linux/drivers/sound/adlib_card.c	Tue Mar  4 10:25:24 1997
+++ linux/drivers/sound/adlib_card.c	Tue Sep 30 08:46:50 1997
@@ -16,7 +16,7 @@
 
 #include "sound_config.h"
 
-#if defined(CONFIG_YM3812)
+#ifdef CONFIG_YM3812
 
 void
 attach_adlib_card (struct address_info *hw_config)
@@ -32,8 +32,7 @@
 
   if (check_region (hw_config->io_base, 4))
     {
-      DDB (printk ("opl3.c: I/O port %x already in use\n",
-		   hw_config->io_base));
+      DDB (printk ("opl3.c: I/O port %x already in use\n", hw_config->io_base));
       return 0;
     }
 
diff -u --recursive --new-file v2.1.62/linux/drivers/sound/audio.c linux/drivers/sound/audio.c
--- v2.1.62/linux/drivers/sound/audio.c	Wed May 28 10:51:32 1997
+++ linux/drivers/sound/audio.c	Sun Nov  9 22:26:32 1997
@@ -30,7 +30,7 @@
 #define		AM_NONE		0
 #define		AM_WRITE	OPEN_WRITE
 #define 	AM_READ		OPEN_READ
-static int      dma_ioctl (int dev, unsigned int cmd, caddr_t arg);
+int      dma_ioctl (int dev, unsigned int cmd, caddr_t arg);
 
 
 static int      local_format[MAX_AUDIO_DEV], audio_format[MAX_AUDIO_DEV];
@@ -59,7 +59,7 @@
   else
     return local_format[dev];
 
-  return audio_format[dev];
+  return local_format[dev];
 }
 
 int
@@ -247,12 +247,16 @@
 	{
 	  if ((dma_buf + l) >
 	      (audio_devs[dev]->dmap_out->raw_buf + audio_devs[dev]->dmap_out->buffsize))
-	    printk ("audio: Buffer error 3 (%lx,%d), (%lx, %d)\n",
-		    (long) dma_buf, l,
-		    (long) audio_devs[dev]->dmap_out->raw_buf,
-		    (int) audio_devs[dev]->dmap_out->buffsize);
+	    {
+	      printk ("audio: Buffer error 3 (%lx,%d), (%lx, %d)\n", (long) dma_buf, l, (long) audio_devs[dev]->dmap_out->raw_buf, (int) audio_devs[dev]->dmap_out->buffsize);
+	      return -EDOM;
+	    }
+
 	  if (dma_buf < audio_devs[dev]->dmap_out->raw_buf)
-	    printk ("audio: Buffer error 13\n");
+	    {
+	      printk ("audio: Buffer error 13 (%lx<%lx)\n", (long) dma_buf, (long) audio_devs[dev]->dmap_out->raw_buf);
+	      return -EDOM;
+	    }
 	  copy_from_user (dma_buf, &(buf)[p], l);
 	}
       else
@@ -352,7 +356,7 @@
 {
   int             val;
 
-  /* printk("audio_ioctl(%x, %x)\n", (int)cmd, (int)arg); */
+  /* printk( "audio_ioctl(%x, %x)\n",  (int)cmd,  (int)arg); */
 
   dev = dev >> 4;
 
@@ -484,8 +488,7 @@
 	  n = *(int *) arg;
 	  if (n > 1)
 	    {
-	      printk ("sound: SNDCTL_DSP_STEREO called with invalid argument %d\n",
-		      n);
+	      printk ("sound: SNDCTL_DSP_STEREO called with invalid argument %d\n", n);
 	      return -EINVAL;
 	    }
 
@@ -550,13 +553,9 @@
   unsigned        i, n;
   unsigned        sr, nc, sz, bsz;
 
-  if (!dmap->needs_reorg)
-    return;
-
   sr = dsp_dev->d->set_speed (dev, 0);
   nc = dsp_dev->d->set_channels (dev, 0);
   sz = dsp_dev->d->set_bits (dev, 0);
-  dmap->needs_reorg = 0;
 
   if (sz == 8)
     dmap->neutral_byte = NEUTRAL8;
@@ -565,8 +564,7 @@
 
   if (sr < 1 || nc < 1 || sz < 1)
     {
-      printk ("Warning: Invalid PCM parameters[%d] sr=%d, nc=%d, sz=%d\n",
-	      dev, sr, nc, sz);
+      printk ("Warning: Invalid PCM parameters[%d] sr=%d, nc=%d, sz=%d\n", dev, sr, nc, sz);
       sr = DSP_DEFAULT_SPEED;
       nc = 1;
       sz = 8;
@@ -577,6 +575,10 @@
   sz /= 8;			/* #bits -> #bytes */
   dmap->data_rate = sz;
 
+  if (!dmap->needs_reorg)
+    return;
+  dmap->needs_reorg = 0;
+
   if (dmap->fragment_size == 0)
     {				/* Compute the fragment size using the default algorithm */
 
@@ -752,7 +754,7 @@
     return 0;
 }
 
-static int
+int
 dma_ioctl (int dev, unsigned int cmd, caddr_t arg)
 {
 
@@ -841,6 +843,11 @@
 
 	if (cmd == SNDCTL_DSP_GETISPACE && dmap->qlen)
 	  info->bytes -= dmap->counts[dmap->qhead];
+	else
+	  {
+	    info->fragments = info->bytes / dmap->fragment_size;
+	    info->bytes -= dmap->user_counter % dmap->fragment_size;
+	  }
       }
       return 0;
 
diff -u --recursive --new-file v2.1.62/linux/drivers/sound/configure.c linux/drivers/sound/configure.c
--- v2.1.62/linux/drivers/sound/configure.c	Thu May 29 21:53:08 1997
+++ linux/drivers/sound/configure.c	Tue Sep 30 08:46:50 1997
@@ -45,18 +45,20 @@
 #define OPT_MAD16	12
 #define OPT_CS4232	13
 #define OPT_MAUI	14
-#define OPT_SPNP		15
-
-#define OPT_HIGHLEVEL   16	/* This must be same than the next one */
-#define OPT_UNUSED1	16
-#define OPT_UNUSED2	17
-#define OPT_AEDSP16     18
-#define OPT_UNUSED3	19
-#define OPT_UNUSED4	20
-#define OPT_UNUSED5	21
-#define OPT_YM3812_AUTO	22
-#define OPT_YM3812	23
-#define OPT_LAST	23	/* Last defined OPT number */
+#define OPT_SPNP	15
+#define OPT_OPL3SA1	16
+#define OPT_SOFTOSS	17
+
+#define OPT_HIGHLEVEL   18	/* This must be same than the next one */
+#define OPT_UNUSED1	18
+#define OPT_UNUSED2	19
+#define OPT_AEDSP16     20
+#define OPT_UNUSED3	21
+#define OPT_UNUSED4	22
+#define OPT_UNUSED5	23
+#define OPT_YM3812_AUTO	24
+#define OPT_YM3812	25
+#define OPT_LAST	25	/* Last defined OPT number */
 
 #define DUMMY_OPTS (B(OPT_YM3812_AUTO))
 
@@ -64,16 +66,17 @@
 		  B(OPT_MPU401)|B(OPT_PSS)|B(OPT_GUS16)|B(OPT_GUSMAX)| \
 		  B(OPT_MSS)|B(OPT_SSCAPE)|B(OPT_UART6850)|B(OPT_TRIX)| \
 		  B(OPT_MAD16)|B(OPT_CS4232)|B(OPT_MAUI)|B(OPT_ADLIB)| \
-		  B(OPT_SPNP))
+		  B(OPT_SPNP)|B(OPT_OPL3SA1)|B(OPT_SOFTOSS))
 #define MPU_DEVS (B(OPT_PSS)|\
 		  B(OPT_CS4232)|B(OPT_SPNP)|B(OPT_MAUI)|B(OPT_SSCAPE))
-#define UART401_DEVS (SBDSP_DEVS|B(OPT_TRIX)|B(OPT_MAD16)|B(OPT_SPNP))
+#define UART401_DEVS (SBDSP_DEVS|B(OPT_TRIX)|B(OPT_MAD16)|B(OPT_SPNP)|\
+		  B(OPT_OPL3SA1))
 #define NON_AUDIO_CARDS (B(OPT_ADLIB)|B(OPT_MPU401)|B(OPT_UART6850)|B(OPT_MAUI))
 #define AUDIO_CARDS (ANY_DEVS & ~NON_AUDIO_CARDS)
 #define MIDI_CARDS (ANY_DEVS & ~(B(OPT_ADLIB)|B(OPT_MSS)))
 #define AD1848_DEVS (B(OPT_GUS16)|B(OPT_MSS)|B(OPT_PSS)|B(OPT_GUSMAX)|\
 		     B(OPT_SSCAPE)|B(OPT_TRIX)|B(OPT_MAD16)|B(OPT_CS4232)|\
-		     B(OPT_SPNP))
+		     B(OPT_SPNP)|B(OPT_OPL3SA1))
 #define SBDSP_DEVS (B(OPT_SB)|B(OPT_SPNP)|B(OPT_MAD16)|B(OPT_TRIX))
 #define SEQUENCER_DEVS 0x7fffffff
 /*
@@ -131,6 +134,8 @@
   {0, 0, "CS4232", 1, 0, 0},
   {0, 0, "MAUI", 1, 0, 0},
   {0, 0, "SPNP", 1, 0, 0},
+  {0, 0, "OPL3SA1", 1, 0, 0},
+  {0, 0, "SOFTOSS", 1, 0, 0},
 
   {B (OPT_SB), B (OPT_PAS), "UNUSED1", 1, 0, 1},
   {B (OPT_SB) | B (OPT_UNUSED1), B (OPT_PAS), "UNUSED2", 1, 0, 1},
@@ -156,10 +161,12 @@
   "Microsoft Sound System support",
   "Ensoniq SoundScape support",
   "MediaTrix AudioTrix Pro support",
-  "Support for MAD16 and/or Mozart based cards",
+  "Support for OPTi MAD16 and/or Mozart based cards",
   "Support for Crystal CS4232 based (PnP) cards",
   "Support for Turtle Beach Wave Front (Maui, Tropez) synthesizers",
   "Support for PnP sound cards (_EXPERIMENTAL_)",
+  "Yamaha OPL3-SA1 audio controller",
+  "SoftOSS software wave table engine",
 
   "*** Unused option 1 ***",
   "*** Unused option 2 ***",
@@ -237,6 +244,11 @@
   "Use this option to enable experimental support for cards that\n"
   "use the Plug and Play protocol.\n",
 
+  "Use this option with Yamaha OPL3-SA1 (YMF701) chip.\n",
+
+  "SoftOSS is a virtual wave table engine by 4Front Technologies. It can\n"
+  "be used together with any 16 bit stereo soundcard.\n"
+
   "Enable this option if your card is a Sound Blaster Pro or\n"
   "Sound Blaster 16. It also works with many Sound Blaster Pro clones.\n",
 
@@ -1044,62 +1056,99 @@
 
   if (dump_only)
     show_comment (B (OPT_TRIX),
-    "ERROR! You have to use old sound configuration method with AudioTrix.");
+    "ERROR! You have to use old sound configuration method with OPL3-SA1.");
 
   ask_int_choice (B (OPT_TRIX), "TRIX_BASE",
-		  "AudioTrix audio I/O base",
+		  "OPL3-SA1 audio I/O base",
 		  FMT_HEX,
 		  0x530,
 		  "530, 604, E80 or F40");
 
   ask_int_choice (B (OPT_TRIX), "TRIX_IRQ",
-		  "AudioTrix audio IRQ",
+		  "OPL3-SA1 audio IRQ",
 		  FMT_INT,
 		  11,
 		  "7, 9, 10 or 11");
 
   ask_int_choice (B (OPT_TRIX), "TRIX_DMA",
-		  "AudioTrix audio DMA",
+		  "OPL3-SA1 audio DMA",
 		  FMT_INT,
 		  0,
 		  "0, 1 or 3");
 
   ask_int_choice (B (OPT_TRIX), "TRIX_DMA2",
-		  "AudioTrix second (duplex) DMA",
+		  "OPL3-SA1 second (duplex) DMA",
 		  FMT_INT,
 		  3,
 		  "0, 1 or 3");
 
   ask_int_choice (B (OPT_TRIX), "TRIX_MPU_BASE",
-		  "AudioTrix MIDI I/O base",
+		  "OPL3-SA1 MIDI I/O base",
 		  FMT_HEX,
 		  0x330,
 		  "330, 370, 3B0 or 3F0");
 
   ask_int_choice (B (OPT_TRIX), "TRIX_MPU_IRQ",
-		  "AudioTrix MIDI IRQ",
+		  "OPL3-SA1 MIDI IRQ",
 		  FMT_INT,
 		  9,
 		  "3, 4, 5, 7 or 9");
 
   ask_int_choice (B (OPT_TRIX), "TRIX_SB_BASE",
-		  "AudioTrix SB I/O base",
+		  "OPL3-SA1 SB I/O base",
 		  FMT_HEX,
 		  0x220,
 		  "220, 210, 230, 240, 250, 260 or 270");
 
   ask_int_choice (B (OPT_TRIX), "TRIX_SB_IRQ",
-		  "AudioTrix SB IRQ",
+		  "OPL3-SA1 SB IRQ",
 		  FMT_INT,
 		  7,
 		  "3, 4, 5 or 7");
 
   ask_int_choice (B (OPT_TRIX), "TRIX_SB_DMA",
-		  "AudioTrix SB DMA",
+		  "OPL3-SA1 SB DMA",
 		  FMT_INT,
 		  1,
 		  "1 or 3");
 
+
+  ask_int_choice (B (OPT_OPL3SA1), "OPL3SA1_BASE",
+		  "OPL3-SA1 audio I/O base",
+		  FMT_HEX,
+		  0x530,
+		  "530, 604, E80 or F40");
+
+  ask_int_choice (B (OPT_OPL3SA1), "OPL3SA1_IRQ",
+		  "OPL3-SA1 audio IRQ",
+		  FMT_INT,
+		  11,
+		  "7, 9, 10 or 11");
+
+  ask_int_choice (B (OPT_OPL3SA1), "OPL3SA1_DMA",
+		  "OPL3-SA1 audio DMA",
+		  FMT_INT,
+		  0,
+		  "0, 1 or 3");
+
+  ask_int_choice (B (OPT_OPL3SA1), "OPL3SA1_DMA2",
+		  "OPL3-SA1 second (duplex) DMA",
+		  FMT_INT,
+		  3,
+		  "0, 1 or 3");
+
+  ask_int_choice (B (OPT_OPL3SA1), "OPL3SA1_MPU_BASE",
+		  "OPL3-SA1 MIDI I/O base",
+		  FMT_HEX,
+		  0x330,
+		  "330, 370, 3B0 or 3F0");
+
+  ask_int_choice (B (OPT_OPL3SA1), "OPL3SA1_MPU_IRQ",
+		  "OPL3-SA1 MIDI IRQ",
+		  FMT_INT,
+		  9,
+		  "3, 4, 5, 7 or 9");
+
   ask_int_choice (B (OPT_CS4232), "CS4232_BASE",
 		  "CS4232 audio I/O base",
 		  FMT_HEX,
@@ -1171,6 +1220,16 @@
 		  FMT_INT,
 		  9,
 		  "5, 7, 9 or 10");
+  ask_int_choice (B (OPT_SOFTOSS), "SOFTOSS_RATE",
+		  "Sampling rate for SoftOSS",
+		  FMT_INT,
+		  22050,
+		  "8000 to 48000");
+  ask_int_choice (B (OPT_SOFTOSS), "SOFTOSS_VOICES",
+		  "Max # of concurrent voices for SoftOSS",
+		  FMT_INT,
+		  32,
+		  "4 to 32");
 }
 
 void
diff -u --recursive --new-file v2.1.62/linux/drivers/sound/cs4232.c linux/drivers/sound/cs4232.c
--- v2.1.62/linux/drivers/sound/cs4232.c	Tue Mar  4 10:25:24 1997
+++ linux/drivers/sound/cs4232.c	Tue Sep 30 08:46:50 1997
@@ -25,7 +25,7 @@
 
 #include "sound_config.h"
 
-#if defined(CONFIG_CS4232)
+#ifdef CONFIG_CS4232
 
 #define KEY_PORT	0x279	/* Same as LPT1 status port */
 #define CSN_NUM		0x99	/* Just a random number */
diff -u --recursive --new-file v2.1.62/linux/drivers/sound/dev_table.c linux/drivers/sound/dev_table.c
--- v2.1.62/linux/drivers/sound/dev_table.c	Wed May 28 10:51:32 1997
+++ linux/drivers/sound/dev_table.c	Mon Oct 20 06:26:29 1997
@@ -16,6 +16,7 @@
 #define _DEV_TABLE_C_
 #include "sound_config.h"
 int             sb_be_quiet = 0;
+int             softoss_dev = 0;
 
 int             sound_started = 0;
 
@@ -154,6 +155,9 @@
 	  }
       }
 
+  	for (i=0;i<num_audiodevs;i++)
+      	   DMAbuf_deinit(i);
+
   if (trace_init)
     printk ("Sound unload complete\n");
 }
@@ -163,8 +167,6 @@
 {
   int             i, drv = -1, n = num_sound_cards;
 
-  unsigned long   flags;
-
 
   DEB (printk ("unload driver %d: ", type));
 
@@ -185,11 +187,6 @@
 	  }
       }
   DEB (printk ("\n"));
-
-  save_flags (flags);
-  cli ();
-
-  restore_flags (flags);
 }
 
 
@@ -302,8 +299,7 @@
 	return 1;
       }
 
-  DEB (printk ("sndtable_init_card: No card defined with type=%d, num cards: %d\n",
-	       unit, num_sound_cards));
+  DEB (printk ("sndtable_init_card: No card defined with type=%d, num cards: %d\n", unit, num_sound_cards));
   return 0;
 }
 
diff -u --recursive --new-file v2.1.62/linux/drivers/sound/dev_table.h linux/drivers/sound/dev_table.h
--- v2.1.62/linux/drivers/sound/dev_table.h	Mon Jul  7 08:24:28 1997
+++ linux/drivers/sound/dev_table.h	Tue Sep 30 08:46:44 1997
@@ -15,7 +15,6 @@
 #ifndef _DEV_TABLE_H_
 #define _DEV_TABLE_H_
 
-#include <linux/config.h>
 
 /*
  * Sound card numbers 27 to 999. (1 to 26 are defined in soundcard.h)
@@ -23,6 +22,23 @@
  */
 #define SNDCARD_DESKPROXL		27	/* Compaq Deskpro XL */
 #define SNDCARD_SBPNP			29
+#define SNDCARD_OPL3SA1			38
+#define SNDCARD_OPL3SA1_SB		39
+#define SNDCARD_OPL3SA1_MPU		40
+#define SNDCARD_SOFTOSS			36
+
+void attach_opl3sa_wss (struct address_info *hw_config);
+int probe_opl3sa_wss (struct address_info *hw_config);
+void attach_opl3sa_sb (struct address_info *hw_config);
+int probe_opl3sa_sb (struct address_info *hw_config);
+void attach_opl3sa_mpu (struct address_info *hw_config);
+int probe_opl3sa_mpu (struct address_info *hw_config);
+void unload_opl3sa_wss(struct address_info *hw_info);
+void unload_opl3sa_sb(struct address_info *hw_info);
+void unload_opl3sa_mpu(struct address_info *hw_info);
+void attach_softsyn_card (struct address_info *hw_config);
+int probe_softsyn (struct address_info *hw_config);
+void unload_softsyn (struct address_info *hw_config);
 
 /*
  *	NOTE! 	NOTE!	NOTE!	NOTE!
@@ -125,6 +141,10 @@
 	OS_DMA_PARMS
 #endif
 	int     applic_profile;	/* Application profile (APF_*) */
+	/* Interrupt callback stuff */
+	void (*audio_callback) (int dev, int parm);
+	int callback_parm;
+
 	int	 buf_flags[MAX_SUB_BUFFERS];
 #define		 BUFF_EOF		0x00000001 /* Increment eof count */
 #define		 BUFF_DIRTY		0x00000002 /* Buffer written */
@@ -381,12 +401,23 @@
 		{"SSCAPE", 0, SNDCARD_SSCAPE, "Ensoniq SoundScape",	attach_sscape, probe_sscape, unload_sscape},
 		{"SSCAPEMSS", 0, SNDCARD_SSCAPE_MSS,	"MS Sound System (SoundScape)",	attach_ss_ms_sound, probe_ss_ms_sound, unload_ss_ms_sound},
 #endif
+
+#ifdef CONFIG_OPL3SA1
+	{"OPL3SA", 0, SNDCARD_OPL3SA1, "Yamaha OPL3-SA",	attach_opl3sa_wss, probe_opl3sa_wss, unload_opl3sa_wss}, 
+/*	{"OPL3SASB", 0, SNDCARD_OPL3SA1_SB, "OPL3-SA (SB mode)",	attach_opl3sa_sb, probe_opl3sa_sb, unload_opl3sa_sb}, */
+	{"OPL3SAMPU", 0, SNDCARD_OPL3SA1_MPU, "OPL3-SA MIDI",	attach_opl3sa_mpu, probe_opl3sa_mpu, unload_opl3sa_mpu},
+#endif
+
 #ifdef CONFIG_TRIX
 		{"TRXPRO", 0, SNDCARD_TRXPRO, "MediaTrix AudioTrix Pro",	attach_trix_wss, probe_trix_wss, unload_trix_wss},
 		{"TRXPROSB", 0, SNDCARD_TRXPRO_SB, "AudioTrix (SB mode)",	attach_trix_sb, probe_trix_sb, unload_trix_sb},
 		{"TRXPROMPU", 0, SNDCARD_TRXPRO_MPU, "AudioTrix MIDI",	attach_trix_mpu, probe_trix_mpu, unload_trix_mpu},
 #endif
 
+#ifdef CONFIG_SOFTOSS
+		{"SOFTSYN", 0, SNDCARD_SOFTOSS,	"SoftOSS Virtual Wave Table", 
+		attach_softsyn_card, probe_softsyn, unload_softsyn},
+#endif
 
 
 
@@ -427,6 +458,18 @@
 	     {SNDCARD_TRXPRO_MPU, {TRIX_MPU_BASE, TRIX_MPU_IRQ, 0, -1}, SND_DEFAULT_ENABLE},
 #	endif
 #endif
+
+#ifdef CONFIG_OPL3SA1
+	     {SNDCARD_OPL3SA1, {OPL3SA1_BASE, OPL3SA1_IRQ, OPL3SA1_DMA, OPL3SA1_DMA2}, SND_DEFAULT_ENABLE},
+#	ifdef OPL3SA1_MPU_BASE
+	     {SNDCARD_OPL3SA1_MPU, {OPL3SA1_MPU_BASE, OPL3SA1_MPU_IRQ, 0, -1}, SND_DEFAULT_ENABLE},
+#	endif
+#endif
+
+#ifdef CONFIG_SOFTOSS
+	     {SNDCARD_SOFTOSS, {0, 0, -1, -1}, SND_DEFAULT_ENABLE},
+#endif
+
 #ifdef CONFIG_SSCAPE
 	     {SNDCARD_SSCAPE, {SSCAPE_BASE, SSCAPE_IRQ, SSCAPE_DMA, -1}, SND_DEFAULT_ENABLE},
 	     {SNDCARD_SSCAPE_MSS, {SSCAPE_MSS_BASE, SSCAPE_MSS_IRQ, SSCAPE_DMA, -1}, SND_DEFAULT_ENABLE},
diff -u --recursive --new-file v2.1.62/linux/drivers/sound/dmabuf.c linux/drivers/sound/dmabuf.c
--- v2.1.62/linux/drivers/sound/dmabuf.c	Wed Sep 24 20:05:47 1997
+++ linux/drivers/sound/dmabuf.c	Mon Oct 20 06:25:00 1997
@@ -94,6 +94,8 @@
   dmap->max_byte_counter = 8000 * 60 * 60;
   dmap->applic_profile = APF_NORMAL;
   dmap->needs_reorg = 1;
+  dmap->audio_callback = NULL;
+  dmap->callback_parm = 0;
 
 
   if (dmap->dma_mode & DMODE_OUTPUT)
@@ -267,7 +269,7 @@
   tmout =
     (dmap->fragment_size * HZ) / dmap->data_rate;
 
-  tmout += HZ / 10;		/* Some safety distance */
+  tmout += HZ / 5;		/* Some safety distance */
 
   if (tmout < (HZ / 2))
     tmout = HZ / 2;
@@ -277,7 +279,7 @@
   audio_devs[dev]->dmap_out->flags |= DMA_SYNCING;
 
   audio_devs[dev]->dmap_out->underrun_count = 0;
-  if (!signal_pending(current)
+  if (!(current->signal & ~current->blocked)
       && audio_devs[dev]->dmap_out->qlen
       && audio_devs[dev]->dmap_out->underrun_count == 0)
     {
@@ -400,7 +402,7 @@
       tmout =
 	(dmap->fragment_size * HZ) / dmap->data_rate;
 
-      tmout += HZ / 10;		/* Some safety distance */
+      tmout += HZ / 5;		/* Some safety distance */
 
       if (tmout < (HZ / 2))
 	tmout = HZ / 2;
@@ -416,7 +418,7 @@
       audio_devs[dev]->dmap_out->flags |= DMA_SYNCING;
 
       audio_devs[dev]->dmap_out->underrun_count = 0;
-      while (!signal_pending(current)
+      while (!(current->signal & ~current->blocked)
 	     && n++ <= audio_devs[dev]->dmap_out->nbufs
 	     && audio_devs[dev]->dmap_out->qlen
 	     && audio_devs[dev]->dmap_out->underrun_count == 0)
@@ -456,7 +458,7 @@
       cli ();
       if (audio_devs[dev]->d->local_qlen)	/* Device has hidden buffers */
 	{
-	  while (!signal_pending(current)
+	  while (!((current->signal & ~current->blocked))
 		 && audio_devs[dev]->d->local_qlen (dev))
 	    {
 
@@ -496,7 +498,7 @@
 
   if (audio_devs[dev]->open_mode & OPEN_WRITE)
     if (!(audio_devs[dev]->dmap_in->mapping_flags & DMA_MAP_MAPPED))
-      if (!signal_pending(current)
+      if (!((current->signal & ~current->blocked))
 	  && (audio_devs[dev]->dmap_out->dma_mode == DMODE_OUTPUT))
 	{
 	  DMAbuf_sync (dev);
@@ -592,6 +594,7 @@
   if (audio_devs[dev]->dmap_in->mapping_flags & DMA_MAP_MAPPED)
     {
       printk ("Sound: Can't read from mmapped device (1)\n");
+      restore_flags (flags);
       return -EINVAL;
     }
   else
@@ -627,7 +630,7 @@
 	    tmout =
 	      (dmap->fragment_size * HZ) / dmap->data_rate;
 
-	    tmout += HZ / 10;	/* Some safety distance */
+	    tmout += HZ / 5;	/* Some safety distance */
 
 	    if (tmout < (HZ / 2))
 	      tmout = HZ / 2;
@@ -747,7 +750,7 @@
       enable_dma (dmap->dma);
     }
   restore_flags (flags);
-  /* printk ("%04x ", pos); */
+  /* printk( "%04x ",  pos); */
 
   return pos;
 }
@@ -802,8 +805,8 @@
    */
 
   max = dmap->max_fragments;
-  if (max > dmap->nbufs)
-     max = dmap->nbufs;
+  if (max > lim)
+    max = lim;
   len = dmap->qlen;
 
   if (audio_devs[dev]->d->local_qlen)
@@ -815,6 +818,8 @@
 				 */
       len += tmp;
     }
+  if (dmap->byte_counter % dmap->fragment_size)		/* There is a partial fragment */
+    len = len + 1;
 
   if (len >= max)
     return 0;
@@ -848,7 +853,7 @@
       tmout =
 	(dmap->fragment_size * HZ) / dmap->data_rate;
 
-      tmout += HZ / 10;		/* Some safety distance */
+      tmout += HZ / 5;		/* Some safety distance */
 
       if (tmout < (HZ / 2))
 	tmout = HZ / 2;
@@ -856,7 +861,7 @@
 	tmout = 20 * HZ;
     }
 
-  if (signal_pending(current))
+  if ((current->signal & ~current->blocked))
     return -EIO;
 
 
@@ -882,7 +887,7 @@
       ;
       dma_reset_output (dev);
     }
-  else if (signal_pending(current))
+  else if ((current->signal & ~current->blocked))
     {
       err = -EINTR;
     }
@@ -895,11 +900,14 @@
 {
   struct dma_buffparms *dmap = audio_devs[dev]->dmap_out;
   unsigned long   flags;
-  unsigned long   offs, active_offs;
-  long            len;
+  unsigned long   active_offs;
+  long            len, offs;
   int             maxfrags;
+  int             occupied_bytes = (dmap->user_counter % dmap->fragment_size);
+
+  *buf = dmap->raw_buf;
 
-  if (!(maxfrags = DMAbuf_space_in_queue (dev)))
+  if (!(maxfrags = DMAbuf_space_in_queue (dev)) && !occupied_bytes)
     {
       return 0;
     }
@@ -918,12 +926,20 @@
 #endif
 
   offs = (dmap->user_counter % dmap->bytes_in_use) & ~3;
+  if (offs < 0 || offs >= dmap->bytes_in_use)
+    {
+      printk ("OSS: Got unexpected offs %ld. Giving up.\n", offs);
+      printk ("Counter = %ld, bytes=%d\n", dmap->user_counter, dmap->bytes_in_use);
+      return 0;
+    }
   *buf = dmap->raw_buf + offs;
 
   len = active_offs + dmap->bytes_in_use - dmap->user_counter;	/* Number of unused bytes in buffer */
 
   if ((offs + len) > dmap->bytes_in_use)
-    len = dmap->bytes_in_use - offs;
+    {
+      len = dmap->bytes_in_use - offs;
+    }
 
   if (len < 0)
     {
@@ -931,8 +947,10 @@
       return 0;
     }
 
-  if (len > maxfrags * dmap->fragment_size)
-    len = maxfrags * dmap->fragment_size;
+  if (len > ((maxfrags * dmap->fragment_size) - occupied_bytes))
+    {
+      len = (maxfrags * dmap->fragment_size) - occupied_bytes;
+    }
 
   *size = len & ~3;
 
@@ -967,7 +985,7 @@
   save_flags (flags);
   cli ();
 
-  while (!find_output_space (dev, buf, size))
+  while (find_output_space (dev, buf, size) <= 0)
     {
       if ((err = output_sleep (dev, dontblock)) < 0)
 	{
@@ -1063,8 +1081,7 @@
   if (dmap->raw_buf == NULL)
     {
       printk ("sound: DMA buffer(1) == NULL\n");
-      printk ("Device %d, chn=%s\n", dev,
-	      (dmap == audio_devs[dev]->dmap_out) ? "out" : "in");
+      printk ("Device %d, chn=%s\n", dev, (dmap == audio_devs[dev]->dmap_out) ? "out" : "in");
       return 0;
     }
 
@@ -1096,8 +1113,7 @@
   if (dmap->raw_buf == NULL)
     {
       printk ("sound: DMA buffer(2) == NULL\n");
-      printk ("Device %d, chn=%s\n", dev,
-	      (dmap == audio_devs[dev]->dmap_out) ? "out" : "in");
+      printk ("Device %d, chn=%s\n", dev, (dmap == audio_devs[dev]->dmap_out) ? "out" : "in");
       return 0;
     }
 
@@ -1120,6 +1136,9 @@
 {
   unsigned long   flags;
 
+  if (dmap->audio_callback != NULL)
+    dmap->audio_callback (dev, dmap->callback_parm);
+
   save_flags (flags);
   cli ();
   if ((out_sleep_flag[dev].opts & WK_SLEEP))
@@ -1308,7 +1327,7 @@
     }
   else if (dmap->qlen >= (dmap->nbufs - 1))
     {
-      /* printk ("Sound: Recording overrun\n"); */
+      printk ("Sound: Recording overrun\n");
       dmap->underrun_count++;
 
       /* Just throw away the oldest fragment but keep the engine running */
@@ -1576,5 +1595,16 @@
   return 0;
 }
 
+void
+DMAbuf_deinit(int dev)
+{
+/* This routine is called when driver is being unloaded */
+#ifdef RUNTIME_DMA_ALLOC
+  sound_free_dmap (dev, audio_devs[dev]->dmap_out, audio_devs[dev]->dmap_out->dma);
+
+  if (audio_devs[dev]->flags & DMA_DUPLEX)
+     sound_free_dmap (dev, audio_devs[dev]->dmap_in, audio_devs[dev]->dmap_in->dma);
+#endif
+}
 
 #endif
diff -u --recursive --new-file v2.1.62/linux/drivers/sound/dmabuf.c.old linux/drivers/sound/dmabuf.c.old
--- v2.1.62/linux/drivers/sound/dmabuf.c.old	Wed Dec 31 16:00:00 1969
+++ linux/drivers/sound/dmabuf.c.old	Mon Oct 20 06:24:40 1997
@@ -0,0 +1,1599 @@
+/*
+ * sound/dmabuf.c
+ *
+ * The DMA buffer manager for digitized voice applications
+ */
+/*
+ * Copyright (C) by Hannu Savolainen 1993-1997
+ *
+ * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
+ * Version 2 (June 1991). See the "COPYING" file distributed with this software
+ * for more info.
+ */
+#include <linux/config.h>
+
+#define BE_CONSERVATIVE
+
+#include "sound_config.h"
+
+#if defined(CONFIG_AUDIO) || defined(CONFIG_GUSHW)
+
+static struct wait_queue *in_sleeper[MAX_AUDIO_DEV] =
+{NULL};
+static volatile struct snd_wait in_sleep_flag[MAX_AUDIO_DEV] =
+{
+  {0}};
+static struct wait_queue *out_sleeper[MAX_AUDIO_DEV] =
+{NULL};
+static volatile struct snd_wait out_sleep_flag[MAX_AUDIO_DEV] =
+{
+  {0}};
+
+static int      ndmaps = 0;
+
+#define MAX_DMAP (MAX_AUDIO_DEV*2)
+
+static struct dma_buffparms dmaps[MAX_DMAP] =
+{
+  {0}};
+
+static void     dma_reset_output (int dev);
+static void     dma_reset_input (int dev);
+static int      local_start_dma (int dev, unsigned long physaddr, int count, int dma_mode);
+
+static void
+dma_init_buffers (int dev, struct dma_buffparms *dmap)
+{
+
+  dmap->qlen = dmap->qhead = dmap->qtail = dmap->user_counter = 0;
+  dmap->byte_counter = 0;
+  dmap->max_byte_counter = 8000 * 60 * 60;
+  dmap->bytes_in_use = dmap->buffsize;
+
+  dmap->dma_mode = DMODE_NONE;
+  dmap->mapping_flags = 0;
+  dmap->neutral_byte = 0x80;
+  dmap->data_rate = 8000;
+  dmap->cfrag = -1;
+  dmap->closing = 0;
+  dmap->nbufs = 1;
+  dmap->flags = DMA_BUSY;	/* Other flags off */
+}
+
+static int
+open_dmap (int dev, int mode, struct dma_buffparms *dmap, int chan)
+{
+  if (dmap->flags & DMA_BUSY)
+    return -EBUSY;
+
+  {
+    int             err;
+
+    if ((err = sound_alloc_dmap (dev, dmap, chan)) < 0)
+      return err;
+  }
+
+  if (dmap->raw_buf == NULL)
+    {
+      printk ("Sound: DMA buffers not available\n");
+      return -ENOSPC;		/* Memory allocation failed during boot */
+    }
+
+  if (sound_open_dma (chan, audio_devs[dev]->name))
+    {
+      printk ("Unable to grab(2) DMA%d for the audio driver\n", chan);
+      return -EBUSY;
+    }
+
+  dma_init_buffers (dev, dmap);
+  dmap->open_mode = mode;
+  dmap->subdivision = dmap->underrun_count = 0;
+  dmap->fragment_size = 0;
+  dmap->max_fragments = 65536;	/* Just a large value */
+  dmap->byte_counter = 0;
+  dmap->max_byte_counter = 8000 * 60 * 60;
+  dmap->applic_profile = APF_NORMAL;
+  dmap->needs_reorg = 1;
+  dmap->audio_callback = NULL;
+  dmap->callback_parm = 0;
+
+
+  if (dmap->dma_mode & DMODE_OUTPUT)
+    {
+      out_sleep_flag[dev].opts = WK_NONE;
+    }
+  else
+    {
+      in_sleep_flag[dev].opts = WK_NONE;
+    }
+
+  return 0;
+}
+
+static void
+close_dmap (int dev, struct dma_buffparms *dmap, int chan)
+{
+  sound_close_dma (chan);
+
+  if (dmap->flags & DMA_BUSY)
+    dmap->dma_mode = DMODE_NONE;
+  dmap->flags &= ~DMA_BUSY;
+
+  disable_dma (dmap->dma);
+}
+
+
+static unsigned int
+default_set_bits (int dev, unsigned int bits)
+{
+  return audio_devs[dev]->d->ioctl (dev, SNDCTL_DSP_SETFMT, (caddr_t) & bits);
+}
+
+static int
+default_set_speed (int dev, int speed)
+{
+  return audio_devs[dev]->d->ioctl (dev, SNDCTL_DSP_SPEED, (caddr_t) & speed);
+}
+
+static short
+default_set_channels (int dev, short channels)
+{
+  int             c = channels;
+
+  return audio_devs[dev]->d->ioctl (dev, SNDCTL_DSP_CHANNELS, (caddr_t) & c);
+}
+
+static void
+check_driver (struct audio_driver *d)
+{
+  if (d->set_speed == NULL)
+    d->set_speed = default_set_speed;
+  if (d->set_bits == NULL)
+    d->set_bits = default_set_bits;
+  if (d->set_channels == NULL)
+    d->set_channels = default_set_channels;
+}
+
+int
+DMAbuf_open (int dev, int mode)
+{
+  int             retval;
+  struct dma_buffparms *dmap_in = NULL;
+  struct dma_buffparms *dmap_out = NULL;
+
+  if (dev >= num_audiodevs)
+    {
+      return -ENXIO;
+    }
+
+  if (!audio_devs[dev])
+    {
+      return -ENXIO;
+    }
+
+  if (!(audio_devs[dev]->flags & DMA_DUPLEX))
+    {
+      audio_devs[dev]->dmap_in = audio_devs[dev]->dmap_out;
+      audio_devs[dev]->dmap_in->dma = audio_devs[dev]->dmap_out->dma;
+    }
+
+  check_driver (audio_devs[dev]->d);
+
+  if ((retval = audio_devs[dev]->d->open (dev, mode)) < 0)
+    return retval;
+
+  dmap_out = audio_devs[dev]->dmap_out;
+  dmap_in = audio_devs[dev]->dmap_in;
+
+  if (dmap_in == dmap_out)
+    audio_devs[dev]->flags &= ~DMA_DUPLEX;
+
+  if (mode & OPEN_WRITE)
+    {
+      if ((retval = open_dmap (dev, mode, dmap_out, audio_devs[dev]->dmap_out->dma)) < 0)
+	{
+	  audio_devs[dev]->d->close (dev);
+	  return retval;
+	}
+    }
+
+  audio_devs[dev]->enable_bits = mode;
+
+  if (mode == OPEN_READ || (mode != OPEN_WRITE &&
+			    audio_devs[dev]->flags & DMA_DUPLEX))
+    {
+      if ((retval = open_dmap (dev, mode, dmap_in, audio_devs[dev]->dmap_in->dma)) < 0)
+	{
+	  audio_devs[dev]->d->close (dev);
+
+	  if (mode & OPEN_WRITE)
+	    {
+	      close_dmap (dev, dmap_out, audio_devs[dev]->dmap_out->dma);
+	    }
+
+	  return retval;
+	}
+    }
+
+  audio_devs[dev]->open_mode = mode;
+  audio_devs[dev]->go = 1;
+
+  if (mode & OPEN_READ)
+    in_sleep_flag[dev].opts = WK_NONE;
+
+  if (mode & OPEN_WRITE)
+    out_sleep_flag[dev].opts = WK_NONE;
+
+  audio_devs[dev]->d->set_bits (dev, 8);
+  audio_devs[dev]->d->set_channels (dev, 1);
+  audio_devs[dev]->d->set_speed (dev, DSP_DEFAULT_SPEED);
+
+  if (audio_devs[dev]->dmap_out->dma_mode == DMODE_OUTPUT)
+    {
+      memset (audio_devs[dev]->dmap_out->raw_buf,
+	      audio_devs[dev]->dmap_out->neutral_byte,
+	      audio_devs[dev]->dmap_out->bytes_in_use);
+    }
+
+  return 0;
+}
+
+void
+DMAbuf_reset (int dev)
+{
+  if (audio_devs[dev]->open_mode & OPEN_WRITE)
+    dma_reset_output (dev);
+
+  if (audio_devs[dev]->open_mode & OPEN_READ)
+    dma_reset_input (dev);
+}
+
+static void
+dma_reset_output (int dev)
+{
+  unsigned long   flags;
+  int             tmout;
+
+  struct dma_buffparms *dmap = audio_devs[dev]->dmap_out;
+
+
+  if (!(dmap->flags & DMA_STARTED))	/* DMA is not active */
+    return;
+
+/*
+ * First wait until the current fragment has been played completely
+ */
+  save_flags (flags);
+  cli ();
+
+  tmout =
+    (dmap->fragment_size * HZ) / dmap->data_rate;
+
+  tmout += HZ / 5;		/* Some safety distance */
+
+  if (tmout < (HZ / 2))
+    tmout = HZ / 2;
+  if (tmout > 20 * HZ)
+    tmout = 20 * HZ;
+
+  audio_devs[dev]->dmap_out->flags |= DMA_SYNCING;
+
+  audio_devs[dev]->dmap_out->underrun_count = 0;
+  if (!(current->signal & ~current->blocked)
+      && audio_devs[dev]->dmap_out->qlen
+      && audio_devs[dev]->dmap_out->underrun_count == 0)
+    {
+
+      {
+	unsigned long   tlimit;
+
+	if (tmout)
+	  current->timeout = tlimit = jiffies + (tmout);
+	else
+	  tlimit = (unsigned long) -1;
+	out_sleep_flag[dev].opts = WK_SLEEP;
+	interruptible_sleep_on (&out_sleeper[dev]);
+	if (!(out_sleep_flag[dev].opts & WK_WAKEUP))
+	  {
+	    if (jiffies >= tlimit)
+	      out_sleep_flag[dev].opts |= WK_TIMEOUT;
+	  }
+	out_sleep_flag[dev].opts &= ~WK_SLEEP;
+      };
+    }
+  audio_devs[dev]->dmap_out->flags &= ~(DMA_SYNCING | DMA_ACTIVE);
+
+/*
+ * Finally shut the device off
+ */
+
+  if (!(audio_devs[dev]->flags & DMA_DUPLEX) ||
+      !audio_devs[dev]->d->halt_output)
+    audio_devs[dev]->d->halt_io (dev);
+  else
+    audio_devs[dev]->d->halt_output (dev);
+  audio_devs[dev]->dmap_out->flags &= ~DMA_STARTED;
+  restore_flags (flags);
+
+  clear_dma_ff (dmap->dma);
+  disable_dma (dmap->dma);
+  dmap->byte_counter = 0;
+  reorganize_buffers (dev, audio_devs[dev]->dmap_out, 0);
+  dmap->qlen = dmap->qhead = dmap->qtail = dmap->user_counter = 0;
+}
+
+static void
+dma_reset_input (int dev)
+{
+  unsigned long   flags;
+  struct dma_buffparms *dmap = audio_devs[dev]->dmap_in;
+
+  save_flags (flags);
+  cli ();
+  if (!(audio_devs[dev]->flags & DMA_DUPLEX) ||
+      !audio_devs[dev]->d->halt_input)
+    audio_devs[dev]->d->halt_io (dev);
+  else
+    audio_devs[dev]->d->halt_input (dev);
+  audio_devs[dev]->dmap_in->flags &= ~DMA_STARTED;
+  restore_flags (flags);
+
+  dmap->qlen = dmap->qhead = dmap->qtail = dmap->user_counter = 0;
+  dmap->byte_counter = 0;
+  reorganize_buffers (dev, audio_devs[dev]->dmap_in, 1);
+}
+
+void
+DMAbuf_launch_output (int dev, struct dma_buffparms *dmap)
+{
+  if (!((audio_devs[dev]->enable_bits * audio_devs[dev]->go) & PCM_ENABLE_OUTPUT))
+    return;			/* Don't start DMA yet */
+
+  dmap->dma_mode = DMODE_OUTPUT;
+
+  if (!(dmap->flags & DMA_ACTIVE) || !(audio_devs[dev]->flags & DMA_AUTOMODE) || dmap->flags & DMA_NODMA)
+    {
+      if (!(dmap->flags & DMA_STARTED))
+	{
+	  reorganize_buffers (dev, dmap, 0);
+
+	  if (audio_devs[dev]->d->prepare_for_output (dev,
+					  dmap->fragment_size, dmap->nbufs))
+	    return;
+
+	  if (!(dmap->flags & DMA_NODMA))
+	    {
+	      local_start_dma (dev, dmap->raw_buf_phys, dmap->bytes_in_use,
+			       DMA_MODE_WRITE);
+	    }
+	  dmap->flags |= DMA_STARTED;
+	}
+      if (dmap->counts[dmap->qhead] == 0)
+	dmap->counts[dmap->qhead] = dmap->fragment_size;
+
+      dmap->dma_mode = DMODE_OUTPUT;
+      audio_devs[dev]->d->output_block (dev, dmap->raw_buf_phys +
+					dmap->qhead * dmap->fragment_size,
+					dmap->counts[dmap->qhead], 1);
+      if (audio_devs[dev]->d->trigger)
+	audio_devs[dev]->d->trigger (dev,
+			audio_devs[dev]->enable_bits * audio_devs[dev]->go);
+    }
+  dmap->flags |= DMA_ACTIVE;
+}
+
+int
+DMAbuf_sync (int dev)
+{
+  unsigned long   flags;
+  int             tmout, n = 0;
+
+  if (!audio_devs[dev]->go && (!audio_devs[dev]->enable_bits & PCM_ENABLE_OUTPUT))
+    return 0;
+
+  if (audio_devs[dev]->dmap_out->dma_mode == DMODE_OUTPUT)
+    {
+
+      struct dma_buffparms *dmap = audio_devs[dev]->dmap_out;
+
+      save_flags (flags);
+      cli ();
+
+      tmout =
+	(dmap->fragment_size * HZ) / dmap->data_rate;
+
+      tmout += HZ / 5;		/* Some safety distance */
+
+      if (tmout < (HZ / 2))
+	tmout = HZ / 2;
+      if (tmout > 20 * HZ)
+	tmout = 20 * HZ;
+
+      ;
+      if (dmap->qlen > 0)
+	if (!(dmap->flags & DMA_ACTIVE))
+	  DMAbuf_launch_output (dev, dmap);
+      ;
+
+      audio_devs[dev]->dmap_out->flags |= DMA_SYNCING;
+
+      audio_devs[dev]->dmap_out->underrun_count = 0;
+      while (!(current->signal & ~current->blocked)
+	     && n++ <= audio_devs[dev]->dmap_out->nbufs
+	     && audio_devs[dev]->dmap_out->qlen
+	     && audio_devs[dev]->dmap_out->underrun_count == 0)
+	{
+
+	  {
+	    unsigned long   tlimit;
+
+	    if (tmout)
+	      current->timeout = tlimit = jiffies + (tmout);
+	    else
+	      tlimit = (unsigned long) -1;
+	    out_sleep_flag[dev].opts = WK_SLEEP;
+	    interruptible_sleep_on (&out_sleeper[dev]);
+	    if (!(out_sleep_flag[dev].opts & WK_WAKEUP))
+	      {
+		if (jiffies >= tlimit)
+		  out_sleep_flag[dev].opts |= WK_TIMEOUT;
+	      }
+	    out_sleep_flag[dev].opts &= ~WK_SLEEP;
+	  };
+	  if ((out_sleep_flag[dev].opts & WK_TIMEOUT))
+	    {
+	      audio_devs[dev]->dmap_out->flags &= ~DMA_SYNCING;
+	      restore_flags (flags);
+	      return audio_devs[dev]->dmap_out->qlen;
+	    }
+	}
+      audio_devs[dev]->dmap_out->flags &= ~(DMA_SYNCING | DMA_ACTIVE);
+      restore_flags (flags);
+      /*
+       * Some devices such as GUS have huge amount of on board RAM for the
+       * audio data. We have to wait until the device has finished playing.
+       */
+
+      save_flags (flags);
+      cli ();
+      if (audio_devs[dev]->d->local_qlen)	/* Device has hidden buffers */
+	{
+	  while (!((current->signal & ~current->blocked))
+		 && audio_devs[dev]->d->local_qlen (dev))
+	    {
+
+	      {
+		unsigned long   tlimit;
+
+		if (tmout)
+		  current->timeout = tlimit = jiffies + (tmout);
+		else
+		  tlimit = (unsigned long) -1;
+		out_sleep_flag[dev].opts = WK_SLEEP;
+		interruptible_sleep_on (&out_sleeper[dev]);
+		if (!(out_sleep_flag[dev].opts & WK_WAKEUP))
+		  {
+		    if (jiffies >= tlimit)
+		      out_sleep_flag[dev].opts |= WK_TIMEOUT;
+		  }
+		out_sleep_flag[dev].opts &= ~WK_SLEEP;
+	      };
+	    }
+	}
+      restore_flags (flags);
+    }
+  audio_devs[dev]->dmap_out->dma_mode = DMODE_NONE;
+  return audio_devs[dev]->dmap_out->qlen;
+}
+
+int
+DMAbuf_release (int dev, int mode)
+{
+  unsigned long   flags;
+
+  if (audio_devs[dev]->open_mode & OPEN_WRITE)
+    audio_devs[dev]->dmap_out->closing = 1;
+  if (audio_devs[dev]->open_mode & OPEN_READ)
+    audio_devs[dev]->dmap_in->closing = 1;
+
+  if (audio_devs[dev]->open_mode & OPEN_WRITE)
+    if (!(audio_devs[dev]->dmap_in->mapping_flags & DMA_MAP_MAPPED))
+      if (!((current->signal & ~current->blocked))
+	  && (audio_devs[dev]->dmap_out->dma_mode == DMODE_OUTPUT))
+	{
+	  DMAbuf_sync (dev);
+	}
+
+  if (audio_devs[dev]->dmap_out->dma_mode == DMODE_OUTPUT)
+    {
+      memset (audio_devs[dev]->dmap_out->raw_buf,
+	      audio_devs[dev]->dmap_out->neutral_byte,
+	      audio_devs[dev]->dmap_out->bytes_in_use);
+    }
+
+  save_flags (flags);
+  cli ();
+
+  DMAbuf_reset (dev);
+  audio_devs[dev]->d->close (dev);
+
+  if (audio_devs[dev]->open_mode & OPEN_WRITE)
+    close_dmap (dev, audio_devs[dev]->dmap_out, audio_devs[dev]->dmap_out->dma);
+
+  if (audio_devs[dev]->open_mode == OPEN_READ ||
+      (audio_devs[dev]->open_mode != OPEN_WRITE &&
+       audio_devs[dev]->flags & DMA_DUPLEX))
+    close_dmap (dev, audio_devs[dev]->dmap_in, audio_devs[dev]->dmap_in->dma);
+  audio_devs[dev]->open_mode = 0;
+
+  restore_flags (flags);
+
+  return 0;
+}
+
+int
+DMAbuf_activate_recording (int dev, struct dma_buffparms *dmap)
+{
+  if (!(audio_devs[dev]->open_mode & OPEN_READ))
+    return 0;
+
+  if (!(audio_devs[dev]->enable_bits & PCM_ENABLE_INPUT))
+    return 0;
+
+  if (dmap->dma_mode == DMODE_OUTPUT)	/* Direction change */
+    {
+      DMAbuf_sync (dev);
+      DMAbuf_reset (dev);
+      dmap->dma_mode = DMODE_NONE;
+    }
+
+  if (!dmap->dma_mode)
+    {
+      int             err;
+
+      reorganize_buffers (dev, dmap, 1);
+      if ((err = audio_devs[dev]->d->prepare_for_input (dev,
+				     dmap->fragment_size, dmap->nbufs)) < 0)
+	{
+	  return err;
+	}
+      dmap->dma_mode = DMODE_INPUT;
+    }
+
+  if (!(dmap->flags & DMA_ACTIVE))
+    {
+      if (dmap->needs_reorg)
+	reorganize_buffers (dev, dmap, 0);
+      local_start_dma (dev, dmap->raw_buf_phys, dmap->bytes_in_use,
+		       DMA_MODE_READ);
+      audio_devs[dev]->d->start_input (dev, dmap->raw_buf_phys +
+				       dmap->qtail * dmap->fragment_size,
+				       dmap->fragment_size, 0);
+      dmap->flags |= DMA_ACTIVE;
+      if (audio_devs[dev]->d->trigger)
+	audio_devs[dev]->d->trigger (dev,
+			audio_devs[dev]->enable_bits * audio_devs[dev]->go);
+    }
+  return 0;
+}
+
+int
+DMAbuf_getrdbuffer (int dev, char **buf, int *len, int dontblock)
+{
+  unsigned long   flags;
+  int             err = 0, n = 0;
+  struct dma_buffparms *dmap = audio_devs[dev]->dmap_in;
+
+  if (!(audio_devs[dev]->open_mode & OPEN_READ))
+    return -EIO;
+  if (dmap->needs_reorg)
+    reorganize_buffers (dev, dmap, 0);
+
+  save_flags (flags);
+  cli ();
+  if (audio_devs[dev]->dmap_in->mapping_flags & DMA_MAP_MAPPED)
+    {
+      printk ("Sound: Can't read from mmapped device (1)\n");
+      restore_flags (flags);
+      return -EINVAL;
+    }
+  else
+    while (dmap->qlen <= 0 && n++ < 10)
+      {
+	int             tmout;
+
+	if (!(audio_devs[dev]->enable_bits & PCM_ENABLE_INPUT) ||
+	    !audio_devs[dev]->go)
+	  {
+	    restore_flags (flags);
+	    return -EAGAIN;
+	  }
+
+	if ((err = DMAbuf_activate_recording (dev, dmap)) < 0)
+	  {
+	    restore_flags (flags);
+	    return err;
+	  }
+
+	/* Wait for the next block */
+
+	if (dontblock)
+	  {
+	    restore_flags (flags);
+	    return -EAGAIN;
+	  }
+
+	if (!audio_devs[dev]->go)
+	  tmout = 0;
+	else
+	  {
+	    tmout =
+	      (dmap->fragment_size * HZ) / dmap->data_rate;
+
+	    tmout += HZ / 5;	/* Some safety distance */
+
+	    if (tmout < (HZ / 2))
+	      tmout = HZ / 2;
+	    if (tmout > 20 * HZ)
+	      tmout = 20 * HZ;
+	  }
+
+
+	{
+	  unsigned long   tlimit;
+
+	  if (tmout)
+	    current->timeout = tlimit = jiffies + (tmout);
+	  else
+	    tlimit = (unsigned long) -1;
+	  in_sleep_flag[dev].opts = WK_SLEEP;
+	  interruptible_sleep_on (&in_sleeper[dev]);
+	  if (!(in_sleep_flag[dev].opts & WK_WAKEUP))
+	    {
+	      if (jiffies >= tlimit)
+		in_sleep_flag[dev].opts |= WK_TIMEOUT;
+	    }
+	  in_sleep_flag[dev].opts &= ~WK_SLEEP;
+	};
+	if ((in_sleep_flag[dev].opts & WK_TIMEOUT))
+	  {
+	    err = -EIO;
+	    printk ("Sound: DMA (input) timed out - IRQ/DRQ config error?\n");
+	    dma_reset_input (dev);
+	    ;
+	  }
+	else
+	  err = -EINTR;
+      }
+  restore_flags (flags);
+
+  if (dmap->qlen <= 0)
+    {
+      if (err == 0)
+	err = -EINTR;
+      return err;
+    }
+
+  *buf = &dmap->raw_buf[dmap->qhead * dmap->fragment_size + dmap->counts[dmap->qhead]];
+  *len = dmap->fragment_size - dmap->counts[dmap->qhead];
+
+  return dmap->qhead;
+}
+
+int
+DMAbuf_rmchars (int dev, int buff_no, int c)
+{
+  struct dma_buffparms *dmap = audio_devs[dev]->dmap_in;
+
+  int             p = dmap->counts[dmap->qhead] + c;
+
+  if (dmap->mapping_flags & DMA_MAP_MAPPED)
+    {
+      printk ("Sound: Can't read from mmapped device (2)\n");
+      return -EINVAL;
+    }
+  else if (dmap->qlen <= 0)
+    return -EIO;
+  else if (p >= dmap->fragment_size)
+    {				/* This buffer is completely empty */
+      dmap->counts[dmap->qhead] = 0;
+      dmap->qlen--;
+      dmap->qhead = (dmap->qhead + 1) % dmap->nbufs;
+    }
+  else
+    dmap->counts[dmap->qhead] = p;
+
+  return 0;
+}
+
+int
+DMAbuf_get_buffer_pointer (int dev, struct dma_buffparms *dmap, int direction)
+{
+/*
+ * Try to approximate the active byte position of the DMA pointer within the
+ * buffer area as well as possible.
+ */
+  int             pos;
+  unsigned long   flags;
+
+  save_flags (flags);
+  cli ();
+  if (!(dmap->flags & DMA_ACTIVE))
+    pos = 0;
+  else
+    {
+      int             chan = dmap->dma;
+
+      clear_dma_ff (chan);
+      disable_dma (dmap->dma);
+      pos = get_dma_residue (chan);
+      pos = dmap->bytes_in_use - pos;
+
+      if (!(dmap->mapping_flags & DMA_MAP_MAPPED))
+	if (direction == DMODE_OUTPUT)
+	  {
+	    if (dmap->qhead == 0)
+	      if (pos > dmap->fragment_size)
+		pos = 0;
+	  }
+	else
+	  {
+	    if (dmap->qtail == 0)
+	      if (pos > dmap->fragment_size)
+		pos = 0;
+	  }
+
+      if (pos < 0)
+	pos = 0;
+      if (pos >= dmap->bytes_in_use)
+	pos = 0;
+      enable_dma (dmap->dma);
+    }
+  restore_flags (flags);
+  /* printk( "%04x ",  pos); */
+
+  return pos;
+}
+
+/*
+ * DMAbuf_start_devices() is called by the /dev/music driver to start
+ * one or more audio devices at desired moment.
+ */
+static void
+DMAbuf_start_device (int dev)
+{
+  if (audio_devs[dev]->open_mode != 0)
+    if (!audio_devs[dev]->go)
+      {
+	/* OK to start the device */
+	audio_devs[dev]->go = 1;
+
+	if (audio_devs[dev]->d->trigger)
+	  audio_devs[dev]->d->trigger (dev,
+			audio_devs[dev]->enable_bits * audio_devs[dev]->go);
+      }
+}
+
+void
+DMAbuf_start_devices (unsigned int devmask)
+{
+  int             dev;
+
+  for (dev = 0; dev < num_audiodevs; dev++)
+    if (devmask & (1 << dev))
+      DMAbuf_start_device (dev);
+}
+
+int
+DMAbuf_space_in_queue (int dev)
+{
+  int             len, max, tmp;
+  struct dma_buffparms *dmap = audio_devs[dev]->dmap_out;
+
+  int             lim = dmap->nbufs;
+
+
+  if (lim < 2)
+    lim = 2;
+
+  if (dmap->qlen >= lim)	/* No space at all */
+    return 0;
+
+  /*
+   * Verify that there are no more pending buffers than the limit
+   * defined by the process.
+   */
+
+  max = dmap->max_fragments;
+  if (max > lim)
+    max = lim;
+  len = dmap->qlen;
+
+  if (audio_devs[dev]->d->local_qlen)
+    {
+      tmp = audio_devs[dev]->d->local_qlen (dev);
+      if (tmp && len)
+	tmp--;			/*
+				 * This buffer has been counted twice
+				 */
+      len += tmp;
+    }
+  if (dmap->byte_counter % dmap->fragment_size)		/* There is a partial fragment */
+    len = len + 1;
+
+  if (len >= max)
+    return 0;
+  return max - len;
+}
+
+static int
+output_sleep (int dev, int dontblock)
+{
+  int             tmout;
+  int             err = 0;
+  struct dma_buffparms *dmap = audio_devs[dev]->dmap_out;
+
+  if (dontblock)
+    {
+      return -EAGAIN;
+    }
+
+  if (!(audio_devs[dev]->enable_bits & PCM_ENABLE_OUTPUT))
+    {
+      return -EAGAIN;
+    }
+
+  /*
+   * Wait for free space
+   */
+  if (!audio_devs[dev]->go || dmap->flags & DMA_NOTIMEOUT)
+    tmout = 0;
+  else
+    {
+      tmout =
+	(dmap->fragment_size * HZ) / dmap->data_rate;
+
+      tmout += HZ / 5;		/* Some safety distance */
+
+      if (tmout < (HZ / 2))
+	tmout = HZ / 2;
+      if (tmout > 20 * HZ)
+	tmout = 20 * HZ;
+    }
+
+  if ((current->signal & ~current->blocked))
+    return -EIO;
+
+
+  {
+    unsigned long   tlimit;
+
+    if (tmout)
+      current->timeout = tlimit = jiffies + (tmout);
+    else
+      tlimit = (unsigned long) -1;
+    out_sleep_flag[dev].opts = WK_SLEEP;
+    interruptible_sleep_on (&out_sleeper[dev]);
+    if (!(out_sleep_flag[dev].opts & WK_WAKEUP))
+      {
+	if (jiffies >= tlimit)
+	  out_sleep_flag[dev].opts |= WK_TIMEOUT;
+      }
+    out_sleep_flag[dev].opts &= ~WK_SLEEP;
+  };
+  if ((out_sleep_flag[dev].opts & WK_TIMEOUT))
+    {
+      printk ("Sound: DMA (output) timed out - IRQ/DRQ config error?\n");
+      ;
+      dma_reset_output (dev);
+    }
+  else if ((current->signal & ~current->blocked))
+    {
+      err = -EINTR;
+    }
+
+  return err;
+}
+
+static int
+find_output_space (int dev, char **buf, int *size)
+{
+  struct dma_buffparms *dmap = audio_devs[dev]->dmap_out;
+  unsigned long   flags;
+  unsigned long   active_offs;
+  long            len, offs;
+  int             maxfrags;
+  int             occupied_bytes = (dmap->user_counter % dmap->fragment_size);
+
+  *buf = dmap->raw_buf;
+
+  if (!(maxfrags = DMAbuf_space_in_queue (dev)) && !occupied_bytes)
+    {
+      return 0;
+    }
+
+  save_flags (flags);
+  cli ();
+
+#ifdef BE_CONSERVATIVE
+  active_offs = dmap->byte_counter + dmap->qhead * dmap->fragment_size;
+#else
+  active_offs = DMAbuf_get_buffer_pointer (dev, dmap, DMODE_OUTPUT);
+  /* Check for pointer wrapping situation */
+  if (active_offs < 0 || active_offs >= dmap->bytes_in_use)
+    active_offs = 0;
+  active_offs += dmap->byte_counter;
+#endif
+
+  offs = (dmap->user_counter % dmap->bytes_in_use) & ~3;
+  if (offs < 0 || offs >= dmap->bytes_in_use)
+    {
+      printk ("OSS: Got unexpected offs %ld. Giving up.\n", offs);
+      printk ("Counter = %ld, bytes=%d\n", dmap->user_counter, dmap->bytes_in_use);
+      return 0;
+    }
+  *buf = dmap->raw_buf + offs;
+
+  len = active_offs + dmap->bytes_in_use - dmap->user_counter;	/* Number of unused bytes in buffer */
+
+  if ((offs + len) > dmap->bytes_in_use)
+    {
+      len = dmap->bytes_in_use - offs;
+    }
+
+  if (len < 0)
+    {
+      restore_flags (flags);
+      return 0;
+    }
+
+  if (len > ((maxfrags * dmap->fragment_size) - occupied_bytes))
+    {
+      len = (maxfrags * dmap->fragment_size) - occupied_bytes;
+    }
+
+  *size = len & ~3;
+
+  restore_flags (flags);
+  return (len > 0);
+}
+
+int
+DMAbuf_getwrbuffer (int dev, char **buf, int *size, int dontblock)
+{
+  unsigned long   flags;
+  int             err = -EIO;
+  struct dma_buffparms *dmap = audio_devs[dev]->dmap_out;
+
+  if (dmap->needs_reorg)
+    reorganize_buffers (dev, dmap, 0);
+
+  if (dmap->mapping_flags & DMA_MAP_MAPPED)
+    {
+      printk ("Sound: Can't write to mmapped device (3)\n");
+      return -EINVAL;
+    }
+
+  if (dmap->dma_mode == DMODE_INPUT)	/* Direction change */
+    {
+      DMAbuf_reset (dev);
+      dmap->dma_mode = DMODE_NONE;
+    }
+
+  dmap->dma_mode = DMODE_OUTPUT;
+
+  save_flags (flags);
+  cli ();
+
+  while (find_output_space (dev, buf, size) <= 0)
+    {
+      if ((err = output_sleep (dev, dontblock)) < 0)
+	{
+	  restore_flags (flags);
+	  return err;
+	}
+    }
+
+  restore_flags (flags);
+
+  return 0;
+}
+
+int
+DMAbuf_move_wrpointer (int dev, int l)
+{
+  struct dma_buffparms *dmap = audio_devs[dev]->dmap_out;
+  unsigned long   ptr = (dmap->user_counter / dmap->fragment_size)
+  * dmap->fragment_size;
+
+  unsigned long   end_ptr, p;
+  int             post = (dmap->flags & DMA_POST);
+
+  ;
+
+  dmap->flags &= ~DMA_POST;
+
+  dmap->cfrag = -1;
+
+  dmap->user_counter += l;
+  dmap->flags |= DMA_DIRTY;
+
+  if (dmap->user_counter >= dmap->max_byte_counter)
+    {				/* Wrap the byte counters */
+      long            decr = dmap->user_counter;
+
+      dmap->user_counter = (dmap->user_counter % dmap->bytes_in_use) + dmap->bytes_in_use;
+      decr -= dmap->user_counter;
+      dmap->byte_counter -= decr;
+    }
+
+  end_ptr = (dmap->user_counter / dmap->fragment_size) * dmap->fragment_size;
+
+  p = (dmap->user_counter - 1) % dmap->bytes_in_use;
+  dmap->neutral_byte = dmap->raw_buf[p];
+
+  /* Update the fragment based bookkeeping too */
+  while (ptr < end_ptr)
+    {
+      dmap->counts[dmap->qtail] = dmap->fragment_size;
+      dmap->qtail = (dmap->qtail + 1) % dmap->nbufs;
+      dmap->qlen++;
+      ptr += dmap->fragment_size;
+    }
+
+  dmap->counts[dmap->qtail] = dmap->user_counter - ptr;
+
+/*
+ * Let the low level driver to perform some postprocessing to
+ * the written data.
+ */
+  if (audio_devs[dev]->d->postprocess_write)
+    audio_devs[dev]->d->postprocess_write (dev);
+
+  if (!(dmap->flags & DMA_ACTIVE))
+    if (dmap->qlen > 1 ||
+	(dmap->qlen > 0 && (post || dmap->qlen >= dmap->nbufs - 1)))
+      {
+	DMAbuf_launch_output (dev, dmap);
+      }
+
+  ;
+  return 0;
+}
+
+int
+DMAbuf_start_dma (int dev, unsigned long physaddr, int count, int dma_mode)
+{
+  int             chan;
+  struct dma_buffparms *dmap;
+
+  if (dma_mode == DMA_MODE_WRITE)
+    {
+      chan = audio_devs[dev]->dmap_out->dma;
+      dmap = audio_devs[dev]->dmap_out;
+    }
+  else
+    {
+      chan = audio_devs[dev]->dmap_in->dma;
+      dmap = audio_devs[dev]->dmap_in;
+    }
+
+  if (dmap->raw_buf == NULL)
+    {
+      printk ("sound: DMA buffer(1) == NULL\n");
+      printk ("Device %d, chn=%s\n", dev, (dmap == audio_devs[dev]->dmap_out) ? "out" : "in");
+      return 0;
+    }
+
+  if (chan < 0)
+    return 0;
+
+  sound_start_dma (dev, dmap, chan, physaddr, count, dma_mode, 0);
+
+  return count;
+}
+
+static int
+local_start_dma (int dev, unsigned long physaddr, int count, int dma_mode)
+{
+  int             chan;
+  struct dma_buffparms *dmap;
+
+  if (dma_mode == DMA_MODE_WRITE)
+    {
+      chan = audio_devs[dev]->dmap_out->dma;
+      dmap = audio_devs[dev]->dmap_out;
+    }
+  else
+    {
+      chan = audio_devs[dev]->dmap_in->dma;
+      dmap = audio_devs[dev]->dmap_in;
+    }
+
+  if (dmap->raw_buf == NULL)
+    {
+      printk ("sound: DMA buffer(2) == NULL\n");
+      printk ("Device %d, chn=%s\n", dev, (dmap == audio_devs[dev]->dmap_out) ? "out" : "in");
+      return 0;
+    }
+
+  if (dmap->flags & DMA_NODMA)
+    {
+      return 1;
+    }
+
+  if (chan < 0)
+    return 0;
+
+  sound_start_dma (dev, dmap, chan, dmap->raw_buf_phys, dmap->bytes_in_use, dma_mode, 1);
+  dmap->flags |= DMA_STARTED;
+
+  return count;
+}
+
+static void
+finish_output_interrupt (int dev, struct dma_buffparms *dmap)
+{
+  unsigned long   flags;
+
+  if (dmap->audio_callback != NULL)
+    dmap->audio_callback (dev, dmap->callback_parm);
+
+  save_flags (flags);
+  cli ();
+  if ((out_sleep_flag[dev].opts & WK_SLEEP))
+    {
+      {
+	out_sleep_flag[dev].opts = WK_WAKEUP;
+	wake_up (&out_sleeper[dev]);
+      };
+    }
+  restore_flags (flags);
+}
+
+static void
+do_outputintr (int dev, int dummy)
+{
+  unsigned long   flags;
+  struct dma_buffparms *dmap = audio_devs[dev]->dmap_out;
+  int             this_fragment;
+
+#ifdef OS_DMA_INTR
+  if (audio_devs[dev]->dmap_out->dma >= 0)
+    sound_dma_intr (dev, audio_devs[dev]->dmap_out, audio_devs[dev]->dmap_out->dma);
+#endif
+
+  if (dmap->raw_buf == NULL)
+    {
+      printk ("Sound: Fatal error. Audio interrupt (%d) after freeing buffers.\n", dev);
+      return;
+    }
+
+  if (dmap->mapping_flags & DMA_MAP_MAPPED)	/* Virtual memory mapped access */
+    {
+      /* mmapped access */
+      dmap->qhead = (dmap->qhead + 1) % dmap->nbufs;
+      if (dmap->qhead == 0)	/* Wrapped */
+	{
+	  dmap->byte_counter += dmap->bytes_in_use;
+	  if (dmap->byte_counter >= dmap->max_byte_counter)	/* Overflow */
+	    {
+	      long            decr = dmap->byte_counter;
+
+	      dmap->byte_counter = (dmap->byte_counter % dmap->bytes_in_use) + dmap->bytes_in_use;
+	      decr -= dmap->byte_counter;
+	      dmap->user_counter -= decr;
+	    }
+	}
+
+      dmap->qlen++;		/* Yes increment it (don't decrement) */
+      if (!(audio_devs[dev]->flags & DMA_AUTOMODE))
+	dmap->flags &= ~DMA_ACTIVE;
+      dmap->counts[dmap->qhead] = dmap->fragment_size;
+
+      DMAbuf_launch_output (dev, dmap);
+      finish_output_interrupt (dev, dmap);
+      return;
+    }
+
+  save_flags (flags);
+  cli ();
+
+  dmap->qlen--;
+  this_fragment = dmap->qhead;
+  dmap->qhead = (dmap->qhead + 1) % dmap->nbufs;
+
+  if (dmap->qhead == 0)		/* Wrapped */
+    {
+      dmap->byte_counter += dmap->bytes_in_use;
+      if (dmap->byte_counter >= dmap->max_byte_counter)		/* Overflow */
+	{
+	  long            decr = dmap->byte_counter;
+
+	  dmap->byte_counter = (dmap->byte_counter % dmap->bytes_in_use) + dmap->bytes_in_use;
+	  decr -= dmap->byte_counter;
+	  dmap->user_counter -= decr;
+	}
+    }
+
+  if (!(audio_devs[dev]->flags & DMA_AUTOMODE))
+    dmap->flags &= ~DMA_ACTIVE;
+
+  while (dmap->qlen < 0)
+    {
+      dmap->underrun_count++;
+
+      dmap->qlen++;
+      if (dmap->flags & DMA_DIRTY && dmap->applic_profile != APF_CPUINTENS)
+	{
+	  dmap->flags &= ~DMA_DIRTY;
+	  memset (audio_devs[dev]->dmap_out->raw_buf,
+		  audio_devs[dev]->dmap_out->neutral_byte,
+		  audio_devs[dev]->dmap_out->buffsize);
+	}
+      dmap->user_counter += dmap->fragment_size;
+      dmap->qtail = (dmap->qtail + 1) % dmap->nbufs;
+    }
+
+  if (dmap->qlen > 0)
+    DMAbuf_launch_output (dev, dmap);
+
+  restore_flags (flags);
+  finish_output_interrupt (dev, dmap);
+}
+
+void
+DMAbuf_outputintr (int dev, int notify_only)
+{
+  unsigned long   flags;
+  struct dma_buffparms *dmap = audio_devs[dev]->dmap_out;
+
+  save_flags (flags);
+  cli ();
+
+  if (!(dmap->flags & DMA_NODMA))
+    {
+      int             chan = dmap->dma, pos, n;
+
+      clear_dma_ff (chan);
+      disable_dma (dmap->dma);
+      pos = dmap->bytes_in_use - get_dma_residue (chan);
+      enable_dma (dmap->dma);
+
+      pos = pos / dmap->fragment_size;	/* Actual qhead */
+      if (pos < 0 || pos >= dmap->nbufs)
+	pos = 0;
+
+      n = 0;
+      while (dmap->qhead != pos && n++ < dmap->nbufs)
+	{
+	  do_outputintr (dev, notify_only);
+	}
+    }
+  else
+    do_outputintr (dev, notify_only);
+  restore_flags (flags);
+}
+
+static void
+do_inputintr (int dev)
+{
+  struct dma_buffparms *dmap = audio_devs[dev]->dmap_in;
+  unsigned long   flags;
+
+#ifdef OS_DMA_INTR
+  if (audio_devs[dev]->dmap_in->dma >= 0)
+    sound_dma_intr (dev, audio_devs[dev]->dmap_in, audio_devs[dev]->dmap_in->dma);
+#endif
+
+  if (dmap->raw_buf == NULL)
+    {
+      printk ("Sound: Fatal error. Audio interrupt after freeing buffers.\n");
+      return;
+    }
+
+  if (dmap->mapping_flags & DMA_MAP_MAPPED)
+    {
+      dmap->qtail = (dmap->qtail + 1) % dmap->nbufs;
+      if (dmap->qtail == 0)	/* Wrapped */
+	{
+	  dmap->byte_counter += dmap->bytes_in_use;
+	  if (dmap->byte_counter >= dmap->max_byte_counter)	/* Overflow */
+	    {
+	      long            decr = dmap->byte_counter;
+
+	      dmap->byte_counter = (dmap->byte_counter % dmap->bytes_in_use) + dmap->bytes_in_use;
+	      decr -= dmap->byte_counter;
+	      dmap->user_counter -= decr;
+	    }
+	}
+      dmap->qlen++;
+
+      if (!(audio_devs[dev]->flags & DMA_AUTOMODE))
+	{
+	  if (dmap->needs_reorg)
+	    reorganize_buffers (dev, dmap, 0);
+	  local_start_dma (dev, dmap->raw_buf_phys, dmap->bytes_in_use,
+			   DMA_MODE_READ);
+	  audio_devs[dev]->d->start_input (dev, dmap->raw_buf_phys +
+					   dmap->qtail * dmap->fragment_size,
+					   dmap->fragment_size, 1);
+	  if (audio_devs[dev]->d->trigger)
+	    audio_devs[dev]->d->trigger (dev,
+			audio_devs[dev]->enable_bits * audio_devs[dev]->go);
+	}
+
+      dmap->flags |= DMA_ACTIVE;
+    }
+  else if (dmap->qlen >= (dmap->nbufs - 1))
+    {
+      printk ("Sound: Recording overrun\n");
+      dmap->underrun_count++;
+
+      /* Just throw away the oldest fragment but keep the engine running */
+      dmap->qhead = (dmap->qhead + 1) % dmap->nbufs;
+      dmap->qtail = (dmap->qtail + 1) % dmap->nbufs;
+    }
+  else if (dmap->qlen >= 0 && dmap->qlen < dmap->nbufs)
+    {
+      dmap->qlen++;
+      dmap->qtail = (dmap->qtail + 1) % dmap->nbufs;
+      if (dmap->qtail == 0)	/* Wrapped */
+	{
+	  dmap->byte_counter += dmap->bytes_in_use;
+	  if (dmap->byte_counter >= dmap->max_byte_counter)	/* Overflow */
+	    {
+	      long            decr = dmap->byte_counter;
+
+	      dmap->byte_counter = (dmap->byte_counter % dmap->bytes_in_use) + dmap->bytes_in_use;
+	      decr -= dmap->byte_counter;
+	      dmap->user_counter -= decr;
+	    }
+	}
+    }
+
+  if (!(audio_devs[dev]->flags & DMA_AUTOMODE) || dmap->flags & DMA_NODMA)
+    {
+      local_start_dma (dev, dmap->raw_buf_phys, dmap->bytes_in_use,
+		       DMA_MODE_READ);
+      audio_devs[dev]->d->start_input (dev, dmap->raw_buf_phys +
+				       dmap->qtail * dmap->fragment_size,
+				       dmap->fragment_size, 1);
+      if (audio_devs[dev]->d->trigger)
+	audio_devs[dev]->d->trigger (dev,
+			audio_devs[dev]->enable_bits * audio_devs[dev]->go);
+    }
+
+  dmap->flags |= DMA_ACTIVE;
+
+  save_flags (flags);
+  cli ();
+  if (dmap->qlen > 0)
+    if ((in_sleep_flag[dev].opts & WK_SLEEP))
+      {
+	{
+	  in_sleep_flag[dev].opts = WK_WAKEUP;
+	  wake_up (&in_sleeper[dev]);
+	};
+      }
+  restore_flags (flags);
+}
+
+void
+DMAbuf_inputintr (int dev)
+{
+  struct dma_buffparms *dmap = audio_devs[dev]->dmap_in;
+  unsigned long   flags;
+
+  save_flags (flags);
+  cli ();
+
+  if (!(dmap->flags & DMA_NODMA))
+    {
+      int             chan = dmap->dma, pos, n;
+
+      clear_dma_ff (chan);
+      disable_dma (dmap->dma);
+      pos = dmap->bytes_in_use - get_dma_residue (chan);
+      enable_dma (dmap->dma);
+
+      pos = pos / dmap->fragment_size;	/* Actual qhead */
+      if (pos < 0 || pos >= dmap->nbufs)
+	pos = 0;
+
+      n = 0;
+      while (dmap->qtail != pos && ++n < dmap->nbufs)
+	{
+	  do_inputintr (dev);
+	}
+    }
+  else
+    do_inputintr (dev);
+  restore_flags (flags);
+}
+
+int
+DMAbuf_open_dma (int dev)
+{
+/*
+ *    NOTE!  This routine opens only the primary DMA channel (output).
+ */
+
+  int             chan = audio_devs[dev]->dmap_out->dma;
+  int             err;
+
+  if ((err = open_dmap (dev, OPEN_READWRITE, audio_devs[dev]->dmap_out, chan)) < 0)
+    {
+      return -EBUSY;
+    }
+  dma_init_buffers (dev, audio_devs[dev]->dmap_out);
+  out_sleep_flag[dev].opts = WK_NONE;
+  audio_devs[dev]->dmap_out->flags |= DMA_ALLOC_DONE;
+  audio_devs[dev]->dmap_out->fragment_size = audio_devs[dev]->dmap_out->buffsize;
+
+  if (chan >= 0)
+    {
+      unsigned long   flags;
+
+      save_flags (flags);
+      cli ();
+      disable_dma (audio_devs[dev]->dmap_out->dma);
+      clear_dma_ff (chan);
+      restore_flags (flags);
+    }
+
+  return 0;
+}
+
+void
+DMAbuf_close_dma (int dev)
+{
+  close_dmap (dev, audio_devs[dev]->dmap_out, audio_devs[dev]->dmap_out->dma);
+}
+
+void
+DMAbuf_init (int dev, int dma1, int dma2)
+{
+  /*
+     * NOTE! This routine could be called several times.
+   */
+
+  if (audio_devs[dev]->dmap_out == NULL)
+    {
+      if (audio_devs[dev]->d == NULL)
+	panic ("OSS: audio_devs[%d]->d == NULL\n", dev);
+
+      if (audio_devs[dev]->parent_dev)
+	{			/* Use DMA map of the parent dev */
+	  int             parent = audio_devs[dev]->parent_dev - 1;
+
+	  audio_devs[dev]->dmap_out = audio_devs[parent]->dmap_out;
+	  audio_devs[dev]->dmap_in = audio_devs[parent]->dmap_in;
+	}
+      else
+	{
+	  audio_devs[dev]->dmap_out =
+	    audio_devs[dev]->dmap_in =
+	    &dmaps[ndmaps++];
+	  audio_devs[dev]->dmap_out->dma = dma1;
+
+	  if (audio_devs[dev]->flags & DMA_DUPLEX)
+	    {
+	      audio_devs[dev]->dmap_in =
+		&dmaps[ndmaps++];
+	      audio_devs[dev]->dmap_in->dma = dma2;
+	    }
+	}
+    }
+}
+
+int
+DMAbuf_select (int dev, struct fileinfo *file, int sel_type, poll_table * wait)
+{
+  struct dma_buffparms *dmap;
+  unsigned long   flags;
+
+  switch (sel_type)
+    {
+    case SEL_IN:
+      if (!(audio_devs[dev]->open_mode & OPEN_READ))
+	return 0;
+
+      dmap = audio_devs[dev]->dmap_in;
+
+      if (dmap->mapping_flags & DMA_MAP_MAPPED)
+	{
+	  if (dmap->qlen)
+	    return 1;
+
+	  save_flags (flags);
+	  cli ();
+
+	  in_sleep_flag[dev].opts = WK_SLEEP;
+	  poll_wait (&in_sleeper[dev], wait);
+	  restore_flags (flags);
+	  return 0;
+	}
+
+      if (dmap->dma_mode != DMODE_INPUT)
+	{
+	  if (dmap->dma_mode == DMODE_NONE &&
+	      audio_devs[dev]->enable_bits & PCM_ENABLE_INPUT &&
+	      !dmap->qlen &&
+	      audio_devs[dev]->go)
+	    {
+	      unsigned long   flags;
+
+	      save_flags (flags);
+	      cli ();
+	      DMAbuf_activate_recording (dev, dmap);
+	      restore_flags (flags);
+	    }
+	  return 0;
+	}
+
+      if (!dmap->qlen)
+	{
+	  save_flags (flags);
+	  cli ();
+
+	  in_sleep_flag[dev].opts = WK_SLEEP;
+	  poll_wait (&in_sleeper[dev], wait);
+	  restore_flags (flags);
+	  return 0;
+	}
+      return 1;
+      break;
+
+    case SEL_OUT:
+      dmap = audio_devs[dev]->dmap_out;
+
+      if (!(audio_devs[dev]->open_mode & OPEN_WRITE))
+	return 0;
+
+      if (dmap->mapping_flags & DMA_MAP_MAPPED)
+	{
+	  if (dmap->qlen)
+	    return 1;
+
+	  save_flags (flags);
+	  cli ();
+
+	  out_sleep_flag[dev].opts = WK_SLEEP;
+	  poll_wait (&out_sleeper[dev], wait);
+	  restore_flags (flags);
+	  return 0;
+	}
+
+      if (dmap->dma_mode == DMODE_INPUT)
+	{
+	  return 0;
+	}
+
+      if (dmap->dma_mode == DMODE_NONE)
+	{
+	  return 1;
+	}
+
+      if (!DMAbuf_space_in_queue (dev))
+	{
+	  save_flags (flags);
+	  cli ();
+
+	  out_sleep_flag[dev].opts = WK_SLEEP;
+	  poll_wait (&out_sleeper[dev], wait);
+	  restore_flags (flags);
+	  return 0;
+	}
+      return 1;
+      break;
+
+    case SEL_EX:
+      return 0;
+    }
+
+  return 0;
+}
+
+
+#endif
diff -u --recursive --new-file v2.1.62/linux/drivers/sound/dmasound.c linux/drivers/sound/dmasound.c
--- v2.1.62/linux/drivers/sound/dmasound.c	Wed Sep 24 20:05:47 1997
+++ linux/drivers/sound/dmasound.c	Tue Sep 30 08:46:59 1997
@@ -628,7 +628,7 @@
 static struct sound_queue sq;
 
 #define sq_block_address(i)	(sq.buffers[i])
-#define SIGNAL_RECEIVED	(signal_pending(current))
+#define SIGNAL_RECEIVED	(current->signal & ~current->blocked)
 #define NON_BLOCKING(open_mode)	(open_mode & O_NONBLOCK)
 #define ONE_SECOND	HZ	/* in jiffies (100ths of a second) */
 #define NO_TIME_LIMIT	0xffffffff
@@ -668,9 +668,10 @@
 
 
 static int sound_open(struct inode *inode, struct file *file);
-static int sound_fsync(struct file *filp, struct dentry *dentry);
+static int sound_fsync(struct inode *inode, struct file *filp);
 static void sound_release(struct inode *inode, struct file *file);
-static long long sound_lseek(struct file *file, long long offset, int orig);
+static long long sound_lseek(struct inode *inode, struct file *file,
+			     long long offset, int orig);
 static long sound_read(struct inode *inode, struct file *file, char *buf,
 		       unsigned long count);
 static long sound_write(struct inode *inode, struct file *file,
@@ -3070,9 +3071,9 @@
 }
 
 
-static int sound_fsync(struct file *filp, struct dentry *dentry)
+static int sound_fsync(struct inode *inode, struct file *filp)
 {
-    int dev = MINOR(dentry->d_inode->i_rdev) & 0x0f;
+    int dev = MINOR(inode->i_rdev) & 0x0f;
 
     switch (dev) {
 	case SND_DEV_STATUS:
@@ -3115,7 +3116,8 @@
 }
 
 
-static long long sound_lseek(struct file *file, long long offset, int orig)
+static long long sound_lseek(struct inode *inode, struct file *file,
+			     long long offset, int orig)
 {
     return -ESPIPE;
 }
@@ -3184,25 +3186,25 @@
 		    return(0);
 		case SNDCTL_DSP_POST:
 		case SNDCTL_DSP_SYNC:
-		    return(sound_fsync(file, file->f_dentry));
+		    return(sound_fsync(inode, file));
 
 		/* ++TeSche: before changing any of these it's probably wise to
 		 * wait until sound playing has settled down
 		 */
 		case SNDCTL_DSP_SPEED:
-		    sound_fsync(file, file->f_dentry);
+		    sound_fsync(inode, file);
 		    IOCTL_IN(arg, data);
 		    return(IOCTL_OUT(arg, sound_set_speed(data)));
 		case SNDCTL_DSP_STEREO:
-		    sound_fsync(file, file->f_dentry);
+		    sound_fsync(inode, file);
 		    IOCTL_IN(arg, data);
 		    return(IOCTL_OUT(arg, sound_set_stereo(data)));
 		case SOUND_PCM_WRITE_CHANNELS:
-		    sound_fsync(file, file->f_dentry);
+		    sound_fsync(inode, file);
 		    IOCTL_IN(arg, data);
 		    return(IOCTL_OUT(arg, sound_set_stereo(data-1)+1));
 		case SNDCTL_DSP_SETFMT:
-		    sound_fsync(file, file->f_dentry);
+		    sound_fsync(inode, file);
 		    IOCTL_IN(arg, data);
 		    return(IOCTL_OUT(arg, sound_set_format(data)));
 		case SNDCTL_DSP_GETFMTS:
diff -u --recursive --new-file v2.1.62/linux/drivers/sound/gus_card.c linux/drivers/sound/gus_card.c
--- v2.1.62/linux/drivers/sound/gus_card.c	Wed May 28 10:51:32 1997
+++ linux/drivers/sound/gus_card.c	Tue Sep 30 08:46:51 1997
@@ -15,13 +15,13 @@
 
 #include "sound_config.h"
 
-#if defined(CONFIG_GUSHW)
+#ifdef CONFIG_GUSHW
 
 #include "gus_hw.h"
 
 void            gusintr (int irq, void *dev_id, struct pt_regs *dummy);
 
-int             gus_base, gus_irq, gus_dma;
+int             gus_base = 0, gus_irq = 0, gus_dma = 0;
 extern int      gus_wave_volume;
 extern int      gus_pcm_volume;
 extern int      have_gus_max;
@@ -171,7 +171,7 @@
 /*
  * Some extra code for the 16 bit sampling option
  */
-#if defined(CONFIG_GUS16)
+#ifdef CONFIG_GUS16
 
 int
 probe_gus_db16 (struct address_info *hw_config)
diff -u --recursive --new-file v2.1.62/linux/drivers/sound/gus_midi.c linux/drivers/sound/gus_midi.c
--- v2.1.62/linux/drivers/sound/gus_midi.c	Wed May 28 10:51:32 1997
+++ linux/drivers/sound/gus_midi.c	Tue Sep 30 08:46:51 1997
@@ -17,7 +17,8 @@
 
 #include "gus_hw.h"
 
-#if defined(CONFIG_GUSHW) && defined(CONFIG_MIDI)
+#ifdef CONFIG_GUSHW
+#ifdef CONFIG_MIDI
 
 static int      midi_busy = 0, input_opened = 0;
 static int      my_dev;
@@ -295,4 +296,5 @@
   restore_flags (flags);
 }
 
+#endif
 #endif
diff -u --recursive --new-file v2.1.62/linux/drivers/sound/gus_wave.c linux/drivers/sound/gus_wave.c
--- v2.1.62/linux/drivers/sound/gus_wave.c	Wed May 28 10:51:32 1997
+++ linux/drivers/sound/gus_wave.c	Tue Sep 30 08:46:52 1997
@@ -19,7 +19,7 @@
 #include <linux/ultrasound.h>
 #include "gus_hw.h"
 
-#if defined(CONFIG_GUSHW)
+#ifdef CONFIG_GUSHW
 
 #define GUS_BANK_SIZE (((iw_mode) ? 256*1024*1024 : 256*1024))
 
@@ -73,7 +73,6 @@
 static int      gus_dma2 = -1;
 static int      dual_dma_mode = 0;
 static long     gus_mem_size = 0;
-static long     gus_rom_size = 0;
 static long     free_mem_ptr = 0;
 static int      gus_busy = 0;
 static int      gus_no_dma = 0;
@@ -681,6 +680,7 @@
   if (voices[voice].mode & WAVE_ENVELOPES)
     {
       start_release (voice, flags);
+      restore_flags (flags);
       return;
     }
 
@@ -692,6 +692,7 @@
       gus_voice_off ();
       gus_rampoff ();
       gus_voice_init (voice);
+      restore_flags (flags);
       return;
     }
 
@@ -1026,9 +1027,7 @@
 
   for (bank = 0; bank < 4; bank++)
     {
-      DDB (printk ("  Bank %d, mem=%dk (limit %dk)\n",
-		   bank, bank_sizes[bank] / 1024,
-		   mem_decode[bits][bank] / 1024));
+      DDB (printk ("  Bank %d, mem=%dk (limit %dk)\n", bank, bank_sizes[bank] / 1024, mem_decode[bits][bank] / 1024));
 
       if (bank_sizes[bank] > mem_decode[bits][bank])
 	total += mem_decode[bits][bank];
@@ -1180,8 +1179,7 @@
 
   if (sample_ptrs[sample_no] == -1)	/* Sample not loaded */
     {
-      printk ("GUS: Sample #%d not loaded for patch %d (voice %d)\n",
-	      sample_no, instr_no, voice);
+      printk ("GUS: Sample #%d not loaded for patch %d (voice %d)\n", sample_no, instr_no, voice);
       return -EINVAL;
     }
 
@@ -1724,7 +1722,7 @@
 
   if ((err = DMAbuf_open_dma (gus_devnum)) < 0)
     {
-      /* printk ("GUS: Loading samples without DMA\n"); */
+      /* printk( "GUS: Loading samples without DMA\n"); */
       gus_no_dma = 1;		/* Upload samples using PIO */
     }
   else
@@ -1808,8 +1806,7 @@
 
   if (count < patch.len)
     {
-      printk ("GUS Warning: Patch record too short (%d<%d)\n",
-	      count, (int) patch.len);
+      printk ("GUS Warning: Patch record too short (%d<%d)\n", count, (int) patch.len);
       patch.len = count;
     }
 
@@ -2335,7 +2332,7 @@
 
   if (gus_pnp_flag && mode & OPEN_READ)
     {
-      printk ("Sound: This audio device doesn't have recording capability\n");
+      printk ("GUS: Audio device #%d is playback only.\n", dev);
       return -EIO;
     }
   gus_initialize ();
@@ -3473,6 +3470,7 @@
 
     default:;
     }
+  restore_flags (flags);
 }
 
 void
diff -u --recursive --new-file v2.1.62/linux/drivers/sound/lowlevel/ChangeLog.awe linux/drivers/sound/lowlevel/ChangeLog.awe
--- v2.1.62/linux/drivers/sound/lowlevel/ChangeLog.awe	Tue Mar  4 10:25:25 1997
+++ linux/drivers/sound/lowlevel/ChangeLog.awe	Tue Oct 28 02:10:48 1997
@@ -1,3 +1,69 @@
+ver.0.4.2
+	- Use indirect voice allocation mode; used as default mode
+	- Add preset mapping
+	- Free buffers when resetting samples
+	- Set default preset/bank/drumset as variable
+	- Fix a bug in exclusive note-off
+	- Add channel reset control macro
+	- Change modwheel sensitivity as variable
+	- Add lock option in open_patch
+	- Add channel priority mode macro, and disable it as default
+	- Add unset effect macro
+	- Add user defined chorus/reverb modes
+
+ver.0.4.1	development versions
+
+ver.0.4.0c
+	- Fix kernel oops when setting AWE_FX_ATTEN
+
+ver.0.4.0b
+	- Do not kill_note in start_note when velocity is zero
+
+ver.0.4.0a
+	- Fix a bug in channel pressure effects
+
+ver.0.4.0
+	- Support dynamic buffer allocation
+	- Add functions to open/close/unload a patch
+	- Change from pointer to integer index in voice/sample lists
+	- Support for Linux/Alpha-AXP
+	- Fix for FreeBSD
+	- Add sostenuto control
+	- Add midi channel priority
+	- Fix a bug in all notes off control
+	- Use AWE_DEFAULT_MEMSIZE always if defined
+	- Fix a bug in awe_reset causes seg fault when no DRAM onboard
+	- Use awe_mem_start variable instead of constant
+
+ver.0.3.3c
+	- Fix IOCTL_TO_USER for OSS-3.8 (on Linux-2.1.25)
+	- Fix i/o macros for mixer controls
+
+ver.0.3.3b
+	- Fix version number in awe_version.h
+	- Fix a small bug in noteoff/relese all
+
+ver.0.3.3a
+	- Fix all notes/sounds off
+	- Add layer effect control
+	- Add misc mode controls; realtime pan, version number, etc.
+	- Move gus bank control in misc mode control
+	- Modify awe_operations for OSS3.8b5
+	- Fix installation script
+
+ver.0.3.3
+	- Add bass/treble control in Emu8000 chip
+	- Add mixer device
+	- Fix sustain on to value 127
+
+ver.0.3.2
+	- Refuse linux-2.0.0 at installation
+	- Move awe_voice.h to /usr/include/linux
+
+ver.0.3.1b (not released)
+	- Rewrite chorus/reverb mode change functions
+	- Rewrite awe_detect & awe_check_dram routines
+
 ver.0.3.1a
 	- Fix a bug to reset voice counter in awe_reset
 	- Fix voice balance on GUS mode
diff -u --recursive --new-file v2.1.62/linux/drivers/sound/lowlevel/Makefile linux/drivers/sound/lowlevel/Makefile
--- v2.1.62/linux/drivers/sound/lowlevel/Makefile	Tue Mar  4 10:25:25 1997
+++ linux/drivers/sound/lowlevel/Makefile	Tue Oct 28 02:10:48 1997
@@ -15,6 +15,13 @@
 endif
 endif
 
+ifndef TOPDIR
+TOPDIR=/usr/src/linux
+endif
+
+.c.o:
+	$(CC) $(CFLAGS) -c $<
+
 lowlevel.o:	$(OBJS)
 	$(LD) -r -o lowlevel.o $(OBJS)
 
@@ -35,10 +42,16 @@
 dep:
 	$(CPP) -M $(CFLAGS) -I. *.c > .depend
 
-ifdef HOSTCC
-include $(TOPDIR)/Rules.make
-else
+ifndef HOSTCC
+#
+#	Running outside the kernel build.
+#
+CC	= gcc
+HOSTCC	= gcc
+CFLAGS	= -O2 -D__KERNEL__ -DMODULE -I/usr/src/linux/include -Wall -Wstrict-prototypes -fomit-frame-pointer -pipe -m486
 USE_DEPEND=y
+else
+include $(TOPDIR)/Rules.make
 endif
 
 ifdef USE_DEPEND
diff -u --recursive --new-file v2.1.62/linux/drivers/sound/lowlevel/README.awe linux/drivers/sound/lowlevel/README.awe
--- v2.1.62/linux/drivers/sound/lowlevel/README.awe	Tue Mar  4 10:25:25 1997
+++ linux/drivers/sound/lowlevel/README.awe	Tue Oct 28 02:10:49 1997
@@ -1,6 +1,6 @@
 ================================================================
 	AWE32 Sound Driver for Linux / FreeBSD
-		version 0.3.1; Jan. 11, 1997
+		version 0.4.2; Sep. 1, 1997
 ================================================================
 
 * GENERAL NOTES
@@ -8,9 +8,9 @@
 This is a sound driver extension for SoundBlaster AWE32 and other
 compatible cards (AWE32-PnP, SB32, SB32-PnP, AWE64 & etc) to enable
 the wave synth operations.  The driver is provided for both Linux
-1.2.x and 2.[01].x kernels, and also FreeBSD.  Consult the
-installation document for installation on the original sound driver
-package.
+1.2.x and 2.[01].x kernels, and also FreeBSD on Intel x86 and DEC
+Alpha systems.   See INSTALL.awe (or INSTALL.fbsd) document for
+installation of the driver package.
 
 This driver was written by Takashi Iwai (iwai@dragon.mm.t.u-tokyo.ac.jp)  
 who also maintains the code.  Please forward any questions, bug fixes
@@ -18,47 +18,80 @@
 Savolainen).
 
 
-* CAUTION
-
-- On ver.0.3.1, some zero size array entries are removed from
-awe_voice.h to avoid compile error in some non-ANSI compilers.
-Due to this fix, the size of awe_voice_rec structure is changed from
-older versions.  Use a constant AWE_VOICE_REC_SIZE instead of
-sizeof(awe_voice_rec).
-You can still have a compatibility by defining AWE_COMPAT_030=1, but
-this feature will be omitted in the future release.
-
-
 * NOTE TO LINUX USERS
 
 To enable this driver on linux-2.[01].x kernels, you need turn on both 
 "lowlevel drivers support" and "AWE32 synth support" options in sound
 menu when configure your linux kernel and modules.  For more details,
 see the installation document in the original driver package
-(awedrv-0.x.x.tar.gz) available at the web page:
+(awedrv-0.4.2.tar.gz) available at the web page:
 	http://bahamut.mm.t.u-tokyo.ac.jp/~iwai/awedrv/
 
 If you're using PnP cards, the card must be initialized before loading
 the sound driver.  There're several options to do this:
     - Initialize the card via ISA PnP tools, and load the sound module.
     - Initialize the card on DOS, and load linux by loadlin.exe
-    - Use PnP driver (for Linux-2.0.x)
+    - Use PnP driver (for Linux-2.x.x)
 See the FAQ list on the URL above.
 
 
+* USING THE DRIVER
+
+The GM and GS sounds include multiple instrument layers.
+The current version supports this type of sounds with a special
+extension, but it uses a non-standard way of sequencer calls.  Then,
+so far, only drvmidi and playmidi can play the multiple instruments
+and stereo sounds properly.
+
+To load SoundFont files, sfxload utility is required.
+All AWE32 driver and utilities can be downloaded from:
+	http://bahamut.mm.t.u-tokyo.ac.jp/~iwai/awedrv/
+
+The sfxload is included in the package awesfx-0.4.2.tgz. Binary
+packages are available there, too.  See the instruction in each
+package for installation.
+
+Sfxload reads a SoundFont file and transfers it to the sound driver.
+Note that new sfxload no longer requires -i option.
+
+	% sfxload synthgm.sbk
+
+You can tune up the sound via some new options, -A, -a and -d.
+
+	% sfxload -A2 synthgm.sbk
+
+See the manual of sfxload for more details.
+
+Now you can hear midi musics by supported midi players (drvmidi or
+playmidi-2.5).
+
+	% drvmidi foo.mid
+
+If you have only 512kb on the sound card, I recommend to use dynamic
+sample loading via -L option of drvmidi.  2MB GM/GS soundfont file is
+available in most midi files.
+
+	% sfxload synthgm
+	% drvmidi -L 2mbgmgs foo.mid
+
+Enjoy.
+
+
 * COMPILE FLAGS
 
 Compile conditions are defined in awe_config.h.
 
+[Compatibility Conditions]
+The following flags are defined automatically when using installation
+shell script.
+
 - AWE_OBSOLETE_VOXWARE		(default: not defined)
     indicates the system is VoxWare-3.0.x (with linux 1.2.x or
-    FreeBSD) if defined.  This option will be set automatically when
-    you use installation script.
+    FreeBSD) if defined.
 
 - AWE_NEW_KERNEL_INTERFACE	(default: not defined)
     indicates the system is OSSLite on Linux 2.1.6 or later if
-    defined.  This option will be set automatically when you use
-    installation script.
+    defined.
 
 - HAS_LOWLEVEL_H		(default: not defined)
     indicates the system has "lowlevel.h" in the sound/lowlevel
@@ -68,27 +101,32 @@
     indicates the sound driver has no patch manager function (for
     OSS-3.707 (in Linux-2.1.13) or newer). 
 
+- AWE_OSS38			(default: not defined)
+    indicates the sound driver has an additional parameter in
+    operation table (for OSS-3.8b5 in Linux-2.1.25 or newer).
+
+
+[Hardware Conditions]
+You don't have to define the following two values.
+Define them only when the driver couldn't detect the card properly.
+
 - AWE_DEFAULT_BASE_ADDR		(default: not defined)
     specifies the base port address of your AWE32 card.
-    Define this only when the driver couldn't detect your card
-    properly. 
 
 - AWE_DEFAULT_MEM_SIZE		(default: not defined)
-    specifies the memory size of your AWE32 card by kilo bytes.
-    Define this only when the driver couldn't detect memory size
-    properly. 
+    specifies the memory size of your AWE32 card in kilo bytes.
     
-- AWE_MAX_SAMPLES		(default: 400)
-    specifies the maximum number of wave samples.
-    The default size is set for the original GM and GS presets from
-    CreativeLab.  If you have a large set of samples (eg 2MB & 8MB GM
-    presets), increase this value to appropriate size. 
-
-- AWE_MAX_INFOS			(default: 900)
-    specifies the maximum number of instruments.
-    The default size is set for the original GM and GS presets from
-    CreativeLab.  If you have a large set of samples (eg 2MB & 8MB GM
-    presets), increase this value to appropriate size. 
+
+[Sample Table Size]
+From ver.0.4.0, sample tables are allocated dynamically (except
+Linux-1.2.x system), so you need NOT to touch these parameters.
+Linux-1.2.x users may need to increase these values to apropriate size 
+if larger DRAM is equipped with the soundcard.
+
+- AWE_MAX_SF_LISTS, AWE_MAX_SAMPLES, AWE_MAX_INFOS
+
+
+[Other Conditions]
 
 - AWE_ALWAYS_INIT_FM		(default: not defined)
     indicates the AWE driver always initialize FM passthrough even
@@ -99,22 +137,24 @@
 - AWE_DEBUG_ON			(default: defined)
     turns on debuggin messages if defined.
 
-- AWE_CHECKSUM_DATA		(default: defined)
-    verifies check sum of sample data with the transferred data if
-    defined. 
-
-- AWE_CHECKSUM_MEMORY		(default: defined)
-    Verifies check sum of sample data with the written data on DRAM.
-
 - AWE_HAS_GUS_COMPATIBILITY	(default: defined)
     Enables GUS compatibility mode if defined, reading GUS patches and 
     GUS control commands.  Define this option to use GMOD or other
     GUS module players.
 
-- AWE_ACCEPT_ALL_SOUNDS_CONTROL	(default: not defined)
+- AWE_ACCEPT_ALL_SOUNDS_CONTROL	(default: defined)
     Enables MIDI control #120 and #123 as "all notes off" and "all
     sounds off" events, respectively.
 
+- CONFIG_AWE32_MIXER		(default: defined)
+    Adds a mixer device for AWE32 bass/treble equalizer control.
+    You can access this device using /dev/mixer?? (usually mixer01).
+
+- AWE_LOOKUP_MIDI_PRIORIITY	(default: defined)
+    Allocates voices according to MIDI channel priority.
+    Drum channels have the highest priorit, followed by #1, #2, and
+    so on.
+
 - DEF_FM_CHORUS_DEPTH		(default: 0x10)
     The default strength to be sent to the chorus effect engine.
     From 0 to 0xff.  Larger numbers may often cause weird sounds.
@@ -124,43 +164,12 @@
     From 0 to 0xff.  Larger numbers may often cause weird sounds.
 
 
-* USING THE DRIVER
-
-To load SoundFont files, sfxload utility is required.
-All AWE32 driver and utilities can be downloaded from:
-	http://bahamut.mm.t.u-tokyo.ac.jp/~iwai/awedrv/
-
-The GM and GS sounds include multiple instrument layers.  The older
-driver couldn't handle multiple instruments.  The current version
-supports this type of sounds with a special extension, but so far only 
-drvmidi and playmidi can play the multiple instruments and stereo
-sounds.
-
-To play drvmidi, load the SoundFont file directly uing sfxload utility.
-
-	% sfxload -i synthgm.sf2
-
-To use other sequencers like musserver, some sounds may become
-inaudible unless converting to SFX file.  Follow the instruction in
-awesfx package to make patched GM and GS presets.  Then, load the SFX
-file on driver by sfxload utility.
-
-	% sfxload -i gm.sfx
-
-Now you can hear midi musics by supported midi players
-(awemidi-0.1.x.tar.gz or patch file to playmidi-2.3).
-
-	% drvmidi foo.mid
-
-Enjoy.
-
-
 * ACKNOWLEDGMENTS
 
 Thanks to Witold Jachimczyk (witek@xfactor.wpi.edu) for many advices
 to programming of AWE32.  Many codes are brought from his AWE32-native 
 MOD player, ALMP.
-The port of awedrv0.1.6 to FreeBSD is done by Randall Hopper
+The port of awedrv to FreeBSD is done by Randall Hopper
 (rhh@ct.picker.com).
 I also thank linux-awe-ml members for their efforts
 to reboot their system many times :-)
@@ -168,9 +177,9 @@
 
 * BUGS & TODO'S
 
+- Can't detect DRAM size on some card
 - More smart patch management
 - More smart DRAM memory control
-- Dynamic buffer allocation
 - etc, etc, etc.
 
 
diff -u --recursive --new-file v2.1.62/linux/drivers/sound/lowlevel/aedsp16.c linux/drivers/sound/lowlevel/aedsp16.c
--- v2.1.62/linux/drivers/sound/lowlevel/aedsp16.c	Tue Mar  4 10:25:25 1997
+++ linux/drivers/sound/lowlevel/aedsp16.c	Tue Oct 28 02:10:50 1997
@@ -250,7 +250,7 @@
 #undef	AEDSP16_INFO  1		/* Define this to enable info code      */
 
 #if defined(AEDSP16_DEBUG)
-# define DBG(x) printk x
+# define DBG(x)  printk x
 # if defined(AEDSP16_DEBUG_MORE)
 #  define DBG1(x) printk x
 # else
diff -u --recursive --new-file v2.1.62/linux/drivers/sound/lowlevel/awe_compat.h linux/drivers/sound/lowlevel/awe_compat.h
--- v2.1.62/linux/drivers/sound/lowlevel/awe_compat.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/sound/lowlevel/awe_compat.h	Tue Oct 28 02:10:50 1997
@@ -0,0 +1,190 @@
+/*----------------------------------------------------------------
+ * compatibility macros for AWE32 driver
+ *----------------------------------------------------------------*/
+
+/* redefine following macros */
+#undef IOCTL_IN
+#undef IOCTL_OUT
+#undef OUTW
+#undef COPY_FROM_USER
+#undef COPY_TO_USER
+#undef GET_BYTE_FROM_USER
+#undef GET_SHORT_FROM_USER
+#undef IOCTL_TO_USER
+  
+#ifdef linux
+
+/*================================================================
+ * Linux macros
+ *================================================================*/
+
+/* use inline prefix */
+#define INLINE	inline
+
+/*----------------------------------------------------------------
+ * memory management for linux
+ *----------------------------------------------------------------*/
+
+#ifdef AWE_OBSOLETE_VOXWARE
+/* old type linux system */
+
+/* i/o requests; nothing */
+#define awe_check_port()	0	/* always false */
+#define awe_request_region()	/* nothing */
+#define awe_release_region()	/* nothing */
+
+static int _mem_start;  /* memory pointer for permanent buffers */
+
+#define my_malloc_init(memptr)	_mem_start = (memptr)
+#define my_malloc_memptr()	_mem_start
+#define my_free(ptr)	/* do nothing */
+#define my_realloc(buf,oldsize,size)	NULL	/* no realloc */
+
+static void *my_malloc(int size)
+{
+	char *ptr;
+	PERMANENT_MALLOC(ptr, char*, size, _mem_start);
+	return (void*)ptr;
+}
+
+/* allocate buffer only once */
+#define INIT_TABLE(buffer,index,nums,type) {\
+buffer = my_malloc(sizeof(type) * (nums)); index = (nums);\
+}
+
+#else
+
+#define AWE_DYNAMIC_BUFFER
+
+#define my_malloc_init(ptr)	/* nothing */
+#define my_malloc_memptr()	0
+#define my_malloc(size)		vmalloc(size)
+#define my_free(ptr)		if (ptr) {vfree(ptr);}
+
+static void *my_realloc(void *buf, int oldsize, int size)
+{
+	void *ptr;
+	if ((ptr = vmalloc(size)) == NULL)
+		return NULL;
+	memcpy(ptr, buf, ((oldsize < size) ? oldsize : size) );
+	vfree(buf);
+	return ptr;
+}
+
+/* do not allocate buffer at beginning */
+#define INIT_TABLE(buffer,index,nums,type) {buffer=NULL; index=0;}
+
+/* old type macro */
+#define RET_ERROR(err)		-err
+
+#endif
+
+/*----------------------------------------------------------------
+ * i/o interfaces for linux
+ *----------------------------------------------------------------*/
+
+#define OUTW(data,addr)		outw(data, addr)
+
+#ifdef AWE_NEW_KERNEL_INTERFACE
+#define COPY_FROM_USER(target,source,offs,count) \
+	copy_from_user(target, (source)+(offs), count)
+#define GET_BYTE_FROM_USER(target,addr,offs) \
+	get_user(target, (unsigned char*)&((addr)[offs]))
+#define GET_SHORT_FROM_USER(target,addr,offs) \
+	get_user(target, (unsigned short*)&((addr)[offs]))
+#ifdef AWE_OSS38
+#define IOCTL_TO_USER(target,offs,source,count) \
+	memcpy(target, (source)+(offs), count)
+#define IO_WRITE_CHECK(cmd)	(_SIOC_DIR(cmd) & _IOC_WRITE)
+#else
+#define IOCTL_TO_USER(target,offs,source,count) \
+	copy_to_user(target, (source)+(offs), count)
+#define IO_WRITE_CHECK(cmd)	(_IOC_DIR(cmd) & _IOC_WRITE)
+#endif /* AWE_OSS38 */
+#define COPY_TO_USER	IOCTL_TO_USER
+#define IOCTL_IN(arg)	(*(int*)(arg))
+#define IOCTL_OUT(arg,val) (*(int*)(arg) = (val))
+
+#else /* old type i/o */
+#define COPY_FROM_USER(target,source,offs,count) \
+	memcpy_fromfs(target, (source)+(offs), (count))
+#define GET_BYTE_FROM_USER(target,addr,offs) \
+	*((char  *)&(target)) = get_fs_byte((addr)+(offs))
+#define GET_SHORT_FROM_USER(target,addr,offs) \
+	*((short *)&(target)) = get_fs_word((addr)+(offs))
+#define IOCTL_TO_USER(target,offs,source,count) \
+	memcpy_tofs(target, (source)+(offs), (count))
+#define COPY_TO_USER	IOCTL_TO_USER
+#define IO_WRITE_CHECK(cmd)	(cmd & IOC_IN)
+#define IOCTL_IN(arg)		get_fs_long((long *)(arg))
+#define IOCTL_OUT(arg,ret)	snd_ioctl_return((int *)arg, ret)
+
+#endif /* AWE_NEW_KERNEL_INTERFACE */
+
+#define BZERO(target,len)	memset(target, 0, len)
+#define MEMCPY(dst,src,len)	memcpy(dst, src, len)
+
+
+#elif defined(__FreeBSD__)
+
+/*================================================================
+ * FreeBSD macros
+ *================================================================*/
+
+/* inline is not checked yet.. maybe it'll work */
+#define INLINE	/*inline*/
+
+/*----------------------------------------------------------------
+ * memory management for freebsd
+ *----------------------------------------------------------------*/
+
+/* i/o requests; nothing */
+#define awe_check_port()	0	/* always false */
+#define awe_request_region()	/* nothing */
+#define awe_release_region()	/* nothing */
+
+#define AWE_DYNAMIC_BUFFER
+
+#define my_malloc_init(ptr)	/* nothing */
+#define my_malloc_memptr()	0
+#define my_malloc(size)		malloc(size, M_TEMP, M_WAITOK)
+#define my_free(ptr)		if (ptr) {free(ptr, M_TEMP);}
+
+#define INIT_TABLE(buffer,index,nums,type) {buffer=NULL; index=0;}
+
+/* it should be realloc? */
+static void *my_realloc(void *buf, int oldsize, int size)
+{
+	void *ptr;
+	if ((ptr = my_malloc(size)) == NULL)
+		return NULL;
+	memcpy(ptr, buf, ((oldsize < size) ? oldsize : size) );
+	my_free(buf);
+	return ptr;
+}
+
+/*----------------------------------------------------------------
+ * i/o interfaces for freebsd
+ *----------------------------------------------------------------*/
+
+/* according to linux rule; the arguments are swapped */
+#define OUTW(data,addr)		outw(addr, data)
+
+#define COPY_FROM_USER(target,source,offs,count) \
+	uiomove(((caddr_t)(target)),(count),((struct uio *)(source)))
+#define COPY_TO_USER(target,source,offs,count) \
+	uiomove(((caddr_t)(source)),(count),((struct uio *)(target)))
+#define GET_BYTE_FROM_USER(target,addr,offs) \
+	uiomove(((char*)&(target)), 1, ((struct uio *)(addr)))
+#define GET_SHORT_FROM_USER(target,addr,offs) \
+	uiomove(((char*)&(target)), 2, ((struct uio *)(addr)))
+#define IOCTL_TO_USER(target,offs,source,count) \
+	memcpy(&((target)[offs]), (source), (count))
+#define IO_WRITE_CHECK(cmd)	(cmd & IOC_IN)
+#define IOCTL_IN(arg)		(*(int*)(arg))
+#define IOCTL_OUT(arg,val)	(*(int*)(arg) = (val))
+#define BZERO(target,len)	bzero((caddr_t)target, len)
+#define MEMCPY(dst,src,len)	bcopy((caddr_t)src, (caddr_t)dst, len)
+
+#endif
+
diff -u --recursive --new-file v2.1.62/linux/drivers/sound/lowlevel/awe_config.h linux/drivers/sound/lowlevel/awe_config.h
--- v2.1.62/linux/drivers/sound/lowlevel/awe_config.h	Tue Mar  4 10:25:25 1997
+++ linux/drivers/sound/lowlevel/awe_config.h	Tue Oct 28 02:10:50 1997
@@ -2,9 +2,9 @@
  * sound/awe_config.h
  *
  * Configuration of AWE32 sound driver
- *   version 0.2.99e; Dec. 10, 1997
+ *   version 0.4.2; Sep. 1, 1997
  *
- * Copyright (C) 1996,1997 Takashi Iwai
+ * Copyright (C) 1996 Takashi Iwai
  *
  * 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
@@ -33,25 +33,20 @@
  */
 #undef AWE_OBSOLETE_VOXWARE
 
-#ifdef __FreeBSD__
-#  define AWE_OBSOLETE_VOXWARE
-#endif
-
-/* if you're using OSS-Lite on Linux 2.1.6 or later, define the
- * following line.
- */
-#define AWE_NEW_KERNEL_INTERFACE
-
 /* if you have lowlevel.h in the lowlevel directory (OSS-Lite), define
  * the following line.
  */
-#define HAS_LOWLEVEL_H
+#undef HAS_LOWLEVEL_H
 
 /* if your system doesn't support patch manager (OSS 3.7 or newer),
  * define the following line.
  */
 #define AWE_NO_PATCHMGR
  
+/* if your system has an additional parameter (OSS 3.8b5 or newer),
+ * define this.
+ */
+#define AWE_OSS38
 
 /*----------------------------------------------------------------
  * AWE32 card configuration:
@@ -64,13 +59,20 @@
 
 
 /*----------------------------------------------------------------
- * maximum size of sample table:
- * the followings are for ROM GM and 512k GS samples.  if your have
- * additional DRAM and SoundFonts, increase these values.
+ * maximum size of soundfont list table:
+ * you usually don't need to touch this value.
+ *----------------------------------------------------------------*/
+
+#define AWE_MAX_SF_LISTS 16
+
+
+/*----------------------------------------------------------------
+ * chunk size of sample and voice tables:
+ * you usually don't need to touch these values.
  *----------------------------------------------------------------*/
 
 #define AWE_MAX_SAMPLES 400
-#define AWE_MAX_INFOS 1500
+#define AWE_MAX_INFOS 800
 
 
 /*----------------------------------------------------------------
@@ -87,29 +89,22 @@
  *----------------------------------------------------------------*/
 
 /* initialize FM passthrough even without extended RAM */
-#undef AWE_ALWAYS_INIT_FM
+#define AWE_ALWAYS_INIT_FM
 
 /* debug on */
 #define AWE_DEBUG_ON
 
-/* verify checksum for uploading samples */
-#define AWE_CHECKSUM_DATA
-#define AWE_CHECKSUM_MEMORY
-
 /* GUS compatible mode */
 #define AWE_HAS_GUS_COMPATIBILITY
 
 /* accept all notes/sounds off controls */
-#undef AWE_ACCEPT_ALL_SOUNDS_CONTROL
-
+#define AWE_ACCEPT_ALL_SOUNDS_CONTROL
 
-#ifdef linux
-/* i tested this only on my linux */
-#define INLINE  __inline__
-#else
-#define INLINE /**/
-#endif
+/* add mixer control of emu8000 equalizer */
+#define CONFIG_AWE32_MIXER
 
+/* look up voices according to MIDI channel priority */
+#define AWE_LOOKUP_MIDI_PRIORITY
 
 /*----------------------------------------------------------------*/
 
diff -u --recursive --new-file v2.1.62/linux/drivers/sound/lowlevel/awe_hw.h linux/drivers/sound/lowlevel/awe_hw.h
--- v2.1.62/linux/drivers/sound/lowlevel/awe_hw.h	Tue Mar  4 10:25:25 1997
+++ linux/drivers/sound/lowlevel/awe_hw.h	Tue Oct 28 02:10:50 1997
@@ -3,7 +3,7 @@
  *
  * Access routines and definitions for the low level driver for the 
  * AWE32/Sound Blaster 32 wave table synth.
- *   version 0.3.1; Jan. 21, 1997
+ *   version 0.4.2; Sep. 1, 1997
  *
  * Copyright (C) 1996,1997 Takashi Iwai
  *
@@ -89,10 +89,12 @@
 #define AWE_NORMAL_VOICES	30	/*30&31 are reserved for DRAM refresh*/
 
 #define AWE_MAX_CHANNELS	32	/* max midi channels (must >= voices) */
+#define AWE_MAX_LAYERS	AWE_MAX_VOICES	/* maximum number of multiple layers */
 
 #define AWE_DRAM_OFFSET		0x200000
 #define AWE_MAX_DRAM_SIZE	(28 * 1024)	/* 28 MB is max onboard memory */
 
 #define AWE_DEFAULT_ATTENUATION	32	/* 12dB below */
+#define AWE_DEFAULT_MOD_SENSE	18
 
 #endif
diff -u --recursive --new-file v2.1.62/linux/drivers/sound/lowlevel/awe_version.h linux/drivers/sound/lowlevel/awe_version.h
--- v2.1.62/linux/drivers/sound/lowlevel/awe_version.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/sound/lowlevel/awe_version.h	Tue Oct 28 02:10:50 1997
@@ -0,0 +1,12 @@
+/* AWE32 driver version number */
+
+#ifndef AWE_VERSION_H_DEF
+#define AWE_VERSION_H_DEF
+
+#define AWE_VERSION_NUMBER	0x00040200
+#define AWEDRV_VERSION		"0.4.2"
+#define AWE_MAJOR_VERSION(id)	(((id) >> 16) & 0xff)
+#define AWE_MINOR_VERSION(id)	(((id) >> 8) & 0xff)
+#define AWE_TINY_VERSION(id)	((id) & 0xff)
+
+#endif
diff -u --recursive --new-file v2.1.62/linux/drivers/sound/lowlevel/awe_wave.c linux/drivers/sound/lowlevel/awe_wave.c
--- v2.1.62/linux/drivers/sound/lowlevel/awe_wave.c	Tue Mar  4 10:25:25 1997
+++ linux/drivers/sound/lowlevel/awe_wave.c	Tue Oct 28 02:10:53 1997
@@ -2,7 +2,7 @@
  * sound/awe_wave.c
  *
  * The low level driver for the AWE32/Sound Blaster 32 wave table synth.
- *   version 0.3.1b; Jan. 21, 1997
+ *   version 0.4.2; Sep. 1, 1997
  *
  * Copyright (C) 1996,1997 Takashi Iwai
  *
@@ -21,7 +21,6 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
-#define AWEDRV_VERSION		"0.3.1b"
 #ifdef __FreeBSD__
 #  include <i386/isa/sound/awe_config.h>
 #else
@@ -34,12 +33,16 @@
 
 #ifdef __FreeBSD__
 #  include <i386/isa/sound/awe_hw.h>
+#  include <i386/isa/sound/awe_version.h>
 #  include <i386/isa/sound/awe_voice.h>
 #else
 #  include "awe_hw.h"
+#  include "awe_version.h"
 #  include <linux/awe_voice.h>
 #endif
 
+#ifdef AWE_HAS_GUS_COMPATIBILITY
+/* include finetune table */
 #ifdef AWE_OBSOLETE_VOXWARE
 #  ifdef __FreeBSD__
 #    define SEQUENCER_C
@@ -57,6 +60,8 @@
 #  include <machine/ultrasound.h>
 #endif
 
+#endif /* AWE_HAS_GUS_COMPATIBILITY */
+
 
 /*----------------------------------------------------------------
  * debug message
@@ -77,33 +82,71 @@
  * bank and voice record
  *----------------------------------------------------------------*/
 
+/* soundfont record */
+typedef struct _sf_list {
+	unsigned short sf_id;
+	unsigned short type;
+	int num_info;		/* current info table index */
+	int num_sample;		/* current sample table index */
+	int mem_ptr;		/* current word byte pointer */
+	int infos;
+	int samples;
+	/*char name[AWE_PATCH_NAME_LEN];*/
+} sf_list;
+
 /* bank record */
 typedef struct _awe_voice_list {
+	int next;	/* index list */
 	unsigned char bank, instr;
+	char type, disabled;
 	awe_voice_info v;
-	struct _awe_voice_list *next_instr;
-	struct _awe_voice_list *next_bank;
+	int next_instr;	/* preset table list */
+	int next_bank;	/* preset table list */
 } awe_voice_list;
 
+#define V_ST_NORMAL	0
+#define V_ST_MAPPED	1
+
+typedef struct _awe_sample_list {
+	int next;	/* sf list */
+	awe_sample_info v;
+} awe_sample_list;
+
 /* sample and information table */
-static awe_sample_info *samples;
-static awe_voice_list *infos;
+static int current_sf_id = 0;
+static int locked_sf_id = 0;
+static int max_sfs;
+static sf_list *sflists = NULL;
+
+#define awe_free_mem_ptr() (current_sf_id <= 0 ? 0 : sflists[current_sf_id-1].mem_ptr)
+#define awe_free_info() (current_sf_id <= 0 ? 0 : sflists[current_sf_id-1].num_info)
+#define awe_free_sample() (current_sf_id <= 0 ? 0 : sflists[current_sf_id-1].num_sample)
+
+static int max_samples;
+static awe_sample_list *samples = NULL;
+
+static int max_infos;
+static awe_voice_list *infos = NULL;
+
 
 #define AWE_MAX_PRESETS		256
+#define AWE_DEFAULT_PRESET	0
 #define AWE_DEFAULT_BANK	0
+#define AWE_DEFAULT_DRUM	0
 #define AWE_DRUM_BANK		128
 
+#define MAX_LAYERS	AWE_MAX_VOICES
+
 /* preset table index */
-static awe_voice_list *preset_table[AWE_MAX_PRESETS];
+static int preset_table[AWE_MAX_PRESETS];
 
 /*----------------------------------------------------------------
  * voice table
  *----------------------------------------------------------------*/
 
 /* effects table */
-#define AWE_FX_NBYTES	((AWE_FX_END+7)/8)
 typedef	struct FX_Rec { /* channel effects */
-	unsigned char flags[AWE_FX_NBYTES];
+	unsigned char flags[AWE_FX_END];
 	short val[AWE_FX_END];
 } FX_Rec;
 
@@ -117,28 +160,35 @@
 	int panning;		/* panning (0-127) */
 	int main_vol;		/* channel volume (0-127) */
 	int expression_vol;	/* midi expression (0-127) */
-	awe_voice_list *vrec;	/* instrument list */
-	awe_voice_list *def_vrec; /* default instrument list */
+	int chan_press;		/* channel pressure */
+	int vrec;		/* instrument list */
+	int def_vrec;		/* default instrument list */
 	FX_Rec fx;		/* effects */
+	FX_Rec fx_layer[MAX_LAYERS]; /* layer effects */
 	int sustained;		/* sustain status in MIDI */
 } awe_chan_info;
 
 /* voice parameters */
 typedef struct _voice_info {
 	int state;
-#define AWE_ST_OFF		0	/* no sound */
-#define AWE_ST_ON		1	/* playing */
-#define AWE_ST_STANDBY		2	/* stand by for playing */
-#define AWE_ST_SUSTAINED	3	/* sustained */
-#define AWE_ST_MARK		4	/* marked for allocation */
+#define AWE_ST_OFF		(1<<0)	/* no sound */
+#define AWE_ST_ON		(1<<1)	/* playing */
+#define AWE_ST_STANDBY		(1<<2)	/* stand by for playing */
+#define AWE_ST_SUSTAINED	(1<<3)	/* sustained */
+#define AWE_ST_MARK		(1<<4)	/* marked for allocation */
+#define AWE_ST_DRAM		(1<<5)	/* DRAM read/write */
+#define AWE_ST_FM		(1<<6)	/* reserved for FM */
+#define AWE_ST_RELEASED		(1<<7)	/* released */
 
 	int ch;			/* midi channel */
 	int key;		/* internal key for search */
+	int layer;		/* layer number (for channel mode only) */
 	int time;		/* allocated time */
 	awe_chan_info	*cinfo;	/* channel info */
 
 	int note;		/* midi key (0-127) */
 	int velocity;		/* midi velocity (0-127) */
+	int sostenuto;		/* sostenuto on/off */
 	awe_voice_info *sample;	/* assigned voice */
 
 	/* EMU8000 parameters */
@@ -148,58 +198,80 @@
 } voice_info;
 
 /* voice information */
-static voice_info voices[AWE_MAX_VOICES];
+static voice_info *voices;
 
-#define IS_NO_SOUND(v)	(voices[v].state == AWE_ST_OFF || voices[v].state == AWE_ST_STANDBY)
+#define IS_NO_SOUND(v)	(voices[v].state & (AWE_ST_OFF|AWE_ST_RELEASED|AWE_ST_STANDBY|AWE_ST_SUSTAINED))
 #define IS_NO_EFFECT(v)	(voices[v].state != AWE_ST_ON)
-#define IS_PLAYING(v)	(!IS_NO_SOUND(v))
+#define IS_PLAYING(v)	(voices[v].state & (AWE_ST_ON|AWE_ST_SUSTAINED|AWE_ST_RELEASED))
 
 
 /* MIDI channel effects information (for hw control) */
-#if AWE_MAX_CHANNELS < AWE_MAX_VOICES
-static awe_chan_info channels[AWE_MAX_VOICES];
-#else
-static awe_chan_info channels[AWE_MAX_CHANNELS];
-#endif
+static awe_chan_info *channels;
 
 
 /*----------------------------------------------------------------
  * global variables
  *----------------------------------------------------------------*/
 
+#ifndef AWE_DEFAULT_BASE_ADDR
+#define AWE_DEFAULT_BASE_ADDR	0	/* autodetect */
+#endif
+
+#ifndef AWE_DEFAULT_MEM_SIZE
+#define AWE_DEFAULT_MEM_SIZE	0	/* autodetect */
+#endif
+
 /* awe32 base address (overwritten at initialization) */
-static int awe_base = 0;
-/* memory byte size (overwritten at initialization) */
-static long awe_mem_size = 0;
+static int awe_base = AWE_DEFAULT_BASE_ADDR;
+/* memory byte size */
+static int awe_mem_size = AWE_DEFAULT_MEM_SIZE;
+/* DRAM start offset */
+static int awe_mem_start = AWE_DRAM_OFFSET;
 
 /* maximum channels for playing */
 static int awe_max_voices = AWE_MAX_VOICES;
 
-static long free_mem_ptr = 0;		/* free word byte size */
-static int free_info = 0;		/* free info tables */
-static int last_info = 0;		/* last loaded info index */
-static int free_sample = 0;		/* free sample tables */
-static int last_sample = 0;		/* last loaded sample index */
-static int loaded_once = 0;		/* samples are loaded after init? */
-static unsigned short current_sf_id = 0;	/* internal id */
-
-static int reverb_mode = 0;		/* reverb mode */
-static int chorus_mode = 0;		/* chorus mode */
-static unsigned short init_atten = AWE_DEFAULT_ATTENUATION; /* 12dB below */
+static int patch_opened = 0;		/* sample already loaded? */
+
+static int reverb_mode = 3;		/* reverb mode */
+static int chorus_mode = 5;		/* chorus mode */
+static short init_atten = AWE_DEFAULT_ATTENUATION; /* 12dB below */
 
 static int awe_present = FALSE;		/* awe device present? */
 static int awe_busy = FALSE;		/* awe device opened? */
 
-#define DEFAULT_DRUM_FLAGS	(1 << 9)
+#define DEFAULT_DRUM_FLAGS	((1 << 9) | (1 << 25))
 #define IS_DRUM_CHANNEL(c)	(drum_flags & (1 << (c)))
-static unsigned long drum_flags = DEFAULT_DRUM_FLAGS; /* channel flags */
+static unsigned int drum_flags = DEFAULT_DRUM_FLAGS; /* channel flags */
 
-static int awe_channel_mode = 0;		/* channel control mode */
+static int playing_mode = AWE_PLAY_INDIRECT;
+#define SINGLE_LAYER_MODE()	(playing_mode == AWE_PLAY_INDIRECT || playing_mode == AWE_PLAY_DIRECT)
+#define MULTI_LAYER_MODE()	(playing_mode == AWE_PLAY_MULTI || playing_mode == AWE_PLAY_MULTI2)
+
+static int current_alloc_time = 0;	/* voice allocation index for channel mode */
+
+static struct MiscModeDef {
+	int value;
+	int init_each_time;
+} misc_modes_default[AWE_MD_END] = {
+	{0,0}, {0,0}, /* <-- not used */
+	{AWE_VERSION_NUMBER, FALSE},
+	{TRUE, TRUE}, /* exclusive */
+	{TRUE, TRUE}, /* realpan */
+	{AWE_DEFAULT_BANK, TRUE}, /* gusbank */
+	{FALSE, TRUE}, /* keep effect */
+	{AWE_DEFAULT_ATTENUATION, FALSE}, /* zero_atten */
+	{FALSE, TRUE}, /* chn_prior */
+	{AWE_DEFAULT_MOD_SENSE, TRUE}, /* modwheel sense */
+	{AWE_DEFAULT_PRESET, TRUE}, /* def_preset */
+	{AWE_DEFAULT_BANK, TRUE}, /* def_bank */
+	{AWE_DEFAULT_DRUM, TRUE}, /* def_drum */
+};
 
-static int current_alloc_time = 0;		/* voice allocation time */
+static int misc_modes[AWE_MD_END];
 
-static int awe_gus_bank = AWE_DEFAULT_BANK;	/* GUS default bank number */
-static int awe_exclusive_sound = TRUE;		/* exclusive sound on */
+static int awe_bass_level = 5;
+static int awe_treble_level = 9;
 
 
 static struct synth_info awe_info = {
@@ -230,15 +302,16 @@
 static void awe_reset_samples(void);
 /* emu8000 chip i/o access */
 static void awe_poke(unsigned short cmd, unsigned short port, unsigned short data);
-static void awe_poke_dw(unsigned short cmd, unsigned short port, unsigned long data);
+static void awe_poke_dw(unsigned short cmd, unsigned short port, unsigned int data);
 static unsigned short awe_peek(unsigned short cmd, unsigned short port);
-static unsigned long awe_peek_dw(unsigned short cmd, unsigned short port);
+static unsigned int awe_peek_dw(unsigned short cmd, unsigned short port);
 static void awe_wait(unsigned short delay);
 
 /* initialize emu8000 chip */
 static void awe_initialize(void);
 
 /* set voice parameters */
+static void awe_init_misc_modes(int init_all);
 static void awe_init_voice_info(awe_voice_info *vp);
 static void awe_init_voice_parm(awe_voice_parm *pp);
 #ifdef AWE_HAS_GUS_COMPATIBILITY
@@ -255,7 +328,8 @@
 static void awe_note_on(int voice);
 static void awe_note_off(int voice);
 static void awe_terminate(int voice);
-static void awe_exclusive_off(int voice, int exclass);
+static void awe_exclusive_off(int voice);
+static void awe_note_off_all(int do_sustain);
 
 /* calculate voice parameters */
 typedef void (*fx_affect_func)(int voice, int forced);
@@ -267,13 +341,12 @@
 static void awe_fx_fmmod(int voice, int forced);
 static void awe_fx_tremfrq(int voice, int forced);
 static void awe_fx_fm2frq2(int voice, int forced);
-static void awe_fx_cutoff(int voice, int forced);
 static void awe_calc_pitch(int voice);
 #ifdef AWE_HAS_GUS_COMPATIBILITY
 static void awe_calc_pitch_from_freq(int voice, int freq);
 #endif
 static void awe_calc_volume(int voice);
-static void awe_voice_init(int voice);
+static void awe_voice_init(int voice, int keep_ch_info);
 
 /* sequencer interface */
 static int awe_open(int dev, int mode);
@@ -304,25 +377,35 @@
 #endif
 static void awe_hw_awe_control(int dev, int cmd, unsigned char *event);
 static void awe_voice_change(int voice, fx_affect_func func);
+static void awe_sostenuto_on(int voice, int forced);
 static void awe_sustain_off(int voice, int forced);
 
 /* voice search */
-static awe_voice_list *awe_search_instr(int bank, int preset);
-static int awe_search_multi_voices(awe_voice_list *rec, int note, int velocity, awe_voice_info **vlist);
-static void awe_alloc_multi_voices(int ch, int note, int velocity);
+static int awe_search_instr(int bank, int preset);
+static int awe_search_multi_voices(int rec, int note, int velocity, awe_voice_info **vlist);
+static void awe_alloc_multi_voices(int ch, int note, int velocity, int key);
 static void awe_alloc_one_voice(int voice, int note, int velocity);
 static int awe_clear_voice(void);
 
 /* load / remove patches */
-static void awe_check_loaded(void);
-static int awe_load_info(awe_patch_info *patch, const char *addr);
-static int awe_load_data(awe_patch_info *patch, const char *addr);
+static int awe_open_patch(awe_patch_info *patch, const char *addr, int count);
+static int awe_close_patch(awe_patch_info *patch, const char *addr, int count);
+static int awe_unload_patch(awe_patch_info *patch, const char *addr, int count);
+static int awe_load_info(awe_patch_info *patch, const char *addr, int count);
+static int awe_load_data(awe_patch_info *patch, const char *addr, int count);
+static int awe_replace_data(awe_patch_info *patch, const char *addr, int count);
+static int awe_load_map(awe_patch_info *patch, const char *addr, int count);
 #ifdef AWE_HAS_GUS_COMPATIBILITY
 static int awe_load_guspatch(const char *addr, int offs, int size, int pmgr_flag);
 #endif
-static int awe_write_wave_data(const char *addr, long offset, int size);
-static awe_voice_list *awe_get_removed_list(awe_voice_list *curp);
-static void awe_remove_samples(void);
+static int check_patch_opened(int type, char *name);
+static int awe_write_wave_data(const char *addr, int offset, awe_sample_info *sp, int channels);
+static void add_sf_info(int rec);
+static void add_sf_sample(int rec);
+static void purge_old_list(int rec, int next);
+static void add_info_list(int rec);
+static void awe_remove_samples(int sf_id);
+static void rebuild_preset_list(void);
 static short awe_set_sample(awe_voice_info *vp);
 
 /* lowlevel functions */
@@ -333,89 +416,28 @@
 static void awe_tweak_voice(int voice);
 static void awe_tweak(void);
 static void awe_init_fm(void);
-static int awe_open_dram_for_write(int offset);
-static int awe_open_dram_for_read(int offset);
+static int awe_open_dram_for_write(int offset, int channels);
 static void awe_open_dram_for_check(void);
 static void awe_close_dram(void);
-static void awe_close_dram_for_read(void);
 static void awe_write_dram(unsigned short c);
 static int awe_detect_base(int addr);
 static int awe_detect(void);
 static int awe_check_dram(void);
+static int awe_load_chorus_fx(awe_patch_info *patch, const char *addr, int count);
 static void awe_set_chorus_mode(int mode);
+static int awe_load_reverb_fx(awe_patch_info *patch, const char *addr, int count);
 static void awe_set_reverb_mode(int mode);
-
-#ifdef AWE_OBSOLETE_VOXWARE
-
-#define awe_check_port()	0	/* always false */
-#define awe_request_region()	/* nothing */
-#define awe_release_region()	/* nothing */
-
-#else /* AWE_OBSOLETE_VOXWARE */
-
-/* the following macros are osbolete */
-
-#define PERMANENT_MALLOC(type,var,size,memptr) \
-	var = (type)(sound_mem_blocks[sound_nblocks++] = vmalloc(size))
-#define RET_ERROR(err)			-err
-
-#endif /* AWE_OBSOLETE_VOXWARE */
-
-
-/* macros for Linux and FreeBSD compatibility */
-
-#undef OUTW
-#undef COPY_FROM_USER
-#undef GET_BYTE_FROM_USER
-#undef GET_SHORT_FROM_USER
-#undef IOCTL_TO_USER
-  
-#ifdef linux
-#  define NO_DATA_ERR                 ENODATA
-#  define OUTW(data, addr)            outw(data, addr)
-
-#ifdef AWE_NEW_KERNEL_INTERFACE
-#  define COPY_FROM_USER(target, source, offs, count) \
-              copy_from_user( ((caddr_t)(target)),(source)+(offs),(count) )
-#  define GET_BYTE_FROM_USER(target, addr, offs)      \
-		get_user(target, (unsigned char*)&((addr)[offs]))
-#  define GET_SHORT_FROM_USER(target, addr, offs)     \
-		get_user(target, (unsigned short*)&((addr)[offs]))
-#  define IOCTL_TO_USER(target, offs, source, count)  \
-              copy_to_user  ( ((caddr_t)(target)),(source)+(offs),(count) )
-#else /* AWE_NEW_KERNEL_INTERFACE */
-#  define COPY_FROM_USER(target, source, offs, count) \
-              memcpy_fromfs( ((caddr_t)(target)),(source)+(offs),(count) )
-#  define GET_BYTE_FROM_USER(target, addr, offs)      \
-              *((char  *)&(target)) = get_fs_byte( (addr)+(offs) )
-#  define GET_SHORT_FROM_USER(target, addr, offs)     \
-              *((short *)&(target)) = get_fs_word( (addr)+(offs) )
-#  define IOCTL_TO_USER(target, offs, source, count)  \
-              memcpy_tofs  ( ((caddr_t)(target)),(source)+(offs),(count) )
-#endif /* AWE_NEW_KERNEL_INTERFACE */
-
-#  define BZERO(target,len)                           \
-              memset( (caddr_t)target, '\0', len )
-#  define MEMCPY(dst,src,len) \
-              memcpy((caddr_t)dst, (caddr_t)src, len)
-
-#elif defined(__FreeBSD__)
-#  define NO_DATA_ERR                 EINVAL
-#  define OUTW(data, addr)            outw(addr, data)
-#  define COPY_FROM_USER(target, source, offs, count) \
-              uiomove( ((caddr_t)(target)),(count),((struct uio *)(source)) )
-#  define GET_BYTE_FROM_USER(target, addr, offs)      \
-              uiomove( ((char*)&(target)), 1, ((struct uio *)(addr)) )
-#  define GET_SHORT_FROM_USER(target, addr, offs)     \
-              uiomove( ((char*)&(target)), 2, ((struct uio *)(addr)) )
-#  define IOCTL_TO_USER(target, offs, source, count)  \
-              memcpy( &((target)[offs]), (source), (count) )
-#  define BZERO(target,len)                           \
-              bzero( (caddr_t)target, len )
-#  define MEMCPY(dst,src,len) \
-              bcopy((caddr_t)src, (caddr_t)dst, len)
+static void awe_equalizer(int bass, int treble);
+#ifdef CONFIG_AWE32_MIXER
+static int awe_mixer_ioctl(int dev, unsigned int cmd, caddr_t arg);
 #endif
 
+/* define macros for compatibility */
+#ifdef __FreeBSD__
+#  include <i386/isa/sound/awe_compat.h>
+#else
+#  include "awe_compat.h"
+#endif
 
 /*----------------------------------------------------------------
  * synth operation table
@@ -423,7 +445,9 @@
 
 static struct synth_operations awe_operations =
 {
-	"EMU8K",
+#ifdef AWE_OSS38
+	"Emu8000",
+#endif
 	&awe_info,
 	0,
 	SYNTH_TYPE_SAMPLE,
@@ -449,6 +473,15 @@
 	awe_setup_voice
 };
 
+#ifdef CONFIG_AWE32_MIXER
+static struct mixer_operations awe_mixer_operations = {
+#ifndef __FreeBSD__
+	"AWE32",
+#endif
+	"AWE32 Equalizer",
+	awe_mixer_ioctl,
+};
+#endif
 
 
 /*================================================================
@@ -456,10 +489,13 @@
  *================================================================*/
 
 #ifdef AWE_OBSOLETE_VOXWARE
-long attach_awe_obsolete(long mem_start, struct address_info *hw_config)
+#define ATTACH_DECL	static
 #else
-int attach_awe(void)
+#define ATTACH_DECL	/**/
 #endif
+
+ATTACH_DECL
+int attach_awe(void)
 {
 	/* check presence of AWE32 card */
 	if (! awe_detect()) {
@@ -473,16 +509,29 @@
 		return 0;
 	}
 
-	/* allocate sample tables */
-	PERMANENT_MALLOC(awe_sample_info *, samples,
-			 AWE_MAX_SAMPLES * AWE_SAMPLE_INFO_SIZE, mem_start);
-	PERMANENT_MALLOC(awe_voice_list *, infos,
-			 AWE_MAX_INFOS * sizeof(awe_voice_list), mem_start);
-	if (samples == NULL || infos == NULL) {
+	/* set buffers to NULL */
+	voices = NULL;
+	channels = NULL;
+	sflists = NULL;
+	samples = NULL;
+	infos = NULL;
+
+	/* voice & channel info */
+	voices = (voice_info*)my_malloc(AWE_MAX_VOICES * sizeof(voice_info));
+	channels = (awe_chan_info*)my_malloc(AWE_MAX_CHANNELS * sizeof(awe_chan_info));
+
+	if (voices == NULL || channels == NULL) {
+		my_free(voices);
+		my_free(channels);
 		printk("AWE32: can't allocate sample tables\n");
 		return 0;
 	}
 
+	/* allocate sample tables */
+	INIT_TABLE(sflists, max_sfs, AWE_MAX_SF_LISTS, sf_list);
+	INIT_TABLE(samples, max_samples, AWE_MAX_SAMPLES, awe_sample_list);
+	INIT_TABLE(infos, max_infos, AWE_MAX_INFOS, awe_voice_list);
+
 	if (num_synths >= MAX_SYNTH_DEV)
 		printk("AWE32 Error: too many synthesizers\n");
 	else {
@@ -491,6 +540,12 @@
 		synth_devs[num_synths++] = &awe_operations;
 	}
 
+#ifdef CONFIG_AWE32_MIXER
+	if (num_mixers < MAX_MIXER_DEV) {
+		mixer_devs[num_mixers++] = &awe_mixer_operations;
+	}
+#endif
+
 	/* reserve I/O ports for awedrv */
 	awe_request_region();
 
@@ -500,15 +555,16 @@
 	/* intialize AWE32 hardware */
 	awe_initialize();
 
-#ifndef __FreeBSD__
-	printk("AWE32 Sound Driver v%s (DRAM %dk)\n",
-	       AWEDRV_VERSION, (int)awe_mem_size/1024);
-#else
-	DEBUG(0,printk("AWE32 Sound Driver v%s (DRAM %dk)\n",
-                   AWEDRV_VERSION, (int)awe_mem_size/1024));
+	sprintf(awe_info.name, "AWE32-%s (RAM%dk)",
+		AWEDRV_VERSION, awe_mem_size/1024);
+#ifdef __FreeBSD__
+	printk("awe0: <SoundBlaster EMU8000 MIDI (RAM%dk)>", awe_mem_size/1024);
+#elif defined(AWE_DEBUG_ON)
+	printk("%s\n", awe_info.name);
 #endif
-	sprintf(awe_info.name, "AWE32 Driver v%s (DRAM %dk)",
-		AWEDRV_VERSION, (int)awe_mem_size/1024);
+
+	/* set default values */
+	awe_init_misc_modes(TRUE);
 
 	/* set reverb & chorus modes */
 	awe_set_reverb_mode(reverb_mode);
@@ -516,24 +572,57 @@
 
 	awe_present = TRUE;
 
-#ifdef AWE_OBSOLETE_VOXWARE
-	return mem_start;
-#else
 	return 1;
-#endif
 }
 
 
+#ifdef AWE_DYNAMIC_BUFFER
+static void free_tables(void)
+{
+	my_free(sflists);
+	sflists = NULL; max_sfs = 0;
+	my_free(samples);
+	samples = NULL; max_samples = 0;
+	my_free(infos);
+	infos = NULL; max_infos = 0;
+}
+#else
+#define free_buffers() /**/
+#endif
+
+
+ATTACH_DECL
 void unload_awe(void)
 {
 	if (awe_present) {
 		awe_reset_samples();
 		awe_release_region();
+		my_free(voices);
+		my_free(channels);
+		free_tables();
+		awe_present = FALSE;
 	}
 }
 
 
+/*----------------------------------------------------------------
+ * old type interface
+ *----------------------------------------------------------------*/
+
 #ifdef AWE_OBSOLETE_VOXWARE
+
+#ifdef __FreeBSD__
+long attach_awe_obsolete(long mem_start, struct address_info *hw_config)
+#else
+int attach_awe_obsolete(int mem_start, struct address_info *hw_config)
+#endif
+{
+	my_malloc_init(mem_start);
+	if (! attach_awe())
+		return 0;
+	return my_malloc_memptr();
+}
+
 int probe_awe_obsolete(struct address_info *hw_config)
 {
 	return 1;
@@ -542,6 +631,7 @@
 
 #endif
 
+
 /*================================================================
  * clear sample tables 
  *================================================================*/
@@ -552,15 +642,14 @@
 	int i;
 
 	/* free all bank tables */
-	for (i = 0; i < AWE_MAX_PRESETS; i++) {
-		preset_table[i] = NULL;
-	}
+	for (i = 0; i < AWE_MAX_PRESETS; i++)
+		preset_table[i] = -1;
+
+	free_tables();
 
-	free_mem_ptr = 0;
-	last_sample = free_sample = 0;
-	last_info = free_info = 0;
 	current_sf_id = 0;
-	loaded_once = 0;
+	locked_sf_id = 0;
+	patch_opened = 0;
 }
 
 
@@ -584,7 +673,7 @@
 
 /* write 32bit data */
 INLINE static void
-awe_poke_dw(unsigned short cmd, unsigned short port, unsigned long data)
+awe_poke_dw(unsigned short cmd, unsigned short port, unsigned int data)
 {
 	awe_set_cmd(cmd);
 	OUTW(data, awe_port(port));		/* write lower 16 bits */
@@ -602,10 +691,10 @@
 }
 
 /* read 32bit data */
-INLINE static unsigned long
+INLINE static unsigned int
 awe_peek_dw(unsigned short cmd, unsigned short port)
 {
-	unsigned long k1, k2;
+	unsigned int k1, k2;
 	awe_set_cmd(cmd);
 	k1 = inw(awe_port(port));
 	k2 = inw(awe_port(port)+2);
@@ -636,6 +725,13 @@
 			break;
 }
 
+/* write a word data */
+INLINE static void
+awe_write_dram(unsigned short c)
+{
+	awe_poke(AWE_SMLD, c);
+}
+
 
 #ifndef AWE_OBSOLETE_VOXWARE
 
@@ -706,6 +802,9 @@
 
 	/* enable audio */
 	awe_poke(AWE_HWCF3, 0x0004);
+
+	/* set equalizer */
+	awe_equalizer(5, 9);
 }
 
 
@@ -717,7 +816,7 @@
 static void
 awe_init_voice_info(awe_voice_info *vp)
 {
-	vp->sf_id = 0;
+	vp->sf_id = 0; /* normal mode */
 	vp->sample = 0;
 	vp->rate_offset = 0;
 
@@ -792,10 +891,10 @@
 freq_to_note(int mHz)
 {
 	/* abscents = log(mHz/8176) / log(2) * 1200 */
-	unsigned long max_val = (unsigned long)0xffffffff / 10000;
+	unsigned int max_val = (unsigned int)0xffffffff / 10000;
 	int i, times;
-	unsigned long base;
-	unsigned long freq;
+	unsigned int base;
+	unsigned int freq;
 	int note, tune;
 
 	if (mHz == 0)
@@ -913,7 +1012,7 @@
 static int
 calc_parm_hold(int msec)
 {
-	int val = 0x7f - (unsigned char)(msec / 92);
+	int val = (0x7f * 92 - msec) / 92;
 	if (val < 1) val = 1;
 	if (val > 127) val = 127;
 	return val;
@@ -956,45 +1055,72 @@
 
 /* set an effect value */
 #define FX_SET(rec,type,value) \
-	((rec)->flags[(type)/8] |= (1 << ((type) % 8)), \
-	 (rec)->val[type] = (value))
+	((rec)->flags[type] = 1, (rec)->val[type] = (value))
+#define FX_ADD(rec,type,value) \
+	((rec)->flags[type] = 2, (rec)->val[type] = (value))
+#define FX_UNSET(rec,type) \
+	((rec)->flags[type] = 0, (rec)->val[type] = 0)
 
 /* check the effect value is set */
-#define FX_ON(rec,type)	((rec)->flags[(type)/8] & (1<<((type)%8)))
+#define FX_ON(rec,type)	((rec)->flags[type])
+
+static unsigned char
+FX_BYTE(FX_Rec *rec, FX_Rec *lay, int type, unsigned char value)
+{
+	int effect = 0;
+	int on = 0;
+	if (lay && (on = FX_ON(lay, type)) != 0)
+		effect = lay->val[type];
+	if (!on && (on = FX_ON(rec, type)) != 0)
+		effect = rec->val[type];
+	if (on == 1)
+		return (unsigned char)effect;
+	else if (on == 2)
+		return (unsigned char)(effect + (int)value);
+	return value;
+}
 
-/* get byte effect value */
-#define FX_BYTE(rec,type,value) \
-	(unsigned char)(FX_ON(rec,type) ? (rec)->val[type] : (value))
 /* get word effect value */
-#define FX_WORD(rec,type,value) \
-	(unsigned short)(FX_ON(rec,type) ? (rec)->val[type] : (value))
+static unsigned short
+FX_WORD(FX_Rec *rec, FX_Rec *lay, int type, unsigned short value)
+{
+	int effect = 0;
+	int on = 0;
+	if (lay && (on = FX_ON(lay, type)) != 0)
+		effect = lay->val[type];
+	if (!on && (on = FX_ON(rec, type)) != 0)
+		effect = rec->val[type];
+	if (on == 1)
+		return (unsigned short)effect;
+	else if (on == 2)
+		return (unsigned short)(effect + (int)value);
+	return value;
+}
 
 /* get word (upper=type1/lower=type2) effect value */
 static unsigned short
-FX_COMB(FX_Rec *rec, int type1, int type2, unsigned short value)
+FX_COMB(FX_Rec *rec, FX_Rec *lay, int type1, int type2, unsigned short value)
 {
 	unsigned short tmp;
-	if (FX_ON(rec, type1))
-		tmp = (unsigned short)(rec->val[type1]) << 8;
-	else
-		tmp = value & 0xff00;
-	if (FX_ON(rec, type2))
-		tmp |= (unsigned short)(rec->val[type2]) & 0xff;
-	else
-		tmp |= value & 0xff;
+	tmp = FX_BYTE(rec, lay, type1, (unsigned char)(value >> 8));
+	tmp <<= 8;
+	tmp |= FX_BYTE(rec, lay, type2, (unsigned char)(value & 0xff));
 	return tmp;
 }
 
 /* address offset */
-static long
-FX_OFFSET(FX_Rec *rec, int lo, int hi, int mode)
+static int
+FX_OFFSET(FX_Rec *rec, FX_Rec *lay, int lo, int hi, int mode)
 {
-	long addr = 0;
-	if (FX_ON(rec, hi)) {
+	int addr = 0;
+	if (lay && FX_ON(lay, hi))
+		addr = (short)lay->val[hi];
+	else if (FX_ON(rec, hi))
 		addr = (short)rec->val[hi];
-		addr = addr << 15;
-	}
-	if (FX_ON(rec, lo))
+	addr = addr << 15;
+	if (lay && FX_ON(lay, lo))
+		addr += (short)lay->val[lo];
+	else if (FX_ON(rec, lo))
 		addr += (short)rec->val[lo];
 	if (!(mode & AWE_SAMPLE_8BITS))
 		addr /= 2;
@@ -1009,10 +1135,13 @@
 static void
 awe_note_on(int voice)
 {
-	unsigned long temp;
-	long addr;
+	unsigned int temp;
+	int addr;
 	awe_voice_info *vp;
 	FX_Rec *fx = &voices[voice].cinfo->fx;
+	FX_Rec *fx_lay = NULL;
+	if (voices[voice].layer < MAX_LAYERS)
+		fx_lay = &voices[voice].cinfo->fx_layer[voices[voice].layer];
 
 	/* A voice sample must assigned before calling */
 	if ((vp = voices[voice].sample) == NULL || vp->index < 0)
@@ -1027,17 +1156,17 @@
 
 	/* modulation & volume envelope */
 	awe_poke(AWE_ENVVAL(voice),
-		 FX_WORD(fx, AWE_FX_ENV1_DELAY, vp->parm.moddelay));
+		 FX_WORD(fx, fx_lay, AWE_FX_ENV1_DELAY, vp->parm.moddelay));
 	awe_poke(AWE_ATKHLD(voice),
-		 FX_COMB(fx, AWE_FX_ENV1_HOLD, AWE_FX_ENV1_ATTACK,
+		 FX_COMB(fx, fx_lay, AWE_FX_ENV1_HOLD, AWE_FX_ENV1_ATTACK,
 			 vp->parm.modatkhld));
 	awe_poke(AWE_DCYSUS(voice),
-		 FX_COMB(fx, AWE_FX_ENV1_SUSTAIN, AWE_FX_ENV1_DECAY,
+		 FX_COMB(fx, fx_lay, AWE_FX_ENV1_SUSTAIN, AWE_FX_ENV1_DECAY,
 			  vp->parm.moddcysus));
 	awe_poke(AWE_ENVVOL(voice),
-		 FX_WORD(fx, AWE_FX_ENV2_DELAY, vp->parm.voldelay));
+		 FX_WORD(fx, fx_lay, AWE_FX_ENV2_DELAY, vp->parm.voldelay));
 	awe_poke(AWE_ATKHLDV(voice),
-		 FX_COMB(fx, AWE_FX_ENV2_HOLD, AWE_FX_ENV2_ATTACK,
+		 FX_COMB(fx, fx_lay, AWE_FX_ENV2_HOLD, AWE_FX_ENV2_ATTACK,
 			 vp->parm.volatkhld));
 	/* decay/sustain parameter for volume envelope must be set at last */
 
@@ -1049,14 +1178,14 @@
 
 	/* modulation envelope heights */
 	awe_poke(AWE_PEFE(voice),
-		 FX_COMB(fx, AWE_FX_ENV1_PITCH, AWE_FX_ENV1_CUTOFF,
+		 FX_COMB(fx, fx_lay, AWE_FX_ENV1_PITCH, AWE_FX_ENV1_CUTOFF,
 			 vp->parm.pefe));
 
 	/* lfo1/2 delay */
 	awe_poke(AWE_LFO1VAL(voice),
-		 FX_WORD(fx, AWE_FX_LFO1_DELAY, vp->parm.lfo1delay));
+		 FX_WORD(fx, fx_lay, AWE_FX_LFO1_DELAY, vp->parm.lfo1delay));
 	awe_poke(AWE_LFO2VAL(voice),
-		 FX_WORD(fx, AWE_FX_LFO2_DELAY, vp->parm.lfo2delay));
+		 FX_WORD(fx, fx_lay, AWE_FX_LFO2_DELAY, vp->parm.lfo2delay));
 
 	/* lfo1 pitch & cutoff shift */
 	awe_fx_fmmod(voice, TRUE);
@@ -1065,27 +1194,25 @@
 	/* lfo2 pitch & freq */
 	awe_fx_fm2frq2(voice, TRUE);
 	/* pan & loop start */
-	 awe_set_pan(voice, 1);
+	awe_set_pan(voice, TRUE);
 
 	/* chorus & loop end (chorus 8bit, MSB) */
 	addr = vp->loopend - 1;
-	addr += FX_OFFSET(fx, AWE_FX_LOOP_END,
+	addr += FX_OFFSET(fx, fx_lay, AWE_FX_LOOP_END,
 			  AWE_FX_COARSE_LOOP_END, vp->mode);
-	temp = FX_BYTE(fx, AWE_FX_CHORUS, vp->parm.chorus);
-	temp = (temp <<24) | (unsigned long)addr;
+	temp = FX_BYTE(fx, fx_lay, AWE_FX_CHORUS, vp->parm.chorus);
+	temp = (temp <<24) | (unsigned int)addr;
 	awe_poke_dw(AWE_CSL(voice), temp);
-	DEBUG(4,printk("AWE32: [-- loopend=%x/%x]\n",
-		       (int)vp->loopend, (int)addr));
+	DEBUG(4,printk("AWE32: [-- loopend=%x/%x]\n", vp->loopend, addr));
 
 	/* Q & current address (Q 4bit value, MSB) */
 	addr = vp->start - 1;
-	addr += FX_OFFSET(fx, AWE_FX_SAMPLE_START,
+	addr += FX_OFFSET(fx, fx_lay, AWE_FX_SAMPLE_START,
 			  AWE_FX_COARSE_SAMPLE_START, vp->mode);
-	temp = FX_BYTE(fx, AWE_FX_FILTERQ, vp->parm.filterQ);
-	temp = (temp<<28) | (unsigned long)addr;
+	temp = FX_BYTE(fx, fx_lay, AWE_FX_FILTERQ, vp->parm.filterQ);
+	temp = (temp<<28) | (unsigned int)addr;
 	awe_poke_dw(AWE_CCCA(voice), temp);
-	DEBUG(4,printk("AWE32: [-- startaddr=%x/%x]\n",
-		       (int)vp->start, (int)addr));
+	DEBUG(4,printk("AWE32: [-- startaddr=%x/%x]\n", vp->start, addr));
 
 	/* reset volume */
 	awe_poke_dw(AWE_VTFT(voice), 0x0000FFFF);
@@ -1093,13 +1220,15 @@
 
 	/* turn on envelope */
 	awe_poke(AWE_DCYSUSV(voice),
-		 FX_COMB(fx, AWE_FX_ENV2_SUSTAIN, AWE_FX_ENV2_DECAY,
+		 FX_COMB(fx, fx_lay, AWE_FX_ENV2_SUSTAIN, AWE_FX_ENV2_DECAY,
 			  vp->parm.voldcysus));
-	/* set chorus */
-	temp = FX_BYTE(fx, AWE_FX_REVERB, vp->parm.reverb);
+	/* set reverb */
+	temp = FX_BYTE(fx, fx_lay, AWE_FX_REVERB, vp->parm.reverb);
 	temp = (awe_peek_dw(AWE_PTRX(voice)) & 0xffff0000) | (temp<<8);
 	awe_poke_dw(AWE_PTRX(voice), temp);
 	awe_poke_dw(AWE_CPF(voice), 0x40000000);
+
+	voices[voice].state = AWE_ST_ON;
 }
 
 
@@ -1110,24 +1239,22 @@
 	awe_voice_info *vp;
 	unsigned short tmp;
 	FX_Rec *fx = &voices[voice].cinfo->fx;
+	FX_Rec *fx_lay = NULL;
+	if (voices[voice].layer < MAX_LAYERS)
+		fx_lay = &voices[voice].cinfo->fx_layer[voices[voice].layer];
+
 	if ((vp = voices[voice].sample) == NULL) {
-		awe_voice_init(voice);
+		voices[voice].state = AWE_ST_OFF;
 		return;
 	}
 
-	if (FX_ON(fx, AWE_FX_ENV1_RELEASE))
-		tmp = 0x8000 | fx->val[AWE_FX_ENV1_RELEASE];
-	else
-		tmp = vp->parm.modrelease;
+	tmp = 0x8000 | FX_BYTE(fx, fx_lay, AWE_FX_ENV1_RELEASE,
+			       (unsigned char)vp->parm.modrelease);
 	awe_poke(AWE_DCYSUS(voice), tmp);
-	if (FX_ON(fx, AWE_FX_ENV2_RELEASE))
-		tmp = 0x8000 | fx->val[AWE_FX_ENV2_RELEASE];
-	else
-		tmp = vp->parm.volrelease;
+	tmp = 0x8000 | FX_BYTE(fx, fx_lay, AWE_FX_ENV2_RELEASE,
+			       (unsigned char)vp->parm.volrelease);
 	awe_poke(AWE_DCYSUSV(voice), tmp);
-	voices[voice].state = AWE_ST_OFF;
-
-	awe_voice_init(voice);
+	awe_voice_init(voice, TRUE);
 }
 
 /* force to terminate the voice (no releasing echo) */
@@ -1136,26 +1263,27 @@
 {
 	awe_poke(AWE_DCYSUSV(voice), 0x807F);
 	awe_tweak_voice(voice);
-	awe_voice_init(voice);
+	awe_voice_init(voice, FALSE);
 }
 
-
 /* turn off other voices with the same exclusive class (for drums) */
 static void
-awe_exclusive_off(int voice, int exclass)
+awe_exclusive_off(int voice)
 {
-	int i;
+	int i, exclass;
 
-	if (exclass == 0) /* not exclusive */
+	if (voices[voice].sample == NULL)
 		return;
+	if ((exclass = voices[voice].sample->exclusiveClass) == 0)
+		return;	/* not exclusive */
 
 	/* turn off voices with the same class */
 	for (i = 0; i < awe_max_voices; i++) {
-		if (i != voice && IS_PLAYING(voice) &&
-		    voices[i].sample &&
+		if (i != voice && IS_PLAYING(i) &&
+		    voices[i].sample && voices[i].ch == voices[voice].ch &&
 		    voices[i].sample->exclusiveClass == exclass) {
 			DEBUG(4,printk("AWE32: [exoff(%d)]\n", i));
-			awe_note_off(i);
+			awe_terminate(i);
 		}
 	}
 }
@@ -1182,20 +1310,25 @@
 	awe_set_pitch(voice, forced);
 }
 
-/* change volume */
+/* change volume & cutoff */
 static void
 awe_set_volume(int voice, int forced)
 {
 	awe_voice_info *vp;
 	unsigned short tmp2;
 	FX_Rec *fx = &voices[voice].cinfo->fx;
+	FX_Rec *fx_lay = NULL;
+	if (voices[voice].layer < MAX_LAYERS)
+		fx_lay = &voices[voice].cinfo->fx_layer[voices[voice].layer];
 
 	if (IS_NO_EFFECT(voice) && !forced) return;
 	if ((vp = voices[voice].sample) == NULL || vp->index < 0)
 		return;
 
-	tmp2 = FX_BYTE(fx, AWE_FX_CUTOFF, vp->parm.cutoff);
-	tmp2 = (tmp2 << 8) | voices[voice].avol;
+	tmp2 = FX_BYTE(fx, fx_lay, AWE_FX_CUTOFF, vp->parm.cutoff);
+	tmp2 = (tmp2 << 8);
+	tmp2 |= FX_BYTE(fx, fx_lay, AWE_FX_ATTEN,
+			(unsigned char)voices[voice].avol);
 	awe_poke(AWE_IFATN(voice), tmp2);
 }
 
@@ -1212,10 +1345,13 @@
 static void
 awe_set_pan(int voice, int forced)
 {
-	unsigned long temp;
-	long addr;
+	unsigned int temp;
+	int addr;
 	awe_voice_info *vp;
 	FX_Rec *fx = &voices[voice].cinfo->fx;
+	FX_Rec *fx_lay = NULL;
+	if (voices[voice].layer < MAX_LAYERS)
+		fx_lay = &voices[voice].cinfo->fx_layer[voices[voice].layer];
 
 	if (IS_NO_EFFECT(voice) && !forced) return;
 	if ((vp = voices[voice].sample) == NULL || vp->index < 0)
@@ -1239,13 +1375,12 @@
 	}
 	if (forced || temp != voices[voice].apan) {
 		addr = vp->loopstart - 1;
-		addr += FX_OFFSET(fx, AWE_FX_LOOP_START,
+		addr += FX_OFFSET(fx, fx_lay, AWE_FX_LOOP_START,
 				  AWE_FX_COARSE_LOOP_START, vp->mode);
-		temp = (temp<<24) | (unsigned long)addr;
+		temp = (temp<<24) | (unsigned int)addr;
 		awe_poke_dw(AWE_PSST(voice), temp);
 		voices[voice].apan = temp;
-		DEBUG(4,printk("AWE32: [-- loopstart=%x/%x]\n",
-			       (int)vp->loopstart, (int)addr));
+		DEBUG(4,printk("AWE32: [-- loopstart=%x/%x]\n", vp->loopstart, addr));
 	}
 }
 
@@ -1255,11 +1390,15 @@
 {
 	awe_voice_info *vp;
 	FX_Rec *fx = &voices[voice].cinfo->fx;
+	FX_Rec *fx_lay = NULL;
+	if (voices[voice].layer < MAX_LAYERS)
+		fx_lay = &voices[voice].cinfo->fx_layer[voices[voice].layer];
+
 	if (IS_NO_EFFECT(voice) && !forced) return;
 	if ((vp = voices[voice].sample) == NULL || vp->index < 0)
 		return;
 	awe_poke(AWE_FMMOD(voice),
-		 FX_COMB(fx, AWE_FX_LFO1_PITCH, AWE_FX_LFO1_CUTOFF,
+		 FX_COMB(fx, fx_lay, AWE_FX_LFO1_PITCH, AWE_FX_LFO1_CUTOFF,
 			 vp->parm.fmmod));
 }
 
@@ -1269,11 +1408,15 @@
 {
 	awe_voice_info *vp;
 	FX_Rec *fx = &voices[voice].cinfo->fx;
+	FX_Rec *fx_lay = NULL;
+	if (voices[voice].layer < MAX_LAYERS)
+		fx_lay = &voices[voice].cinfo->fx_layer[voices[voice].layer];
+
 	if (IS_NO_EFFECT(voice) && !forced) return;
 	if ((vp = voices[voice].sample) == NULL || vp->index < 0)
 		return;
 	awe_poke(AWE_TREMFRQ(voice),
-		 FX_COMB(fx, AWE_FX_LFO1_VOLUME, AWE_FX_LFO1_FREQ,
+		 FX_COMB(fx, fx_lay, AWE_FX_LFO1_VOLUME, AWE_FX_LFO1_FREQ,
 			 vp->parm.tremfrq));
 }
 
@@ -1283,29 +1426,18 @@
 {
 	awe_voice_info *vp;
 	FX_Rec *fx = &voices[voice].cinfo->fx;
+	FX_Rec *fx_lay = NULL;
+	if (voices[voice].layer < MAX_LAYERS)
+		fx_lay = &voices[voice].cinfo->fx_layer[voices[voice].layer];
+
 	if (IS_NO_EFFECT(voice) && !forced) return;
 	if ((vp = voices[voice].sample) == NULL || vp->index < 0)
 		return;
 	awe_poke(AWE_FM2FRQ2(voice),
-		 FX_COMB(fx, AWE_FX_LFO2_PITCH, AWE_FX_LFO2_FREQ,
+		 FX_COMB(fx, fx_lay, AWE_FX_LFO2_PITCH, AWE_FX_LFO2_FREQ,
 			 vp->parm.fm2frq2));
 }
 
-/* set total cutoff  & current pitch */
-static void
-awe_fx_cutoff(int voice, int forced)
-{
-	unsigned short tmp2;
-	awe_voice_info *vp;
-	FX_Rec *fx = &voices[voice].cinfo->fx;
-	if (IS_NO_EFFECT(voice)) return;
-	if ((vp = voices[voice].sample) == NULL || vp->index < 0)
-		return;
-	tmp2 = FX_BYTE(fx, AWE_FX_CUTOFF, vp->parm.cutoff);
-	tmp2 = (tmp2 << 8) | voices[voice].avol;
-	awe_poke(AWE_IFATN(voice), tmp2);
-}
-
 
 /*================================================================
  * calculate pitch offset
@@ -1326,6 +1458,7 @@
 	if ((ap = vp->sample) == NULL)
 			return;
 	if (ap->index < 0) {
+		DEBUG(3,printk("AWE32: set sample (%d)\n", ap->sample));
 		if (awe_set_sample(ap) < 0)
 			return;
 	}
@@ -1350,10 +1483,10 @@
 	DEBUG(4,printk("AWE32: p-> scale* ofs=%d\n", offset));
 
 	/* add initial pitch correction */
-	if (FX_ON(&cp->fx, AWE_FX_INIT_PITCH)) {
-		DEBUG(3,printk("AWE32: fx_pitch(%d) %d\n", voice, cp->fx.val[AWE_FX_INIT_PITCH]));
+	if (FX_ON(&cp->fx_layer[vp->layer], AWE_FX_INIT_PITCH))
+		offset += cp->fx_layer[vp->layer].val[AWE_FX_INIT_PITCH];
+	else if (FX_ON(&cp->fx, AWE_FX_INIT_PITCH))
 		offset += cp->fx.val[AWE_FX_INIT_PITCH];
-	}
 
 	/* 0xe000: root pitch */
 	vp->apitch = 0xe000 + ap->rate_offset + offset;
@@ -1373,20 +1506,27 @@
 	voice_info *vp = &voices[voice];
 	awe_voice_info *ap;
 	FX_Rec *fx = &voices[voice].cinfo->fx;
+	FX_Rec *fx_lay = NULL;
 	int offset;
 	int note;
 
+	if (voices[voice].layer < MAX_LAYERS)
+		fx_lay = &voices[voice].cinfo->fx_layer[voices[voice].layer];
+
 	/* search voice information */
 	if ((ap = vp->sample) == NULL)
 		return;
 	if (ap->index < 0) {
+		DEBUG(3,printk("AWE32: set sample (%d)\n", ap->sample));
 		if (awe_set_sample(ap) < 0)
 			return;
 	}
 	note = freq_to_note(freq);
 	offset = (note - ap->root * 100 + ap->tune) * 4096 / 1200;
 	offset = (offset * ap->scaleTuning) / 100;
-	if (FX_ON(fx, AWE_FX_INIT_PITCH))
+	if (fx_lay && FX_ON(fx_lay, AWE_FX_INIT_PITCH))
+		offset += fx_lay->val[AWE_FX_INIT_PITCH];
+	else if (FX_ON(fx, AWE_FX_INIT_PITCH))
 		offset += fx->val[AWE_FX_INIT_PITCH];
 	vp->apitch = 0xe000 + ap->rate_offset + offset;
 	if (vp->apitch > 0xffff)
@@ -1430,6 +1570,7 @@
 
 	ap = vp->sample;
 	if (ap->index < 0) {
+		DEBUG(3,printk("AWE32: set sample (%d)\n", ap->sample));
 		if (awe_set_sample(ap) < 0)
 			return;
 	}
@@ -1437,6 +1578,7 @@
 	/* 0 - 127 */
 	vol = (vp->velocity * cp->main_vol * cp->expression_vol) / (127*127);
 	vol = vol * ap->amplitude / 127;
+
 	if (vol < 0) vol = 0;
 	if (vol > 127) vol = 127;
 
@@ -1450,56 +1592,121 @@
 }
 
 
+/* set sostenuto on */
+static void awe_sostenuto_on(int voice, int forced)
+{
+	if (IS_NO_EFFECT(voice) && !forced) return;
+	voices[voice].sostenuto = 127;
+}
+
+
+/* drop sustain */
+static void awe_sustain_off(int voice, int forced)
+{
+	if (voices[voice].state == AWE_ST_SUSTAINED)
+		awe_note_off(voice);
+}
+
+
 /*================================================================
  * synth operation routines
  *================================================================*/
 
 #define AWE_VOICE_KEY(v)	(0x8000 | (v))
 #define AWE_CHAN_KEY(c,n)	(((c) << 8) | ((n) + 1))
+#define KEY_CHAN_MATCH(key,c)	(((key) >> 8) == (c))
 
 /* initialize the voice */
 static void
-awe_voice_init(int voice)
+awe_voice_init(int voice, int keep_ch_info)
 {
-	voices[voice].note = -1;
-	voices[voice].velocity = 0;
-	voices[voice].sample = NULL;
-	voices[voice].state = AWE_ST_OFF;
-	voices[voice].cinfo = &channels[voice];
-	voices[voice].ch = -1;
-	voices[voice].key = AWE_VOICE_KEY(voice);
-	voices[voice].time = current_alloc_time;
+	voice_info *vp = &voices[voice];
+	vp->note = -1;
+	vp->velocity = 0;
+	vp->sostenuto = 0;
+	if (playing_mode == AWE_PLAY_DIRECT)
+		vp->key = AWE_VOICE_KEY(voice);
+	else
+		vp->key = 0;
+	vp->time = current_alloc_time;
 
-	/* clear voice mapping */
-	voice_alloc->map[voice] = 0;
+	/* keep channel info for terminating release echo if necessary */
+	if (keep_ch_info)
+		vp->state = AWE_ST_RELEASED;
+	else {
+		vp->sample = NULL;
+		vp->cinfo = &channels[voice];
+		vp->ch = voice;
+		vp->state = AWE_ST_OFF;
+	}
 
 	/* emu8000 parameters */
-	voices[voice].apitch = 0;
-	voices[voice].avol = 255;
-	voices[voice].apan = -1;
+	vp->apitch = 0;
+	vp->avol = 255;
+	vp->apan = -1;
+
+	/* clear voice mapping */
+	voice_alloc->map[voice] = 0;
 
 	/* clear effects */
-	if (! awe_channel_mode)
-		BZERO(&voices[voice].cinfo->fx, sizeof(FX_Rec));
+	if (SINGLE_LAYER_MODE() && !misc_modes[AWE_MD_KEEP_EFFECT]) {
+		BZERO(&vp->cinfo->fx, sizeof(vp->cinfo->fx));
+		BZERO(&vp->cinfo->fx_layer, sizeof(vp->cinfo->fx_layer));
+	}
+
+	/*awe_tweak_voice(voice);*/
 }
 
 /* initialize channel info */
-static void awe_channel_init(int ch)
+static void awe_channel_init(int ch, int init_all)
 {
-	channels[ch].panning = 0; /* zero center */
-	channels[ch].bender = 0; /* zero tune skew */
-	channels[ch].bender_range = 200; /* sense * 100 */
-	channels[ch].main_vol = 127;
-	channels[ch].expression_vol = 127;
-	if (awe_channel_mode && IS_DRUM_CHANNEL(ch))
-		channels[ch].bank = AWE_DRUM_BANK;
-	else
-		channels[ch].bank = AWE_DEFAULT_BANK;
-	channels[ch].instr = 0;
-	channels[ch].vrec = NULL;
-	channels[ch].def_vrec = NULL;
-	channels[ch].sustained = 0;
-	BZERO(&channels[ch].fx, sizeof(FX_Rec));
+	awe_chan_info *cp = &channels[ch];
+	if (init_all) {
+		cp->panning = 0; /* zero center */
+		cp->bender_range = 200; /* sense * 100 */
+		cp->main_vol = 127;
+		if (MULTI_LAYER_MODE() && IS_DRUM_CHANNEL(ch)) {
+			cp->instr = misc_modes[AWE_MD_DEF_DRUM];
+			cp->bank = AWE_DRUM_BANK;
+		} else {
+			cp->instr = misc_modes[AWE_MD_DEF_PRESET];
+			cp->bank = misc_modes[AWE_MD_DEF_BANK];
+		}
+		cp->vrec = -1;
+		cp->def_vrec = -1;
+	}
+
+	cp->bender = 0; /* zero tune skew */
+	cp->expression_vol = 127;
+	cp->chan_press = 0;
+	cp->sustained = 0;
+
+	if (! misc_modes[AWE_MD_KEEP_EFFECT]) {
+		BZERO(&cp->fx, sizeof(cp->fx));
+		BZERO(&cp->fx_layer, sizeof(cp->fx_layer));
+	}
+}
+
+
+/* change the voice parameters; voice = channel */
+static void awe_voice_change(int voice, fx_affect_func func)
+{
+	int i; 
+	switch (playing_mode) {
+	case AWE_PLAY_DIRECT:
+		func(voice, FALSE);
+		break;
+	case AWE_PLAY_INDIRECT:
+		for (i = 0; i < awe_max_voices; i++)
+			if (voices[i].key == AWE_VOICE_KEY(voice))
+				func(i, FALSE);
+		break;
+	default:
+		for (i = 0; i < awe_max_voices; i++)
+			if (KEY_CHAN_MATCH(voices[i].key, voice))
+				func(i, FALSE);
+		break;
+	}
 }
 
 
@@ -1520,14 +1727,13 @@
 	awe_reset(dev);
 
 	/* clear sample position flag */
-	loaded_once = 0;
+	patch_opened = 0;
 
 	/* set default mode */
-	init_atten = AWE_DEFAULT_ATTENUATION; /* 12dB below */
-	awe_gus_bank = AWE_DEFAULT_BANK;
+	awe_init_misc_modes(FALSE);
+	init_atten = misc_modes[AWE_MD_ZERO_ATTEN];
 	drum_flags = DEFAULT_DRUM_FLAGS;
-	awe_exclusive_sound = TRUE;
-	awe_channel_mode = 0;
+	playing_mode = AWE_PLAY_INDIRECT;
 
 	return 0;
 }
@@ -1544,6 +1750,19 @@
 }
 
 
+/* set miscellaneous mode parameters
+ */
+static void
+awe_init_misc_modes(int init_all)
+{
+	int i;
+	for (i = 0; i < AWE_MD_END; i++) {
+		if (init_all || misc_modes_default[i].init_each_time)
+			misc_modes[i] = misc_modes_default[i].value;
+	}
+}
+
+
 /* sequencer I/O control:
  */
 static int
@@ -1551,7 +1770,10 @@
 {
 	switch (cmd) {
 	case SNDCTL_SYNTH_INFO:
-		awe_info.nr_voices = awe_max_voices;
+		if (playing_mode == AWE_PLAY_DIRECT)
+			awe_info.nr_voices = awe_max_voices;
+		else
+			awe_info.nr_voices = AWE_MAX_CHANNELS;
 		IOCTL_TO_USER((char*)arg, 0, &awe_info, sizeof(awe_info));
 		return 0;
 		break;
@@ -1568,62 +1790,104 @@
 		break;
 
 	case SNDCTL_SYNTH_MEMAVL:
-		DEBUG(0,printk("AWE32: [ioctl memavl = %d]\n", (int)free_mem_ptr));
-		return awe_mem_size - free_mem_ptr*2;
+		return awe_mem_size - awe_free_mem_ptr() * 2;
 
 	default:
-		ERRMSG(printk("AWE32: unsupported ioctl %d\n", cmd));
+		printk("AWE32: unsupported ioctl %d\n", cmd);
 		return RET_ERROR(EINVAL);
 	}
 }
 
 
+static int voice_in_range(int voice)
+{
+	if (playing_mode == AWE_PLAY_DIRECT) {
+		if (voice < 0 || voice >= awe_max_voices)
+			return FALSE;
+	} else {
+		if (voice < 0 || voice >= AWE_MAX_CHANNELS)
+			return FALSE;
+	}
+	return TRUE;
+}
+
+static void release_voice(int voice, int do_sustain)
+{
+	if (IS_NO_SOUND(voice))
+		return;
+	if (do_sustain && (voices[voice].cinfo->sustained == 127 ||
+			    voices[voice].sostenuto == 127))
+		voices[voice].state = AWE_ST_SUSTAINED;
+	else
+		awe_note_off(voice);
+}
+
+/* release all notes */
+static void awe_note_off_all(int do_sustain)
+{
+	int i;
+	for (i = 0; i < awe_max_voices; i++)
+		release_voice(i, do_sustain);
+}
+
 /* kill a voice:
  *   not terminate, just release the voice.
  */
 static int
 awe_kill_note(int dev, int voice, int note, int velocity)
 {
-	int i, key, besttime;
+	int i, v2, key;
 
 	DEBUG(2,printk("AWE32: [off(%d) nt=%d vl=%d]\n", voice, note, velocity));
-	if (awe_channel_mode) {
-		if (awe_channel_mode == 2) { /* get channel */
-			int v2 = voice_alloc->map[voice] >> 8;
-			voice_alloc->map[voice] = 0;
-			voice = v2;
-		}
+	if (! voice_in_range(voice))
+		return RET_ERROR(EINVAL);
+
+	switch (playing_mode) {
+	case AWE_PLAY_DIRECT:
+	case AWE_PLAY_INDIRECT:
+		key = AWE_VOICE_KEY(voice);
+		break;
+
+	case AWE_PLAY_MULTI2:
+		v2 = voice_alloc->map[voice] >> 8;
+		voice_alloc->map[voice] = 0;
+		voice = v2;
 		if (voice < 0 || voice >= AWE_MAX_CHANNELS)
 			return RET_ERROR(EINVAL);
-		if (channels[voice].instr > 128)
-			note = channels[voice].instr - 128;
+		/* continue to below */
+	default:
 		key = AWE_CHAN_KEY(voice, note);
-	} else {
-		if (voice < 0 || voice >= awe_max_voices)
-			return RET_ERROR(EINVAL);
-		key = AWE_VOICE_KEY(voice);
+		break;
 	}
 
-	besttime = current_alloc_time + 1;
-	for (i = 0; i < awe_max_voices; i++) {
-		if (voices[i].key == key) {
-			if (voices[i].time < besttime)
-				besttime = voices[i].time;
-		}
-	}
 	for (i = 0; i < awe_max_voices; i++) {
-		if (voices[i].key == key &&
-		    (!awe_exclusive_sound || voices[i].time == besttime)) {
-			if (voices[i].cinfo->sustained)
-				voices[i].state = AWE_ST_SUSTAINED;
-			else
-				awe_note_off(i);
-		}
+		if (voices[i].key == key)
+			release_voice(i, TRUE);
 	}
 	return 0;
 }
 
 
+static void start_or_volume_change(int voice, int velocity)
+{
+	voices[voice].velocity = velocity;
+	awe_calc_volume(voice);
+	if (voices[voice].state == AWE_ST_STANDBY)
+		awe_note_on(voice);
+	else if (voices[voice].state == AWE_ST_ON)
+		awe_set_volume(voice, FALSE);
+}
+
+static void set_and_start_voice(int voice, int state)
+{
+	/* calculate pitch & volume parameters */
+	voices[voice].state = state;
+	awe_calc_pitch(voice);
+	awe_calc_volume(voice);
+	if (state == AWE_ST_ON)
+		awe_note_on(voice);
+}
+
 /* start a voice:
  *   if note is 255, identical with aftertouch function.
  *   Otherwise, start a voice with specified not and volume.
@@ -1634,107 +1898,87 @@
 	int i, key, state, volonly;
 
 	DEBUG(2,printk("AWE32: [on(%d) nt=%d vl=%d]\n", voice, note, velocity));
-	volonly = 0;
-	if (awe_channel_mode) {
-		if (awe_channel_mode == 2) /* get channel */
-			voice = voice_alloc->map[voice] >> 8;
-		else if (voice & 0x80) { /* channel volume mode */
-			voice &= ~0x80;
-			volonly = 2;
-		}
+	if (! voice_in_range(voice))
+		return RET_ERROR(EINVAL);
+	    
+	if (velocity == 0)
+		state = AWE_ST_STANDBY; /* stand by for playing */
+	else
+		state = AWE_ST_ON;	/* really play */
+	volonly = FALSE;
+
+	switch (playing_mode) {
+	case AWE_PLAY_DIRECT:
+	case AWE_PLAY_INDIRECT:
+		key = AWE_VOICE_KEY(voice);
+		if (note == 255)
+			volonly = TRUE;
+		break;
+
+	case AWE_PLAY_MULTI2:
+		voice = voice_alloc->map[voice] >> 8;
 		if (voice < 0 || voice >= AWE_MAX_CHANNELS)
 			return RET_ERROR(EINVAL);
-		if (channels[voice].instr > 128)
-			note = channels[voice].instr - 128;
+		/* continue to below */
+	default:
 		if (note >= 128) { /* key volume mode */
 			note -= 128;
-			volonly = 1;
+			volonly = TRUE;
 		}
 		key = AWE_CHAN_KEY(voice, note);
-	} else {
-		if (voice < 0 || voice >= awe_max_voices)
-		      return RET_ERROR(EINVAL);
-		if (voices[voice].cinfo->instr > 128)
-			note = voices[voice].cinfo->instr - 128;
-		key = AWE_VOICE_KEY(voice);
-		if (note == 255)
-			volonly = 1;
+		break;
 	}
 
 	/* dynamic volume change */
 	if (volonly) {
 		for (i = 0; i < awe_max_voices; i++) {
-			if ((volonly == 2 && voices[i].ch == voice) ||
-			    voices[i].key == key) {
-				voices[i].velocity = velocity;
-				if (velocity == 0) /* for GUS compatibility */
-					return awe_kill_note(dev, voice, note, velocity);
-				awe_calc_volume(i);
-				state = voices[i].state;
-				voices[i].state = AWE_ST_ON;
-				if (state == AWE_ST_STANDBY)
-					awe_note_on(i);
-				else
-					awe_set_volume(i, FALSE);
-			}
+			if (voices[i].key == key)
+				start_or_volume_change(i, velocity);
 		}
 		return 0;
 	}
 
-	/* stop the sound if still playing */
-	if (awe_exclusive_sound) {
-		for (i = 0; i < awe_max_voices; i++)
-			if (voices[i].key == key &&
-			    voices[i].state != AWE_ST_OFF)
+	/* if the same note still playing, stop it */
+	for (i = 0; i < awe_max_voices; i++)
+		if (voices[i].key == key) {
+			if (voices[i].state == AWE_ST_ON)
 				awe_note_off(i);
-	}
+			else if (voices[i].state == AWE_ST_STANDBY)
+				awe_voice_init(i, FALSE);
+		}
 
 	/* allocate voices */
-	if (awe_channel_mode)
-		awe_alloc_multi_voices(voice, note, velocity);
-	else
+	if (playing_mode == AWE_PLAY_DIRECT)
 		awe_alloc_one_voice(voice, note, velocity);
-
-	/* turn off other voices (for drums) */
-	for (i = 0; i < awe_max_voices; i++)
-		if (voices[i].key == key && voices[i].sample)
-			awe_exclusive_off(i, voices[i].sample->exclusiveClass);
-
-	if (velocity == 0)
-		state = AWE_ST_STANDBY; /* stand by for playing */
 	else
-		state = AWE_ST_ON;	/* really play */
+		awe_alloc_multi_voices(voice, note, velocity, key);
 
-	/* set up pitch and volume parameters */
+	/* turn off other voices exlusively (for drums) */
 	for (i = 0; i < awe_max_voices; i++)
-		if (voices[i].key == key) {
-			/* calculate pitch & volume parameters */
-			voices[i].state = state;
-			voices[i].note = note;
-			voices[i].velocity = velocity;
-			awe_calc_pitch(i);
-			awe_calc_volume(i);
-			if (state == AWE_ST_ON)
-				awe_note_on(i);
-		}
+		if (voices[i].key == key)
+			awe_exclusive_off(i);
+
+	/* set up pitch and volume parameters */
+	for (i = 0; i < awe_max_voices; i++) {
+		if (voices[i].key == key && voices[i].state == AWE_ST_OFF)
+			set_and_start_voice(i, state);
+	}
 
 	return 0;
 }
 
 
 /* search instrument from preset table with the specified bank */
-static awe_voice_list *
+static int
 awe_search_instr(int bank, int preset)
 {
-	awe_voice_list *p;
-	int maxc;
+	int i;
 
-	for (maxc = AWE_MAX_INFOS, p = preset_table[preset];
-	     p && maxc; p = p->next_bank, maxc--) {
-		if (p->bank == bank)
-			return p;
+	for (i = preset_table[preset]; i >= 0; i = infos[i].next_bank) {
+		if (infos[i].bank == bank)
+			return i;
 	}
-	return NULL;
+	return -1;
 }
 
 
@@ -1742,53 +1986,45 @@
 static int
 awe_set_instr_2(int dev, int voice, int instr_no)
 {
-	if (awe_channel_mode == 2)
+	if (playing_mode == AWE_PLAY_MULTI2) {
 		voice = voice_alloc->map[voice] >> 8;
+		if (voice < 0 || voice >= AWE_MAX_CHANNELS)
+			return RET_ERROR(EINVAL);
+	}
 	return awe_set_instr(dev, voice, instr_no);
 }
 
-/* assign the instrument to a voice */
+/* assign the instrument to a channel; voice is the channel number */
 static int
 awe_set_instr(int dev, int voice, int instr_no)
 {
 	awe_chan_info *cinfo;
 	int def_bank;
 
-	if (awe_channel_mode) {
-		/* skip the percussion instr in SEQ2 mode */
-		if (voice < 0 || voice >= AWE_MAX_CHANNELS)
-			return RET_ERROR(EINVAL);
-		cinfo = &channels[voice];
-		if (IS_DRUM_CHANNEL(voice))
-			def_bank = AWE_DRUM_BANK;
-		else
-			def_bank = cinfo->bank;
-	} else {
-		if (voice < 0 || voice >= awe_max_voices)
-			return RET_ERROR(EINVAL);
-		cinfo = voices[voice].cinfo;
-		def_bank = cinfo->bank;
-	}
+	if (! voice_in_range(voice))
+		return RET_ERROR(EINVAL);
 
 	if (instr_no < 0 || instr_no >= AWE_MAX_PRESETS)
 		return RET_ERROR(EINVAL);
 
-	cinfo->vrec = NULL;
-	cinfo->def_vrec = NULL;
-	if (instr_no > 128) {
-		cinfo->vrec = awe_search_instr(128, cinfo->bank);
-		if (cinfo->bank != 0)
-			cinfo->def_vrec = awe_search_instr(128, 0);
-	} else {
-		cinfo->vrec = awe_search_instr(def_bank, instr_no);
-		if (def_bank == AWE_DRUM_BANK)
-			cinfo->def_vrec = awe_search_instr(def_bank, 0);
-		else
-			cinfo->def_vrec = awe_search_instr(AWE_DEFAULT_BANK, instr_no);
+	cinfo = &channels[voice];
+	def_bank = cinfo->bank;
+
+	if (MULTI_LAYER_MODE()) {
+		if (IS_DRUM_CHANNEL(voice))
+			def_bank = AWE_DRUM_BANK;
 	}
-	if (cinfo->vrec == NULL && cinfo->def_vrec == NULL) {
+
+	cinfo->vrec = -1;
+	cinfo->def_vrec = -1;
+	cinfo->vrec = awe_search_instr(def_bank, instr_no);
+	if (def_bank == AWE_DRUM_BANK)
+		cinfo->def_vrec = awe_search_instr(def_bank, misc_modes[AWE_MD_DEF_DRUM]);
+	else
+		cinfo->def_vrec = awe_search_instr(misc_modes[AWE_MD_DEF_BANK], instr_no);
+
+	if (cinfo->vrec < 0 && cinfo->def_vrec < 0) {
 		DEBUG(1,printk("AWE32 Warning: can't find instrument %d\n", instr_no));
-		return 0;
 	}
 
 	cinfo->instr = instr_no;
@@ -1804,10 +2040,10 @@
 	int i;
 	current_alloc_time = 0;
 	/* don't turn off voice 31 and 32.  they are used also for FM voices */
-	for (i = 0; i < AWE_NORMAL_VOICES; i++)
+	for (i = 0; i < awe_max_voices; i++)
 		awe_terminate(i);
 	for (i = 0; i < AWE_MAX_CHANNELS; i++)
-		awe_channel_init(i);
+		awe_channel_init(i, TRUE);
 	for (i = 0; i < 16; i++) {
 		awe_operations.chn_info[i].controllers[CTL_MAIN_VOLUME] = 127;
 		awe_operations.chn_info[i].controllers[CTL_EXPRESSION] = 127;
@@ -1839,55 +2075,38 @@
 static void
 awe_hw_gus_control(int dev, int cmd, unsigned char *event)
 {
-	int voice;
+	int voice, i, key;
 	unsigned short p1;
 	short p2;
 	int plong;
 
+	if (MULTI_LAYER_MODE())
+		return;
+	if (cmd == _GUS_NUMVOICES)
+		return;
+
 	voice = event[3];
+	if (! voice_in_range(voice))
+		return;
+
 	p1 = *(unsigned short *) &event[4];
 	p2 = *(short *) &event[6];
 	plong = *(int*) &event[4];
 
 	switch (cmd) {
-	case _GUS_NUMVOICES:
-		if (p1 >= awe_max_voices)
-			DEBUG(0,printk("AWE32: num_voices: voices out of range %d\n", p1));
-		break;
 	case _GUS_VOICESAMPLE:
-		if (voice < awe_max_voices)
-			awe_set_instr(dev, voice, p1);
-		break;
-
-	case _GUS_VOICEON:
-		if (voice < awe_max_voices)
-			awe_note_on(voice);
-		break;
-		
-	case _GUS_VOICEOFF:
-		if (voice < awe_max_voices)
-			awe_note_off(voice);
-		break;
-		
-	case _GUS_VOICEMODE:
-		/* not supported */
-		break;
+		awe_set_instr(dev, voice, p1);
+		return;
 
 	case _GUS_VOICEBALA:
 		/* 0 to 15 --> -128 to 127 */
-		if (voice < awe_max_voices)
-			awe_panning(dev, voice, ((int)p1 << 4) - 128);
-		break;
+		awe_panning(dev, voice, ((int)p1 << 4) - 128);
+		return;
 
-	case _GUS_VOICEFREQ:
-		if (voice < awe_max_voices)
-			awe_calc_pitch_from_freq(voice, plong);
-		break;
-		
 	case _GUS_VOICEVOL:
 	case _GUS_VOICEVOL2:
 		/* not supported yet */
-		break;
+		return;
 
 	case _GUS_RAMPRANGE:
 	case _GUS_RAMPRATE:
@@ -1895,19 +2114,40 @@
 	case _GUS_RAMPON:
 	case _GUS_RAMPOFF:
 		/* volume ramping not supported */
-		break;
+		return;
 
 	case _GUS_VOLUME_SCALE:
-		break;
+		return;
 
 	case _GUS_VOICE_POS:
-		if (voice < awe_max_voices) {
-			FX_SET(&voices[voice].cinfo->fx, AWE_FX_SAMPLE_START,
-			       (short)(plong & 0x7fff));
-			FX_SET(&voices[voice].cinfo->fx, AWE_FX_COARSE_SAMPLE_START,
-			       (plong >> 15) & 0xffff);
+		FX_SET(&channels[voice].fx, AWE_FX_SAMPLE_START,
+		       (short)(plong & 0x7fff));
+		FX_SET(&channels[voice].fx, AWE_FX_COARSE_SAMPLE_START,
+		       (plong >> 15) & 0xffff);
+		return;
+	}
+
+	key = AWE_VOICE_KEY(voice);
+	for (i = 0; i < awe_max_voices; i++) {
+		if (voices[i].key == key) {
+			switch (cmd) {
+			case _GUS_VOICEON:
+				awe_note_on(i);
+				break;
+
+			case _GUS_VOICEOFF:
+			awe_terminate(i);
+				break;
+
+			case _GUS_VOICEFADE:
+				awe_note_off(i);
+				break;
+	
+			case _GUS_VOICEFREQ:
+			awe_calc_pitch_from_freq(i, plong);
+				break;
+			}
 		}
-		break;
 	}
 }
 
@@ -1926,9 +2166,11 @@
 	/* lfo2: delay, freq, pitch */
 	NULL, awe_fx_fm2frq2, awe_fx_fm2frq2,
 	/* global: initpitch, chorus, reverb, cutoff, filterQ */
-	awe_set_voice_pitch, NULL, NULL, awe_fx_cutoff, NULL,
+	awe_set_voice_pitch, NULL, NULL, awe_set_volume, NULL,
 	/* sample: start, loopstart, loopend */
-	NULL, NULL, NULL,
+	NULL, NULL, NULL, NULL, NULL, NULL,
+	/* global: attenuation */
+	awe_set_volume,
 };
 
 
@@ -1939,49 +2181,41 @@
 	int voice;
 	unsigned short p1;
 	short p2;
-	int chn;
 	awe_chan_info *cinfo;
+	FX_Rec *fx;
 	int i;
 
-	chn = event[1];
 	voice = event[3];
-	p1 = *(unsigned short *) &event[4];
-	p2 = *(short *) &event[6];
-	
-	if (awe_channel_mode) {
+	if (! voice_in_range(voice))
+		return;
+
+	if (playing_mode == AWE_PLAY_MULTI2) {
+		voice = voice_alloc->map[voice] >> 8;
 		if (voice < 0 || voice >= AWE_MAX_CHANNELS)
 			return;
-		cinfo = &channels[voice];
-	} else {
-		if (voice < 0 || voice >= awe_max_voices)
-			return;
-		cinfo = voices[voice].cinfo;
 	}
 
+	p1 = *(unsigned short *) &event[4];
+	p2 = *(short *) &event[6];
+	cinfo = &channels[voice];
+
 	switch (cmd) {
 	case _AWE_DEBUG_MODE:
 		debug_mode = p1;
 		printk("AWE32: debug mode = %d\n", debug_mode);
 		break;
 	case _AWE_REVERB_MODE:
-		if (p1 <= 7) {
-			reverb_mode = p1;
-			DEBUG(0,printk("AWE32: reverb mode %d\n", reverb_mode));
-			awe_set_reverb_mode(reverb_mode);
-		}
+		awe_set_reverb_mode(p1);
 		break;
 
 	case _AWE_CHORUS_MODE:
-		if (p1 <= 7) {
-			chorus_mode = p1;
-			DEBUG(0,printk("AWE32: chorus mode %d\n", chorus_mode));
-			awe_set_chorus_mode(chorus_mode);
-		}
+		awe_set_chorus_mode(p1);
 		break;
 		      
 	case _AWE_REMOVE_LAST_SAMPLES:
 		DEBUG(0,printk("AWE32: remove last samples\n"));
-		awe_remove_samples();
+		if (locked_sf_id > 0)
+			awe_remove_samples(locked_sf_id);
 		break;
 
 	case _AWE_INITIALIZE_CHIP:
@@ -1989,19 +2223,37 @@
 		break;
 
 	case _AWE_SEND_EFFECT:
+		fx = &cinfo->fx;
+		i = 0; /* set */
+		if (p1 >= 0x100) {
+			int layer = (p1 >> 8);
+			if (layer >= 0 && layer < MAX_LAYERS)
+				fx = &cinfo->fx_layer[layer];
+			p1 &= 0xff;
+		}
+		if (p1 & 0x40) i = 2; /* clear */
+		if (p1 & 0x80) i = 1; /* add */
+		p1 &= 0x3f;
 		if (p1 < AWE_FX_END) {
-			FX_SET(&cinfo->fx, p1, p2);
 			DEBUG(0,printk("AWE32: effects (%d) %d %d\n", voice, p1, cinfo->fx.val[p1]));
-			FX_SET(&cinfo->fx, p1, p2);
-			if (fx_realtime[p1]) {
+			if (i == 0)
+				FX_SET(fx, p1, p2);
+			else if (i == 1)
+				FX_ADD(fx, p1, p2);
+			else
+				FX_UNSET(fx, p1);
+			if (i != 2 && fx_realtime[p1]) {
 				DEBUG(0,printk("AWE32: fx_realtime (%d)\n", voice));
 				awe_voice_change(voice, fx_realtime[p1]);
 			}
 		}
 		break;
 
+	case _AWE_RESET_CHANNEL:
+		awe_channel_init(voice, !p1);
+		break;
+		
 	case _AWE_TERMINATE_ALL:
-		DEBUG(0,printk("AWE32: terminate all\n"));
 		awe_reset(0);
 		break;
 
@@ -2009,63 +2261,54 @@
 		awe_voice_change(voice, (fx_affect_func)awe_terminate);
 		break;
 
+	case _AWE_RELEASE_ALL:
+		awe_note_off_all(FALSE);
+		break;
 	case _AWE_NOTEOFF_ALL:
-		for (i = 0; i < awe_max_voices; i++)
-			awe_note_off(i);
+		awe_note_off_all(TRUE);
 		break;
 
 	case _AWE_INITIAL_VOLUME:
 		DEBUG(0,printk("AWE32: init attenuation %d\n", p1));
-		init_atten = p1;
+		if (p2 == 0) /* absolute value */
+			init_atten = (short)p1;
+		else /* relative value */
+			init_atten = misc_modes[AWE_MD_ZERO_ATTEN] + (short)p1;
+		if (init_atten < 0) init_atten = 0;
 		for (i = 0; i < awe_max_voices; i++)
 			awe_set_voice_vol(i, FALSE);
 		break;
 
-	case _AWE_SET_GUS_BANK:
-		DEBUG(0,printk("AWE32: set gus bank %d\n", p1));
-		awe_gus_bank = p1;
+	case _AWE_CHN_PRESSURE:
+		cinfo->chan_press = p1;
+		p1 = p1 * misc_modes[AWE_MD_MOD_SENSE] / 1200;
+		FX_ADD(&cinfo->fx, AWE_FX_LFO1_PITCH, p1);
+		awe_voice_change(voice, awe_fx_fmmod);
+		FX_ADD(&cinfo->fx, AWE_FX_LFO2_PITCH, p1);
+		awe_voice_change(voice, awe_fx_fm2frq2);
 		break;
-		
-	 /* v0.3 stuffs */
+
 	case _AWE_CHANNEL_MODE:
 		DEBUG(0,printk("AWE32: channel mode = %d\n", p1));
-		awe_channel_mode = p1;
+		playing_mode = p1;
 		awe_reset(0);
 		break;
 
 	case _AWE_DRUM_CHANNELS:
 		DEBUG(0,printk("AWE32: drum flags = %x\n", p1));
-		drum_flags = p1;
+		drum_flags = *(unsigned int*)&event[4];
+		break;
+
+	case _AWE_MISC_MODE:
+		DEBUG(0,printk("AWE32: misc mode = %d %d\n", p1, p2));
+		if (p1 > AWE_MD_VERSION && p1 < AWE_MD_END)
+			misc_modes[p1] = p2;
+		break;
+
+	case _AWE_EQUALIZER:
+		awe_equalizer((int)p1, (int)p2);
 		break;
 
-	case _AWE_EXCLUSIVE_SOUND:
-		DEBUG(0,printk("AWE32: exclusive mode = %d\n", p1));
-		awe_exclusive_sound = p1;
-		break;
-
-	case _AWE_GET_CURRENT_MODE:
-		{
-			awe_mode_rec tmprec;
-			tmprec.base_addr = awe_base;
-			tmprec.mem_size = awe_mem_size / 2;
-			tmprec.max_voices = awe_max_voices;
-			tmprec.max_infos = AWE_MAX_INFOS;
-			tmprec.max_samples = AWE_MAX_SAMPLES;
-			tmprec.current_sf_id = current_sf_id;
-			tmprec.free_mem = free_mem_ptr;
-			tmprec.free_info = free_info;
-			tmprec.free_sample = free_sample;
-			tmprec.reverb_mode = reverb_mode;
-			tmprec.chorus_mode = chorus_mode;
-			tmprec.init_atten = init_atten;
-			tmprec.channel_mode = awe_channel_mode;
-			tmprec.gus_bank = awe_gus_bank;
-			tmprec.exclusive_sound = awe_exclusive_sound;
-			tmprec.drum_flags = drum_flags;
-			tmprec.debug_mode = debug_mode;
-			IOCTL_TO_USER(*(awe_mode_rec**)&event[4], 0, &tmprec, sizeof(tmprec));
-			break;
-		}
 	default:
 		DEBUG(0,printk("AWE32: hw control cmd=%d voice=%d\n", cmd, voice));
 		break;
@@ -2073,6 +2316,211 @@
 }
 
 
+/* voice pressure change */
+static void
+awe_aftertouch(int dev, int voice, int pressure)
+{
+	int note;
+
+	DEBUG(2,printk("AWE32: [after(%d) %d]\n", voice, pressure));
+	if (! voice_in_range(voice))
+		return;
+
+	switch (playing_mode) {
+	case AWE_PLAY_DIRECT:
+	case AWE_PLAY_INDIRECT:
+		awe_start_note(dev, voice, 255, pressure);
+		break;
+	case AWE_PLAY_MULTI2:
+		note = (voice_alloc->map[voice] & 0xff) - 1;
+		awe_start_note(dev, voice, note + 0x80, pressure);
+		break;
+	}
+}
+
+
+/* voice control change */
+static void
+awe_controller(int dev, int voice, int ctrl_num, int value)
+{
+	int i;
+	awe_chan_info *cinfo;
+
+	if (! voice_in_range(voice))
+		return;
+
+	if (playing_mode == AWE_PLAY_MULTI2) {
+		voice = voice_alloc->map[voice] >> 8;
+		if (voice < 0 || voice >= AWE_MAX_CHANNELS)
+			return;
+	}
+
+	cinfo = &channels[voice];
+
+	switch (ctrl_num) {
+	case CTL_BANK_SELECT: /* MIDI control #0 */
+		DEBUG(2,printk("AWE32: [bank(%d) %d]\n", voice, value));
+		cinfo->bank = value;
+		awe_set_instr(dev, voice, cinfo->instr);
+		break;
+
+	case CTL_MODWHEEL: /* MIDI control #1 */
+		DEBUG(2,printk("AWE32: [modwheel(%d) %d]\n", voice, value));
+		i = value * misc_modes[AWE_MD_MOD_SENSE] / 1200;
+		FX_ADD(&cinfo->fx, AWE_FX_LFO1_PITCH, i);
+		awe_voice_change(voice, awe_fx_fmmod);
+		FX_ADD(&cinfo->fx, AWE_FX_LFO2_PITCH, i);
+		awe_voice_change(voice, awe_fx_fm2frq2);
+		break;
+
+	case CTRL_PITCH_BENDER: /* SEQ1 V2 contorl */
+		DEBUG(2,printk("AWE32: [bend(%d) %d]\n", voice, value));
+		/* zero centered */
+		cinfo->bender = value;
+		awe_voice_change(voice, awe_set_voice_pitch);
+		break;
+
+	case CTRL_PITCH_BENDER_RANGE: /* SEQ1 V2 control */
+		DEBUG(2,printk("AWE32: [range(%d) %d]\n", voice, value));
+		/* value = sense x 100 */
+		cinfo->bender_range = value;
+		/* no audible pitch change yet.. */
+		break;
+
+	case CTL_EXPRESSION: /* MIDI control #11 */
+		if (SINGLE_LAYER_MODE())
+			value /= 128;
+	case CTRL_EXPRESSION: /* SEQ1 V2 control */
+		DEBUG(2,printk("AWE32: [expr(%d) %d]\n", voice, value));
+		/* 0 - 127 */
+		cinfo->expression_vol = value;
+		awe_voice_change(voice, awe_set_voice_vol);
+		break;
+
+	case CTL_PAN:	/* MIDI control #10 */
+		DEBUG(2,printk("AWE32: [pan(%d) %d]\n", voice, value));
+		/* (0-127) -> signed 8bit */
+		cinfo->panning = value * 2 - 128;
+		if (misc_modes[AWE_MD_REALTIME_PAN])
+			awe_voice_change(voice, awe_set_pan);
+		break;
+
+	case CTL_MAIN_VOLUME:	/* MIDI control #7 */
+		if (SINGLE_LAYER_MODE())
+			value = (value * 100) / 16383;
+	case CTRL_MAIN_VOLUME:	/* SEQ1 V2 control */
+		DEBUG(2,printk("AWE32: [mainvol(%d) %d]\n", voice, value));
+		/* 0 - 127 */
+		cinfo->main_vol = value;
+		awe_voice_change(voice, awe_set_voice_vol);
+		break;
+
+	case CTL_EXT_EFF_DEPTH: /* reverb effects: 0-127 */
+		DEBUG(2,printk("AWE32: [reverb(%d) %d]\n", voice, value));
+		FX_SET(&cinfo->fx, AWE_FX_REVERB, value * 2);
+		break;
+
+	case CTL_CHORUS_DEPTH: /* chorus effects: 0-127 */
+		DEBUG(2,printk("AWE32: [chorus(%d) %d]\n", voice, value));
+		FX_SET(&cinfo->fx, AWE_FX_CHORUS, value * 2);
+		break;
+
+#ifdef AWE_ACCEPT_ALL_SOUNDS_CONTROLL
+	case 120:  /* all sounds off */
+		awe_note_off_all(FALSE);
+		break;
+	case 123:  /* all notes off */
+		awe_note_off_all(TRUE);
+		break;
+#endif
+
+	case CTL_SUSTAIN: /* MIDI control #64 */
+		cinfo->sustained = value;
+		if (value != 127)
+			awe_voice_change(voice, awe_sustain_off);
+		break;
+
+	case CTL_SOSTENUTO: /* MIDI control #66 */
+		if (value == 127)
+			awe_voice_change(voice, awe_sostenuto_on);
+		else
+			awe_voice_change(voice, awe_sustain_off);
+		break;
+
+	default:
+		DEBUG(0,printk("AWE32: [control(%d) ctrl=%d val=%d]\n",
+			   voice, ctrl_num, value));
+		break;
+	}
+}
+
+
+/* voice pan change (value = -128 - 127) */
+static void
+awe_panning(int dev, int voice, int value)
+{
+	awe_chan_info *cinfo;
+
+	if (! voice_in_range(voice))
+		return;
+
+	if (playing_mode == AWE_PLAY_MULTI2) {
+		voice = voice_alloc->map[voice] >> 8;
+		if (voice < 0 || voice >= AWE_MAX_CHANNELS)
+			return;
+	}
+
+	cinfo = &channels[voice];
+	cinfo->panning = value;
+	DEBUG(2,printk("AWE32: [pan(%d) %d]\n", voice, cinfo->panning));
+	if (misc_modes[AWE_MD_REALTIME_PAN])
+		awe_voice_change(voice, awe_set_pan);
+}
+
+
+/* volume mode change */
+static void
+awe_volume_method(int dev, int mode)
+{
+	/* not impremented */
+	DEBUG(0,printk("AWE32: [volmethod mode=%d]\n", mode));
+}
+
+
+#ifndef AWE_NO_PATCHMGR
+/* patch manager */
+static int
+awe_patchmgr(int dev, struct patmgr_info *rec)
+{
+	printk("AWE32 Warning: patch manager control not supported\n");
+	return 0;
+}
+#endif
+
+
+/* pitch wheel change: 0-16384 */
+static void
+awe_bender(int dev, int voice, int value)
+{
+	awe_chan_info *cinfo;
+
+	if (! voice_in_range(voice))
+		return;
+
+	if (playing_mode == AWE_PLAY_MULTI2) {
+		voice = voice_alloc->map[voice] >> 8;
+		if (voice < 0 || voice >= AWE_MAX_CHANNELS)
+			return;
+	}
+
+	/* convert to zero centered value */
+	cinfo = &channels[voice];
+	cinfo->bender = value - 8192;
+	DEBUG(2,printk("AWE32: [bend(%d) %d]\n", voice, cinfo->bender));
+	awe_voice_change(voice, awe_set_voice_pitch);
+}
+
+
 /*----------------------------------------------------------------
  * load a sound patch:
  *   three types of patches are accepted: AWE, GUS, and SYSEX.
@@ -2094,12 +2542,12 @@
 		/* no system exclusive message supported yet */
 		return 0;
 	} else if (format != AWE_PATCH) {
-		FATALERR(printk("AWE32 Error: Invalid patch format (key) 0x%x\n", format));
+		printk("AWE32 Error: Invalid patch format (key) 0x%x\n", format);
 		return RET_ERROR(EINVAL);
 	}
 	
 	if (count < AWE_PATCH_INFO_SIZE) {
-		FATALERR(printk("AWE32 Error: Patch header too short\n"));
+		printk("AWE32 Error: Patch header too short\n");
 		return RET_ERROR(EINVAL);
 	}
 	COPY_FROM_USER(((char*)&patch) + offs, addr, offs, 
@@ -2107,27 +2555,43 @@
 
 	count -= AWE_PATCH_INFO_SIZE;
 	if (count < patch.len) {
-		FATALERR(printk("AWE32 Warning: Patch record too short (%d<%d)\n",
-		       count, (int)patch.len));
-		patch.len = count;
+		printk("AWE32: sample: Patch record too short (%d<%d)\n",
+		       count, patch.len);
+		return RET_ERROR(EINVAL);
 	}
 	
 	switch (patch.type) {
 	case AWE_LOAD_INFO:
-		rc = awe_load_info(&patch, addr);
+		rc = awe_load_info(&patch, addr, count);
 		break;
-
 	case AWE_LOAD_DATA:
-		rc = awe_load_data(&patch, addr);
-		/*
-		if (!pmgr_flag && rc == 0)
-			pmgr_inform(dev, PM_E_PATCH_LOADED, instr, free_sample, 0, 0);
-		*/
+		rc = awe_load_data(&patch, addr, count);
+		break;
+	case AWE_OPEN_PATCH:
+		rc = awe_open_patch(&patch, addr, count);
+		break;
+	case AWE_CLOSE_PATCH:
+		rc = awe_close_patch(&patch, addr, count);
+		break;
+	case AWE_UNLOAD_PATCH:
+		rc = awe_unload_patch(&patch, addr, count);
+		break;
+	case AWE_REPLACE_DATA:
+		rc = awe_replace_data(&patch, addr, count);
+		break;
+	case AWE_MAP_PRESET:
+		rc = awe_load_map(&patch, addr, count);
+		break;
+	case AWE_LOAD_CHORUS_FX:
+		rc = awe_load_chorus_fx(&patch, addr, count);
+		break;
+	case AWE_LOAD_REVERB_FX:
+		rc = awe_load_reverb_fx(&patch, addr, count);
 		break;
 
 	default:
-		FATALERR(printk("AWE32 Error: unknown patch format type %d\n",
-		       patch.type));
+		printk("AWE32 Error: unknown patch format type %d\n",
+		       patch.type);
 		rc = RET_ERROR(EINVAL);
 	}
 
@@ -2135,140 +2599,384 @@
 }
 
 
+/* create an sflist record */
+static int
+awe_create_sf(int type, char *name)
+{
+	sf_list *rec;
+
+	/* terminate sounds */
+	awe_reset(0);
+	if (current_sf_id >= max_sfs) {
+		int newsize = max_sfs + AWE_MAX_SF_LISTS;
+		sf_list *newlist = my_realloc(sflists, sizeof(sf_list)*max_sfs,
+					      sizeof(sf_list)*newsize);
+		if (newlist == NULL)
+			return 1;
+		sflists = newlist;
+		max_sfs = newsize;
+	}
+	rec = &sflists[current_sf_id];
+	rec->sf_id = current_sf_id + 1;
+	rec->type = type;
+	if (current_sf_id == 0 || (type & AWE_PAT_LOCKED) != 0)
+		locked_sf_id = current_sf_id + 1;
+	/*
+	if (name)
+		MEMCPY(rec->name, name, AWE_PATCH_NAME_LEN);
+	else
+		BZERO(rec->name, AWE_PATCH_NAME_LEN);
+	 */
+	rec->num_info = awe_free_info();
+	rec->num_sample = awe_free_sample();
+	rec->mem_ptr = awe_free_mem_ptr();
+	rec->infos = -1;
+	rec->samples = -1;
+
+	current_sf_id++;
+	return 0;
+}
+
+
+/* open patch; create sf list and set opened flag */
+static int
+awe_open_patch(awe_patch_info *patch, const char *addr, int count)
+{
+	awe_open_parm parm;
+	COPY_FROM_USER(&parm, addr, AWE_PATCH_INFO_SIZE, sizeof(parm));
+	if (awe_create_sf(parm.type, parm.name)) {
+		printk("AWE32: can't open: failed to alloc new list\n");
+		return RET_ERROR(ENOSPC);
+	}
+	patch_opened = TRUE;
+	return current_sf_id;
+}
+
+/* check if the patch is already opened */
+static int
+check_patch_opened(int type, char *name)
+{
+	if (! patch_opened) {
+		if (awe_create_sf(type, name)) {
+			printk("AWE32: failed to alloc new list\n");
+			return RET_ERROR(ENOSPC);
+		}
+		patch_opened = TRUE;
+		return current_sf_id;
+	}
+	return current_sf_id;
+}
+
+/* close the patch; if no voice is loaded, remove the patch */
+static int
+awe_close_patch(awe_patch_info *patch, const char *addr, int count)
+{
+	if (patch_opened && current_sf_id > 0) {
+		/* if no voice is loaded, release the current patch */
+		if (sflists[current_sf_id-1].infos == -1)
+			awe_remove_samples(current_sf_id - 1);
+	}
+	patch_opened = 0;
+	return 0;
+}
+
+
+/* remove the latest patch */
+static int
+awe_unload_patch(awe_patch_info *patch, const char *addr, int count)
+{
+	if (current_sf_id > 0)
+		awe_remove_samples(current_sf_id - 1);
+	return 0;
+}
+
+/* allocate voice info list records */
+static int alloc_new_info(int nvoices)
+{
+	int newsize, free_info;
+	awe_voice_list *newlist;
+	free_info = awe_free_info();
+	if (free_info + nvoices >= max_infos) {
+		do {
+			newsize = max_infos + AWE_MAX_INFOS;
+		} while (free_info + nvoices >= newsize);
+		newlist = my_realloc(infos, sizeof(awe_voice_list)*max_infos,
+				     sizeof(awe_voice_list)*newsize);
+		if (newlist == NULL) {
+			printk("AWE32: can't alloc info table\n");
+			return RET_ERROR(ENOSPC);
+		}
+		infos = newlist;
+		max_infos = newsize;
+	}
+	return 0;
+}
+
+/* allocate sample info list records */
+static int alloc_new_sample(void)
+{
+	int newsize, free_sample;
+	awe_sample_list *newlist;
+	free_sample = awe_free_sample();
+	if (free_sample >= max_samples) {
+		newsize = max_samples + AWE_MAX_SAMPLES;
+		newlist = my_realloc(samples,
+				     sizeof(awe_sample_list)*max_samples,
+				     sizeof(awe_sample_list)*newsize);
+		if (newlist == NULL) {
+			printk("AWE32: can't alloc sample table\n");
+			return RET_ERROR(ENOSPC);
+		}
+		samples = newlist;
+		max_samples = newsize;
+	}
+	return 0;
+}
+
+/* load voice map */
+static int
+awe_load_map(awe_patch_info *patch, const char *addr, int count)
+{
+	awe_voice_map map;
+	awe_voice_list *rec;
+	int free_info;
+
+	if (check_patch_opened(AWE_PAT_TYPE_MAP, NULL) < 0)
+		return RET_ERROR(ENOSPC);
+	if (alloc_new_info(1) < 0)
+		return RET_ERROR(ENOSPC);
+
+	COPY_FROM_USER(&map, addr, AWE_PATCH_INFO_SIZE, sizeof(map));
+	
+	free_info = awe_free_info();
+	rec = &infos[free_info];
+	rec->bank = map.map_bank;
+	rec->instr = map.map_instr;
+	rec->type = V_ST_MAPPED;
+	rec->disabled = FALSE;
+	awe_init_voice_info(&rec->v);
+	if (map.map_key >= 0) {
+		rec->v.low = map.map_key;
+		rec->v.high = map.map_key;
+	}
+	rec->v.start = map.src_instr;
+	rec->v.end = map.src_bank;
+	rec->v.fixkey = map.src_key;
+	rec->v.sf_id = current_sf_id;
+	add_info_list(free_info);
+	add_sf_info(free_info);
+
+	return 0;
+}
+
 /* load voice information data */
 static int
-awe_load_info(awe_patch_info *patch, const char *addr)
+awe_load_info(awe_patch_info *patch, const char *addr, int count)
 {
-	awe_voice_list *rec, *curp;
-	long offset;
-	short i, nvoices;
-	unsigned char bank, instr;
+	int offset;
+	awe_voice_rec_hdr hdr;
+	int i;
 	int total_size;
 
-	if (patch->len < AWE_VOICE_REC_SIZE) {
-		FATALERR(printk("AWE32 Error: invalid patch info length\n"));
+	if (count < AWE_VOICE_REC_SIZE) {
+		printk("AWE32 Error: invalid patch info length\n");
 		return RET_ERROR(EINVAL);
 	}
 
 	offset = AWE_PATCH_INFO_SIZE;
-	GET_BYTE_FROM_USER(bank, addr, offset); offset++;
-	GET_BYTE_FROM_USER(instr, addr, offset); offset++;
-	GET_SHORT_FROM_USER(nvoices, addr, offset); offset+=2;
+	COPY_FROM_USER((char*)&hdr, addr, offset, AWE_VOICE_REC_SIZE);
+	offset += AWE_VOICE_REC_SIZE;
 
-	if (nvoices <= 0 || nvoices >= 100) {
-		FATALERR(printk("AWE32 Error: Illegal voice number %d\n", nvoices));
+	if (hdr.nvoices <= 0 || hdr.nvoices >= 100) {
+		printk("AWE32 Error: Illegal voice number %d\n", hdr.nvoices);
 		return RET_ERROR(EINVAL);
 	}
-	if (free_info + nvoices > AWE_MAX_INFOS) {
-		ERRMSG(printk("AWE32 Error: Too many voice informations\n"));
-		return RET_ERROR(ENOSPC);
+	total_size = AWE_VOICE_REC_SIZE + AWE_VOICE_INFO_SIZE * hdr.nvoices;
+	if (count < total_size) {
+		printk("AWE32 Error: patch length(%d) is smaller than nvoices(%d)\n",
+		       count, hdr.nvoices);
+		return RET_ERROR(EINVAL);
 	}
 
-	total_size = AWE_VOICE_REC_SIZE + AWE_VOICE_INFO_SIZE * nvoices;
-	if (patch->len < total_size) {
-		ERRMSG(printk("AWE32 Error: patch length(%d) is smaller than nvoices(%d)\n",
-		       (int)patch->len, nvoices));
-		return RET_ERROR(EINVAL);
+	if (check_patch_opened(AWE_PAT_TYPE_MISC, NULL) < 0)
+		return RET_ERROR(ENOSPC);
+
+#if 0 /* it looks like not so useful.. */
+	/* check if the same preset already exists in the info list */
+	for (i = sflists[current_sf_id-1].infos; i >= 0; i = infos[i].next) {
+		if (infos[i].disabled) continue;
+		if (infos[i].bank == hdr.bank && infos[i].instr == hdr.instr) {
+			/* in exclusive mode, do skip loading this */
+			if (hdr.write_mode == AWE_WR_EXCLUSIVE)
+				return 0;
+			/* in replace mode, disable the old data */
+			else if (hdr.write_mode == AWE_WR_REPLACE)
+				infos[i].disabled = TRUE;
+		}
 	}
+	if (hdr.write_mode == AWE_WR_REPLACE)
+		rebuild_preset_list();
+#endif
+
+	if (alloc_new_info(hdr.nvoices) < 0)
+		return RET_ERROR(ENOSPC);
 
-	curp = awe_search_instr(bank, instr);
-	for (i = 0; i < nvoices; i++) {
-		rec = &infos[free_info + i];
+	for (i = 0; i < hdr.nvoices; i++) {
+		int rec = awe_free_info();
 
-		rec->bank = bank;
-		rec->instr = instr;
-		if (i < nvoices - 1)
-			rec->next_instr = rec + 1;
-		else
-			rec->next_instr = curp;
-		rec->next_bank = NULL;
+		infos[rec].bank = hdr.bank;
+		infos[rec].instr = hdr.instr;
+		infos[rec].type = V_ST_NORMAL;
+		infos[rec].disabled = FALSE;
 
 		/* copy awe_voice_info parameters */
-		COPY_FROM_USER(&rec->v, addr, offset, AWE_VOICE_INFO_SIZE);
+		COPY_FROM_USER(&infos[rec].v, addr, offset, AWE_VOICE_INFO_SIZE);
 		offset += AWE_VOICE_INFO_SIZE;
-		rec->v.sf_id = current_sf_id;
-		if (rec->v.mode & AWE_MODE_INIT_PARM)
-			awe_init_voice_parm(&rec->v.parm);
-		awe_set_sample(&rec->v);
+		infos[rec].v.sf_id = current_sf_id;
+		if (infos[rec].v.mode & AWE_MODE_INIT_PARM)
+			awe_init_voice_parm(&infos[rec].v.parm);
+		awe_set_sample(&infos[rec].v);
+		add_info_list(rec);
+		add_sf_info(rec);
 	}
 
-	/* prepend to top of the list */
-	infos[free_info].next_bank = preset_table[instr];
-	preset_table[instr] = &infos[free_info];
-	free_info += nvoices;
-
 	return 0;
 }
 
-
 /* load wave sample data */
 static int
-awe_load_data(awe_patch_info *patch, const char *addr)
+awe_load_data(awe_patch_info *patch, const char *addr, int count)
 {
-	long offset;
-	int size;
-	int rc;
+	int offset, size;
+	int rc, free_sample;
+	awe_sample_info *rec;
 
-	if (free_sample >= AWE_MAX_SAMPLES) {
-		ERRMSG(printk("AWE32 Error: Sample table full\n"));
+	if (check_patch_opened(AWE_PAT_TYPE_MISC, NULL) < 0)
 		return RET_ERROR(ENOSPC);
-	}
 
-	size = (patch->len - AWE_SAMPLE_INFO_SIZE) / 2;
+	if (alloc_new_sample() < 0)
+		return RET_ERROR(ENOSPC);
+
+	free_sample = awe_free_sample();
+	rec = &samples[free_sample].v;
+
+	size = (count - AWE_SAMPLE_INFO_SIZE) / 2;
 	offset = AWE_PATCH_INFO_SIZE;
-	COPY_FROM_USER(&samples[free_sample], addr, offset,
-		       AWE_SAMPLE_INFO_SIZE);
+	COPY_FROM_USER(rec, addr, offset, AWE_SAMPLE_INFO_SIZE);
 	offset += AWE_SAMPLE_INFO_SIZE;
-	if (size != samples[free_sample].size) {
-		ERRMSG(printk("AWE32 Warning: sample size differed (%d != %d)\n",
-		       (int)samples[free_sample].size, (int)size));
-		samples[free_sample].size = size;
+	if (size != rec->size) {
+		printk("AWE32: load: sample size differed (%d != %d)\n",
+		       rec->size, size);
+		return RET_ERROR(EINVAL);
 	}
-	if (samples[free_sample].size > 0)
-		if ((rc = awe_write_wave_data(addr, offset, size)) != 0)
+	if (rec->size > 0)
+		if ((rc = awe_write_wave_data(addr, offset, rec, -1)) != 0)
 			return rc;
 
-	awe_check_loaded();
-	samples[free_sample].sf_id = current_sf_id;
+	rec->sf_id = current_sf_id;
+
+	add_sf_sample(free_sample);
 
-	free_sample++;
 	return 0;
 }
 
-/* check the other samples are already loaded */
-static void
-awe_check_loaded(void)
+
+/* replace wave sample data */
+static int
+awe_replace_data(awe_patch_info *patch, const char *addr, int count)
 {
-	if (!loaded_once) {
-		/* it's the first time */
-		if (current_sf_id == 1) {
-			last_sample = free_sample;
-			last_info = free_info;
-		}
-		current_sf_id++;
-		loaded_once = 1;
+	int offset;
+	int size;
+	int rc, i;
+	int channels;
+	awe_sample_info cursmp;
+	int save_mem_ptr;
+
+	if (! patch_opened) {
+		printk("AWE32: replace: patch not opened\n");
+		return RET_ERROR(EINVAL);
+	}
+
+	size = (count - AWE_SAMPLE_INFO_SIZE) / 2;
+	offset = AWE_PATCH_INFO_SIZE;
+	COPY_FROM_USER(&cursmp, addr, offset, AWE_SAMPLE_INFO_SIZE);
+	offset += AWE_SAMPLE_INFO_SIZE;
+	if (cursmp.size == 0 || size != cursmp.size) {
+		printk("AWE32: replace: illegal sample size (%d!=%d)\n",
+		       cursmp.size, size);
+		return RET_ERROR(EINVAL);
+	}
+	channels = patch->optarg;
+	if (channels <= 0 || channels > AWE_NORMAL_VOICES) {
+		printk("AWE32: replace: illegal channels %d\n", channels);
+		return RET_ERROR(EINVAL);
+	}
+
+	for (i = sflists[current_sf_id-1].samples;
+	     i >= 0; i = samples[i].next) {
+		if (samples[i].v.sample == cursmp.sample)
+			break;
+	}
+	if (i < 0) {
+		printk("AWE32: replace: cannot find existing sample data %d\n",
+		       cursmp.sample);
+		return RET_ERROR(EINVAL);
+	}
+		
+	if (samples[i].v.size != cursmp.size) {
+		printk("AWE32: replace: exiting size differed (%d!=%d)\n",
+		       samples[i].v.size, cursmp.size);
+		return RET_ERROR(EINVAL);
 	}
+
+	save_mem_ptr = awe_free_mem_ptr();
+	sflists[current_sf_id-1].mem_ptr = samples[i].v.start - awe_mem_start;
+	MEMCPY(&samples[i].v, &cursmp, sizeof(cursmp));
+	if ((rc = awe_write_wave_data(addr, offset, &samples[i].v, channels)) != 0)
+		return rc;
+	sflists[current_sf_id-1].mem_ptr = save_mem_ptr;
+	samples[i].v.sf_id = current_sf_id;
+
+	return 0;
 }
 
 
 /*----------------------------------------------------------------*/
 
 static const char *readbuf_addr;
-static long readbuf_offs;
+static int readbuf_offs;
 static int readbuf_flags;
+#ifdef __FreeBSD__
+static unsigned short *readbuf_loop;
+static int readbuf_loopstart, readbuf_loopend;
+#endif
 
 /* initialize read buffer */
-static void
-awe_init_readbuf(const char *addr, long offset, int size, int mode_flags)
+static int
+readbuf_init(const char *addr, int offset, awe_sample_info *sp)
 {
+#ifdef __FreeBSD__
+	readbuf_loop = NULL;
+	readbuf_loopstart = sp->loopstart;
+	readbuf_loopend = sp->loopend;
+	if (sp->mode_flags & (AWE_SAMPLE_BIDIR_LOOP|AWE_SAMPLE_REVERSE_LOOP)) {
+		int looplen = sp->loopend - sp->loopstart;
+		readbuf_loop = my_malloc(looplen * 2);
+		if (readbuf_loop == NULL) {
+			printk("AWE32: can't malloc temp buffer\n");
+			return RET_ERROR(ENOSPC);
+		}
+	}
+#endif
 	readbuf_addr = addr;
 	readbuf_offs = offset;
-	readbuf_flags = mode_flags;
+	readbuf_flags = sp->mode_flags;
+	return 0;
 }
 
 /* read directly from user buffer */
 static unsigned short
-awe_read_word(int pos)
+readbuf_word(int pos)
 {
 	unsigned short c;
 	/* read from user buffer */
@@ -2281,10 +2989,43 @@
 	}
 	if (readbuf_flags & AWE_SAMPLE_UNSIGNED)
 		c ^= 0x8000; /* unsigned -> signed */
+#ifdef __FreeBSD__
+	/* write on cache for reverse loop */
+	if (readbuf_flags & (AWE_SAMPLE_BIDIR_LOOP|AWE_SAMPLE_REVERSE_LOOP)) {
+		if (pos >= readbuf_loopstart && pos < readbuf_loopend)
+			readbuf_loop[pos - readbuf_loopstart] = c;
+	}
+#endif
 	return c;
 }
 
+#ifdef __FreeBSD__
+/* read from cache */
+static unsigned short
+readbuf_word_cache(int pos)
+{
+	if (pos >= readbuf_loopstart && pos < readbuf_loopend)
+		return readbuf_loop[pos - readbuf_loopstart];
+	return 0;
+}
+
+static void
+readbuf_end(void)
+{
+	if (readbuf_loop) {
+		my_free(readbuf_loop);
+	}
+	readbuf_loop = NULL;
+}
+
+#else
+
+#define readbuf_word_cache	readbuf_word
+#define readbuf_end()		/**/
+
+#endif
 
+/*----------------------------------------------------------------*/
 
 #define BLANK_LOOP_START	8
 #define BLANK_LOOP_END		40
@@ -2292,28 +3033,26 @@
 
 /* loading onto memory */
 static int 
-awe_write_wave_data(const char *addr, long offset, int size)
+awe_write_wave_data(const char *addr, int offset, awe_sample_info *sp, int channels)
 {
-	awe_sample_info *sp = &samples[free_sample];
-	int i, truesize;
+	int i, truesize, dram_offset;
 	int rc;
-	unsigned long csum1, csum2;
 
 	/* be sure loop points start < end */
 	if (sp->loopstart > sp->loopend) {
-		long tmp = sp->loopstart;
+		int tmp = sp->loopstart;
 		sp->loopstart = sp->loopend;
 		sp->loopend = tmp;
 	}
 
 	/* compute true data size to be loaded */
-	truesize = size;
+	truesize = sp->size;
 	if (sp->mode_flags & AWE_SAMPLE_BIDIR_LOOP)
 		truesize += sp->loopend - sp->loopstart;
 	if (sp->mode_flags & AWE_SAMPLE_NO_BLANK)
 		truesize += BLANK_LOOP_SIZE;
-	if (size > 0 && free_mem_ptr + truesize >= awe_mem_size/2) {
-		ERRMSG(printk("AWE32 Error: Sample memory full\n"));
+	if (awe_free_mem_ptr() + truesize >= awe_mem_size/2) {
+		printk("AWE32 Error: Sample memory full\n");
 		return RET_ERROR(ENOSPC);
 	}
 
@@ -2321,32 +3060,36 @@
 	sp->end -= sp->start;
 	sp->loopstart -= sp->start;
 	sp->loopend -= sp->start;
-	sp->size = truesize;
 
-	sp->start = free_mem_ptr + AWE_DRAM_OFFSET;
-	sp->end += free_mem_ptr + AWE_DRAM_OFFSET;
-	sp->loopstart += free_mem_ptr + AWE_DRAM_OFFSET;
-	sp->loopend += free_mem_ptr + AWE_DRAM_OFFSET;
+	dram_offset = awe_free_mem_ptr() + awe_mem_start;
+	sp->start = dram_offset;
+	sp->end += dram_offset;
+	sp->loopstart += dram_offset;
+	sp->loopend += dram_offset;
+
+	/* set the total size (store onto obsolete checksum value) */
+	if (sp->size == 0)
+		sp->checksum = 0;
+	else
+		sp->checksum = truesize;
 
-	if ((rc = awe_open_dram_for_write(free_mem_ptr)) != 0) {
+	if ((rc = awe_open_dram_for_write(dram_offset, channels)) != 0)
 		return rc;
-	}
 
-	awe_init_readbuf(addr, offset, size, sp->mode_flags);
-	csum1 = 0;
-	for (i = 0; i < size; i++) {
+	if (readbuf_init(addr, offset, sp) < 0)
+		return RET_ERROR(ENOSPC);
+
+	for (i = 0; i < sp->size; i++) {
 		unsigned short c;
-		c = awe_read_word(i);
-		csum1 += c;
+		c = readbuf_word(i);
 		awe_write_dram(c);
 		if (i == sp->loopend &&
 		    (sp->mode_flags & (AWE_SAMPLE_BIDIR_LOOP|AWE_SAMPLE_REVERSE_LOOP))) {
 			int looplen = sp->loopend - sp->loopstart;
 			/* copy reverse loop */
 			int k;
-			for (k = 0; k < looplen; k++) {
-				/* non-buffered data */
-				c = awe_read_word(i - k);
+			for (k = 1; k <= looplen; k++) {
+				c = readbuf_word_cache(i - k);
 				awe_write_dram(c);
 			}
 			if (sp->mode_flags & AWE_SAMPLE_BIDIR_LOOP) {
@@ -2357,6 +3100,7 @@
 			}
 		}
 	}
+	readbuf_end();
 
 	/* if no blank loop is attached in the sample, add it */
 	if (sp->mode_flags & AWE_SAMPLE_NO_BLANK) {
@@ -2366,40 +3110,14 @@
 			sp->loopstart = sp->end + BLANK_LOOP_START;
 			sp->loopend = sp->end + BLANK_LOOP_END;
 		}
-		sp->size += BLANK_LOOP_SIZE;
 	}
 
+	sflists[current_sf_id-1].mem_ptr += truesize;
 	awe_close_dram();
-	if (sp->checksum_flag) {
-#ifdef AWE_CHECKSUM_DATA
-		if (sp->checksum_flag != 2 && csum1 != sp->checksum) {
-			ERRMSG(printk("AWE32: [%d] checksum mismatch on data %x:%x\n",
-			       free_sample, (int)sp->checksum, (int)csum1));
-			return RET_ERROR(NO_DATA_ERR);
-		}
-#endif /* AWE_CHECKSUM_DATA */
-#ifdef AWE_CHECKSUM_MEMORY
-		if (awe_open_dram_for_read(free_mem_ptr) == 0) {
-			csum2 = 0;
-			for (i = 0; i < size; i++) {
-				unsigned short c;
-				c = awe_peek(AWE_SMLD);
-				csum2 += c;
-			}
-			awe_close_dram_for_read();
-			if (csum1 != csum2) {
-				ERRMSG(printk("AWE32: [%d] checksum mismatch on DRAM %x:%x\n",
-					      free_sample, (int)csum1, (int)csum2));
-				return RET_ERROR(NO_DATA_ERR);
-			}
-		}
-#endif /* AWE_CHECKSUM_MEMORY */
-	}
-	free_mem_ptr += sp->size;
 
-	/* re-initialize FM passthrough */
+	/* initialize FM */
 	awe_init_fm();
-	awe_tweak();
+	/*awe_tweak();*/
 
 	return 0;
 }
@@ -2435,91 +3153,89 @@
 awe_load_guspatch(const char *addr, int offs, int size, int pmgr_flag)
 {
 	struct patch_info patch;
-	awe_voice_list *rec, *curp;
-	long sizeof_patch;
-	int note;
+	awe_voice_info *rec;
+	awe_sample_info *smp;
+	int sizeof_patch;
+	int note, free_sample, free_info;
 	int rc;
 
-	sizeof_patch = (long)&patch.data[0] - (long)&patch; /* header size */
-	if (free_sample >= AWE_MAX_SAMPLES) {
-		ERRMSG(printk("AWE32 Error: Sample table full\n"));
-		return RET_ERROR(ENOSPC);
-	}
-	if (free_info >= AWE_MAX_INFOS) {
-		ERRMSG(printk("AWE32 Error: Too many voice informations\n"));
-		return RET_ERROR(ENOSPC);
-	}
+	sizeof_patch = (int)((long)&patch.data[0] - (long)&patch); /* header size */
 	if (size < sizeof_patch) {
-		ERRMSG(printk("AWE32 Error: Patch header too short\n"));
+		printk("AWE32 Error: Patch header too short\n");
 		return RET_ERROR(EINVAL);
 	}
 	COPY_FROM_USER(((char*)&patch) + offs, addr, offs, sizeof_patch - offs);
 	size -= sizeof_patch;
 	if (size < patch.len) {
-		FATALERR(printk("AWE32 Warning: Patch record too short (%d<%d)\n",
-		       size, (int)patch.len));
-		patch.len = size;
+		printk("AWE32 Warning: Patch record too short (%d<%d)\n",
+		       size, patch.len);
+		return RET_ERROR(EINVAL);
 	}
+	if (check_patch_opened(AWE_PAT_TYPE_GUS, NULL) < 0)
+		return RET_ERROR(ENOSPC);
+	if (alloc_new_sample() < 0)
+		return RET_ERROR(ENOSPC);
+	if (alloc_new_info(1))
+		return RET_ERROR(ENOSPC);
 
-	samples[free_sample].sf_id = 0;
-	samples[free_sample].sample = free_sample;
-	samples[free_sample].start = 0;
-	samples[free_sample].end = patch.len;
-	samples[free_sample].loopstart = patch.loop_start;
-	samples[free_sample].loopend = patch.loop_end + 1;
-	samples[free_sample].size = patch.len;
+	free_sample = awe_free_sample();
+	smp = &samples[free_sample].v;
+
+	smp->sample = free_sample;
+	smp->start = 0;
+	smp->end = patch.len;
+	smp->loopstart = patch.loop_start;
+	smp->loopend = patch.loop_end;
+	smp->size = patch.len;
 
 	/* set up mode flags */
-	samples[free_sample].mode_flags = 0;
+	smp->mode_flags = 0;
 	if (!(patch.mode & WAVE_16_BITS))
-		samples[free_sample].mode_flags |= AWE_SAMPLE_8BITS;
+		smp->mode_flags |= AWE_SAMPLE_8BITS;
 	if (patch.mode & WAVE_UNSIGNED)
-		samples[free_sample].mode_flags |= AWE_SAMPLE_UNSIGNED;
-	samples[free_sample].mode_flags |= AWE_SAMPLE_NO_BLANK;
+		smp->mode_flags |= AWE_SAMPLE_UNSIGNED;
+	smp->mode_flags |= AWE_SAMPLE_NO_BLANK;
 	if (!(patch.mode & (WAVE_LOOPING|WAVE_BIDIR_LOOP|WAVE_LOOP_BACK)))
-		samples[free_sample].mode_flags |= AWE_SAMPLE_SINGLESHOT;
+		smp->mode_flags |= AWE_SAMPLE_SINGLESHOT;
 	if (patch.mode & WAVE_BIDIR_LOOP)
-		samples[free_sample].mode_flags |= AWE_SAMPLE_BIDIR_LOOP;
+		smp->mode_flags |= AWE_SAMPLE_BIDIR_LOOP;
 	if (patch.mode & WAVE_LOOP_BACK)
-		samples[free_sample].mode_flags |= AWE_SAMPLE_REVERSE_LOOP;
+		smp->mode_flags |= AWE_SAMPLE_REVERSE_LOOP;
 
-	DEBUG(0,printk("AWE32: [sample %d mode %x]\n", patch.instr_no,
-		       samples[free_sample].mode_flags));
+	DEBUG(0,printk("AWE32: [sample %d mode %x]\n", patch.instr_no, smp->mode_flags));
 	if (patch.mode & WAVE_16_BITS) {
 		/* convert to word offsets */
-		samples[free_sample].size /= 2;
-		samples[free_sample].end /= 2;
-		samples[free_sample].loopstart /= 2;
-		samples[free_sample].loopend /= 2;
+		smp->size /= 2;
+		smp->end /= 2;
+		smp->loopstart /= 2;
+		smp->loopend /= 2;
 	}
-	samples[free_sample].checksum_flag = 0;
-	samples[free_sample].checksum = 0;
+	smp->checksum_flag = 0;
+	smp->checksum = 0;
 
-	if ((rc = awe_write_wave_data(addr, sizeof_patch,
-				      samples[free_sample].size)) != 0)
+	if ((rc = awe_write_wave_data(addr, sizeof_patch, smp, -1)) != 0)
 		return rc;
 
-	awe_check_loaded();
-	samples[free_sample].sf_id = current_sf_id;
-	free_sample++;
+	smp->sf_id = current_sf_id;
+	add_sf_sample(free_sample);
 
 	/* set up voice info */
-	rec = &infos[free_info];
-	awe_init_voice_info(&rec->v);
-	rec->v.sf_id = current_sf_id;
-	rec->v.sample = free_sample - 1; /* the last sample */
-	rec->v.rate_offset = calc_rate_offset(patch.base_freq);
+	free_info = awe_free_info();
+	rec = &infos[free_info].v;
+	awe_init_voice_info(rec);
+	rec->sample = free_sample; /* the last sample */
+	rec->rate_offset = calc_rate_offset(patch.base_freq);
 	note = freq_to_note(patch.base_note);
-	rec->v.root = note / 100;
-	rec->v.tune = -(note % 100);
-	rec->v.low = freq_to_note(patch.low_note) / 100;
-	rec->v.high = freq_to_note(patch.high_note) / 100;
+	rec->root = note / 100;
+	rec->tune = -(note % 100);
+	rec->low = freq_to_note(patch.low_note) / 100;
+	rec->high = freq_to_note(patch.high_note) / 100;
 	DEBUG(1,printk("AWE32: [gus base offset=%d, note=%d, range=%d-%d(%d-%d)]\n",
-		       rec->v.rate_offset, note,
-		       rec->v.low, rec->v.high,
+		       rec->rate_offset, note,
+		       rec->low, rec->high,
 	      patch.low_note, patch.high_note));
 	/* panning position; -128 - 127 => 0-127 */
-	rec->v.pan = (patch.panning + 128) / 2;
+	rec->pan = (patch.panning + 128) / 2;
 
 	/* detuning is ignored */
 	/* 6points volume envelope */
@@ -2542,367 +3258,215 @@
 		release += calc_gus_envelope_time
 			(patch.env_rate[5], patch.env_offset[4],
 			 patch.env_offset[5]);
-		rec->v.parm.volatkhld = (calc_parm_attack(attack) << 8) |
+		rec->parm.volatkhld = (calc_parm_attack(attack) << 8) |
 			calc_parm_hold(hold);
-		rec->v.parm.voldcysus = (calc_gus_sustain(patch.env_offset[2]) << 8) |
+		rec->parm.voldcysus = (calc_gus_sustain(patch.env_offset[2]) << 8) |
 			calc_parm_decay(decay);
-		rec->v.parm.volrelease = 0x8000 | calc_parm_decay(release);
+		rec->parm.volrelease = 0x8000 | calc_parm_decay(release);
 		DEBUG(2,printk("AWE32: [gusenv atk=%d, hld=%d, dcy=%d, rel=%d]\n", attack, hold, decay, release));
-		rec->v.attenuation = calc_gus_attenuation(patch.env_offset[0]);
+		rec->attenuation = calc_gus_attenuation(patch.env_offset[0]);
 	}
 
 	/* tremolo effect */
 	if (patch.mode & WAVE_TREMOLO) {
 		int rate = (patch.tremolo_rate * 1000 / 38) / 42;
-		rec->v.parm.tremfrq = ((patch.tremolo_depth / 2) << 8) | rate;
+		rec->parm.tremfrq = ((patch.tremolo_depth / 2) << 8) | rate;
 		DEBUG(2,printk("AWE32: [gusenv tremolo rate=%d, dep=%d, tremfrq=%x]\n",
 			       patch.tremolo_rate, patch.tremolo_depth,
-			       rec->v.parm.tremfrq));
+			       rec->parm.tremfrq));
 	}
 	/* vibrato effect */
 	if (patch.mode & WAVE_VIBRATO) {
 		int rate = (patch.vibrato_rate * 1000 / 38) / 42;
-		rec->v.parm.fm2frq2 = ((patch.vibrato_depth / 6) << 8) | rate;
+		rec->parm.fm2frq2 = ((patch.vibrato_depth / 6) << 8) | rate;
 		DEBUG(2,printk("AWE32: [gusenv vibrato rate=%d, dep=%d, tremfrq=%x]\n",
 			       patch.tremolo_rate, patch.tremolo_depth,
-			       rec->v.parm.tremfrq));
+			       rec->parm.tremfrq));
 	}
 	
 	/* scale_freq, scale_factor, volume, and fractions not implemented */
 
-	/* set the voice index */
-	awe_set_sample(&rec->v);
+	/* append to the tail of the list */
+	infos[free_info].bank = misc_modes[AWE_MD_GUS_BANK];
+	infos[free_info].instr = patch.instr_no;
+	infos[free_info].disabled = FALSE;
+	infos[free_info].type = V_ST_NORMAL;
+	infos[free_info].v.sf_id = current_sf_id;
+
+	add_info_list(free_info);
+	add_sf_info(free_info);
 
-	/* prepend to top of the list */
-	curp = awe_search_instr(awe_gus_bank, patch.instr_no);
-	rec->bank = awe_gus_bank;
-	rec->instr = patch.instr_no;
-	rec->next_instr = curp;
-	rec->next_bank = preset_table[rec->instr];
-	preset_table[rec->instr] = rec;
-	free_info++;
+	/* set the voice index */
+	awe_set_sample(rec);
 
 	return 0;
 }
 
 #endif  /* AWE_HAS_GUS_COMPATIBILITY */
 
-/*----------------------------------------------------------------*/
-
-/* remove samples with different sf_id from instrument list */
-static awe_voice_list *
-awe_get_removed_list(awe_voice_list *curp)
-{
-	awe_voice_list *lastp, **prevp;
-	int maxc;
-	lastp = curp;
-	prevp = &lastp;
-	for (maxc = AWE_MAX_INFOS;
-	     curp && maxc; curp = curp->next_instr, maxc--) {
-		if (curp->v.sf_id != 1)
-			*prevp = curp->next_instr;
-		else
-			prevp = &curp->next_instr;
-	}
-	return lastp;
-}
-
-
-/* remove last loaded samples */
-static void
-awe_remove_samples(void)
-{
-	awe_voice_list **prevp, *p, *nextp;
-	int maxc;
-	int i;
-
-	/* no sample is loaded yet */
-	if (last_sample == free_sample && last_info == free_info)
-		return;
-
-	/* only the primary samples are loaded */
-	if (current_sf_id <= 1)
-		return;
-
-	/* remove the records from preset table */
-	for (i = 0; i < AWE_MAX_PRESETS; i++) {
-		prevp = &preset_table[i];
-		for (maxc = AWE_MAX_INFOS, p = preset_table[i];
-		     p && maxc; p = nextp, maxc--) {
-			nextp = p->next_bank;
-			p = awe_get_removed_list(p);
-			if (p == NULL)
-				*prevp = nextp;
-			else {
-				*prevp = p;
-				prevp = &p->next_bank;
-			}
-		}
-	}
-
-	for (i = last_sample; i < free_sample; i++)
-		free_mem_ptr -= samples[i].size;
-
-	free_sample = last_sample;
-	free_info = last_info;
-	current_sf_id = 1;
-	loaded_once = 0;
-}
-
+/*----------------------------------------------------------------
+ * sample and voice list handlers
+ *----------------------------------------------------------------*/
 
-/* search the specified sample */
-static short
-awe_set_sample(awe_voice_info *vp)
+/* append this to the sf list */
+static void add_sf_info(int rec)
 {
-	int i;
-	for (i = 0; i < free_sample; i++) {
-		if (samples[i].sf_id == vp->sf_id &&
-		    samples[i].sample == vp->sample) {
-			/* set the actual sample offsets */
-			vp->start += samples[i].start;
-			vp->end += samples[i].end;
-			vp->loopstart += samples[i].loopstart;
-			vp->loopend += samples[i].loopend;
-			/* copy mode flags */
-			vp->mode = samples[i].mode_flags;
-			/* set index */
-			vp->index = i;
-			return i;
+	int sf_id = infos[rec].v.sf_id;
+	if (sf_id == 0) return;
+	sf_id--;
+	if (sflists[sf_id].infos < 0)
+		sflists[sf_id].infos = rec;
+	else {
+		int i, prev;
+		prev = sflists[sf_id].infos;
+		while ((i = infos[prev].next) >= 0)
+			prev = i;
+		infos[prev].next = rec;
+	}
+	infos[rec].next = -1;
+	sflists[sf_id].num_info++;
+}
+
+/* prepend this sample to sf list */
+static void add_sf_sample(int rec)
+{
+	int sf_id = samples[rec].v.sf_id;
+	if (sf_id == 0) return;
+	sf_id--;
+	samples[rec].next = sflists[sf_id].samples;
+	sflists[sf_id].samples = rec;
+	sflists[sf_id].num_sample++;
+}
+
+/* purge the old records which don't belong with the same file id */
+static void purge_old_list(int rec, int next)
+{
+	infos[rec].next_instr = next;
+	if (infos[rec].bank == AWE_DRUM_BANK) {
+		/* remove samples with the same note range */
+		int cur, *prevp = &infos[rec].next_instr;
+		int low = infos[rec].v.low;
+		int high = infos[rec].v.high;
+		for (cur = next; cur >= 0; cur = infos[cur].next_instr) {
+			if (infos[cur].v.low == low &&
+			    infos[cur].v.high == high &&
+			    infos[cur].v.sf_id != infos[rec].v.sf_id)
+				*prevp = infos[cur].next_instr;
+			prevp = &infos[cur].next_instr;
 		}
-	}
-	return -1;
-}
-
-
-/* voice pressure change */
-static void
-awe_aftertouch(int dev, int voice, int pressure)
-{
-	DEBUG(2,printk("AWE32: [after(%d) %d]\n", voice, pressure));
-	if (awe_channel_mode == 2) {
-		int note = (voice_alloc->map[voice] & 0xff) - 1;
-		awe_start_note(dev, voice, note + 0x80, pressure);
-
-	} else if (awe_channel_mode == 0) {
-		if (voice < 0 || voice >= awe_max_voices)
-			return;
-		voices[voice].velocity = pressure;
-		awe_set_voice_vol(voice, FALSE);
-	}
-}
-
-
-/* voice control change */
-static void
-awe_controller(int dev, int voice, int ctrl_num, int value)
-{
-	awe_chan_info *cinfo;
-
-	if (awe_channel_mode) {
-		if (awe_channel_mode == 2) /* get channel */
-			voice = voice_alloc->map[voice] >> 8;
-		if (voice < 0 || voice >= AWE_MAX_CHANNELS)
-			return;
-		cinfo = &channels[voice];
 	} else {
-		if (voice < 0 || voice >= awe_max_voices)
-			return;
-		cinfo = voices[voice].cinfo;
-	}
-
-	switch (ctrl_num) {
-	case CTL_BANK_SELECT: /* SEQ1 control */
-		DEBUG(2,printk("AWE32: [bank(%d) %d]\n", voice, value));
-		cinfo->bank = value;
-		awe_set_instr(dev, voice, cinfo->instr);
-		break;
-
-	case CTRL_PITCH_BENDER: /* SEQ1 V2 contorl */
-		DEBUG(2,printk("AWE32: [bend(%d) %d]\n", voice, value));
-		/* zero centered */
-		cinfo->bender = value;
-		awe_voice_change(voice, awe_set_voice_pitch);
-		break;
-
-	case CTRL_PITCH_BENDER_RANGE: /* SEQ1 V2 control */
-		DEBUG(2,printk("AWE32: [range(%d) %d]\n", voice, value));
-		/* value = sense x 100 */
-		cinfo->bender_range = value;
-		/* no audible pitch change yet.. */
-		break;
-
-	case CTL_EXPRESSION: /* SEQ1 control */
-		if (!awe_channel_mode)
-			value /= 128;
-	case CTRL_EXPRESSION: /* SEQ1 V2 control */
-		DEBUG(2,printk("AWE32: [expr(%d) %d]\n", voice, value));
-		/* 0 - 127 */
-		cinfo->expression_vol = value;
-		awe_voice_change(voice, awe_set_voice_vol);
-		break;
-
-	case CTL_PAN:	/* SEQ1 control */
-		DEBUG(2,printk("AWE32: [pan(%d) %d]\n", voice, value));
-		/* (0-127) -> signed 8bit */
-		cinfo->panning = value * 2 - 128;
-		awe_voice_change(voice, awe_set_pan);
-		break;
-
-	case CTL_MAIN_VOLUME:	/* SEQ1 control */
-		if (!awe_channel_mode)
-			value = (value * 100) / 16383;
-	case CTRL_MAIN_VOLUME:	/* SEQ1 V2 control */
-		DEBUG(2,printk("AWE32: [mainvol(%d) %d]\n", voice, value));
-		/* 0 - 127 */
-		cinfo->main_vol = value;
-		awe_voice_change(voice, awe_set_voice_vol);
-		break;
-
-	case CTL_EXT_EFF_DEPTH: /* reverb effects: 0-127 */
-		DEBUG(2,printk("AWE32: [reverb(%d) %d]\n", voice, value));
-		FX_SET(&cinfo->fx, AWE_FX_REVERB, value * 2);
-		break;
-
-	case CTL_CHORUS_DEPTH: /* chorus effects: 0-127 */
-		DEBUG(2,printk("AWE32: [chorus(%d) %d]\n", voice, value));
-		FX_SET(&cinfo->fx, AWE_FX_CHORUS, value * 2);
-		break;
-
-#ifdef AWE_ACCEPT_ALL_SOUNDS_CONTROLL
-	case 120:  /* all sounds off */
-		{int i; for (i = 0; i < AWE_NORMAL_VOICES; i++)
-			awe_terminate(i);
-		}
-		/*awe_reset(0);*/
-		break;
-	case 123:  /* all notes off */
-		{int i;
-		for (i = 0; i < awe_max_voices; i++)
-			awe_note_off(i);
-		}
-		break;
-#endif
-
-	case CTL_SUSTAIN: /* sustain the channel */
-		cinfo->sustained = value;
-		if (value == 0)
-			awe_voice_change(voice, awe_sustain_off);
-		break;
-
-	default:
-		DEBUG(0,printk("AWE32: [control(%d) ctrl=%d val=%d]\n",
-			   voice, ctrl_num, value));
-		break;
-	}
-}
-
-
-
-/* change the voice parameters */
-static void awe_voice_change(int voice, fx_affect_func func)
-{
-	int i; 
-	if (! awe_channel_mode) {
-		func(voice, FALSE);
-		return;
+		if (infos[next].v.sf_id != infos[rec].v.sf_id)
+			infos[rec].next_instr = -1;
 	}
-
-	for (i = 0; i < awe_max_voices; i++)
-		if (voices[i].ch == voice)
-			func(i, FALSE);
 }
 
-
-/* drop sustain */
-static void awe_sustain_off(int voice, int forced)
+/* prepend to top of the preset table */
+static void add_info_list(int rec)
 {
-	if (voices[voice].state == AWE_ST_SUSTAINED)
-		awe_note_off(voice);
-}
+	int *prevp, cur;
+	int instr = infos[rec].instr;
+	int bank = infos[rec].bank;
 
-
-/* voice pan change (value = -128 - 127) */
-static void
-awe_panning(int dev, int voice, int value)
-{
-	awe_chan_info *cinfo;
-	if (awe_channel_mode) {
-		if (voice < 0 || voice >= AWE_MAX_CHANNELS)
-			return;
-		cinfo = &channels[voice];
-	} else {
-		if (voice < 0 || voice >= awe_max_voices)
+	if (infos[rec].disabled)
+		return;
+
+	prevp = &preset_table[instr];
+	cur = *prevp;
+	while (cur >= 0) {
+		/* search the first record with the same bank number */
+		if (infos[cur].bank == bank) {
+			/* replace the list with the new record */
+			infos[rec].next_bank = infos[cur].next_bank;
+			*prevp = rec;
+			purge_old_list(rec, cur);
 			return;
-		cinfo = voices[voice].cinfo;
+		}
+		prevp = &infos[cur].next_bank;
+		cur = infos[cur].next_bank;
 	}
 
-	cinfo->panning = value;
-	DEBUG(2,printk("AWE32: [pan(%d) %d]\n", voice, cinfo->panning));
-	awe_voice_change(voice, awe_set_pan);
+	/* this is the first bank record.. just add this */
+	infos[rec].next_instr = -1;
+	infos[rec].next_bank = preset_table[instr];
+	preset_table[instr] = rec;
 }
 
-
-/* volume mode change */
+/* remove samples later than the specified sf_id */
 static void
-awe_volume_method(int dev, int mode)
+awe_remove_samples(int sf_id)
 {
-	/* not impremented */
-	DEBUG(0,printk("AWE32: [volmethod mode=%d]\n", mode));
-}
+	if (sf_id <= 0) {
+		awe_reset_samples();
+		return;
+	}
+	/* already removed? */
+	if (current_sf_id <= sf_id)
+		return;
 
+	current_sf_id = sf_id;
+	if (locked_sf_id > sf_id)
+		locked_sf_id = sf_id;
 
-#ifndef AWE_NO_PATCHMGR
-/* patch manager */
-static int
-awe_patchmgr(int dev, struct patmgr_info *rec)
-{
-	FATALERR(printk("AWE32 Warning: patch manager control not supported\n"));
-	return 0;
+	rebuild_preset_list();
 }
-#endif
 
+/* rebuild preset search list */
+static void rebuild_preset_list(void)
+{
+	int i, j;
 
-/* pitch wheel change: 0-16384 */
-static void
-awe_bender(int dev, int voice, int value)
+	for (i = 0; i < AWE_MAX_PRESETS; i++)
+		preset_table[i] = -1;
+
+	for (i = 0; i < current_sf_id; i++) {
+		for (j = sflists[i].infos; j >= 0; j = infos[j].next)
+			add_info_list(j);
+	}
+}
+
+/* search the specified sample */
+static short
+awe_set_sample(awe_voice_info *vp)
 {
-	awe_chan_info *cinfo;
-	if (awe_channel_mode) {
-		if (awe_channel_mode == 2)
-			voice = voice_alloc->map[voice] >> 8;
-		if (voice < 0 || voice >= AWE_MAX_CHANNELS)
-			return;
-		cinfo = &channels[voice];
-	} else {
-		if (voice < 0 || voice >= awe_max_voices)
-			return;
-		cinfo = voices[voice].cinfo;
+	int i;
+	vp->index = -1;
+	for (i = sflists[vp->sf_id-1].samples; i >= 0; i = samples[i].next) {
+		if (samples[i].v.sample == vp->sample) {
+			/* set the actual sample offsets */
+			vp->start += samples[i].v.start;
+			vp->end += samples[i].v.end;
+			vp->loopstart += samples[i].v.loopstart;
+			vp->loopend += samples[i].v.loopend;
+			/* copy mode flags */
+			vp->mode = samples[i].v.mode_flags;
+			/* set index */
+			vp->index = i;
+			return i;
+		}
 	}
-	/* convert to zero centered value */
-	cinfo->bender = value - 8192;
-	DEBUG(2,printk("AWE32: [bend(%d) %d]\n", voice, cinfo->bender));
-	awe_voice_change(voice, awe_set_voice_pitch);
+	return -1;
 }
 
-/* calculate the number of voices for this note & velocity */
+
+/*----------------------------------------------------------------
+ * voice allocation
+ *----------------------------------------------------------------*/
+
+/* look for all voices associated with the specified note & velocity */
 static int
-awe_search_multi_voices(awe_voice_list *rec, int note, int velocity, awe_voice_info **vlist)
+awe_search_multi_voices(int rec, int note, int velocity, awe_voice_info **vlist)
 {
-	int maxc;
 	int nvoices;
-	unsigned short sf_id;
 
-	sf_id = current_sf_id;
 	nvoices = 0;
-	for (maxc = AWE_MAX_INFOS;
-	     rec && maxc; rec = rec->next_instr, maxc--) {
-		if (rec->v.low <= note && note <= rec->v.high &&
-		    velocity >= rec->v.vellow && velocity <= rec->v.velhigh) {
-			if (nvoices == 0)
-				sf_id = rec->v.sf_id;
-			else if (rec->v.sf_id != sf_id)
-				continue;
-			vlist[nvoices] = &rec->v;
+	for (; rec >= 0; rec = infos[rec].next_instr) {
+		if (note >= infos[rec].v.low &&
+		    note <= infos[rec].v.high &&
+		    velocity >= infos[rec].v.vellow &&
+		    velocity <= infos[rec].v.velhigh) {
+			vlist[nvoices] = &infos[rec].v;
+			if (infos[rec].type == V_ST_MAPPED) /* mapper */
+				return -1;
 			nvoices++;
 			if (nvoices >= AWE_MAX_VOICES)
 				break;
@@ -2911,112 +3475,178 @@
 	return nvoices;	
 }
 
+/* store the voice list from the specified note and velocity.
+   if the preset is mapped, seek for the destination preset, and rewrite
+   the note number if necessary.
+   */
+static int
+really_alloc_voices(int vrec, int def_vrec, int *note, int velocity, awe_voice_info **vlist, int level)
+{
+	int nvoices;
+
+	nvoices = awe_search_multi_voices(vrec, *note, velocity, vlist);
+	if (nvoices == 0)
+		nvoices = awe_search_multi_voices(def_vrec, *note, velocity, vlist);
+	if (nvoices < 0) { /* mapping */
+		int preset = vlist[0]->start;
+		int bank = vlist[0]->end;
+		int key = vlist[0]->fixkey;
+		if (level > 5) {
+			printk("AWE32: too deep mapping level\n");
+			return 0;
+		}
+		vrec = awe_search_instr(bank, preset);
+		if (bank == AWE_DRUM_BANK)
+			def_vrec = awe_search_instr(bank, 0);
+		else
+			def_vrec = awe_search_instr(0, preset);
+		if (key >= 0)
+			*note = key;
+		return really_alloc_voices(vrec, def_vrec, note, velocity, vlist, level+1);
+	}
+
+	return nvoices;
+}
+
 /* allocate voices corresponding note and velocity; supports multiple insts. */
 static void
-awe_alloc_multi_voices(int ch, int note, int velocity)
+awe_alloc_multi_voices(int ch, int note, int velocity, int key)
 {
 	int i, v, nvoices;
 	awe_voice_info *vlist[AWE_MAX_VOICES];
 
-	if (channels[ch].vrec == NULL && channels[ch].def_vrec == NULL)
+	if (channels[ch].vrec < 0 && channels[ch].def_vrec < 0)
 		awe_set_instr(0, ch, channels[ch].instr);
 
-	nvoices = awe_search_multi_voices(channels[ch].vrec, note, velocity, vlist);
-	if (nvoices == 0)
-		nvoices = awe_search_multi_voices(channels[ch].def_vrec, note, velocity, vlist);
+	/* check the possible voices; note may be changeable if mapped */
+	nvoices = really_alloc_voices(channels[ch].vrec, channels[ch].def_vrec,
+				      &note, velocity, vlist, 0);
 
-	/* allocate the voices */
+	/* set the voices */
 	current_alloc_time++;
 	for (i = 0; i < nvoices; i++) {
 		v = awe_clear_voice();
-		voices[v].key = AWE_CHAN_KEY(ch, note);
+		voices[v].key = key;
 		voices[v].ch = ch;
+		voices[v].note = note;
+		voices[v].velocity = velocity;
 		voices[v].time = current_alloc_time;
 		voices[v].cinfo = &channels[ch];
 		voices[v].sample = vlist[i];
 		voices[v].state = AWE_ST_MARK;
+		voices[v].layer = nvoices - i - 1;  /* in reverse order */
 	}
 
 	/* clear the mark in allocated voices */
-	for (i = 0; i < awe_max_voices; i++)
+	for (i = 0; i < awe_max_voices; i++) {
 		if (voices[i].state == AWE_ST_MARK)
 			voices[i].state = AWE_ST_OFF;
+			
+	}
 }
 
 
-/* search an empty voice; used internally */
+/* search the best voice from the specified status condition */
 static int
-awe_clear_voice(void)
+search_best_voice(int condition)
 {
 	int i, time, best;
-
-	/* looking for the oldest empty voice */
 	best = -1;
-	time = 0x7fffffff;
+	time = current_alloc_time + 1;
 	for (i = 0; i < awe_max_voices; i++) {
-		if (voices[i].state == AWE_ST_OFF && voices[i].time < time) {
+		if ((voices[i].state & condition) &&
+		    (best < 0 || voices[i].time < time)) {
 			best = i;
 			time = voices[i].time;
 		}
 	}
+	/* clear voice */
 	if (best >= 0)
-		return best;
+		awe_terminate(best);
+
+	return best;
+}
+
+/* search an empty voice.
+   if no empty voice is found, at least terminate a voice
+   */
+static int
+awe_clear_voice(void)
+{
+	int best;
 
+	/* looking for the oldest empty voice */
+	if ((best = search_best_voice(AWE_ST_OFF)) >= 0)
+		return best;
+	if ((best = search_best_voice(AWE_ST_RELEASED)) >= 0)
+		return best;
 	/* looking for the oldest sustained voice */
-	time = 0x7fffffff;
-	for (i = 0; i < awe_max_voices; i++) {
-		if (voices[i].state == AWE_ST_SUSTAINED &&
-		    voices[i].time < time) {
-			best = i;
-			time = voices[i].time;
-		}
-	}
-	if (best >= 0) {
-		awe_note_off(best);
+	if ((best = search_best_voice(AWE_ST_SUSTAINED)) >= 0)
 		return best;
-	}
 
-	/* looking for the oldest voice not marked */
-	time = 0x7fffffff;
-	best = 0;
-	for (i = 0; i < awe_max_voices; i++) {
-		if (voices[i].state != AWE_ST_MARK && voices[i].time < time) {
-			best = i;
-			time = voices[i].time;
+#ifdef AWE_LOOKUP_MIDI_PRIORITY
+	if (MULTI_LAYER_MODE() && misc_modes[AWE_MD_CHN_PRIOR]) {
+		int ch = -1;
+		int time = current_alloc_time + 1;
+		int i;
+		/* looking for the voices from high channel (except drum ch) */
+		for (i = 0; i < awe_max_voices; i++) {
+			if (IS_DRUM_CHANNEL(voices[i].ch)) continue;
+			if (voices[i].ch < ch) continue;
+			if (voices[i].state != AWE_ST_MARK &&
+			    (voices[i].ch > ch || voices[i].time < time)) {
+				best = i;
+				time = voices[i].time;
+				ch = voices[i].ch;
+			}
 		}
 	}
-	/*awe_terminate(best);*/
-	awe_note_off(best);
-	return best;
+#endif
+	if (best < 0)
+		best = search_best_voice(~AWE_ST_MARK);
+
+	if (best >= 0)
+		return best;
+
+	return 0;
 }
 
 
-/* allocate a voice corresponding note and velocity; single instrument */
+/* search sample for the specified note & velocity and set it on the voice;
+ * note that voice is the voice index (not channel index)
+ */
 static void
 awe_alloc_one_voice(int voice, int note, int velocity)
 {
-	int nvoices;
+	int ch, nvoices;
 	awe_voice_info *vlist[AWE_MAX_VOICES];
 
-	if (voices[voice].cinfo->vrec == NULL && voices[voice].cinfo->def_vrec == NULL)
-		awe_set_instr(0, voice, voices[voice].cinfo->instr);
-
-	nvoices = awe_search_multi_voices(voices[voice].cinfo->vrec, note, velocity, vlist);
-	if (nvoices == 0)
-		nvoices = awe_search_multi_voices(voices[voice].cinfo->def_vrec, note, velocity, vlist);
+	ch = voices[voice].ch;
+	if (channels[ch].vrec < 0 && channels[ch].def_vrec < 0)
+		awe_set_instr(0, ch, channels[ch].instr);
 
+	nvoices = really_alloc_voices(voices[voice].cinfo->vrec,
+				      voices[voice].cinfo->def_vrec,
+				      &note, velocity, vlist, 0);
 	if (nvoices > 0) {
 		voices[voice].time = ++current_alloc_time;
 		voices[voice].sample = vlist[0]; /* use the first one */
+		voices[voice].layer = 0;
+		voices[voice].note = note;
+		voices[voice].velocity = velocity;
 	}
 }
 
 
+/*----------------------------------------------------------------
+ * sequencer2 functions
+ *----------------------------------------------------------------*/
+
 /* search an empty voice; used by sequencer2 */
 static int
 awe_alloc(int dev, int chn, int note, struct voice_alloc_info *alloc)
 {
-	awe_channel_mode = 2;
+	playing_mode = AWE_PLAY_MULTI2;
 	return awe_clear_voice();
 }
 
@@ -3053,6 +3683,75 @@
 }
 
 
+#ifdef CONFIG_AWE32_MIXER
+/*================================================================
+ * AWE32 mixer device control
+ *================================================================*/
+
+static int
+awe_mixer_ioctl(int dev, unsigned int cmd, caddr_t arg)
+{
+	int i, level;
+
+	if (((cmd >> 8) & 0xff) != 'M')
+		return RET_ERROR(EINVAL);
+
+	level = (int)IOCTL_IN(arg);
+	level = ((level & 0xff) + (level >> 8)) / 2;
+	DEBUG(0,printk("AWEMix: cmd=%x val=%d\n", cmd & 0xff, level));
+
+	if (IO_WRITE_CHECK(cmd)) {
+		switch (cmd & 0xff) {
+		case SOUND_MIXER_BASS:
+			awe_bass_level = level * 12 / 100;
+			if (awe_bass_level >= 12)
+				awe_bass_level = 11;
+			awe_equalizer(awe_bass_level, awe_treble_level);
+			break;
+		case SOUND_MIXER_TREBLE:
+			awe_treble_level = level * 12 / 100;
+			if (awe_treble_level >= 12)
+				awe_treble_level = 11;
+			awe_equalizer(awe_bass_level, awe_treble_level);
+			break;
+		case SOUND_MIXER_VOLUME:
+			level = level * 127 / 100;
+			if (level >= 128) level = 127;
+			init_atten = vol_table[level];
+			for (i = 0; i < awe_max_voices; i++)
+				awe_set_voice_vol(i, FALSE);
+			break;
+		}
+	}
+	switch (cmd & 0xff) {
+	case SOUND_MIXER_BASS:
+		level = awe_bass_level * 100 / 24;
+		level = (level << 8) | level;
+		break;
+	case SOUND_MIXER_TREBLE:
+		level = awe_treble_level * 100 / 24;
+		level = (level << 8) | level;
+		break;
+	case SOUND_MIXER_VOLUME:
+		for (i = 127; i > 0; i--) {
+			if (init_atten <= vol_table[i])
+				break;
+		}
+		level = i * 100 / 127;
+		level = (level << 8) | level;
+		break;
+	case SOUND_MIXER_DEVMASK:
+		level = SOUND_MASK_BASS|SOUND_MASK_TREBLE|SOUND_MASK_VOLUME;
+		break;
+	default:
+		level = 0;
+		break;
+	}
+	return IOCTL_OUT(arg, level);
+}
+#endif /* CONFIG_AWE32_MIXER */
+
+
 /*================================================================
  * initialization of AWE32
  *================================================================*/
@@ -3299,13 +3998,16 @@
 	awe_poke_dw(AWE_CSL(31), 0x00FFFFF8 |
 		    (DEF_FM_CHORUS_DEPTH << 24));
 	awe_poke_dw(AWE_PTRX(31), (DEF_FM_REVERB_DEPTH << 8));
-	awe_poke_dw(AWE_CPF(31), 0);
+	awe_poke_dw(AWE_CPF(31), 0x8000);
 	awe_poke_dw(AWE_CCCA(31), 0x00FFFFF3);
 
 	/* skew volume & cutoff */
 	awe_poke_dw(AWE_VTFT(30), 0x8000FFFF);
 	awe_poke_dw(AWE_VTFT(31), 0x8000FFFF);
 
+	voices[30].state = AWE_ST_FM;
+	voices[31].state = AWE_ST_FM;
+
 	/* change maximum channels to 30 */
 	awe_max_voices = AWE_NORMAL_VOICES;
 	awe_info.nr_voices = awe_max_voices;
@@ -3318,20 +4020,32 @@
 
 /* open DRAM write accessing mode */
 static int
-awe_open_dram_for_write(int offset)
+awe_open_dram_for_write(int offset, int channels)
 {
+	int vidx[AWE_NORMAL_VOICES];
 	int i;
 
+	if (channels < 0 || channels >= AWE_NORMAL_VOICES) {
+		channels = AWE_NORMAL_VOICES;
+		for (i = 0; i < AWE_NORMAL_VOICES; i++)
+			vidx[i] = i;
+	} else {
+		for (i = 0; i < channels; i++)
+			vidx[i] = awe_clear_voice();
+	}
+
 	/* use all channels for DMA transfer */
-	for (i = 0; i < AWE_NORMAL_VOICES; i++) {
-		awe_poke(AWE_DCYSUSV(i), 0x80);
-		awe_poke_dw(AWE_VTFT(i), 0);
-		awe_poke_dw(AWE_CVCF(i), 0);
-		awe_poke_dw(AWE_PTRX(i), 0x40000000);
-		awe_poke_dw(AWE_CPF(i), 0x40000000);
-		awe_poke_dw(AWE_PSST(i), 0);
-		awe_poke_dw(AWE_CSL(i), 0);
-		awe_poke_dw(AWE_CCCA(i), 0x06000000);
+	for (i = 0; i < channels; i++) {
+		if (vidx[i] < 0) continue;
+		awe_poke(AWE_DCYSUSV(vidx[i]), 0x80);
+		awe_poke_dw(AWE_VTFT(vidx[i]), 0);
+		awe_poke_dw(AWE_CVCF(vidx[i]), 0);
+		awe_poke_dw(AWE_PTRX(vidx[i]), 0x40000000);
+		awe_poke_dw(AWE_CPF(vidx[i]), 0x40000000);
+		awe_poke_dw(AWE_PSST(vidx[i]), 0);
+		awe_poke_dw(AWE_CSL(vidx[i]), 0);
+		awe_poke_dw(AWE_CCCA(vidx[i]), 0x06000000);
+		voices[vidx[i]].state = AWE_ST_DRAM;
 	}
 	/* point channels 31 & 32 to ROM samples for DRAM refresh */
 	awe_poke_dw(AWE_VTFT(30), 0);
@@ -3342,16 +4056,20 @@
 	awe_poke_dw(AWE_PSST(31), 0x1d8);
 	awe_poke_dw(AWE_CSL(31), 0x1e0);
 	awe_poke_dw(AWE_CCCA(31), 0x1d8);
+	voices[30].state = AWE_ST_FM;
+	voices[31].state = AWE_ST_FM;
 
 	/* if full bit is on, not ready to write on */
 	if (awe_peek_dw(AWE_SMALW) & 0x80000000) {
-		for (i = 0; i < AWE_NORMAL_VOICES; i++)
-			awe_poke_dw(AWE_CCCA(i), 0);
+		for (i = 0; i < channels; i++) {
+			awe_poke_dw(AWE_CCCA(vidx[i]), 0);
+			voices[i].state = AWE_ST_OFF;
+		}
 		return RET_ERROR(ENOSPC);
 	}
 
 	/* set address to write */
-	awe_poke_dw(AWE_SMALW, offset + AWE_DRAM_OFFSET);
+	awe_poke_dw(AWE_SMALW, offset);
 
 	return 0;
 }
@@ -3373,6 +4091,7 @@
 			awe_poke_dw(AWE_CCCA(i), 0x06000000);
 		else	   /* DMA read */
 			awe_poke_dw(AWE_CCCA(i), 0x04000000);
+		voices[i].state = AWE_ST_DRAM;
 	}
 }
 
@@ -3390,79 +4109,14 @@
 	}
 
 	for (i = 0; i < AWE_NORMAL_VOICES; i++) {
-		awe_poke_dw(AWE_CCCA(i), 0);
-		awe_poke(AWE_DCYSUSV(i), 0x807F);
-	}
-}
-
-
-#ifdef AWE_CHECKSUM_MEMORY
-/* open DRAM read accessing mode */
-static int
-awe_open_dram_for_read(int offset)
-{
-	int i;
-
-	/* use all channels for DMA transfer */
-	for (i = 0; i < AWE_NORMAL_VOICES; i++) {
-		awe_poke(AWE_DCYSUSV(i), 0x80);
-		awe_poke_dw(AWE_VTFT(i), 0);
-		awe_poke_dw(AWE_CVCF(i), 0);
-		awe_poke_dw(AWE_PTRX(i), 0x40000000);
-		awe_poke_dw(AWE_CPF(i), 0x40000000);
-		awe_poke_dw(AWE_PSST(i), 0);
-		awe_poke_dw(AWE_CSL(i), 0);
-		awe_poke_dw(AWE_CCCA(i), 0x04000000);
-	}
-	/* point channels 31 & 32 to ROM samples for DRAM refresh */
-	awe_poke_dw(AWE_VTFT(30), 0);
-	awe_poke_dw(AWE_PSST(30), 0x1d8);
-	awe_poke_dw(AWE_CSL(30), 0x1e0);
-	awe_poke_dw(AWE_CCCA(30), 0x1d8);
-	awe_poke_dw(AWE_VTFT(31), 0);
-	awe_poke_dw(AWE_PSST(31), 0x1d8);
-	awe_poke_dw(AWE_CSL(31), 0x1e0);
-	awe_poke_dw(AWE_CCCA(31), 0x1d8);
-
-	/* if empty flag is on, not ready to read */
-	if (awe_peek_dw(AWE_SMALR) & 0x80000000) {
-		for (i = 0; i < AWE_NORMAL_VOICES; i++)
+		if (voices[i].state == AWE_ST_DRAM) {
 			awe_poke_dw(AWE_CCCA(i), 0);
-		return RET_ERROR(ENOSPC);
-	}
-
-	/* set address to read */
-	awe_poke_dw(AWE_SMALR, offset + AWE_DRAM_OFFSET);
-	/* drop stale data */
-	awe_peek(AWE_SMLD);
-	return 0;
-}
-
-/* close dram access for read */
-static void
-awe_close_dram_for_read(void)
-{
-	int i;
-	/* wait until FULL bit in SMAxW register be false */
-	for (i = 0; i < 10000; i++) {
-		if (!(awe_peek_dw(AWE_SMALR) & 0x80000000))
-			break;
-		awe_wait(10);
-	}
-	for (i = 0; i < AWE_NORMAL_VOICES; i++) {
-		awe_poke_dw(AWE_CCCA(i), 0);
-		awe_poke(AWE_DCYSUSV(i), 0x807F);
+			awe_poke(AWE_DCYSUSV(i), 0x807F);
+			voices[i].state = AWE_ST_OFF;
+		}
 	}
 }
-#endif /* AWE_CHECKSUM_MEMORY */
-
 
-/* write a word data */
-static void
-awe_write_dram(unsigned short c)
-{
-	awe_poke(AWE_SMLD, c);
-}
 
 /*================================================================
  * detect presence of AWE32 and check memory size
@@ -3488,18 +4142,14 @@
 awe_detect(void)
 {
 	int base;
-#ifdef AWE_DEFAULT_BASE_ADDR
-	if (awe_detect_base(AWE_DEFAULT_BASE_ADDR))
-		return 1;
-#endif
 	if (awe_base == 0) {
 		for (base = 0x620; base <= 0x680; base += 0x20)
 			if (awe_detect_base(base))
 				return 1;
+		DEBUG(0,printk("AWE32 not found\n"));
+		return 0;
 	}
-	FATALERR(printk("AWE32 not found\n"));
-	awe_base = 0;
-	return 0;
+	return 1;
 }
 
 
@@ -3515,14 +4165,20 @@
 static int
 awe_check_dram(void)
 {
+	if (awe_mem_size > 0) {
+		awe_mem_size *= 1024; /* convert to Kbytes */
+		return awe_mem_size;
+	}
+
 	awe_open_dram_for_check();
 
+	awe_mem_size = 0;
+
 	/* set up unique two id numbers */
 	awe_poke_dw(AWE_SMALW, AWE_DRAM_OFFSET);
 	awe_poke(AWE_SMLD, UNIQUE_ID1);
 	awe_poke(AWE_SMLD, UNIQUE_ID2);
 
-	awe_mem_size = 0;
 	while (awe_mem_size < AWE_MAX_DRAM_SIZE) {
 		awe_wait(2);
 		/* read a data on the DRAM start address */
@@ -3540,14 +4196,17 @@
 		 */
 		awe_poke_dw(AWE_SMALW, AWE_DRAM_OFFSET + awe_mem_size*512L);
 		awe_poke(AWE_SMLD, UNIQUE_ID3);
+		awe_wait(2);
+		/* read a data on the just written DRAM address */
+		awe_poke_dw(AWE_SMALR, AWE_DRAM_OFFSET + awe_mem_size*512L);
+		awe_peek(AWE_SMLD); /* discard stale data  */
+		if (awe_peek(AWE_SMLD) != UNIQUE_ID3)
+			break;
 	}
 	awe_close_dram();
 
-	DEBUG(0,printk("AWE32: %d Kbytes memory detected\n", (int)awe_mem_size));
-#ifdef AWE_DEFAULT_MEM_SIZE
-	if (awe_mem_size == 0)
-		awe_mem_size = AWE_DEFAULT_MEM_SIZE;
-#endif
+	DEBUG(0,printk("AWE32: %d Kbytes memory detected\n", awe_mem_size));
+
 	/* convert to Kbytes */
 	awe_mem_size *= 1024;
 	return awe_mem_size;
@@ -3559,26 +4218,49 @@
  *================================================================*/
 
 /* 5 parameters for each chorus mode; 3 x 16bit, 2 x 32bit */
-static unsigned short chorus_parm[8][5] = {
-	{0xE600, 0x03F6, 0xBC2C ,0x0000, 0x006D}, /* chorus 1 */
-	{0xE608, 0x031A, 0xBC6E, 0x0000, 0x017C}, /* chorus 2 */
-	{0xE610, 0x031A, 0xBC84, 0x0000, 0x0083}, /* chorus 3 */
-	{0xE620, 0x0269, 0xBC6E, 0x0000, 0x017C}, /* chorus 4 */
-	{0xE680, 0x04D3, 0xBCA6, 0x0000, 0x005B}, /* feedback */
-	{0xE6E0, 0x044E, 0xBC37, 0x0000, 0x0026}, /* flanger */
-	{0xE600, 0x0B06, 0xBC00, 0xE000, 0x0083}, /* short delay */
-	{0xE6C0, 0x0B06, 0xBC00, 0xE000, 0x0083}, /* short delay + feedback */
+static char chorus_defined[AWE_CHORUS_NUMBERS];
+static awe_chorus_fx_rec chorus_parm[AWE_CHORUS_NUMBERS] = {
+	{0xE600, 0x03F6, 0xBC2C ,0x00000000, 0x0000006D}, /* chorus 1 */
+	{0xE608, 0x031A, 0xBC6E, 0x00000000, 0x0000017C}, /* chorus 2 */
+	{0xE610, 0x031A, 0xBC84, 0x00000000, 0x00000083}, /* chorus 3 */
+	{0xE620, 0x0269, 0xBC6E, 0x00000000, 0x0000017C}, /* chorus 4 */
+	{0xE680, 0x04D3, 0xBCA6, 0x00000000, 0x0000005B}, /* feedback */
+	{0xE6E0, 0x044E, 0xBC37, 0x00000000, 0x00000026}, /* flanger */
+	{0xE600, 0x0B06, 0xBC00, 0x0000E000, 0x00000083}, /* short delay */
+	{0xE6C0, 0x0B06, 0xBC00, 0x0000E000, 0x00000083}, /* short delay + feedback */
 };
 
-static void awe_set_chorus_mode(int effect)
+static int
+awe_load_chorus_fx(awe_patch_info *patch, const char *addr, int count)
+{
+	if (patch->optarg < AWE_CHORUS_PREDEFINED || patch->optarg >= AWE_CHORUS_NUMBERS) {
+		printk("AWE32 Error: illegal chorus mode %d for uploading\n", patch->optarg);
+		return RET_ERROR(EINVAL);
+	}
+	if (count < sizeof(awe_chorus_fx_rec)) {
+		printk("AWE32 Error: too short chorus fx parameters\n");
+		return RET_ERROR(EINVAL);
+	}
+	COPY_FROM_USER(&chorus_parm[patch->optarg], addr, AWE_PATCH_INFO_SIZE,
+		       sizeof(awe_chorus_fx_rec));
+	chorus_defined[patch->optarg] = TRUE;
+	return 0;
+}
+
+static void
+awe_set_chorus_mode(int effect)
 {
-	awe_poke(AWE_INIT3(9), chorus_parm[effect][0]);
-	awe_poke(AWE_INIT3(12), chorus_parm[effect][1]);
-	awe_poke(AWE_INIT4(3), chorus_parm[effect][2]);
-	awe_poke_dw(AWE_HWCF4, (unsigned long)chorus_parm[effect][3]);
-	awe_poke_dw(AWE_HWCF5, (unsigned long)chorus_parm[effect][4]);
+	if (effect < 0 || effect >= AWE_CHORUS_NUMBERS ||
+	    (effect >= AWE_CHORUS_PREDEFINED && !chorus_defined[effect]))
+		return;
+	awe_poke(AWE_INIT3(9), chorus_parm[effect].feedback);
+	awe_poke(AWE_INIT3(12), chorus_parm[effect].delay_offset);
+	awe_poke(AWE_INIT4(3), chorus_parm[effect].lfo_depth);
+	awe_poke_dw(AWE_HWCF4, chorus_parm[effect].delay);
+	awe_poke_dw(AWE_HWCF5, chorus_parm[effect].lfo_freq);
 	awe_poke_dw(AWE_HWCF6, 0x8000);
 	awe_poke_dw(AWE_HWCF7, 0x0000);
+	chorus_mode = effect;
 }
 
 /*----------------------------------------------------------------*/
@@ -3586,55 +4268,56 @@
 /* reverb mode settings; write the following 28 data of 16 bit length
  *   on the corresponding ports in the reverb_cmds array
  */
-static unsigned short reverb_parm[8][28] = {
-{  /* room 1 */
+static char reverb_defined[AWE_CHORUS_NUMBERS];
+static awe_reverb_fx_rec reverb_parm[AWE_REVERB_NUMBERS] = {
+{{  /* room 1 */
 	0xB488, 0xA450, 0x9550, 0x84B5, 0x383A, 0x3EB5, 0x72F4,
 	0x72A4, 0x7254, 0x7204, 0x7204, 0x7204, 0x4416, 0x4516,
 	0xA490, 0xA590, 0x842A, 0x852A, 0x842A, 0x852A, 0x8429,
 	0x8529, 0x8429, 0x8529, 0x8428, 0x8528, 0x8428, 0x8528,
-},
-{  /* room 2 */
+}},
+{{  /* room 2 */
 	0xB488, 0xA458, 0x9558, 0x84B5, 0x383A, 0x3EB5, 0x7284,
 	0x7254, 0x7224, 0x7224, 0x7254, 0x7284, 0x4448, 0x4548,
 	0xA440, 0xA540, 0x842A, 0x852A, 0x842A, 0x852A, 0x8429,
 	0x8529, 0x8429, 0x8529, 0x8428, 0x8528, 0x8428, 0x8528,
-},
-{  /* room 3 */
+}},
+{{  /* room 3 */
 	0xB488, 0xA460, 0x9560, 0x84B5, 0x383A, 0x3EB5, 0x7284,
 	0x7254, 0x7224, 0x7224, 0x7254, 0x7284, 0x4416, 0x4516,
 	0xA490, 0xA590, 0x842C, 0x852C, 0x842C, 0x852C, 0x842B,
 	0x852B, 0x842B, 0x852B, 0x842A, 0x852A, 0x842A, 0x852A,
-},
-{  /* hall 1 */
+}},
+{{  /* hall 1 */
 	0xB488, 0xA470, 0x9570, 0x84B5, 0x383A, 0x3EB5, 0x7284,
 	0x7254, 0x7224, 0x7224, 0x7254, 0x7284, 0x4448, 0x4548,
 	0xA440, 0xA540, 0x842B, 0x852B, 0x842B, 0x852B, 0x842A,
 	0x852A, 0x842A, 0x852A, 0x8429, 0x8529, 0x8429, 0x8529,
-},
-{  /* hall 2 */
+}},
+{{  /* hall 2 */
 	0xB488, 0xA470, 0x9570, 0x84B5, 0x383A, 0x3EB5, 0x7254,
 	0x7234, 0x7224, 0x7254, 0x7264, 0x7294, 0x44C3, 0x45C3,
 	0xA404, 0xA504, 0x842A, 0x852A, 0x842A, 0x852A, 0x8429,
 	0x8529, 0x8429, 0x8529, 0x8428, 0x8528, 0x8428, 0x8528,
-},
-{  /* plate */
+}},
+{{  /* plate */
 	0xB4FF, 0xA470, 0x9570, 0x84B5, 0x383A, 0x3EB5, 0x7234,
 	0x7234, 0x7234, 0x7234, 0x7234, 0x7234, 0x4448, 0x4548,
 	0xA440, 0xA540, 0x842A, 0x852A, 0x842A, 0x852A, 0x8429,
 	0x8529, 0x8429, 0x8529, 0x8428, 0x8528, 0x8428, 0x8528,
-},
-{  /* delay */
+}},
+{{  /* delay */
 	0xB4FF, 0xA470, 0x9500, 0x84B5, 0x333A, 0x39B5, 0x7204,
 	0x7204, 0x7204, 0x7204, 0x7204, 0x72F4, 0x4400, 0x4500,
 	0xA4FF, 0xA5FF, 0x8420, 0x8520, 0x8420, 0x8520, 0x8420,
 	0x8520, 0x8420, 0x8520, 0x8420, 0x8520, 0x8420, 0x8520,
-},
-{  /* panning delay */
+}},
+{{  /* panning delay */
 	0xB4FF, 0xA490, 0x9590, 0x8474, 0x333A, 0x39B5, 0x7204,
 	0x7204, 0x7204, 0x7204, 0x7204, 0x72F4, 0x4400, 0x4500,
 	0xA4FF, 0xA5FF, 0x8420, 0x8520, 0x8420, 0x8520, 0x8420,
 	0x8520, 0x8420, 0x8520, 0x8420, 0x8520, 0x8420, 0x8520,
-},
+}},
 };
 
 static struct ReverbCmdPair {
@@ -3649,13 +4332,97 @@
   {AWE_INIT2(0x09)}, {AWE_INIT2(0x0B)}, {AWE_INIT2(0x11)}, {AWE_INIT2(0x13)},
 };
 
+static int
+awe_load_reverb_fx(awe_patch_info *patch, const char *addr, int count)
+{
+	if (patch->optarg < AWE_REVERB_PREDEFINED || patch->optarg >= AWE_REVERB_NUMBERS) {
+		printk("AWE32 Error: illegal reverb mode %d for uploading\n", patch->optarg);
+		return RET_ERROR(EINVAL);
+	}
+	if (count < sizeof(awe_reverb_fx_rec)) {
+		printk("AWE32 Error: too short reverb fx parameters\n");
+		return RET_ERROR(EINVAL);
+	}
+	COPY_FROM_USER(&reverb_parm[patch->optarg], addr, AWE_PATCH_INFO_SIZE,
+		       sizeof(awe_reverb_fx_rec));
+	reverb_defined[patch->optarg] = TRUE;
+	return 0;
+}
 
-static void awe_set_reverb_mode(int effect)
+static void
+awe_set_reverb_mode(int effect)
 {
 	int i;
+	if (effect < 0 || effect >= AWE_REVERB_NUMBERS ||
+	    (effect >= AWE_REVERB_PREDEFINED && !reverb_defined[effect]))
+		return;
 	for (i = 0; i < 28; i++)
 		awe_poke(reverb_cmds[i].cmd, reverb_cmds[i].port,
-			 reverb_parm[effect][i]);
+			 reverb_parm[effect].parms[i]);
+	reverb_mode = effect;
+}
+
+/*================================================================
+ * treble/bass equalizer control
+ *================================================================*/
+
+static unsigned short bass_parm[12][3] = {
+	{0xD26A, 0xD36A, 0x0000}, /* -12 dB */
+	{0xD25B, 0xD35B, 0x0000}, /*  -8 */
+	{0xD24C, 0xD34C, 0x0000}, /*  -6 */
+	{0xD23D, 0xD33D, 0x0000}, /*  -4 */
+	{0xD21F, 0xD31F, 0x0000}, /*  -2 */
+	{0xC208, 0xC308, 0x0001}, /*   0 (HW default) */
+	{0xC219, 0xC319, 0x0001}, /*  +2 */
+	{0xC22A, 0xC32A, 0x0001}, /*  +4 */
+	{0xC24C, 0xC34C, 0x0001}, /*  +6 */
+	{0xC26E, 0xC36E, 0x0001}, /*  +8 */
+	{0xC248, 0xC348, 0x0002}, /* +10 */
+	{0xC26A, 0xC36A, 0x0002}, /* +12 dB */
+};
+
+static unsigned short treble_parm[12][9] = {
+	{0x821E, 0xC26A, 0x031E, 0xC36A, 0x021E, 0xD208, 0x831E, 0xD308, 0x0001}, /* -12 dB */
+	{0x821E, 0xC25B, 0x031E, 0xC35B, 0x021E, 0xD208, 0x831E, 0xD308, 0x0001},
+	{0x821E, 0xC24C, 0x031E, 0xC34C, 0x021E, 0xD208, 0x831E, 0xD308, 0x0001},
+	{0x821E, 0xC23D, 0x031E, 0xC33D, 0x021E, 0xD208, 0x831E, 0xD308, 0x0001},
+	{0x821E, 0xC21F, 0x031E, 0xC31F, 0x021E, 0xD208, 0x831E, 0xD308, 0x0001},
+	{0x821E, 0xD208, 0x031E, 0xD308, 0x021E, 0xD208, 0x831E, 0xD308, 0x0002},
+	{0x821E, 0xD208, 0x031E, 0xD308, 0x021D, 0xD219, 0x831D, 0xD319, 0x0002},
+	{0x821E, 0xD208, 0x031E, 0xD308, 0x021C, 0xD22A, 0x831C, 0xD32A, 0x0002},
+	{0x821E, 0xD208, 0x031E, 0xD308, 0x021A, 0xD24C, 0x831A, 0xD34C, 0x0002},
+	{0x821E, 0xD208, 0x031E, 0xD308, 0x0219, 0xD26E, 0x8319, 0xD36E, 0x0002}, /* +8 (HW default) */
+	{0x821D, 0xD219, 0x031D, 0xD319, 0x0219, 0xD26E, 0x8319, 0xD36E, 0x0002},
+	{0x821C, 0xD22A, 0x031C, 0xD32A, 0x0219, 0xD26E, 0x8319, 0xD36E, 0x0002}, /* +12 dB */
+};
+
+
+/*
+ * set Emu8000 digital equalizer; from 0 to 11 [-12dB - 12dB]
+ */
+static void
+awe_equalizer(int bass, int treble)
+{
+	unsigned short w;
+
+	if (bass < 0 || bass > 11 || treble < 0 || treble > 11)
+		return;
+	awe_bass_level = bass;
+	awe_treble_level = treble;
+	awe_poke(AWE_INIT4(0x01), bass_parm[bass][0]);
+	awe_poke(AWE_INIT4(0x11), bass_parm[bass][1]);
+	awe_poke(AWE_INIT3(0x11), treble_parm[treble][0]);
+	awe_poke(AWE_INIT3(0x13), treble_parm[treble][1]);
+	awe_poke(AWE_INIT3(0x1B), treble_parm[treble][2]);
+	awe_poke(AWE_INIT4(0x07), treble_parm[treble][3]);
+	awe_poke(AWE_INIT4(0x0B), treble_parm[treble][4]);
+	awe_poke(AWE_INIT4(0x0D), treble_parm[treble][5]);
+	awe_poke(AWE_INIT4(0x17), treble_parm[treble][6]);
+	awe_poke(AWE_INIT4(0x19), treble_parm[treble][7]);
+	w = bass_parm[bass][2] + treble_parm[treble][8];
+	awe_poke(AWE_INIT4(0x15), (unsigned short)(w + 0x0262));
+	awe_poke(AWE_INIT4(0x1D), (unsigned short)(w + 0x8362));
 }
+
 
 #endif /* CONFIG_AWE32_SYNTH */
diff -u --recursive --new-file v2.1.62/linux/drivers/sound/mad16.c linux/drivers/sound/mad16.c
--- v2.1.62/linux/drivers/sound/mad16.c	Tue Mar  4 10:25:25 1997
+++ linux/drivers/sound/mad16.c	Tue Sep 30 08:46:52 1997
@@ -60,7 +60,7 @@
 
 #include "sound_config.h"
 
-#if defined(CONFIG_MAD16)
+#ifdef CONFIG_MAD16
 
 #include "sb.h"
 
@@ -297,8 +297,7 @@
   if ((inb (hw_config->io_base + 3) & 0x3f) != 0x04 &&
       (inb (hw_config->io_base + 3) & 0x3f) != 0x00)
     {
-      DDB (printk ("No MSS signature detected on port 0x%x (0x%x)\n",
-		   hw_config->io_base, inb (hw_config->io_base + 3)));
+      DDB (printk ("No MSS signature detected on port 0x%x (0x%x)\n", hw_config->io_base, inb (hw_config->io_base + 3)));
       return 0;
     }
 
diff -u --recursive --new-file v2.1.62/linux/drivers/sound/maui.c linux/drivers/sound/maui.c
--- v2.1.62/linux/drivers/sound/maui.c	Wed Sep 24 20:05:47 1997
+++ linux/drivers/sound/maui.c	Tue Sep 30 08:46:52 1997
@@ -18,7 +18,7 @@
 
 #include "sound_config.h"
 
-#if defined(CONFIG_MAUI)
+#ifdef CONFIG_MAUI
 
 static int      maui_base = 0x330;
 
@@ -95,8 +95,8 @@
 	      maui_sleep_flag.opts |= WK_TIMEOUT;
 	  }
 	maui_sleep_flag.opts &= ~WK_SLEEP;
-      }
-      if (signal_pending(current))
+      };
+      if ((current->signal & ~current->blocked))
 	{
 	  return 0;
 	}
@@ -321,8 +321,7 @@
 
   if (count < header.len)
     {
-      printk ("Maui warning: Host command record too short (%d<%d)\n",
-	      count, (int) header.len);
+      printk ("Maui warning: Host command record too short (%d<%d)\n", count, (int) header.len);
       header.len = count;
     }
 
diff -u --recursive --new-file v2.1.62/linux/drivers/sound/midi_synth.c linux/drivers/sound/midi_synth.c
--- v2.1.62/linux/drivers/sound/midi_synth.c	Wed Sep 24 20:05:47 1997
+++ linux/drivers/sound/midi_synth.c	Tue Sep 30 08:46:52 1997
@@ -18,7 +18,7 @@
 
 #include "sound_config.h"
 
-#if defined(CONFIG_MIDI)
+#ifdef CONFIG_MIDI
 
 #define _MIDI_SYNTH_C_
 
@@ -89,7 +89,7 @@
       break;
 
     default:
-      /* printk ("MPU: Unknown midi channel message %02x\n", msg[0]); */
+      /* printk( "MPU: Unknown midi channel message %02x\n",  msg[0]); */
       ;
     }
 }
@@ -231,8 +231,7 @@
       break;			/* MST_SYSEX */
 
     default:
-      printk ("MIDI%d: Unexpected state %d (%02x)\n", orig_dev, inc->m_state,
-	      (int) data);
+      printk ("MIDI%d: Unexpected state %d (%02x)\n", orig_dev, inc->m_state, (int) data);
       inc->m_state = MST_INIT;
     }
 }
@@ -523,8 +522,7 @@
 
   if (count < sysex.len)
     {
-      printk ("MIDI Warning: Sysex record too short (%d<%d)\n",
-	      count, (int) sysex.len);
+      printk ("MIDI Warning: Sysex record too short (%d<%d)\n", count, (int) sysex.len);
       sysex.len = count;
     }
 
@@ -533,7 +531,7 @@
 
   sysex_sleep_flag.opts = WK_NONE;
 
-  for (i = 0; i < left && !signal_pending(current); i++)
+  for (i = 0; i < left && !(current->signal & ~current->blocked); i++)
     {
       unsigned char   data;
 
@@ -554,7 +552,7 @@
 	}
 
       while (!midi_devs[orig_dev]->outputc (orig_dev, (unsigned char) (data & 0xff)) &&
-	     !signal_pending(current))
+	     !(current->signal & ~current->blocked))
 
 	{
 	  unsigned long   tlimit;
diff -u --recursive --new-file v2.1.62/linux/drivers/sound/midibuf.c linux/drivers/sound/midibuf.c
--- v2.1.62/linux/drivers/sound/midibuf.c	Wed Sep 24 20:05:47 1997
+++ linux/drivers/sound/midibuf.c	Tue Sep 30 08:46:52 1997
@@ -16,7 +16,7 @@
 
 #include "sound_config.h"
 
-#if defined(CONFIG_MIDI)
+#ifdef CONFIG_MIDI
 
 /*
  * Don't make MAX_QUEUE_SIZE larger than 4000
@@ -93,7 +93,7 @@
    */
 
   if (midi_devs[dev]->buffer_status != NULL)
-    while (!signal_pending(current) &&
+    while (!(current->signal & ~current->blocked) &&
 	   midi_devs[dev]->buffer_status (dev))
 
       {
@@ -289,7 +289,7 @@
 						   * devices
 						 */
 
-      while (!signal_pending(current) &&
+      while (!(current->signal & ~current->blocked) &&
 	     DATA_AVAIL (midi_out_buf[dev]))
 
 	{
@@ -370,8 +370,8 @@
 		  midi_sleep_flag[dev].opts |= WK_TIMEOUT;
 	      }
 	    midi_sleep_flag[dev].opts &= ~WK_SLEEP;
-	  }
-	  if (signal_pending(current))
+	  };
+	  if ((current->signal & ~current->blocked))
 	    {
 	      restore_flags (flags);
 	      return -EINTR;
@@ -430,7 +430,7 @@
 	  }
 	input_sleep_flag[dev].opts &= ~WK_SLEEP;
       };
-      if (signal_pending(current))
+      if ((current->signal & ~current->blocked))
 	c = -EINTR;		/*
 				   * The user is getting restless
 				 */
diff -u --recursive --new-file v2.1.62/linux/drivers/sound/mpu401.c linux/drivers/sound/mpu401.c
--- v2.1.62/linux/drivers/sound/mpu401.c	Wed May 28 10:51:32 1997
+++ linux/drivers/sound/mpu401.c	Tue Sep 30 08:46:53 1997
@@ -55,7 +55,7 @@
 
 #define MBUF_MAX	10
 #define BUFTEST(dc) if (dc->m_ptr >= MBUF_MAX || dc->m_ptr < 0) \
-	{printk("MPU: Invalid buffer pointer %d/%d, s=%d\n", dc->m_ptr, dc->m_left, dc->m_state);dc->m_ptr--;}
+	{printk( "MPU: Invalid buffer pointer %d/%d, s=%d\n",  dc->m_ptr,  dc->m_left,  dc->m_state);dc->m_ptr--;}
     int             m_busy;
     unsigned char   m_buf[MBUF_MAX];
     int             m_ptr;
@@ -213,7 +213,7 @@
 	default:
 	  if (midic <= 0xef)
 	    {
-	      /* printk("mpu time: %d ", midic); */
+	      /* printk( "mpu time: %d ",  midic); */
 	      devc->m_state = ST_TIMED;
 	    }
 	  else
@@ -229,7 +229,7 @@
 
 	if (msg < 8)		/* Data byte */
 	  {
-	    /* printk("midi msg (running status) "); */
+	    /* printk( "midi msg (running status) "); */
 	    msg = ((int) (devc->last_status & 0xf0) >> 4);
 	    msg -= 8;
 	    devc->m_left = len_tab[msg] - 1;
@@ -252,15 +252,15 @@
 	    switch (midic)
 	      {
 	      case 0xf8:
-		/* printk("NOP "); */
+		/* printk( "NOP "); */
 		break;
 
 	      case 0xf9:
-		/* printk("meas end "); */
+		/* printk( "meas end "); */
 		break;
 
 	      case 0xfc:
-		/* printk("data end "); */
+		/* printk( "data end "); */
 		break;
 
 	      default:
@@ -270,7 +270,7 @@
 	else
 	  {
 	    devc->last_status = midic;
-	    /* printk ("midi msg "); */
+	    /* printk( "midi msg "); */
 	    msg -= 8;
 	    devc->m_left = len_tab[msg];
 
@@ -309,7 +309,7 @@
 	  break;
 
 	case 0xf6:
-	  /* printk("tune_request\n"); */
+	  /* printk( "tune_request\n"); */
 	  devc->m_state = ST_INIT;
 
 	  /*
@@ -342,7 +342,7 @@
 	  break;
 
 	case 0xff:
-	  /* printk("midi hard reset"); */
+	  /* printk( "midi hard reset"); */
 	  devc->m_state = ST_INIT;
 	  break;
 
@@ -659,7 +659,7 @@
   if (!ok)
     {
       restore_flags (flags);
-      /*       printk ("MPU: No ACK to command (0x%x)\n", (int) cmd->cmd); */
+      /*       printk( "MPU: No ACK to command (0x%x)\n",  (int) cmd->cmd); */
       return -EIO;
     }
 
@@ -693,7 +693,7 @@
 	if (!ok)
 	  {
 	    restore_flags (flags);
-	    /* printk ("MPU: No response(%d) to command (0x%x)\n", i, (int) cmd->cmd);  */
+	    /* printk( "MPU: No response(%d) to command (0x%x)\n",  i,  (int) cmd->cmd);  */
 	    return -EIO;
 	  }
       }
@@ -1274,8 +1274,7 @@
 
   if (check_region (hw_config->io_base, 2))
     {
-      printk ("\n\nmpu401.c: I/O port %x already in use\n\n",
-	      hw_config->io_base);
+      printk ("\n\nmpu401.c: I/O port %x already in use\n\n", hw_config->io_base);
       return 0;
     }
 
diff -u --recursive --new-file v2.1.62/linux/drivers/sound/opl3.c linux/drivers/sound/opl3.c
--- v2.1.62/linux/drivers/sound/opl3.c	Wed May 28 10:51:33 1997
+++ linux/drivers/sound/opl3.c	Tue Sep 30 08:46:53 1997
@@ -22,7 +22,7 @@
 
 #include "sound_config.h"
 
-#if defined(CONFIG_YM3812)
+#ifdef CONFIG_YM3812
 
 #include "opl3.h"
 
@@ -111,8 +111,8 @@
       {
 	struct sbi_instrument ins;
 
+	printk ("Warning: Obsolete ioctl(SNDCTL_FM_LOAD_INSTR) used. Fix the program.\n");
 	memcpy ((char *) &ins, (&((char *) arg)[0]), sizeof (ins));
-	printk("Warning: Obsolete ioctl(SNDCTL_FM_LOAD_INSTR) used. Fix the program.\n");
 
 	if (ins.channel < 0 || ins.channel >= SBFM_MAXINSTR)
 	  {
@@ -519,9 +519,7 @@
 
   if (instr->channel < 0)
     {
-      printk (
-	       "OPL3: Initializing voice %d with undefined instrument\n",
-	       voice);
+      printk ("OPL3: Initializing voice %d with undefined instrument\n", voice);
       return 0;
     }
 
diff -u --recursive --new-file v2.1.62/linux/drivers/sound/opl3sa.c linux/drivers/sound/opl3sa.c
--- v2.1.62/linux/drivers/sound/opl3sa.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/sound/opl3sa.c	Tue Sep 30 08:46:53 1997
@@ -0,0 +1,287 @@
+/*
+ * sound/Xopl3sa.c
+ *
+ * Low level driver for Yamaha YMF701B aka OPL3-SA chip
+ * 
+ */
+/*
+ * Copyright (C) by Hannu Savolainen 1993-1997
+ *
+ * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
+ * Version 2 (June 1991). See the "COPYING" file distributed with this software
+ * for more info.
+ */
+#include <linux/config.h>
+
+#undef  SB_OK
+
+#include "sound_config.h"
+#ifdef SB_OK
+#include "sb.h"
+static int      sb_initialized = 0;
+
+#endif
+
+#ifdef CONFIG_OPL3SA1
+
+static int      kilroy_was_here = 0;	/* Don't detect twice */
+static int      mpu_initialized = 0;
+
+static int     *opl3sa_osp = NULL;
+
+static unsigned char
+opl3sa_read (int addr)
+{
+  unsigned long   flags;
+  unsigned char   tmp;
+
+  save_flags (flags);
+  cli ();
+  outb ((0x1d), 0xf86);		/* password */
+  outb (((unsigned char) addr), 0xf86);		/* address */
+  tmp = inb (0xf87);		/* data */
+  restore_flags (flags);
+
+  return tmp;
+}
+
+static void
+opl3sa_write (int addr, int data)
+{
+  unsigned long   flags;
+
+  save_flags (flags);
+  cli ();
+  outb ((0x1d), 0xf86);		/* password */
+  outb (((unsigned char) addr), 0xf86);		/* address */
+  outb (((unsigned char) data), 0xf87);		/* data */
+  restore_flags (flags);
+}
+
+static int
+opl3sa_detect (void)
+{
+  int             tmp;
+
+  if (((tmp = opl3sa_read (0x01)) & 0xc4) != 0x04)
+    {
+      DDB (printk ("OPL3-SA detect error 1 (%x)\n", opl3sa_read (0x01)));
+      /* return 0; */
+    }
+
+/*
+ * Check that the password feature has any effect
+ */
+  if (inb (0xf87) == tmp)
+    {
+      DDB (printk ("OPL3-SA detect failed 2 (%x/%x)\n", tmp, inb (0xf87)));
+      return 0;
+    }
+
+  tmp = (opl3sa_read (0x04) & 0xe0) >> 5;
+
+  if (tmp != 0 && tmp != 1)
+    {
+      DDB (printk ("OPL3-SA detect failed 3 (%d)\n", tmp));
+      return 0;
+    }
+
+  DDB (printk ("OPL3-SA mode %x detected\n", tmp));
+
+  opl3sa_write (0x01, 0x00);	/* Disable MSS */
+  opl3sa_write (0x02, 0x00);	/* Disable SB */
+  opl3sa_write (0x03, 0x00);	/* Disable MPU */
+
+  return 1;
+}
+
+/*
+ *    Probe and attach routines for the Windows Sound System mode of
+ *     OPL3-SA
+ */
+
+int
+probe_opl3sa_wss (struct address_info *hw_config)
+{
+  int             ret;
+  unsigned char   tmp = 0x24;	/* WSS enable */
+
+  if (check_region (0xf86, 2))	/* Control port is busy */
+    return 0;
+  /*
+     * Check if the IO port returns valid signature. The original MS Sound
+     * system returns 0x04 while some cards (OPL3-SA for example)
+     * return 0x00.
+   */
+  if (check_region (hw_config->io_base, 8))
+    {
+      printk ("OPL3-SA: MSS I/O port conflict (%x)\n", hw_config->io_base);
+      return 0;
+    }
+
+  opl3sa_osp = hw_config->osp;
+
+  if (!opl3sa_detect ())
+    {
+      printk ("OSS: OPL3-SA chip not found\n");
+      return 0;
+    }
+
+  switch (hw_config->io_base)
+    {
+    case 0x530:
+      tmp |= 0x00;
+      break;
+    case 0xe80:
+      tmp |= 0x08;
+      break;
+    case 0xf40:
+      tmp |= 0x10;
+      break;
+    case 0x604:
+      tmp |= 0x18;
+      break;
+    default:
+      printk ("OSS: Unsupported OPL3-SA/WSS base %x\n", hw_config->io_base);
+      return 0;
+    }
+
+  opl3sa_write (0x01, tmp);	/* WSS setup register */
+  kilroy_was_here = 1;
+
+  ret = probe_ms_sound (hw_config);
+  if (ret)
+    request_region (0xf86, 2, "OPL3-SA");
+
+  return ret;
+}
+
+void
+attach_opl3sa_wss (struct address_info *hw_config)
+{
+  int             nm = num_mixers;
+
+  attach_ms_sound (hw_config);
+  if (num_mixers > nm)		/* A mixer was installed */
+    {
+      AD1848_REROUTE (SOUND_MIXER_LINE1, SOUND_MIXER_CD);
+      AD1848_REROUTE (SOUND_MIXER_LINE2, SOUND_MIXER_SYNTH);
+      AD1848_REROUTE (SOUND_MIXER_LINE3, SOUND_MIXER_LINE);
+    }
+}
+
+
+void
+attach_opl3sa_mpu (struct address_info *hw_config)
+{
+#if defined(CONFIG_UART401) && defined(CONFIG_MIDI)
+  hw_config->name = "OPL3-SA (MPU401)";
+  attach_uart401 (hw_config);
+#endif
+}
+
+int
+probe_opl3sa_mpu (struct address_info *hw_config)
+{
+#if defined(CONFIG_UART401) && defined(CONFIG_MIDI)
+  unsigned char   conf;
+  static char     irq_bits[] =
+  {-1, -1, -1, -1, -1, 1, -1, 2, -1, 3, 4};
+
+  if (!kilroy_was_here)
+    {
+      return 0;			/* OPL3-SA has not been detected earlier */
+    }
+
+  if (mpu_initialized)
+    {
+      DDB (printk ("OPL3-SA: MPU mode already initialized\n"));
+      return 0;
+    }
+
+  if (check_region (hw_config->io_base, 4))
+    {
+      printk ("OPL3-SA: MPU I/O port conflict (%x)\n", hw_config->io_base);
+      return 0;
+    }
+
+  if (hw_config->irq > 10)
+    {
+      printk ("OPL3-SA: Bad MPU IRQ %d\n", hw_config->irq);
+      return 0;
+    }
+
+  if (irq_bits[hw_config->irq] == -1)
+    {
+      printk ("OPL3-SA: Bad MPU IRQ %d\n", hw_config->irq);
+      return 0;
+    }
+
+  switch (hw_config->io_base)
+    {
+    case 0x330:
+      conf = 0x00;
+      break;
+    case 0x332:
+      conf = 0x20;
+      break;
+    case 0x334:
+      conf = 0x40;
+      break;
+    case 0x300:
+      conf = 0x60;
+      break;
+    default:
+      return 0;			/* Invalid port */
+    }
+
+  conf |= 0x83;			/* MPU & OPL3 (synth) & game port enable */
+  conf |= irq_bits[hw_config->irq] << 2;
+
+  opl3sa_write (0x03, conf);
+
+  mpu_initialized = 1;
+
+  return probe_uart401 (hw_config);
+#else
+  return 0;
+#endif
+}
+
+void
+unload_opl3sa_wss (struct address_info *hw_config)
+{
+  int             dma2 = hw_config->dma2;
+
+  if (dma2 == -1)
+    dma2 = hw_config->dma;
+
+  release_region (0xf86, 2);
+  release_region (hw_config->io_base, 4);
+
+  ad1848_unload (hw_config->io_base + 4,
+		 hw_config->irq,
+		 hw_config->dma,
+		 dma2,
+		 0);
+}
+
+void
+unload_opl3sa_mpu (struct address_info *hw_config)
+{
+#if defined(CONFIG_UART401) && defined(CONFIG_MIDI)
+  unload_uart401 (hw_config);
+#endif
+}
+#ifdef SB_OK
+void
+unload_opl3sa_sb (struct address_info *hw_config)
+{
+#ifdef CONFIG_SBDSP
+  sb_dsp_unload (hw_config);
+#endif
+}
+#endif
+
+
+#endif
diff -u --recursive --new-file v2.1.62/linux/drivers/sound/os.h linux/drivers/sound/os.h
--- v2.1.62/linux/drivers/sound/os.h	Wed May 28 10:51:33 1997
+++ linux/drivers/sound/os.h	Sun Nov  9 23:01:38 1997
@@ -1,21 +1,13 @@
-
-#ifdef __alpha__
-#else
-#endif
-
 #define ALLOW_SELECT
 #undef NO_INLINE_ASM
 #define SHORT_BANNERS
 #define MANUAL_PNP
-#undef DO_TIMINGS
+#undef  DO_TIMINGS
 
 #ifdef MODULE
 #define __NO_VERSION__
 #include <linux/module.h>
 #include <linux/version.h>
-#ifdef MODVERSIONS
-#include <linux/modversions.h>
-#endif
 #endif
 #if LINUX_VERSION_CODE > 131328
 #define LINUX21X
@@ -41,19 +33,14 @@
 #include <linux/poll.h>
 #include <linux/pci.h>
 #include <linux/bios32.h>
-#else
 #endif
 
 #include <linux/wrapper.h>
-
 #include <linux/soundcard.h>
 
-
 #define FALSE	0
 #define TRUE	1
 
-
-
 struct snd_wait {
 	  volatile int opts;
 	};
@@ -69,8 +56,6 @@
 extern caddr_t sound_mem_blocks[1024];
 extern int sound_mem_sizes[1024];
 extern int sound_nblocks;
-
-
 
 #undef PSEUDO_DMA_AUTOINIT
 #define ALLOW_BUFFER_MAPPING
diff -u --recursive --new-file v2.1.62/linux/drivers/sound/pas2_card.c linux/drivers/sound/pas2_card.c
--- v2.1.62/linux/drivers/sound/pas2_card.c	Wed May 28 10:51:33 1997
+++ linux/drivers/sound/pas2_card.c	Tue Sep 30 08:46:53 1997
@@ -7,7 +7,7 @@
 #include <linux/config.h>
 #include "sound_config.h"
 
-#if defined(CONFIG_PAS)
+#ifdef CONFIG_PAS
 
 static unsigned char dma_bits[] =
 {4, 1, 2, 3, 0, 5, 6, 7};
@@ -23,13 +23,13 @@
  * be relative to the given base -register
  */
 
-int             translate_code;
+int             translate_code = 0;
 static int      pas_intr_mask = 0;
 static int      pas_irq = 0;
 static int      pas_sb_base = 0;
 
 
-char            pas_model;
+char            pas_model = 0;
 static char    *pas_model_names[] =
 {"", "Pro AudioSpectrum+", "CDPC", "Pro AudioSpectrum 16", "Pro AudioSpectrum 16D"};
 
@@ -227,12 +227,10 @@
 	pas_sb_base = sb_config->io_base;
 
 	if (!sb_dma_bits[sb_config->dma])
-	  printk ("\n\nPAS16 Warning: Invalid SB DMA %d\n\n",
-		  sb_config->dma);
+	  printk ("\n\nPAS16 Warning: Invalid SB DMA %d\n\n", sb_config->dma);
 
 	if (!sb_irq_bits[sb_config->irq])
-	  printk ("\n\nPAS16 Warning: Invalid SB IRQ %d\n\n",
-		  sb_config->irq);
+	  printk ("\n\nPAS16 Warning: Invalid SB IRQ %d\n\n", sb_config->irq);
 
 	irq_dma = sb_dma_bits[sb_config->dma] |
 	  sb_irq_bits[sb_config->irq];
diff -u --recursive --new-file v2.1.62/linux/drivers/sound/pas2_midi.c linux/drivers/sound/pas2_midi.c
--- v2.1.62/linux/drivers/sound/pas2_midi.c	Wed May 28 10:51:33 1997
+++ linux/drivers/sound/pas2_midi.c	Tue Sep 30 08:46:53 1997
@@ -15,7 +15,8 @@
 
 #include "sound_config.h"
 
-#if defined(CONFIG_PAS) && defined(CONFIG_MIDI)
+#ifdef CONFIG_PAS
+#ifdef CONFIG_MIDI
 
 static int      midi_busy = 0, input_opened = 0;
 static int      my_dev;
@@ -53,7 +54,10 @@
   cli ();
 
   if ((err = pas_set_intr (0x10)) < 0)
-    return err;
+    {
+      restore_flags (flags);
+      return err;
+    }
 
   /*
    * Enable input available and output FIFO empty interrupts
@@ -286,4 +290,5 @@
   pas_write (stat, 0x1B88);	/* Acknowledge interrupts */
 }
 
+#endif
 #endif
diff -u --recursive --new-file v2.1.62/linux/drivers/sound/pas2_mixer.c linux/drivers/sound/pas2_mixer.c
--- v2.1.62/linux/drivers/sound/pas2_mixer.c	Tue Mar  4 10:25:25 1997
+++ linux/drivers/sound/pas2_mixer.c	Tue Sep 30 08:46:54 1997
@@ -15,7 +15,7 @@
 
 #include "sound_config.h"
 
-#if defined(CONFIG_PAS)
+#ifdef CONFIG_PAS
 
 #ifndef DEB
 #define DEB(what)		/* (what) */
diff -u --recursive --new-file v2.1.62/linux/drivers/sound/pas2_pcm.c linux/drivers/sound/pas2_pcm.c
--- v2.1.62/linux/drivers/sound/pas2_pcm.c	Wed May 28 10:51:33 1997
+++ linux/drivers/sound/pas2_pcm.c	Tue Sep 30 08:46:54 1997
@@ -12,7 +12,8 @@
 
 #include "sound_config.h"
 
-#if defined(CONFIG_PAS) && defined(CONFIG_AUDIO)
+#ifdef CONFIG_PAS
+#ifdef CONFIG_AUDIO
 
 #ifndef DEB
 #define DEB(WHAT)
@@ -459,4 +460,5 @@
     }
 }
 
+#endif
 #endif
diff -u --recursive --new-file v2.1.62/linux/drivers/sound/pss.c linux/drivers/sound/pss.c
--- v2.1.62/linux/drivers/sound/pss.c	Tue Mar  4 10:25:25 1997
+++ linux/drivers/sound/pss.c	Tue Sep 30 08:46:54 1997
@@ -15,7 +15,8 @@
 
 #include "sound_config.h"
 
-#if defined(CONFIG_PSS) && defined(CONFIG_AUDIO)
+#ifdef CONFIG_PSS
+#ifdef CONFIG_AUDIO
 
 /*
  * PSS registers.
@@ -123,7 +124,7 @@
   id = inw (REG (PSS_ID));
   if ((id >> 8) != 'E')
     {
-      /* printk ("No PSS signature detected at 0x%x (0x%x)\n", devc->base, id); */
+      /* printk( "No PSS signature detected at 0x%x (0x%x)\n",  devc->base,  id); */
       return 0;
     }
 
@@ -285,8 +286,7 @@
 	    break;
 	  else
 	    {
-	      printk ("\nPSS: Download timeout problems, byte %d=%d\n",
-		      count, size);
+	      printk ("\nPSS: Download timeout problems, byte %d=%d\n", count, size);
 	      return 0;
 	    }
 	}
@@ -323,7 +323,7 @@
 	return 0;
 
       val = inw (REG (PSS_DATA));
-      /* printk("<PSS: microcode version %d.%d loaded>", val/16, val % 16); */
+      /* printk( "<PSS: microcode version %d.%d loaded>",  val/16,  val % 16); */
     }
 
   return 1;
@@ -518,7 +518,7 @@
 static int
 pss_coproc_ioctl (void *dev_info, unsigned int cmd, caddr_t arg, int local)
 {
-  /* printk("PSS coproc ioctl %x %x %d\n", cmd, arg, local); */
+  /* printk( "PSS coproc ioctl %x %x %d\n",  cmd,  arg,  local); */
 
   switch (cmd)
     {
@@ -890,4 +890,5 @@
   unload_ms_sound (hw_config);
 }
 
+#endif
 #endif
diff -u --recursive --new-file v2.1.62/linux/drivers/sound/sb.h linux/drivers/sound/sb.h
--- v2.1.62/linux/drivers/sound/sb.h	Wed May 28 10:51:33 1997
+++ linux/drivers/sound/sb.h	Tue Sep 30 08:46:45 1997
@@ -1,3 +1,4 @@
+#ifdef CONFIG_SBDSP
 #define DSP_RESET	(devc->base + 0x6)
 #define DSP_READ	(devc->base + 0xA)
 #define DSP_WRITE	(devc->base + 0xC)
@@ -124,3 +125,4 @@
 void sb_midi_interrupt (sb_devc *devc);
 int ess_write (sb_devc *devc, unsigned char reg, unsigned char data);
 int ess_read (sb_devc *devc, unsigned char reg);
+#endif
diff -u --recursive --new-file v2.1.62/linux/drivers/sound/sb_audio.c linux/drivers/sound/sb_audio.c
--- v2.1.62/linux/drivers/sound/sb_audio.c	Tue Mar  4 10:25:25 1997
+++ linux/drivers/sound/sb_audio.c	Tue Sep 30 08:46:54 1997
@@ -15,7 +15,7 @@
 
 #include "sound_config.h"
 
-#if defined(CONFIG_SBDSP)
+#ifdef CONFIG_SBDSP
 
 #include "sb_mixer.h"
 #include "sb.h"
@@ -34,8 +34,9 @@
 
   if (devc->caps & SB_NO_RECORDING && mode & OPEN_READ)
     {
-      printk ("SB: Recording is not possible with this device\n");
-      return -EPERM;
+      printk ("Notice: Recording is not possible with /dev/dsp%d\n", dev);
+      if (mode == OPEN_READ)
+	return -EPERM;
     }
 
   save_flags (flags);
@@ -50,6 +51,7 @@
     {
       if (sound_open_dma (devc->dma16, "Sound Blaster 16 bit"))
 	{
+	  restore_flags (flags);
 	  return -EBUSY;
 	}
     }
@@ -542,8 +544,12 @@
     if (channels != devc->channels)
       {
 	devc->channels = channels;
-	if (devc->model == MDL_SBPRO)
-	  sbpro_audio_set_speed (dev, devc->speed);
+	if (devc->model == MDL_SBPRO && devc->channels == 2)
+	  {
+	    if (devc->speed > 22050)
+	      printk ("OSS: Application error. Wrong ioctl call order.\n");
+	    sbpro_audio_set_speed (dev, devc->speed);
+	  }
       }
   return devc->channels;
 }
diff -u --recursive --new-file v2.1.62/linux/drivers/sound/sb_card.c linux/drivers/sound/sb_card.c
--- v2.1.62/linux/drivers/sound/sb_card.c	Tue Mar  4 10:25:25 1997
+++ linux/drivers/sound/sb_card.c	Tue Sep 30 08:46:54 1997
@@ -15,7 +15,7 @@
 
 #include "sound_config.h"
 
-#if defined(CONFIG_SBDSP)
+#ifdef CONFIG_SBDSP
 
 #include "sb_mixer.h"
 #include "sb.h"
@@ -33,8 +33,7 @@
 {
   if (check_region (hw_config->io_base, 16))
     {
-      printk ("\n\nsb_dsp.c: I/O port %x already in use\n\n",
-	      hw_config->io_base);
+      printk ("\n\nsb_dsp.c: I/O port %x already in use\n\n", hw_config->io_base);
       return 0;
     }
 
diff -u --recursive --new-file v2.1.62/linux/drivers/sound/sb_common.c linux/drivers/sound/sb_common.c
--- v2.1.62/linux/drivers/sound/sb_common.c	Wed May 28 10:51:33 1997
+++ linux/drivers/sound/sb_common.c	Tue Sep 30 08:46:54 1997
@@ -15,7 +15,7 @@
 
 #include "sound_config.h"
 
-#if defined(CONFIG_SBDSP)
+#ifdef CONFIG_SBDSP
 
 #ifndef CONFIG_AUDIO
 #error You will need to configure the sound driver with CONFIG_AUDIO option.
@@ -52,6 +52,7 @@
 
 #endif
 
+
 int
 sb_dsp_command (sb_devc * devc, unsigned char val)
 {
@@ -170,7 +171,7 @@
 	break;
 
       default:
-	/* printk ("Sound Blaster: Unexpected interrupt\n"); */
+	/* printk( "Sound Blaster: Unexpected interrupt\n"); */
 	;
       }
 /*
@@ -184,12 +185,13 @@
     status = inb (DSP_DATA_AVL16);
 }
 
+
 int
 sb_dsp_reset (sb_devc * devc)
 {
   int             loopc;
 
-  DDB (printk ("Entered sb_dsp_reset()\n"));
+  DEB (printk ("Entered sb_dsp_reset()\n"));
 
   if (devc->model == MDL_ESS)
     outb ((3), DSP_RESET);	/* Reset FIFO too */
@@ -213,7 +215,7 @@
   if (devc->model == MDL_ESS)
     sb_dsp_command (devc, 0xc6);	/* Enable extended mode */
 
-  DDB (printk ("sb_dsp_reset() OK\n"));
+  DEB (printk ("sb_dsp_reset() OK\n"));
   return 1;
 }
 
@@ -435,6 +437,66 @@
   return 1;
 }
 
+static void
+relocate_ess1688 (sb_devc * devc)
+{
+  unsigned char   bits;
+
+  switch (devc->base)
+    {
+    case 0x220:
+      bits = 0x04;
+      break;
+    case 0x230:
+      bits = 0x05;
+      break;
+    case 0x240:
+      bits = 0x06;
+      break;
+    case 0x250:
+      bits = 0x07;
+      break;
+    default:
+      return;			/* Wrong port */
+    }
+
+  DDB (printk ("Doing ESS1688 address selection\n"));
+
+/*
+ * ES1688 supports two alternative ways for software address config.
+ * First try the so called Read-Sequence-Key method.
+ */
+
+  /* Reset the sequence logic */
+  inb (0x229);
+  inb (0x229);
+  inb (0x229);
+
+  /* Perform the read sequence */
+  inb (0x22b);
+  inb (0x229);
+  inb (0x22b);
+  inb (0x229);
+  inb (0x229);
+  inb (0x22b);
+  inb (0x229);
+
+  /* Select the base address by reading from it. Then probe using the port. */
+  inb (devc->base);
+  if (sb_dsp_reset (devc))	/* Bingo */
+    return;
+
+#if 0				/* This causes system lockups (Nokia 386/25 at least) */
+/*
+ * The last resort is the system control register method.
+ */
+
+  outb ((0x00), 0xfb);		/* 0xFB is the unlock register */
+  outb ((0x00), 0xe0);		/* Select index 0 */
+  outb ((bits), 0xe1);		/* Write the config bits */
+  outb ((0x00), 0xf9);		/* 0xFB is the lock register */
+#endif
+}
 
 static int
 ess_init (sb_devc * devc, struct address_info *hw_config)
@@ -608,6 +670,8 @@
     if (devc->major == 0 || (devc->major == 3 && devc->minor == 1))
       relocate_Jazz16 (devc, hw_config);
 
+  if (devc->major == 0 && (devc->type == MDL_ESS || devc->type == 0))
+    relocate_ess1688 (devc);
 
   if (!sb_dsp_reset (devc))
     {
@@ -654,8 +718,7 @@
 
   memcpy ((char *) detected_devc, (char *) devc, sizeof (sb_devc));
 
-  DDB (printk ("SB %d.%d detected OK (%x)\n", devc->major, devc->minor,
-	       hw_config->io_base));
+  DDB (printk ("SB %d.%d detected OK (%x)\n", devc->major, devc->minor, hw_config->io_base));
   return 1;
 }
 
@@ -663,7 +726,6 @@
 sb_dsp_init (struct address_info *hw_config)
 {
   sb_devc        *devc;
-  int             n;
   char            name[100];
   extern int      sb_be_quiet;
 
@@ -679,6 +741,7 @@
       return;
     }
 
+
   devc = detected_devc;
   detected_devc = NULL;
 
@@ -727,7 +790,7 @@
 	      }
 	}
 
-#ifdef __SMP__
+#if defined(__SMP__) || defined(__FreeBSD__)
       /* Skip IRQ detection if SMP (doesn't work) */
       devc->irq_ok = 1;
 #else
@@ -735,6 +798,8 @@
 	devc->irq_ok = 1;
       else
 	{
+	  int             n;
+
 	  for (n = 0; n < 3 && devc->irq_ok == 0; n++)
 	    if (sb_dsp_command (devc, 0xf2))	/* Cause interrupt immediately */
 	      {
@@ -822,16 +887,16 @@
  * properly.
  */
   if (devc->model <= MDL_SBPRO)
-    if (devc->major == 3 && devc->minor != 1)	/* "True" SB Pro should have v3.1. */
+    if (devc->major == 3 && devc->minor != 1)	/* "True" SB Pro should have v3.1 (rare ones may have 3.2). */
       {
-	printk ("This soundcard doesn't seem to be fully Sound Blaster Pro compatible.\n");
-	printk ("Almost certainly there is another way to configure OSS so that\n");
+	printk ("This soundcard may not be fully Sound Blaster Pro compatible.\n");
+	printk ("In many cases there is another way to configure OSS so that\n");
 	printk ("it works properly with OSS (for example in 16 bit mode).\n");
+	printk ("Please ignore this message if you _really_ have a SB Pro.\n");
       }
     else if (!sb_be_quiet && devc->model == MDL_SBPRO)
       {
-	printk ("SB DSP version is just %d.%d which means that your card is\n",
-		devc->major, devc->minor);
+	printk ("SB DSP version is just %d.%d which means that your card is\n", devc->major, devc->minor);
 	printk ("several years old (8 bit only device)\n");
 	printk ("or alternatively the sound driver is incorrectly configured.\n");
       }
@@ -899,6 +964,7 @@
     }
   else
     release_region (hw_config->io_base, 16);
+
 }
 
 /*
@@ -1010,8 +1076,7 @@
 
   if (smw_getmem (devc, mp_base, 0) != 0x00 || smw_getmem (devc, mp_base, 1) != 0xff)
     {
-      DDB (printk ("\nSM Wave: No microcontroller RAM detected (%02x, %02x)\n",
-	     smw_getmem (devc, mp_base, 0), smw_getmem (devc, mp_base, 1)));
+      DDB (printk ("\nSM Wave: No microcontroller RAM detected (%02x, %02x)\n", smw_getmem (devc, mp_base, 0), smw_getmem (devc, mp_base, 1)));
       return 0;			/* No RAM */
     }
 
@@ -1250,8 +1315,10 @@
 	}
       hw_config->name = "Sound Blaster 16";
       hw_config->irq = -devc->irq;
+#if defined(CONFIG_MIDI) && defined(CONFIG_UART401)
       if (devc->minor > 12)	/* What is Vibra's version??? */
 	sb16_set_mpu_port (devc, hw_config);
+#endif
       break;
 
     case MDL_ESS:
diff -u --recursive --new-file v2.1.62/linux/drivers/sound/sb_midi.c linux/drivers/sound/sb_midi.c
--- v2.1.62/linux/drivers/sound/sb_midi.c	Wed May 28 10:51:33 1997
+++ linux/drivers/sound/sb_midi.c	Tue Sep 30 08:46:55 1997
@@ -15,7 +15,8 @@
 
 #include "sound_config.h"
 
-#if defined(CONFIG_SBDSP) && defined(CONFIG_MIDI)
+#ifdef CONFIG_SBDSP
+#ifdef CONFIG_MIDI
 
 #include "sb.h"
 #undef SB_TEST_IRQ
@@ -231,4 +232,5 @@
   sequencer_init ();
 }
 
+#endif
 #endif
diff -u --recursive --new-file v2.1.62/linux/drivers/sound/sb_mixer.c linux/drivers/sound/sb_mixer.c
--- v2.1.62/linux/drivers/sound/sb_mixer.c	Tue Mar  4 10:25:25 1997
+++ linux/drivers/sound/sb_mixer.c	Tue Sep 30 08:46:55 1997
@@ -16,7 +16,7 @@
 
 #include "sound_config.h"
 
-#if defined(CONFIG_SBDSP)
+#ifdef CONFIG_SBDSP
 #define __SB_MIXER_C__
 
 #include "sb.h"
@@ -36,19 +36,7 @@
 static int
 detect_mixer (sb_devc * devc)
 {
-  /*
-   * Detect the mixer by changing parameters of two volume channels. If the
-   * values read back match with the values written, the mixer is there (is
-   * it?)
-   */
-  sb_setmixer (devc, FM_VOL, 0xff);
-  sb_setmixer (devc, VOC_VOL, 0x33);
-
-  if (sb_getmixer (devc, FM_VOL) != 0xff)
-    return 0;
-  if (sb_getmixer (devc, VOC_VOL) != 0x33)
-    return 0;
-
+  /* Just trust the mixer is there */
   return 1;
 }
 
@@ -406,6 +394,7 @@
   switch (devc->model)
     {
     case MDL_SBPRO:
+    case MDL_AZTECH:
     case MDL_JAZZ:
       devc->mixer_caps = SOUND_CAP_EXCL_INPUT;
       devc->supported_devices = SBPRO_MIXER_DEVICES;
diff -u --recursive --new-file v2.1.62/linux/drivers/sound/sb_mixer.h linux/drivers/sound/sb_mixer.h
--- v2.1.62/linux/drivers/sound/sb_mixer.h	Tue Mar  4 10:25:25 1997
+++ linux/drivers/sound/sb_mixer.h	Tue Sep 30 08:46:45 1997
@@ -17,6 +17,7 @@
  *	Added defines for the Sound Galaxy NX Pro mixer.
  * 
  */
+#ifdef CONFIG_SBDSP
 
 #define SBPRO_RECORDING_DEVICES	(SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD)
 
@@ -282,4 +283,5 @@
 #define SRC__CD          3	/* Select CD recording source */
 #define SRC__LINE        7	/* Use Line-in for recording source */
 
+#endif
 #endif
diff -u --recursive --new-file v2.1.62/linux/drivers/sound/sequencer.c linux/drivers/sound/sequencer.c
--- v2.1.62/linux/drivers/sound/sequencer.c	Wed Sep 24 20:05:47 1997
+++ linux/drivers/sound/sequencer.c	Tue Sep 30 08:46:55 1997
@@ -17,6 +17,8 @@
 #include "sound_config.h"
 
 #ifdef CONFIG_SEQUENCER
+#include "softoss.h"
+int             (*softsynthp) (int cmd, int parm1, int parm2, unsigned long parm3) = NULL;
 
 #include "midi_ctrl.h"
 
@@ -57,8 +59,8 @@
 static int      midi_written[MAX_MIDI_DEV] =
 {0};
 
-unsigned long   prev_input_time = 0;
-int             prev_event_time;
+static unsigned long prev_input_time = 0;
+static int      prev_event_time;
 
 #include "tuning.h"
 
@@ -200,7 +202,10 @@
   if (data == 0xfe)		/* Ignore active sensing */
     return;
 
-  tstamp = jiffies - seq_time;
+  if (softsynthp != NULL)
+    tstamp = softsynthp (SSYN_GETTIME, 0, 0, 0);
+  else
+    tstamp = jiffies - seq_time;
 
   if (tstamp != prev_input_time)
     {
@@ -225,6 +230,8 @@
 
   if (seq_mode == SEQ_2)
     this_time = tmr->get_time (tmr_no);
+  else if (softsynthp != NULL)
+    this_time = softsynthp (SSYN_GETTIME, 0, 0, 0);
   else
     this_time = jiffies - seq_time;
 
@@ -732,7 +739,10 @@
 	  prev_event_time = time;
 
 	  seq_playing = 1;
-	  request_sound_timer (time);
+	  if (softsynthp != NULL)
+	    softsynthp (SSYN_REQUEST, time, 0, 0);
+	  else
+	    request_sound_timer (time);
 
 	  if ((SEQ_MAX_QUEUE - qlen) >= output_threshold)
 	    {
@@ -755,7 +765,13 @@
       break;
 
     case TMR_START:
-      seq_time = jiffies;
+      if (softsynthp != NULL)
+	{
+	  softsynthp (SSYN_START, 0, 0, 0);
+	  seq_time = 0;
+	}
+      else
+	seq_time = jiffies;
       prev_input_time = 0;
       prev_event_time = 0;
       break;
@@ -868,7 +884,10 @@
 	  time = *delay;
 	  prev_event_time = time;
 
-	  request_sound_timer (time);
+	  if (softsynthp != NULL)
+	    softsynthp (SSYN_REQUEST, time, 0, 0);
+	  else
+	    request_sound_timer (time);
 
 	  if ((SEQ_MAX_QUEUE - qlen) >= output_threshold)
 	    {
@@ -902,9 +921,14 @@
     case SEQ_SYNCTIMER:	/*
 				 * Reset timer
 				 */
-      seq_time = jiffies;
+      if (softsynthp != NULL)
+	seq_time = 0;
+      else
+	seq_time = jiffies;
       prev_input_time = 0;
       prev_event_time = 0;
+      if (softsynthp != NULL)
+	softsynthp (SSYN_START, 0, 0, 0);
       break;
 
     case SEQ_MIDIPUTC:		/*
@@ -926,7 +950,10 @@
 	       */
 
 	      seq_playing = 1;
-	      request_sound_timer (-1);
+	      if (softsynthp != NULL)
+		softsynthp (SSYN_REQUEST, -1, 0, 0);
+	      else
+		request_sound_timer (-1);
 	      return 2;
 	    }
 	  else
@@ -1186,10 +1213,15 @@
 	}
     }
 
-  seq_time = jiffies;
+  if (softsynthp != NULL)
+    seq_time = 0;
+  else
+    seq_time = jiffies;
 
   prev_input_time = 0;
   prev_event_time = 0;
+  if (softsynthp != NULL)
+    softsynthp (SSYN_START, 0, 0, 0);
 
   if (seq_mode == SEQ_1 && (mode == OPEN_READ || mode == OPEN_READWRITE))
     {				/*
@@ -1227,7 +1259,7 @@
 
   n = 1;
 
-  while (!signal_pending(current) && n)
+  while (!(current->signal & ~current->blocked) && n)
     {
       n = 0;
 
@@ -1279,7 +1311,7 @@
 
   if (mode != OPEN_READ && !(file->flags & (O_NONBLOCK) ?
 			     1 : 0))
-    while (!signal_pending(current) && qlen > 0)
+    while (!(current->signal & ~current->blocked) && qlen > 0)
       {
 	seq_sync ();
 
@@ -1344,7 +1376,7 @@
 {
   unsigned long   flags;
 
-  if (qlen && !seq_playing && !signal_pending(current))
+  if (qlen && !seq_playing && !(current->signal & ~current->blocked))
     seq_startplay ();
 
   save_flags (flags);
@@ -1429,7 +1461,10 @@
   int             chn;
   unsigned long   flags;
 
-  sound_stop_timer ();
+  if (softsynthp != NULL)
+    softsynthp (SSYN_STOP, 0, 0, 0);
+  else
+    sound_stop_timer ();
 
   seq_time = jiffies;
   prev_input_time = 0;
@@ -1493,7 +1528,7 @@
   cli ();
   if ((seq_sleep_flag.opts & WK_SLEEP))
     {
-      /*      printk ("Sequencer Warning: Unexpected sleeping process - Waking up\n"); */
+      /*      printk( "Sequencer Warning: Unexpected sleeping process - Waking up\n"); */
       {
 	seq_sleep_flag.opts = WK_WAKEUP;
 	wake_up (&seq_sleeper);
@@ -1572,7 +1607,7 @@
 
       if (mode == OPEN_READ)
 	return 0;
-      while (qlen > 0 && !signal_pending(current))
+      while (qlen > 0 && !(current->signal & ~current->blocked))
 	seq_sync ();
       if (qlen)
 	return -EINTR;
@@ -1624,7 +1659,10 @@
       if (seq_mode == SEQ_2)
 	return tmr->ioctl (tmr_no, cmd, arg);
 
-      return (*(int *) arg = jiffies - seq_time);
+      if (softsynthp != NULL)
+	return (*(int *) arg = softsynthp (SSYN_GETTIME, 0, 0, 0));
+      else
+	return (*(int *) arg = jiffies - seq_time);
       break;
 
     case SNDCTL_SEQ_CTRLRATE:
@@ -1740,6 +1778,7 @@
 
 	memcpy ((char *) &inf, (char *) synth_devs[dev]->info, sizeof (inf));
 	strcpy (inf.name, synth_devs[dev]->id);
+	inf.device = dev;
 	memcpy ((&((char *) arg)[0]), (char *) &inf, sizeof (inf));
 	return 0;
       }
@@ -1773,6 +1812,7 @@
 	if (dev < 0 || dev >= max_mididev)
 	  return -ENXIO;
 
+	midi_devs[dev]->info.device = dev;
 	pp = (char *) &midi_devs[dev]->info;
 	memcpy ((&((char *) arg)[0]), pp, sizeof (inf));
 	return 0;
diff -u --recursive --new-file v2.1.62/linux/drivers/sound/softoss.c linux/drivers/sound/softoss.c
--- v2.1.62/linux/drivers/sound/softoss.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/sound/softoss.c	Tue Sep 30 08:49:30 1997
@@ -0,0 +1,1593 @@
+/* 
+ * sound/softoss.c
+ *
+ * Software based MIDI synthsesizer driver.
+ */
+/*
+ * Copyright (C) by Hannu Savolainen 1993-1997
+ *
+ * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
+ * Version 2 (June 1991). See the "COPYING" file distributed with this software
+ * for more info.
+ */
+#include <linux/config.h>
+
+
+/*
+ * When POLLED_MODE is defined, the resampling loop is run using a timer
+ * callback routine. Normally the resampling loop is executed inside
+ * audio buffer interrupt handler which doesn't work with single mode DMA.
+ */
+#define SOFTSYN_MAIN
+#undef  POLLED_MODE
+#define HANDLE_LFO
+
+#define ENVELOPE_SCALE		8
+#define NO_SAMPLE		0xffff
+
+#include "sound_config.h"
+
+#ifdef CONFIG_SOFTOSS
+#include "softoss.h"
+#include <sys/ultrasound.h>
+
+int             softsynth_disabled = 0;
+
+static volatile int intr_pending = 0;
+
+#ifdef POLLED_MODE
+
+static struct timer_list poll_timer =
+{NULL, NULL, 0, 0, softsyn_poll};
+
+#else
+#endif
+
+#ifdef HANDLE_LFO
+/*
+ * LFO table. Playback at 128 Hz gives 1 Hz LFO frequency.
+ */
+static int      tremolo_table[128] =
+{
+  0, 39, 158, 355, 630, 982, 1411, 1915,
+  2494, 3146, 3869, 4662, 5522, 6448, 7438, 8489,
+  9598, 10762, 11980, 13248, 14563, 15922, 17321, 18758,
+  20228, 21729, 23256, 24806, 26375, 27960, 29556, 31160,
+  32768, 34376, 35980, 37576, 39161, 40730, 42280, 43807,
+  45308, 46778, 48215, 49614, 50973, 52288, 53556, 54774,
+  55938, 57047, 58098, 59088, 60014, 60874, 61667, 62390,
+  63042, 63621, 64125, 64554, 64906, 65181, 65378, 65497,
+  65536, 65497, 65378, 65181, 64906, 64554, 64125, 63621,
+  63042, 62390, 61667, 60874, 60014, 59087, 58098, 57047,
+  55938, 54774, 53556, 52288, 50973, 49614, 48215, 46778,
+  45308, 43807, 42280, 40730, 39161, 37576, 35980, 34376,
+  32768, 31160, 29556, 27960, 26375, 24806, 23256, 21729,
+  20228, 18758, 17321, 15922, 14563, 13248, 11980, 10762,
+  9598, 8489, 7438, 6448, 5522, 4662, 3869, 3146,
+  2494, 1915, 1411, 982, 630, 355, 158, 39
+};
+
+static int      vibrato_table[128] =
+{
+  0, 1608, 3212, 4808, 6393, 7962, 9512, 11039,
+  12540, 14010, 15447, 16846, 18205, 19520, 20788, 22006,
+  23170, 24279, 25330, 26320, 27246, 28106, 28899, 29622,
+  30274, 30853, 31357, 31786, 32138, 32413, 32610, 32729,
+  32768, 32729, 32610, 32413, 32138, 31786, 31357, 30853,
+  30274, 29622, 28899, 28106, 27246, 26320, 25330, 24279,
+  23170, 22006, 20788, 19520, 18205, 16846, 15447, 14010,
+  12540, 11039, 9512, 7962, 6393, 4808, 3212, 1608,
+  0, -1608, -3212, -4808, -6393, -7962, -9512, -11039,
+  -12540, -14010, -15447, -16846, -18205, -19520, -20788, -22006,
+  -23170, -24279, -25330, -26320, -27246, -28106, -28899, -29622,
+  -30274, -30853, -31357, -31786, -32138, -32413, -32610, -32729,
+  -32768, -32729, -32610, -32413, -32138, -31786, -31357, -30853,
+  -30274, -29622, -28899, -28106, -27246, -26320, -25330, -24279,
+  -23170, -22006, -20788, -19520, -18205, -16846, -15447, -14010,
+  -12540, -11039, -9512, -7962, -6393, -4808, -3212, -1608
+};
+
+#endif
+
+static unsigned long last_resample_jiffies;
+static unsigned long resample_counter;
+
+extern int     *sound_osp;
+
+static volatile int is_running = 0;
+static int      softsynth_loaded = 0;
+
+static struct synth_info softsyn_info =
+{"SoftOSS", 0, SYNTH_TYPE_SAMPLE, SAMPLE_TYPE_GUS, 0, 16, 0, MAX_PATCH};
+
+static struct softsyn_devc sdev_info =
+{0};
+softsyn_devc   *devc = &sdev_info;
+
+static struct voice_alloc_info *voice_alloc;
+
+static int      softsyn_open (int synthdev, int mode);
+static void     init_voice (softsyn_devc * devc, int voice);
+static void     compute_step (int voice);
+
+static volatile int tmr_running = 0;
+static int      voice_limit = 24;
+
+static void
+set_max_voices (int nr)
+{
+  int             i;
+
+  if (nr < 4)
+    nr = 4;
+
+  if (nr > voice_limit)
+    nr = voice_limit;
+
+  voice_alloc->max_voice = devc->maxvoice = nr;
+  devc->afterscale = 5;
+
+  for (i = 31; i > 0; i--)
+    if (nr & (1 << i))
+      {
+	devc->afterscale = i + 1;
+	return;
+      }
+}
+
+static void
+update_vibrato (int voice)
+{
+  voice_info     *v = &softoss_voices[voice];
+
+#ifdef HANDLE_LFO
+  int             x;
+
+  x = vibrato_table[v->vibrato_phase >> 8];
+  v->vibrato_phase = (v->vibrato_phase + v->vibrato_step) & 0x7fff;
+
+  x = (x * v->vibrato_depth) >> 15;
+  v->vibrato_level = (x * 600) >> 8;
+
+  compute_step (voice);
+#else
+  v->vibrato_level = 0;
+#endif
+}
+
+#ifdef HANDLE_LFO
+static void
+update_tremolo (int voice)
+{
+  voice_info     *v = &softoss_voices[voice];
+  int             x;
+
+  x = tremolo_table[v->tremolo_phase >> 8];
+  v->tremolo_phase = (v->tremolo_phase + v->tremolo_step) & 0x7fff;
+
+  v->tremolo_level = (x * v->tremolo_depth) >> 20;
+}
+#endif
+
+static void
+start_vibrato (int voice)
+{
+  voice_info     *v = &softoss_voices[voice];
+  int             rate;
+
+  if (!v->vibrato_depth)
+    return;
+
+  rate = v->vibrato_rate * 6 * 128;
+  v->vibrato_step = (rate * devc->control_rate) / devc->speed;
+
+  devc->vibratomap |= (1 << voice);	/* Enable vibrato */
+}
+
+static void
+start_tremolo (int voice)
+{
+  voice_info     *v = &softoss_voices[voice];
+  int             rate;
+
+  if (!v->tremolo_depth)
+    return;
+
+  rate = v->tremolo_rate * 6 * 128;
+  v->tremolo_step = (rate * devc->control_rate) / devc->speed;
+
+  devc->tremolomap |= (1 << voice);	/* Enable tremolo */
+}
+
+static void
+update_volume (int voice)
+{
+  voice_info     *v = &softoss_voices[voice];
+  unsigned int    vol;
+
+/*
+ * Compute plain volume
+ */
+
+  vol = (v->velocity * v->expression_vol * v->main_vol) >> 12;
+
+#ifdef HANDLE_LFO
+/*
+ * Handle LFO
+ */
+
+  if (devc->tremolomap & (1 << voice))
+    {
+      int             t;
+
+      t = 32768 - v->tremolo_level;
+      vol = (vol * t) >> 15;
+      update_tremolo (voice);
+    }
+#endif
+/*
+ * Envelope
+ */
+  if (v->mode & WAVE_ENVELOPES && !v->percussive_voice)
+    vol = (vol * (v->envelope_vol >> 16)) >> 19;
+  else
+    vol >>= 4;
+
+/*
+ * Handle panning
+ */
+
+  if (v->panning < 0)		/* Pan left */
+    v->rightvol = (vol * (128 + v->panning)) / 128;
+  else
+    v->rightvol = vol;
+
+  if (v->panning > 0)		/* Pan right */
+    v->leftvol = (vol * (128 - v->panning)) / 128;
+  else
+    v->leftvol = vol;
+}
+
+static void
+step_envelope (int voice, int do_release, int velocity)
+{
+  voice_info     *v = &softoss_voices[voice];
+  int             r, rate, time, dif;
+  unsigned int    vol;
+  unsigned long   flags;
+
+  save_flags (flags);
+  cli ();
+
+  if (!voice_active[voice] || v->sample == NULL)
+    {
+      restore_flags (flags);
+      return;
+    }
+
+  if (!do_release)
+    if (v->mode & WAVE_SUSTAIN_ON && v->envelope_phase == 2)
+      {				/* Stop envelope until note off */
+	v->envelope_volstep = 0;
+	v->envelope_time = 0x7fffffff;
+	if (v->mode & WAVE_VIBRATO)
+	  start_vibrato (voice);
+	if (v->mode & WAVE_TREMOLO)
+	  start_tremolo (voice);
+	restore_flags (flags);
+	return;
+      }
+
+  if (do_release)
+    v->envelope_phase = 3;
+  else
+    v->envelope_phase++;
+
+  if (v->envelope_phase >= 5)	/* Finished */
+    {
+      init_voice (devc, voice);
+      restore_flags (flags);
+      return;
+    }
+
+  vol = v->envelope_target = v->sample->env_offset[v->envelope_phase] << 22;
+
+
+  rate = v->sample->env_rate[v->envelope_phase];
+  r = 3 - ((rate >> 6) & 0x3);
+  r *= 3;
+  r = (int) (rate & 0x3f) << r;
+  rate = (((r * 44100) / devc->speed) * devc->control_rate) << 8;
+
+  if (rate < (1 << 20))		/* Avoid infinitely "releasing" voices */
+    rate = 1 << 20;
+
+  dif = (v->envelope_vol - vol);
+  if (dif < 0)
+    dif *= -1;
+  if (dif < rate * 2)		/* Too close */
+    {
+      step_envelope (voice, 0, 60);
+      restore_flags (flags);
+      return;
+    }
+
+  if (vol > v->envelope_vol)
+    {
+      v->envelope_volstep = rate;
+      time = (vol - v->envelope_vol) / rate;
+    }
+  else
+    {
+      v->envelope_volstep = -rate;
+      time = (v->envelope_vol - vol) / rate;
+    }
+
+  time--;
+  if (time <= 0)
+    time = 1;
+
+  v->envelope_time = time;
+
+
+  restore_flags (flags);
+}
+
+static void
+step_envelope_lfo (int voice)
+{
+  voice_info     *v = &softoss_voices[voice];
+
+/*
+ * Update pitch (vibrato) LFO 
+ */
+
+  if (devc->vibratomap & (1 << voice))
+    update_vibrato (voice);
+
+/* 
+ * Update envelope
+ */
+
+  if (v->mode & WAVE_ENVELOPES)
+    {
+      v->envelope_vol += v->envelope_volstep;
+      /* Overshoot protection */
+      if (v->envelope_vol < 0)
+	{
+	  v->envelope_vol = v->envelope_target;
+	  v->envelope_volstep = 0;
+	}
+
+      if (v->envelope_time-- <= 0)
+	{
+	  v->envelope_vol = v->envelope_target;
+	  step_envelope (voice, 0, 60);
+	}
+    }
+}
+
+static void
+compute_step (int voice)
+{
+  voice_info     *v = &softoss_voices[voice];
+
+  /*
+   * Since the pitch bender may have been set before playing the note, we
+   * have to calculate the bending now.
+   */
+
+  v->current_freq = compute_finetune (v->orig_freq,
+				      v->bender,
+				      v->bender_range,
+				      v->vibrato_level);
+  v->step = (((v->current_freq << 9) + (devc->speed >> 1)) / devc->speed);
+
+  if (v->mode & WAVE_LOOP_BACK)
+    v->step *= -1;		/* Reversed playback */
+}
+
+static void
+init_voice (softsyn_devc * devc, int voice)
+{
+  voice_info     *v = &softoss_voices[voice];
+  unsigned long   flags;
+
+  save_flags (flags);
+  cli ();
+  voice_active[voice] = 0;
+  devc->vibratomap &= ~(1 << voice);
+  devc->tremolomap &= ~(1 << voice);
+  v->mode = 0;
+  v->wave = NULL;
+  v->sample = NULL;
+  v->ptr = 0;
+  v->step = 0;
+  v->startloop = 0;
+  v->startbackloop = 0;
+  v->endloop = 0;
+  v->looplen = 0;
+  v->bender = 0;
+  v->bender_range = 200;
+  v->panning = 0;
+  v->main_vol = 127;
+  v->expression_vol = 127;
+  v->patch_vol = 127;
+  v->percussive_voice = 0;
+  v->sustain_mode = 0;
+  v->envelope_phase = 1;
+  v->envelope_vol = 1 << 24;
+  v->envelope_volstep = 256;
+  v->envelope_time = 0;
+  v->vibrato_phase = 0;
+  v->vibrato_step = 0;
+  v->vibrato_level = 0;
+  v->vibrato_rate = 0;
+  v->vibrato_depth = 0;
+  v->tremolo_phase = 0;
+  v->tremolo_step = 0;
+  v->tremolo_level = 0;
+  v->tremolo_rate = 0;
+  v->tremolo_depth = 0;
+  voice_alloc->map[voice] = 0;
+  voice_alloc->alloc_times[voice] = 0;
+  restore_flags (flags);
+}
+
+static void
+reset_samples (softsyn_devc * devc)
+{
+  int             i;
+
+  for (i = 0; i < MAX_VOICE; i++)
+    voice_active[i] = 0;
+  for (i = 0; i < devc->maxvoice; i++)
+    {
+      init_voice (devc, i);
+      softoss_voices[i].instr = 0;
+    }
+
+  devc->ram_used = 0;
+
+  for (i = 0; i < MAX_PATCH; i++)
+    devc->programs[i] = NO_SAMPLE;
+
+  for (i = 0; i < devc->nrsamples; i++)
+    {
+      vfree (devc->samples[i]);
+      vfree (devc->wave[i]);
+      devc->samples[i] = NULL;
+      devc->wave[i] = NULL;
+    }
+
+  devc->nrsamples = 0;
+}
+
+static void
+init_engine (softsyn_devc * devc)
+{
+  int             i, fz, srate, sz = devc->channels;
+
+  set_max_voices (devc->default_max_voices);
+  voice_alloc->timestamp = 0;
+
+  if (devc->bits == 16)
+    sz *= 2;
+
+  fz = devc->fragsize / sz;	/* Samples per fragment */
+  devc->samples_per_fragment = fz;
+
+  devc->usecs = 0;
+  devc->usecs_per_frag = (1000000 * fz) / devc->speed;
+
+  for (i = 0; i < devc->maxvoice; i++)
+    {
+      init_voice (devc, i);
+      softoss_voices[i].instr = 0;
+    }
+
+  devc->engine_state = ES_STOPPED;
+
+/*
+ *    Initialize delay
+ */
+
+  for (i = 0; i < DELAY_SIZE; i++)
+    left_delay[i] = right_delay[i] = 0;
+  delayp = 0;
+  srate = (devc->speed / 10000);	/* 1 to 4 */
+  if (srate <= 0)
+    srate = 1;
+  devc->delay_size = (DELAY_SIZE * srate) / 4;
+  if (devc->delay_size == 0 || devc->delay_size > DELAY_SIZE)
+    devc->delay_size = DELAY_SIZE;
+}
+
+void
+softsyn_control_loop (void)
+{
+  int             voice;
+
+/*
+ *    Recompute envlope, LFO, etc.
+ */
+  for (voice = 0; voice < devc->maxvoice; voice++)
+    if (voice_active[voice])
+      {
+	update_volume (voice);
+	step_envelope_lfo (voice);
+      }
+    else
+      voice_alloc->map[voice] = 0;
+}
+
+static void     start_engine (softsyn_devc * devc);
+
+static void
+do_resample (int dummy)
+{
+  struct dma_buffparms *dmap = audio_devs[devc->audiodev]->dmap_out;
+  struct voice_info *vinfo;
+  unsigned long   flags, jif;
+
+  int             voice, loops;
+  short          *buf;
+
+  if (softsynth_disabled)
+    return;
+
+  save_flags (flags);
+  cli ();
+
+  if (is_running)
+    {
+      printk ("SoftOSS: Playback overrun\n");
+      restore_flags (flags);
+      return;
+    }
+
+  jif = jiffies;
+  if (jif == last_resample_jiffies)
+    {
+      if (resample_counter++ > 50)
+	{
+	  for (voice = 0; voice < devc->maxvoice; voice++)
+	    init_voice (devc, voice);
+	  voice_limit--;
+	  resample_counter = 0;
+	  printk ("SoftOSS: CPU overload. Limiting # of voices to %d\n", voice_limit);
+
+	  if (voice_limit < 10)
+	    {
+	      voice_limit = 10;
+	      devc->speed = (devc->speed * 2) / 3;
+
+	      printk ("SoftOSS: Dropping sampling rate and stopping the device.\n");
+	      softsynth_disabled = 1;
+	    }
+	}
+    }
+  else
+    {
+      last_resample_jiffies = jif;
+      resample_counter = 0;
+    }
+
+  /* is_running = 1; */
+
+  if (dmap->qlen > devc->max_playahead)
+    {
+      printk ("SoftOSS: audio buffers full\n");
+      is_running = 0;
+      restore_flags (flags);
+      return;
+    }
+
+/*
+ * First verify that all active voices are valid (do this just once per block).
+ */
+  for (voice = 0; voice < devc->maxvoice; voice++)
+    if (voice_active[voice])
+      {
+	int             ptr;
+
+	vinfo = &softoss_voices[voice];
+	ptr = vinfo->ptr >> 9;
+
+	if (vinfo->wave == NULL ||
+	    ptr < 0 ||
+	    ptr > vinfo->sample->len)
+	  init_voice (devc, voice);
+	else if (!(vinfo->mode & WAVE_LOOPING) &&
+		 (vinfo->ptr + vinfo->step) > vinfo->endloop)
+	  voice_active[voice] = 0;
+      }
+
+/*
+ *    Start the resampling process
+ */
+
+  loops = devc->samples_per_fragment;
+  buf = (short *) (dmap->raw_buf + (dmap->qtail * dmap->fragment_size));
+
+  softsynth_resample_loop (buf, loops);		/* In Xsoftsynth_rs.c */
+
+  dmap->qtail = (dmap->qtail + 1) % dmap->nbufs;
+  dmap->qlen++;
+  dmap->user_counter += dmap->fragment_size;
+
+  devc->usecs += devc->usecs_per_frag;
+
+  if (tmr_running)
+    {
+      sound_timer_interrupt ();
+    }
+
+/*
+ *    Execute timer
+ */
+
+  if (!tmr_running)
+    if (devc->usecs >= devc->next_event_usecs)
+      {
+	devc->next_event_usecs = ~0;
+	sequencer_timer (0);
+      }
+  is_running = 0;
+  restore_flags (flags);
+}
+
+static void
+delayed_resample (int dummy)
+{
+  struct dma_buffparms *dmap = audio_devs[devc->audiodev]->dmap_out;
+  int             n = 0;
+
+  if (is_running)
+    return;
+
+  while (devc->engine_state != ES_STOPPED &&
+	 dmap->qlen < devc->max_playahead && n++ < 2)
+    do_resample (0);
+  intr_pending = 0;
+}
+
+#ifdef POLLED_MODE
+static void
+softsyn_poll (unsigned long dummy)
+{
+  delayed_resample (0);
+
+  if (devc->engine_state != ES_STOPPED)
+
+    {
+      poll_timer.expires = (1) + jiffies;
+      add_timer (&poll_timer);
+    };
+}
+#else
+static void
+softsyn_callback (int dev, int parm)
+{
+  delayed_resample (0);
+}
+#endif
+
+static void
+start_engine (softsyn_devc * devc)
+{
+  struct dma_buffparms *dmap;
+
+  if (!devc->audio_opened)
+    if (softsyn_open (devc->synthdev, 0) < 0)
+      return;
+
+  if (devc->audiodev >= num_audiodevs)
+    return;
+
+  dmap = audio_devs[devc->audiodev]->dmap_out;
+
+  devc->usecs = 0;
+  devc->next_event_usecs = ~0;
+  devc->control_rate = 64;
+  devc->control_counter = 0;
+
+  if (devc->engine_state == ES_STOPPED)
+    {
+      int             trig, n = 0;
+
+      trig = 0;
+      dma_ioctl (devc->audiodev, SNDCTL_DSP_SETTRIGGER, (caddr_t) & trig);
+#ifdef POLLED_MODE
+      ;
+
+      {
+	poll_timer.expires = (1) + jiffies;
+	add_timer (&poll_timer);
+      };			/* Start polling */
+#else
+      dmap->audio_callback = softsyn_callback;
+      dmap->qhead = dmap->qtail = dmap->qlen = 0;
+#endif
+
+      while (dmap->qlen < devc->max_playahead && n++ < 2)
+	do_resample (0);
+
+      devc->engine_state = ES_STARTED;
+      last_resample_jiffies = jiffies;
+      resample_counter = 0;
+
+      trig = PCM_ENABLE_OUTPUT;
+      if (dma_ioctl (devc->audiodev, SNDCTL_DSP_SETTRIGGER,
+		     (caddr_t) & trig) < 0)
+	{
+	  printk ("SoftOSS: Trigger failed\n");
+	}
+
+
+    }
+}
+
+static void
+stop_engine (softsyn_devc * devc)
+{
+}
+
+static void
+request_engine (softsyn_devc * devc, int ticks)
+{
+  if (ticks < 0)		/* Relative time */
+    devc->next_event_usecs = devc->usecs - ticks * (1000000 / HZ);
+  else
+    devc->next_event_usecs = ticks * (1000000 / HZ);
+}
+
+/*
+ * Softsync hook serves mode1 (timing) calls made by sequencer.c
+ */
+static int
+softsynth_hook (int cmd, int parm1, int parm2, unsigned long parm3)
+{
+  switch (cmd)
+    {
+    case SSYN_START:
+      start_engine (devc);
+      break;
+
+    case SSYN_STOP:
+      stop_engine (devc);
+      break;
+
+    case SSYN_REQUEST:
+      request_engine (devc, parm1);
+      break;
+
+    case SSYN_GETTIME:
+      return devc->usecs / (1000000 / HZ);
+      break;
+
+    default:
+      printk ("SoftOSS: Unknown request %d\n", cmd);
+    }
+
+  return 0;
+}
+
+static int
+softsyn_ioctl (int dev,
+	       unsigned int cmd, caddr_t arg)
+{
+  switch (cmd)
+    {
+
+    case SNDCTL_SYNTH_INFO:
+      softsyn_info.nr_voices = devc->maxvoice;
+
+      memcpy ((&((char *) arg)[0]), (char *) &softsyn_info, sizeof (softsyn_info));
+      return 0;
+      break;
+
+    case SNDCTL_SEQ_RESETSAMPLES:
+      stop_engine (devc);
+      reset_samples (devc);
+      return 0;
+      break;
+
+    case SNDCTL_SYNTH_MEMAVL:
+      return devc->ram_size - devc->ram_used;
+      break;
+
+    default:
+      return -EINVAL;
+    }
+
+}
+
+static int
+softsyn_kill_note (int devno, int voice, int note, int velocity)
+{
+  if (voice < 0 || voice > devc->maxvoice)
+    return 0;
+  voice_alloc->map[voice] = 0xffff;	/* Releasing */
+
+  if (softoss_voices[voice].sustain_mode & 1)	/* Sustain controller on */
+    {
+      softoss_voices[voice].sustain_mode = 3;	/* Note off pending */
+      return 0;
+    }
+
+  if (velocity > 127 || softoss_voices[voice].mode & WAVE_FAST_RELEASE)
+    {
+      init_voice (devc, voice);	/* Mark it inactive */
+      return 0;
+    }
+
+  if (softoss_voices[voice].mode & WAVE_ENVELOPES)
+    step_envelope (voice, 1, velocity);		/* Enter sustain phase */
+  else
+    init_voice (devc, voice);	/* Mark it inactive */
+  return 0;
+}
+
+static int
+softsyn_set_instr (int dev, int voice, int instr)
+{
+  if (voice < 0 || voice > devc->maxvoice)
+    return 0;
+
+  if (instr < 0 || instr > MAX_PATCH)
+    {
+      printk ("SoftOSS: Invalid instrument number %d\n", instr);
+      return 0;
+    }
+
+  softoss_voices[voice].instr = instr;
+
+  return 0;
+}
+
+static int
+softsyn_start_note (int dev, int voice, int note, int volume)
+{
+  int             instr = 0;
+  int             best_sample, best_delta, delta_freq, selected;
+  unsigned long   note_freq, freq, base_note, flags;
+  voice_info     *v = &softoss_voices[voice];
+
+  struct patch_info *sample;
+
+  if (voice < 0 || voice > devc->maxvoice)
+    return 0;
+
+  if (volume == 0)		/* Actually note off */
+    softsyn_kill_note (dev, voice, note, volume);
+
+  save_flags (flags);
+  cli ();
+
+  if (note == 255)
+    {				/* Just volume update */
+      v->velocity = volume;
+      if (voice_active[voice])
+	update_volume (voice);
+      restore_flags (flags);
+      return 0;
+    }
+
+  voice_active[voice] = 0;	/* Stop the voice for a while */
+  devc->vibratomap &= ~(1 << voice);
+  devc->tremolomap &= ~(1 << voice);
+
+  instr = v->instr;
+  if (instr < 0 || instr > MAX_PATCH || devc->programs[instr] == NO_SAMPLE)
+    {
+      printk ("SoftOSS: Undefined MIDI instrument %d\n", instr);
+      restore_flags (flags);
+      return 0;
+    }
+
+  instr = devc->programs[instr];
+
+  if (instr < 0 || instr >= devc->nrsamples)
+    {
+      printk ("SoftOSS: Corrupted MIDI instrument %d (%d)\n", v->instr, instr);
+      restore_flags (flags);
+      return 0;
+    }
+
+  note_freq = note_to_freq (note);
+
+  selected = -1;
+
+  best_sample = instr;
+  best_delta = 1000000;
+
+  while (instr != NO_SAMPLE && instr >= 0 && selected == -1)
+    {
+      delta_freq = note_freq - devc->samples[instr]->base_note;
+
+      if (delta_freq < 0)
+	delta_freq = -delta_freq;
+      if (delta_freq < best_delta)
+	{
+	  best_sample = instr;
+	  best_delta = delta_freq;
+	}
+      if (devc->samples[instr]->low_note <= note_freq &&
+	  note_freq <= devc->samples[instr]->high_note)
+	selected = instr;
+      else
+	instr = devc->samples[instr]->key;	/* Link to next sample */
+
+      if (instr < 0 || instr >= devc->nrsamples)
+	instr = NO_SAMPLE;
+    }
+
+  if (selected == -1)
+    instr = best_sample;
+  else
+    instr = selected;
+
+  if (instr < 0 || instr == NO_SAMPLE || instr > devc->nrsamples)
+    {
+      printk ("SoftOSS: Unresolved MIDI instrument %d\n", v->instr);
+      restore_flags (flags);
+      return 0;
+    }
+
+  sample = devc->samples[instr];
+  v->sample = sample;
+
+  if (v->percussive_voice)	/* No key tracking */
+    {
+      v->orig_freq = sample->base_freq;		/* Fixed pitch */
+    }
+  else
+    {
+      base_note = sample->base_note / 100;
+      note_freq /= 100;
+
+      freq = sample->base_freq * note_freq / base_note;
+      v->orig_freq = freq;
+    }
+
+  if (!(sample->mode & WAVE_LOOPING))
+    {
+      sample->loop_end = sample->len;
+    }
+
+  v->wave = devc->wave[instr];
+
+  if (volume < 0)
+    volume = 0;
+  else if (volume > 127)
+    volume = 127;
+
+  v->ptr = 0;
+  v->startloop = sample->loop_start * 512;
+  v->startbackloop = 0;
+  v->endloop = sample->loop_end * 512;
+  v->looplen = (sample->loop_end - sample->loop_start) * 512;
+  v->leftvol = 64;
+  v->rightvol = 64;
+  v->patch_vol = sample->volume;
+  v->velocity = volume;
+  v->mode = sample->mode;
+  v->vibrato_phase = 0;
+  v->vibrato_step = 0;
+  v->vibrato_level = 0;
+  v->vibrato_rate = 0;
+  v->vibrato_depth = 0;
+  v->tremolo_phase = 0;
+  v->tremolo_step = 0;
+  v->tremolo_level = 0;
+  v->tremolo_rate = 0;
+  v->tremolo_depth = 0;
+
+  if (!(v->mode & WAVE_LOOPING))
+    v->mode &= ~(WAVE_BIDIR_LOOP | WAVE_LOOP_BACK);
+  else if (v->mode & WAVE_LOOP_BACK)
+    {
+      v->ptr = sample->len;
+      v->startbackloop = v->startloop;
+    }
+
+  if (v->mode & WAVE_VIBRATO)
+    {
+      v->vibrato_rate = sample->vibrato_rate;
+      v->vibrato_depth = sample->vibrato_depth;
+    }
+
+  if (v->mode & WAVE_TREMOLO)
+    {
+      v->tremolo_rate = sample->tremolo_rate;
+      v->tremolo_depth = sample->tremolo_depth;
+    }
+
+  if (v->mode & WAVE_ENVELOPES)
+    {
+      v->envelope_phase = -1;
+      v->envelope_vol = 0;
+      step_envelope (voice, 0, 60);
+    }
+  update_volume (voice);
+  compute_step (voice);
+
+  voice_active[voice] = 1;	/* Mark it active */
+
+  restore_flags (flags);
+  return 0;
+}
+
+static int
+softsyn_open (int synthdev, int mode)
+{
+  int             err;
+  extern int      softoss_dev;
+  int             frags = 0x7fff0007;	/* fragment size of 128 bytes */
+
+  if (devc->audio_opened)	/* Already opened */
+    return 0;
+
+  softsynth_disabled = 0;
+  devc->finfo.mode = OPEN_WRITE;
+  devc->finfo.flags = 0;
+
+  if (softoss_dev >= num_audiodevs)
+    softoss_dev = num_audiodevs - 1;
+
+  if (softoss_dev < 0)
+    softoss_dev = 0;
+  if (softoss_dev >= num_audiodevs)
+    return -ENXIO;
+  devc->audiodev = softoss_dev;
+
+  if (!(audio_devs[devc->audiodev]->format_mask & AFMT_S16_LE))
+    {
+      printk ("SoftOSS: The audio device doesn't support 16 bits\n");
+      return -ENXIO;
+    }
+  if ((err = audio_open ((devc->audiodev << 4) | SND_DEV_DSP16, &devc->finfo)) < 0)
+    return err;
+
+  devc->speed = audio_devs[devc->audiodev]->d->set_speed (
+					       devc->audiodev, devc->speed);
+  devc->channels = audio_devs[devc->audiodev]->d->set_channels (
+					    devc->audiodev, devc->channels);
+  devc->bits = audio_devs[devc->audiodev]->d->set_bits (
+						devc->audiodev, devc->bits);
+
+
+  DDB (printk ("SoftOSS: Using audio dev %d, speed %d, bits %d, channels %d\n", devc->audiodev, devc->speed, devc->bits, devc->channels));
+
+  dma_ioctl (devc->audiodev, SNDCTL_DSP_SETFRAGMENT, (caddr_t) & frags);
+  dma_ioctl (devc->audiodev, SNDCTL_DSP_GETBLKSIZE, (caddr_t) & devc->fragsize);
+
+  if (devc->bits != 16 || devc->channels != 2)
+    {
+      audio_release ((devc->audiodev << 4) | SND_DEV_DSP16, &devc->finfo);
+      printk ("SoftOSS: A 16 bit stereo soundcard is required\n");
+      return 0;
+    }
+
+  if (devc->max_playahead >= audio_devs[devc->audiodev]->dmap_out->nbufs)
+    devc->max_playahead = audio_devs[devc->audiodev]->dmap_out->nbufs;
+
+  DDB (printk ("SoftOSS: Using %d fragments of %d bytes\n", devc->max_playahead, devc->fragsize));
+
+  init_engine (devc);
+  devc->audio_opened = 1;
+  devc->sequencer_mode = mode;
+  return 0;
+}
+
+static void
+softsyn_close (int synthdev)
+{
+  devc->engine_state = ES_STOPPED;
+#ifdef POLLED_MODE
+  del_timer (&poll_timer);;
+#endif
+  dma_ioctl (devc->audiodev, SNDCTL_DSP_RESET, 0);
+  if (devc->audio_opened)
+    audio_release ((devc->audiodev << 4) | SND_DEV_DSP16, &devc->finfo);
+  devc->audio_opened = 0;
+}
+
+static void
+softsyn_hw_control (int dev, unsigned char *event_rec)
+{
+  int             voice, cmd;
+  unsigned short  p1, p2;
+  unsigned int    plong;
+
+  cmd = event_rec[2];
+  voice = event_rec[3];
+  p1 = *(unsigned short *) &event_rec[4];
+  p2 = *(unsigned short *) &event_rec[6];
+  plong = *(unsigned int *) &event_rec[4];
+
+  switch (cmd)
+    {
+
+    case _GUS_NUMVOICES:
+      set_max_voices (p1);
+      break;
+
+
+    default:;
+    }
+}
+
+static int
+softsyn_load_patch (int dev, int format, const char *addr,
+		    int offs, int count, int pmgr_flag)
+{
+  struct patch_info *patch = NULL;
+
+  int             i, p, instr;
+  long            sizeof_patch;
+  int             memlen, adj;
+  unsigned short  data;
+  short          *wave = NULL;
+
+  sizeof_patch = (long) &patch->data[0] - (long) patch;		/* Header size */
+
+  if (format != GUS_PATCH)
+    {
+      printk ("SoftOSS: Invalid patch format (key) 0x%x\n", format);
+      return -EINVAL;
+    }
+
+  if (count < sizeof_patch)
+    {
+      printk ("SoftOSS: Patch header too short\n");
+      return -EINVAL;
+    }
+
+  count -= sizeof_patch;
+
+  if (devc->nrsamples >= MAX_SAMPLE)
+    {
+      printk ("SoftOSS: Sample table full\n");
+      return -ENOSPC;
+    }
+
+  /*
+   * Copy the header from user space but ignore the first bytes which have
+   * been transferred already.
+   */
+
+  patch = vmalloc (sizeof (*patch));
+
+  if (patch == NULL)
+    {
+      printk ("SoftOSS: Out of memory\n");
+      return -ENOSPC;
+    }
+
+  copy_from_user (&((char *) patch)[offs], &(addr)[offs], sizeof_patch - offs);
+
+  if (patch->mode & WAVE_ROM)
+    {
+      vfree (patch);
+      return -EINVAL;
+    }
+
+  instr = patch->instr_no;
+
+  if (instr < 0 || instr > MAX_PATCH)
+    {
+      printk ("SoftOSS: Invalid patch number %d\n", instr);
+      vfree (patch);
+      return -EINVAL;
+    }
+
+  if (count < patch->len)
+    {
+      printk ("SoftOSS: Patch record too short (%d<%d)\n", count, (int) patch->len);
+      patch->len = count;
+    }
+
+  if (patch->len <= 0 || patch->len > (devc->ram_size - devc->ram_used))
+    {
+      printk ("SoftOSS: Invalid sample length %d\n", (int) patch->len);
+      vfree (patch);
+      return -EINVAL;
+    }
+
+  if (patch->mode & WAVE_LOOPING)
+    {
+      if (patch->loop_start < 0 || patch->loop_start >= patch->len)
+	{
+	  printk ("SoftOSS: Invalid loop start %d\n", patch->loop_start);
+	  vfree (patch);
+	  return -EINVAL;
+	}
+
+      if (patch->loop_end < patch->loop_start || patch->loop_end > patch->len)
+	{
+	  printk ("SoftOSS: Invalid loop start or end point (%d, %d)\n", patch->loop_start, patch->loop_end);
+	  vfree (patch);
+	  return -EINVAL;
+	}
+    }
+
+/* 
+ * Next load the wave data to memory
+ */
+
+  memlen = patch->len;
+  adj = 1;
+
+  if (!(patch->mode & WAVE_16_BITS))
+    memlen *= 2;
+  else
+    adj = 2;
+
+  wave = vmalloc (memlen);
+
+  if (wave == NULL)
+    {
+      printk ("SoftOSS: Can't allocate %d bytes of mem for a sample\n", memlen);
+      vfree (patch);
+      return -ENOSPC;
+    }
+
+  p = 0;
+  for (i = 0; i < memlen / 2; i++)	/* Handle words */
+    {
+      unsigned char   tmp;
+
+      data = 0;
+
+      if (patch->mode & WAVE_16_BITS)
+	{
+	  get_user (*(unsigned char *) &tmp, (unsigned char *) &((addr)[sizeof_patch + p++]));	/* Get lsb */
+	  data = tmp;
+	  get_user (*(unsigned char *) &tmp, (unsigned char *) &((addr)[sizeof_patch + p++]));	/* Get msb */
+	  if (patch->mode & WAVE_UNSIGNED)
+	    tmp ^= 0x80;	/* Convert to signed */
+	  data |= (tmp << 8);
+	}
+      else
+	{
+	  get_user (*(unsigned char *) &tmp, (unsigned char *) &((addr)[sizeof_patch + p++]));
+	  if (patch->mode & WAVE_UNSIGNED)
+	    tmp ^= 0x80;	/* Convert to signed */
+	  data = (tmp << 8);	/* Convert to 16 bits */
+	}
+
+      wave[i] = (short) data;
+    }
+
+  devc->ram_used += patch->len;
+/*
+ * Convert pointers to 16 bit indexes
+ */
+  patch->len /= adj;
+  patch->loop_start /= adj;
+  patch->loop_end /= adj;
+
+/*
+ * Finally link the loaded patch to the chain
+ */
+
+  patch->key = devc->programs[instr];
+  devc->programs[instr] = devc->nrsamples;
+  devc->wave[devc->nrsamples] = (short *) wave;
+  devc->samples[devc->nrsamples++] = patch;
+
+  return 0;
+}
+
+static void
+softsyn_panning (int dev, int voice, int pan)
+{
+  if (voice < 0 || voice > devc->maxvoice)
+    return;
+
+  if (pan < -128)
+    pan = -128;
+  if (pan > 127)
+    pan = 127;
+
+  softoss_voices[voice].panning = pan;
+  if (voice_active[voice])
+    update_volume (voice);
+}
+
+static void
+softsyn_volume_method (int dev, int mode)
+{
+}
+
+static void
+softsyn_aftertouch (int dev, int voice, int pressure)
+{
+  if (voice < 0 || voice > devc->maxvoice)
+    return;
+
+  if (voice_active[voice])
+    update_volume (voice);
+}
+
+static void
+softsyn_controller (int dev, int voice, int ctrl_num, int value)
+{
+  unsigned long   flags;
+
+  if (voice < 0 || voice > devc->maxvoice)
+    return;
+  save_flags (flags);
+  cli ();
+
+  switch (ctrl_num)
+    {
+    case CTRL_PITCH_BENDER:
+      softoss_voices[voice].bender = value;
+
+      if (voice_active[voice])
+	compute_step (voice);	/* Update pitch */
+      break;
+
+
+    case CTRL_PITCH_BENDER_RANGE:
+      softoss_voices[voice].bender_range = value;
+      break;
+    case CTL_EXPRESSION:
+      value /= 128;
+    case CTRL_EXPRESSION:
+      softoss_voices[voice].expression_vol = value;
+      if (voice_active[voice])
+	update_volume (voice);
+      break;
+
+    case CTL_PAN:
+      softsyn_panning (dev, voice, (value * 2) - 128);
+      break;
+
+    case CTL_MAIN_VOLUME:
+      value = (value * 100) / 16383;
+
+    case CTRL_MAIN_VOLUME:
+      softoss_voices[voice].main_vol = value;
+      if (voice_active[voice])
+	update_volume (voice);
+      break;
+
+    default:
+      break;
+    }
+
+  restore_flags (flags);
+}
+
+static void
+softsyn_bender (int dev, int voice, int value)
+{
+  if (voice < 0 || voice > devc->maxvoice)
+    return;
+
+  softoss_voices[voice].bender = value - 8192;
+  if (voice_active[voice])
+    compute_step (voice);	/* Update pitch */
+}
+
+static int
+softsyn_alloc_voice (int dev, int chn, int note, struct voice_alloc_info *alloc)
+{
+  int             i, p, best = -1, best_time = 0x7fffffff;
+
+  p = alloc->ptr;
+  /*
+     * First look for a completely stopped voice
+   */
+
+  for (i = 0; i < alloc->max_voice; i++)
+    {
+      if (alloc->map[p] == 0)
+	{
+	  alloc->ptr = p;
+	  voice_active[p] = 0;
+	  return p;
+	}
+      if (alloc->alloc_times[p] < best_time)
+	{
+	  best = p;
+	  best_time = alloc->alloc_times[p];
+	}
+      p = (p + 1) % alloc->max_voice;
+    }
+
+  /*
+     * Then look for a releasing voice
+   */
+
+  for (i = 0; i < alloc->max_voice; i++)
+    {
+      if (alloc->map[p] == 0xffff)
+	{
+	  alloc->ptr = p;
+	  voice_active[p] = 0;
+	  return p;
+	}
+      p = (p + 1) % alloc->max_voice;
+    }
+
+  if (best >= 0)
+    p = best;
+
+  alloc->ptr = p;
+  voice_active[p] = 0;
+  return p;
+}
+
+static void
+softsyn_setup_voice (int dev, int voice, int chn)
+{
+  unsigned long   flags;
+
+  struct channel_info *info =
+  &synth_devs[dev]->chn_info[chn];
+
+  save_flags (flags);
+  cli ();
+  /* init_voice(devc, voice); */
+  softsyn_set_instr (dev, voice, info->pgm_num);
+
+  softoss_voices[voice].expression_vol =
+    info->controllers[CTL_EXPRESSION];	/* Just MSB */
+  softoss_voices[voice].main_vol =
+    (info->controllers[CTL_MAIN_VOLUME] * 100) / (unsigned) 128;
+  softsyn_panning (dev, voice, (info->controllers[CTL_PAN] * 2) - 128);
+  softoss_voices[voice].bender = 0;	/* info->bender_value; */
+  softoss_voices[voice].bender_range = info->bender_range;
+
+  if (chn == 9)
+    softoss_voices[voice].percussive_voice = 1;
+  restore_flags (flags);
+}
+
+static void
+softsyn_reset (int devno)
+{
+  int             i;
+  unsigned long   flags;
+
+  save_flags (flags);
+  cli ();
+
+  for (i = 0; i < devc->maxvoice; i++)
+    init_voice (devc, i);
+  restore_flags (flags);
+}
+
+static struct synth_operations softsyn_operations =
+{
+  "SoftOSS",
+  &softsyn_info,
+  0,
+  SYNTH_TYPE_SAMPLE,
+  0,
+  softsyn_open,
+  softsyn_close,
+  softsyn_ioctl,
+  softsyn_kill_note,
+  softsyn_start_note,
+  softsyn_set_instr,
+  softsyn_reset,
+  softsyn_hw_control,
+  softsyn_load_patch,
+  softsyn_aftertouch,
+  softsyn_controller,
+  softsyn_panning,
+  softsyn_volume_method,
+  softsyn_bender,
+  softsyn_alloc_voice,
+  softsyn_setup_voice
+};
+
+/*
+ * Timer stuff (for /dev/music).
+ */
+
+static unsigned int
+soft_tmr_start (int dev, unsigned int usecs)
+{
+  tmr_running = 1;
+  start_engine (devc);
+  return devc->usecs_per_frag;
+}
+
+static void
+soft_tmr_disable (int dev)
+{
+  stop_engine (devc);
+  tmr_running = 0;
+}
+
+static void
+soft_tmr_restart (int dev)
+{
+  tmr_running = 1;
+}
+
+static struct sound_lowlev_timer soft_tmr =
+{
+  0,
+  9999,
+  soft_tmr_start,
+  soft_tmr_disable,
+  soft_tmr_restart
+};
+
+int
+probe_softsyn (struct address_info *hw_config)
+{
+  int             i;
+
+  if (softsynth_loaded)
+    return 0;
+
+  devc->ram_size = 8 * 1024 * 1024;
+  devc->ram_used = 0;
+  devc->nrsamples = 0;
+  for (i = 0; i < MAX_PATCH; i++)
+    {
+      devc->programs[i] = NO_SAMPLE;
+      devc->wave[i] = NULL;
+    }
+
+  devc->maxvoice = DEFAULT_VOICES;
+
+  devc->audiodev = 0;
+  devc->audio_opened = 0;
+  devc->channels = 2;
+  devc->bits = 16;
+  devc->max_playahead = 32;
+
+#ifdef SOFTOSS_RATE
+  devc->speed = SOFTOSS_RATE;
+#else
+  devc->speed = 32000;
+#endif
+
+#ifdef SOFTOSS_VOICES
+  devc->default_max_voices = SOFTOSS_VOICES;
+#else
+  devc->default_max_voices = 32;
+#endif
+
+  softsynth_loaded = 1;
+  return 1;
+}
+
+void
+attach_softsyn_card (struct address_info *hw_config)
+{
+
+  voice_alloc = &softsyn_operations.alloc;
+  synth_devs[num_synths++] = &softsyn_operations;
+  sequencer_init ();
+  sound_timer_init (&soft_tmr, "SoftOSS");
+  devc->synthdev = num_synths;
+  softsynthp = softsynth_hook;
+
+#ifndef POLLED_MODE
+#endif
+}
+
+void
+unload_softsyn (struct address_info *hw_config)
+{
+  if (!softsynth_loaded)
+    return;
+#ifndef POLLED_MODE
+#endif
+
+  softsynthp = NULL;
+  reset_samples (devc);
+}
+
+#endif
diff -u --recursive --new-file v2.1.62/linux/drivers/sound/softoss.h linux/drivers/sound/softoss.h
--- v2.1.62/linux/drivers/sound/softoss.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/sound/softoss.h	Tue Sep 30 08:46:45 1997
@@ -0,0 +1,160 @@
+/*
+ * softoss.h	- Definitions for Software MIDI Synthesizer.
+ */
+/*
+ * Copyright (C) by Hannu Savolainen 1993-1997
+ *
+ * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
+ * Version 2 (June 1991). See the "COPYING" file distributed with this software
+ * for more info.
+ */
+
+
+/*
+ * Sequencer mode1 timer calls made by sequencer.c
+ */
+extern int (*softsynthp) (int cmd, int parm1, int parm2, unsigned long parm3);
+
+#define SSYN_START	1
+#define SSYN_REQUEST	2	/* parm1 = time */
+#define SSYN_STOP	3
+#define SSYN_GETTIME	4	/* Returns number of ticks since reset */
+
+#define MAX_PATCH 256
+#define MAX_SAMPLE 512
+#define MAX_VOICE 32
+#define DEFAULT_VOICES 16
+
+typedef struct voice_info
+{
+/*
+ * Don't change anything in the beginning of this struct. These fields are used
+ * by the resampling loop which may have been written in assembly for some
+ * architectures. Any change may make the resampling code incompatible
+ */
+  int instr;
+  short *wave;
+  struct patch_info *sample;
+
+  unsigned int ptr; int step; /* Pointer to the wave data and pointer increment */
+
+  int mode;
+  int startloop, startbackloop, endloop, looplen;
+
+  unsigned int leftvol, rightvol;
+/***** Don't change anything above this */
+
+  volatile unsigned long orig_freq, current_freq;
+  volatile int bender, bender_range, panning;
+  volatile int main_vol, expression_vol, patch_vol, velocity;
+
+/* Envelope parameters */
+
+  int envelope_phase;
+  volatile int envelope_vol;
+  volatile int envelope_volstep;
+  int envelope_time; /* Number of remaining envelope steps */
+  unsigned int envelope_target;
+  int percussive_voice;
+  int sustain_mode; /* 0=off, 1=sustain on, 2=sustain on+key released */
+
+/*	Vibrato	*/
+  int vibrato_rate;
+  int vibrato_depth;
+  int vibrato_phase;
+  int vibrato_step;
+  int vibrato_level;
+
+/*	Tremolo	*/
+  int tremolo_rate;
+  int tremolo_depth;
+  int tremolo_phase;
+  int tremolo_step;
+  int tremolo_level;
+} voice_info;
+
+extern voice_info softoss_voices[MAX_VOICE]; /* Voice spesific info */
+
+typedef struct softsyn_devc
+{
+/*
+ * Don't change anything in the beginning of this struct. These fields are used
+ * by the resampling loop which may have been written in assembly for some
+ * architectures. Any change may make the resampling code incompatible
+ */
+	int maxvoice;		/* # of voices to be processed */
+  	int afterscale;
+	int delay_size;
+  	int control_rate, control_counter;
+/***** Don't change anything above this */
+
+	int ram_size;
+	int ram_used;
+
+	int synthdev;
+	int sequencer_mode;
+/*
+ *	Audio parameters
+ */
+
+	int audiodev;
+	int audio_opened;
+	int speed;
+	int channels;
+	int bits;
+	int default_max_voices;
+	int max_playahead;
+	struct fileinfo finfo;
+	int fragsize;
+	int samples_per_fragment;
+	
+/*
+ * 	Sample storage
+ */
+	int nrsamples;
+	struct patch_info *samples[MAX_SAMPLE];
+	short *wave[MAX_SAMPLE];
+
+/*
+ * 	Programs
+ */
+	int programs[MAX_SAMPLE];
+
+/*
+ *	Timer parameters
+ */
+	volatile unsigned long usecs;
+	volatile unsigned long usecs_per_frag;
+	volatile unsigned long next_event_usecs;
+
+/*
+ * 	Engine state
+ */
+
+	volatile int engine_state;
+#define ES_STOPPED			0
+#define ES_STARTED			1
+
+	/* Voice spesific bitmaps */
+	volatile int tremolomap; 
+	volatile int vibratomap;
+
+} softsyn_devc;
+
+void softsynth_resample_loop(short *buf, int loops);
+extern void softsyn_control_loop(void);
+
+#define DELAY_SIZE	4096
+
+#ifdef SOFTSYN_MAIN
+  short voice_active[MAX_VOICE] = {0};
+  voice_info softoss_voices[MAX_VOICE] = {{0}}; /* Voice spesific info */
+  int left_delay[DELAY_SIZE]={0}, right_delay[DELAY_SIZE]={0};
+  int delayp=0;
+#else
+  extern softsyn_devc *devc;
+
+  extern int left_delay[DELAY_SIZE], right_delay[DELAY_SIZE];
+  extern int delayp;
+  extern short voice_active[MAX_VOICE];
+#endif
diff -u --recursive --new-file v2.1.62/linux/drivers/sound/softoss_rs.c linux/drivers/sound/softoss_rs.c
--- v2.1.62/linux/drivers/sound/softoss_rs.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/sound/softoss_rs.c	Tue Sep 30 08:46:55 1997
@@ -0,0 +1,139 @@
+/*
+ * sound/softoss_rs.c
+ *
+ * Software based MIDI synthsesizer driver, the actual mixing loop.
+ * Keep the loop as simple as possible to make it easier to rewrite this 
+ * routine in assembly.
+ */
+/*
+ * Copyright (C) by Hannu Savolainen 1993-1997
+ *
+ * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
+ * Version 2 (June 1991). See the "COPYING" file distributed with this software
+ * for more info.
+ */
+#include <linux/config.h>
+
+
+#include "sound_config.h"
+
+#ifdef CONFIG_SOFTOSS
+#include "softoss.h"
+
+void
+softsynth_resample_loop (short *buf, int loops)
+{
+  int             iloop, voice;
+  volatile voice_info *v;
+
+#ifdef OSS_BIG_ENDIAN
+  unsigned char  *cbuf = (unsigned char *) buf;
+
+#endif
+
+  for (iloop = 0; iloop < loops; iloop++)
+    {				/* Mix one sample */
+
+      int             accum, left = 0, right = 0;
+      int             ix, position;
+
+      for (voice = 0; voice < devc->maxvoice; voice++)
+	if (voice_active[voice])
+	  {			/* Compute voice */
+
+	    v = &softoss_voices[voice];
+#ifdef SOFTOSS_TEST
+	    ix = iloop << 3;
+	    position = v->ptr;
+#else
+	    ix = (position = v->ptr) >> 9;
+#endif
+	    /* Interpolation (resolution of 512 steps) */
+	    {
+	      int             fract = v->ptr & 0x1f;	/* 9 bits */
+
+	      /* This method works with less arithmetic operations */
+	      register int    v1 = v->wave[ix];
+
+	      accum = v1 + ((((v->wave[ix + 1] - v1)) * (fract)) >> 9);
+	    }
+
+	    left += (accum * v->leftvol);
+	    right += (accum * v->rightvol);
+
+	    /* Update sample pointer */
+
+	    position += v->step;
+	    if (position <= v->endloop)
+	      v->ptr = position;
+	    else if (v->mode & WAVE_LOOPING)
+	      {
+		if (v->mode & WAVE_BIDIR_LOOP)
+		  {
+		    v->mode ^= WAVE_LOOP_BACK;	/* Turn around */
+		    v->step *= -1;
+		  }
+		else
+		  {
+		    position -= v->looplen;
+		    v->ptr = position;
+		  }
+	      }
+	    /*  else leave the voice looping the current sample */
+
+	    if (v->mode & WAVE_LOOP_BACK && position < v->startloop)
+	      {
+		if (v->mode & WAVE_BIDIR_LOOP)
+		  {
+		    v->mode ^= WAVE_LOOP_BACK;	/* Turn around */
+		    v->step *= -1;
+		  }
+		else
+		  {
+		    position += v->looplen;
+		    v->ptr = position;
+		  }
+	      }
+
+	  }			/* Compute voice */
+
+#if 1				/* Delay */
+      left += left_delay[delayp];
+      right += right_delay[delayp];
+
+      left_delay[delayp] = right >> 2;
+      right_delay[delayp] = left >> 2;
+      delayp = (delayp + 1) % devc->delay_size;
+#endif
+
+#define AFTERSCALE devc->afterscale;
+
+      left >>= AFTERSCALE;
+      right >>= AFTERSCALE;
+
+      if (left > 32767)
+	left = 32767;
+      if (left < -32768)
+	left = -32768;
+      if (right > 32767)
+	right = 32767;
+      if (right < -32768)
+	right = -32768;
+
+#ifdef OSS_BIG_ENDIAN
+      *cbuf++ = left & 0xff;
+      *cbuf++ = (left >> 8) & 0xff;
+      *cbuf++ = right & 0xff;
+      *cbuf++ = (right >> 8) & 0xff;
+#else
+      *buf++ = left;
+      *buf++ = right;
+#endif
+      if (devc->control_counter++ >= devc->control_rate)
+	{
+	  devc->control_counter = 0;
+	  softsyn_control_loop ();
+	}
+    }				/* Mix one sample */
+}
+#endif
diff -u --recursive --new-file v2.1.62/linux/drivers/sound/sound_calls.h linux/drivers/sound/sound_calls.h
--- v2.1.62/linux/drivers/sound/sound_calls.h	Wed May 28 10:51:33 1997
+++ linux/drivers/sound/sound_calls.h	Tue Sep 30 08:46:45 1997
@@ -41,6 +41,7 @@
 
 int audio_select(int dev, struct fileinfo *file, int sel_type, poll_table * wait);
 void reorganize_buffers (int dev, struct dma_buffparms *dmap, int recording);
+int dma_ioctl (int dev, unsigned int cmd, caddr_t arg);
 
 /*
  *	System calls for the /dev/sequencer
@@ -83,7 +84,7 @@
  */
 
 /*	From soundcard.c	*/
-#ifndef __bsdi__
+#if !defined(__bsdi__) && !defined(__NjetBSD__)
 void tenmicrosec(int *osp);
 #endif
 void request_sound_timer (int count);
diff -u --recursive --new-file v2.1.62/linux/drivers/sound/sound_config.h linux/drivers/sound/sound_config.h
--- v2.1.62/linux/drivers/sound/sound_config.h	Tue Mar  4 10:25:25 1997
+++ linux/drivers/sound/sound_config.h	Sun Nov  9 22:14:51 1997
@@ -12,6 +12,7 @@
 
 
 #include "local.h"
+
 #include "os.h"
 #include "soundvers.h"
 
@@ -145,6 +146,7 @@
 #define WK_SIGNAL	0x04
 #define WK_SLEEP	0x08
 #define WK_SELECT	0x10
+#define WK_ABORT	0x20
 
 #define OPEN_READ	PCM_ENABLE_INPUT
 #define OPEN_WRITE	PCM_ENABLE_OUTPUT
diff -u --recursive --new-file v2.1.62/linux/drivers/sound/sound_switch.c linux/drivers/sound/sound_switch.c
--- v2.1.62/linux/drivers/sound/sound_switch.c	Wed May 28 10:51:33 1997
+++ linux/drivers/sound/sound_switch.c	Tue Sep 30 08:46:56 1997
@@ -195,9 +195,6 @@
   put_status (" ");
   put_status (system_utsname.machine);
   put_status ("\n");
-#ifdef MODULE
-  put_status ("Driver loaded as a module\n");
-#endif
 
 
   if (!put_status ("Config options: "))
@@ -511,7 +508,10 @@
 	return -EBUSY;
       status_busy = 1;
       if ((status_buf = (char *) vmalloc (4000)) == NULL)
-	return -EIO;
+	{
+	  status_busy = 0;
+	  return -EIO;
+	}
       status_len = status_ptr = 0;
       init_status ();
       break;
@@ -605,12 +605,15 @@
 get_mixer_info (int dev, caddr_t arg)
 {
   mixer_info      info;
+  int             i;
 
   if (dev < 0 || dev >= num_mixers)
     return -ENXIO;
 
   strcpy (info.id, mixer_devs[dev]->id);
-  strcpy (info.name, mixer_devs[dev]->name);
+  for (i = 0; i < 32 && mixer_devs[dev]->name; i++)
+    info.name[i] = mixer_devs[dev]->name[i];
+  info.name[i] = 0;
   info.modify_counter = mixer_devs[dev]->modify_counter;
 
   memcpy ((&((char *) arg)[0]), (char *) &info, sizeof (info));
@@ -621,12 +624,15 @@
 get_old_mixer_info (int dev, caddr_t arg)
 {
   _old_mixer_info info;
+  int             i;
 
   if (dev < 0 || dev >= num_mixers)
     return -ENXIO;
 
   strcpy (info.id, mixer_devs[dev]->id);
-  strcpy (info.name, mixer_devs[dev]->name);
+  for (i = 0; i < 32 && mixer_devs[dev]->name; i++)
+    info.name[i] = mixer_devs[dev]->name[i];
+  info.name[i] = 0;
 
   memcpy ((&((char *) arg)[0]), (char *) &info, sizeof (info));
   return 0;
diff -u --recursive --new-file v2.1.62/linux/drivers/sound/soundcard.c linux/drivers/sound/soundcard.c
--- v2.1.62/linux/drivers/sound/soundcard.c	Tue Sep 23 16:48:48 1997
+++ linux/drivers/sound/soundcard.c	Tue Nov  4 11:54:40 1997
@@ -56,31 +56,34 @@
 
 
 
-static long
-sound_read (struct inode *inode, struct file *file, char *buf, unsigned long count)
+static ssize_t
+sound_read (struct file *file, char *buf, size_t count, loff_t *ppos)
 {
   int             dev;
+  struct inode *inode = file->f_dentry->d_inode;
 
   dev = MINOR (inode->i_rdev);
 
   files[dev].flags = file->f_flags;
 
-  return sound_read_sw (dev, &files[dev], buf, count);
+  return (ssize_t)sound_read_sw (dev, &files[dev], buf, count);
 }
 
-static long
-sound_write (struct inode *inode, struct file *file, const char *buf, unsigned long count)
+static ssize_t
+sound_write (struct file *file, const char *buf, size_t count, loff_t *ppos)
 {
   int             dev;
+  struct inode *inode = file->f_dentry->d_inode;
 
   dev = MINOR (inode->i_rdev);
 
   files[dev].flags = file->f_flags;
 
-  return sound_write_sw (dev, &files[dev], buf, count);
+  return (ssize_t)sound_write_sw (dev, &files[dev], buf, count);
 }
 
-static long long sound_lseek (struct file *file, long long offset, int orig)
+static long long
+sound_lseek (struct file *file, long long offset, int orig)
 {
   return -EPERM;
 }
@@ -316,8 +319,7 @@
 
   if (size != dmap->bytes_in_use)
     {
-      printk ("Sound: mmap() size = %ld. Should be %d\n",
-	      size, dmap->bytes_in_use);
+      printk ("Sound: mmap() size = %ld. Should be %d\n", size, dmap->bytes_in_use);
     }
 
   if (remap_page_range (vma->vm_start, virt_to_phys (dmap->raw_buf),
@@ -325,7 +327,7 @@
 			vma->vm_page_prot))
     return -EAGAIN;
 
-  vma->vm_dentry = dget(file->f_dentry);
+  vma->vm_dentry = file->f_dentry;
 
   dmap->mapping_flags |= DMA_MAP_MAPPED;
 
@@ -571,7 +573,7 @@
 {
   if (dma_alloc_map[chn] == DMA_MAP_UNAVAIL)
     {
-      /* printk ("sound_free_dma: Bad access to DMA channel %d\n", chn); */
+      /* printk( "sound_free_dma: Bad access to DMA channel %d\n",  chn); */
       return;
     }
   free_dma (chn);
@@ -707,8 +709,7 @@
       end_addr = start_addr + dmap->buffsize - 1;
 
       if (debugmem)
-	printk ("sound: start 0x%lx, end 0x%lx\n",
-		(long) start_addr, (long) end_addr);
+	printk ("sound: start 0x%lx, end 0x%lx\n", (long) start_addr, (long) end_addr);
 
       /* now check if it fits into the same dma-pagesize */
 
@@ -716,10 +717,7 @@
 	  != ((long) end_addr & ~(dma_pagesize - 1))
 	  || end_addr >= (char *) (MAX_DMA_ADDRESS))
 	{
-	  printk (
-		   "sound: Got invalid address 0x%lx for %db DMA-buffer\n",
-		   (long) start_addr,
-		   dmap->buffsize);
+	  printk ("sound: Got invalid address 0x%lx for %db DMA-buffer\n", (long) start_addr, dmap->buffsize);
 	  return -EFAULT;
 	}
     }
@@ -771,7 +769,7 @@
 {
   unsigned long   flags;
 
-  /* printk("Start DMA%d %d, %d\n", chan, (int)(physaddr-dmap->raw_buf_phys), count); */
+  /* printk( "Start DMA%d %d, %d\n",  chan,  (int)(physaddr-dmap->raw_buf_phys),  count); */
   if (autoinit)
     dma_mode |= DMA_AUTOINIT;
   save_flags (flags);
diff -u --recursive --new-file v2.1.62/linux/drivers/sound/soundvers.h linux/drivers/sound/soundvers.h
--- v2.1.62/linux/drivers/sound/soundvers.h	Wed May 28 10:51:33 1997
+++ linux/drivers/sound/soundvers.h	Sun Nov  9 23:02:06 1997
@@ -1,2 +1,2 @@
-#define SOUND_VERSION_STRING "3.8a"
+#define SOUND_VERSION_STRING "3.8s-971110"
 #define SOUND_INTERNAL_VERSION 0x030804
diff -u --recursive --new-file v2.1.62/linux/drivers/sound/sscape.c linux/drivers/sound/sscape.c
--- v2.1.62/linux/drivers/sound/sscape.c	Wed May 28 10:51:33 1997
+++ linux/drivers/sound/sscape.c	Tue Sep 30 08:46:56 1997
@@ -15,7 +15,7 @@
 
 #include "sound_config.h"
 
-#if defined(CONFIG_SSCAPEHW)
+#ifdef CONFIG_SSCAPEHW
 
 #include "coproc.h"
 
@@ -213,15 +213,6 @@
   return data;
 }
 
-static int
-host_command1 (struct sscape_info *devc, int cmd)
-{
-  unsigned char   buf[10];
-
-  buf[0] = (unsigned char) (cmd & 0xff);
-
-  return host_write (devc, buf, 1);
-}
 
 static int
 host_command2 (struct sscape_info *devc, int cmd, int parm1)
@@ -254,7 +245,7 @@
 		 value ? 1 : 0);
   if (host_read (devc) != CMD_ACK)
     {
-      /* printk ("SNDSCAPE: Setting MT32 mode failed\n"); */
+      /* printk( "SNDSCAPE: Setting MT32 mode failed\n"); */
     }
   host_close (devc);
 }
@@ -266,7 +257,7 @@
   host_command3 (devc, CMD_SET_CONTROL, ctrl, value);
   if (host_read (devc) != CMD_ACK)
     {
-      /* printk ("SNDSCAPE: Setting control (%d) failed\n", ctrl); */
+      /* printk( "SNDSCAPE: Setting control (%d) failed\n",  ctrl); */
     }
   host_close (devc);
 }
diff -u --recursive --new-file v2.1.62/linux/drivers/sound/trix.c linux/drivers/sound/trix.c
--- v2.1.62/linux/drivers/sound/trix.c	Wed May 28 10:51:33 1997
+++ linux/drivers/sound/trix.c	Tue Sep 30 08:46:56 1997
@@ -17,7 +17,7 @@
 #include "sound_config.h"
 #include "sb.h"
 
-#if defined(CONFIG_TRIX)
+#ifdef CONFIG_TRIX
 
 #ifdef INCLUDE_TRIX_BOOT
 #include "trix_boot.h"
@@ -188,13 +188,13 @@
 
   if (hw_config->dma == 0 && inb (hw_config->io_base + 3) & 0x80)
     {
-      printk ("AudioTrix: Can't use DMA0 with a 8 bit card\n");
+      printk ("AudioTrix: Can't use DMA0 with a 8 bit card slot\n");
       return 0;
     }
 
   if (hw_config->irq > 7 && hw_config->irq != 9 && inb (hw_config->io_base + 3) & 0x80)
     {
-      printk ("AudioTrix: Can't use IRQ%d with a 8 bit card\n", hw_config->irq);
+      printk ("AudioTrix: Can't use IRQ%d with a 8 bit card slot\n", hw_config->irq);
       return 0;
     }
 
diff -u --recursive --new-file v2.1.62/linux/drivers/sound/uart401.c linux/drivers/sound/uart401.c
--- v2.1.62/linux/drivers/sound/uart401.c	Wed May 28 10:51:33 1997
+++ linux/drivers/sound/uart401.c	Tue Sep 30 08:46:57 1997
@@ -15,7 +15,8 @@
 
 #include "sound_config.h"
 
-#if defined(CONFIG_UART401) && defined(CONFIG_MIDI)
+#ifdef CONFIG_UART401
+#ifdef CONFIG_MIDI
 
 typedef struct uart401_devc
   {
@@ -233,7 +234,7 @@
 
   save_flags (flags);
   cli ();
-  for (timeout = 30000; timeout < 0 && !output_ready (devc); timeout--);
+  for (timeout = 30000; timeout > 0 && !output_ready (devc); timeout--);
 
   devc->input_byte = 0;
   uart401_cmd (devc, UART_MODE_ON);
@@ -288,7 +289,7 @@
     return;
 
   if (!devc->share_irq)
-    if (snd_set_irq_handler (devc->irq, uart401intr, "uart401", devc->osp) < 0)
+    if (snd_set_irq_handler (devc->irq, uart401intr, "MPU-401 UART", devc->osp) < 0)
       {
 	printk ("uart401: Failed to allocate IRQ%d\n", devc->irq);
 	devc->share_irq = 1;
@@ -297,7 +298,7 @@
   irq2devc[devc->irq] = devc;
   devc->my_dev = num_midis;
 
-  request_region (hw_config->io_base, 4, "SB MIDI");
+  request_region (hw_config->io_base, 4, "MPU-401 UART");
   enter_uart_mode (devc);
 
   if (num_midis >= MAX_MIDI_DEV)
@@ -363,7 +364,7 @@
 
   for (n = 0; n < 2 && !ok; n++)
     {
-      for (timeout = 30000; timeout < 0 && !output_ready (devc); timeout--);
+      for (timeout = 30000; timeout > 0 && !output_ready (devc); timeout--);
 
       devc->input_byte = 0;
       uart401_cmd (devc, MPU_RESET);
@@ -385,7 +386,7 @@
 
   if (ok)
     {
-      DDB (printk ("Reset UART401 OK\n"));
+      DEB (printk ("Reset UART401 OK\n"));
     }
   else
     DDB (printk ("Reset UART401 failed - No hardware detected.\n"));
@@ -442,7 +443,9 @@
   int             irq = hw_config->irq;
 
   if (irq < 0)
-    irq *= -1;
+    {
+      irq *= -1;
+    }
 
   if (irq < 1 || irq > 15)
     return;
@@ -459,4 +462,5 @@
 }
 
 
+#endif
 #endif
diff -u --recursive --new-file v2.1.62/linux/drivers/sound/uart6850.c linux/drivers/sound/uart6850.c
--- v2.1.62/linux/drivers/sound/uart6850.c	Wed May 28 10:51:33 1997
+++ linux/drivers/sound/uart6850.c	Tue Sep 30 08:46:57 1997
@@ -16,7 +16,8 @@
 
 #include "sound_config.h"
 
-#if defined(CONFIG_UART6850) && defined(CONFIG_MIDI)
+#ifdef CONFIG_UART6850
+#ifdef CONFIG_MIDI
 
 static int      uart6850_base = 0x330;
 
@@ -331,4 +332,5 @@
   snd_release_irq (hw_config->irq);
 }
 
+#endif
 #endif
diff -u --recursive --new-file v2.1.62/linux/fs/buffer.c linux/fs/buffer.c
--- v2.1.62/linux/fs/buffer.c	Tue Sep 23 16:48:48 1997
+++ linux/fs/buffer.c	Sat Nov  8 11:39:12 1997
@@ -553,6 +553,11 @@
 	return next;
 }
 
+struct buffer_head *efind_buffer(kdev_t dev, int block, int size)
+{
+	return find_buffer(dev, block, size);
+}
+
 /*
  * Why like this, I hear you say... The reason is race-conditions.
  * As we don't lock buffers (unless we are reading them, that is),
@@ -817,6 +822,24 @@
 	goto repeat;
 }
 
+void init_buffer(struct buffer_head *bh, kdev_t dev, int block,
+		 bh_end_io_t *handler, void *dev_id)
+{
+	bh->b_count = 1;
+	bh->b_list = BUF_CLEAN;
+	bh->b_flushtime = 0;
+	bh->b_dev = dev;
+	bh->b_blocknr = block;
+	bh->b_end_io = handler;
+	bh->b_dev_id = dev_id;
+}
+
+static void end_buffer_io_sync(struct buffer_head *bh, int uptodate)
+{
+	mark_buffer_uptodate(bh, uptodate);
+	unlock_buffer(bh);
+}
+
 /*
  * Ok, this is getblk, and it isn't very clear, again to hinder
  * race-conditions. Most of the code is seldom used, (ie repeating),
@@ -854,13 +877,9 @@
 	/* OK, FINALLY we know that this buffer is the only one of its kind,
 	 * and that it's unused (b_count=0), unlocked, and clean.
 	 */
-	bh->b_count=1;
-	bh->b_list	= BUF_CLEAN;
+	init_buffer(bh, dev, block, end_buffer_io_sync, NULL);
 	bh->b_lru_time	= jiffies;
-	bh->b_flushtime=0;
 	bh->b_state=(1<<BH_Touched);
-	bh->b_dev=dev;
-	bh->b_blocknr=block;
 	insert_into_queues(bh);
 	return bh;
 
@@ -1254,18 +1273,75 @@
 
 	tmp = bh;
 	do {
-		if (!test_bit(BH_FreeOnIO, &tmp->b_state)) {
-			printk ("Whoops: unlock_buffer: "
-				"async IO mismatch on page.\n");
-			return;
-		}
 		tmp->b_next_free = xchg(&reuse_list, NULL);
 		reuse_list = tmp;
-		clear_bit(BH_FreeOnIO, &tmp->b_state);
 		tmp = tmp->b_this_page;
 	} while (tmp != bh);
 }
 
+static void end_buffer_io_async(struct buffer_head * bh, int uptodate)
+{
+	unsigned long flags;
+	struct buffer_head *tmp;
+	struct page *page;
+
+	mark_buffer_uptodate(bh, uptodate);
+	unlock_buffer(bh);
+
+	/* This is a temporary buffer used for page I/O. */
+	page = mem_map + MAP_NR(bh->b_data);
+	if (!PageLocked(page))
+		goto not_locked;
+	if (bh->b_count != 1)
+		goto bad_count;
+
+	if (!test_bit(BH_Uptodate, &bh->b_state))
+		set_bit(PG_error, &page->flags);
+
+	/*
+	 * Be _very_ careful from here on. Bad things can happen if
+	 * two buffer heads end IO at almost the same time and both
+	 * decide that the page is now completely done.
+	 *
+	 * Async buffer_heads are here only as labels for IO, and get
+	 * thrown away once the IO for this page is complete.  IO is
+	 * deemed complete once all buffers have been visited
+	 * (b_count==0) and are now unlocked. We must make sure that
+	 * only the _last_ buffer that decrements its count is the one
+	 * that free's the page..
+	 */
+	save_flags(flags);
+	cli();
+	bh->b_count--;
+	tmp = bh;
+	do {
+		if (tmp->b_count)
+			goto still_busy;
+		tmp = tmp->b_this_page;
+	} while (tmp != bh);
+
+	/* OK, the async IO on this page is complete. */
+	free_async_buffers(bh);
+	restore_flags(flags);
+	clear_bit(PG_locked, &page->flags);
+	wake_up(&page->wait);
+	after_unlock_page(page);
+	wake_up(&buffer_wait);
+	return;
+
+still_busy:
+	restore_flags(flags);
+	return;
+
+not_locked:
+	printk ("Whoops: end_buffer_io_async: async io complete on unlocked page\n");
+	return;
+
+bad_count:
+	printk ("Whoops: end_buffer_io_async: b_count != 1 on async io.\n");
+	return;
+}
+
 /*
  * Start I/O on a page.
  * This function expects the page to be locked and may return before I/O is complete.
@@ -1298,12 +1374,7 @@
 		struct buffer_head * tmp;
 		block = *(b++);
 
-		set_bit(BH_FreeOnIO, &next->b_state);
-		next->b_list = BUF_CLEAN;
-		next->b_dev = dev;
-		next->b_blocknr = block;
-		next->b_count = 1;
-		next->b_flushtime = 0;
+		init_buffer(next, dev, block, end_buffer_io_async, NULL);
 		set_bit(BH_Uptodate, &next->b_state);
 
 		/*
@@ -1383,74 +1454,6 @@
 }
 
 /*
- * This is called by end_request() when I/O has completed.
- */
-void unlock_buffer(struct buffer_head * bh)
-{
-	unsigned long flags;
-	struct buffer_head *tmp;
-	struct page *page;
-
-	clear_bit(BH_Lock, &bh->b_state);
-	wake_up(&bh->b_wait);
-
-	if (!test_bit(BH_FreeOnIO, &bh->b_state))
-		return;
-	/* This is a temporary buffer used for page I/O. */
-	page = mem_map + MAP_NR(bh->b_data);
-	if (!PageLocked(page))
-		goto not_locked;
-	if (bh->b_count != 1)
-		goto bad_count;
-
-	if (!test_bit(BH_Uptodate, &bh->b_state))
-		set_bit(PG_error, &page->flags);
-
-	/*
-	 * Be _very_ careful from here on. Bad things can happen if
-	 * two buffer heads end IO at almost the same time and both
-	 * decide that the page is now completely done.
-	 *
-	 * Async buffer_heads are here only as labels for IO, and get
-	 * thrown away once the IO for this page is complete.  IO is
-	 * deemed complete once all buffers have been visited
-	 * (b_count==0) and are now unlocked. We must make sure that
-	 * only the _last_ buffer that decrements its count is the one
-	 * that free's the page..
-	 */
-	save_flags(flags);
-	cli();
-	bh->b_count--;
-	tmp = bh;
-	do {
-		if (tmp->b_count)
-			goto still_busy;
-		tmp = tmp->b_this_page;
-	} while (tmp != bh);
-
-	/* OK, the async IO on this page is complete. */
-	free_async_buffers(bh);
-	restore_flags(flags);
-	clear_bit(PG_locked, &page->flags);
-	wake_up(&page->wait);
-	after_unlock_page(page);
-	wake_up(&buffer_wait);
-	return;
-
-still_busy:
-	restore_flags(flags);
-	return;
-
-not_locked:
-	printk ("Whoops: unlock_buffer: async io complete on unlocked page\n");
-	return;
-
-bad_count:
-	printk ("Whoops: unlock_buffer: b_count != 1 on async io.\n");
-	return;
-}
-
-/*
  * Generic "readpage" function for block devices that have the normal
  * bmap functionality. This is most of the block device filesystems.
  * Reads the page asynchronously --- the unlock_buffer() and
@@ -1755,7 +1758,7 @@
 	if (ncount) printk("sync_old_buffers: %d dirty buffers not on dirty list\n", ncount);
 	printk("Wrote %d/%d buffers\n", nwritten, ndirty);
 #endif
-	
+	run_task_queue(&tq_disk);
 	return 0;
 }
 
@@ -1916,7 +1919,7 @@
 		 * dirty buffers, then make the next write to a
 		 * loop device to be a blocking write.
 		 * This lets us block--which we _must_ do! */
-		if (ndirty == 0 && nr_buffers_type[BUF_DIRTY] > 0) {
+		if (ndirty == 0 && nr_buffers_type[BUF_DIRTY] > 0 && wrta_cmd != WRITE) {
 			wrta_cmd = WRITE;
 			continue;
 		}
@@ -1925,7 +1928,7 @@
 		
 		/* If there are still a lot of dirty buffers around, skip the sleep
 		   and flush some more */
-		if(nr_buffers_type[BUF_DIRTY] <= nr_buffers * bdf_prm.b_un.nfract/100) {
+		if(ndirty == 0 || nr_buffers_type[BUF_DIRTY] <= nr_buffers * bdf_prm.b_un.nfract/100) {
 			current->signal = 0;
 			interruptible_sleep_on(&bdflush_wait);
 		}
diff -u --recursive --new-file v2.1.62/linux/fs/dcache.c linux/fs/dcache.c
--- v2.1.62/linux/fs/dcache.c	Sat Nov  1 11:04:27 1997
+++ linux/fs/dcache.c	Tue Nov  4 10:26:38 1997
@@ -97,12 +97,14 @@
 			goto out;
 	}
 
-	if (!list_empty(&dentry->d_lru))
+	if (!list_empty(&dentry->d_lru)) {
 		dentry_stat.nr_unused--;
-	list_del(&dentry->d_lru);
+		list_del(&dentry->d_lru);
+	}
 	if (list_empty(&dentry->d_hash)) {
 		struct inode *inode = dentry->d_inode;
 		struct dentry * parent;
+		list_del(&dentry->d_child);
 		if (inode) {
 			dentry->d_inode = NULL;
 			iput(inode);
@@ -247,6 +249,7 @@
 	struct dentry * parent;
 
 	list_del(&dentry->d_hash);
+	list_del(&dentry->d_child);
 	if (dentry->d_inode) {
 		struct inode * inode = dentry->d_inode;
 
@@ -338,6 +341,69 @@
 }
 
 /*
+ * Search the dentry child list for the specified parent,
+ * and move any unused dentries to the end of the unused
+ * list for prune_dcache(). We descend to the next level
+ * whenever the d_subdirs list is non-empty and continue
+ * searching.
+ */
+static int select_parent(struct dentry * parent)
+{
+	struct dentry *this_parent = parent;
+	struct list_head *next;
+	int found = 0;
+
+repeat:
+	next = this_parent->d_subdirs.next;
+resume:
+	while (next != &this_parent->d_subdirs) {
+		struct list_head *tmp = next;
+		struct dentry *dentry = list_entry(tmp, struct dentry, d_child);
+		next = tmp->next;
+		if (!dentry->d_count) {
+			list_del(&dentry->d_lru);
+			list_add(&dentry->d_lru, dentry_unused.prev);
+			found++;
+		}
+		/*
+		 * Descend a level if the d_subdirs list is non-empty.
+		 */
+		if (!list_empty(&dentry->d_subdirs)) {
+			this_parent = dentry;
+#ifdef DCACHE_DEBUG
+printk("select_parent: descending to %s/%s, found=%d\n",
+dentry->d_parent->d_name.name, dentry->d_name.name, found);
+#endif
+			goto repeat;
+		}
+	}
+	/*
+	 * All done at this level ... ascend and resume the search.
+	 */
+	if (this_parent != parent) {
+		next = this_parent->d_child.next; 
+		this_parent = this_parent->d_parent;
+#ifdef DCACHE_DEBUG
+printk("select_parent: ascending to %s/%s, found=%d\n",
+this_parent->d_parent->d_name.name, this_parent->d_name.name, found);
+#endif
+		goto resume;
+	}
+	return found;
+}
+
+/*
+ * Prune the dcache to remove unused children of the parent dentry.
+ */
+void shrink_dcache_parent(struct dentry * parent)
+{
+	int found;
+
+	while ((found = select_parent(parent)) != 0)
+		prune_dcache(found);
+}
+
+/*
  * This is called from do_try_to_free_page() to indicate
  * that we should reduce the dcache and inode cache memory.
  */
@@ -415,11 +481,15 @@
 	if (parent) {
 		dentry->d_parent = dget(parent);
 		dentry->d_sb = parent->d_sb;
-	}
+		list_add(&dentry->d_child, &parent->d_subdirs);
+	} else
+		INIT_LIST_HEAD(&dentry->d_child);
+		
 	dentry->d_mounts = dentry;
 	dentry->d_covers = dentry;
 	INIT_LIST_HEAD(&dentry->d_hash);
 	INIT_LIST_HEAD(&dentry->d_lru);
+	INIT_LIST_HEAD(&dentry->d_subdirs);
 
 	dentry->d_name.name = str;
 	dentry->d_name.len = name->len;
@@ -608,11 +678,16 @@
 	list_del(&target->d_hash);
 	INIT_LIST_HEAD(&target->d_hash);
 
+	list_del(&dentry->d_child);
+	list_del(&target->d_child);
+
 	/* Switch the parents and the names.. */
 	switch(dentry->d_parent, target->d_parent);
 	switch(dentry->d_name.name, target->d_name.name);
 	switch(dentry->d_name.len, target->d_name.len);
 	switch(dentry->d_name.hash, target->d_name.hash);
+	list_add(&target->d_child, &target->d_parent->d_subdirs);
+	list_add(&dentry->d_child, &dentry->d_parent->d_subdirs);
 }
 
 /*
diff -u --recursive --new-file v2.1.62/linux/fs/ext2/namei.c linux/fs/ext2/namei.c
--- v2.1.62/linux/fs/ext2/namei.c	Mon Aug 11 14:47:05 1997
+++ linux/fs/ext2/namei.c	Sat Nov  8 11:46:32 1997
@@ -353,8 +353,6 @@
 	struct ext2_dir_entry * de;
 	int err = -EIO;
 
-	if (!dir)
-		return -ENOENT;
 	/*
 	 * N.B. Several error exits in ext2_new_inode don't set err.
 	 */
@@ -391,15 +389,13 @@
 	struct ext2_dir_entry * de;
 	int err = -EIO;
 
-	if (!dir)
-		return -ENOENT;
-
+	err = -ENAMETOOLONG;
 	if (dentry->d_name.len > EXT2_NAME_LEN)
-		return -ENAMETOOLONG;
+		goto out;
 
 	inode = ext2_new_inode (dir, mode, &err);
 	if (!inode)
-		return err;
+		goto out;
 
 	inode->i_uid = current->fsuid;
 	inode->i_mode = mode;
@@ -423,12 +419,8 @@
 		inode->i_rdev = to_kdev_t(rdev);
 	mark_inode_dirty(inode);
 	bh = ext2_add_entry (dir, dentry->d_name.name, dentry->d_name.len, &de, &err);
-	if (!bh) {
-		inode->i_nlink--;
-		mark_inode_dirty(inode);
-		iput(inode);
-		return err;
-	}
+	if (!bh)
+		goto out_no_entry;
 	de->inode = cpu_to_le32(inode->i_ino);
 	dir->i_version = ++event;
 	mark_buffer_dirty(bh, 1);
@@ -436,9 +428,17 @@
 		ll_rw_block (WRITE, 1, &bh);
 		wait_on_buffer (bh);
 	}
-	brelse(bh);
 	d_instantiate(dentry, inode);
-	return 0;
+	brelse(bh);
+	err = 0;
+out:
+	return err;
+
+out_no_entry:
+	inode->i_nlink--;
+	mark_inode_dirty(inode);
+	iput(inode);
+	goto out;
 }
 
 int ext2_mkdir(struct inode * dir, struct dentry * dentry, int mode)
@@ -446,23 +446,26 @@
 	struct inode * inode;
 	struct buffer_head * bh, * dir_block;
 	struct ext2_dir_entry * de;
-	int err = -EIO;
+	int err;
 
+	err = -ENAMETOOLONG;
 	if (dentry->d_name.len > EXT2_NAME_LEN)
-		return -ENAMETOOLONG;
+		goto out;
 
+	err = -EMLINK;
 	if (dir->i_nlink >= EXT2_LINK_MAX)
-		return -EMLINK;
+		goto out;
 
+	err = -EIO;
 	inode = ext2_new_inode (dir, S_IFDIR, &err);
 	if (!inode)
-		return err;
+		goto out;
 
 	inode->i_op = &ext2_dir_inode_operations;
 	inode->i_size = inode->i_sb->s_blocksize;
 	dir_block = ext2_bread (inode, 0, 1, &err);
 	if (!dir_block) {
-		inode->i_nlink--;
+		inode->i_nlink--; /* is this nlink == 0? */
 		mark_inode_dirty(inode);
 		iput (inode);
 		return err;
@@ -486,12 +489,8 @@
 		inode->i_mode |= S_ISGID;
 	mark_inode_dirty(inode);
 	bh = ext2_add_entry (dir, dentry->d_name.name, dentry->d_name.len, &de, &err);
-	if (!bh) {
-		inode->i_nlink = 0;
-		mark_inode_dirty(inode);
-		iput (inode);
-		return err;
-	}
+	if (!bh)
+		goto out_no_entry;
 	de->inode = cpu_to_le32(inode->i_ino);
 	dir->i_version = ++event;
 	mark_buffer_dirty(bh, 1);
@@ -503,7 +502,15 @@
 	mark_inode_dirty(dir);
 	d_instantiate(dentry, inode);
 	brelse (bh);
-	return 0;
+	err = 0;
+out:
+	return err;
+
+out_no_entry:
+	inode->i_nlink = 0;
+	mark_inode_dirty(inode);
+	iput (inode);
+	goto out;
 }
 
 /*
@@ -572,25 +579,23 @@
 	struct buffer_head * bh;
 	struct ext2_dir_entry * de;
 
-	if (!dir)
-		return -ENOENT;
-	inode = NULL;
+	retval = -ENAMETOOLONG;
 	if (dentry->d_name.len > EXT2_NAME_LEN)
-		return -ENAMETOOLONG;
+		goto out;
 
-	bh = ext2_find_entry (dir, dentry->d_name.name, dentry->d_name.len, &de);
 	retval = -ENOENT;
+	bh = ext2_find_entry (dir, dentry->d_name.name, dentry->d_name.len, &de);
 	if (!bh)
 		goto end_rmdir;
-	retval = -EPERM;
-	inode = dentry->d_inode;
 
+	inode = dentry->d_inode;
 	if (inode->i_sb->dq_op)
 		inode->i_sb->dq_op->initialize (inode, -1);
 
-        if ((dir->i_mode & S_ISVTX) && !fsuser() &&
-            current->fsuid != inode->i_uid &&
-            current->fsuid != dir->i_uid)
+	retval = -EPERM;
+	if ((dir->i_mode & S_ISVTX) && 
+	    current->fsuid != inode->i_uid &&
+	    current->fsuid != dir->i_uid && !fsuser())
 		goto end_rmdir;
 	if (inode == dir)	/* we may not delete ".", but "../dir" is ok */
 		goto end_rmdir;
@@ -606,12 +611,19 @@
 		goto end_rmdir;
 
 	down(&inode->i_sem);
+	/*
+	 * Prune any child dentries so that this dentry becomes negative.
+	 */
+	if (dentry->d_count > 1) {
+		printk("ext2_rmdir: d_count=%d, pruning\n", dentry->d_count);
+		shrink_dcache_parent(dentry);
+	}
 	if (!empty_dir (inode))
 		retval = -ENOTEMPTY;
 	else if (le32_to_cpu(de->inode) != inode->i_ino)
 		retval = -ENOENT;
 	else {
-		if (inode->i_count > 1) {
+		if (dentry->d_count > 1) {
 		/*
 		 * Are we deleting the last instance of a busy directory?
 		 * Better clean up if so.
@@ -646,6 +658,7 @@
 
 end_rmdir:
 	brelse (bh);
+out:
 	return retval;
 }
 
@@ -656,11 +669,11 @@
 	struct buffer_head * bh;
 	struct ext2_dir_entry * de;
 
-	retval = -ENOENT;
-	inode = NULL;
+	retval = -ENAMETOOLONG;
 	if (dentry->d_name.len > EXT2_NAME_LEN)
-		return -ENAMETOOLONG;
+		goto out;
 
+	retval = -ENOENT;
 	bh = ext2_find_entry (dir, dentry->d_name.name, dentry->d_name.len, &de);
 	if (!bh)
 		goto end_unlink;
@@ -674,9 +687,9 @@
 		goto end_unlink;
 	if (IS_APPEND(inode) || IS_IMMUTABLE(inode))
 		goto end_unlink;
-	if ((dir->i_mode & S_ISVTX) && !fsuser() &&
+	if ((dir->i_mode & S_ISVTX) &&
 	    current->fsuid != inode->i_uid &&
-	    current->fsuid != dir->i_uid)
+	    current->fsuid != dir->i_uid && !fsuser())
 		goto end_unlink;
 
 	retval = -EIO;
@@ -708,13 +721,14 @@
 
 end_unlink:
 	brelse (bh);
+out:
 	return retval;
 }
 
 int ext2_symlink (struct inode * dir, struct dentry *dentry, const char * symname)
 {
 	struct ext2_dir_entry * de;
-	struct inode * inode = NULL;
+	struct inode * inode;
 	struct buffer_head * bh = NULL, * name_block = NULL;
 	char * link;
 	int i, l, err = -EIO;
@@ -758,12 +772,8 @@
 	mark_inode_dirty(inode);
 
 	bh = ext2_add_entry (dir, dentry->d_name.name, dentry->d_name.len, &de, &err);
-	if (!bh) {
-		inode->i_nlink--;
-		mark_inode_dirty(inode);
-		iput (inode);
-		return err;
-	}
+	if (!bh)
+		goto out_no_entry;
 	de->inode = cpu_to_le32(inode->i_ino);
 	dir->i_version = ++event;
 	mark_buffer_dirty(bh, 1);
@@ -773,7 +783,15 @@
 	}
 	brelse (bh);
 	d_instantiate(dentry, inode);
-	return 0;
+	err = 0;
+out:
+	return err;
+
+out_no_entry:
+	inode->i_nlink--;
+	mark_inode_dirty(inode);
+	iput (inode);
+	goto out;
 }
 
 int ext2_link (struct inode * inode, struct inode * dir, struct dentry *dentry)
@@ -888,10 +906,9 @@
 				new_inode->i_sb->dq_op->initialize (new_inode, -1);
 		}
 	}
-	if (new_inode == old_inode) {
-		retval = 0;
+	retval = 0;
+	if (new_inode == old_inode)
 		goto end_rename;
-	}
 	if (new_inode && S_ISDIR(new_inode->i_mode)) {
 		retval = -EISDIR;
 		if (!S_ISDIR(old_inode->i_mode))
@@ -903,7 +920,7 @@
 		if (!empty_dir (new_inode))
 			goto end_rename;
 		retval = -EBUSY;
-		if (new_inode->i_count > 1)
+		if (new_dentry->d_count > 1)
 			goto end_rename;
 	}
 	retval = -EPERM;
diff -u --recursive --new-file v2.1.62/linux/fs/fat/cache.c linux/fs/fat/cache.c
--- v2.1.62/linux/fs/fat/cache.c	Sat Oct 25 02:44:17 1997
+++ linux/fs/fat/cache.c	Thu Nov  6 17:42:44 1997
@@ -188,7 +188,8 @@
 		    && walk->ino == inode->i_ino
 		    && walk->file_cluster == f_clu) {
 			if (walk->disk_cluster != d_clu) {
-				printk("FAT cache corruption");
+				printk("FAT cache corruption inode=%ld\n",
+					inode->i_ino);
 				fat_cache_inval_inode(inode);
 				return;
 			}
diff -u --recursive --new-file v2.1.62/linux/fs/fat/dir.c linux/fs/fat/dir.c
--- v2.1.62/linux/fs/fat/dir.c	Sat Oct 25 02:44:17 1997
+++ linux/fs/fat/dir.c	Sat Nov  8 11:58:11 1997
@@ -97,7 +97,7 @@
 	return (op - ascii);
 }
 
-
+#if 0
 static void dump_de(struct msdos_dir_entry *de)
 {
 	int i;
@@ -109,7 +109,7 @@
 	}
 	printk("]\n");
 }
-
+#endif
 int fat_readdirx(
 	struct inode *inode,
 	struct file *filp,
diff -u --recursive --new-file v2.1.62/linux/fs/fat/inode.c linux/fs/fat/inode.c
--- v2.1.62/linux/fs/fat/inode.c	Sat Oct 25 02:44:17 1997
+++ linux/fs/fat/inode.c	Thu Nov  6 17:42:44 1997
@@ -5,7 +5,6 @@
  *  VFAT extensions by Gordon Chaffee, merged with msdos fs by Henrik Storner
  */
 
-#define ASC_LINUX_VERSION(V, P, S)	(((V) * 65536) + ((P) * 256) + (S))
 #include <linux/version.h>
 #define __NO_VERSION__
 #include <linux/module.h>
@@ -25,79 +24,111 @@
 
 #include "msbuffer.h"
 
-#if LINUX_VERSION_CODE >= ASC_LINUX_VERSION(2,1,0)
 #include <asm/uaccess.h>
-#define FAT_COPY_TO_USER(uaddr, kaddr, len) copy_to_user(uaddr, kaddr, len)
-#else
-#include <asm/segment.h>
-#define FAT_COPY_TO_USER(uaddr, kaddr, len) memcpy_tofs(uaddr, kaddr, len)
-#endif
 #include <asm/unaligned.h>
 
-#if 0
+/* #define FAT_PARANOIA 1 */
+#ifdef FAT_DEBUG
 #  define PRINTK(x) printk x
 #else
 #  define PRINTK(x)
 #endif
 
-void fat_put_inode(struct inode *inode)
-{
-	struct inode *depend, *linked;
-	struct super_block *sb;
-
-	depend = MSDOS_I(inode)->i_depend;
-	linked = MSDOS_I(inode)->i_linked;
-	sb = inode->i_sb;
-	if (inode->i_nlink) {
-		if (depend) {
-			iput(depend);
-		}
-		if (linked) {
-			iput(linked);
-			MSDOS_I(inode)->i_linked = NULL;
-		}
-		if (MSDOS_I(inode)->i_busy) fat_cache_inval_inode(inode);
-	}
-}
-
-void fat_delete_inode(struct inode *inode)
+/*
+ * Free any dependent inodes at the (effective) last use.
+ */
+static int fat_free_links(struct inode *inode)
 {
-	struct inode *depend, *linked;
-	struct super_block *sb;
+	struct inode *depend, *linked, *old_inode;
+	int success = 0;
 
+	/*
+	 * Clear the fields first to avoid races
+	 */
 	depend = MSDOS_I(inode)->i_depend;
+	MSDOS_I(inode)->i_depend = NULL;
 	linked = MSDOS_I(inode)->i_linked;
-	sb = inode->i_sb;
+	MSDOS_I(inode)->i_linked = NULL;
 
-	inode->i_size = 0;
-	fat_truncate(inode);
 	if (depend) {
-		if (MSDOS_I(depend)->i_old != inode) {
-			printk("Invalid link (0x%p): expected 0x%p, got 0x%p\n",
-			    depend, inode, MSDOS_I(depend)->i_old);
-			fat_fs_panic(sb,"...");
-			goto done;
+#ifdef FAT_PARANOIA
+printk("fat_put_inode: depend inode is %ld, i_count=%d\n",
+depend->i_ino, depend->i_count); 
+#endif
+		old_inode = MSDOS_I(depend)->i_old;
+		if (old_inode != inode) {
+			printk("fat_free_link: Invalid depend for inode %ld: "
+				"expected 0x%p, got 0x%p\n",
+				depend->i_ino, inode, old_inode);
+			goto out;
 		}
 		MSDOS_I(depend)->i_old = NULL;
 		iput(depend);
 	}
+
 	if (linked) {
-		if (MSDOS_I(linked)->i_oldlink != inode) {
-			printk("Invalid link (0x%p): expected 0x%p, got 0x%p\n",
-			    linked, inode, MSDOS_I(linked)->i_oldlink);
-			fat_fs_panic(sb,"...");
-			goto done;
+#ifdef FAT_PARANOIA
+printk("fat_put_inode: linked inode is %ld, i_count=%d\n",
+linked->i_ino, linked->i_count); 
+#endif
+		old_inode = MSDOS_I(linked)->i_oldlink;
+		if (old_inode != inode) {
+			printk("fat_free_link: Invalid link for inode %ld: "
+				"expected 0x%p, got 0x%p\n",
+				linked->i_ino, inode, old_inode);
+			goto out;
 		}
 		MSDOS_I(linked)->i_oldlink = NULL;
 		iput(linked);
 	}
-done:
+	success = 1;
+out:
+	return success;
+}
+
+/*
+ * This is a little tricky, as we may have links and may be linked
+ * by other inodes. Also, we're subject to race conditions ...
+ */
+void fat_put_inode(struct inode *inode)
+{
+	int last_use = 1;
+
+	/*
+	 * Check whether we're a dependent of other inodes ...
+	 */ 
+	if (MSDOS_I(inode)->i_oldlink)
+		last_use++;
+	if (MSDOS_I(inode)->i_old)
+		last_use++;
+
+	if (inode->i_count <= last_use) {
+#ifdef FAT_PARANOIA
+printk("fat_put_inode: last use for %ld, i_count=%d\n",
+inode->i_ino, inode->i_count); 
+#endif
+		if (inode->i_nlink) {
+			if (MSDOS_I(inode)->i_busy)
+				fat_cache_inval_inode(inode);
+			fat_free_links(inode);
+		}
+	}
+}
+
+void fat_delete_inode(struct inode *inode)
+{
+	fat_cache_inval_inode(inode);
+	inode->i_size = 0;
+	fat_truncate(inode);
+	if (!fat_free_links(inode))
+		fat_fs_panic(inode->i_sb,"..."); /* is this necessary? */
 	clear_inode(inode);
 }
 
 
 void fat_put_super(struct super_block *sb)
 {
+	lock_super(sb);
 	if (MSDOS_SB(sb)->fat_bits == 32) {
 		fat_clusters_flush(sb);
 	}
@@ -111,12 +142,15 @@
 	if (MSDOS_SB(sb)->nls_io) {
 		unload_nls(MSDOS_SB(sb)->nls_io);
 		MSDOS_SB(sb)->nls_io = NULL;
-		if (MSDOS_SB(sb)->options.iocharset) {
-			kfree(MSDOS_SB(sb)->options.iocharset);
-			MSDOS_SB(sb)->options.iocharset = NULL;
-		}
 	}
-	lock_super(sb);
+	/*
+	 * Note: the iocharset option might have been specified
+	 * without enabling nls_io, so check for it here.
+	 */
+	if (MSDOS_SB(sb)->options.iocharset) {
+		kfree(MSDOS_SB(sb)->options.iocharset);
+		MSDOS_SB(sb)->options.iocharset = NULL;
+	}
 	sb->s_dev = 0;
 	unlock_super(sb);
 	MOD_DEC_USE_COUNT;
@@ -129,7 +163,7 @@
 {
 	char *this_char,*value,save,*savep;
 	char *p;
-	int ret, len;
+	int ret = 1, len;
 
 	opts->name_check = 'n';
 	opts->conversion = 'b';
@@ -142,11 +176,12 @@
 	opts->iocharset = NULL;
 	*debug = *fat = 0;
 
-	if (!options) return 1;
+	if (!options)
+		goto out;
 	save = 0;
 	savep = NULL;
-	ret = 1;
-	for (this_char = strtok(options,","); this_char; this_char = strtok(NULL,",")) {
+	for (this_char = strtok(options,","); this_char;
+	     this_char = strtok(NULL,",")) {
 		if ((value = strchr(this_char,'=')) != NULL) {
 			save = *value;
 			savep = value;
@@ -155,17 +190,23 @@
 		if (!strcmp(this_char,"check") && value) {
 			if (value[0] && !value[1] && strchr("rns",*value))
 				opts->name_check = *value;
-			else if (!strcmp(value,"relaxed")) opts->name_check = 'r';
-			else if (!strcmp(value,"normal")) opts->name_check = 'n';
-			else if (!strcmp(value,"strict")) opts->name_check = 's';
+			else if (!strcmp(value,"relaxed"))
+				opts->name_check = 'r';
+			else if (!strcmp(value,"normal"))
+				opts->name_check = 'n';
+			else if (!strcmp(value,"strict"))
+				opts->name_check = 's';
 			else ret = 0;
 		}
 		else if (!strcmp(this_char,"conv") && value) {
 			if (value[0] && !value[1] && strchr("bta",*value))
 				opts->conversion = *value;
-			else if (!strcmp(value,"binary")) opts->conversion = 'b';
-			else if (!strcmp(value,"text")) opts->conversion = 't';
-			else if (!strcmp(value,"auto")) opts->conversion = 'a';
+			else if (!strcmp(value,"binary"))
+				opts->conversion = 'b';
+			else if (!strcmp(value,"text"))
+				opts->conversion = 't';
+			else if (!strcmp(value,"auto"))
+				opts->conversion = 'a';
 			else ret = 0;
 		}
 		else if (!strcmp(this_char,"dots")) {
@@ -222,8 +263,11 @@
 		}
 		else if (!strcmp(this_char,"blocksize")) {
 			if (*value) ret = 0;
-			else if (*blksize != 512 && *blksize != 1024 && *blksize != 2048){
-				printk ("MSDOS FS: Invalid blocksize (512, 1024, or 2048)\n");
+			else if (*blksize != 512  &&
+				 *blksize != 1024 &&
+				 *blksize != 2048) {
+				printk ("MSDOS FS: Invalid blocksize "
+					"(512, 1024, or 2048)\n");
 			}
 		}
 		else if (!strcmp(this_char,"sys_immutable")) {
@@ -233,46 +277,54 @@
 		else if (!strcmp(this_char,"codepage")) {
 			opts->codepage = simple_strtoul(value,&value,0);
 			if (*value) ret = 0;
-			else printk ("MSDOS FS: Using codepage %d\n", opts->codepage);
+			else printk ("MSDOS FS: Using codepage %d\n",
+					opts->codepage);
 		}
 		else if (!strcmp(this_char,"iocharset")) {
 			p = value;
 			while (*value && *value != ',') value++;
 			len = value - p;
-			if (len) {
-				opts->iocharset = kmalloc(len+1, GFP_KERNEL);
-				memcpy(opts->iocharset, p, len);
-				opts->iocharset[len] = 0;
-				printk ("MSDOS FS: Using IO charset %s\n",
-					opts->iocharset);
-			} else {
-				opts->iocharset = NULL;
-				ret = 0;
+			if (len) { 
+				char * buffer = kmalloc(len+1, GFP_KERNEL);
+				if (buffer) {
+					opts->iocharset = buffer;
+					memcpy(buffer, p, len);
+					buffer[len] = 0;
+					printk("MSDOS FS: IO charset %s\n",
+						buffer);
+				} else
+					ret = 0;
 			}
 		}
 
 		if (this_char != options) *(this_char-1) = ',';
 		if (value) *savep = save;
-		if (ret == 0) return 0;
+		if (ret == 0)
+			break;
 	}
-	return 1;
+out:
+	return ret;
 }
 
-
-/* Read the super block of an MS-DOS FS. */
-
-struct super_block *fat_read_super(struct super_block *sb,void *data, int silent)
+/*
+ * Read the super block of an MS-DOS FS.
+ *
+ * Note that this may be called from vfat_read_super
+ * with some fields already initialized.
+ */
+struct super_block *
+fat_read_super(struct super_block *sb, void *data, int silent)
 {
+	struct inode *root_inode;
 	struct buffer_head *bh;
 	struct fat_boot_sector *b;
+	char *p;
 	int data_sectors,logical_sector_size,sector_mult,fat_clusters=0;
 	int debug,error,fat,cp;
 	int blksize = 512;
 	int fat32;
 	struct fat_mount_options opts;
 	char buf[50];
-	char *p;
-	struct inode *root_inode;
 
 	MOD_INC_USE_COUNT;
 	if (hardsect_size[MAJOR(sb->s_dev)] != NULL){
@@ -281,14 +333,14 @@
 			printk ("MSDOS: Hardware sector size is %d\n",blksize);
 		}
 	}
+
 	opts.isvfat = MSDOS_SB(sb)->options.isvfat;
 	if (!parse_options((char *) data, &fat, &blksize, &debug, &opts)
-		|| (blksize != 512 && blksize != 1024 && blksize != 2048)) 
-	{
-		sb->s_dev = 0;
-		MOD_DEC_USE_COUNT;
-		return NULL;
-	}
+	    || (blksize != 512 && blksize != 1024 && blksize != 2048))
+		goto out_fail;
+	/* N.B. we should parse directly into the sb structure */
+	memcpy(&(MSDOS_SB(sb)->options), &opts, sizeof(struct fat_mount_options));
+
 	cache_init();
 	lock_super(sb);
 	if( blksize > 1024 )
@@ -307,10 +359,7 @@
 	unlock_super(sb);
 	if (bh == NULL || !fat_is_uptodate(sb,bh)) {
 		fat_brelse (sb, bh);
-		sb->s_dev = 0;
-		printk("FAT bread failed\n");
-		MOD_DEC_USE_COUNT;
-		return NULL;
+		goto out_no_bread;
 	}
 	set_blocksize(sb->s_dev, blksize);
 
@@ -351,7 +400,9 @@
 		fsinfo = (struct fat_boot_fsinfo *)
 			&bh->b_data[MSDOS_SB(sb)->fsinfo_offset];
 		if (CF_LE_L(fsinfo->signature) != 0x61417272) {
-			printk("fat_read_super: Did not find valid FSINFO signature. Found 0x%x\n", CF_LE_L(fsinfo->signature));
+			printk("fat_read_super: Did not find valid FSINFO "
+				"signature. Found 0x%x\n",
+				CF_LE_L(fsinfo->signature));
 		} else {
 			MSDOS_SB(sb)->free_clusters = CF_LE_L(fsinfo->free_clusters);
 		}
@@ -405,8 +456,10 @@
 		       opts.conversion,opts.fs_uid,opts.fs_gid,opts.fs_umask,
 		       MSDOS_CAN_BMAP(MSDOS_SB(sb)) ? ",bmap" : "");
 		printk("[me=0x%x,cs=%d,#f=%d,fs=%d,fl=%d,ds=%d,de=%d,data=%d,"
-		       "se=%d,ts=%ld,ls=%d,rc=%ld,fc=%u]\n",b->media,MSDOS_SB(sb)->cluster_size,
-		       MSDOS_SB(sb)->fats,MSDOS_SB(sb)->fat_start,MSDOS_SB(sb)->fat_length,
+		       "se=%d,ts=%ld,ls=%d,rc=%ld,fc=%u]\n",
+			b->media,MSDOS_SB(sb)->cluster_size,
+			MSDOS_SB(sb)->fats,MSDOS_SB(sb)->fat_start,
+			MSDOS_SB(sb)->fat_length,
 		       MSDOS_SB(sb)->dir_start,MSDOS_SB(sb)->dir_entries,
 		       MSDOS_SB(sb)->data_start,
 		       CF_LE_W(*(unsigned short *) &b->sectors),
@@ -416,67 +469,70 @@
 	}
 	if (MSDOS_SB(sb)->clusters+2 > fat_clusters)
 		MSDOS_SB(sb)->clusters = fat_clusters-2;
-	if (error) {
-		if (!silent)
-			printk("VFS: Can't find a valid MSDOS filesystem on dev "
-			       "%s.\n", kdevname(sb->s_dev));
-		sb->s_dev = 0;
-		MOD_DEC_USE_COUNT;
-		return NULL;
-	}
+	if (error)
+		goto out_invalid;
+
 	sb->s_magic = MSDOS_SUPER_MAGIC;
 	/* set up enough so that it can read an inode */
 	MSDOS_SB(sb)->fat_wait = NULL;
 	MSDOS_SB(sb)->fat_lock = 0;
 	MSDOS_SB(sb)->prev_free = 0;
-	memcpy(&(MSDOS_SB(sb)->options), &opts, sizeof(struct fat_mount_options));
 
 	cp = opts.codepage ? opts.codepage : 437;
 	sprintf(buf, "cp%d", cp);
 	MSDOS_SB(sb)->nls_disk = load_nls(buf);
 	if (! MSDOS_SB(sb)->nls_disk) {
 		/* Fail only if explicit charset specified */
-		if (opts.codepage == 0) {
-			MSDOS_SB(sb)->options.codepage = 0;
-			MSDOS_SB(sb)->nls_disk = load_nls_default();
-		} else {
-			sb->s_dev = 0;
-			MOD_DEC_USE_COUNT;
-			return NULL;
-		}
+		if (opts.codepage != 0)
+			goto out_fail;
+		MSDOS_SB(sb)->options.codepage = 0; /* already 0?? */
+		MSDOS_SB(sb)->nls_disk = load_nls_default();
 	}
 
-	p = opts.iocharset ? opts.iocharset : "iso8859-1";
+	MSDOS_SB(sb)->nls_io = NULL;
 	if (MSDOS_SB(sb)->options.isvfat && !opts.utf8) {
+		p = opts.iocharset ? opts.iocharset : "iso8859-1";
 		MSDOS_SB(sb)->nls_io = load_nls(p);
 		if (! MSDOS_SB(sb)->nls_io) {
 			/* Fail only if explicit charset specified */
-			if (opts.iocharset) {
-				kfree(opts.iocharset);
-				unload_nls(MSDOS_SB(sb)->nls_disk);
-				sb->s_dev = 0;
-				MOD_DEC_USE_COUNT;
-				return NULL;
-			} else {
-				MSDOS_SB(sb)->nls_io = load_nls_default();
-			}
+			if (opts.iocharset)
+				goto out_unload_nls;
+			MSDOS_SB(sb)->nls_io = load_nls_default();
 		}
 	}
 
-	root_inode = iget(sb,MSDOS_ROOT_INO);
+	root_inode = iget(sb, MSDOS_ROOT_INO);
+	if (!root_inode)
+		goto out_no_root;
 	sb->s_root = d_alloc_root(root_inode, NULL);
-	if (!sb->s_root) {
-		sb->s_dev = 0;
-		printk("get root inode failed\n");
-		unload_nls(MSDOS_SB(sb)->nls_disk);
-		if (MSDOS_SB(sb)->nls_io) unload_nls(MSDOS_SB(sb)->nls_io);
-		if (opts.iocharset) kfree(opts.iocharset);
-		MOD_DEC_USE_COUNT;
-		return NULL;
-	}
+	if (!sb->s_root)
+		goto out_no_root;
 	return sb;
-}
 
+out_no_root:
+	printk("get root inode failed\n");
+	iput(root_inode);
+	if (MSDOS_SB(sb)->nls_io)
+		unload_nls(MSDOS_SB(sb)->nls_io);
+out_unload_nls:
+	unload_nls(MSDOS_SB(sb)->nls_disk);
+	goto out_fail;
+out_invalid:
+	if (!silent)
+		printk("VFS: Can't find a valid MSDOS filesystem on dev %s.\n",
+			kdevname(sb->s_dev));
+	goto out_fail;
+out_no_bread:
+	printk("FAT bread failed\n");
+out_fail:
+	if (opts.iocharset) {
+		printk("VFS: freeing iocharset=%s\n", opts.iocharset);
+		kfree(opts.iocharset);
+	}
+	sb->s_dev = 0;
+	MOD_DEC_USE_COUNT;
+	return NULL;
+}
 
 int fat_statfs(struct super_block *sb,struct statfs *buf, int bufsiz)
 {
diff -u --recursive --new-file v2.1.62/linux/fs/fat/mmap.c linux/fs/fat/mmap.c
--- v2.1.62/linux/fs/fat/mmap.c	Sat Oct 25 02:44:17 1997
+++ linux/fs/fat/mmap.c	Sat Nov  8 11:57:40 1997
@@ -21,11 +21,7 @@
 #include <linux/malloc.h>
 #include <linux/msdos_fs.h>
 
-#if LINUX_VERSION_CODE >= ASC_LINUX_VERSION(2,1,0)
 #include <asm/uaccess.h>
-#else
-#include <asm/segment.h>
-#endif
 #include <asm/system.h>
 
 /*
diff -u --recursive --new-file v2.1.62/linux/fs/filesystems.c linux/fs/filesystems.c
--- v2.1.62/linux/fs/filesystems.c	Sat Oct 25 02:44:17 1997
+++ linux/fs/filesystems.c	Fri Nov  7 09:41:54 1997
@@ -33,6 +33,7 @@
 #include <linux/lockd/bind.h>
 #include <linux/lockd/xdr.h>
 #include <linux/init.h>
+#include <linux/nls.h>
 
 extern void device_setup(void);
 extern void binfmt_setup(void);
diff -u --recursive --new-file v2.1.62/linux/fs/msdos/namei.c linux/fs/msdos/namei.c
--- v2.1.62/linux/fs/msdos/namei.c	Sat Oct 25 02:44:17 1997
+++ linux/fs/msdos/namei.c	Thu Nov  6 17:45:06 1997
@@ -21,9 +21,10 @@
 
 #include "../fat/msbuffer.h"
 
+#define MSDOS_PARANOIA 1	
+/* #define MSDOS_DEBUG 1 */	
 #define PRINTK(x)
 
-
 /* MS-DOS "device special files" */
 
 static const char *reserved_names[] = {
@@ -256,7 +257,7 @@
 
 	dentry->d_op = &msdos_dentry_operations;
 
-	if(!dir) {
+	if(!dir) { /* N.B. This test is bogus -- should never happen */
 		d_add(dentry, NULL);
 		return 0;
 	}
@@ -283,7 +284,7 @@
 	}
 	if (MSDOS_I(inode)->i_busy) { /* mkdir in progress */
 		iput(inode);
-		d_add(dentry, NULL);
+		d_add(dentry, NULL); /* N.B. Do we really want a negative? */
 		return 0;
 	}
 	PRINTK (("msdos_lookup 6\n"));
@@ -292,8 +293,7 @@
 		iput(inode);
 		if (!(inode = iget(next->i_sb,next->i_ino))) {
 			fat_fs_panic(dir->i_sb,"msdos_lookup: Can't happen");
-			d_add(dentry, NULL);
-			return -ENOENT;
+			return -ENOENT; /* N.B. Maybe ENOMEM is better? */
 		}
 	}
 	PRINTK (("msdos_lookup 7\n"));
@@ -382,7 +382,8 @@
 	res = msdos_create_entry(dir,msdos_name,S_ISDIR(mode),is_hid,
 				 &inode);
 	fat_unlock_creation();
-	d_instantiate(dentry, inode);
+	if (!res)
+		d_instantiate(dentry, inode);
 	return res;
 }
 
@@ -409,61 +410,77 @@
 /***** See if directory is empty */
 static int msdos_empty(struct inode *dir)
 {
-	struct super_block *sb = dir->i_sb;
 	loff_t pos;
 	struct buffer_head *bh;
 	struct msdos_dir_entry *de;
+	int result = 0;
 
-	if (dir->i_count > 1)
-		return -EBUSY;
 	if (MSDOS_I(dir)->i_start) { /* may be zero in mkdir */
 		pos = 0;
 		bh = NULL;
-		while (fat_get_entry(dir,&pos,&bh,&de) > -1)
-			if (!IS_FREE(de->name) && strncmp(de->name,MSDOS_DOT,
-			    MSDOS_NAME) && strncmp(de->name,MSDOS_DOTDOT,
-			    MSDOS_NAME)) {
-				fat_brelse(sb, bh);
-				return -ENOTEMPTY;
+		while (fat_get_entry(dir,&pos,&bh,&de) > -1) {
+			if (!IS_FREE(de->name) && 
+			    strncmp(de->name,MSDOS_DOT   , MSDOS_NAME) &&
+			    strncmp(de->name,MSDOS_DOTDOT, MSDOS_NAME)) {
+				result = -ENOTEMPTY;
+				break;
 			}
+		}
 		if (bh)
-			fat_brelse(sb, bh);
+			fat_brelse(dir->i_sb, bh);
 	}
-	return 0;
+	return result;
 }
 
 /***** Remove a directory */
-int msdos_rmdir(struct inode *dir,struct dentry *dentry)
+int msdos_rmdir(struct inode *dir, struct dentry *dentry)
 {
+	struct inode *inode = dentry->d_inode;
 	struct super_block *sb = dir->i_sb;
 	int res,ino;
 	struct buffer_head *bh;
 	struct msdos_dir_entry *de;
-	struct inode *inode;
 
 	bh = NULL;
-	inode = NULL;
-	res = -EPERM;
-	if ((res = msdos_find(dir,dentry->d_name.name,dentry->d_name.len,
-			      &bh,&de,&ino)) < 0)
+	res = msdos_find(dir, dentry->d_name.name, dentry->d_name.len,
+				&bh, &de, &ino);
+	if (res < 0)
 		goto rmdir_done;
-	inode = dentry->d_inode;
 	res = -ENOTDIR;
-	if (!S_ISDIR(inode->i_mode)) goto rmdir_done;
-	res = -EBUSY;
+	if (!S_ISDIR(inode->i_mode))
+		goto rmdir_done;
 	if (dir->i_dev != inode->i_dev || dir == inode)
-	  goto rmdir_done;
+		printk("msdos_rmdir: impossible condition\n");
+	/*
+	 * Prune any child dentries, then verify that
+	 * the directory is empty and not in use.
+	 */
+	shrink_dcache_parent(dentry);
 	res = msdos_empty(inode);
 	if (res)
 		goto rmdir_done;
+	res = -EBUSY;
+	if (dentry->d_count > 1) {
+#ifdef MSDOS_DEBUG
+printk("rename_diff_dir: %s/%s busy, d_count=%d\n",
+dentry->d_parent->d_name.name, dentry->d_name.name, dentry->d_count);
+#endif
+		goto rmdir_done;
+	}
+
 	inode->i_nlink = 0;
 	inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
 	dir->i_nlink--;
 	mark_inode_dirty(inode);
 	mark_inode_dirty(dir);
+	/*
+	 * Do the d_delete before any blocking operations.
+	 * We must make a negative dentry, as the FAT code
+	 * apparently relies on the inode being iput().
+	 */
+	d_delete(dentry);
 	de->name[0] = DELETED_FLAG;
 	fat_mark_buffer_dirty(sb, bh, 1);
-	d_delete(dentry);
 	res = 0;
 rmdir_done:
 	fat_brelse(sb, bh);
@@ -489,18 +506,18 @@
 	fat_lock_creation();
 	if (fat_scan(dir,msdos_name,&bh,&de,&ino,SCAN_ANY) >= 0) {
 		fat_unlock_creation();
+		/* N.B. does this need to be released on the other path? */
 		fat_brelse(sb, bh);
 		return -EEXIST;
  	}
-	if ((res = msdos_create_entry(dir,msdos_name,1,is_hid,
-				      &inode)) < 0) {
-		fat_unlock_creation();
-		return res;
-	}
+	res = msdos_create_entry(dir,msdos_name,1,is_hid, &inode);
+	if (res < 0)
+		goto out_unlock;
 	dir->i_nlink++;
 	inode->i_nlink = 2; /* no need to mark them dirty */
 	MSDOS_I(inode)->i_busy = 1; /* prevent lookups */
-	if ((res = fat_add_cluster(inode)) < 0) goto mkdir_error;
+	if ((res = fat_add_cluster(inode)) < 0)
+		goto mkdir_error;
 	if ((res = msdos_create_entry(inode,MSDOS_DOT,1,0,&dot)) < 0)
 		goto mkdir_error;
 	dot->i_size = inode->i_size; /* doesn't grow in the 2nd create_entry */
@@ -524,6 +541,7 @@
 mkdir_error:
 	if (msdos_rmdir(dir,dentry) < 0)
 		fat_fs_panic(dir->i_sb,"rmdir in mkdir failed");
+out_unlock:
 	fat_unlock_creation();
 	return res;
 }
@@ -535,25 +553,20 @@
 	int nospc)	/* Flag special file ? */
 {
 	struct super_block *sb = dir->i_sb;
+	struct inode *inode = dentry->d_inode;
 	int res,ino;
 	struct buffer_head *bh;
 	struct msdos_dir_entry *de;
-	struct inode *inode;
 
 	bh = NULL;
-	inode = NULL;
 	if ((res = msdos_find(dir,dentry->d_name.name,dentry->d_name.len,
 			      &bh,&de,&ino)) < 0)
 		goto unlink_done;
-	inode = dentry->d_inode;
-	if (!S_ISREG(inode->i_mode) && nospc){
-		res = -EPERM;
+	res = -EPERM;
+	if (!S_ISREG(inode->i_mode) && nospc)
 		goto unlink_done;
-	}
-	if (IS_IMMUTABLE(inode)){
-		res = -EPERM;
+	if (IS_IMMUTABLE(inode))
 		goto unlink_done;
-	}
 	inode->i_nlink = 0;
 	inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
 	MSDOS_I(inode)->i_busy = 1;
@@ -562,6 +575,7 @@
 	de->name[0] = DELETED_FLAG;
 	fat_mark_buffer_dirty(sb, bh, 1);
 	d_delete(dentry);	/* This also frees the inode */
+	res = 0;
 unlink_done:
 	fat_brelse(sb, bh);
 	return res;
@@ -707,7 +721,10 @@
 		: (free_de->attr&~ATTR_HIDDEN);
 	if (!(free_inode = iget(new_dir->i_sb,free_ino))) {
 		free_de->name[0] = DELETED_FLAG;
-/*  Don't mark free_bh as dirty. Both states are supposed to be equivalent. */
+		/*
+		 * Don't mark free_bh as dirty. Both states 
+		 * are supposed to be equivalent.
+		 */
 		fat_brelse(sb, free_bh);
 		if (exists)
 			fat_brelse(sb, new_bh);
@@ -718,19 +735,53 @@
 		mark_inode_dirty(new_dir);
 	}
 	msdos_read_inode(free_inode);
+	/*
+	 * Check whether there's already a linked inode ...
+	 */
+	if (MSDOS_I(old_inode)->i_linked) {
+		struct inode *linked = MSDOS_I(old_inode)->i_linked;
+#ifdef MSDOS_PARANOIA
+printk("rename_diff_dir: inode %ld already has link %ld, freeing it\n",
+old_inode->i_ino, linked->i_ino);
+#endif
+		MSDOS_I(old_inode)->i_linked = NULL;
+		MSDOS_I(linked)->i_oldlink = NULL;
+		iput(linked);
+	}
 	MSDOS_I(old_inode)->i_busy = 1;
 	MSDOS_I(old_inode)->i_linked = free_inode;
 	MSDOS_I(free_inode)->i_oldlink = old_inode;
+#ifdef MSDOS_DEBUG
+printk("rename_diff_dir: inode %ld added as link of %ld\n",
+free_inode->i_ino, old_inode->i_ino);
+#endif
 	fat_cache_inval_inode(old_inode);
 	mark_inode_dirty(old_inode);
 	old_de->name[0] = DELETED_FLAG;
 	fat_mark_buffer_dirty(sb, old_bh, 1);
 	fat_mark_buffer_dirty(sb, free_bh, 1);
 	if (exists) {
+		/*
+		 * Check whether there's already a depend inode ...
+		 */
+		if (MSDOS_I(new_inode)->i_depend) {
+			struct inode *depend = MSDOS_I(new_inode)->i_depend;
+#ifdef MSDOS_PARANOIA
+printk("rename_diff_dir: inode %ld already has depend %ld, freeing it\n",
+new_inode->i_ino, depend->i_ino);
+#endif
+			MSDOS_I(new_inode)->i_depend = NULL;
+			MSDOS_I(depend)->i_old = NULL;
+			iput(depend);
+		}
 		MSDOS_I(new_inode)->i_depend = free_inode;
 		MSDOS_I(free_inode)->i_old = new_inode;
 		/* Two references now exist to free_inode so increase count */
 		free_inode->i_count++;
+#ifdef MSDOS_DEBUG
+printk("rename_diff_dir: inode %ld added as depend of %ld\n",
+free_inode->i_ino, new_inode->i_ino);
+#endif
 		/* free_inode is put after putting new_inode and old_inode */
 		fat_brelse(sb, new_bh);
 	}
diff -u --recursive --new-file v2.1.62/linux/fs/ncpfs/dir.c linux/fs/ncpfs/dir.c
--- v2.1.62/linux/fs/ncpfs/dir.c	Mon Oct 20 10:36:52 1997
+++ linux/fs/ncpfs/dir.c	Wed Nov 12 11:10:40 1997
@@ -3,6 +3,7 @@
  *
  *  Copyright (C) 1995, 1996 by Volker Lendecke
  *  Modified for big endian by J.F. Chadima and David S. Miller
+ *  Modified 1997 Peter Waltenberg, Bill Hawes, David Woodhouse for 2.1 dcache
  *
  */
 
@@ -13,13 +14,18 @@
 #include <linux/malloc.h>
 #include <linux/vmalloc.h>
 #include <linux/mm.h>
-#include <linux/ncp_fs.h>
 #include <asm/uaccess.h>
 #include <asm/byteorder.h>
 #include <linux/errno.h>
 #include <linux/locks.h>
+
+#include <linux/ncp_fs.h>
 #include "ncplib_kernel.h"
 
+#ifndef shrink_dcache_parent
+#define shrink_dcache_parent(dentry) shrink_dcache_sb((dentry)->d_sb)
+#endif
+
 
 struct ncp_dirent {
 	struct nw_info_struct i;
@@ -27,79 +33,30 @@
 	unsigned long f_pos;
 };
 
-static long
- ncp_dir_read(struct inode *inode, struct file *filp, char *buf, unsigned long count);
-
-static int
- ncp_readdir(struct file *filp,
-	     void *dirent, filldir_t filldir);
-
-static int
- ncp_read_volume_list(struct ncp_server *server, int start_with,
-		      int cache_size);
-
-static int
- ncp_do_readdir(struct ncp_server *server, struct inode *dir, int fpos,
-		int cache_size, struct ncp_dirent *entry);
-
-static struct inode *
- ncp_iget(struct inode *dir, struct nw_file_info *finfo);
-
-static struct ncp_inode_info *
- ncp_find_dir_inode(struct inode *dir, const char *name);
-
-static int
- ncp_lookup(struct inode *dir, const char *__name,
-	    int len, struct inode **result);
-
-static int
- ncp_create(struct inode *dir, const char *name, int len, int mode,
-	    struct inode **result);
-
-static int
- ncp_mkdir(struct inode *dir, const char *name, int len, int mode);
-
-static int
- ncp_rmdir(struct inode *dir, const char *name, int len);
-
-static int
- ncp_unlink(struct inode *dir, const char *name, int len);
-
-static int
- ncp_rename(struct inode *old_dir, const char *old_name, int old_len,
-	    struct inode *new_dir, const char *new_name, int new_len);
-
-static inline void str_upper(char *name)
-{
-	while (*name) {
-		if (*name >= 'a' && *name <= 'z') {
-			*name -= ('a' - 'A');
-		}
-		name++;
-	}
-}
-
-static inline void str_lower(char *name)
-{
-	while (*name) {
-		if (*name >= 'A' && *name <= 'Z') {
-			*name += ('a' - 'A');
-		}
-		name++;
-	}
-}
-
-static inline int ncp_namespace(struct inode *i)
-{
-	struct ncp_server *server = NCP_SERVER(i);
-	struct nw_info_struct *info = NCP_ISTRUCT(i);
-	return server->name_space[info->volNumber];
-}
+static kdev_t c_dev = 0;
+static unsigned long c_ino = 0;
+static int c_size;
+static int c_seen_eof;
+static int c_last_returned_index;
+static struct ncp_dirent *c_entry = NULL;
+static int c_lock = 0;
+static struct wait_queue *c_wait = NULL;
 
-static inline int ncp_preserve_case(struct inode *i)
-{
-	return (ncp_namespace(i) == NW_NS_OS2);
-}
+static int ncp_read_volume_list(struct ncp_server *, int, int,
+					struct ncp_dirent *);
+static int ncp_do_readdir(struct ncp_server *, struct dentry *, int, int,
+					struct ncp_dirent *);
+
+static ssize_t ncp_dir_read(struct file *, char *, size_t, loff_t *);
+static int ncp_readdir(struct file *, void *, filldir_t);
+
+static int ncp_create(struct inode *, struct dentry *, int);
+static int ncp_lookup(struct inode *, struct dentry *);
+static int ncp_unlink(struct inode *, struct dentry *);
+static int ncp_mkdir(struct inode *, struct dentry *, int);
+static int ncp_rmdir(struct inode *, struct dentry *);
+static int ncp_rename(struct inode *, struct dentry *,
+	  	      struct inode *, struct dentry *);
 
 static struct file_operations ncp_dir_operations =
 {
@@ -128,74 +85,169 @@
 	NULL,			/* mknod */
 	ncp_rename,		/* rename */
 	NULL,			/* readlink */
+	NULL,			/* follow link */
+	NULL,			/* readpage */
+	NULL,			/* writepage */
 	NULL,			/* bmap */
 	NULL,			/* truncate */
 	NULL,			/* permission */
-	NULL			/* smap */
+	NULL,			/* smap */
+	NULL,			/* updatepage */
+	NULL,			/* revalidate */
 };
 
+static ssize_t 
+ncp_dir_read(struct file *filp, char *buf, size_t count, loff_t *ppos)
+{
+	return -EISDIR;
+}
 
-/* Here we encapsulate the inode number handling that depends upon the
- * mount mode: When we mount a complete server, the memory address of
- * the ncp_inode_info is used as the inode number. When only a single
- * volume is mounted, then the dirEntNum is used as the inode
- * number. As this is unique for the complete volume, this should
- * enable the NFS exportability of a ncpfs-mounted volume.
+/*
+ * Dentry operations routines
  */
+static int  ncp_lookup_validate(struct dentry *);
+static void ncp_delete_dentry(struct dentry *);
+static int ncp_hash_dentry(struct dentry *, struct qstr *);
+static int ncp_compare_dentry (struct dentry *, struct qstr *, struct qstr *);
+
+static struct dentry_operations ncp_dentry_operations =
+{
+	ncp_lookup_validate,	/* d_validate(struct dentry *) */
+	ncp_hash_dentry,	/* d_hash */
+	ncp_compare_dentry,    	/* d_compare */
+	ncp_delete_dentry	/* d_delete(struct dentry *) */
+};
 
-static inline int ncp_single_volume(struct ncp_server *server)
+static int 
+ncp_hash_dentry(struct dentry *dentry, struct qstr *this)
 {
-	return (server->m.mounted_vol[0] != '\0');
+  char al[NCP_MAXPATHLEN];
+  unsigned long hash;
+  int i;
+
+  memcpy(al,this->name,this->len);
+  al[this->len] = 0;
+
+  if (!ncp_preserve_case(dentry->d_inode))
+      str_lower(al);
+
+  hash = init_name_hash();
+  for (i=0; i<this->len ; i++)
+    hash = partial_name_hash(al[i],hash);
+  this->hash = end_name_hash(hash);
+  
+  return 0;
 }
 
-inline ino_t
- ncp_info_ino(struct ncp_server * server, struct ncp_inode_info * info)
+static int
+ncp_compare_dentry(struct dentry *dentry, struct qstr *a, struct qstr *b)
 {
-	return ncp_single_volume(server)
-	    ? info->finfo.i.dirEntNum : (ino_t) info;
+	char al[NCP_MAXPATHLEN],bl[NCP_MAXPATHLEN];
+
+	if (a->len != b->len) return 1;
+
+	if (ncp_preserve_case(dentry->d_inode))
+	    return strncmp(a->name, b->name, a->len);
+
+	memcpy (al,a->name,a->len);
+	memcpy (bl,b->name,b->len);
+	al[a->len] = bl[b->len] = 0;
+
+	str_lower(al);
+	str_lower(bl);
+	
+	return strcmp(al,bl);
 }
 
-static inline int ncp_is_server_root(struct inode *inode)
+/*
+ * This is the callback from dput() when d_count is going to 0.
+ * We use this to unhash dentries with bad inodes and close files.
+ */
+static void
+ncp_delete_dentry(struct dentry * dentry)
 {
-	struct ncp_server *s = NCP_SERVER(inode);
+	struct inode *inode = dentry->d_inode;
 
-	return ((!ncp_single_volume(s))
-		&& (inode->i_ino == ncp_info_ino(s, &(s->root))));
+	if (inode)
+	{
+		if (is_bad_inode(inode))
+		{
+			d_drop(dentry);
+		}
+		/*
+		 * Lock the superblock, then recheck the dentry count.
+		 * (Somebody might have used it again ...)
+		 */
+		if (dentry->d_count == 1 && NCP_FINFO(inode)->opened) {
+#ifdef NCPFS_PARANOIA
+printk(KERN_DEBUG "ncp_delete_dentry: closing file %s/%s\n",
+dentry->d_parent->d_name.name, dentry->d_name.name);
+#endif
+			ncp_make_closed(inode);
+		}
+	} else
+	{
+	/* N.B. Unhash negative dentries? */
+	}
 }
 
-struct ncp_inode_info *
- ncp_find_inode(struct inode *inode)
-{
-	struct ncp_server *server = NCP_SERVER(inode);
-	struct ncp_inode_info *root = &(server->root);
-	struct ncp_inode_info *this = root;
+/* Here we encapsulate the inode number handling that depends upon the
+ * mount mode: When we mount a complete server, the memory address of
+ * the ncp_inode_info is used as the inode number. When only a single
+ * volume is mounted, then the dirEntNum is used as the inode
+ * number. As this is unique for the complete volume, this should
+ * enable the NFS exportability of a ncpfs-mounted volume.
+ */
 
-	ino_t ino = inode->i_ino;
+/*
+ * Generate a unique inode number.
+ */
+ino_t ncp_invent_inos(unsigned long n)
+{
+	static ino_t ino = 1;
 
-	do {
-		if (ino == ncp_info_ino(server, this)) {
-			return this;
-		}
-		this = this->next;
+	if (ino + 2*n < ino)
+	{
+		/* wrap around */
+		ino += n;
 	}
-	while (this != root);
+	ino += n;
+	return ino;
+}
 
-	return NULL;
+/*
+ * Check whether a dentry already exists for the given name,
+ * and return the inode number if it has an inode.  This is
+ * needed to keep getcwd() working.
+ */
+static ino_t
+find_inode_number(struct dentry *dir, struct qstr *name)
+{
+	struct dentry * dentry;
+	ino_t ino = 0;
+
+	name->hash = full_name_hash(name->name, name->len);
+	dentry = d_lookup(dir, name);
+	if (dentry)
+	{
+		if (dentry->d_inode)
+			ino = dentry->d_inode->i_ino;
+		dput(dentry);
+	}
+	return ino;
 }
 
-static long ncp_dir_read(struct inode *inode, struct file *filp, char *buf, unsigned long count)
+static inline int
+ncp_single_volume(struct ncp_server *server)
 {
-	return -EISDIR;
+	return (server->m.mounted_vol[0] != '\0');
 }
 
-static kdev_t c_dev = 0;
-static unsigned long c_ino = 0;
-static int c_size;
-static int c_seen_eof;
-static int c_last_returned_index;
-static struct ncp_dirent *c_entry = NULL;
-static int c_lock = 0;
-static struct wait_queue *c_wait = NULL;
+static inline int ncp_is_server_root(struct inode *inode)
+{
+	return (!ncp_single_volume(NCP_SERVER(inode)) &&
+		inode == inode->i_sb->s_root->d_inode);
+}
 
 static inline void ncp_lock_dircache(void)
 {
@@ -210,54 +262,143 @@
 	wake_up(&c_wait);
 }
 
-static int ncp_readdir(struct file *filp,
-		       void *dirent, filldir_t filldir)
+
+/*
+ * This is the callback when the dcache has a lookup hit.
+ */
+
+static int
+ncp_lookup_validate(struct dentry * dentry)
 {
-	int result = 0;
-	int i = 0;
-	int index = 0;
-	struct inode *inode = filp->f_dentry->d_inode;
-	struct ncp_dirent *entry = NULL;
+	struct ncp_server *server;
+	struct inode *dir = dentry->d_parent->d_inode;
+	int down_case = 0;
+	int val = 0,res;
+	int len = dentry->d_name.len;      
+	struct ncpfs_inode_info finfo;
+	__u8 __name[dentry->d_name.len + 1];
+
+	printk("ncp_lookup_validate called\n");
+      
+	if (!dir || !S_ISDIR(dir->i_mode)) {
+		printk(KERN_WARNING "ncp_lookup_validate: inode is NULL or not a directory.\n");
+		goto finished;
+	}
+	server = NCP_SERVER(dir);
+
+	if (!ncp_conn_valid(server))
+		goto finished;
+
+	strncpy(__name, dentry->d_name.name, len);
+	__name[len] = '\0';
+#ifdef NCPFS_PARANOIA
+printk(KERN_DEBUG "ncp_lookup_validate: %s, len %d\n", __name, len);
+#endif
+
+	if (!ncp_preserve_case(dir)) { 
+	  str_lower(__name);
+          down_case = 1;
+	}
+	
+	/* If the file is in the dir cache, we do not have to ask the
+	   server. */
+
+#ifdef NCPFS_PARANOIA
+printk(KERN_DEBUG "ncp_lookup_validate: server lookup for %s/%s\n",
+dentry->d_parent->d_name.name, __name);
+#endif
+		if (ncp_is_server_root(dir))
+		{
+			str_upper(__name);
+			down_case = 1;
+			res = ncp_lookup_volume(server, __name,
+						&(finfo.nw_info.i));
+		} else
+	    	{
+			if (!ncp_preserve_case(dir))
+			{
+				str_upper(__name);
+				down_case = 1;
+			}
+			res = ncp_obtain_info(server, dir, __name,
+						&(finfo.nw_info.i));
+		}
+#ifdef NCPFS_PARANOIA
+printk(KERN_DEBUG "ncp_lookup_validate: looked for %s/%s, res=%d\n",
+dentry->d_parent->d_name.name, __name, res);
+#endif
+		/*
+		 * If we didn't find it, or if it has a different dirEntNum to
+		 * what we remember, it's not valid any more.
+		 */
+	   	if (!res)
+		  if (finfo.nw_info.i.dirEntNum == NCP_FINFO(dentry->d_inode)->dirEntNum)
+		    val=1;
+#ifdef NCPFS_PARANOIA
+		  else
+		    printk(KERN_DEBUG "ncp_lookup_validate: found, but dirEntNum changed\n");
+#endif
+		if (!val) ncp_invalid_dir_cache(dir);
+
+finished:
+#ifdef NCPFS_PARANOIA
+printk(KERN_DEBUG "ncp_lookup_validate: result=%d\n", val);
+#endif
+
+	return val;
+}
+
+
+static int ncp_readdir(struct file *filp, void *dirent, filldir_t filldir)
+{
+	struct dentry *dentry = filp->f_dentry;
+	struct inode *inode = dentry->d_inode;
 	struct ncp_server *server = NCP_SERVER(inode);
-	struct ncp_inode_info *dir = NCP_INOP(inode);
+	struct ncp_dirent *entry = NULL;
+	int result, i, index = 0;
 
-	DDPRINTK("ncp_readdir: filp->f_pos = %d\n", (int) filp->f_pos);
-	DDPRINTK("ncp_readdir: inode->i_ino = %ld, c_ino = %ld\n",
+	DDPRINTK(KERN_DEBUG "ncp_readdir: reading %s/%s, pos=%d\n",
+		dentry->d_parent->d_name.name, dentry->d_name.name,
+		(int) filp->f_pos);
+	DDPRINTK(KERN_DEBUG "ncp_readdir: inode->i_ino = %ld, c_ino = %ld\n",
 		 inode->i_ino, c_ino);
 
+	result = -EBADF;
 	if (!inode || !S_ISDIR(inode->i_mode)) {
-		printk("ncp_readdir: inode is NULL or not a directory\n");
-		return -EBADF;
-	}
-	if (!ncp_conn_valid(server)) {
-		return -EIO;
+		printk(KERN_WARNING "ncp_readdir: inode is NULL or not a directory\n");
+		goto out;
 	}
-	ncp_lock_dircache();
+	result = -EIO;
+	if (!ncp_conn_valid(server))
+		goto out;
 
+	ncp_lock_dircache();
+	result = -ENOMEM;
 	if (c_entry == NULL) {
 		i = sizeof(struct ncp_dirent) * NCP_READDIR_CACHE_SIZE;
 		c_entry = (struct ncp_dirent *) vmalloc(i);
 		if (c_entry == NULL) {
-			printk("ncp_readdir: no MEMORY for cache\n");
-			result = -ENOMEM;
+			printk(KERN_WARNING "ncp_readdir: no MEMORY for cache\n");
 			goto finished;
 		}
 	}
+
+	result = 0;
 	if (filp->f_pos == 0) {
 		ncp_invalid_dir_cache(inode);
-		if (filldir(dirent, ".", 1, filp->f_pos,
-			    ncp_info_ino(server, dir)) < 0) {
+		if (filldir(dirent, ".", 1, 0, inode->i_ino) < 0) {
 			goto finished;
 		}
-		filp->f_pos += 1;
+		filp->f_pos = 1;
 	}
 	if (filp->f_pos == 1) {
-		if (filldir(dirent, "..", 2, filp->f_pos,
-			    ncp_info_ino(server, dir->dir)) < 0) {
+		if (filldir(dirent, "..", 2, 1,
+				dentry->d_parent->d_inode->i_ino) < 0) {
 			goto finished;
 		}
-		filp->f_pos += 1;
+		filp->f_pos = 2;
 	}
+
 	if ((inode->i_dev == c_dev) && (inode->i_ino == c_ino)) {
 		for (i = 0; i < c_size; i++) {
 			if (filp->f_pos == c_entry[i].f_pos) {
@@ -273,18 +414,17 @@
 	}
 	if (entry == NULL) {
 		int entries;
-		DDPRINTK("ncp_readdir: Not found in cache.\n");
+		DDPRINTK(KERN_DEBUG "ncp_readdir: Not found in cache.\n");
 
 		if (ncp_is_server_root(inode)) {
 			entries = ncp_read_volume_list(server, filp->f_pos,
-						 NCP_READDIR_CACHE_SIZE);
-			DPRINTK("ncp_read_volume_list returned %d\n", entries);
+					 NCP_READDIR_CACHE_SIZE, c_entry);
+			DPRINTK(KERN_DEBUG "ncp_read_volume_list returned %d\n", entries);
 
 		} else {
-			entries = ncp_do_readdir(server, inode, filp->f_pos,
-						 NCP_READDIR_CACHE_SIZE,
-						 c_entry);
-			DPRINTK("ncp_readdir returned %d\n", entries);
+			entries = ncp_do_readdir(server, dentry, filp->f_pos,
+					 NCP_READDIR_CACHE_SIZE, c_entry);
+			DPRINTK(KERN_DEBUG "ncp_readdir: returned %d\n", entries);
 		}
 
 		if (entries < 0) {
@@ -313,31 +453,24 @@
 		/* Nothing found, even from a ncp call */
 		goto finished;
 	}
+
 	while (index < c_size) {
 		ino_t ino;
+		struct qstr qname;
 
-		if (ncp_single_volume(server)) {
-			ino = (ino_t) (entry->i.dirEntNum);
-		} else {
-			/* For getwd() we have to return the correct
-			 * inode in d_ino if the inode is currently in
-			 * use. Otherwise the inode number does not
-			 * matter. (You can argue a lot about this..) */
-			struct ncp_inode_info *ino_info;
-			ino_info = ncp_find_dir_inode(inode,
-						      entry->i.entryName);
-
-			/* Some programs seem to be confused about a
-			 * zero inode number, so we set it to one.
-			 * Thanks to Gordon Chaffee for this one. */
-			if (ino_info == NULL) {
-				ino_info = (struct ncp_inode_info *) 1;
-			}
-			ino = (ino_t) (ino_info);
-		}
+		DDPRINTK(KERN_DEBUG "ncp_readdir: entry->path= %s\n", entry->i.entryName);
+		DDPRINTK(KERN_DEBUG "ncp_readdir: entry->f_pos = %ld\n", entry->f_pos);
 
-		DDPRINTK("ncp_readdir: entry->path= %s\n", entry->i.entryName);
-		DDPRINTK("ncp_readdir: entry->f_pos = %ld\n", entry->f_pos);
+		/* For getwd() we have to return the correct
+		 * inode in d_ino if the inode is currently in
+		 * use. Otherwise the inode number does not
+		 * matter. (You can argue a lot about this..)
+		 */
+		qname.name = entry->i.entryName;
+		qname.len  = entry->i.nameLen;
+		ino = find_inode_number(dentry, &qname);
+		if (!ino)
+			ino = ncp_invent_inos(1);
 
 		if (filldir(dirent, entry->i.entryName, entry->i.nameLen,
 			    entry->f_pos, ino) < 0) {
@@ -356,103 +489,114 @@
 	}
       finished:
 	ncp_unlock_dircache();
+out:
 	return result;
 }
 
-static int ncp_read_volume_list(struct ncp_server *server, int fpos, int cache_size)
+static int 
+ncp_read_volume_list(struct ncp_server *server, int fpos,
+			 int cache_size, struct ncp_dirent *entry)
 {
-	struct ncp_dirent *entry = c_entry;
-
-	int total_count = 2;
-	int i;
+	int i, total_count = 2;
+	struct ncp_volume_info info;
 
+	DPRINTK(KERN_DEBUG "ncp_read_volume_list: pos=%d\n", fpos);
 #if 1
 	if (fpos < 2) {
-		printk("OOPS, we expect fpos >= 2");
+		printk(KERN_ERR "OOPS, we expect fpos >= 2");
 		fpos = 2;
 	}
 #endif
 
 	for (i = 0; i < NCP_NUMBER_OF_VOLUMES; i++) {
-		struct ncp_volume_info info;
 
-		if (ncp_get_volume_info_with_number(server, i, &info) != 0) {
-			return (total_count - fpos);
-		}
-		if (strlen(info.volume_name) > 0) {
-			if (total_count < fpos) {
-				DPRINTK("ncp_read_volumes: skipped vol: %s\n",
-					info.volume_name);
-			} else if (total_count >= fpos + cache_size) {
-				return (total_count - fpos);
-			} else {
-				DPRINTK("ncp_read_volumes: found vol: %s\n",
-					info.volume_name);
+		if (ncp_get_volume_info_with_number(server, i, &info) != 0)
+			goto out;
+		if (!strlen(info.volume_name))
+			continue;
 
-				if (ncp_lookup_volume(server,
-						      info.volume_name,
-						      &(entry->i)) != 0) {
-					DPRINTK("ncpfs: could not lookup vol "
-						"%s\n", info.volume_name);
-					continue;
-				}
-				entry->f_pos = total_count;
-				entry += 1;
+		if (total_count < fpos) {
+			DPRINTK(KERN_DEBUG "ncp_read_volume_list: skipped vol: %s\n",
+				info.volume_name);
+		} else if (total_count >= fpos + cache_size) {
+			goto out;
+		} else {
+			DPRINTK(KERN_DEBUG "ncp_read_volume_list: found vol: %s\n",
+				info.volume_name);
+
+			if (ncp_lookup_volume(server, info.volume_name,
+					      &(entry->i)) != 0) {
+				DPRINTK(KERN_DEBUG "ncpfs: could not lookup vol %s\n",
+					info.volume_name);
+				continue;
 			}
-			total_count += 1;
+			entry->f_pos = total_count;
+			entry += 1;
 		}
+		total_count += 1;
 	}
+out:
 	return (total_count - fpos);
 }
 
-static int ncp_do_readdir(struct ncp_server *server, struct inode *dir, int fpos,
-			  int cache_size, struct ncp_dirent *entry)
+static int ncp_do_readdir(struct ncp_server *server, struct dentry *dentry,
+			int fpos, int cache_size, struct ncp_dirent *entry)
 {
-	static struct nw_search_sequence seq;
+	struct inode *dir = dentry->d_inode;
 	static struct inode *last_dir;
 	static int total_count;
+	static struct nw_search_sequence seq;
+	int err;
 
 #if 1
 	if (fpos < 2) {
-		printk("OOPS, we expect fpos >= 2");
+		printk(KERN_ERR "OOPS, we expect fpos >= 2");
 		fpos = 2;
 	}
 #endif
-	DPRINTK("ncp_do_readdir: fpos = %d\n", fpos);
+	DPRINTK(KERN_DEBUG "ncp_do_readdir: %s/%s, fpos=%d\n",
+		dentry->d_parent->d_name.name, dentry->d_name.name, fpos);
 
 	if (fpos == 2) {
 		last_dir = NULL;
 		total_count = 2;
 	}
-	if ((fpos != total_count) || (dir != last_dir)) {
+	if ((fpos != total_count) || (dir != last_dir))
+	{
 		total_count = 2;
 		last_dir = dir;
-
-		DPRINTK("ncp_do_readdir: re-used seq for %s\n",
-			NCP_ISTRUCT(dir)->entryName);
-
-		if (ncp_initialize_search(server, NCP_ISTRUCT(dir), &seq) != 0) {
-			DPRINTK("ncp_init_search failed\n");
-			return total_count - fpos;
+		
+#ifdef NCPFS_PARANOIA
+printk(KERN_DEBUG "ncp_do_readdir: init %s, volnum=%d, dirent=%u\n",
+dentry->d_name.name, NCP_FINFO(dir)->volNumber, NCP_FINFO(dir)->dirEntNum);
+#endif
+		err = ncp_initialize_search(server, dir, &seq); 
+		if (err)
+		{
+			DPRINTK(KERN_DEBUG "ncp_do_readdir: init failed, err=%d\n", err);
+			goto out;
 		}
 	}
+
 	while (total_count < fpos + cache_size) {
-		if (ncp_search_for_file_or_subdir(server, &seq,
-						  &(entry->i)) != 0) {
-			return total_count - fpos;
+		err = ncp_search_for_file_or_subdir(server, &seq, &(entry->i));
+		if (err) {
+			DPRINTK(KERN_DEBUG "ncp_do_readdir: search failed, err=%d\n", err);
+			goto out;
 		}
 		if (total_count < fpos) {
-			DPRINTK("ncp_do_readdir: skipped file: %s\n",
-				entry->i.entryName);
+			DPRINTK(KERN_DEBUG "ncp_do_readdir: skipped file: %s/%s\n",
+				dentry->d_name.name, entry->i.entryName);
 		} else {
-			DDPRINTK("ncp_do_r: file: %s, f_pos=%d,total_count=%d",
-				 entry->i.entryName, fpos, total_count);
+			DDPRINTK(KERN_DEBUG "ncp_do_r: file: %s, f_pos=%d,total_count=%d",
+				entry->i.entryName, fpos, total_count);
 			entry->s = seq;
 			entry->f_pos = total_count;
 			entry += 1;
 		}
 		total_count += 1;
 	}
+out:
 	return (total_count - fpos);
 }
 
@@ -474,7 +618,7 @@
 
 void ncp_free_dir_cache(void)
 {
-	DPRINTK("ncp_free_dir_cache: enter\n");
+	DPRINTK(KERN_DEBUG "ncp_free_dir_cache: enter\n");
 
 	if (c_entry == NULL) {
 		return;
@@ -482,540 +626,398 @@
 	vfree(c_entry);
 	c_entry = NULL;
 
-	DPRINTK("ncp_free_dir_cache: exit\n");
-}
-
-
-static struct inode *
- ncp_iget(struct inode *dir, struct nw_file_info *finfo)
-{
-	struct inode *inode;
-	struct ncp_inode_info *new_inode_info;
-	struct ncp_inode_info *root;
-
-	if (dir == NULL) {
-		printk("ncp_iget: dir is NULL\n");
-		return NULL;
-	}
-	if (finfo == NULL) {
-		printk("ncp_iget: finfo is NULL\n");
-		return NULL;
-	}
-	new_inode_info = ncp_kmalloc(sizeof(struct ncp_inode_info),
-				     GFP_KERNEL);
-
-	if (new_inode_info == NULL) {
-		printk("ncp_iget: could not alloc mem for %s\n",
-		       finfo->i.entryName);
-		return NULL;
-	}
-	new_inode_info->state = NCP_INODE_LOOKED_UP;
-	new_inode_info->nused = 0;
-	new_inode_info->dir = NCP_INOP(dir);
-	new_inode_info->finfo = *finfo;
-
-	NCP_INOP(dir)->nused += 1;
-
-	/* We have to link the new inode_info into the doubly linked
-	   list of inode_infos to make a complete linear search
-	   possible. */
-
-	root = &(NCP_SERVER(dir)->root);
-
-	new_inode_info->prev = root;
-	new_inode_info->next = root->next;
-	root->next->prev = new_inode_info;
-	root->next = new_inode_info;
-
-	if (!(inode = iget(dir->i_sb, ncp_info_ino(NCP_SERVER(dir),
-						   new_inode_info)))) {
-		printk("ncp_iget: iget failed!");
-		return NULL;
-	}
-	return inode;
-}
-
-void ncp_free_inode_info(struct ncp_inode_info *i)
-{
-	if (i == NULL) {
-		printk("ncp_free_inode: i == NULL\n");
-		return;
-	}
-	i->state = NCP_INODE_CACHED;
-	while ((i->nused == 0) && (i->state == NCP_INODE_CACHED)) {
-		struct ncp_inode_info *dir = i->dir;
-
-		i->next->prev = i->prev;
-		i->prev->next = i->next;
-
-		DDPRINTK("ncp_free_inode_info: freeing %s\n",
-			 i->finfo.i.entryName);
-
-		ncp_kfree_s(i, sizeof(struct ncp_inode_info));
-
-		if (dir == i)
-			return;
-
-		(dir->nused)--;
-		i = dir;
-	}
-}
-
-void ncp_init_root(struct ncp_server *server)
-{
-	struct ncp_inode_info *root = &(server->root);
-	struct nw_info_struct *i = &(root->finfo.i);
-	unsigned short dummy;
-
-	DPRINTK("ncp_init_root: i = %x\n", (int) i);
-
-	root->finfo.opened = 0;
-	i->attributes = aDIR;
-	i->dataStreamSize = 1024;
-	i->dirEntNum = i->DosDirNum = 0;
-	i->volNumber = NCP_NUMBER_OF_VOLUMES + 1;	/* illegal volnum */
-	ncp_date_unix2dos(0, &(i->creationTime), &(i->creationDate));
-	ncp_date_unix2dos(0, &(i->modifyTime), &(i->modifyDate));
-	ncp_date_unix2dos(0, &dummy, &(i->lastAccessDate));
-	i->creationTime = le16_to_cpu(i->creationTime);
-	i->creationDate = le16_to_cpu(i->creationDate);
-	i->modifyTime = le16_to_cpu(i->modifyTime);
-	i->modifyDate = le16_to_cpu(i->modifyDate);
-	i->lastAccessDate = le16_to_cpu(i->lastAccessDate);
-	i->nameLen = 0;
-	i->entryName[0] = '\0';
-
-	root->state = NCP_INODE_LOOKED_UP;
-	root->nused = 1;
-	root->dir = root;
-	root->next = root->prev = root;
-	return;
+	DPRINTK(KERN_DEBUG "ncp_free_dir_cache: exit\n");
 }
 
 int ncp_conn_logged_in(struct ncp_server *server)
 {
-	if (server->m.mounted_vol[0] == '\0') {
-		return 0;
-	}
-	str_upper(server->m.mounted_vol);
-	if (ncp_lookup_volume(server, server->m.mounted_vol,
-			      &(server->root.finfo.i)) != 0) {
-		return -ENOENT;
-	}
-	str_lower(server->root.finfo.i.entryName);
+	int result;
 
-	return 0;
-}
-
-void ncp_free_all_inodes(struct ncp_server *server)
-{
-	/* Here nothing should be to do. I do not know whether it's
-	   better to leave some memory allocated or be stuck in an
-	   endless loop */
-#if 1
-	struct ncp_inode_info *root = &(server->root);
-
-	if (root->next != root) {
-		printk("ncp_free_all_inodes: INODES LEFT!!!\n");
-	}
-	while (root->next != root) {
-		printk("ncp_free_all_inodes: freeing inode\n");
-		ncp_free_inode_info(root->next);
-		/* In case we have an endless loop.. */
-		schedule();
-	}
+	if (ncp_single_volume(server)) {
+		result = -ENOENT;
+		str_upper(server->m.mounted_vol);
+		if (ncp_lookup_volume(server, server->m.mounted_vol,
+				      &(server->root.finfo.i)) != 0) {
+#ifdef NCPFS_PARANOIA
+printk(KERN_DEBUG "ncp_conn_logged_in: %s not found\n", server->m.mounted_vol);
 #endif
-
-	return;
-}
-
-/* We will search the inode that belongs to this name, currently by a
-   complete linear search through the inodes belonging to this
-   filesystem. This has to be fixed. */
-static struct ncp_inode_info *
- ncp_find_dir_inode(struct inode *dir, const char *name)
-{
-	struct ncp_server *server = NCP_SERVER(dir);
-	struct nw_info_struct *dir_info = NCP_ISTRUCT(dir);
-	struct ncp_inode_info *result = &(server->root);
-
-	if (name == NULL) {
-		return NULL;
-	}
-	do {
-		if ((result->dir->finfo.i.dirEntNum == dir_info->dirEntNum)
-		&& (result->dir->finfo.i.volNumber == dir_info->volNumber)
-		    && (strcmp(result->finfo.i.entryName, name) == 0)
-		/* The root dir is never looked up using this
-		 * routine.  Without the following test a root
-		 * directory 'sys' in a volume named 'sys' could
-		 * never be looked up, because
-		 * server->root->dir==server->root. */
-		    && (result != &(server->root))) {
-			return result;
+			goto out;
 		}
-		result = result->next;
-
+		str_lower(server->root.finfo.i.entryName);
 	}
-	while (result != &(server->root));
+	result = 0;
 
-	return NULL;
+out:
+	return result;
 }
 
-static int ncp_lookup(struct inode *dir, const char *__name, int len,
-		      struct inode **result)
+static int ncp_lookup(struct inode *dir, struct dentry *dentry)
 {
-	struct nw_file_info finfo;
 	struct ncp_server *server;
-	struct ncp_inode_info *result_info;
-	int found_in_cache;
-	int down_case = 0;
-	char name[len + 1];
-
-	*result = NULL;
-
+	struct inode *inode = NULL;
+	int found_in_cache, down_case = 0;
+	int error;
+	int len = dentry->d_name.len;      
+	struct ncpfs_inode_info finfo;
+	__u8 __name[dentry->d_name.len + 1];
+      
+	error =  -ENOENT;
 	if (!dir || !S_ISDIR(dir->i_mode)) {
-		printk("ncp_lookup: inode is NULL or not a directory.\n");
-		iput(dir);
-		return -ENOENT;
+		printk(KERN_WARNING "ncp_lookup: inode is NULL or not a directory.\n");
+		goto finished;
 	}
 	server = NCP_SERVER(dir);
 
-	if (!ncp_conn_valid(server)) {
-		iput(dir);
-		return -EIO;
-	}
-	DPRINTK("ncp_lookup: %s, len %d\n", __name, len);
-
-	/* Fast cheat for . */
-	if (len == 0 || (len == 1 && __name[0] == '.')) {
-		*result = dir;
-		return 0;
-	}
-	/* ..and for .. */
-	if (len == 2 && __name[0] == '.' && __name[1] == '.') {
-		struct ncp_inode_info *parent = NCP_INOP(dir)->dir;
-
-		if (parent->state == NCP_INODE_CACHED) {
-			parent->state = NCP_INODE_LOOKED_UP;
-		}
-		*result = iget(dir->i_sb, ncp_info_ino(server, parent));
-		iput(dir);
-		if (*result == 0) {
-			return -EACCES;
-		} else {
-			return 0;
-		}
-	}
-	memcpy(name, __name, len);
-	name[len] = 0;
-	lock_super(dir->i_sb);
-	result_info = ncp_find_dir_inode(dir, name);
-
-	if (result_info != 0) {
-		if (result_info->state == NCP_INODE_CACHED) {
-			result_info->state = NCP_INODE_LOOKED_UP;
-		}
-		/* Here we convert the inode_info address into an
-		   inode number */
+	error = -EIO;
+	if (!ncp_conn_valid(server))
+		goto finished;
 
-		*result = iget(dir->i_sb, ncp_info_ino(server, result_info));
-		unlock_super(dir->i_sb);
-		iput(dir);
+	strncpy(__name, dentry->d_name.name, len);
+	__name[len] = '\0';
+#ifdef NCPFS_PARANOIA
+printk(KERN_DEBUG "ncp_lookup: %s, len %d\n", __name, len);
+#endif
 
-		if (*result == NULL) {
-			return -EACCES;
-		}
-		return 0;
+	if (!ncp_preserve_case(dir)) { 
+	  str_lower(__name);
+          down_case = 1;
 	}
+	
 	/* If the file is in the dir cache, we do not have to ask the
 	   server. */
 
 	found_in_cache = 0;
 	ncp_lock_dircache();
 
-	if ((dir->i_dev == c_dev) && (dir->i_ino == c_ino)) {
+	if ((dir->i_dev == c_dev) && (dir->i_ino == c_ino))
+	{
 		int first = c_last_returned_index;
 		int i;
-
+	    
 		i = first;
 		do {
-			DDPRINTK("ncp_lookup: trying index: %d, name: %s\n",
-				 i, c_entry[i].i.entryName);
-
-			if (strcmp(c_entry[i].i.entryName, name) == 0) {
-				DPRINTK("ncp_lookup: found in cache!\n");
-				finfo.i = c_entry[i].i;
+#ifdef NCPFS_PARANOIA
+printk(KERN_DEBUG "ncp_lookup: trying index: %d, name: %s\n", i, c_entry[i].i.entryName);
+#endif
+			if (strcmp(c_entry[i].i.entryName, __name) == 0) {
+#ifdef NCPFS_PARANOIA
+printk(KERN_DEBUG "ncp_lookup: found in cache!\n");
+#endif
+				finfo.nw_info.i = c_entry[i].i;
 				found_in_cache = 1;
 				break;
 			}
 			i = (i + 1) % c_size;
-		}
-		while (i != first);
+		} while (i != first);
 	}
 	ncp_unlock_dircache();
 
-	if (found_in_cache == 0) {
+	if (found_in_cache == 0)
+	{
 		int res;
-
-		DDPRINTK("ncp_lookup: do_lookup on %s/%s\n",
-			 NCP_ISTRUCT(dir)->entryName, name);
-
-		if (ncp_is_server_root(dir)) {
-			str_upper(name);
+	    
+#ifdef NCPFS_PARANOIA
+printk(KERN_DEBUG "ncp_lookup: server lookup for %s/%s\n",
+dentry->d_parent->d_name.name, __name);
+#endif
+		if (ncp_is_server_root(dir))
+		{
+			str_upper(__name);
 			down_case = 1;
-			res = ncp_lookup_volume(server, name, &(finfo.i));
-		} else {
-			if (!ncp_preserve_case(dir)) {
-				str_upper(name);
+			res = ncp_lookup_volume(server, __name,
+						&(finfo.nw_info.i));
+		} else
+	    	{
+			if (!ncp_preserve_case(dir))
+			{
+				str_upper(__name);
 				down_case = 1;
 			}
-			res = ncp_obtain_info(server,
-					      NCP_ISTRUCT(dir)->volNumber,
-					      NCP_ISTRUCT(dir)->dirEntNum,
-					      name, &(finfo.i));
-		}
-		if (res != 0) {
-			unlock_super(dir->i_sb);
-			iput(dir);
-			return -ENOENT;
+			res = ncp_obtain_info(server, dir, __name,
+						&(finfo.nw_info.i));
 		}
+#ifdef NCPFS_PARANOIA
+printk(KERN_DEBUG "ncp_lookup: looked for %s/%s, res=%d\n",
+dentry->d_parent->d_name.name, __name, res);
+#endif
+		/*
+		 * If we didn't find an entry, make a negative dentry.
+		 */
+	   	if (res != 0)
+			goto add_entry;
+	}
+
+	/*
+	 * Create an inode for the entry.
+	 */
+	finfo.nw_info.opened = 0;
+	finfo.ino = ncp_invent_inos(1);
+	error = -EACCES;
+	inode = ncp_iget(dir->i_sb, &finfo);
+	if (inode)
+	{
+	add_entry:
+		dentry->d_op = &ncp_dentry_operations;
+		d_add(dentry, inode);
+		error = 0;
 	}
-	finfo.opened = 0;
 
-	if (down_case != 0) {
-		str_lower(finfo.i.entryName);
-	}
-	if (!(*result = ncp_iget(dir, &finfo))) {
-		unlock_super(dir->i_sb);
-		iput(dir);
-		return -EACCES;
-	}
-	unlock_super(dir->i_sb);
-	iput(dir);
-	return 0;
+finished:
+#ifdef NCPFS_PARANOIA
+printk(KERN_DEBUG "ncp_lookup: result=%d\n", error);
+#endif
+	return error;
 }
 
-static int ncp_create(struct inode *dir, const char *name, int len, int mode,
-		      struct inode **result)
+/*
+ * This code is common to create, mkdir, and mknod.
+ */
+static int ncp_instantiate(struct inode *dir, struct dentry *dentry,
+			struct ncpfs_inode_info *finfo)
 {
-	struct nw_file_info finfo;
-	__u8 _name[len + 1];
+	struct inode *inode;
+	int error = -EINVAL;
 
-	*result = NULL;
+	ncp_invalid_dir_cache(dir);
 
+	finfo->ino = ncp_invent_inos(1);
+	inode = ncp_iget(dir->i_sb, finfo);
+	if (!inode)
+		goto out_close;
+	d_instantiate(dentry,inode);
+	error = 0;
+out:
+	return error;
+
+out_close:
+#ifdef NCPFS_PARANOIA
+printk(KERN_DEBUG "ncp_instantiate: %s/%s failed, closing file\n",
+dentry->d_parent->d_name.name, dentry->d_name.name);
+#endif
+	ncp_close_file(NCP_SERVER(dir), finfo->nw_info.file_handle);
+	goto out;
+}
+
+static int ncp_create(struct inode *dir, struct dentry *dentry, int mode)
+{
+	int error, result;
+	struct ncpfs_inode_info finfo;
+	__u8 _name[dentry->d_name.len + 1];
+
+#ifdef NCPFS_PARANOIA
+printk(KERN_DEBUG "ncp_create: creating %s/%s, mode=%x\n",
+dentry->d_parent->d_name.name, dentry->d_name.name, mode);
+#endif
 	if (!dir || !S_ISDIR(dir->i_mode)) {
-		printk("ncp_create: inode is NULL or not a directory\n");
-		iput(dir);
+		printk(KERN_WARNING "ncp_create: inode is NULL or not a directory\n");
 		return -ENOENT;
 	}
-	if (!ncp_conn_valid(NCP_SERVER(dir))) {
-		iput(dir);
-		return -EIO;
-	}
-	strncpy(_name, name, len);
-	_name[len] = '\0';
+	error = -EIO;
+	if (!ncp_conn_valid(NCP_SERVER(dir)))
+		goto out;
+
+	strncpy(_name, dentry->d_name.name, dentry->d_name.len);
+	_name[dentry->d_name.len] = '\0';
 
 	if (!ncp_preserve_case(dir)) {
 		str_upper(_name);
 	}
-	lock_super(dir->i_sb);
-	if (ncp_open_create_file_or_subdir(NCP_SERVER(dir),
-					   NCP_ISTRUCT(dir), _name,
-					   OC_MODE_CREATE | OC_MODE_OPEN |
-					   OC_MODE_REPLACE,
-					   0, AR_READ | AR_WRITE,
-					   &finfo) != 0) {
-		unlock_super(dir->i_sb);
-		iput(dir);
-		return -EACCES;
-	}
-	ncp_invalid_dir_cache(dir);
 
-	if (!ncp_preserve_case(dir)) {
-		str_lower(finfo.i.entryName);
+	error = -EACCES;
+	result = ncp_open_create_file_or_subdir(NCP_SERVER(dir), dir, _name,
+			   OC_MODE_CREATE | OC_MODE_OPEN | OC_MODE_REPLACE,
+			   0, AR_READ | AR_WRITE, &finfo.nw_info);
+	if (!result) {
+		finfo.nw_info.access = O_RDWR;
+		error = ncp_instantiate(dir, dentry, &finfo);
+	} else {
+		DPRINTK(KERN_DEBUG "ncp_create: %s/%s failed\n",
+			dentry->d_parent->d_name.name, dentry->d_name.name);
 	}
-	finfo.access = O_RDWR;
 
-	if (!(*result = ncp_iget(dir, &finfo)) < 0) {
-		ncp_close_file(NCP_SERVER(dir), finfo.file_handle);
-		unlock_super(dir->i_sb);
-		iput(dir);
-		return -EINVAL;
-	}
-	unlock_super(dir->i_sb);
-	iput(dir);
-	return 0;
+out:
+	return error;
 }
 
-static int ncp_mkdir(struct inode *dir, const char *name, int len, int mode)
+static int ncp_mkdir(struct inode *dir, struct dentry *dentry, int mode)
 {
 	int error;
-	struct nw_file_info new_dir;
-	__u8 _name[len + 1];
+	struct ncpfs_inode_info finfo;
+	__u8 _name[dentry->d_name.len + 1];
 
-	if ((name[0] == '.')
-	    && ((len == 1)
-		|| ((len == 2)
-		    && (name[1] == '.')))) {
-		iput(dir);
-		return -EEXIST;
+	DPRINTK(KERN_DEBUG "ncp_mkdir: making %s/%s\n",
+		dentry->d_parent->d_name.name, dentry->d_name.name);
+	error = -ENOTDIR;
+	if (!dir || !S_ISDIR(dir->i_mode)) {
+		printk(KERN_WARNING "ncp_mkdir: inode is NULL or not a directory\n");
+		goto out;
 	}
-	strncpy(_name, name, len);
-	_name[len] = '\0';
+	error = -EIO;
+	if (!ncp_conn_valid(NCP_SERVER(dir)))
+		goto out;
 
+	strncpy(_name, dentry->d_name.name, dentry->d_name.len);
+	_name[dentry->d_name.len] = '\0';
 	if (!ncp_preserve_case(dir)) {
 		str_upper(_name);
 	}
-	if (!dir || !S_ISDIR(dir->i_mode)) {
-		printk("ncp_mkdir: inode is NULL or not a directory\n");
-		iput(dir);
-		return -ENOENT;
-	}
-	if (!ncp_conn_valid(NCP_SERVER(dir))) {
-		iput(dir);
-		return -EIO;
-	}
-	if (ncp_open_create_file_or_subdir(NCP_SERVER(dir),
-					   NCP_ISTRUCT(dir), _name,
+
+	error = -EACCES;
+	if (ncp_open_create_file_or_subdir(NCP_SERVER(dir), dir, _name,
 					   OC_MODE_CREATE, aDIR, 0xffff,
-					   &new_dir) != 0) {
-		error = -EACCES;
-	} else {
-		error = 0;
-		ncp_invalid_dir_cache(dir);
+					   &finfo.nw_info) == 0)
+	{
+		error = ncp_instantiate(dir, dentry, &finfo);
 	}
-
-	iput(dir);
+out:
 	return error;
 }
 
-static int ncp_rmdir(struct inode *dir, const char *name, int len)
+static int ncp_rmdir(struct inode *dir, struct dentry *dentry)
 {
-	int error;
-	__u8 _name[len + 1];
+	int error, result;
+	__u8 _name[dentry->d_name.len + 1];
 
-	if (!dir || !S_ISDIR(dir->i_mode)) {
-		printk("ncp_rmdir: inode is NULL or not a directory\n");
-		iput(dir);
-		return -ENOENT;
-	}
-	if (!ncp_conn_valid(NCP_SERVER(dir))) {
-		iput(dir);
-		return -EIO;
-	}
-	if (ncp_find_dir_inode(dir, name) != NULL) {
-		iput(dir);
+	DPRINTK(KERN_DEBUG "ncp_rmdir: removing %s/%s\n",
+		dentry->d_parent->d_name.name, dentry->d_name.name);
+	
+	error = -ENOENT;
+	if (!dir || !S_ISDIR(dir->i_mode))
+	{
+		printk(KERN_WARNING "ncp_rmdir: inode is NULL or not a directory\n");
+		goto out;
+	}
+	error = -EIO;
+	if (!ncp_conn_valid(NCP_SERVER(dir)))
+		goto out;
+
+	if (dentry->d_count > 1)
+	{
+		shrink_dcache_parent(dentry);
 		error = -EBUSY;
-	} else {
-
-		strncpy(_name, name, len);
-		_name[len] = '\0';
+		if (dentry->d_count > 1)
+			goto out;
+	}
 
-		if (!ncp_preserve_case(dir)) {
-			str_upper(_name);
-		}
-		if ((error = ncp_del_file_or_subdir(NCP_SERVER(dir),
-						    NCP_ISTRUCT(dir),
-						    _name)) == 0) {
-			ncp_invalid_dir_cache(dir);
-		} else {
-			error = -EACCES;
-		}
+	strncpy(_name, dentry->d_name.name, dentry->d_name.len);
+	_name[dentry->d_name.len] = '\0';
+	    
+	if (!ncp_preserve_case(dir))
+	{
+		str_upper(_name);
 	}
-	iput(dir);
+	error = -EACCES;
+	result = ncp_del_file_or_subdir(NCP_SERVER(dir), dir, _name);
+	if (!result)
+	{
+		ncp_invalid_dir_cache(dir);
+		d_delete(dentry);
+		error = 0;
+    	}
+out:
 	return error;
 }
 
-static int ncp_unlink(struct inode *dir, const char *name, int len)
+static int ncp_unlink(struct inode *dir, struct dentry *dentry)
 {
-	int error;
-	__u8 _name[len + 1];
-
+	struct inode *inode = dentry->d_inode;
+	int error, result;
+	__u8 _name[dentry->d_name.len + 1];
+
+	DPRINTK(KERN_DEBUG "ncp_unlink: unlinking %s/%s\n",
+		dentry->d_parent->d_name.name, dentry->d_name.name);
+	
+	error = -ENOTDIR;
 	if (!dir || !S_ISDIR(dir->i_mode)) {
-		printk("ncp_unlink: inode is NULL or not a directory\n");
-		iput(dir);
-		return -ENOENT;
+		printk(KERN_WARNING "ncp_unlink: inode is NULL or not a directory\n");
+		goto out;
 	}
-	if (!ncp_conn_valid(NCP_SERVER(dir))) {
-		iput(dir);
-		return -EIO;
+	error = -EIO;
+	if (!ncp_conn_valid(NCP_SERVER(dir)))
+		goto out;
+
+	/*
+	 * Check whether to close the file ...
+	 */
+	if (inode && NCP_FINFO(inode)->opened) {
+#ifdef NCPFS_PARANOIA
+printk(KERN_DEBUG "ncp_unlink: closing file\n");
+#endif
+		ncp_make_closed(inode);
 	}
-	if (ncp_find_dir_inode(dir, name) != NULL) {
-		iput(dir);
-		error = -EBUSY;
-	} else {
-		strncpy(_name, name, len);
-		_name[len] = '\0';
 
-		if (!ncp_preserve_case(dir)) {
-			str_upper(_name);
-		}
-		if ((error = ncp_del_file_or_subdir(NCP_SERVER(dir),
-						    NCP_ISTRUCT(dir),
-						    _name)) == 0) {
-			ncp_invalid_dir_cache(dir);
-		} else {
-			error = -EACCES;
-		}
+	strncpy(_name, dentry->d_name.name, dentry->d_name.len);
+	_name[dentry->d_name.len] = '\0';
+	if (!ncp_preserve_case(dir))
+	{
+		str_upper(_name);
 	}
-	iput(dir);
+	error = -EACCES;
+	result = ncp_del_file_or_subdir(NCP_SERVER(dir), dir, _name);
+	if (!result) {
+		DPRINTK(KERN_DEBUG "ncp: removed %s/%s\n",
+			dentry->d_parent->d_name.name, dentry->d_name.name);
+		ncp_invalid_dir_cache(dir);
+		d_delete(dentry);
+		error = 0;
+	}
+out:
 	return error;
 }
 
-static int ncp_rename(struct inode *old_dir, const char *old_name, int old_len,
-		      struct inode *new_dir, const char *new_name, int new_len)
+static int ncp_rename(struct inode *old_dir, struct dentry *old_dentry,
+		      struct inode *new_dir, struct dentry *new_dentry)
 {
-	int res;
-	char _old_name[old_len + 1];
-	char _new_name[new_len + 1];
+	int old_len = old_dentry->d_name.len;
+	int new_len = new_dentry->d_name.len;
+	int error, result;
+	char _old_name[old_dentry->d_name.len + 1];
+	char _new_name[new_dentry->d_name.len + 1];
+
+	DPRINTK(KERN_DEBUG "ncp_rename: %s/%s to %s/%s\n",
+		old_dentry->d_parent->d_name.name, old_dentry->d_name.name,
+		new_dentry->d_parent->d_name.name, new_dentry->d_name.name);
 
+	error = -ENOTDIR;
 	if (!old_dir || !S_ISDIR(old_dir->i_mode)) {
-		printk("ncp_rename: old inode is NULL or not a directory\n");
-		res = -ENOENT;
-		goto finished;
-	}
-	if (!ncp_conn_valid(NCP_SERVER(old_dir))) {
-		res = -EIO;
-		goto finished;
+		printk(KERN_WARNING "ncp_rename: old inode is NULL or not a directory\n");
+		goto out;
 	}
 	if (!new_dir || !S_ISDIR(new_dir->i_mode)) {
-		printk("ncp_rename: new inode is NULL or not a directory\n");
-		res = -ENOENT;
-		goto finished;
+		printk(KERN_WARNING "ncp_rename: new inode is NULL or not a directory\n");
+		goto out;
 	}
-	if ((ncp_find_dir_inode(old_dir, old_name) != NULL)
-	    || (ncp_find_dir_inode(new_dir, new_name) != NULL)) {
-		res = -EBUSY;
-		goto finished;
-	}
-	strncpy(_old_name, old_name, old_len);
-	_old_name[old_len] = '\0';
+	error = -EIO;
+	if (!ncp_conn_valid(NCP_SERVER(old_dir)))
+		goto out;
 
+	strncpy(_old_name, old_dentry->d_name.name, old_len);
+	_old_name[old_len] = '\0';
 	if (!ncp_preserve_case(old_dir)) {
 		str_upper(_old_name);
 	}
-	strncpy(_new_name, new_name, new_len);
-	_new_name[new_len] = '\0';
 
+	strncpy(_new_name, new_dentry->d_name.name, new_len);
+	_new_name[new_len] = '\0';
 	if (!ncp_preserve_case(new_dir)) {
 		str_upper(_new_name);
 	}
-	res = ncp_ren_or_mov_file_or_subdir(NCP_SERVER(old_dir),
-					 NCP_ISTRUCT(old_dir), _old_name,
-					NCP_ISTRUCT(new_dir), _new_name);
 
-	if (res == 0) {
+	error = -EACCES;
+	result = ncp_ren_or_mov_file_or_subdir(NCP_SERVER(old_dir),
+					    old_dir, _old_name,
+					    new_dir, _new_name);
+	if (result == 0)
+	{
+		DPRINTK(KERN_DEBUG "ncp renamed %s -> %s.\n",
+			old_dentry->d_name.name,new_dentry->d_name.name);
 		ncp_invalid_dir_cache(old_dir);
 		ncp_invalid_dir_cache(new_dir);
-	} else {
-		res = -EACCES;
+		d_move(old_dentry,new_dentry);
+		error = 0;
 	}
-
-      finished:
-	iput(old_dir);
-	iput(new_dir);
-	return res;
+out:
+	return error;
 }
 
 /* The following routines are taken directly from msdos-fs */
@@ -1024,7 +1026,7 @@
 
 static int day_n[] =
 {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 0, 0, 0, 0};
-		  /* JanFebMarApr May Jun Jul Aug Sep Oct Nov Dec */
+/* Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec */
 
 
 extern struct timezone sys_tz;
@@ -1042,23 +1044,24 @@
 }
 
 /* Convert a MS-DOS time/date pair to a UNIX date (seconds since 1 1 70). */
-
-int ncp_date_dos2unix(unsigned short time, unsigned short date)
+int
+ncp_date_dos2unix(unsigned short time, unsigned short date)
 {
 	int month, year, secs;
 
 	month = ((date >> 5) & 15) - 1;
 	year = date >> 9;
-	secs = (time & 31) * 2 + 60 * ((time >> 5) & 63) + (time >> 11) * 3600 + 86400 *
-	    ((date & 31) - 1 + day_n[month] + (year / 4) + year * 365 - ((year & 3) == 0 &&
-					      month < 2 ? 1 : 0) + 3653);
+	secs = (time & 31) * 2 + 60 * ((time >> 5) & 63) + (time >> 11) * 3600 +
+		86400 * ((date & 31) - 1 + day_n[month] + (year / 4) + 
+		year * 365 - ((year & 3) == 0 && month < 2 ? 1 : 0) + 3653);
 	/* days since 1.1.70 plus 80's leap day */
 	return local2utc(secs);
 }
 
 
 /* Convert linear UNIX date to a MS-DOS time/date pair. */
-void ncp_date_unix2dos(int unix_date, unsigned short *time, unsigned short *date)
+void
+ncp_date_unix2dos(int unix_date, unsigned short *time, unsigned short *date)
 {
 	int day, year, nl_day, month;
 
diff -u --recursive --new-file v2.1.62/linux/fs/ncpfs/file.c linux/fs/ncpfs/file.c
--- v2.1.62/linux/fs/ncpfs/file.c	Tue Sep 23 16:48:49 1997
+++ linux/fs/ncpfs/file.c	Wed Nov 12 11:10:40 1997
@@ -2,6 +2,7 @@
  *  file.c
  *
  *  Copyright (C) 1995, 1996 by Volker Lendecke
+ *  Modified 1997 Peter Waltenberg, Bill Hawes, David Woodhouse for 2.1 dcache
  *
  */
 
@@ -29,82 +30,113 @@
 	return 0;
 }
 
-int ncp_make_open(struct inode *i, int right)
+/*
+ * Open a file with the specified read/write mode.
+ */
+int ncp_make_open(struct inode *inode, int right)
 {
-	struct nw_file_info *finfo;
-
-	if (i == NULL) {
-		printk("ncp_make_open: got NULL inode\n");
-		return -EINVAL;
-	}
-	finfo = NCP_FINFO(i);
-
-	DPRINTK("ncp_make_open: dirent->opened = %d\n", finfo->opened);
-
-	lock_super(i->i_sb);
-	if (finfo->opened == 0) {
-		finfo->access = -1;
+	int error, result;
+	int access;
+	struct nw_file_info finfo;
+
+	error = -EINVAL;
+	if (!inode) {
+		printk(KERN_ERR "ncp_make_open: got NULL inode\n");
+		goto out;
+	}
+
+	DPRINTK(KERN_DEBUG "ncp_make_open: opened=%d, volume # %u, dir entry # %u\n",
+		NCP_FINFO(inode)->opened, 
+		NCP_FINFO(inode)->volNumber, 
+		NCP_FINFO(inode)->dirEntNum);
+	error = -EACCES;
+	lock_super(inode->i_sb);
+	if (!NCP_FINFO(inode)->opened) {
+		finfo.i.dirEntNum = NCP_FINFO(inode)->dirEntNum;
+		finfo.i.volNumber = NCP_FINFO(inode)->volNumber;
 		/* tries max. rights */
-		if (ncp_open_create_file_or_subdir(NCP_SERVER(i),
-						   NULL, NULL,
-						   OC_MODE_OPEN, 0,
-						   AR_READ | AR_WRITE,
-						   finfo) == 0) {
-			finfo->access = O_RDWR;
-		} else if (ncp_open_create_file_or_subdir(NCP_SERVER(i),
-							  NULL, NULL,
-							  OC_MODE_OPEN, 0,
-							  AR_READ,
-							  finfo) == 0) {
-			finfo->access = O_RDONLY;
+		finfo.access = O_RDWR;
+		result = ncp_open_create_file_or_subdir(NCP_SERVER(inode),
+					NULL, NULL, OC_MODE_OPEN,
+					0, AR_READ | AR_WRITE, &finfo);
+		if (!result)
+			goto update;
+		finfo.access = O_RDONLY;
+		result = ncp_open_create_file_or_subdir(NCP_SERVER(inode),
+					NULL, NULL, OC_MODE_OPEN,
+					0, AR_READ, &finfo);
+		if (!result) {
+#ifdef NCPFS_PARANOIA
+printk(KERN_DEBUG "ncp_make_open: failed, result=%d\n", result);
+#endif
+			goto out_unlock;
 		}
-	}
-	unlock_super(i->i_sb);
-
-	if (((right == O_RDONLY) && ((finfo->access == O_RDONLY)
-				     || (finfo->access == O_RDWR)))
-	    || ((right == O_WRONLY) && ((finfo->access == O_WRONLY)
-					|| (finfo->access == O_RDWR)))
-	    || ((right == O_RDWR) && (finfo->access == O_RDWR)))
-		return 0;
-
-	return -EACCES;
+		/*
+		 * Update the inode information.
+		 */
+	update:
+		ncp_update_inode(inode, &finfo);
+	}
+
+	access = NCP_FINFO(inode)->access;
+#ifdef NCPFS_PARANOIA
+printk(KERN_DEBUG "ncp_make_open: file open, access=%x\n", access);
+#endif
+	if (((right == O_RDONLY) && ((access == O_RDONLY)
+				     || (access == O_RDWR)))
+	    || ((right == O_WRONLY) && ((access == O_WRONLY)
+					|| (access == O_RDWR)))
+	    || ((right == O_RDWR) && (access == O_RDWR)))
+		error = 0;
+
+out_unlock:
+	unlock_super(inode->i_sb);
+out:
+	return error;
 }
 
-static long ncp_file_read(struct inode *inode, struct file *file, char *buf, unsigned long count)
+static ssize_t
+ncp_file_read(struct file *file, char *buf, size_t count, loff_t *ppos)
 {
-	int bufsize, already_read;
+	struct dentry *dentry = file->f_dentry;
+	struct inode *inode = dentry->d_inode;
+	size_t already_read = 0;
 	off_t pos;
-	int errno;
+	int bufsize, error;
 
-	DPRINTK("ncp_file_read: enter %s\n", NCP_ISTRUCT(inode)->entryName);
+	DPRINTK(KERN_DEBUG "ncp_file_read: enter %s/%s\n",
+		dentry->d_parent->d_name.name, dentry->d_name.name);
 
+	error = -EINVAL;
 	if (inode == NULL) {
-		DPRINTK("ncp_file_read: inode = NULL\n");
-		return -EINVAL;
-	}
-	if (!ncp_conn_valid(NCP_SERVER(inode))) {
-		return -EIO;
+		DPRINTK(KERN_DEBUG "ncp_file_read: inode = NULL\n");
+		goto out;
 	}
+	error = -EIO;
+	if (!ncp_conn_valid(NCP_SERVER(inode)))
+		goto out;
+	error = -EINVAL;
 	if (!S_ISREG(inode->i_mode)) {
-		DPRINTK("ncp_file_read: read from non-file, mode %07o\n",
+		DPRINTK(KERN_DEBUG "ncp_file_read: read from non-file, mode %07o\n",
 			inode->i_mode);
-		return -EINVAL;
+		goto out;
 	}
-	pos = file->f_pos;
 
+	pos = file->f_pos;
 	if (pos + count > inode->i_size) {
 		count = inode->i_size - pos;
 	}
-	if (count <= 0) {
-		return 0;
+	error = 0;
+	if (!count)	/* size_t is never < 0 */
+		goto out;
+
+	error = ncp_make_open(inode, O_RDONLY);
+	if (error) {
+		printk(KERN_ERR "ncp_file_read: open failed, error=%d\n", error);
+		goto out;
 	}
-	if ((errno = ncp_make_open(inode, O_RDONLY)) != 0) {
-		return errno;
-	}
-	bufsize = NCP_SERVER(inode)->buffer_size;
 
-	already_read = 0;
+	bufsize = NCP_SERVER(inode)->buffer_size;
 
 	/* First read in as much as possible for each bufsize. */
 	while (already_read < count) {
@@ -112,9 +144,12 @@
 		int to_read = min(bufsize - (pos % bufsize),
 				  count - already_read);
 
-		if (ncp_read(NCP_SERVER(inode), NCP_FINFO(inode)->file_handle,
-			     pos, to_read, buf, &read_this_time) != 0) {
-			return -EIO;	/* This is not exact, i know.. */
+		error = ncp_read(NCP_SERVER(inode),
+			 	NCP_FINFO(inode)->file_handle,
+				pos, to_read, buf, &read_this_time);
+		if (error) {
+			error = -EIO;	/* This is not exact, i know.. */
+			goto out;
 		}
 		pos += read_this_time;
 		buf += read_this_time;
@@ -130,38 +165,43 @@
 	if (!IS_RDONLY(inode)) {
 		inode->i_atime = CURRENT_TIME;
 	}
-	mark_inode_dirty(inode);
 	
-	DPRINTK("ncp_file_read: exit %s\n", NCP_ISTRUCT(inode)->entryName);
-
-	return already_read;
+	DPRINTK(KERN_DEBUG "ncp_file_read: exit %s/%s\n",
+		dentry->d_parent->d_name.name, dentry->d_name.name);
+out:
+	return already_read ? already_read : error;
 }
 
-static long ncp_file_write(struct inode *inode, struct file *file, const char *buf,
-			   unsigned long count)
+static ssize_t
+ncp_file_write(struct file *file, const char *buf, size_t count, loff_t *ppos)
 {
-	int bufsize, already_written;
+	struct dentry *dentry = file->f_dentry;
+	struct inode *inode = dentry->d_inode;
+	size_t already_written = 0;
 	off_t pos;
-	int errno;
+	int bufsize, errno;
 
+	DPRINTK(KERN_DEBUG "ncp_file_write: enter %s/%s\n",
+		dentry->d_parent->d_name.name, dentry->d_name.name);
 	if (inode == NULL) {
-		DPRINTK("ncp_file_write: inode = NULL\n");
+		DPRINTK(KERN_DEBUG "ncp_file_write: inode = NULL\n");
 		return -EINVAL;
 	}
-	if (!ncp_conn_valid(NCP_SERVER(inode))) {
-		return -EIO;
-	}
+	errno = -EIO;
+	if (!ncp_conn_valid(NCP_SERVER(inode)))
+		goto out;
 	if (!S_ISREG(inode->i_mode)) {
-		DPRINTK("ncp_file_write: write to non-file, mode %07o\n",
+		DPRINTK(KERN_DEBUG "ncp_file_write: write to non-file, mode %07o\n",
 			inode->i_mode);
 		return -EINVAL;
 	}
-	DPRINTK("ncp_file_write: enter %s\n", NCP_ISTRUCT(inode)->entryName);
 
-	if (count <= 0) {
-		return 0;
-	}
-	if ((errno = ncp_make_open(inode, O_RDWR)) != 0) {
+	errno = 0;
+	if (!count)
+		goto out;
+	errno = ncp_make_open(inode, O_RDWR);
+	if (errno) {
+		printk(KERN_ERR "ncp_file_write: open failed, error=%d\n", errno);
 		return errno;
 	}
 	pos = file->f_pos;
@@ -192,17 +232,17 @@
 	}
 
 	inode->i_mtime = inode->i_ctime = CURRENT_TIME;
-	mark_inode_dirty(inode);
 	
 	file->f_pos = pos;
 
 	if (pos > inode->i_size) {
 		inode->i_size = pos;
-		ncp_invalid_dir_cache(NCP_INOP(inode)->dir->inode);
+		ncp_invalid_dir_cache(dentry->d_parent->d_inode);
 	}
-	DPRINTK("ncp_file_write: exit %s\n", NCP_ISTRUCT(inode)->entryName);
-
-	return already_written;
+	DPRINTK(KERN_DEBUG "ncp_file_write: exit %s/%s\n",
+		dentry->d_parent->d_name.name, dentry->d_name.name);
+out:
+	return already_written ? already_written : errno;
 }
 
 static struct file_operations ncp_file_operations =
diff -u --recursive --new-file v2.1.62/linux/fs/ncpfs/inode.c linux/fs/ncpfs/inode.c
--- v2.1.62/linux/fs/ncpfs/inode.c	Mon Aug 18 18:19:46 1997
+++ linux/fs/ncpfs/inode.c	Wed Nov 12 11:10:40 1997
@@ -3,6 +3,7 @@
  *
  *  Copyright (C) 1995, 1996 by Volker Lendecke
  *  Modified for big endian by J.F. Chadima and David S. Miller
+ *  Modified 1997 Peter Waltenberg, Bill Hawes, David Woodhouse for 2.1 dcache
  *
  */
 
@@ -14,7 +15,6 @@
 #include <asm/byteorder.h>
 
 #include <linux/sched.h>
-#include <linux/ncp_fs.h>
 #include <linux/kernel.h>
 #include <linux/mm.h>
 #include <linux/string.h>
@@ -28,84 +28,108 @@
 #ifdef CONFIG_KERNELD
 #include <linux/kerneld.h>
 #endif
+
+#include <linux/ncp_fs.h>
 #include "ncplib_kernel.h"
 
 extern int close_fp(struct file *filp);
 
-static void ncp_put_inode(struct inode *);
 static void ncp_read_inode(struct inode *);
+static void ncp_put_inode(struct inode *);
+static void ncp_delete_inode(struct inode *);
+static int  ncp_notify_change(struct inode *, struct iattr *);
 static void ncp_put_super(struct super_block *);
-static void ncp_statfs(struct super_block *sb, struct statfs *buf, int bufsiz);
-static int ncp_notify_change(struct inode *inode, struct iattr *attr);
+static int  ncp_statfs(struct super_block *, struct statfs *, int);
 
 static struct super_operations ncp_sops =
 {
 	ncp_read_inode,		/* read inode */
-	ncp_notify_change,	/* notify change */
 	NULL,			/* write inode */
 	ncp_put_inode,		/* put inode */
+	ncp_delete_inode,       /* delete inode */
+	ncp_notify_change,	/* notify change */
 	ncp_put_super,		/* put superblock */
 	NULL,			/* write superblock */
 	ncp_statfs,		/* stat filesystem */
-	NULL
+	NULL                    /* remount */
 };
 
-/* ncp_read_inode: Called from iget, it only traverses the allocated
-   ncp_inode_info's and initializes the inode from the data found
-   there.  It does not allocate or deallocate anything. */
+static struct nw_file_info *read_nwinfo = NULL;
+static struct semaphore read_sem = MUTEX;
 
-static void ncp_read_inode(struct inode *inode)
+/*
+ * Fill in the ncpfs-specific information in the inode.
+ */
+void ncp_update_inode(struct inode *inode, struct nw_file_info *nwinfo)
 {
-	/* Our task should be extremely simple here. We only have to
-	   look up the information somebody else (ncp_iget) put into
-	   the inode tree. The address of this information is the
-	   inode->i_ino. Just to make sure everything went well, we
-	   check it's there. */
-
-	struct ncp_inode_info *inode_info = ncp_find_inode(inode);
-
-	if (inode_info == NULL) {
-		/* Ok, now we're in trouble. The inode info is not there. What
-		   should we do now??? */
-		printk("ncp_read_inode: inode info not found\n");
-		return;
-	}
-	inode_info->state = NCP_INODE_VALID;
+	NCP_FINFO(inode)->DosDirNum = nwinfo->i.DosDirNum;
+	NCP_FINFO(inode)->dirEntNum = nwinfo->i.dirEntNum;
+	NCP_FINFO(inode)->volNumber = nwinfo->i.volNumber;
+
+	NCP_FINFO(inode)->opened = nwinfo->opened;
+	NCP_FINFO(inode)->access = nwinfo->access;
+	NCP_FINFO(inode)->server_file_handle = nwinfo->server_file_handle;
+	memcpy(NCP_FINFO(inode)->file_handle, nwinfo->file_handle,
+			sizeof(nwinfo->file_handle));
+#ifdef NCPFS_DEBUG_VERBOSE
+printk(KERN_DEBUG "ncp_update_inode: updated %s, volnum=%d, dirent=%u\n",
+nwinfo->i.entryName, NCP_FINFO(inode)->volNumber, NCP_FINFO(inode)->dirEntNum);
+#endif
+}
 
-	NCP_INOP(inode) = inode_info;
-	inode_info->inode = inode;
+/*
+ * Fill in the inode based on the nw_file_info structure.
+ */
+static void ncp_set_attr(struct inode *inode, struct nw_file_info *nwinfo)
+{
+	struct nw_info_struct *nwi = &nwinfo->i;
+	struct ncp_server *server = NCP_SERVER(inode);
 
-	if (NCP_ISTRUCT(inode)->attributes & aDIR) {
-		inode->i_mode = NCP_SERVER(inode)->m.dir_mode;
+	if (nwi->attributes & aDIR) {
+		inode->i_mode = server->m.dir_mode;
 		/* for directories dataStreamSize seems to be some
 		   Object ID ??? */
 		inode->i_size = 512;
 	} else {
-		inode->i_mode = NCP_SERVER(inode)->m.file_mode;
-		inode->i_size = le32_to_cpu(NCP_ISTRUCT(inode)->dataStreamSize);
+		inode->i_mode = server->m.file_mode;
+		inode->i_size = le32_to_cpu(nwi->dataStreamSize);
 	}
 
-	DDPRINTK("ncp_read_inode: inode->i_mode = %u\n", inode->i_mode);
+	DDPRINTK(KERN_DEBUG "ncp_read_inode: inode->i_mode = %u\n", inode->i_mode);
 
 	inode->i_nlink = 1;
-	inode->i_uid = NCP_SERVER(inode)->m.uid;
-	inode->i_gid = NCP_SERVER(inode)->m.gid;
+	inode->i_uid = server->m.uid;
+	inode->i_gid = server->m.gid;
 	inode->i_blksize = 512;
 	inode->i_rdev = 0;
 
+	inode->i_blocks = 0;
 	if ((inode->i_blksize != 0) && (inode->i_size != 0)) {
 		inode->i_blocks =
 		    (inode->i_size - 1) / inode->i_blksize + 1;
-	} else {
-		inode->i_blocks = 0;
 	}
 
-	inode->i_mtime = ncp_date_dos2unix(le16_to_cpu(NCP_ISTRUCT(inode)->modifyTime),
-			    le16_to_cpu(NCP_ISTRUCT(inode)->modifyDate));
-	inode->i_ctime = ncp_date_dos2unix(le16_to_cpu(NCP_ISTRUCT(inode)->creationTime),
-			  le16_to_cpu(NCP_ISTRUCT(inode)->creationDate));
+	inode->i_mtime = ncp_date_dos2unix(le16_to_cpu(nwi->modifyTime),
+			  		   le16_to_cpu(nwi->modifyDate));
+	inode->i_ctime = ncp_date_dos2unix(le16_to_cpu(nwi->creationTime),
+			    		   le16_to_cpu(nwi->creationDate));
 	inode->i_atime = ncp_date_dos2unix(0,
-			le16_to_cpu(NCP_ISTRUCT(inode)->lastAccessDate));
+					   le16_to_cpu(nwi->lastAccessDate));
+	ncp_update_inode(inode, nwinfo);
+}
+
+/*
+ * This is called from iget() with the read semaphore held. 
+ * The global ncpfs_file_info structure has been set up by ncp_iget.
+ */
+static void ncp_read_inode(struct inode *inode)
+{
+	if (read_nwinfo == NULL) {
+		printk(KERN_ERR "ncp_read_inode: invalid call\n");
+		return;
+	}
+
+	ncp_set_attr(inode, read_nwinfo);
 
 	if (S_ISREG(inode->i_mode)) {
 		inode->i_op = &ncp_file_inode_operations;
@@ -116,73 +140,103 @@
 	}
 }
 
-static void ncp_put_inode(struct inode *inode)
+/*
+ * Set up the ncpfs_inode_info pointer and get a new inode.
+ */
+struct inode * 
+ncp_iget(struct super_block *sb, struct ncpfs_inode_info *info)
 {
-	struct nw_file_info *finfo = NCP_FINFO(inode);
-	struct super_block *sb = inode->i_sb;
+	struct inode *inode;
 
-	lock_super(sb);
-	if (finfo->opened != 0) {
-		if (ncp_close_file(NCP_SERVER(inode), finfo->file_handle) != 0) {
-			/* We can't do anything but complain. */
-			printk("ncp_put_inode: could not close\n");
-		}
+	if (info == NULL) {
+		printk(KERN_ERR "ncp_iget: info is NULL\n");
+		return NULL;
 	}
-	DDPRINTK("ncp_put_inode: put %s\n",
-		 finfo->i.entryName);
 
-	ncp_free_inode_info(NCP_INOP(inode));
+	down(&read_sem);
+	read_nwinfo = &info->nw_info;
+	inode = iget(sb, info->ino);
+	read_nwinfo = NULL;
+	up(&read_sem);
+	if (!inode)
+		printk(KERN_ERR "ncp_iget: iget failed!\n");
+	return inode;
+}
+
+static void ncp_put_inode(struct inode *inode)
+{
+	if (inode->i_count == 1)
+		inode->i_nlink = 0;
+}
 
+static void
+ncp_delete_inode(struct inode *inode)
+{
 	if (S_ISDIR(inode->i_mode)) {
-		DDPRINTK("ncp_put_inode: put directory %ld\n",
-			 inode->i_ino);
+		DDPRINTK(KERN_DEBUG "ncp_delete_inode: put directory %ld\n", inode->i_ino);
 		ncp_invalid_dir_cache(inode);
 	}
+
+	if (NCP_FINFO(inode)->opened && ncp_make_closed(inode) != 0) {
+		/* We can't do anything but complain. */
+		printk(KERN_ERR "ncp_delete_inode: could not close\n");
+	}
 	clear_inode(inode);
-	unlock_super(sb);
+}
+
+static void ncp_init_root(struct ncp_server *server,
+			struct ncpfs_inode_info *info)
+{
+	struct ncp_inode_info *root = &(server->root);
+	struct nw_info_struct *i = &(root->finfo.i);
+	unsigned short dummy;
+
+	DPRINTK(KERN_DEBUG "ncp_init_root: i = %x\n", (int) i);
+
+	i->attributes	 = aDIR;
+	i->dataStreamSize= 1024;
+	i->dirEntNum	 = 0;
+	i->DosDirNum	 = 0;
+	i->volNumber	 = NCP_NUMBER_OF_VOLUMES + 1;	/* illegal volnum */
+	ncp_date_unix2dos(0, &(i->creationTime), &(i->creationDate));
+	ncp_date_unix2dos(0, &(i->modifyTime  ), &(i->modifyDate));
+	ncp_date_unix2dos(0, &(dummy          ), &(i->lastAccessDate));
+	i->creationTime	 = le16_to_cpu(i->creationTime);
+	i->creationDate	 = le16_to_cpu(i->creationDate);
+	i->modifyTime	 = le16_to_cpu(i->modifyTime);
+	i->modifyDate	 = le16_to_cpu(i->modifyDate);
+	i->lastAccessDate= le16_to_cpu(i->lastAccessDate);
+	i->nameLen	 = 0;
+	i->entryName[0]  = '\0';
+
+	root->finfo.opened= 0;
+	info->ino = 1;
+	info->nw_info = root->finfo;
 }
 
 struct super_block *
- ncp_read_super(struct super_block *sb, void *raw_data, int silent)
+ncp_read_super(struct super_block *sb, void *raw_data, int silent)
 {
 	struct ncp_mount_data *data = (struct ncp_mount_data *) raw_data;
 	struct ncp_server *server;
 	struct file *ncp_filp;
+	struct inode *root_inode;
 	kdev_t dev = sb->s_dev;
 	int error;
+	struct ncpfs_inode_info finfo;
 
-	if (data == NULL) {
-		printk("ncp_read_super: missing data argument\n");
-		sb->s_dev = 0;
-		return NULL;
-	}
-	if (data->version != NCP_MOUNT_VERSION) {
-		printk("ncp warning: mount version %s than kernel\n",
-		       (data->version < NCP_MOUNT_VERSION) ?
-		       "older" : "newer");
-		sb->s_dev = 0;
-		return NULL;
-	}
-	if ((data->ncp_fd >= NR_OPEN)
-	    || ((ncp_filp = current->files->fd[data->ncp_fd]) == NULL)
-	    || (!S_ISSOCK(ncp_filp->f_dentry->d_inode->i_mode))) {
-		printk("ncp_read_super: invalid ncp socket\n");
-		sb->s_dev = 0;
-		return NULL;
-	}
-	/* We must malloc our own super-block info */
-	server = (struct ncp_server *) ncp_kmalloc(sizeof(struct ncp_server),
-						   GFP_KERNEL);
-
-	if (server == NULL) {
-		printk("ncp_read_super: could not alloc ncp_server\n");
-		return NULL;
-	}
-	ncp_filp->f_count++;
+	MOD_INC_USE_COUNT;
+	if (data == NULL)
+		goto out_no_data;
+	if (data->version != NCP_MOUNT_VERSION)
+		goto out_bad_mount;
+	if ((data->ncp_fd >= NR_OPEN) ||
+	    ((ncp_filp = current->files->fd[data->ncp_fd]) == NULL) ||
+	    !S_ISSOCK(ncp_filp->f_dentry->d_inode->i_mode))
+		goto out_bad_file;
 
 	lock_super(sb);
-
-	NCP_SBP(sb) = server;
+	ncp_filp->f_count++;
 
 	sb->s_blocksize = 1024;	/* Eh...  Is this correct? */
 	sb->s_blocksize_bits = 10;
@@ -190,6 +244,13 @@
 	sb->s_dev = dev;
 	sb->s_op = &ncp_sops;
 
+	/* We must malloc our own super-block info */
+	server = (struct ncp_server *) ncp_kmalloc(sizeof(struct ncp_server),
+						   GFP_KERNEL);
+	if (server == NULL)
+		goto out_no_server;
+	NCP_SBP(sb) = server;
+
 	server->ncp_filp = ncp_filp;
 	server->lock = 0;
 	server->wait = NULL;
@@ -202,7 +263,7 @@
 	   now because of PATH_MAX changes.. */
 	if (server->m.time_out < 10) {
 		server->m.time_out = 10;
-		printk("You need to recompile your ncpfs utils..\n");
+		printk(KERN_INFO "You need to recompile your ncpfs utils..\n");
 	}
 	server->m.file_mode = (server->m.file_mode &
 			       (S_IRWXU | S_IRWXG | S_IRWXO)) | S_IFREG;
@@ -211,54 +272,74 @@
 
 	server->packet_size = NCP_PACKET_SIZE;
 	server->packet = ncp_kmalloc(NCP_PACKET_SIZE, GFP_KERNEL);
+	if (server->packet == NULL)
+		goto out_no_packet;
 
-	if (server->packet == NULL) {
-		printk("ncpfs: could not alloc packet\n");
-		error = -ENOMEM;
-		unlock_super(sb);
-		goto fail;
-	}
 	ncp_lock_server(server);
 	error = ncp_connect(server);
 	ncp_unlock_server(server);
-	unlock_super(sb);
+	if (error < 0)
+		goto out_no_connect;
+	DPRINTK(KERN_DEBUG "ncp_read_super: NCP_SBP(sb) = %x\n", (int) NCP_SBP(sb));
+
+	error = ncp_negotiate_buffersize(server, NCP_DEFAULT_BUFSIZE,
+				    		&(server->buffer_size));
+	if (error)
+		goto out_no_bufsize;
+	DPRINTK(KERN_DEBUG "ncpfs: bufsize = %d\n", server->buffer_size);
+
+	ncp_init_root(server, &finfo);
+        root_inode = ncp_iget(sb, &finfo);
+        if (!root_inode)
+		goto out_no_root;
+	DPRINTK(KERN_DEBUG "ncp_read_super: root vol=%d\n", NCP_FINFO(root_inode)->volNumber);
+        sb->s_root = d_alloc_root(root_inode, NULL);
+        if (!sb->s_root)
+		goto out_no_root;
 
-	if (error < 0) {
-		sb->s_dev = 0;
-		printk("ncp_read_super: Failed connection, bailing out "
-		       "(error = %d).\n", -error);
-		ncp_kfree_s(server->packet, server->packet_size);
-		goto fail;
-	}
-	DPRINTK("ncp_read_super: NCP_SBP(sb) = %x\n", (int) NCP_SBP(sb));
-
-	ncp_init_root(server);
-
-        if (!(sb->s_root = d_alloc_root(iget(sb,ncp_info_ino(server,
-						    &(server->root))),NULL))) {
-		sb->s_dev = 0;
-		printk("ncp_read_super: get root inode failed\n");
-		goto disconnect;
-	}
-	if (ncp_negotiate_buffersize(server, NCP_DEFAULT_BUFSIZE,
-				     &(server->buffer_size)) != 0) {
-		sb->s_dev = 0;
-		printk("ncp_read_super: could not get bufsize\n");
-		goto disconnect;
-	}
-	DPRINTK("ncpfs: bufsize = %d\n", server->buffer_size);
-
-	MOD_INC_USE_COUNT;
+	unlock_super(sb);
 	return sb;
 
-      disconnect:
+out_no_root:
+	printk(KERN_ERR "ncp_read_super: get root inode failed\n");
+	iput(root_inode);
+	goto out_disconnect;
+out_no_bufsize:
+	printk(KERN_ERR "ncp_read_super: could not get bufsize\n");
+out_disconnect:
 	ncp_lock_server(server);
 	ncp_disconnect(server);
 	ncp_unlock_server(server);
+	goto out_free_packet;
+out_no_connect:
+	printk(KERN_ERR "ncp_read_super: Failed connection, error=%d\n", error);
+out_free_packet:
 	ncp_kfree_s(server->packet, server->packet_size);
-      fail:
-	put_filp(ncp_filp);
+	goto out_free_server;
+out_no_packet:
+	printk(KERN_ERR "ncp_read_super: could not alloc packet\n");
+out_free_server:
 	ncp_kfree_s(NCP_SBP(sb), sizeof(struct ncp_server));
+	goto out_unlock;
+out_no_server:
+	printk(KERN_ERR "ncp_read_super: could not alloc ncp_server\n");
+out_unlock:
+	put_filp(ncp_filp);
+	unlock_super(sb);
+	goto out;
+
+out_bad_file:
+	printk(KERN_ERR "ncp_read_super: invalid ncp socket\n");
+	goto out;
+out_bad_mount:
+	printk(KERN_INFO "ncp_read_super: kernel requires mount version %d\n",
+		NCP_MOUNT_VERSION);
+	goto out;
+out_no_data:
+	printk(KERN_ERR "ncp_read_super: missing data argument\n");
+out:
+	sb->s_dev = 0;
+	MOD_DEC_USE_COUNT;
 	return NULL;
 }
 
@@ -275,20 +356,17 @@
 	close_fp(server->ncp_filp);
 	kill_proc(server->m.wdog_pid, SIGTERM, 0);
 
-	ncp_free_all_inodes(server);
-
 	ncp_kfree_s(server->packet, server->packet_size);
 
-	sb->s_dev = 0;
 	ncp_kfree_s(NCP_SBP(sb), sizeof(struct ncp_server));
-	NCP_SBP(sb) = NULL;
 
+	sb->s_dev = 0;
 	unlock_super(sb);
 
 	MOD_DEC_USE_COUNT;
 }
 
-static void ncp_statfs(struct super_block *sb, struct statfs *buf, int bufsiz)
+static int ncp_statfs(struct super_block *sb, struct statfs *buf, int bufsiz)
 {
 	struct statfs tmp;
 
@@ -306,7 +384,7 @@
 	tmp.f_files = -1;
 	tmp.f_ffree = -1;
 	tmp.f_namelen = 12;
-	copy_to_user(buf, &tmp, bufsiz);
+	return copy_to_user(buf, &tmp, bufsiz) ? -EFAULT : 0;
 }
 
 static int ncp_notify_change(struct inode *inode, struct iattr *attr)
@@ -359,11 +437,9 @@
 		info.lastAccessDate = le16_to_cpu(info.lastAccessDate);
 	}
 	if (info_mask != 0) {
-		if ((result =
-		     ncp_modify_file_or_subdir_dos_info(NCP_SERVER(inode),
-						      NCP_ISTRUCT(inode),
-							info_mask,
-							&info)) != 0) {
+		result = ncp_modify_file_or_subdir_dos_info(NCP_SERVER(inode),
+				      inode, info_mask, &info);
+		if (result != 0) {
 			result = -EACCES;
 
 			if (info_mask == (DM_CREATE_TIME | DM_CREATE_DATE)) {
@@ -379,8 +455,7 @@
 	if ((attr->ia_valid & ATTR_SIZE) != 0) {
 		int written;
 
-		DPRINTK("ncpfs: trying to change size of %s to %ld\n",
-			NCP_ISTRUCT(inode)->entryName, attr->ia_size);
+		DPRINTK(KERN_DEBUG "ncpfs: trying to change size to %ld\n", attr->ia_size);
 
 		if ((result = ncp_make_open(inode, O_RDWR)) < 0) {
 			return -EACCES;
@@ -390,13 +465,12 @@
 
 		/* According to ndir, the changes only take effect after
 		   closing the file */
-		ncp_close_file(NCP_SERVER(inode),
-			       NCP_FINFO(inode)->file_handle);
-		NCP_FINFO(inode)->opened = 0;
-
-		result = 0;
+		result = ncp_make_closed(inode);
 	}
-	ncp_invalid_dir_cache(NCP_INOP(inode)->dir->inode);
+	/*
+	 * We need a dentry here ...
+	 */
+	/* ncp_invalid_dir_cache(NCP_INOP(inode)->dir->inode); */
 
 	return result;
 }
@@ -423,7 +497,10 @@
 
 int init_module(void)
 {
-	DPRINTK("ncpfs: init_module called\n");
+	DPRINTK(KERN_DEBUG "ncpfs: init_module called\n");
+
+	read_sem = MUTEX;
+	read_nwinfo = NULL;
 
 #ifdef DEBUG_NCP_MALLOC
 	ncp_malloced = 0;
@@ -436,12 +513,12 @@
 
 void cleanup_module(void)
 {
-	DPRINTK("ncpfs: cleanup_module called\n");
+	DPRINTK(KERN_DEBUG "ncpfs: cleanup_module called\n");
 	ncp_free_dir_cache();
 	unregister_filesystem(&ncp_fs_type);
 #ifdef DEBUG_NCP_MALLOC
-	printk("ncp_malloced: %d\n", ncp_malloced);
-	printk("ncp_current_malloced: %d\n", ncp_current_malloced);
+	printk(KERN_DEBUG "ncp_malloced: %d\n", ncp_malloced);
+	printk(KERN_DEBUG "ncp_current_malloced: %d\n", ncp_current_malloced);
 #endif
 }
 
diff -u --recursive --new-file v2.1.62/linux/fs/ncpfs/ioctl.c linux/fs/ncpfs/ioctl.c
--- v2.1.62/linux/fs/ncpfs/ioctl.c	Fri Apr  4 08:52:24 1997
+++ linux/fs/ncpfs/ioctl.c	Wed Nov 12 11:10:40 1997
@@ -2,25 +2,27 @@
  *  ioctl.c
  *
  *  Copyright (C) 1995, 1996 by Volker Lendecke
+ *  Modified 1997 Peter Waltenberg, Bill Hawes, David Woodhouse for 2.1 dcache
  *
  */
 
 #include <asm/uaccess.h>
 #include <linux/errno.h>
 #include <linux/fs.h>
-#include <linux/ncp_fs.h>
 #include <linux/ioctl.h>
 #include <linux/sched.h>
 #include <linux/mm.h>
+
 #include <linux/ncp.h>
+#include <linux/ncp_fs.h>
 
 int ncp_ioctl(struct inode *inode, struct file *filp,
 	      unsigned int cmd, unsigned long arg)
 {
+	struct ncp_server *server = NCP_SERVER(inode);
 	int result;
 	struct ncp_ioctl_request request;
 	struct ncp_fs_info info;
-	struct ncp_server *server = NCP_SERVER(inode);
 
 	switch (cmd) {
 	case NCP_IOC_NCPREQUEST:
@@ -56,7 +58,7 @@
 
 		ncp_request(server, request.function);
 
-		DPRINTK("ncp_ioctl: copy %d bytes\n",
+		DPRINTK(KERN_DEBUG "ncp_ioctl: copy %d bytes\n",
 			server->reply_size);
 		copy_to_user(request.data, server->packet, server->reply_size);
 
@@ -82,19 +84,18 @@
 					  sizeof(info))) != 0) {
 			return result;
 		}
-		copy_from_user(&info, (struct ncp_fs_info *) arg,
-			       sizeof(info));
+		copy_from_user(&info, (struct ncp_fs_info *) arg, sizeof(info));
 
 		if (info.version != NCP_GET_FS_INFO_VERSION) {
-			DPRINTK("info.version invalid: %d\n", info.version);
+			DPRINTK(KERN_DEBUG "info.version invalid: %d\n", info.version);
 			return -EINVAL;
 		}
 		/* TODO: info.addr = server->m.serv_addr; */
-		info.mounted_uid = server->m.mounted_uid;
-		info.connection = server->connection;
-		info.buffer_size = server->buffer_size;
-		info.volume_number = NCP_ISTRUCT(inode)->volNumber;
-		info.directory_id = NCP_ISTRUCT(inode)->DosDirNum;
+		info.mounted_uid	= server->m.mounted_uid;
+		info.connection		= server->connection;
+		info.buffer_size	= server->buffer_size;
+		info.volume_number	= NCP_FINFO(inode)->volNumber;
+		info.directory_id	= NCP_FINFO(inode)->DosDirNum;
 
 		copy_to_user((struct ncp_fs_info *) arg, &info, sizeof(info));
 		return 0;
diff -u --recursive --new-file v2.1.62/linux/fs/ncpfs/mmap.c linux/fs/ncpfs/mmap.c
--- v2.1.62/linux/fs/ncpfs/mmap.c	Tue Sep 23 16:48:49 1997
+++ linux/fs/ncpfs/mmap.c	Wed Nov 12 11:10:40 1997
@@ -2,6 +2,7 @@
  *  mmap.c
  *
  *  Copyright (C) 1995, 1996 by Volker Lendecke
+ *  Modified 1997 Peter Waltenberg, Bill Hawes, David Woodhouse for 2.1 dcache
  *
  */
 
@@ -32,13 +33,14 @@
 static unsigned long ncp_file_mmap_nopage(struct vm_area_struct *area,
 				     unsigned long address, int no_share)
 {
-	struct inode *inode = area->vm_dentry->d_inode;
+	struct dentry *dentry = area->vm_dentry;
+	struct inode *inode = dentry->d_inode;
 	unsigned long page;
 	unsigned int clear;
 	unsigned long tmp;
 	int bufsize;
 	int pos;
-	unsigned long fs;
+	mm_segment_t fs;
 
 	page = __get_free_page(GFP_KERNEL);
 	if (!page)
@@ -120,7 +122,7 @@
 {
 	struct inode *inode = file->f_dentry->d_inode;
 	
-	DPRINTK("ncp_mmap: called\n");
+	DPRINTK(KERN_DEBUG "ncp_mmap: called\n");
 
 	if (!ncp_conn_valid(NCP_SERVER(inode))) {
 		return -EIO;
@@ -132,7 +134,6 @@
 		return -EACCES;
 	if (!IS_RDONLY(inode)) {
 		inode->i_atime = CURRENT_TIME;
-		mark_inode_dirty(inode);
 	}
 
 	vma->vm_dentry = dget(file->f_dentry);
diff -u --recursive --new-file v2.1.62/linux/fs/ncpfs/ncplib_kernel.c linux/fs/ncpfs/ncplib_kernel.c
--- v2.1.62/linux/fs/ncpfs/ncplib_kernel.c	Fri Apr  4 08:52:24 1997
+++ linux/fs/ncpfs/ncplib_kernel.c	Wed Nov 12 11:10:40 1997
@@ -3,9 +3,11 @@
  *
  *  Copyright (C) 1995, 1996 by Volker Lendecke
  *  Modified for big endian by J.F. Chadima and David S. Miller
+ *  Modified 1997 Peter Waltenberg, Bill Hawes, David Woodhouse for 2.1 dcache
  *
  */
 
+
 #include "ncplib_kernel.h"
 
 static inline int min(int a, int b)
@@ -16,7 +18,7 @@
 static void assert_server_locked(struct ncp_server *server)
 {
 	if (server->lock == 0) {
-		DPRINTK("ncpfs: server not locked!\n");
+		DPRINTK(KERN_DEBUG "ncpfs: server not locked!\n");
 	}
 }
 
@@ -65,7 +67,7 @@
 	int len = strlen(s);
 	assert_server_locked(server);
 	if (len > 255) {
-		DPRINTK("ncpfs: string too long: %s\n", s);
+		DPRINTK(KERN_DEBUG "ncpfs: string too long: %s\n", s);
 		len = 255;
 	}
 	ncp_add_byte(server, len);
@@ -115,8 +117,8 @@
 	return get_unaligned((__u32 *) ncp_reply_data(server, offset));
 }
 
-int ncp_negotiate_buffersize(struct ncp_server *server,
-			     int size, int *target)
+int
+ncp_negotiate_buffersize(struct ncp_server *server, int size, int *target)
 {
 	int result;
 
@@ -133,7 +135,8 @@
 	return 0;
 }
 
-int ncp_get_volume_info_with_number(struct ncp_server *server, int n,
+int
+ncp_get_volume_info_with_number(struct ncp_server *server, int n,
 				    struct ncp_volume_info *target)
 {
 	int result;
@@ -143,8 +146,7 @@
 	ncp_add_byte(server, n);
 
 	if ((result = ncp_request(server, 22)) != 0) {
-		ncp_unlock_server(server);
-		return result;
+		goto out;
 	}
 	target->total_blocks = ncp_reply_dword(server, 0);
 	target->free_blocks = ncp_reply_dword(server, 4);
@@ -156,18 +158,21 @@
 
 	memset(&(target->volume_name), 0, sizeof(target->volume_name));
 
+	result = -EIO;
 	len = ncp_reply_byte(server, 29);
 	if (len > NCP_VOLNAME_LEN) {
-		DPRINTK("ncpfs: volume name too long: %d\n", len);
-		ncp_unlock_server(server);
-		return -EIO;
+		DPRINTK(KERN_DEBUG "ncpfs: volume name too long: %d\n", len);
+		goto out;
 	}
 	memcpy(&(target->volume_name), ncp_reply_data(server, 30), len);
+	result = 0;
+out:
 	ncp_unlock_server(server);
-	return 0;
+	return result;
 }
 
-int ncp_close_file(struct ncp_server *server, const char *file_id)
+int
+ncp_close_file(struct ncp_server *server, const char *file_id)
 {
 	int result;
 
@@ -180,10 +185,25 @@
 	return result;
 }
 
-static void ncp_add_handle_path(struct ncp_server *server,
-				__u8 vol_num,
-				__u32 dir_base, int have_dir_base,
-				char *path)
+/*
+ * Called with the superblock locked.
+ */
+int
+ncp_make_closed(struct inode *inode)
+{
+	int err;
+	NCP_FINFO(inode)->opened = 0;
+	err = ncp_close_file(NCP_SERVER(inode), NCP_FINFO(inode)->file_handle);
+#ifdef NCPFS_PARANOIA
+if (!err)
+printk(KERN_DEBUG "ncp_make_closed: volnum=%d, dirent=%u, error=%d\n",
+NCP_FINFO(inode)->volNumber, NCP_FINFO(inode)->dirEntNum, err);
+#endif
+	return err;
+}
+
+static void ncp_add_handle_path(struct ncp_server *server, __u8 vol_num,
+				__u32 dir_base, int have_dir_base, char *path)
 {
 	ncp_add_byte(server, vol_num);
 	ncp_add_dword(server, dir_base);
@@ -213,34 +233,40 @@
 	return;
 }
 
-int ncp_obtain_info(struct ncp_server *server,
-		    __u8 vol_num, __u32 dir_base,
-		    char *path,	/* At most 1 component */
-		    struct nw_info_struct *target)
+/*
+ * Returns information for a (one-component) name relative to
+ * the specified directory.
+ */
+int ncp_obtain_info(struct ncp_server *server, struct inode *dir, char *path,
+			struct nw_info_struct *target)
 {
+	__u8  volnum = NCP_FINFO(dir)->volNumber;
+	__u32 dirent = NCP_FINFO(dir)->dirEntNum;
 	int result;
 
 	if (target == NULL) {
+		printk(KERN_ERR "ncp_obtain_info: invalid call\n");
 		return -EINVAL;
 	}
 	ncp_init_request(server);
 	ncp_add_byte(server, 6);	/* subfunction */
-	ncp_add_byte(server, server->name_space[vol_num]);
-	ncp_add_byte(server, server->name_space[vol_num]);
+	ncp_add_byte(server, server->name_space[volnum]);
+	ncp_add_byte(server, server->name_space[volnum]); /* N.B. twice ?? */
 	ncp_add_word(server, htons(0xff00));	/* get all */
 	ncp_add_dword(server, RIM_ALL);
-	ncp_add_handle_path(server, vol_num, dir_base, 1, path);
+	ncp_add_handle_path(server, volnum, dirent, 1, path);
 
-	if ((result = ncp_request(server, 87)) != 0) {
-		ncp_unlock_server(server);
-		return result;
-	}
+	if ((result = ncp_request(server, 87)) != 0)
+		goto out;
 	ncp_extract_file_info(ncp_reply_data(server, 0), target);
+
+out:
 	ncp_unlock_server(server);
-	return 0;
+	return result;
 }
 
-static inline int ncp_has_os2_namespace(struct ncp_server *server, __u8 volume)
+static inline int
+ncp_has_os2_namespace(struct ncp_server *server, __u8 volume)
 {
 	int result;
 	__u8 *namespace;
@@ -253,34 +279,36 @@
 
 	if ((result = ncp_request(server, 87)) != 0) {
 		ncp_unlock_server(server);
-		return 0;
+		return 0; /* not result ?? */
 	}
 	no_namespaces = ncp_reply_word(server, 0);
 	namespace = ncp_reply_data(server, 2);
 
+	result = 1;
 	while (no_namespaces > 0) {
-		DPRINTK("get_namespaces: found %d on %d\n", *namespace, volume);
+		DPRINTK(KERN_DEBUG "get_namespaces: found %d on %d\n", *namespace, volume);
 
 		if (*namespace == 4) {
-			DPRINTK("get_namespaces: found OS2\n");
-			ncp_unlock_server(server);
-			return 1;
+			DPRINTK(KERN_DEBUG "get_namespaces: found OS2\n");
+			goto out;
 		}
 		namespace += 1;
 		no_namespaces -= 1;
 	}
+	result = 0;
+out:
 	ncp_unlock_server(server);
-	return 0;
+	return result;
 }
 
-int ncp_lookup_volume(struct ncp_server *server,
-		      char *volname,
+int 
+ncp_lookup_volume(struct ncp_server *server, char *volname,
 		      struct nw_info_struct *target)
 {
 	int result;
 	int volnum;
 
-	DPRINTK("ncp_lookup_volume: looking up vol %s\n", volname);
+	DPRINTK(KERN_DEBUG "ncp_lookup_volume: looking up vol %s\n", volname);
 
 	ncp_init_request(server);
 	ncp_add_byte(server, 22);	/* Subfunction: Generate dir handle */
@@ -306,7 +334,7 @@
 
 	server->name_space[volnum] = ncp_has_os2_namespace(server, volnum) ? 4 : 0;
 
-	DPRINTK("lookup_vol: namespace[%d] = %d\n",
+	DPRINTK(KERN_DEBUG "lookup_vol: namespace[%d] = %d\n",
 		volnum, server->name_space[volnum]);
 
 	target->nameLen = strlen(volname);
@@ -316,22 +344,22 @@
 }
 
 int ncp_modify_file_or_subdir_dos_info(struct ncp_server *server,
-				       struct nw_info_struct *file,
-				       __u32 info_mask,
+					struct inode *dir, __u32 info_mask,
 				       struct nw_modify_dos_info *info)
 {
+	__u8  volnum = NCP_FINFO(dir)->volNumber;
+	__u32 dirent = NCP_FINFO(dir)->dirEntNum;
 	int result;
 
 	ncp_init_request(server);
 	ncp_add_byte(server, 7);	/* subfunction */
-	ncp_add_byte(server, server->name_space[file->volNumber]);
+	ncp_add_byte(server, server->name_space[volnum]);
 	ncp_add_byte(server, 0);	/* reserved */
 	ncp_add_word(server, htons(0x0680));	/* search attribs: all */
 
 	ncp_add_dword(server, info_mask);
 	ncp_add_mem(server, info, sizeof(*info));
-	ncp_add_handle_path(server, file->volNumber,
-			    file->dirEntNum, 1, NULL);
+	ncp_add_handle_path(server, volnum, dirent, 1, NULL);
 
 	result = ncp_request(server, 87);
 	ncp_unlock_server(server);
@@ -339,17 +367,18 @@
 }
 
 int ncp_del_file_or_subdir(struct ncp_server *server,
-			   struct nw_info_struct *dir, char *name)
+			   struct inode *dir, char *name)
 {
+	__u8  volnum = NCP_FINFO(dir)->volNumber;
+	__u32 dirent = NCP_FINFO(dir)->dirEntNum;
 	int result;
 
 	ncp_init_request(server);
 	ncp_add_byte(server, 8);	/* subfunction */
-	ncp_add_byte(server, server->name_space[dir->volNumber]);
+	ncp_add_byte(server, server->name_space[volnum]);
 	ncp_add_byte(server, 0);	/* reserved */
 	ncp_add_word(server, ntohs(0x0680));	/* search attribs: all */
-	ncp_add_handle_path(server, dir->volNumber,
-			    dir->dirEntNum, 1, name);
+	ncp_add_handle_path(server, volnum, dirent, 1, name);
 
 	result = ncp_request(server, 87);
 	ncp_unlock_server(server);
@@ -367,22 +396,29 @@
 /* If both dir and name are NULL, then in target there's already a
    looked-up entry that wants to be opened. */
 int ncp_open_create_file_or_subdir(struct ncp_server *server,
-				   struct nw_info_struct *dir, char *name,
+				   struct inode *dir, char *name,
 				   int open_create_mode,
 				   __u32 create_attributes,
 				   int desired_acc_rights,
 				   struct nw_file_info *target)
 {
-	int result;
 	__u16 search_attribs = ntohs(0x0600);
-	__u8 volume = (dir != NULL) ? dir->volNumber : target->i.volNumber;
+	__u8  volnum = target->i.volNumber;
+	__u32 dirent = target->i.dirEntNum;
+	int result;
+
+	if (dir)
+	{
+		volnum = NCP_FINFO(dir)->volNumber;
+		dirent = NCP_FINFO(dir)->dirEntNum;
+	}
 
 	if ((create_attributes & aDIR) != 0) {
 		search_attribs |= ntohs(0x0080);
 	}
 	ncp_init_request(server);
 	ncp_add_byte(server, 1);	/* subfunction */
-	ncp_add_byte(server, server->name_space[volume]);
+	ncp_add_byte(server, server->name_space[volnum]);
 	ncp_add_byte(server, open_create_mode);
 	ncp_add_word(server, search_attribs);
 	ncp_add_dword(server, RIM_ALL);
@@ -390,53 +426,47 @@
 	/* The desired acc rights seem to be the inherited rights mask
 	   for directories */
 	ncp_add_word(server, desired_acc_rights);
+	ncp_add_handle_path(server, volnum, dirent, 1, name);
 
-	if (dir != NULL) {
-		ncp_add_handle_path(server, volume, dir->dirEntNum, 1, name);
-	} else {
-		ncp_add_handle_path(server, volume, target->i.dirEntNum,
-				    1, NULL);
-	}
-
-	if ((result = ncp_request(server, 87)) != 0) {
-		ncp_unlock_server(server);
-		return result;
-	}
+	if ((result = ncp_request(server, 87)) != 0)
+		goto out;
 	target->opened = 1;
 	target->server_file_handle = ncp_reply_dword(server, 0);
 	target->open_create_action = ncp_reply_byte(server, 4);
 
 	if (dir != NULL) {
 		/* in target there's a new finfo to fill */
-		ncp_extract_file_info(ncp_reply_data(server, 5), &(target->i));
+		ncp_extract_file_info(ncp_reply_data(server, 6), &(target->i));
 	}
 	ConvertToNWfromDWORD(target->server_file_handle, target->file_handle);
 
+out:
 	ncp_unlock_server(server);
-	return 0;
+	return result;
 }
 
-
-int ncp_initialize_search(struct ncp_server *server,
-			  struct nw_info_struct *dir,
-			  struct nw_search_sequence *target)
+int
+ncp_initialize_search(struct ncp_server *server, struct inode *dir,
+			struct nw_search_sequence *target)
 {
+	__u8  volnum = NCP_FINFO(dir)->volNumber;
+	__u32 dirent = NCP_FINFO(dir)->dirEntNum;
 	int result;
 
 	ncp_init_request(server);
 	ncp_add_byte(server, 2);	/* subfunction */
-	ncp_add_byte(server, server->name_space[dir->volNumber]);
+	ncp_add_byte(server, server->name_space[volnum]);
 	ncp_add_byte(server, 0);	/* reserved */
-	ncp_add_handle_path(server, dir->volNumber, dir->dirEntNum, 1, NULL);
+	ncp_add_handle_path(server, volnum, dirent, 1, NULL);
 
-	if ((result = ncp_request(server, 87)) != 0) {
-		ncp_unlock_server(server);
-		return result;
-	}
+	result = ncp_request(server, 87);
+	if (result)
+		goto out;
 	memcpy(target, ncp_reply_data(server, 0), sizeof(*target));
 
+out:
 	ncp_unlock_server(server);
-	return 0;
+	return result;
 }
 
 /* Search for everything */
@@ -457,42 +487,41 @@
 	ncp_add_byte(server, 0xff);	/* following is a wildcard */
 	ncp_add_byte(server, '*');
 
-	if ((result = ncp_request(server, 87)) != 0) {
-		ncp_unlock_server(server);
-		return result;
-	}
+	if ((result = ncp_request(server, 87)) != 0)
+		goto out;
 	memcpy(seq, ncp_reply_data(server, 0), sizeof(*seq));
 	ncp_extract_file_info(ncp_reply_data(server, 10), target);
 
+out:
 	ncp_unlock_server(server);
-	return 0;
+	return result;
 }
 
 int ncp_ren_or_mov_file_or_subdir(struct ncp_server *server,
-			  struct nw_info_struct *old_dir, char *old_name,
-			  struct nw_info_struct *new_dir, char *new_name)
+				struct inode *old_dir, char *old_name,
+				struct inode *new_dir, char *new_name)
 {
-	int result;
+	int result = -EINVAL;
 
-	if ((old_dir == NULL) || (old_name == NULL)
-	    || (new_dir == NULL) || (new_name == NULL))
-		return -EINVAL;
+	if ((old_dir == NULL) || (old_name == NULL) ||
+	    (new_dir == NULL) || (new_name == NULL))
+		goto out;
 
 	ncp_init_request(server);
 	ncp_add_byte(server, 4);	/* subfunction */
-	ncp_add_byte(server, server->name_space[old_dir->volNumber]);
+	ncp_add_byte(server, server->name_space[NCP_FINFO(old_dir)->volNumber]);
 	ncp_add_byte(server, 1);	/* rename flag */
 	ncp_add_word(server, ntohs(0x0680));	/* search attributes */
 
 	/* source Handle Path */
-	ncp_add_byte(server, old_dir->volNumber);
-	ncp_add_dword(server, old_dir->dirEntNum);
+	ncp_add_byte(server, NCP_FINFO(old_dir)->volNumber);
+	ncp_add_dword(server, NCP_FINFO(old_dir)->dirEntNum);
 	ncp_add_byte(server, 1);
 	ncp_add_byte(server, 1);	/* 1 source component */
 
 	/* dest Handle Path */
-	ncp_add_byte(server, new_dir->volNumber);
-	ncp_add_dword(server, new_dir->dirEntNum);
+	ncp_add_byte(server, NCP_FINFO(new_dir)->volNumber);
+	ncp_add_dword(server, NCP_FINFO(new_dir)->dirEntNum);
 	ncp_add_byte(server, 1);
 	ncp_add_byte(server, 1);	/* 1 destination component */
 
@@ -503,15 +532,17 @@
 
 	result = ncp_request(server, 87);
 	ncp_unlock_server(server);
+out:
 	return result;
 }
 
 
 /* We have to transfer to/from user space */
-int ncp_read(struct ncp_server *server, const char *file_id,
-	     __u32 offset, __u16 to_read,
-	     char *target, int *bytes_read)
+int
+ncp_read(struct ncp_server *server, const char *file_id,
+	     __u32 offset, __u16 to_read, char *target, int *bytes_read)
 {
+	char *source;
 	int result;
 
 	ncp_init_request(server);
@@ -521,20 +552,23 @@
 	ncp_add_word(server, htons(to_read));
 
 	if ((result = ncp_request(server, 72)) != 0) {
-		ncp_unlock_server(server);
-		return result;
+		goto out;
 	}
 	*bytes_read = ntohs(ncp_reply_word(server, 0));
+	source = ncp_reply_data(server, 2 + (offset & 1));
 
-	copy_to_user(target, ncp_reply_data(server, 2 + (offset & 1)), *bytes_read);
-
+	result = -EFAULT;
+	if (!copy_to_user(target, source, *bytes_read))
+		result = 0;
+out:
 	ncp_unlock_server(server);
-	return 0;
+	return result;
 }
 
-int ncp_write(struct ncp_server *server, const char *file_id,
+int
+ncp_write(struct ncp_server *server, const char *file_id,
 	      __u32 offset, __u16 to_write,
-	      const char *source, int *bytes_written)
+		const char *source, int *bytes_written)
 {
 	int result;
 
@@ -545,12 +579,11 @@
 	ncp_add_word(server, htons(to_write));
 	ncp_add_mem_fromfs(server, source, to_write);
 
-	if ((result = ncp_request(server, 73)) != 0) {
-		ncp_unlock_server(server);
-		return result;
-	}
+	if ((result = ncp_request(server, 73)) != 0)
+		goto out;
 	*bytes_written = to_write;
-
+	result = 0;
+out:
 	ncp_unlock_server(server);
-	return 0;
+	return result;
 }
diff -u --recursive --new-file v2.1.62/linux/fs/ncpfs/ncplib_kernel.h linux/fs/ncpfs/ncplib_kernel.h
--- v2.1.62/linux/fs/ncpfs/ncplib_kernel.h	Fri Apr  4 08:52:24 1997
+++ linux/fs/ncpfs/ncplib_kernel.h	Wed Nov 12 11:10:40 1997
@@ -3,6 +3,7 @@
  *
  *  Copyright (C) 1995, 1996 by Volker Lendecke
  *  Modified for big endian by J.F. Chadima and David S. Miller
+ *  Modified 1997 Peter Waltenberg, Bill Hawes, David Woodhouse for 2.1 dcache
  *
  */
 
@@ -10,9 +11,6 @@
 #define _NCPLIB_H
 
 #include <linux/fs.h>
-#include <linux/ncp.h>
-#include <linux/ncp_fs.h>
-#include <linux/ncp_fs_sb.h>
 #include <linux/types.h>
 #include <linux/errno.h>
 #include <linux/malloc.h>
@@ -24,71 +22,35 @@
 #include <asm/string.h>
 
 #include <linux/ncp.h>
+#include <linux/ncp_fs.h>
+#include <linux/ncp_fs_sb.h>
+
+int ncp_negotiate_buffersize(struct ncp_server *, int, int *);
+int ncp_get_volume_info_with_number(struct ncp_server *, int,
+				struct ncp_volume_info *);
+int ncp_close_file(struct ncp_server *, const char *);
+int ncp_read(struct ncp_server *, const char *, __u32, __u16, char *, int *);
+int ncp_write(struct ncp_server *, const char *, __u32, __u16,
+		const char *, int *);
 
-int
-ncp_negotiate_buffersize(struct ncp_server *server, int size,
-			 int *target);
-int
-ncp_get_volume_info_with_number(struct ncp_server *server, int n,
-				struct ncp_volume_info *target);
-
-int
-ncp_close_file(struct ncp_server *server, const char *file_id);
-
-int
-ncp_read(struct ncp_server *server, const char *file_id,
-	 __u32 offset, __u16 to_read,
-	 char *target, int *bytes_read);
-
-int
-ncp_write(struct ncp_server *server, const char *file_id,
-	  __u32 offset, __u16 to_write,
-	  const char *source, int *bytes_written);
-
-int
-ncp_obtain_info(struct ncp_server *server,
-		__u8 vol_num, __u32 dir_base,
-		char *path, /* At most 1 component */
+int ncp_obtain_info(struct ncp_server *server, struct inode *, char *,
 		struct nw_info_struct *target);
+int ncp_lookup_volume(struct ncp_server *, char *, struct nw_info_struct *);
+int ncp_modify_file_or_subdir_dos_info(struct ncp_server *, struct inode *,
+			 __u32, struct nw_modify_dos_info *info);
+
+int ncp_del_file_or_subdir(struct ncp_server *, struct inode *, char *);
+int ncp_open_create_file_or_subdir(struct ncp_server *, struct inode *, char *,
+			       int, __u32, int, struct nw_file_info *);
 
-int
-ncp_lookup_volume(struct ncp_server *server,
-		  char *volname,
-		  struct nw_info_struct *target);
-
-
-int
-ncp_modify_file_or_subdir_dos_info(struct ncp_server *server,
-				   struct nw_info_struct *file,
-				   __u32 info_mask,
-				   struct nw_modify_dos_info *info);
-
-int
-ncp_del_file_or_subdir(struct ncp_server *server,
-		       struct nw_info_struct *dir, char *name);
-
-int
-ncp_open_create_file_or_subdir(struct ncp_server *server,
-			       struct nw_info_struct *dir, char *name,
-			       int open_create_mode,
-			       __u32 create_attributes,
-			       int desired_acc_rights,
-			       struct nw_file_info *target);
-
-int
-ncp_initialize_search(struct ncp_server *server,
-		      struct nw_info_struct *dir,
+int ncp_initialize_search(struct ncp_server *, struct inode *,
 		      struct nw_search_sequence *target);
-
-int
-ncp_search_for_file_or_subdir(struct ncp_server *server,
+int ncp_search_for_file_or_subdir(struct ncp_server *server,
 			      struct nw_search_sequence *seq,
 			      struct nw_info_struct *target);
 
-int
-ncp_ren_or_mov_file_or_subdir(struct ncp_server *server,
-			      struct nw_info_struct *old_dir, char *old_name,
-			      struct nw_info_struct *new_dir, char *new_name);
+int ncp_ren_or_mov_file_or_subdir(struct ncp_server *server,
+			      struct inode *, char *, struct inode *, char *);
 
 
 #endif /* _NCPLIB_H */
diff -u --recursive --new-file v2.1.62/linux/fs/ncpfs/sock.c linux/fs/ncpfs/sock.c
--- v2.1.62/linux/fs/ncpfs/sock.c	Wed Sep 24 20:05:48 1997
+++ linux/fs/ncpfs/sock.c	Wed Nov 12 11:10:40 1997
@@ -4,11 +4,11 @@
  *  Copyright (C) 1992, 1993  Rick Sladkey
  *
  *  Modified 1995, 1996 by Volker Lendecke to be usable for ncp
+ *  Modified 1997 Peter Waltenberg, Bill Hawes, David Woodhouse for 2.1 dcache
  *
  */
 
 #include <linux/sched.h>
-#include <linux/ncp_fs.h>
 #include <linux/errno.h>
 #include <linux/socket.h>
 #include <linux/fcntl.h>
@@ -19,13 +19,13 @@
 #include <linux/mm.h>
 #include <linux/netdevice.h>
 #include <net/scm.h>
+#include <net/sock.h>
 #include <linux/ipx.h>
+#include <linux/poll.h>
 
 #include <linux/ncp.h>
 #include <linux/ncp_fs.h>
 #include <linux/ncp_fs_sb.h>
-#include <net/sock.h>
-#include <linux/poll.h>
 
 static int _recv(struct socket *sock, unsigned char *ubuf, int size,
 		 unsigned flags)
@@ -82,7 +82,7 @@
 	struct file *file;
 	struct inode *inode;
 	struct socket *sock;
-	unsigned long fs;
+	mm_segment_t fs;
 	int result;
 	char *start = server->packet;
 	poll_table wait_table;
@@ -104,10 +104,12 @@
 	file = server->ncp_filp;
 	inode = file->f_dentry->d_inode;
 	sock = &inode->u.socket_i;
+	/* N.B. this isn't needed ... check socket type? */
 	if (!sock) {
-		printk("ncp_rpc_call: socki_lookup failed\n");
+		printk(KERN_ERR "ncp_rpc_call: socki_lookup failed\n");
 		return -EBADF;
 	}
+
 	init_timeout = server->m.time_out;
 	max_timeout = NCP_MAX_RPC_TIMEOUT;
 	retrans = server->m.retry_count;
@@ -127,7 +129,8 @@
 	fs = get_fs();
 	set_fs(get_ds());
 	for (n = 0, timeout = init_timeout;; n++, timeout <<= 1) {
-		DDPRINTK("ncpfs: %08lX:%02X%02X%02X%02X%02X%02X:%04X\n",
+		/*
+		DDPRINTK(KERN_DEBUG "ncpfs: %08lX:%02X%02X%02X%02X%02X%02X:%04X\n",
 			 htonl(server->m.serv_addr.sipx_network),
 			 server->m.serv_addr.sipx_node[0],
 			 server->m.serv_addr.sipx_node[1],
@@ -136,17 +139,18 @@
 			 server->m.serv_addr.sipx_node[4],
 			 server->m.serv_addr.sipx_node[5],
 			 ntohs(server->m.serv_addr.sipx_port));
-		DDPRINTK("ncpfs: req.typ: %04X, con: %d, "
+		*/
+		DDPRINTK(KERN_DEBUG "ncpfs: req.typ: %04X, con: %d, "
 			 "seq: %d",
 			 request.type,
 			 (request.conn_high << 8) + request.conn_low,
 			 request.sequence);
-		DDPRINTK(" func: %d\n",
+		DDPRINTK(KERN_DEBUG " func: %d\n",
 			 request.function);
 
 		result = _send(sock, (void *) start, size);
 		if (result < 0) {
-			printk("ncp_rpc_call: send error = %d\n", result);
+			printk(KERN_ERR "ncp_rpc_call: send error = %d\n", result);
 			break;
 		}
 	      re_select:
@@ -159,7 +163,7 @@
 				 * This is useful to see if the system is
 				 * hanging */
 				if (acknowledge_seen == 0) {
-					printk("NCP max timeout\n");
+					printk(KERN_WARNING "NCP max timeout\n");
 				}
 				timeout = max_timeout;
 			}
@@ -176,7 +180,7 @@
 				if (n < retrans)
 					continue;
 				if (server->m.flags & NCP_MOUNT_SOFT) {
-					printk("NCP server not responding\n");
+					printk(KERN_WARNING "NCP server not responding\n");
 					result = -EIO;
 					break;
 				}
@@ -184,7 +188,7 @@
 				timeout = init_timeout;
 				init_timeout <<= 1;
 				if (!major_timeout_seen) {
-					printk("NCP server not responding\n");
+					printk(KERN_WARNING "NCP server not responding\n");
 				}
 				major_timeout_seen = 1;
 				continue;
@@ -201,15 +205,15 @@
 			       MSG_PEEK | MSG_DONTWAIT);
 		if (result < 0) {
 			if (result == -EAGAIN) {
-				DPRINTK("ncp_rpc_call: bad select ready\n");
+				DDPRINTK(KERN_DEBUG "ncp_rpc_call: bad select ready\n");
 				goto re_select;
 			}
 			if (result == -ECONNREFUSED) {
-				DPRINTK("ncp_rpc_call: server playing coy\n");
+				DPRINTK(KERN_WARNING "ncp_rpc_call: server playing coy\n");
 				goto re_select;
 			}
 			if (result != -ERESTARTSYS) {
-				printk("ncp_rpc_call: recv error = %d\n",
+				printk(KERN_ERR "ncp_rpc_call: recv error = %d\n",
 				       -result);
 			}
 			break;
@@ -217,7 +221,7 @@
 		if ((result == sizeof(reply))
 		    && (reply.type == NCP_POSITIVE_ACK)) {
 			/* Throw away the packet */
-			DPRINTK("ncp_rpc_call: got positive acknowledge\n");
+			DPRINTK(KERN_DEBUG "ncp_rpc_call: got positive acknowledge\n");
 			_recv(sock, (void *) &reply, sizeof(reply),
 			      MSG_DONTWAIT);
 			n = 0;
@@ -225,7 +229,7 @@
 			acknowledge_seen = 1;
 			goto re_select;
 		}
-		DDPRINTK("ncpfs: rep.typ: %04X, con: %d, tsk: %d,"
+		DDPRINTK(KERN_DEBUG "ncpfs: rep.typ: %04X, con: %d, tsk: %d,"
 			 "seq: %d\n",
 			 reply.type,
 			 (reply.conn_high << 8) + reply.conn_low,
@@ -240,7 +244,7 @@
 /* seem to get wrong task from NW311 && (reply.task      == request.task) */
 			    && (reply.conn_high == request.conn_high)))) {
 			if (major_timeout_seen)
-				printk("NCP server OK\n");
+				printk(KERN_NOTICE "NCP server OK\n");
 			break;
 		}
 		/* JEJB/JSP 2/7/94
@@ -249,7 +253,7 @@
 		 * a null buffer yet. */
 		_recv(sock, (void *) &reply, sizeof(reply), MSG_DONTWAIT);
 
-		DPRINTK("ncp_rpc_call: reply mismatch\n");
+		DPRINTK(KERN_WARNING "ncp_rpc_call: reply mismatch\n");
 		goto re_select;
 	}
 	/* 
@@ -258,11 +262,11 @@
 	 */
 	result = _recv(sock, (void *) start, server->packet_size, MSG_DONTWAIT);
 	if (result < 0) {
-		printk("NCP: notice message: result=%d\n", result);
+		printk(KERN_WARNING "NCP: notice message: result=%d\n", result);
 	} else if (result < sizeof(struct ncp_reply_header)) {
-		printk("NCP: just caught a too small read memory size..., "
+		printk(KERN_ERR "NCP: just caught a too small read memory size..., "
 		       "email to NET channel\n");
-		printk("NCP: result=%d\n", result);
+		printk(KERN_ERR "NCP: result=%d\n", result);
 		result = -EIO;
 	}
 	current->blocked = old_mask;
@@ -279,7 +283,7 @@
 	int result;
 
 	if (server->lock == 0) {
-		printk("ncpfs: Server not locked!\n");
+		printk(KERN_ERR "ncpfs: Server not locked!\n");
 		return -EIO;
 	}
 	if (!ncp_conn_valid(server)) {
@@ -287,7 +291,7 @@
 	}
 	result = do_ncp_rpc_call(server, size);
 
-	DDPRINTK("do_ncp_rpc_call returned %d\n", result);
+	DDPRINTK(KERN_DEBUG "do_ncp_rpc_call returned %d\n", result);
 
 	if (result < 0) {
 		/* There was a problem with I/O, so the connections is
@@ -299,19 +303,17 @@
 
 /* ncp_do_request assures that at least a complete reply header is
  * received. It assumes that server->current_size contains the ncp
- * request size */
+ * request size
+ */
 int ncp_request(struct ncp_server *server, int function)
 {
-	struct ncp_request_header *h
-	= (struct ncp_request_header *) (server->packet);
-	struct ncp_reply_header *reply
-	= (struct ncp_reply_header *) (server->packet);
-
+	struct ncp_request_header *h;
+	struct ncp_reply_header *reply;
 	int request_size = server->current_size
-	- sizeof(struct ncp_request_header);
-
+			 - sizeof(struct ncp_request_header);
 	int result;
 
+	h = (struct ncp_request_header *) (server->packet);
 	if (server->has_subfunction != 0) {
 		*(__u16 *) & (h->data[0]) = htons(request_size - 2);
 	}
@@ -321,13 +323,19 @@
 	h->sequence = server->sequence;
 	h->conn_low = (server->connection) & 0xff;
 	h->conn_high = ((server->connection) & 0xff00) >> 8;
-	h->task = (current->pid) & 0xff;
+	/*
+	 * The server shouldn't know or care what task is making a
+	 * request, so we always use the same task number.
+	 */
+	h->task = 2; /* (current->pid) & 0xff; */
 	h->function = function;
 
-	if ((result = ncp_do_request(server, request_size + sizeof(*h))) < 0) {
-		DPRINTK("ncp_request_error: %d\n", result);
-		return result;
+	result = ncp_do_request(server, request_size + sizeof(*h));
+	if (result < 0) {
+		DPRINTK(KERN_WARNING "ncp_request_error: %d\n", result);
+		goto out;
 	}
+	reply = (struct ncp_reply_header *) (server->packet);
 	server->completion = reply->completion_code;
 	server->conn_status = reply->connection_state;
 	server->reply_size = result;
@@ -335,48 +343,52 @@
 
 	result = reply->completion_code;
 
-	if (result != 0) {
-		DPRINTK("ncp_completion_code: %x\n", result);
-	}
+#ifdef NCPFS_PARANOIA
+if (result != 0)
+printk(KERN_DEBUG "ncp_request: completion code=%x\n", result);
+#endif
+out:
 	return result;
 }
 
 int ncp_connect(struct ncp_server *server)
 {
-	struct ncp_request_header *h
-	= (struct ncp_request_header *) (server->packet);
+	struct ncp_request_header *h;
 	int result;
 
+	h = (struct ncp_request_header *) (server->packet);
 	h->type = NCP_ALLOC_SLOT_REQUEST;
 
 	server->sequence = 0;
-	h->sequence = server->sequence;
-	h->conn_low = 0xff;
-	h->conn_high = 0xff;
-	h->task = (current->pid) & 0xff;
-	h->function = 0;
-
-	if ((result = ncp_do_request(server, sizeof(*h))) < 0) {
-		return result;
-	}
+	h->sequence	= server->sequence;
+	h->conn_low	= 0xff;
+	h->conn_high	= 0xff;
+	h->task		= 2; /* see above */
+	h->function	= 0;
+
+	result = ncp_do_request(server, sizeof(*h));
+	if (result < 0)
+		goto out;
 	server->sequence = 0;
 	server->connection = h->conn_low + (h->conn_high * 256);
-	return 0;
+	result = 0;
+out:
+	return result;
 }
 
 int ncp_disconnect(struct ncp_server *server)
 {
-	struct ncp_request_header *h
-	= (struct ncp_request_header *) (server->packet);
+	struct ncp_request_header *h;
 
+	h = (struct ncp_request_header *) (server->packet);
 	h->type = NCP_DEALLOC_SLOT_REQUEST;
 
 	server->sequence += 1;
-	h->sequence = server->sequence;
-	h->conn_low = (server->connection) & 0xff;
-	h->conn_high = ((server->connection) & 0xff00) >> 8;
-	h->task = (current->pid) & 0xff;
-	h->function = 0;
+	h->sequence	= server->sequence;
+	h->conn_low	= (server->connection) & 0xff;
+	h->conn_high	= ((server->connection) & 0xff00) >> 8;
+	h->task		= 2; /* see above */
+	h->function	= 0;
 
 	return ncp_do_request(server, sizeof(*h));
 }
@@ -386,7 +398,7 @@
 #if 0
 	/* For testing, only 1 process */
 	if (server->lock != 0) {
-		DPRINTK("ncpfs: server locked!!!\n");
+		DPRINTK(KERN_WARNING "ncpfs: server locked!!!\n");
 	}
 #endif
 	while (server->lock)
@@ -397,7 +409,7 @@
 void ncp_unlock_server(struct ncp_server *server)
 {
 	if (server->lock != 1) {
-		printk("ncp_unlock_server: was not locked!\n");
+		printk(KERN_WARNING "ncp_unlock_server: was not locked!\n");
 	}
 	server->lock = 0;
 	wake_up(&server->wait);
diff -u --recursive --new-file v2.1.62/linux/fs/nfs/dir.c linux/fs/nfs/dir.c
--- v2.1.62/linux/fs/nfs/dir.c	Mon Nov  3 13:04:27 1997
+++ linux/fs/nfs/dir.c	Sat Nov  8 11:33:05 1997
@@ -31,9 +31,13 @@
 
 #define NFS_MAX_AGE 10*HZ	/* max age for dentry validation */
 
+#ifndef shrink_dcache_parent
+#define shrink_dcache_parent(dentry) shrink_dcache_sb((dentry)->d_sb)
+#endif
+
 /* needed by smbfs as well ... move to dcache? */
 extern void nfs_renew_times(struct dentry *);
-extern void nfs_invalidate_dircache_sb(struct super_block *);
+
 #define NFS_PARANOIA 1
 
 /*
@@ -626,7 +630,7 @@
 	error = -EBUSY;
 	if (dentry->d_count > 1) {
 		/* Attempt to shrink child dentries ... */
-		shrink_dcache_sb(dentry->d_sb); /* Arghhh */
+		shrink_dcache_parent(dentry);
 		if (dentry->d_count > 1)
 			goto out;
 	}
@@ -811,6 +815,14 @@
 	}
 	error = nfs_proc_remove(NFS_SERVER(dir),
 					NFS_FH(dir), dentry->d_name.name);
+#ifdef NFS_PARANOIA
+if (dentry->d_count > 1)
+printk("nfs_safe_remove: %s/%s busy after delete?? d_count=%d\n",
+dentry->d_parent->d_name.name, dentry->d_name.name, dentry->d_count);
+if (inode && inode->i_count > 1)
+printk("nfs_safe_remove: %s/%s inode busy?? i_count=%d\n",
+dentry->d_parent->d_name.name, dentry->d_name.name, inode->i_count);
+#endif
 	if (!error) {
 		nfs_invalidate_dircache(dir);
 		if (inode && inode->i_nlink)
@@ -1005,7 +1017,7 @@
 	 * Moving a directory ... prune child dentries if needed.
 	 */
 	else if (old_dentry->d_count > 1)
-		shrink_dcache_sb(old_dentry->d_sb); /* Arghhh */
+		shrink_dcache_parent(old_dentry);
 
 	/*
 	 * Now check the use counts ... we can't safely do the
diff -u --recursive --new-file v2.1.62/linux/include/asm-i386/bugs.h linux/include/asm-i386/bugs.h
--- v2.1.62/linux/include/asm-i386/bugs.h	Thu Sep 11 09:02:24 1997
+++ linux/include/asm-i386/bugs.h	Wed Nov 12 11:09:55 1997
@@ -166,6 +166,32 @@
 	}
 }
 
+/*
+ * All current models of Pentium and Pentium with MMX technology CPUs
+ * have the F0 0F bug, which lets nonpriviledged users lock up the system:
+ */
+
+extern int pentium_f00f_bug;
+
+__initfunc(static void check_pentium_f00f(void))
+{
+	/*
+	 * Pentium and Pentium MMX
+	 */
+	printk("checking for F00F bug ...");
+	if(x86==5 && !memcmp(x86_vendor_id, "GenuineIntel", 12))
+	{
+		extern void trap_init_f00f_bug(void);
+
+		printk(KERN_INFO "\nIntel Pentium/[MMX] F0 0F bug detected - turning on workaround.\n");
+		pentium_f00f_bug = 1;
+		trap_init_f00f_bug();
+	} else {
+		printk(KERN_INFO " no F0 0F bug in this CPU, great!\n");
+		pentium_f00f_bug = 0;
+	}
+}
+
 __initfunc(static void check_bugs(void))
 {
 	check_tlb();
@@ -173,5 +199,6 @@
 	check_hlt();
 	check_popad();
 	check_amd_k6();
+	check_pentium_f00f();
 	system_utsname.machine[1] = '0' + x86;
 }
diff -u --recursive --new-file v2.1.62/linux/include/asm-i386/io.h linux/include/asm-i386/io.h
--- v2.1.62/linux/include/asm-i386/io.h	Mon Jul  7 16:02:04 1997
+++ linux/include/asm-i386/io.h	Mon Nov 10 10:45:53 1997
@@ -25,14 +25,18 @@
  *		Linus
  */
 
+ /*
+  *  Bit simplified and optimized by Jan Hubicka
+  */
+
 #ifdef SLOW_IO_BY_JUMPING
-#define __SLOW_DOWN_IO __asm__ __volatile__("jmp 1f\n1:\tjmp 1f\n1:")
+#define __SLOW_DOWN_IO "\njmp 1f\n1:\tjmp 1f\n1:"
 #else
-#define __SLOW_DOWN_IO __asm__ __volatile__("outb %al,$0x80")
+#define __SLOW_DOWN_IO "\noutb %%al,$0x80"
 #endif
 
 #ifdef REALLY_SLOW_IO
-#define SLOW_DOWN_IO { __SLOW_DOWN_IO; __SLOW_DOWN_IO; __SLOW_DOWN_IO; __SLOW_DOWN_IO; }
+#define SLOW_DOWN_IO __SLOW_DOWN_IO __SLOW_DOWN_IO __SLOW_DOWN_IO __SLOW_DOWN_IO
 #else
 #define SLOW_DOWN_IO __SLOW_DOWN_IO
 #endif
@@ -40,30 +44,25 @@
 /*
  * Talk about misusing macros..
  */
-
 #define __OUT1(s,x) \
-extern inline void __out##s(unsigned x value, unsigned short port) {
+extern inline void out##s(unsigned x value, unsigned short port) {
 
 #define __OUT2(s,s1,s2) \
 __asm__ __volatile__ ("out" #s " %" s1 "0,%" s2 "1"
 
 #define __OUT(s,s1,x) \
-__OUT1(s,x) __OUT2(s,s1,"w") : : "a" (value), "d" (port)); } \
-__OUT1(s##c,x) __OUT2(s,s1,"") : : "a" (value), "id" (port)); } \
-__OUT1(s##_p,x) __OUT2(s,s1,"w") : : "a" (value), "d" (port)); SLOW_DOWN_IO; } \
-__OUT1(s##c_p,x) __OUT2(s,s1,"") : : "a" (value), "id" (port)); SLOW_DOWN_IO; }
+__OUT1(s,x) __OUT2(s,s1,"w") : : "a" (value), "Nd" (port)); } \
+__OUT1(s##_p,x) __OUT2(s,s1,"w") SLOW_DOWN_IO : : "a" (value), "Nd" (port));} \
 
 #define __IN1(s) \
-extern inline RETURN_TYPE __in##s(unsigned short port) { RETURN_TYPE _v;
+extern inline RETURN_TYPE in##s(unsigned short port) { RETURN_TYPE _v;
 
 #define __IN2(s,s1,s2) \
 __asm__ __volatile__ ("in" #s " %" s2 "1,%" s1 "0"
 
 #define __IN(s,s1,i...) \
-__IN1(s) __IN2(s,s1,"w") : "=a" (_v) : "d" (port) ,##i ); return _v; } \
-__IN1(s##c) __IN2(s,s1,"") : "=a" (_v) : "id" (port) ,##i ); return _v; } \
-__IN1(s##_p) __IN2(s,s1,"w") : "=a" (_v) : "d" (port) ,##i ); SLOW_DOWN_IO; return _v; } \
-__IN1(s##c_p) __IN2(s,s1,"") : "=a" (_v) : "id" (port) ,##i ); SLOW_DOWN_IO; return _v; }
+__IN1(s) __IN2(s,s1,"w") : "=a" (_v) : "Nd" (port) ,##i ); return _v; } \
+__IN1(s##_p) __IN2(s,s1,"w") SLOW_DOWN_IO : "=a" (_v) : "Nd" (port) ,##i ); return _v; } \
 
 #define __INS(s) \
 extern inline void ins##s(unsigned short port, void * addr, unsigned long count) \
@@ -76,11 +75,9 @@
 : "=S" (addr), "=c" (count) : "d" (port),"0" (addr),"1" (count)); }
 
 #define RETURN_TYPE unsigned char
-/* __IN(b,"b","0" (0)) */
 __IN(b,"")
 #undef RETURN_TYPE
 #define RETURN_TYPE unsigned short
-/* __IN(w,"w","0" (0)) */
 __IN(w,"")
 #undef RETURN_TYPE
 #define RETURN_TYPE unsigned int
@@ -98,71 +95,6 @@
 __OUTS(b)
 __OUTS(w)
 __OUTS(l)
-
-/*
- * Note that due to the way __builtin_constant_p() works, you
- *  - can't use it inside an inline function (it will never be true)
- *  - you don't have to worry about side effects within the __builtin..
- */
-#define outb(val,port) \
-((__builtin_constant_p((port)) && (port) < 256) ? \
-	__outbc((val),(port)) : \
-	__outb((val),(port)))
-
-#define inb(port) \
-((__builtin_constant_p((port)) && (port) < 256) ? \
-	__inbc(port) : \
-	__inb(port))
-
-#define outb_p(val,port) \
-((__builtin_constant_p((port)) && (port) < 256) ? \
-	__outbc_p((val),(port)) : \
-	__outb_p((val),(port)))
-
-#define inb_p(port) \
-((__builtin_constant_p((port)) && (port) < 256) ? \
-	__inbc_p(port) : \
-	__inb_p(port))
-
-#define outw(val,port) \
-((__builtin_constant_p((port)) && (port) < 256) ? \
-	__outwc((val),(port)) : \
-	__outw((val),(port)))
-
-#define inw(port) \
-((__builtin_constant_p((port)) && (port) < 256) ? \
-	__inwc(port) : \
-	__inw(port))
-
-#define outw_p(val,port) \
-((__builtin_constant_p((port)) && (port) < 256) ? \
-	__outwc_p((val),(port)) : \
-	__outw_p((val),(port)))
-
-#define inw_p(port) \
-((__builtin_constant_p((port)) && (port) < 256) ? \
-	__inwc_p(port) : \
-	__inw_p(port))
-
-#define outl(val,port) \
-((__builtin_constant_p((port)) && (port) < 256) ? \
-	__outlc((val),(port)) : \
-	__outl((val),(port)))
-
-#define inl(port) \
-((__builtin_constant_p((port)) && (port) < 256) ? \
-	__inlc(port) : \
-	__inl(port))
-
-#define outl_p(val,port) \
-((__builtin_constant_p((port)) && (port) < 256) ? \
-	__outlc_p((val),(port)) : \
-	__outl_p((val),(port)))
-
-#define inl_p(port) \
-((__builtin_constant_p((port)) && (port) < 256) ? \
-	__inlc_p(port) : \
-	__inl_p(port))
 
 #ifdef __KERNEL__
 
diff -u --recursive --new-file v2.1.62/linux/include/linux/awe_voice.h linux/include/linux/awe_voice.h
--- v2.1.62/linux/include/linux/awe_voice.h	Tue Mar  4 10:25:26 1997
+++ linux/include/linux/awe_voice.h	Tue Oct 28 02:10:51 1997
@@ -3,7 +3,7 @@
  *
  * Voice information definitions for the low level driver for the 
  * AWE32/Sound Blaster 32 wave table synth.
- *   version 0.3.1b; Jan. 21, 1997
+ *   version 0.4.2; Sep. 1, 1997
  *
  * Copyright (C) 1996,1997 Takashi Iwai
  *
@@ -44,14 +44,19 @@
 
 	short device_no;		/* synthesizer number */
 	unsigned short sf_id;		/* file id (should be zero) */
-	short sf_version;		/* patch version (not referred) */
-	long len;			/* data length (without this header) */
+	short optarg;			/* optional argument */
+	int len;			/* data length (without this header) */
 
-	short type;			/* following data type */
-#define AWE_LOAD_INFO		0
-#define AWE_LOAD_DATA		1
-#define AWE_APPEND_DATA		0x00
-#define AWE_REPLACE_DATA	0x80
+	short type;			/* patch operation type */
+#define AWE_LOAD_INFO		0	/* awe_voice_rec */
+#define AWE_LOAD_DATA		1	/* awe_sample_info */
+#define AWE_OPEN_PATCH		2	/* awe_open_parm */
+#define AWE_CLOSE_PATCH		3	/* none */
+#define AWE_UNLOAD_PATCH	4	/* none */
+#define AWE_REPLACE_DATA	5	/* awe_sample_info (optarg=#channels)*/
+#define AWE_MAP_PRESET		6	/* awe_voice_map */
+#define AWE_LOAD_CHORUS_FX	0x10	/* awe_chorus_fx_rec (optarg=mode) */
+#define AWE_LOAD_REVERB_FX	0x11	/* awe_reverb_fx_rec (optarg=mode) */
 
 	short reserved;			/* word alignment data */
 
@@ -61,7 +66,35 @@
 #endif
 } awe_patch_info;
 
-#define AWE_PATCH_INFO_SIZE	16
+/*#define AWE_PATCH_INFO_SIZE	16*/
+#define AWE_PATCH_INFO_SIZE	sizeof(awe_patch_info)
+
+
+/*----------------------------------------------------------------
+ * open patch
+ *----------------------------------------------------------------*/
+
+#define AWE_PATCH_NAME_LEN	32
+
+typedef struct _awe_open_parm {
+	unsigned short type;		/* sample type */
+#define AWE_PAT_TYPE_MISC	0
+#define AWE_PAT_TYPE_GM		1
+#define AWE_PAT_TYPE_GS		2
+#define AWE_PAT_TYPE_MT32	3
+#define AWE_PAT_TYPE_XG		4
+#define AWE_PAT_TYPE_SFX	5
+#define AWE_PAT_TYPE_GUS	6
+#define AWE_PAT_TYPE_MAP	7
+
+#define AWE_PAT_LOCKED		0x100	/* lock the samples */
+
+	short reserved;
+	char name[AWE_PATCH_NAME_LEN];
+} awe_open_parm;
+
+/*#define AWE_OPEN_PARM_SIZE	28*/
+#define AWE_OPEN_PARM_SIZE	sizeof(awe_open_parm)
 
 
 /*----------------------------------------------------------------
@@ -100,8 +133,8 @@
 typedef struct _awe_voice_info {
 	unsigned short sf_id;		/* file id (should be zero) */
 	unsigned short sample;		/* sample id */
-	long start, end;		/* sample offset correction */
-	long loopstart, loopend;	/* loop offset correction */
+	int start, end;			/* sample offset correction */
+	int loopstart, loopend;		/* loop offset correction */
 	short rate_offset;		/* sample rate pitch offset */
 	unsigned short mode;		/* sample mode */
 #define AWE_MODE_ROMSOUND		0x8000
@@ -124,7 +157,8 @@
 	short index;			/* internal index (set by driver) */
 } awe_voice_info;
 
-#define AWE_VOICE_INFO_SIZE	92
+/*#define AWE_VOICE_INFO_SIZE	92*/
+#define AWE_VOICE_INFO_SIZE	sizeof(awe_voice_info)
 
 /*----------------------------------------------------------------*/
 
@@ -134,24 +168,44 @@
  * from older versions.
  * Use AWE_VOICE_REC_SIZE instead.
  */
+
+/* instrument info header: 4 bytes */
+typedef struct _awe_voice_rec_hdr {
+	unsigned char bank;		/* midi bank number */
+	unsigned char instr;		/* midi preset number */
+	char nvoices;			/* number of voices */
+	char write_mode;		/* write mode; normally 0 */
+#define AWE_WR_APPEND		0	/* append anyway */
+#define AWE_WR_EXCLUSIVE	1	/* skip if already exists */
+#define AWE_WR_REPLACE		2	/* replace if already exists */
+} awe_voice_rec_hdr;
+
+/*#define AWE_VOICE_REC_SIZE	4*/
+#define AWE_VOICE_REC_SIZE	sizeof(awe_voice_rec_hdr)
+
+/* the standard patch structure for one sample */
+typedef struct _awe_voice_rec_patch {
+	awe_patch_info		patch;
+	awe_voice_rec_hdr	hdr;
+	awe_voice_info		info;
+} awe_voice_rec_patch;
+
+
+/* obsolete data type */
 #if defined(AWE_COMPAT_030) && AWE_COMPAT_030
 #define AWE_INFOARRAY_SIZE	0
 #else
 #define AWE_INFOARRAY_SIZE	1
 #endif
 
-/* instrument info header: 4 bytes */
 typedef struct _awe_voice_rec {
 	unsigned char bank;		/* midi bank number */
 	unsigned char instr;		/* midi preset number */
 	short nvoices;			/* number of voices */
-
 	/* voice information follows here */
 	awe_voice_info info[AWE_INFOARRAY_SIZE];
 } awe_voice_rec;
 
-#define AWE_VOICE_REC_SIZE	4
-
 
 /*----------------------------------------------------------------
  * sample wave information
@@ -161,9 +215,9 @@
 typedef struct awe_sample_info {
 	unsigned short sf_id;		/* file id (should be zero) */
 	unsigned short sample;		/* sample id */
-	long start, end;		/* start & end offset */
-	long loopstart, loopend;	/* loop start & end offset */
-	long size;			/* size (0 = ROM) */
+	int start, end;			/* start & end offset */
+	int loopstart, loopend;		/* loop start & end offset */
+	int size;			/* size (0 = ROM) */
 	short checksum_flag;		/* use check sum = 1 */
 	unsigned short mode_flags;	/* mode flags */
 #define AWE_SAMPLE_8BITS	1	/* wave data is 8bits */
@@ -174,36 +228,31 @@
 #define AWE_SAMPLE_STEREO_LEFT	32	/* stereo left sound */
 #define AWE_SAMPLE_STEREO_RIGHT	64	/* stereo right sound */
 #define AWE_SAMPLE_REVERSE_LOOP 128	/* reverse looping */
-	unsigned long checksum;		/* check sum */
+	unsigned int checksum;		/* check sum */
 #if defined(AWE_COMPAT_030) && AWE_COMPAT_030
 	unsigned short data[0];		/* sample data follows here */
 #endif
 } awe_sample_info;
 
-#define AWE_SAMPLE_INFO_SIZE	32
+/*#define AWE_SAMPLE_INFO_SIZE	32*/
+#define AWE_SAMPLE_INFO_SIZE	sizeof(awe_sample_info)
 
 
 /*----------------------------------------------------------------
- * awe hardware controls
+ * voice preset mapping
  *----------------------------------------------------------------*/
 
-typedef struct _awe_mode_rec {
-	int base_addr;
-	long mem_size; /* word size */
-	int max_voices, max_infos, max_samples;
-	unsigned short current_sf_id;
-	long free_mem; /* word offset */
-	int free_info;
-	int free_sample;
-	short reverb_mode;
-	short chorus_mode;
-	unsigned short init_atten;
-	short channel_mode;
-	short gus_bank;
-	short exclusive_sound;
-	unsigned long drum_flags;
-	int debug_mode;
-} awe_mode_rec;
+typedef struct awe_voice_map {
+	int map_bank, map_instr, map_key;	/* key = -1 means all keys */
+	int src_bank, src_instr, src_key;
+} awe_voice_map;
+
+#define AWE_VOICE_MAP_SIZE	sizeof(awe_voice_map)
+
+
+/*----------------------------------------------------------------
+ * awe hardware controls
+ *----------------------------------------------------------------*/
 
 #define _AWE_DEBUG_MODE			0x00
 #define _AWE_REVERB_MODE		0x01
@@ -214,56 +263,116 @@
 #define _AWE_TERMINATE_CHANNEL		0x06
 #define _AWE_TERMINATE_ALL		0x07
 #define _AWE_INITIAL_VOLUME		0x08
-#define _AWE_SET_GUS_BANK		0x09
-#define _AWE_CHANNEL_MODE		0x0a	/* v0.3 features */
-#define _AWE_DRUM_CHANNELS		0x0b	/* v0.3 features */
-#define _AWE_EXCLUSIVE_SOUND		0x0c	/* v0.3 features */
 #define _AWE_INITIAL_ATTEN	_AWE_INITIAL_VOLUME
+#define _AWE_RESET_CHANNEL		0x09
+#define _AWE_CHANNEL_MODE		0x0a
+#define _AWE_DRUM_CHANNELS		0x0b
+#define _AWE_MISC_MODE			0x0c
+#define _AWE_RELEASE_ALL		0x0d
 #define _AWE_NOTEOFF_ALL		0x0e
-#define _AWE_GET_CURRENT_MODE		0x10	/* v0.3 features */
+#define _AWE_CHN_PRESSURE		0x0f
+/*#define _AWE_GET_CURRENT_MODE		0x10*/
+#define _AWE_EQUALIZER			0x11
+/*#define _AWE_GET_MISC_MODE		0x12*/
+/*#define _AWE_GET_FONTINFO		0x13*/
 
 #define _AWE_MODE_FLAG			0x80
 #define _AWE_COOKED_FLAG		0x40	/* not supported */
 #define _AWE_MODE_VALUE_MASK		0x3F
 
-#define _AWE_CMD(chn, voice, cmd, p1, p2) \
-{_SEQ_NEEDBUF(8); _seqbuf[_seqbufptr] = SEQ_PRIVATE;\
- _seqbuf[_seqbufptr+1] = chn;\
- _seqbuf[_seqbufptr+2] = _AWE_MODE_FLAG|(cmd);\
- _seqbuf[_seqbufptr+3] = voice;\
- *(unsigned short*)&_seqbuf[_seqbufptr+4] = p1;\
- *(unsigned short*)&_seqbuf[_seqbufptr+6] = p2;\
+/*----------------------------------------------------------------*/
+
+#define _AWE_SET_CMD(p,dev,voice,cmd,p1,p2) \
+{((char*)(p))[0] = SEQ_PRIVATE;\
+ ((char*)(p))[1] = dev;\
+ ((char*)(p))[2] = _AWE_MODE_FLAG|(cmd);\
+ ((char*)(p))[3] = voice;\
+ ((unsigned short*)(p))[2] = p1;\
+ ((unsigned short*)(p))[3] = p2;}
+
+/* buffered access */
+#define _AWE_CMD(dev, voice, cmd, p1, p2) \
+{_SEQ_NEEDBUF(8);\
+ _AWE_SET_CMD(_seqbuf + _seqbufptr, dev, voice, cmd, p1, p2);\
  _SEQ_ADVBUF(8);}
 
+/* direct access */
+#define _AWE_CMD_NOW(seqfd,dev,voice,cmd,p1,p2) \
+{struct seq_event_rec tmp;\
+ _AWE_SET_CMD(&tmp, dev, voice, cmd, p1, p2);\
+ ioctl(seqfd, SNDCTL_SEQ_OUTOFBAND, &tmp);}
+
+/*----------------------------------------------------------------*/
+
+/* set debugging mode */
 #define AWE_DEBUG_MODE(dev,p1)	_AWE_CMD(dev, 0, _AWE_DEBUG_MODE, p1, 0)
+/* set reverb mode; from 0 to 7 */
 #define AWE_REVERB_MODE(dev,p1)	_AWE_CMD(dev, 0, _AWE_REVERB_MODE, p1, 0)
+/* set chorus mode; from 0 to 7 */
 #define AWE_CHORUS_MODE(dev,p1)	_AWE_CMD(dev, 0, _AWE_CHORUS_MODE, p1, 0)
-#define AWE_REMOVE_LAST_SAMPLES(dev) _AWE_CMD(dev, 0, _AWE_REMOVE_LAST_SAMPLES, 0, 0)
-#define AWE_INITIALIZE_CHIP(dev) _AWE_CMD(dev, 0, _AWE_INITIALIZE_CHIP, 0, 0)
+
+/* reset channel */
+#define AWE_RESET_CHANNEL(dev,ch) _AWE_CMD(dev, ch, _AWE_RESET_CHANNEL, 0, 0)
+#define AWE_RESET_CONTROL(dev,ch) _AWE_CMD(dev, ch, _AWE_RESET_CHANNEL, 1, 0)
+
+/* send an effect to all layers */
 #define AWE_SEND_EFFECT(dev,voice,type,value) _AWE_CMD(dev,voice,_AWE_SEND_EFFECT,type,value)
+#define AWE_ADD_EFFECT(dev,voice,type,value) _AWE_CMD(dev,voice,_AWE_SEND_EFFECT,((type)|0x80),value)
+#define AWE_UNSET_EFFECT(dev,voice,type) _AWE_CMD(dev,voice,_AWE_SEND_EFFECT,((type)|0x40),0)
+/* send an effect to a layer */
+#define AWE_SEND_LAYER_EFFECT(dev,voice,layer,type,value) _AWE_CMD(dev,voice,_AWE_SEND_EFFECT,((layer+1)<<8|(type)),value)
+#define AWE_ADD_LAYER_EFFECT(dev,voice,layer,type,value) _AWE_CMD(dev,voice,_AWE_SEND_EFFECT,((layer+1)<<8|(type)|0x80),value)
+#define AWE_UNSET_LAYER_EFFECT(dev,voice,layer,type) _AWE_CMD(dev,voice,_AWE_SEND_EFFECT,((layer+1)<<8|(type)|0x40),0)
+
+/* terminate sound on the channel/voice */
 #define AWE_TERMINATE_CHANNEL(dev,voice) _AWE_CMD(dev,voice,_AWE_TERMINATE_CHANNEL,0,0)
+/* terminate all sounds */
 #define AWE_TERMINATE_ALL(dev) _AWE_CMD(dev, 0, _AWE_TERMINATE_ALL, 0, 0)
+/* release all sounds (w/o sustain effect) */
+#define AWE_RELEASE_ALL(dev) _AWE_CMD(dev, 0, _AWE_RELEASE_ALL, 0, 0)
+/* note off all sounds (w sustain effect) */
 #define AWE_NOTEOFF_ALL(dev) _AWE_CMD(dev, 0, _AWE_NOTEOFF_ALL, 0, 0)
+
+/* set initial attenuation */
 #define AWE_INITIAL_VOLUME(dev,atten) _AWE_CMD(dev, 0, _AWE_INITIAL_VOLUME, atten, 0)
 #define AWE_INITIAL_ATTEN  AWE_INITIAL_VOLUME
-#define AWE_SET_GUS_BANK(dev,bank) _AWE_CMD(dev, 0, _AWE_SET_GUS_BANK, bank, 0)
-#define AWE_SET_CHANNEL_MODE(dev,mode) _AWE_CMD(dev, 0, _AWE_CHANNEL_MODE, mode, 0)
-#define AWE_DRUM_CHANNELS(dev,channels) _AWE_CMD(dev, 0, _AWE_DRUM_CHANNELS, channels, 0)
-#define AWE_EXCLUSIVE_SOUND(dev,mode) _AWE_CMD(dev, 0, _AWE_EXCLUSIVE_SOUND, mode, 0)
+/* relative attenuation */
+#define AWE_SET_ATTEN(dev,atten)  _AWE_CMD(dev, 0, _AWE_INITIAL_VOLUME, atten, 1)
 
-/* it must be direct access */
-#define AWE_GET_CURRENT_MODE(dev,addr) \
-{char tmpbuf[8];\
- tmpbuf[0] = SEQ_PRIVATE; tmpbuf[1] = dev;\
- tmpbuf[2] = _AWE_MODE_FLAG|_AWE_GET_CURRENT_MODE;\
- tmpbuf[3] = 0; *(awe_mode_rec**)(tmpbuf +4) = (awe_mode_rec*)(addr);\
- write(seqfd, tmpbuf, 8);}
+/* set channel playing mode; mode=0/1/2 */
+#define AWE_SET_CHANNEL_MODE(dev,mode) _AWE_CMD(dev, 0, _AWE_CHANNEL_MODE, mode, 0)
+#define AWE_PLAY_INDIRECT	0	/* indirect voice mode (default) */
+#define AWE_PLAY_MULTI		1	/* multi note voice mode */
+#define AWE_PLAY_DIRECT		2	/* direct single voice mode */
+#define AWE_PLAY_MULTI2		3	/* sequencer2 mode; used internally */
+
+/* set drum channel mask; channels is 32bit long value */
+#define AWE_DRUM_CHANNELS(dev,channels) _AWE_CMD(dev, 0, _AWE_DRUM_CHANNELS, ((channels) & 0xffff), ((channels) >> 16))
+
+/* set bass and treble control; values are from 0 to 11 */
+#define AWE_EQUALIZER(dev,bass,treble) _AWE_CMD(dev, 0, _AWE_EQUALIZER, bass, treble)
+
+/* remove last loaded samples */
+#define AWE_REMOVE_LAST_SAMPLES(seqfd,dev) _AWE_CMD_NOW(seqfd, dev, 0, _AWE_REMOVE_LAST_SAMPLES, 0, 0)
+/* initialize emu8000 chip */
+#define AWE_INITIALIZE_CHIP(seqfd,dev) _AWE_CMD_NOW(seqfd, dev, 0, _AWE_INITIALIZE_CHIP, 0, 0)
+
+/* set miscellaneous modes; meta command */
+#define AWE_MISC_MODE(dev,mode,value) _AWE_CMD(dev, 0, _AWE_MISC_MODE, mode, value)
+/* exclusive sound off; 1=off */
+#define AWE_EXCLUSIVE_SOUND(dev,mode) AWE_MISC_MODE(dev,AWE_MD_EXCLUSIVE_SOUND,mode)
+/* default GUS bank number */
+#define AWE_SET_GUS_BANK(dev,bank) AWE_MISC_MODE(dev,AWE_MD_GUS_BANK,bank)
+/* change panning position in realtime; 0=don't 1=do */
+#define AWE_REALTIME_PAN(dev,mode) AWE_MISC_MODE(dev,AWE_MD_REALTIME_PAN,mode)
 
 /* extended pressure controls; not portable with other sound drivers */
 #define AWE_KEY_PRESSURE(dev,ch,note,vel) SEQ_START_NOTE(dev,ch,(note)+128,vel)
-#define AWE_CHN_PRESSURE(dev,ch,vel) SEQ_START_NOTE(dev,(ch)+128,0,vel)
+#define AWE_CHN_PRESSURE(dev,ch,vel) _AWE_CMD(dev,ch,_AWE_CHN_PRESSURE,vel,0)
 
-/* reverb mode */
+/*----------------------------------------------------------------*/
+
+/* reverb mode parameters */
 #define	AWE_REVERB_ROOM1	0
 #define AWE_REVERB_ROOM2	1
 #define	AWE_REVERB_ROOM3	2
@@ -272,8 +381,17 @@
 #define	AWE_REVERB_PLATE	5
 #define	AWE_REVERB_DELAY	6
 #define	AWE_REVERB_PANNINGDELAY 7
+#define AWE_REVERB_PREDEFINED	8
+/* user can define reverb modes up to 32 */
+#define AWE_REVERB_NUMBERS	32
+
+typedef struct awe_reverb_fx_rec {
+	unsigned short parms[28];
+} awe_reverb_fx_rec;
+
+/*----------------------------------------------------------------*/
 
-/* chorus mode */
+/* chorus mode parameters */
 #define AWE_CHORUS_1		0
 #define	AWE_CHORUS_2		1
 #define	AWE_CHORUS_3		2
@@ -282,8 +400,41 @@
 #define	AWE_CHORUS_FLANGER	5
 #define	AWE_CHORUS_SHORTDELAY	6
 #define	AWE_CHORUS_SHORTDELAY2	7
+#define AWE_CHORUS_PREDEFINED	8
+/* user can define chorus modes up to 32 */
+#define AWE_CHORUS_NUMBERS	32
+
+typedef struct awe_chorus_fx_rec {
+	unsigned short feedback;	/* feedback level (0xE600-0xE6FF) */
+	unsigned short delay_offset;	/* delay (0-0x0DA3) [1/44100 sec] */
+	unsigned short lfo_depth;	/* LFO depth (0xBC00-0xBCFF) */
+	unsigned int delay;	/* right delay (0-0xFFFFFFFF) [1/256/44100 sec] */
+	unsigned int lfo_freq;		/* LFO freq LFO freq (0-0xFFFFFFFF) */
+} awe_chorus_fx_rec;
 
-/* effects */
+/*----------------------------------------------------------------*/
+
+/* misc mode types */
+enum {
+/* 0*/	AWE_MD_EXCLUSIVE_OFF,	/* obsolete */
+/* 1*/	AWE_MD_EXCLUSIVE_ON,	/* obsolete */
+/* 2*/	AWE_MD_VERSION,		/* read only */
+/* 3*/	AWE_MD_EXCLUSIVE_SOUND,	/* 0/1: (default=1) */
+/* 4*/	AWE_MD_REALTIME_PAN,	/* 0/1: do realtime pan change (default=1) */
+/* 5*/	AWE_MD_GUS_BANK,	/* bank number (default=0) */
+/* 6*/	AWE_MD_KEEP_EFFECT,	/* 0/1: keep effect values, (default=0) */
+/* 7*/	AWE_MD_ZERO_ATTEN,	/* attenuation of max volume (default=32) */
+/* 8*/	AWE_MD_CHN_PRIOR,	/* 0/1: set MIDI channel priority mode (default=1) */
+/* 9*/	AWE_MD_MOD_SENSE,	/* integer: modwheel sensitivity */
+/*10*/	AWE_MD_DEF_PRESET,	/* integer: default preset number */
+/*11*/	AWE_MD_DEF_BANK,	/* integer: default bank number */
+/*12*/	AWE_MD_DEF_DRUM,	/* integer: default drumset number */
+	AWE_MD_END,
+};
+
+/*----------------------------------------------------------------*/
+
+/* effect parameters */
 enum {
 
 /* modulation envelope parameters */
@@ -330,9 +481,9 @@
 /*30*/	AWE_FX_COARSE_SAMPLE_START,	/* SHORT: upper word offset */
 /*31*/	AWE_FX_COARSE_LOOP_START,	/* SHORT: upper word offset */
 /*32*/	AWE_FX_COARSE_LOOP_END,		/* SHORT: upper word offset */
+/*33*/	AWE_FX_ATTEN,		/* BYTE: lo IFATN */
 
 	AWE_FX_END,
 };
-
 
 #endif /* AWE_VOICE_H */
diff -u --recursive --new-file v2.1.62/linux/include/linux/blk.h linux/include/linux/blk.h
--- v2.1.62/linux/include/linux/blk.h	Mon Aug 18 18:19:47 1997
+++ linux/include/linux/blk.h	Sat Nov  8 11:39:12 1997
@@ -461,8 +461,7 @@
 	if ((bh = req->bh) != NULL) {
 		req->bh = bh->b_reqnext;
 		bh->b_reqnext = NULL;
-		mark_buffer_uptodate(bh, uptodate);
-		unlock_buffer(bh);
+		bh->b_end_io(bh, uptodate);
 		if ((bh = req->bh) != NULL) {
 			req->current_nr_sectors = bh->b_size >> 9;
 			if (req->nr_sectors < req->current_nr_sectors) {
diff -u --recursive --new-file v2.1.62/linux/include/linux/blkdev.h linux/include/linux/blkdev.h
--- v2.1.62/linux/include/linux/blkdev.h	Mon Jul  7 16:02:03 1997
+++ linux/include/linux/blkdev.h	Sat Nov  8 11:39:12 1997
@@ -55,14 +55,19 @@
 extern struct wait_queue * wait_for_request;
 extern void resetup_one_dev(struct gendisk *dev, int drive);
 extern void unplug_device(void * data);
+extern void make_request(int major,int rw, struct buffer_head * bh);
 
 /* md needs this function to remap requests */
 extern int md_map (int minor, kdev_t *rdev, unsigned long *rsector, unsigned long size);
+extern int md_make_request (int minor, int rw, struct buffer_head * bh);
+extern int md_error (kdev_t mddev, kdev_t rdev);
 
 extern int * blk_size[MAX_BLKDEV];
 
 extern int * blksize_size[MAX_BLKDEV];
 
 extern int * hardsect_size[MAX_BLKDEV];
+
+extern int * max_readahead[MAX_BLKDEV];
 
 #endif
diff -u --recursive --new-file v2.1.62/linux/include/linux/cyclades.h linux/include/linux/cyclades.h
--- v2.1.62/linux/include/linux/cyclades.h	Thu Jul 17 10:06:08 1997
+++ linux/include/linux/cyclades.h	Tue Nov 11 11:04:15 1997
@@ -1,4 +1,4 @@
-/* $Revision: 2.0 $$Date: 1997/06/30 10:30:00 $
+/* $Revision: 2.1 $$Date: 1997/10/24 16:03:00 $
  * linux/include/linux/cyclades.h
  *
  * This file is maintained by Marcio Saito <marcio@cyclades.com> and
@@ -6,6 +6,12 @@
  *
  * This file contains the general definitions for the cyclades.c driver
  *$Log: cyclades.h,v $
+ *Revision 2.1	1997/10/24 16:03:00  ivan
+ *added rflow (which allows enabling the CD1400 special flow control 
+ *feature) and rtsdtr_inv (which allows DTR/RTS pin inversion) to 
+ *cyclades_port structure;
+ *added Alpha support
+ *
  *Revision 2.0  1997/06/30 10:30:00  ivan
  *added some new doorbell command constants related to IOCTLW and
  *UART error signaling
@@ -51,6 +57,11 @@
 #define CYSETTIMEOUT            0x435907
 #define CYGETDEFTIMEOUT         0x435908
 #define CYSETDEFTIMEOUT         0x435909
+#define CYSETRFLOW		0x43590a
+#define CYRESETRFLOW		0x43590b
+#define CYSETRTSDTR_INV		0x43590c
+#define CYRESETRTSDTR_INV	0x43590d
+#define CYZPOLLCYCLE		0x43590e
 
 /*************** CYCLOM-Z ADDITIONS ***************/
 
@@ -61,6 +72,8 @@
 #define CZ_BOOT_END     (CZIOC|0xfd)
 #define CZ_TEST         (CZIOC|0xfe)
 
+#define CZ_DEF_POLL	(HZ/25)
+
 #define MAX_BOARD       4       /* Max number of boards */
 #define MAX_PORT        128     /* Max number of ports per board */
 #define MAX_DEV         256     /* Max number of ports total */
@@ -85,7 +98,12 @@
  *	architectures and compilers.
  */
 
+#if defined(__alpha__)
+typedef unsigned long	ucdouble;	/* 64 bits, unsigned */
+typedef unsigned int	uclong;		/* 32 bits, unsigned */
+#else
 typedef unsigned long	uclong;		/* 32 bits, unsigned */
+#endif
 typedef unsigned short	ucshort;	/* 16 bits, unsigned */
 typedef unsigned char	ucchar;		/* 8 bits, unsigned */
 
@@ -96,7 +114,7 @@
 #define	DP_WINDOW_SIZE		(0x00080000)	/* window size 512 Kb */
 #define	ZE_DP_WINDOW_SIZE	(0x00100000)	/* window size 1 Mb (Ze and
 						  8Zo V.2 */
-#define	CTRL_WINDOW_SIZE	(0x00000100)	/* runtime regs 256 bytes */
+#define	CTRL_WINDOW_SIZE	(0x00000080)	/* runtime regs 128 bytes */
 
 /*
  *	CUSTOM_REG - Cyclom-Z/PCI Custom Registers Set. The driver
@@ -194,6 +212,12 @@
 #define ID_ADDRESS	0x00000180L	/* signature/pointer address */
 #define	ZFIRM_ID	0x5557465AL	/* ZFIRM/U signature */
 #define	ZFIRM_HLT	0x59505B5CL	/* ZFIRM needs external power supply */
+#define	ZFIRM_RST	0x56040674L	/* RST signal (due to FW reset) */
+
+#define	ZF_TINACT_DEF	1000		/* default inactivity timeout 
+					   (1000 ms) */
+#define	ZF_TINACT	ZF_TINACT_DEF
+
 struct	FIRM_ID {
 	uclong	signature;		/* ZFIRM/U signature */
 	uclong	zfwctrl_addr;		/* pointer to ZFW_CTRL structure */
@@ -334,7 +358,9 @@
 	uclong	rs_status;	/* RS-232 inputs */
 	uclong	flow_xon;	/* xon char */
 	uclong	flow_xoff;	/* xoff char */
-	uclong	filler[3];	/* filler to align structures */
+	uclong	hw_overflow;	/* hw overflow counter */
+	uclong	sw_overflow;	/* sw overflow counter */
+	uclong	comm_error;	/* frame/parity error counter */
 };
 
 
@@ -378,11 +404,11 @@
 
 	/* host to FW commands */
 	uclong	hcmd_channel;	/* channel number */
-	uclong	*hcmd_param;	/* pointer to parameters */
+	uclong	hcmd_param;	/* pointer to parameters */
 
 	/* FW to Host commands */
 	uclong	fwcmd_channel;	/* channel number */
-	uclong	*fwcmd_param;	/* pointer to parameters */
+	uclong	fwcmd_param;	/* pointer to parameters */
 
 	/* filler so the structures are aligned */
 	uclong	filler[7];
@@ -404,8 +430,22 @@
 
 
 
+
 #ifdef __KERNEL__
 
+/***************************************
+ * Memory access functions/macros      *
+ * (required to support Alpha systems) *
+ ***************************************/
+
+#define cy_writeb(port,val)     {writeb((ucchar)(val),(ulong)(port)); mb();}
+#define cy_writew(port,val)     {writew((ushort)(val),(ulong)(port)); mb();}
+#define cy_writel(port,val)     {writel((uclong)(val),(ulong)(port)); mb();}
+
+#define cy_readb(port)  readb(port)
+#define cy_readw(port)  readw(port)
+#define cy_readl(port)  readl(port)
+
 /* Per card data structure */
 
 struct cyclades_card {
@@ -415,6 +455,7 @@
     int num_chips;	/* 0 if card absent, 1 if Z/PCI, else Y */
     int first_line;	/* minor number of first channel on card */
     int bus_index;	/* address shift - 0 for ISA, 1 for PCI */
+    int	inact_ctrl;	/* FW Inactivity control - 0 disabled, 1 enabled */
 };
 
 struct cyclades_chip {
@@ -443,6 +484,8 @@
 	int                     cor1,cor2,cor3,cor4,cor5;
 	int                     tbpr,tco,rbpr,rco;
 	int			baud;
+	int			rflow;
+	int			rtsdtr_inv;
 	int			ignore_status_mask;
 	int			close_delay;
 	int			IER; 	/* Interrupt Enable Register */
@@ -482,10 +525,14 @@
 
 
 
-#define CyMaxChipsPerCard 8
+#define CyMAX_CHIPS_PER_CARD	8
+#define CyMAX_CHAR_FIFO		12
+#define CyPORTS_PER_CHIP	4
+
+#define	CyISA_Ywin	0x2000
 
 #define CyPCI_Ywin 	0x4000
-#define CyPCI_Zctl 	0x100
+#define CyPCI_Zctl 	CTRL_WINDOW_SIZE
 #define CyPCI_Zwin 	0x80000
 #define CyPCI_Ze_win 	(2 * CyPCI_Zwin)
 
@@ -530,6 +577,9 @@
 #define CyPPR 		(0x7E*2)
 #define      CyCLOCK_20_1MS	(0x27)
 #define      CyCLOCK_25_1MS	(0x31)
+#define      CyCLOCK_25_5MS	(0xf4)
+#define      CyCLOCK_60_1MS	(0x75)
+#define      CyCLOCK_60_2MS	(0xea)
 
 /* Virtual Registers */
 
@@ -652,12 +702,7 @@
 #define CyTBPR		(0x72*2)
 #define CyTCOR		(0x76*2)
 
-/* max number of chars in the FIFO */
-
-#define CyMAX_CHAR_FIFO	12
-
 /***************************************************************************/
 
 #endif /* __KERNEL__ */
 #endif /* _LINUX_CYCLADES_H */
-
diff -u --recursive --new-file v2.1.62/linux/include/linux/dcache.h linux/include/linux/dcache.h
--- v2.1.62/linux/include/linux/dcache.h	Sat Nov  1 11:04:27 1997
+++ linux/include/linux/dcache.h	Tue Nov  4 10:26:38 1997
@@ -57,10 +57,13 @@
 	struct dentry * d_covers;
 	struct list_head d_hash;	/* lookup hash list */
 	struct list_head d_lru;		/* d_count = 0 LRU list */
+	struct list_head d_child;	/* child of parent list */
+	struct list_head d_subdirs;	/* our children */
 	struct qstr d_name;
 	unsigned long d_time;		/* used by d_revalidate */
 	struct dentry_operations  *d_op;
 	struct super_block * d_sb;	/* The root of the dentry tree */
+	unsigned long d_reftime;	/* last time referenced */
 };
 
 struct dentry_operations {
@@ -115,6 +118,7 @@
 extern struct dentry * d_alloc(struct dentry * parent, const struct qstr *name);
 extern void prune_dcache(int);
 extern void shrink_dcache_sb(struct super_block *);
+extern void shrink_dcache_parent(struct dentry *);
 extern int d_invalidate(struct dentry *);
 
 #define shrink_dcache() prune_dcache(0)
diff -u --recursive --new-file v2.1.62/linux/include/linux/fs.h linux/include/linux/fs.h
--- v2.1.62/linux/include/linux/fs.h	Sat Oct 25 02:44:18 1997
+++ linux/include/linux/fs.h	Sat Nov  8 11:39:13 1997
@@ -164,7 +164,6 @@
 #define BH_Touched	4	/* 1 if the buffer has been touched (aging) */
 #define BH_Has_aged	5	/* 1 if the buffer has been aged (aging) */
 #define BH_Protected	6	/* 1 if the buffer is protected */
-#define BH_FreeOnIO	7	/* 1 to discard the buffer_head after IO */
 
 /*
  * Try to keep the most commonly used fields in single cache lines (16
@@ -202,8 +201,18 @@
 	struct buffer_head ** b_pprev;		/* doubly linked list of hash-queue */
 	struct buffer_head * b_prev_free;	/* doubly linked list of buffers */
 	struct buffer_head * b_reqnext;		/* request queue */
+
+	/*
+	 * I/O completion
+	 */
+	void (*b_end_io)(struct buffer_head *bh, int uptodate);
+	void *b_dev_id;
 };
 
+typedef void (bh_end_io_t)(struct buffer_head *bh, int uptodate);
+void init_buffer(struct buffer_head *bh, kdev_t dev, int block,
+		 bh_end_io_t *handler, void *dev_id);
+
 static inline int buffer_uptodate(struct buffer_head * bh)
 {
 	return test_bit(BH_Uptodate, &bh->b_state);
@@ -756,6 +765,7 @@
 extern int close_fp(struct file *);
 extern struct buffer_head * get_hash_table(kdev_t, int, int);
 extern struct buffer_head * getblk(kdev_t, int, int);
+extern struct buffer_head *efind_buffer(kdev_t dev, int block, int size);
 extern void ll_rw_block(int, int, struct buffer_head * bh[]);
 extern void ll_rw_page(int, kdev_t, unsigned long, char *);
 extern void ll_rw_swap_file(int, kdev_t, unsigned int *, int, char *);
diff -u --recursive --new-file v2.1.62/linux/include/linux/ipx.h linux/include/linux/ipx.h
--- v2.1.62/linux/include/linux/ipx.h	Mon Jun 30 15:23:58 1997
+++ linux/include/linux/ipx.h	Sun Nov  9 10:13:48 1997
@@ -7,8 +7,8 @@
 
 struct sockaddr_ipx
 {
-	sa_family_t	 sipx_family;
-	__u16		 sipx_port;
+	sa_family_t	sipx_family;
+	__u16		sipx_port;
 	__u32		sipx_network;
 	unsigned char 	sipx_node[IPX_NODE_LEN];
 	__u8		sipx_type;
@@ -26,14 +26,14 @@
 
 typedef struct ipx_route_definition
 {
-	unsigned long ipx_network;
-	unsigned long ipx_router_network;
+	__u32         ipx_network;
+	__u32         ipx_router_network;
 	unsigned char ipx_router_node[IPX_NODE_LEN];
 }	ipx_route_definition;
 
 typedef struct ipx_interface_definition
 {
-	unsigned long ipx_network;
+	__u32         ipx_network;
 	unsigned char ipx_device[16];
 	unsigned char ipx_dlink_type;
 #define IPX_FRAME_NONE		0
@@ -61,8 +61,8 @@
 
 struct ipx_route_def
 {
-	unsigned long ipx_network;
-	unsigned long ipx_router_network;
+	__u32         ipx_network;
+	__u32         ipx_router_network;
 #define IPX_ROUTE_NO_ROUTER	0
 	unsigned char ipx_router_node[IPX_NODE_LEN];
 	unsigned char ipx_device[16];
diff -u --recursive --new-file v2.1.62/linux/include/linux/locks.h linux/include/linux/locks.h
--- v2.1.62/linux/include/linux/locks.h	Mon Jul  7 16:02:11 1997
+++ linux/include/linux/locks.h	Sat Nov  8 11:39:13 1997
@@ -26,8 +26,11 @@
 		__wait_on_buffer(bh);
 }
 
-void unlock_buffer(struct buffer_head *);
-
+extern inline void unlock_buffer(struct buffer_head *bh)
+{
+	clear_bit(BH_Lock, &bh->b_state);
+	wake_up(&bh->b_wait);
+}
 
 /*
  * super-block locking. Again, interrupts may only unlock
diff -u --recursive --new-file v2.1.62/linux/include/linux/md.h linux/include/linux/md.h
--- v2.1.62/linux/include/linux/md.h	Tue Oct 29 17:06:23 1996
+++ linux/include/linux/md.h	Sat Nov  8 11:39:13 1997
@@ -1,4 +1,3 @@
-
 /*
    md.h : Multiple Devices driver for Linux
           Copyright (C) 1994-96 Marc ZYNGIER
@@ -19,10 +18,19 @@
 #define _MD_H
 
 #include <linux/major.h>
-#include <linux/mm.h>
 #include <linux/ioctl.h>
+#include <linux/types.h>
+
+/*
+ * Different major versions are not compatible.
+ * Different minor versions are only downward compatible.
+ * Different patchlevel versions are downward and upward compatible.
+ */
+#define MD_MAJOR_VERSION		0
+#define MD_MINOR_VERSION		36
+#define MD_PATCHLEVEL_VERSION		6
 
-#define MD_VERSION "0.35"
+#define MD_DEFAULT_DISK_READAHEAD	(256 * 1024)
 
 /* ioctls */
 #define REGISTER_DEV _IO (MD_MAJOR, 1)
@@ -42,9 +50,9 @@
 #define FAULT_SHIFT       8
 #define PERSONALITY_SHIFT 16
 
-#define FACTOR_MASK       0xFFUL
-#define FAULT_MASK        0xFF00UL
-#define PERSONALITY_MASK  0xFF0000UL
+#define FACTOR_MASK       0x000000FFUL
+#define FAULT_MASK        0x0000FF00UL
+#define PERSONALITY_MASK  0x00FF0000UL
 
 #define MD_RESERVED       0	/* Not used by now */
 #define LINEAR            (1UL << PERSONALITY_SHIFT)
@@ -54,15 +62,159 @@
 #define RAID5             (4UL << PERSONALITY_SHIFT)
 #define MAX_PERSONALITY   5
 
+/*
+ * MD superblock.
+ *
+ * The MD superblock maintains some statistics on each MD configuration.
+ * Each real device in the MD set contains it near the end of the device.
+ * Some of the ideas are copied from the ext2fs implementation.
+ *
+ * We currently use 4096 bytes as follows:
+ *
+ *	word offset	function
+ *
+ *	   0  -    31	Constant generic MD device information.
+ *        32  -    63   Generic state information.
+ *	  64  -   127	Personality specific information.
+ *	 128  -   511	12 32-words descriptors of the disks in the raid set.
+ *	 512  -   911	Reserved.
+ *	 912  -  1023	Disk specific descriptor.
+ */
+
+/*
+ * If x is the real device size in bytes, we return an apparent size of:
+ *
+ *	y = (x & ~(MD_RESERVED_BYTES - 1)) - MD_RESERVED_BYTES
+ *
+ * and place the 4kB superblock at offset y.
+ */
+#define MD_RESERVED_BYTES		(64 * 1024)
+#define MD_RESERVED_SECTORS		(MD_RESERVED_BYTES / 512)
+#define MD_RESERVED_BLOCKS		(MD_RESERVED_BYTES / BLOCK_SIZE)
+
+#define MD_NEW_SIZE_SECTORS(x)		((x & ~(MD_RESERVED_SECTORS - 1)) - MD_RESERVED_SECTORS)
+#define MD_NEW_SIZE_BLOCKS(x)		((x & ~(MD_RESERVED_BLOCKS - 1)) - MD_RESERVED_BLOCKS)
+
+#define MD_SB_BYTES			4096
+#define MD_SB_WORDS			(MD_SB_BYTES / 4)
+#define MD_SB_BLOCKS			(MD_SB_BYTES / BLOCK_SIZE)
+#define MD_SB_SECTORS			(MD_SB_BYTES / 512)
+
+/*
+ * The following are counted in 32-bit words
+ */
+#define	MD_SB_GENERIC_OFFSET		0
+#define MD_SB_PERSONALITY_OFFSET	64
+#define MD_SB_DISKS_OFFSET		128
+#define MD_SB_DESCRIPTOR_OFFSET		992
+
+#define MD_SB_GENERIC_CONSTANT_WORDS	32
+#define MD_SB_GENERIC_STATE_WORDS	32
+#define MD_SB_GENERIC_WORDS		(MD_SB_GENERIC_CONSTANT_WORDS + MD_SB_GENERIC_STATE_WORDS)
+#define MD_SB_PERSONALITY_WORDS		64
+#define MD_SB_DISKS_WORDS		384
+#define MD_SB_DESCRIPTOR_WORDS		32
+#define MD_SB_RESERVED_WORDS		(1024 - MD_SB_GENERIC_WORDS - MD_SB_PERSONALITY_WORDS - MD_SB_DISKS_WORDS - MD_SB_DESCRIPTOR_WORDS)
+#define MD_SB_EQUAL_WORDS		(MD_SB_GENERIC_WORDS + MD_SB_PERSONALITY_WORDS + MD_SB_DISKS_WORDS)
+#define MD_SB_DISKS			(MD_SB_DISKS_WORDS / MD_SB_DESCRIPTOR_WORDS)
+
+/*
+ * Device "operational" state bits
+ */
+#define MD_FAULTY_DEVICE		0	/* Device is faulty / operational */
+#define MD_ACTIVE_DEVICE		1	/* Device is a part or the raid set / spare disk */
+#define MD_SYNC_DEVICE			2	/* Device is in sync with the raid set */
+
+typedef struct md_device_descriptor_s {
+	__u32 number;		/* 0 Device number in the entire set */
+	__u32 major;		/* 1 Device major number */
+	__u32 minor;		/* 2 Device minor number */
+	__u32 raid_disk;	/* 3 The role of the device in the raid set */
+	__u32 state;		/* 4 Operational state */
+	__u32 reserved[MD_SB_DESCRIPTOR_WORDS - 5];
+} md_descriptor_t;
+
+#define MD_SB_MAGIC		0xa92b4efc
+
+/*
+ * Superblock state bits
+ */
+#define MD_SB_CLEAN		0
+#define MD_SB_ERRORS		1
+
+typedef struct md_superblock_s {
+
+	/*
+	 * Constant generic information
+	 */
+	__u32 md_magic;		/*  0 MD identifier */
+	__u32 major_version;	/*  1 major version to which the set conforms */
+	__u32 minor_version;	/*  2 minor version to which the set conforms */
+	__u32 patch_version;	/*  3 patchlevel version to which the set conforms */
+	__u32 gvalid_words;	/*  4 Number of non-reserved words in this section */
+	__u32 set_magic;	/*  5 Raid set identifier */
+	__u32 ctime;		/*  6 Creation time */
+	__u32 level;		/*  7 Raid personality (mirroring, raid5, ...) */
+	__u32 size;		/*  8 Apparent size of each individual disk, in kB */
+	__u32 nr_disks;		/*  9 Number of total disks in the raid set */
+	__u32 raid_disks;	/* 10 Number of disks in a fully functional raid set */
+	__u32 gstate_creserved[MD_SB_GENERIC_CONSTANT_WORDS - 11];
+
+	/*
+	 * Generic state information
+	 */
+	__u32 utime;		/*  0 Superblock update time */
+	__u32 state;		/*  1 State bits (clean, ...) */
+	__u32 active_disks;	/*  2 Number of currently active disks (some non-faulty disks might not be in sync) */
+	__u32 working_disks;	/*  3 Number of working disks */
+	__u32 failed_disks;	/*  4 Number of failed disks */
+	__u32 spare_disks;	/*  5 Number of spare disks */
+	__u32 gstate_sreserved[MD_SB_GENERIC_STATE_WORDS - 6];
+
+	/*
+	 * Personality information
+	 */
+	__u32 parity_algorithm;
+	__u32 chunk_size;
+	__u32 pstate_reserved[MD_SB_PERSONALITY_WORDS - 2];
+
+	/*
+	 * Disks information
+	 */
+	md_descriptor_t disks[MD_SB_DISKS];
+
+	/*
+	 * Reserved
+	 */
+	__u32 reserved[MD_SB_RESERVED_WORDS];
+
+	/*
+	 * Active descriptor
+	 */
+	md_descriptor_t descriptor;
+} md_superblock_t;
+
 #ifdef __KERNEL__
 
-#include <linux/types.h>
+#include <linux/mm.h>
 #include <linux/fs.h>
 #include <linux/blkdev.h>
 
+/*
+ * Kernel-based reconstruction is mostly working, but still requires
+ * some additional work.
+ */
+#define SUPPORT_RECONSTRUCTION	0
+
 #define MAX_REAL     8		/* Max number of physical dev per md dev */
 #define MAX_MD_DEV   4		/* Max number of md dev */
 
+#if SUPPORT_RECONSTRUCTION
+#define MAX_MD_THREADS (MAX_MD_DEV * 3)	/* Max number of kernel threads */
+#else
+#define MAX_MD_THREADS (MAX_MD_DEV)	/* Max number of kernel threads */
+#endif /* SUPPORT_RECONSTRUCTION */
+
 #define FACTOR(a)         ((a)->repartition & FACTOR_MASK)
 #define MAX_FAULT(a)      (((a)->repartition & FAULT_MASK)>>8)
 #define PERSONALITY(a)    ((a)->repartition & PERSONALITY_MASK)
@@ -76,40 +228,75 @@
   int offset;			/* Real device offset (in blocks) in md dev
 				   (only used in linear mode) */
   struct inode *inode;		/* Lock inode */
+  md_superblock_t *sb;
+  u32 sb_offset;
 };
 
 struct md_dev;
 
+#define SPARE_INACTIVE	0
+#define SPARE_WRITE	1
+#define SPARE_ACTIVE	2
+
 struct md_personality
 {
   char *name;
-  int (*map)(struct md_dev *md_dev, kdev_t *rdev,
-	     unsigned long *rsector, unsigned long size);
-  int (*run)(int minor, struct md_dev *md_dev);
-  int (*stop)(int minor, struct md_dev *md_dev);
-  int (*status)(char *page, int minor, struct md_dev *md_dev);
+  int (*map)(struct md_dev *mddev, kdev_t *rdev,
+	              unsigned long *rsector, unsigned long size);
+  int (*make_request)(struct md_dev *mddev, int rw, struct buffer_head * bh);
+  void (*end_request)(struct buffer_head * bh, int uptodate);
+  int (*run)(int minor, struct md_dev *mddev);
+  int (*stop)(int minor, struct md_dev *mddev);
+  int (*status)(char *page, int minor, struct md_dev *mddev);
   int (*ioctl)(struct inode *inode, struct file *file,
 	       unsigned int cmd, unsigned long arg);
   int max_invalid_dev;
+  int (*error_handler)(struct md_dev *mddev, kdev_t dev);
+
+/*
+ * Some personalities (RAID-1, RAID-5) can get disks hot-added and
+ * hot-removed. Hot removal is different from failure. (failure marks
+ * a disk inactive, but the disk is still part of the array)
+ */
+  int (*hot_add_disk) (struct md_dev *mddev, kdev_t dev);
+  int (*hot_remove_disk) (struct md_dev *mddev, kdev_t dev);
+  int (*mark_spare) (struct md_dev *mddev, md_descriptor_t *descriptor, int state);
 };
 
 struct md_dev
 {
-  struct real_dev devices[MAX_REAL];
-  struct md_personality *pers;
-  int repartition;
-  int busy;
-  int nb_dev;
-  void *private;
+  struct real_dev	devices[MAX_REAL];
+  struct md_personality	*pers;
+  md_superblock_t	*sb;
+  int			sb_dirty;
+  int			repartition;
+  int			busy;
+  int			nb_dev;
+  void			*private;
+};
+
+struct md_thread {
+	void			(*run) (void *data);
+	void			*data;
+	struct wait_queue	*wqueue;
+	__u32			flags;
 };
 
+#define THREAD_WAKEUP	0
+
 extern struct md_dev md_dev[MAX_MD_DEV];
 extern int md_size[MAX_MD_DEV];
+extern int md_maxreadahead[MAX_MD_DEV];
 
 extern char *partition_name (kdev_t dev);
 
 extern int register_md_personality (int p_num, struct md_personality *p);
 extern int unregister_md_personality (int p_num);
+extern struct md_thread *md_register_thread (void (*run) (void *data), void *data);
+extern void md_unregister_thread (struct md_thread *thread);
+extern void md_wakeup_thread(struct md_thread *thread);
+extern int md_update_sb (int minor);
+extern int md_do_sync(struct md_dev *mddev);
 
 #endif __KERNEL__
 #endif _MD_H
diff -u --recursive --new-file v2.1.62/linux/include/linux/miscdevice.h linux/include/linux/miscdevice.h
--- v2.1.62/linux/include/linux/miscdevice.h	Mon Aug  4 16:25:40 1997
+++ linux/include/linux/miscdevice.h	Tue Nov  4 10:23:25 1997
@@ -9,6 +9,7 @@
 #define ATARIMOUSE_MINOR 5
 #define SUN_MOUSE_MINOR 6
 #define PC110PAD_MINOR 9
+#define RADIO_MINOR 129
 #define RTC_MINOR 135
 #define SUN_OPENPROM_MINOR 139
 #define NVRAM_MINOR 144
diff -u --recursive --new-file v2.1.62/linux/include/linux/ncp_fs.h linux/include/linux/ncp_fs.h
--- v2.1.62/linux/include/linux/ncp_fs.h	Tue Sep 23 16:48:50 1997
+++ linux/include/linux/ncp_fs.h	Wed Nov 12 11:10:40 1997
@@ -55,6 +55,20 @@
 
 #ifdef __KERNEL__
 
+#undef NCPFS_PARANOIA
+#define DEBUG_NCP 0
+#if DEBUG_NCP > 0
+#define DPRINTK(format, args...) printk(format , ## args)
+#else
+#define DPRINTK(format, args...)
+#endif
+
+#if DEBUG_NCP > 1
+#define DDPRINTK(format, args...) printk(format , ## args)
+#else
+#define DDPRINTK(format, args...)
+#endif
+
 /* The readdir cache size controls how many directory entries are
  * cached.
  */
@@ -62,16 +76,40 @@
 
 #define NCP_MAX_RPC_TIMEOUT (6*HZ)
 
+/*
+ * This is the ncpfs part of the inode structure. This must contain
+ * all the information we need to work with an inode after creation.
+ * (Move to ncp_fs_i.h once it stabilizes, and add a union in fs.h)
+ */
+struct ncpfs_i {
+	__u32	dirEntNum __attribute__((packed));
+	__u32	DosDirNum __attribute__((packed));
+	__u32	volNumber __attribute__((packed));
+	int	opened;
+	int	access;
+	__u32	server_file_handle __attribute__((packed));
+	__u8	open_create_action __attribute__((packed));
+	__u8	file_handle[6] __attribute__((packed));
+};
+
+/*
+ * This is an extension of the nw_file_info structure with
+ * the additional information we need to create an inode.
+ */
+struct ncpfs_inode_info {
+	ino_t	ino;		/* dummy inode number */
+	struct nw_file_info nw_info;
+};
+
 /* Guess, what 0x564c is :-) */
 #define NCP_SUPER_MAGIC  0x564c
 
 
 #define NCP_SBP(sb)          ((struct ncp_server *)((sb)->u.generic_sbp))
-#define NCP_INOP(inode)      ((struct ncp_inode_info *)((inode)->u.generic_ip))
 
 #define NCP_SERVER(inode)    NCP_SBP((inode)->i_sb)
-#define NCP_FINFO(inode)     (&(NCP_INOP(inode)->finfo))
-#define NCP_ISTRUCT(inode)   (&(NCP_FINFO(inode)->i))
+/* We don't have an ncpfs union yet, so use smbfs ... */
+#define NCP_FINFO(inode)     ((struct ncpfs_i *)&((inode)->u.smbfs_i))
 
 #ifdef DEBUG_NCP_MALLOC
 
@@ -101,46 +139,23 @@
 
 #endif				/* DEBUG_NCP_MALLOC */
 
-#if DEBUG_NCP > 0
-#define DPRINTK(format, args...) printk(format , ## args)
-#else
-#define DPRINTK(format, args...)
-#endif
-
-#if DEBUG_NCP > 1
-#define DDPRINTK(format, args...) printk(format , ## args)
-#else
-#define DDPRINTK(format, args...)
-#endif
-
-
-/* linux/fs/ncpfs/file.c */
-extern struct inode_operations ncp_file_inode_operations;
-int ncp_make_open(struct inode *i, int right);
+/* linux/fs/ncpfs/inode.c */
+struct super_block *ncp_read_super(struct super_block *, void *, int);
+struct inode *ncp_iget(struct super_block *, struct ncpfs_inode_info *);
+void ncp_update_inode(struct inode *, struct nw_file_info *);
+extern int init_ncp_fs(void);
 
 /* linux/fs/ncpfs/dir.c */
 extern struct inode_operations ncp_dir_inode_operations;
-void ncp_free_inode_info(struct ncp_inode_info *i);
-void ncp_free_all_inodes(struct ncp_server *server);
-void ncp_init_root(struct ncp_server *server);
-int ncp_conn_logged_in(struct ncp_server *server);
+int ncp_conn_logged_in(struct ncp_server *);
 void ncp_init_dir_cache(void);
-void ncp_invalid_dir_cache(struct inode *ino);
-struct ncp_inode_info *ncp_find_inode(struct inode *inode);
-ino_t ncp_info_ino(struct ncp_server *server, struct ncp_inode_info *info);
+void ncp_invalid_dir_cache(struct inode *);
 void ncp_free_dir_cache(void);
 int ncp_date_dos2unix(__u16 time, __u16 date);
 void ncp_date_unix2dos(int unix_date, __u16 * time, __u16 * date);
 
-
 /* linux/fs/ncpfs/ioctl.c */
-int ncp_ioctl(struct inode *inode, struct file *filp,
-	      unsigned int cmd, unsigned long arg);
-
-/* linux/fs/ncpfs/inode.c */
-struct super_block *ncp_read_super(struct super_block *sb,
-				   void *raw_data, int silent);
-extern int init_ncp_fs(void);
+int ncp_ioctl(struct inode *, struct file *, unsigned int, unsigned long);
 
 /* linux/fs/ncpfs/sock.c */
 int ncp_request(struct ncp_server *server, int function);
@@ -149,8 +164,50 @@
 void ncp_lock_server(struct ncp_server *server);
 void ncp_unlock_server(struct ncp_server *server);
 
+/* linux/fs/ncpfs/file.c */
+extern struct inode_operations ncp_file_inode_operations;
+int ncp_make_open(struct inode *, int);
+
 /* linux/fs/ncpfs/mmap.c */
-int ncp_mmap(struct file *file, struct vm_area_struct *vma);
+int ncp_mmap(struct file *, struct vm_area_struct *);
+
+/* linux/fs/ncpfs/ncplib_kernel.c */
+int ncp_make_closed(struct inode *);
+
+static inline void str_upper(char *name)
+{
+	while (*name) {
+		if (*name >= 'a' && *name <= 'z') {
+			*name -= ('a' - 'A');
+		}
+		name++;
+	}
+}
+
+static inline void str_lower(char *name)
+{
+	while (*name) {
+		if (*name >= 'A' && *name <= 'Z') {
+			*name += ('a' - 'A');
+		}
+		name++;
+	}
+}
+
+static inline int ncp_namespace(struct inode *inode)
+{
+	struct ncp_server *server = NCP_SERVER(inode);
+	return server->name_space[NCP_FINFO(inode)->volNumber];
+}
+
+static inline int ncp_preserve_case(struct inode *i)
+{
+	/* If we can get case-sensitive server lookups working, then
+	 *
+	 *  return (ncp_namespace(i) == NW_NS_OS2);
+	 */
+	return 0;
+}
 
 #endif				/* __KERNEL__ */
 
diff -u --recursive --new-file v2.1.62/linux/include/linux/nfs_fs.h linux/include/linux/nfs_fs.h
--- v2.1.62/linux/include/linux/nfs_fs.h	Sat Nov  1 11:04:27 1997
+++ linux/include/linux/nfs_fs.h	Sat Nov  8 11:33:05 1997
@@ -133,12 +133,11 @@
 /*
  * linux/fs/nfs/inode.c
  */
-extern struct super_block *nfs_read_super(struct super_block *sb, 
-					  void *data,int);
+extern struct super_block *nfs_read_super(struct super_block *, void *, int);
 extern int init_nfs_fs(void);
-extern struct inode *nfs_fhget(struct super_block *sb, struct nfs_fh *fhandle,
-			       struct nfs_fattr *fattr);
-extern int nfs_refresh_inode(struct inode *inode, struct nfs_fattr *fattr);
+extern struct inode *nfs_fhget(struct super_block *, struct nfs_fh *,
+			       struct nfs_fattr *);
+extern int nfs_refresh_inode(struct inode *, struct nfs_fattr *);
 extern int nfs_revalidate(struct inode *);
 extern int _nfs_revalidate_inode(struct nfs_server *, struct inode *);
 
@@ -151,20 +150,14 @@
  * linux/fs/nfs/dir.c
  */
 extern struct inode_operations nfs_dir_inode_operations;
-extern void nfs_sillyrename_cleanup(struct inode *);
 extern void nfs_free_dircache(void);
 extern void nfs_invalidate_dircache(struct inode *);
+extern void nfs_invalidate_dircache_sb(struct super_block *);
 
 /*
  * linux/fs/nfs/symlink.c
  */
 extern struct inode_operations nfs_symlink_inode_operations;
-
-/*
- * linux/fs/nfs/mmap.c
- */
-extern int nfs_mmap(struct inode *inode, struct file *file,
-			struct vm_area_struct * vma);
 
 /*
  * linux/fs/nfs/locks.c
diff -u --recursive --new-file v2.1.62/linux/include/linux/proc_fs.h linux/include/linux/proc_fs.h
--- v2.1.62/linux/include/linux/proc_fs.h	Wed Oct 15 16:04:24 1997
+++ linux/include/linux/proc_fs.h	Wed Nov  5 13:15:47 1997
@@ -147,6 +147,9 @@
 enum scsi_directory_inos {
 	PROC_SCSI_SCSI = 256,
 	PROC_SCSI_ADVANSYS,
+	PROC_SCSI_PCI2000,
+	PROC_SCSI_PCI2220I,
+	PROC_SCSI_PSI240I,
 	PROC_SCSI_EATA,
 	PROC_SCSI_EATA_PIO,
 	PROC_SCSI_AHA152X,
@@ -156,6 +159,7 @@
 	PROC_SCSI_BUSLOGIC,
 	PROC_SCSI_U14_34F,
 	PROC_SCSI_FDOMAIN,
+	PROC_SCSI_GDTH,
 	PROC_SCSI_GENERIC_NCR5380,
 	PROC_SCSI_IN2000,
 	PROC_SCSI_PAS16,
diff -u --recursive --new-file v2.1.62/linux/include/linux/radio.h linux/include/linux/radio.h
--- v2.1.62/linux/include/linux/radio.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/radio.h	Tue Nov  4 10:23:25 1997
@@ -0,0 +1,134 @@
+/*
+ * linux/radio.h
+ *
+ * Include for radio card support under linux
+ * Another pointless suid-binary removal utility... :-)
+ */
+
+#ifndef _LINUX_RADIO_H
+#define _LINUX_RADIO_H
+
+#include <linux/ioctl.h>
+
+/*
+ * Constants
+ */
+/* Various types of Radio card... */
+/* (NB.  I've made this a bit-field.  It might make the difference one day.) */
+#define	RADIO_TYPE_UNSUP	0x0000
+#define	RADIO_TYPE_RTRACK	0x0001	/* AIMSlab RadioTrack (RadioReveal) card -- basic, to say the least */
+#define	RADIO_TYPE_WINRADIO	0x0002	/* Dunno, but made by someone */
+#define	RADIO_TYPE_TYPHOON	0x0004	/* It exists... */
+
+/* waveband types */
+#define	RADIO_PROTOCOL_AM	0x0010	/* AM "protocol" */
+#define	RADIO_PROTOCOL_FM	0x0020	/* FM "protocol" */
+#define	RADIO_PROTOCOL_SSB	0x0040	/* SSB */
+/* and no doubt some other stuff, too (Brian?) */
+
+
+/* the following are _very_ inaccurate; essentially, all that
+ * they do is provide a "name" for client programs
+ */
+#define	RADIO_BAND_UNKNOWN	0x0000	/* other */
+#define	RADIO_BAND_AM_SW	0x0100	/* short wave (?) */
+#define	RADIO_BAND_AM_MW	0x0200	/* medium wave (540 - 1600) */
+#define	RADIO_BAND_AM_LW	0x0400	/* long wave (150 - 270) */
+#define	RADIO_BAND_FM_STD	0x1000	/* "standard" FM band (i.e.  88 - 108 or so) */
+
+
+/* Since floating-point stuff is illegal in the kernel, we use these
+ * pairs of macros to convert to, and from userland floats
+ * (I hope these are general enough!)
+ */
+/* Remember to make sure that all of these are integral... */
+/* Also remember to pass sensible things in here (MHz for FM, kHz for AM) */
+#define	RADIO_FM_RES		100	/* 0.01 MHZ */
+#define	RADIO_FM_FRTOINT(fl)	((int)(((float)(fl))*RADIO_FM_RES))
+#define	RADIO_FM_INTTOFR(fr)	((float)(((int)(fr))/RADIO_FM_RES))
+
+/* Old RadioTrack definitions
+#define RADIO_FM_FRTOINT(fl)	((int)(((float)(fl)-88.0)*40)+0xf6c)
+#define RADIO_FM_INTTOFR(fr)	((float)(((fr)-0xf6c)/40)+88.0)
+*/
+
+#define	RADIO_AM_RES		1	/* 1 kHz */
+#define	RADIO_AM_FRTOINT(fl)	((int)(((float)(fl))*RADIO_AM_RES))
+#define	RADIO_AM_INTTOFR(fr)	((float)(((int)(fr))/RADIO_AM_RES))
+
+
+/*
+ * Structures 
+ */
+/* query structures */
+struct radio_cap {
+	int dev_num;			/* device index */
+	int type;			/* device type (see above) */
+	int num_bwidths;		/* number of "bandwidths" supported */
+	int volmin, volmax;		/* min/max in steps of one */
+};
+
+struct radio_band {
+	int dev_num;			/* device index (IN) */
+	int index;			/* "bandwidth" index (IN) */
+	int proto;			/* protocol (AM, FM, SSB, etc) (OUT) */
+	int types;			/* see RADIO_BAND_* above */
+	int freqmin,freqmax;		/* encoded according to the macros above */
+	int strmin,strmax;		/* min/max signal strength (steps of 1) */
+};
+
+/* Previously, this was in four separate structures:
+ * radio_vol, radio_freq, radio_band and radio_sigstr,
+ * That was foolish, but now it's not so obvious what's going on.
+ * Be careful.
+ */
+
+struct radio_ctl {
+	int dev_num;			/* device index (IN) */
+	int value;			/* volume, frequency, band, sigstr */
+};
+
+
+/*
+ * ioctl numbers
+ */
+/* You have _how_ many radio devices? =) */
+#define	RADIO_NUMDEVS	_IOR(0x8c, 0x00, int)
+#define	RADIO_GETCAPS	_IOR(0x8c, 0x01, struct radio_cap)
+#define	RADIO_GETBNDCAP	_IOR(0x8c, 0x02, struct radio_band)
+
+#define	RADIO_SETVOL	_IOW(0x8c, 0x10, struct radio_ctl)
+#define	RADIO_GETVOL	_IOR(0x8c, 0x11, struct radio_ctl)
+#define	RADIO_SETBAND	_IOW(0x8c, 0x12, struct radio_ctl)
+#define	RADIO_GETBAND	_IOR(0x8c, 0x13, struct radio_ctl)
+#define	RADIO_SETFREQ	_IOW(0x8c, 0x14, struct radio_ctl)
+#define	RADIO_GETFREQ	_IOR(0x8c, 0x15, struct radio_ctl)
+
+#define	RADIO_GETSIGSTR	_IOR(0x8c, 0x30, struct radio_ctl)
+
+/* kernel specific stuff... */
+#ifdef __KERNEL__
+/* Try to keep the number of function pointers to a minimum.
+ * Devices are responsible for updating, or otherwise, the
+ * variables here, not the outside wrapper.
+ */
+struct radio_device;
+
+int radio_add_device(struct radio_device *newdev);
+
+struct radio_device {
+	struct radio_cap *cap;
+	struct radio_band *bands;	/* pointer to array of radio_bands */
+	int (*setvol)(struct radio_device*,int);
+	int curvol;
+	int (*setband)(struct radio_device*,int);
+	int curband;
+	int (*setfreq)(struct radio_device*,int);
+	int curfreq;
+	int (*getsigstr)(struct radio_device*);
+	struct radio_device *next;
+	void *misc;		/* device internal storage... (eg i/o addresses, etc */
+};
+#endif /* __KERNEL__ */
+
+#endif /* _LINUX_RADIO_H */
diff -u --recursive --new-file v2.1.62/linux/include/linux/raid1.h linux/include/linux/raid1.h
--- v2.1.62/linux/include/linux/raid1.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/raid1.h	Sat Nov  8 11:39:13 1997
@@ -0,0 +1,49 @@
+#ifndef _RAID1_H
+#define _RAID1_H
+
+#include <linux/md.h>
+
+struct mirror_info {
+	int		number;
+	int		raid_disk;
+	kdev_t		dev;
+	int		next;
+	int		sect_limit;
+
+	/*
+	 * State bits:
+	 */
+	int		operational;
+	int		write_only;
+	int		spare;
+};
+
+struct raid1_data {
+	struct md_dev *mddev;
+	struct mirror_info mirrors[MD_SB_DISKS];  	/* RAID1 devices, 2 to MD_SB_DISKS */
+	int raid_disks;
+	int working_disks;			/* Number of working disks */
+	int last_used;
+	unsigned long	next_sect;
+	int		sect_count;
+	int resync_running;
+};
+
+/*
+ * this is our 'private' 'collective' RAID1 buffer head.
+ * it contains information about what kind of IO operations were started
+ * for this RAID5 operation, and about their status:
+ */
+
+struct raid1_bh {
+	unsigned int		remaining;
+	unsigned int		state;
+	int			cmd;
+	struct md_dev		*mddev;
+	struct buffer_head	*master_bh;
+	struct buffer_head	*mirror_bh [MD_SB_DISKS];
+	struct buffer_head	bh_req;
+	struct buffer_head	*next_retry;
+};
+
+#endif
diff -u --recursive --new-file v2.1.62/linux/include/linux/raid5.h linux/include/linux/raid5.h
--- v2.1.62/linux/include/linux/raid5.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/raid5.h	Sat Nov  8 11:39:13 1997
@@ -0,0 +1,110 @@
+#ifndef _RAID5_H
+#define _RAID5_H
+
+#ifdef __KERNEL__
+#include <linux/md.h>
+#include <asm/atomic.h>
+
+struct disk_info {
+	kdev_t	dev;
+	int	operational;
+	int	number;
+	int	raid_disk;
+	int	write_only;
+	int	spare;
+};
+
+struct stripe_head {
+	struct stripe_head	*hash_next, **hash_pprev; /* hash pointers */
+	struct stripe_head	*free_next;		/* pool of free sh's */
+	struct buffer_head	*buffer_pool;		/* pool of free buffers */
+	struct buffer_head	*bh_pool;		/* pool of free bh's */
+	struct raid5_data	*raid_conf;
+	struct buffer_head	*bh_old[MD_SB_DISKS];	/* disk image */
+	struct buffer_head	*bh_new[MD_SB_DISKS];	/* buffers of the MD device (present in buffer cache) */
+	struct buffer_head	*bh_copy[MD_SB_DISKS];	/* copy on write of bh_new (bh_new can change from under us) */
+	struct buffer_head	*bh_req[MD_SB_DISKS];	/* copy of bh_new (only the buffer heads), queued to the lower levels */
+	int			cmd_new[MD_SB_DISKS];	/* READ/WRITE for new */
+	int			new[MD_SB_DISKS];	/* buffer added since the last handle_stripe() */
+	unsigned long		sector;			/* sector of this row */
+	int			size;			/* buffers size */
+	int			pd_idx;			/* parity disk index */
+	int			nr_pending;		/* nr of pending cmds */
+	__u32			state;			/* state flags */
+	int			cmd;			/* stripe cmd */
+	int			count;			/* nr of waiters */
+	int			write_method;		/* reconstruct-write / read-modify-write */
+	int			phase;			/* PHASE_BEGIN, ..., PHASE_COMPLETE */
+	struct wait_queue	*wait;			/* processes waiting for this stripe */
+};
+
+/*
+ * Phase
+ */
+#define PHASE_BEGIN		0
+#define PHASE_READ_OLD		1
+#define PHASE_WRITE		2
+#define PHASE_READ		3
+#define PHASE_COMPLETE		4
+
+/*
+ * Write method
+ */
+#define METHOD_NONE		0
+#define RECONSTRUCT_WRITE	1
+#define READ_MODIFY_WRITE	2
+
+/*
+ * Stripe state
+ */
+#define STRIPE_LOCKED		0
+#define STRIPE_ERROR		1
+
+/*
+ * Stripe commands
+ */
+#define STRIPE_NONE		0
+#define	STRIPE_WRITE		1
+#define STRIPE_READ		2
+
+struct raid5_data {
+	struct stripe_head	**stripe_hashtbl;
+	struct md_dev		*mddev;
+	struct md_thread	*thread, *resync_thread;
+	struct disk_info	disks[MD_SB_DISKS];
+	struct disk_info	*spare;
+	int			buffer_size;
+	int			chunk_size, level, algorithm;
+	int			raid_disks, working_disks, failed_disks;
+	int			sector_count;
+	unsigned long		next_sector;
+	atomic_t		nr_handle;
+	struct stripe_head	*next_free_stripe;
+	int			nr_stripes;
+	int			resync_parity;
+	int			max_nr_stripes;
+	int			clock;
+	int			nr_hashed_stripes;
+	int			nr_locked_stripes;
+	int			nr_pending_stripes;
+	int			nr_cached_stripes;
+
+	/*
+	 * Free stripes pool
+	 */
+	int			nr_free_sh;
+	struct stripe_head	*free_sh_list;
+	struct wait_queue	*wait_for_stripe;
+};
+
+#endif
+
+/*
+ * Our supported algorithms
+ */
+#define ALGORITHM_LEFT_ASYMMETRIC	0
+#define ALGORITHM_RIGHT_ASYMMETRIC	1
+#define ALGORITHM_LEFT_SYMMETRIC	2
+#define ALGORITHM_RIGHT_SYMMETRIC	3
+
+#endif
diff -u --recursive --new-file v2.1.62/linux/include/linux/soundcard.h linux/include/linux/soundcard.h
--- v2.1.62/linux/include/linux/soundcard.h	Wed May 28 10:51:33 1997
+++ linux/include/linux/soundcard.h	Tue Sep 30 08:46:45 1997
@@ -33,7 +33,7 @@
  * Use ioctl(fd, OSS_GETVERSION, &int) to get the version number of
  * the currently active driver.
  */
-#define SOUND_VERSION	0x030800
+#define SOUND_VERSION	0x030802
 #define OPEN_SOUND_SYSTEM
 
 /* In Linux we need to be prepared for cross compiling */
@@ -127,7 +127,7 @@
 #define SNDCTL_SEQ_GETOUTCOUNT		_SIOR ('Q', 4, int)
 #define SNDCTL_SEQ_GETINCOUNT		_SIOR ('Q', 5, int)
 #define SNDCTL_SEQ_PERCMODE		_SIOW ('Q', 6, int)
-#define SNDCTL_FM_LOAD_INSTR		_SIOW ('Q', 7, struct sbi_instrument)	/* Obsolete. Don't use. */
+#define SNDCTL_FM_LOAD_INSTR		_SIOW ('Q', 7, struct sbi_instrument)	/* Obsolete. Don't use!!!!!! */
 #define SNDCTL_SEQ_TESTMIDI		_SIOW ('Q', 8, int)
 #define SNDCTL_SEQ_RESETSAMPLES		_SIOW ('Q', 9, int)
 #define SNDCTL_SEQ_NRSYNTHS		_SIOR ('Q',10, int)
@@ -697,7 +697,7 @@
  * the devices supported by the particular mixer.
  */
 
-#define SOUND_MIXER_NRDEVICES	17
+#define SOUND_MIXER_NRDEVICES	25
 #define SOUND_MIXER_VOLUME	0
 #define SOUND_MIXER_BASS	1
 #define SOUND_MIXER_TREBLE	2
@@ -721,6 +721,14 @@
 #define SOUND_MIXER_LINE1	14	/* Input source 1  (aux1) */
 #define SOUND_MIXER_LINE2	15	/* Input source 2  (aux2) */
 #define SOUND_MIXER_LINE3	16	/* Input source 3  (line) */
+#define SOUND_MIXER_DIGITAL1	17	/* Digital (input) 1 */
+#define SOUND_MIXER_DIGITAL2	18	/* Digital (input) 2 */
+#define SOUND_MIXER_DIGITAL3	19	/* Digital (input) 3 */
+#define SOUND_MIXER_PHONEIN	20	/* Phone input */
+#define SOUND_MIXER_PHONEOUT	21	/* Phone output */
+#define SOUND_MIXER_VIDEO	22	/* Video/TV (audio) in */
+#define SOUND_MIXER_RADIO	23	/* Radio in */
+#define SOUND_MIXER_MONITOR	24	/* Monitor (usually mic) volume */
 
 /* Some on/off settings (SOUND_SPECIAL_MIN - SOUND_SPECIAL_MAX) */
 /* Not counted to SOUND_MIXER_NRDEVICES, but use the same number space */
@@ -741,11 +749,13 @@
 
 #define SOUND_DEVICE_LABELS	{"Vol  ", "Bass ", "Trebl", "Synth", "Pcm  ", "Spkr ", "Line ", \
 				 "Mic  ", "CD   ", "Mix  ", "Pcm2 ", "Rec  ", "IGain", "OGain", \
-				 "Line1", "Line2", "Line3"}
+				 "Line1", "Line2", "Line3", "Digital1", "Digital2", "Digital3", \
+				 "PhoneIn", "PhoneOut", "Video", "Radio", "Monitor"}
 
 #define SOUND_DEVICE_NAMES	{"vol", "bass", "treble", "synth", "pcm", "speaker", "line", \
 				 "mic", "cd", "mix", "pcm2", "rec", "igain", "ogain", \
-				 "line1", "line2", "line3"}
+				 "line1", "line2", "line3", "dig1", "dig2", "dig3", \
+				 "phin", "phout", "video", "radio", "monitor"}
 
 /*	Device bitmask identifiers	*/
 
@@ -775,6 +785,14 @@
 #define SOUND_MASK_LINE1	(1 << SOUND_MIXER_LINE1)
 #define SOUND_MASK_LINE2	(1 << SOUND_MIXER_LINE2)
 #define SOUND_MASK_LINE3	(1 << SOUND_MIXER_LINE3)
+#define SOUND_MASK_DIGITAL1	(1 << SOUND_MIXER_DIGITAL1)
+#define SOUND_MASK_DIGITAL2	(1 << SOUND_MIXER_DIGITAL2)
+#define SOUND_MASK_DIGITAL3	(1 << SOUND_MIXER_DIGITAL3)
+#define SOUND_MASK_PHONEIN	(1 << SOUND_MIXER_PHONEIN)
+#define SOUND_MASK_PHONEOUT	(1 << SOUND_MIXER_PHONEOUT)
+#define SOUND_MASK_RADIO	(1 << SOUND_MIXER_RADIO)
+#define SOUND_MASK_VIDEO	(1 << SOUND_MIXER_VIDEO)
+#define SOUND_MASK_MONITOR	(1 << SOUND_MIXER_MONITOR)
 
 /* Obsolete macros */
 #define SOUND_MASK_MUTE		(1 << SOUND_MIXER_MUTE)
diff -u --recursive --new-file v2.1.62/linux/include/net/ipx.h linux/include/net/ipx.h
--- v2.1.62/linux/include/net/ipx.h	Mon Jul  7 16:02:45 1997
+++ linux/include/net/ipx.h	Sun Nov  9 10:13:48 1997
@@ -18,9 +18,9 @@
 
 typedef struct
 {
-	unsigned long net;
-	unsigned char  node[IPX_NODE_LEN]; 
-	unsigned short sock;
+	__u32   net;
+	__u8    node[IPX_NODE_LEN]; 
+	__u16   sock;
 } ipx_address;
 
 #define ipx_broadcast_node	"\377\377\377\377\377\377"
@@ -28,11 +28,11 @@
 
 struct ipxhdr
 {
-	unsigned short	ipx_checksum __attribute__ ((packed));
+	__u16           ipx_checksum __attribute__ ((packed));
 #define IPX_NO_CHECKSUM	0xFFFF
-	unsigned short  ipx_pktsize __attribute__ ((packed));
-	unsigned char   ipx_tctrl;
-	unsigned char   ipx_type;
+	__u16           ipx_pktsize __attribute__ ((packed));
+	__u8            ipx_tctrl;
+	__u8            ipx_type;
 #define IPX_TYPE_UNKNOWN	0x00
 #define IPX_TYPE_RIP		0x01	/* may also be 0 */
 #define IPX_TYPE_SAP		0x04	/* may also be 0 */
@@ -49,7 +49,7 @@
 
 typedef struct ipx_interface {
 	/* IPX address */
-	unsigned long	if_netnum;
+	__u32           if_netnum;
 	unsigned char	if_node[IPX_NODE_LEN];
 
 	/* physical device info */
@@ -70,7 +70,7 @@
 }	ipx_interface;
 
 typedef struct ipx_route {
-	unsigned long ir_net;
+	__u32         ir_net;
 	ipx_interface *ir_intrfc;
 	unsigned char ir_routed;
 	unsigned char ir_router_node[IPX_NODE_LEN];
diff -u --recursive --new-file v2.1.62/linux/include/scsi/scsi.h linux/include/scsi/scsi.h
--- v2.1.62/linux/include/scsi/scsi.h	Fri Apr  4 08:52:26 1997
+++ linux/include/scsi/scsi.h	Fri Nov  7 10:14:49 1997
@@ -180,6 +180,8 @@
 /* Used to obtain the host number of a device. */
 #define SCSI_IOCTL_PROBE_HOST 0x5385
 
+/* Used to get the bus number for a device */
+#define SCSI_IOCTL_GET_BUS_NUMBER 0x5386
 
 /*
  * Overrides for Emacs so that we follow Linus's tabbing style.
diff -u --recursive --new-file v2.1.62/linux/init/main.c linux/init/main.c
--- v2.1.62/linux/init/main.c	Sun Sep  7 13:10:43 1997
+++ linux/init/main.c	Tue Nov  4 09:17:30 1997
@@ -117,6 +117,7 @@
 extern void generic_NCR53C400_setup(char *str, int *intr);
 extern void aha152x_setup(char *str, int *ints);
 extern void aha1542_setup(char *str, int *ints);
+extern void gdth_setup(char *str, int *ints);
 extern void aic7xxx_setup(char *str, int *ints);
 extern void AM53C974_setup(char *str, int *ints);
 extern void BusLogic_Setup(char *str, int *ints);
@@ -404,6 +405,9 @@
 #endif
 #ifdef CONFIG_SCSI_AHA1542
 	{ "aha1542=", aha1542_setup},
+#endif
+#ifdef CONFIG_SCSI_GDTH
+	{ "gdth=", gdth_setup},
 #endif
 #ifdef CONFIG_SCSI_AIC7XXX
 	{ "aic7xxx=", aic7xxx_setup},
diff -u --recursive --new-file v2.1.62/linux/kernel/ksyms.c linux/kernel/ksyms.c
--- v2.1.62/linux/kernel/ksyms.c	Mon Nov  3 13:04:27 1997
+++ linux/kernel/ksyms.c	Sat Nov  8 11:39:13 1997
@@ -184,7 +184,6 @@
 EXPORT_SYMBOL(ll_rw_block);
 EXPORT_SYMBOL(__wait_on_buffer);
 EXPORT_SYMBOL(mark_buffer_uptodate);
-EXPORT_SYMBOL(unlock_buffer);
 EXPORT_SYMBOL(add_blkdev_randomness);
 EXPORT_SYMBOL(generic_file_read);
 EXPORT_SYMBOL(generic_file_write);
@@ -232,6 +231,10 @@
 EXPORT_SYMBOL(gendisk_head);
 EXPORT_SYMBOL(resetup_one_dev);
 EXPORT_SYMBOL(unplug_device);
+EXPORT_SYMBOL(make_request);
+EXPORT_SYMBOL(tq_disk);
+EXPORT_SYMBOL(efind_buffer);
+EXPORT_SYMBOL(init_buffer);
 
 /* tty routines */
 EXPORT_SYMBOL(tty_hangup);
diff -u --recursive --new-file v2.1.62/linux/mm/filemap.c linux/mm/filemap.c
--- v2.1.62/linux/mm/filemap.c	Sat Oct 25 02:44:18 1997
+++ linux/mm/filemap.c	Sat Nov  8 11:39:13 1997
@@ -24,6 +24,7 @@
 #include <linux/swap.h>
 #include <linux/smp.h>
 #include <linux/smp_lock.h>
+#include <linux/blkdev.h>
 
 #include <asm/system.h>
 #include <asm/pgtable.h>
@@ -445,12 +446,20 @@
 #define MIN_READAHEAD PageAlignSize(4096*3)
 #endif
 
+static inline int get_max_readahead(struct inode * inode)
+{
+	if (!inode->i_dev || !max_readahead[MAJOR(inode->i_dev)])
+		return MAX_READAHEAD;
+	return max_readahead[MAJOR(inode->i_dev)][MINOR(inode->i_dev)];
+}
+
 static inline unsigned long generic_file_readahead(int reada_ok, struct file * filp, struct inode * inode,
 	unsigned long ppos, struct page * page,
 	unsigned long page_cache)
 {
 	unsigned long max_ahead, ahead;
 	unsigned long raend;
+	int max_readahead = get_max_readahead(inode);
 
 	raend = filp->f_raend & PAGE_MASK;
 	max_ahead = 0;
@@ -534,8 +543,8 @@
 
 		filp->f_ramax += filp->f_ramax;
 
-		if (filp->f_ramax > MAX_READAHEAD)
-			filp->f_ramax = MAX_READAHEAD;
+		if (filp->f_ramax > max_readahead)
+			filp->f_ramax = max_readahead;
 
 #ifdef PROFILE_READAHEAD
 		profile_readahead((reada_ok == 2), filp);
@@ -562,6 +571,7 @@
 	ssize_t error, read;
 	size_t pos, pgpos, page_cache;
 	int reada_ok;
+	int max_readahead = get_max_readahead(inode);
 
 	if (!access_ok(VERIFY_WRITE, buf, count))
 		return -EFAULT;
@@ -608,8 +618,8 @@
 
 		if (reada_ok && filp->f_ramax < MIN_READAHEAD)
 				filp->f_ramax = MIN_READAHEAD;
-		if (filp->f_ramax > MAX_READAHEAD)
-			filp->f_ramax = MAX_READAHEAD;
+		if (filp->f_ramax > max_readahead)
+			filp->f_ramax = max_readahead;
 	}
 
 	for (;;) {
diff -u --recursive --new-file v2.1.62/linux/net/ipv4/ip_fragment.c linux/net/ipv4/ip_fragment.c
--- v2.1.62/linux/net/ipv4/ip_fragment.c	Sun Sep  7 13:10:43 1997
+++ linux/net/ipv4/ip_fragment.c	Thu Nov  6 15:02:38 1997
@@ -14,6 +14,7 @@
  *		Alan Cox	:	Split from ip.c , see ip_input.c for history.
  *		David S. Miller :	Begin massive cleanup...
  *		Andi Kleen	:	Add sysctls.
+ *		xxxx		:	Overlapfrag bug.
  */
 
 #include <linux/types.h>
@@ -339,7 +340,7 @@
 	/* Copy the data portions of all fragments into the new buffer. */
 	fp = qp->fragments;
 	while(fp) {
-		if(count+fp->len > skb->len) {
+		if (fp->len < 0 || count+fp->len > skb->len) {
 			NETDEBUG(printk(KERN_ERR "Invalid fragment list: "
 					"Fragment over size.\n"));
 			ip_free(qp);
diff -u --recursive --new-file v2.1.62/linux/net/ipx/af_ipx.c linux/net/ipx/af_ipx.c
--- v2.1.62/linux/net/ipx/af_ipx.c	Thu Sep  4 17:07:32 1997
+++ linux/net/ipx/af_ipx.c	Sun Nov  9 10:13:48 1997
@@ -221,7 +221,7 @@
  * IPX interface is defined by a physical device and a frame type.
  */
 
-static ipx_route * ipxrtr_lookup(unsigned long);
+static ipx_route * ipxrtr_lookup(__u32);
 
 static void ipxitf_clear_primary_net(void)
 {
@@ -242,11 +242,11 @@
 	return i;
 }
 
-static ipx_interface *ipxitf_find_using_net(unsigned long net)
+static ipx_interface *ipxitf_find_using_net(__u32 net)
 {
 	ipx_interface	*i;
 
-	if (net == 0L)
+	if (!net)
 		return ipx_primary_net;
 
 	for (i=ipx_interfaces; i && (i->if_netnum!=net); i=i->if_next)
@@ -699,7 +699,7 @@
 	return 0;
 }
 
-static int ipxrtr_add_route(unsigned long, ipx_interface *, unsigned char *);
+static int ipxrtr_add_route(__u32, ipx_interface *, unsigned char *);
 
 static int ipxitf_add_local_route(ipx_interface *intrfc)
 {
@@ -726,9 +726,9 @@
 	}
 
 	/* See if we should update our network number */
-	if ((intrfc->if_netnum == 0L) &&
-		(ipx->ipx_source.net == ipx->ipx_dest.net) &&
-		(ipx->ipx_source.net != 0L))
+	if ( !intrfc->if_netnum &&  /* net number of intrfc not known yet (== 0) */
+		(ipx->ipx_source.net == ipx->ipx_dest.net) && /* intra-net packet */
+		ipx->ipx_source.net)  /* source net number of packet != 0 */
 	{
 		/* NB: NetWare servers lie about their hop count so we
 		 * dropped the test based on it.  This is the best way
@@ -742,7 +742,7 @@
 		else
 		{
 			printk(KERN_WARNING "IPX: Network number collision %lx\n        %s %s and %s %s\n",
-				htonl(ipx->ipx_source.net),
+				(long unsigned int) htonl(ipx->ipx_source.net),
 				ipx_device_name(i),
 				ipx_frame_name(i->if_dlink_type),
 				ipx_device_name(intrfc),
@@ -756,12 +756,12 @@
 		int i;
         	ipx_interface *ifcs;
 		struct sk_buff *skb2;
-  		long *l;
+  		__u32 *l;
 		char *c;
 		
 		c = (char *) skb->data;
 		c += sizeof( struct ipxhdr );
-		l = (long *) c;
+		l = (__u32 *) c;
 
 		i = 0;
 		/* 
@@ -780,7 +780,7 @@
 			for ( ifcs = ipx_interfaces; ifcs != NULL ; ifcs = ifcs->if_next) 
 			{
 				/* That aren't in the list */
-				l = (long *) c;
+				l = (__u32 *) c;
 				for( i = 0 ; i <= ipx->ipx_tctrl ; i++ )
 					if( ifcs->if_netnum == *l++ )
 						break;
@@ -804,9 +804,9 @@
 	}
 #endif
 
-	if (ipx->ipx_dest.net == 0L)
+	if (!ipx->ipx_dest.net)
 		ipx->ipx_dest.net = intrfc->if_netnum;
-	if (ipx->ipx_source.net == 0L)
+	if (!ipx->ipx_source.net)
 		ipx->ipx_source.net = intrfc->if_netnum;
 
 	if (intrfc->if_netnum != ipx->ipx_dest.net)
@@ -873,7 +873,7 @@
 		return -EEXIST;
 
 	/* Must have a valid network number */
-	if (idef->ipx_network == 0L)
+	if (!idef->ipx_network)
 		return -EADDRNOTAVAIL;
 	if (ipxitf_find_using_net(idef->ipx_network) != NULL)
 		return -EADDRINUSE;
@@ -927,7 +927,7 @@
 	if ((idef->ipx_special == IPX_PRIMARY) && (ipx_primary_net != NULL))
 		return -EEXIST;
 
-	if ((idef->ipx_network != 0L) &&
+	if (idef->ipx_network &&
 		(ipxitf_find_using_net(idef->ipx_network) != NULL))
 		return -EADDRINUSE;
 
@@ -1001,7 +1001,7 @@
 	}
 
 	/* If the network number is known, add a route */
-	if (intrfc->if_netnum == 0L)
+	if (!intrfc->if_netnum)
 		return 0;
 
 	return ipxitf_add_local_route(intrfc);
@@ -1075,7 +1075,7 @@
 	if (intrfc!=NULL) 
 	{
 		intrfc->if_dev=dev;
-		intrfc->if_netnum=0L;
+		intrfc->if_netnum=0;
 		intrfc->if_dlink_type = dlink_type;
 		intrfc->if_dlink = datalink;
 		intrfc->if_sklist = NULL;
@@ -1178,7 +1178,7 @@
 *														    *
 \*******************************************************************************************************************/
 
-static ipx_route *ipxrtr_lookup(unsigned long net)
+static ipx_route *ipxrtr_lookup(__u32 net)
 {
 	ipx_route *r;
 
@@ -1188,7 +1188,7 @@
 	return r;
 }
 
-static int ipxrtr_add_route(unsigned long network, ipx_interface *intrfc, unsigned char *node)
+static int ipxrtr_add_route(__u32 network, ipx_interface *intrfc, unsigned char *node)
 {
 	ipx_route	*rt;
 
@@ -1340,7 +1340,7 @@
 	int err;
 
 	/* Find the appropriate interface on which to send packet */
-	if ((usipx->sipx_network == 0L) && (ipx_primary_net != NULL))
+	if (!usipx->sipx_network && (ipx_primary_net != NULL))
 	{
 		usipx->sipx_network = ipx_primary_net->if_netnum;
 		intrfc = ipx_primary_net;
@@ -1519,7 +1519,7 @@
 	len += sprintf (buffer,"%-11s%-15s%-9s%-11s%s\n", "Network",
 		"Node_Address", "Primary", "Device", "Frame_Type");
 	for (i = ipx_interfaces; i != NULL; i = i->if_next) {
-		len += sprintf(buffer+len, "%08lX   ", ntohl(i->if_netnum));
+		len += sprintf(buffer+len, "%08lX   ", (long unsigned int)ntohl(i->if_netnum));
 		len += sprintf (buffer+len,"%02X%02X%02X%02X%02X%02X   ",
 				i->if_node[0], i->if_node[1], i->if_node[2],
 				i->if_node[3], i->if_node[4], i->if_node[5]);
@@ -1572,7 +1572,7 @@
 #ifdef CONFIG_IPX_INTERN
 			len += sprintf(buffer+len,
 				       "%08lX:%02X%02X%02X%02X%02X%02X:%04X  ",
-				 htonl(s->protinfo.af_ipx.intrfc->if_netnum),
+                                       (long unsigned int) htonl(s->protinfo.af_ipx.intrfc->if_netnum),
 				       s->protinfo.af_ipx.node[0],
 				       s->protinfo.af_ipx.node[1],
 				       s->protinfo.af_ipx.node[2],
@@ -1590,7 +1590,7 @@
 			} else {
 				len += sprintf (buffer+len,
 					"%08lX:%02X%02X%02X%02X%02X%02X:%04X  ",
-					htonl(s->protinfo.af_ipx.dest_addr.net),
+					(long unsigned int) htonl(s->protinfo.af_ipx.dest_addr.net),
 					s->protinfo.af_ipx.dest_addr.node[0],
 					s->protinfo.af_ipx.dest_addr.node[1],
 					s->protinfo.af_ipx.dest_addr.node[2],
@@ -1639,10 +1639,10 @@
 			"Network", "Router_Net", "Router_Node");
 	for (rt = ipx_routes; rt != NULL; rt = rt->ir_next)
 	{
-		len += sprintf (buffer+len,"%08lX   ", ntohl(rt->ir_net));
+		len += sprintf (buffer+len,"%08lX   ", (long unsigned int) ntohl(rt->ir_net));
 		if (rt->ir_routed) {
 			len += sprintf (buffer+len,"%08lX     %02X%02X%02X%02X%02X%02X\n",
-				ntohl(rt->ir_intrfc->if_netnum),
+				(long unsigned int) ntohl(rt->ir_intrfc->if_netnum),
 				rt->ir_router_node[0], rt->ir_router_node[1],
 				rt->ir_router_node[2], rt->ir_router_node[3],
 				rt->ir_router_node[4], rt->ir_router_node[5]);
@@ -1906,7 +1906,7 @@
 		int ret;
 
 		uaddr.sipx_port = 0;
-		uaddr.sipx_network = 0L;
+		uaddr.sipx_network = 0;
 #ifdef CONFIG_IPX_INTERN
 		memcpy(uaddr.sipx_node, sk->protinfo.af_ipx.intrfc->if_node,
 		       IPX_NODE_LEN);
@@ -1966,7 +1966,7 @@
 #endif
 
 		} else {
-			sipx.sipx_network = 0L;
+			sipx.sipx_network = 0;
 			memset(sipx.sipx_node, '\0', IPX_NODE_LEN);
 		}
 		sipx.sipx_port = sk->protinfo.af_ipx.port;
@@ -2022,8 +2022,8 @@
 
 void dump_addr(char *str,ipx_address *p)
 {
-	printk(KERN_DEBUG"%s: %08X:%02X%02X%02X%02X%02X%02X:%04X\n",
-		str,ntohl(p->net),p->node[0],p->node[1],p->node[2],
+	printk(KERN_DEBUG"%s: %08lX:%02X%02X%02X%02X%02X%02X:%04X\n",
+		str,(long unsigned int)ntohl(p->net),p->node[0],p->node[1],p->node[2],
 		p->node[3],p->node[4],p->node[5],ntohs(p->sock));
 }
 
