diff -u --recursive --new-file v2.1.87/linux/CREDITS linux/CREDITS
--- v2.1.87/linux/CREDITS	Tue Feb 17 13:12:43 1998
+++ linux/CREDITS	Thu Feb 19 14:58:40 1998
@@ -836,6 +836,12 @@
 S: D-64283 Darmstadt
 S: Germany
 
+N: Russell King
+E: rmk@arm.uk.linux.org
+D: Linux/arm integrater, maintainer & hacker
+S: Burgh Heath, Tadworth, Surrey.
+S: England
+
 N: Olaf Kirch
 E: okir@monad.swb.de
 D: Author of the Linux Network Administrators' Guide
diff -u --recursive --new-file v2.1.87/linux/Documentation/devices.tex linux/Documentation/devices.tex
--- v2.1.87/linux/Documentation/devices.tex	Thu Dec  4 17:37:00 1997
+++ linux/Documentation/devices.tex	Tue Feb 17 20:11:25 1998
@@ -4,7 +4,7 @@
 % pages to print... :-)  If you're actually putting this in print, you
 % may wish to change these.
 %
-% $Id: devices.tex,v 1.4 1997/12/05 01:34:21 hpa Exp $
+% $Id: devices.tex,v 1.7 1998/02/18 04:07:45 hpa Exp $
 %
 \oddsidemargin=0in
 \textwidth=6.5in
@@ -50,7 +50,7 @@
 %
 \title{{\bf Linux Allocated Devices}}
 \author{Maintained by H. Peter Anvin $<$hpa@zytor.com$>$}
-\date{Last revised: December 4, 1997}
+\date{Last revised: February 17, 1998}
 \maketitle
 %
 \noindent
@@ -199,6 +199,7 @@
 \major{47}{}{char }{Comtrol Rocketport serial card -- alternate devices}
 \major{  }{}{block}{Reserved for parallel port ATAPI disk}
 \major{48}{}{char }{SDL RISCom serial card}
+\major{48}{--55}{block}{Reserved for Mylex DAC960 PCI RAID Controller}
 \major{49}{}{char }{SDL RISCom serial card -- alternate devices}
 \major{50}{}{char }{Reserved for GLINT}
 \major{51}{}{char }{Baycom radio modem}
@@ -243,8 +244,10 @@
 \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}{}{char }{Reserved for parallel port ATAPI tape}
-\major{97}{--119}{}{Unallocated}
+\major{96}{}{char }{Parallel port ATAPI tape}
+\major{97}{}{char }{Parallel port generic ATAPI interface}
+\major{98}{}{char }{Control and Measurement Device (comedi)}
+\major{99}{--119}{}{Unallocated}
 \major{120}{--127}{}{Local/experimental use}
 \major{128}{--239}{}{Unallocated}
 \major{240}{--254}{}{Local/experimental use}
@@ -299,6 +302,11 @@
 the position within the series.
 \end{itemize}
 
+\noindent
+In the future, it is likely that the PTY master multiplex ({\file
+/dev/ptmx}) device will be used to acquire a PTY on demand.  If so,
+the actual PTY masters will be unnamed devices.
+
 \begin{devicelist}
 \major{}{}{block}{Floppy disks}
 	\minor{0}{/dev/fd0}{Controller 1, drive 1 autodetect}
@@ -325,7 +333,7 @@
 	\minor{ 72}{/dev/fd?h1494}{5.25\tum\ 1494K in a 1200K drive}
 	\minor{ 92}{/dev/fd?h1600}{5.25\tum\ 1600K in a 1200K drive\1}
 	\minor{}{}{}
-	\minor{ 12}{/dev/fd?u360}{3.5\tum\ \num{4}{360}K Double Density}
+	\minor{ 12}{/dev/fd?u360}{3.5\tum\ \num{4}{360}K Double Density\2}
 	\minor{ 16}{/dev/fd?u720}{3.5\tum\ \num{4}{720}K Double Density\1}
 	\minor{120}{/dev/fd?u800}{3.5\tum\ \num{4}{800}K Double Density\2}
 	\minor{ 52}{/dev/fd?u820}{3.5\tum\ \num{4}{820}K Double Density}
@@ -366,7 +374,13 @@
 	\minor{1}{/dev/ttyp1}{Second PTY slave}
 	\minordots
 	\minor{255}{/dev/ttyef}{256th PTY slave}
-\\
+\end{devicelist}
+
+\noindent
+In the future, Linux may adopt the Unix98 naming scheme {\file
+/dev/pts/0}, {\file /dev/pts/1}, ...
+
+\begin{devicelist}
 \major{}{}{block}{First MFM, RLL and IDE hard disk/CD-ROM interface}
 	\minor{0}{/dev/hda}{Master: whole disk (or CD-ROM)}
 	\minor{64}{/dev/hdb}{Slave: whole disk (or CD-ROM)}
@@ -412,6 +426,7 @@
 \major{ 5}{}{char }{Alternate TTY devices}
 	\minor{0}{/dev/tty}{Current TTY device}
 	\minor{1}{/dev/console}{System console}
+	\minor{2}{/dev/ptmx}{PTY master multiplex}
 	\minor{64}{/dev/cua0}{Callout device corresponding to {\file ttyS0}}
 	\minordots
 	\minor{127}{/dev/cua63}{Callout device corresponding to {\file ttyS63}}
@@ -1087,8 +1102,10 @@
 \end{devicelist}
 
 \noindent
-Partitions are handled the same way as for IDE disks (see major number
-3).
+This device is obsolete and will be removed in a future version of
+Linux.  It has been replaced with the parallel port IDE disk driver at
+major number 45.  Partitions are handled the same way as for IDE disks
+(see major number 3).
 
 \begin{devicelist}
 \major{41}{}{char }{Yet Another Micro Monitor}
@@ -1098,6 +1115,11 @@
 	\minor{0}{/dev/bpcd}{BackPack CD-ROM}
 \end{devicelist}
 
+\noindent
+This device is obsolete and will be removed in a future version of
+Linux.  It has been replaced with the parallel port ATAPI CD-ROM
+driver at major number 46.
+
 \begin{devicelist}
 \major{42}{}{}{Demo/sample use}
 \end{devicelist}
@@ -1163,13 +1185,30 @@
 	\minordots
 	\minor{191}{/dev/ippp63}{64th SyncPPP device}
 	\minor{255}{/dev/isdninfo}{ISDN monitor interface}
+\\
+\major{  }{}{block}{Parallel port IDE disk devices}
+	\minor{0}{/dev/pda}{First parallel port IDE disk}
+	\minor{16}{/dev/pdb}{Second parallel port IDE disk}
+	\minor{32}{/dev/pdc}{Third parallel port IDE disk}
+	\minor{48}{/dev/pdd}{Fourth parallel port IDE disk}
 \end{devicelist}
 
+\noindent
+Partitions are handled in the same way as for IDE disks (see major
+number 3) except that the partition limit is 15 rather than 63 per
+disk.
+
 \begin{devicelist}
 \major{46}{}{char }{Comtrol Rocketport serial card}
 	\minor{0}{/dev/ttyR0}{First Rocketport port}
 	\minor{1}{/dev/ttyR1}{Second Rocketport port}
 	\minordots
+\\
+\major{  }{}{block}{Parallel port ATAPI CD-ROM devices}
+	\minor{0}{/dev/pcd0}{First parallel port ATAPI CD-ROM}
+	\minor{1}{/dev/pcd1}{Second parallel port ATAPI CD-ROM}
+	\minor{2}{/dev/pcd2}{Third parallel port ATAPI CD-ROM}
+	\minor{3}{/dev/pcd3}{Fourth parallel port ATAPI CD-ROM}
 \end{devicelist}
 
 \begin{devicelist}
@@ -1177,13 +1216,25 @@
 	\minor{0}{/dev/cur0}{Callout device corresponding to {\file ttyR0}}
 	\minor{1}{/dev/cur1}{Callout device corresponding to {\file ttyR1}}
 	\minordots
+\\
+\major{  }{}{block}{Parallel port ATAPI disk devices}
+	\minor{0}{/dev/pf0}{First parallel port ATAPI disk}
+	\minor{1}{/dev/pf1}{Second parallel port ATAPI disk}
+	\minor{2}{/dev/pf2}{Third parallel port ATAPI disk}
+	\minor{3}{/dev/pf3}{Fourth parallel port ATAPI disk}
 \end{devicelist}
 
+\noindent
+This driver is intended for floppy disks and similar devices and hence
+does not support partitioning.
+
 \begin{devicelist}
 \major{48}{}{char }{SDL RISCom serial card}
 	\minor{0}{/dev/ttyL0}{First RISCom port}
 	\minor{1}{/dev/ttyL1}{Second RISCom port}
 	\minordots
+\\
+\major{  }{}{block}{Reserved for Mylex DAC960 PCI RAID Controller}
 \end{devicelist}
 
 \begin{devicelist}
@@ -1191,10 +1242,14 @@
 	\minor{0}{/dev/cul0}{Callout device corresponding to {\file ttyL0}}
 	\minor{1}{/dev/cul1}{Callout device corresponding to {\file ttyL1}}
 	\minordots
+\\
+\major{  }{}{block}{Reserved for Mylex DAC960 PCI RAID Controller}
 \end{devicelist}
 
 \begin{devicelist}
 \major{50}{}{char}{Reserved for GLINT}
+\\
+\major{  }{}{block}{Reserved for Mylex DAC960 PCI RAID Controller}
 \end{devicelist}
 
 \begin{devicelist}
@@ -1202,6 +1257,8 @@
 	\minor{0}{/dev/bc0}{First Baycom radio modem}
 	\minor{1}{/dev/bc1}{Second Baycom radio modem}
 	\minordots
+\\
+\major{  }{}{block}{Reserved for Mylex DAC960 PCI RAID Controller}
 \end{devicelist}
 
 \begin{devicelist}
@@ -1210,6 +1267,8 @@
 	\minor{1}{/dev/dcbri1}{Second DataComm card}
 	\minor{2}{/dev/dcbri2}{Third DataComm card}
 	\minor{3}{/dev/dcbri3}{Fourth DataComm card}
+\\
+\major{  }{}{block}{Reserved for Mylex DAC960 PCI RAID Controller}
 \end{devicelist}
 
 \begin{devicelist}
@@ -1230,6 +1289,10 @@
 commercial interface by P\&E.
 
 \begin{devicelist}
+\major{  }{}{block}{Reserved for Mylex DAC960 PCI RAID Controller}
+\end{devicelist}
+
+\begin{devicelist}
 \major{54}{}{char }{Electrocardiognosis Holter serial card}
 	\minor{0}{/dev/holter0}{First Holter port}
 	\minor{1}{/dev/holter1}{Second Holter port}
@@ -1242,8 +1305,14 @@
 heart monitoring equipment.
 
 \begin{devicelist}
+\major{  }{}{block}{Reserved for Mylex DAC960 PCI RAID Controller}
+\end{devicelist}
+
+\begin{devicelist}
 \major{55}{}{char }{DSP56001 digital signal processor}
 	\minor{0}{/dev/dsp56k}{First DSP56001}
+\\
+\major{  }{}{block}{Reserved for Mylex DAC960 PCI RAID Controller}
 \end{devicelist}
 
 \begin{devicelist}
@@ -1582,7 +1651,41 @@
 \end{devicelist}
 
 \begin{devicelist}
-\major{96}{--119}{}{Unallocated}
+\major{96}{}{char }{Parallel port ATAPI tape devices}
+	\minor{0}{/dev/pt0}{First parallel port ATAPI tape}
+	\minor{1}{/dev/pt1}{Second parallel port ATAPI tape}
+	\minor{2}{/dev/pt2}{Third parallel port ATAPI tape}
+	\minor{3}{/dev/pt3}{Fourth parallel port ATAPI tape}
+	\minor{128}{/dev/npt0}{First parallel port ATAPI tape, no rewind}
+	\minor{129}{/dev/npt1}{Second parallel port ATAPI tape, no rewind}
+	\minor{130}{/dev/npt2}{Third parallel port ATAPI tape, no rewind}
+	\minor{131}{/dev/npt3}{Fourth parallel port ATAPI tape, no rewind}
+\end{devicelist}
+
+\begin{devicelist}
+\major{97}{}{char }{Parallel port generic ATAPI interface}
+	\minor{0}{/dev/pg0}{First parallel port ATAPI device}
+	\minor{1}{/dev/pg1}{Second parallel port ATAPI device}
+	\minor{2}{/dev/pg2}{Third parallel port ATAPI device}
+	\minor{3}{/dev/pg3}{Fourth parallel port ATAPI device}
+\end{devicelist}
+
+\noindent
+These devices support the same API as the generic SCSI devices.
+
+\begin{devicelist}
+\major{98}{}{char }{Control and Mesurement Device (comedi)}
+	\minor{0}{/dev/comedi0}{First comedi device}
+	\minor{1}{/dev/comedi1}{Second comedi device}
+	\minordots
+\end{devicelist}
+
+\noindent
+See {\url http://stm.lbl.gov/comedi/} or {\url
+http://www.llp.fu-berlin.de/}.
+
+\begin{devicelist}
+\major{99}{--119}{}{Unallocated}
 \end{devicelist}
 
 \begin{devicelist}
diff -u --recursive --new-file v2.1.87/linux/Documentation/devices.txt linux/Documentation/devices.txt
--- v2.1.87/linux/Documentation/devices.txt	Sun Jan  4 10:40:15 1998
+++ linux/Documentation/devices.txt	Tue Feb 17 20:11:25 1998
@@ -1,7 +1,7 @@
 		       LINUX ALLOCATED DEVICES
 	     Maintained by H. Peter Anvin <hpa@zytor.com>
 
-		    Last revised: December 4, 1997
+		   Last revised: February 17, 1998
 
 This list is the Linux Device List, the official registry of allocated
 device numbers and /dev directory nodes for the Linux operating
@@ -91,16 +91,17 @@
 		  the 1st through 16th series of 16 pseudo-ttys each, and
 		* the fifth letter is one of 0123456789abcdef indicating
 		  the position within the series.
+
+		In the future, it is likely that the PTY master
+		multiplex (/dev/ptmx) device will be used to acquire a
+		PTY on demand.  If so, the actual PTY masters will be
+		unnamed devices.
   
     block	Floppy disks
-		  0 = /dev/fd0		Controller 1, drive 1 autodetect
-		  1 = /dev/fd1		Controller 1, drive 2 autodetect
-		  2 = /dev/fd2		Controller 1, drive 3 autodetect
-		  3 = /dev/fd3		Controller 1, drive 4 autodetect
-		128 = /dev/fd4		Controller 2, drive 1 autodetect
-		129 = /dev/fd5		Controller 2, drive 2 autodetect
-		130 = /dev/fd6		Controller 2, drive 3 autodetect
-		131 = /dev/fd7		Controller 2, drive 4 autodetect
+		  0 = /dev/fd0		First floppy disk autodetect
+		  1 = /dev/fd1		Second floppy disk autodetect
+		  2 = /dev/fd2		Third floppy disk autodetect
+		  3 = /dev/fd3		Fourth floppy disk autodetect
 
 		To specify format, add to the autodetect device number:
 		  0 = /dev/fd?		Autodetect format
@@ -116,9 +117,10 @@
 		 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
-		120 = /dev/fd?u800	3.5"   800K Double Density(1)
-		 52 = /dev/fd?u820	3.5"   820K Double Density(2)
+		 12 = /dev/fd?u360	3.5"   360K Double Density(2)
+		 16 = /dev/fd?u720	3.5"   720K Double Density(1)
+		120 = /dev/fd?u800	3.5"   800K Double Density(2)
+		 52 = /dev/fd?u820	3.5"   820K Double Density
 		 68 = /dev/fd?u830	3.5"   830K Double Density
 		 84 = /dev/fd?u1040	3.5"  1040K Double Density(1)
 		 88 = /dev/fd?u1120	3.5"  1120K Double Density(1)
@@ -152,12 +154,15 @@
 		  0 = /dev/ttyp0	First PTY slave
 		  1 = /dev/ttyp1	Second PTY slave
 		    ...
-		256 = /dev/ttyef	256th PTY slave
+		255 = /dev/ttyef	256th PTY slave
+
+		In the future, Linux may adopt the Unix98 naming
+		scheme (/dev/pts/0, /dev/pts/1, ...)
 
     block	First MFM, RLL and IDE hard disk/CD-ROM interface
 		  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:
 		  0 = /dev/hd?		Whole disk
 		  1 = /dev/hd?1		First partition
@@ -169,7 +174,7 @@
 		partitions, and 5 and above are logical partitions.
 		Other versions of Linux use partitioning schemes
 		appropriate to their respective architectures.
-		
+
   4 char	TTY devices
 		  0 = /dev/tty0		Current virtual console
 
@@ -196,6 +201,7 @@
   5 char	Alternate TTY devices
 		  0 = /dev/tty		Current TTY device
 		  1 = /dev/console	System console
+		  2 = /dev/ptmx		PTY master multiplex
 		 64 = /dev/cua0		Callout device corresponding to ttyS0
 		      ...
 		127 = /dev/cua63	Callout device corresponding to ttyS63
@@ -742,6 +748,9 @@
     block	Syquest EZ135 parallel port removable drive
 		  0 = /dev/eza		Parallel EZ135 drive, whole disk
 
+		This device is obsolete and will be removed in a
+		future version of Linux.  It has been replaced with
+		the parallel port IDE disk driver at major number 45.
 		Partitions are handled in the same way as IDE disks
 		(see major number 3).
 
@@ -750,6 +759,10 @@
     block	MicroSolutions BackPack parallel port CD-ROM
 		  0 = /dev/bpcd		BackPack CD-ROM
 
+		This device is obsolete and will be removed in a
+		future version of Linux.  It has been replaced with
+		the parallel port ATAPI CD-ROM driver at major number 46.
+
  42		Demo/sample use
 
 		This number is intended for use in sample code, as
@@ -807,39 +820,66 @@
 		191 = /dev/ippp63	64th SyncPPP device
 
 		255 = /dev/isdninfo	ISDN monitor interface
+    block	Parallel port IDE disk devices
+		  0 = /dev/pda		First parallel port IDE disk
+		 16 = /dev/pdb		Second parallel port IDE disk
+		 32 = /dev/pdc		Third parallel port IDE disk
+		 48 = /dev/pdd		Fourth parallel port IDE disk
+
+		Partitions are handled in the same way as for IDE
+		disks (see major number 3) except that the partition
+		limit is 15 rather than 63 per disk.
 
  46 char	Comtrol Rocketport serial card
 		  0 = /dev/ttyR0	First Rocketport port
 		  1 = /dev/ttyR1	Second Rocketport port
 		      ...
+    block	Parallel port ATAPI CD-ROM devices
+		  0 = /dev/pcd0		First parallel port ATAPI CD-ROM
+		  1 = /dev/pcd1		Second parallel port ATAPI CD-ROM
+		  2 = /dev/pcd2		Third parallel port ATAPI CD-ROM
+		  3 = /dev/pcd3		Fourth parallel port ATAPI CD-ROM
 
  47 char	Comtrol Rocketport serial card - alternate devices
 		  0 = /dev/cur0		Callout device corresponding to ttyR0
 		  1 = /dev/cur1		Callout device corresponding to ttyR1
 		      ...
+    block	Parallel port ATAPI disk devices
+		  0 = /dev/pf0		First parallel port ATAPI disk
+		  1 = /dev/pf1		Second parallel port ATAPI disk
+		  2 = /dev/pf2		Third parallel port ATAPI disk
+		  3 = /dev/pf3		Fourth parallel port ATAPI disk
+
+		This driver is intended for floppy disks and similar
+		devices and hence does not support partitioning.
 
  48 char	SDL RISCom serial card
 		  0 = /dev/ttyL0	First RISCom port
 		  1 = /dev/ttyL1	Second RISCom port
 		      ...
+    block	Reserved for Mylex DAC960 PCI RAID controller
 
  49 char	SDL RISCom serial card - alternate devices
 		  0 = /dev/cul0		Callout device corresponding to ttyL0
 		  1 = /dev/cul1		Callout device corresponding to ttyL1
 		      ...
+    block	Reserved for Mylex DAC960 PCI RAID controller
 
  50 char	Reserved for GLINT
+    block	Reserved for Mylex DAC960 PCI RAID controller
 
  51 char	Baycom radio modem
 		  0 = /dev/bc0		First Baycom radio modem
 		  1 = /dev/bc1		Second Baycom radio modem
 		      ...
+    block	Reserved for Mylex DAC960 PCI RAID controller
 
  52 char	Spellcaster DataComm/BRI ISDN card
 		  0 = /dev/dcbri0	First DataComm card
 		  1 = /dev/dcbri1	Second DataComm card
 		  2 = /dev/dcbri2	Third DataComm card
 		  3 = /dev/dcbri3	Fourth DataComm card
+    block	Reserved for Mylex DAC960 PCI RAID controller
 
  53 char	BDM interface for remote debugging MC683xx microcontrollers
 		  0 = /dev/pd_bdm0	PD BDM interface on lp0
@@ -855,6 +895,8 @@
 		Domain Interface and ICD is the commercial interface
 		by P&E.
 
+    block	Reserved for Mylex DAC960 PCI RAID controller
+
  54 char	Electrocardiognosis Holter serial card
 		  0 = /dev/holter0	First Holter port
 		  1 = /dev/holter1	Second Holter port
@@ -864,8 +906,11 @@
 		<mseritan@ottonel.pub.ro> to transfer data from Holter
 		24-hour heart monitoring equipment.
 
+    block	Reserved for Mylex DAC960 PCI RAID controller
+
  55 char	DSP56001 digital signal processor
 		  0 = /dev/dsp56k	First DSP56001
+    block	Reserved for Mylex DAC960 PCI RAID controller
 
  56 char	Apple Desktop Bus
 		  0 = /dev/adb		ADB bus control
@@ -1120,7 +1165,33 @@
 		  2 = /dev/ipstate	State information log file
 		  3 = /dev/ipauth	Authentication control device/log file
 
- 96-119		UNALLOCATED
+ 96 char	Parallel port ATAPI tape devices
+		  0 = /dev/pt0		First parallel port ATAPI tape
+		  1 = /dev/pt1		Second parallel port ATAPI tape
+		  2 = /dev/pt2		Third parallel port ATAPI tape
+		  3 = /dev/pt3		Fourth parallel port ATAPI tape
+		128 = /dev/npt0		First p.p. ATAPI tape, no rewind
+		129 = /dev/npt1		Second p.p. ATAPI tape, no rewind
+		130 = /dev/npt2		Third p.p. ATAPI tape, no rewind
+		131 = /dev/npt3		Fourth p.p. ATAPI tape, no rewind
+
+ 97 char	Parallel port generic ATAPI interface
+		  0 = /dev/pg0		First parallel port ATAPI device
+		  1 = /dev/pg1		Second parallel port ATAPI device
+		  2 = /dev/pg2		Third parallel port ATAPI device
+		  3 = /dev/pg3		Fourth parallel port ATAPI device
+
+		These devices support the same API as the generic SCSI
+		devices.
+
+ 98 char	Control and Measurement Device (comedi)
+		  0 = /dev/comedi0	First comedi device
+		  1 = /dev/comedi1	Second comedi device
+		    ...
+
+		See http://stm.lbl.gov/comedi or http://www.llp.fu-berlin.de/.
+
+ 99-119		UNALLOCATED
 
 120-127		LOCAL/EXPERIMENTAL USE
 
diff -u --recursive --new-file v2.1.87/linux/Documentation/ioctl-number.txt linux/Documentation/ioctl-number.txt
--- v2.1.87/linux/Documentation/ioctl-number.txt	Tue Feb 17 13:12:43 1998
+++ linux/Documentation/ioctl-number.txt	Fri Feb 20 17:54:35 1998
@@ -1,5 +1,5 @@
 Ioctl Numbers
-28 Jan 1998
+18 Feb 1998
 Michael Chastain
 <mec@shout.net>
 
@@ -99,6 +99,8 @@
 'n'	all	linux/ncp_fs.h
 'p'	00-3F	linux/mc146818rtc.h
 'p'	40-7F	linux/nvram.h
+'p'	80-9F	user-space parport	in development:
+					<tim@cyberelk.demon.co.uk>
 'r'	all	linux/msdos_fs.h
 's'	all	linux/cdk.h
 't'	00-7F	linux/if_ppp.h
diff -u --recursive --new-file v2.1.87/linux/Makefile linux/Makefile
--- v2.1.87/linux/Makefile	Tue Feb 17 13:12:43 1998
+++ linux/Makefile	Fri Feb 20 18:27:17 1998
@@ -1,6 +1,6 @@
 VERSION = 2
 PATCHLEVEL = 1
-SUBLEVEL = 87
+SUBLEVEL = 88
 
 ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/)
 
@@ -402,7 +402,7 @@
 depend dep: dep-files $(MODVERFILE)
 
 checkconfig:
-	perl -w scripts/checkconfig.pl `find $(FINDHPATH) $(SUBDIRS) -name '*.[hcS]' -print | sort`
+	perl -w scripts/checkconfig.pl `find * -name '*.[hcS]' -print | sort`
 
 ifdef CONFIGURATION
 ..$(CONFIGURATION):
diff -u --recursive --new-file v2.1.87/linux/arch/alpha/kernel/irq.c linux/arch/alpha/kernel/irq.c
--- v2.1.87/linux/arch/alpha/kernel/irq.c	Wed Feb  4 11:35:59 1998
+++ linux/arch/alpha/kernel/irq.c	Thu Feb 19 14:58:40 1998
@@ -423,6 +423,16 @@
 #endif /* CONFIG_ALPHA_SABLE */
 }
 
+int check_irq(unsigned int irq)
+{
+	struct irqaction **p;
+
+	p = irq_action + irq;
+	if (*p == NULL)
+		return 0;
+	return -EBUSY;
+}
+
 int request_irq(unsigned int irq, 
 		void (*handler)(int, void *, struct pt_regs *),
 		unsigned long irqflags, 
diff -u --recursive --new-file v2.1.87/linux/arch/alpha/kernel/t2.c linux/arch/alpha/kernel/t2.c
--- v2.1.87/linux/arch/alpha/kernel/t2.c	Fri Jan 30 11:28:05 1998
+++ linux/arch/alpha/kernel/t2.c	Fri Feb 20 18:28:21 1998
@@ -8,7 +8,6 @@
  *
  */
 #include <linux/kernel.h>
-#include <linux/config.h>
 #include <linux/types.h>
 #include <linux/bios32.h>
 #include <linux/pci.h>
diff -u --recursive --new-file v2.1.87/linux/arch/arm/kernel/entry-armv.S linux/arch/arm/kernel/entry-armv.S
--- v2.1.87/linux/arch/arm/kernel/entry-armv.S	Tue Jan 20 16:39:41 1998
+++ linux/arch/arm/kernel/entry-armv.S	Fri Feb 20 18:28:21 1998
@@ -9,6 +9,7 @@
  * Note:  there is a StrongARM bug in the STMIA rn, {regs}^ instruction that causes
  * it to save wrong values...  Be aware!
  */
+#include <linux/config.h> /* for CONFIG_ARCH_EBSA110 /*
 #include <linux/autoconf.h>
 #include <linux/linkage.h>
 
diff -u --recursive --new-file v2.1.87/linux/arch/arm/kernel/head-armo.S linux/arch/arm/kernel/head-armo.S
--- v2.1.87/linux/arch/arm/kernel/head-armo.S	Tue Jan 20 16:39:41 1998
+++ linux/arch/arm/kernel/head-armo.S	Fri Feb 20 18:28:21 1998
@@ -5,7 +5,6 @@
  *
  * 26-bit kernel startup code
  */
-#include <linux/config.h>
 #include <linux/linkage.h>
 
 		.text
diff -u --recursive --new-file v2.1.87/linux/arch/arm/kernel/irq.c linux/arch/arm/kernel/irq.c
--- v2.1.87/linux/arch/arm/kernel/irq.c	Thu Feb 12 20:56:04 1998
+++ linux/arch/arm/kernel/irq.c	Fri Feb 20 18:28:21 1998
@@ -15,6 +15,7 @@
  * IRQ's are in fact implemented a bit like signal handlers for the kernel.
  * Naturally it's not a 1:1 relation, but there are similarities.
  */
+#include <linux/config.h> /* for CONFIG_DEBUG_ERRORS */
 #include <linux/ptrace.h>
 #include <linux/errno.h>
 #include <linux/kernel_stat.h>
diff -u --recursive --new-file v2.1.87/linux/arch/arm/kernel/signal.c linux/arch/arm/kernel/signal.c
--- v2.1.87/linux/arch/arm/kernel/signal.c	Tue Jan 20 16:39:41 1998
+++ linux/arch/arm/kernel/signal.c	Fri Feb 20 18:28:21 1998
@@ -4,6 +4,7 @@
  *  Copyright (C) 1995, 1996 Russell King
  */
 
+#include <linux/config.h> /* for CONFIG_CPU_ARM6 and CONFIG_CPU_SA110 */
 #include <linux/sched.h>
 #include <linux/mm.h>
 #include <linux/smp.h>
diff -u --recursive --new-file v2.1.87/linux/arch/arm/lib/io-acorn.S linux/arch/arm/lib/io-acorn.S
--- v2.1.87/linux/arch/arm/lib/io-acorn.S	Tue Jan 20 16:39:41 1998
+++ linux/arch/arm/lib/io-acorn.S	Fri Feb 20 18:28:21 1998
@@ -3,6 +3,7 @@
  *
  * Copyright (C) 1995, 1996 Russell King
  */
+#include <linux/config.h> /* for CONFIG_CPU_ARM2 and CONFIG_CPU_ARM3 */
 #include <linux/autoconf.h>
 #include <linux/linkage.h>
 #include <asm/assembler.h>
diff -u --recursive --new-file v2.1.87/linux/arch/arm/lib/system.S linux/arch/arm/lib/system.S
--- v2.1.87/linux/arch/arm/lib/system.S	Tue Jan 20 16:39:42 1998
+++ linux/arch/arm/lib/system.S	Fri Feb 20 18:28:21 1998
@@ -6,7 +6,6 @@
  * 07/06/96: Now support tasks running in SVC mode.
  */
 #include <linux/linkage.h>
-#include <linux/config.h>
 #include <asm/assembler.h>
 
 		.text
diff -u --recursive --new-file v2.1.87/linux/arch/i386/boot/bootsect.S linux/arch/i386/boot/bootsect.S
--- v2.1.87/linux/arch/i386/boot/bootsect.S	Wed Apr 16 14:14:59 1997
+++ linux/arch/i386/boot/bootsect.S	Fri Feb 20 18:28:21 1998
@@ -22,6 +22,7 @@
 ! read errors will result in a unbreakable loop. Reboot by hand. It
 ! loads pretty fast by getting whole tracks at a time whenever possible.
 
+#include <linux/config.h> /* for CONFIG_ROOT_RDONLY */
 #include <asm/boot.h>
 
 .text
diff -u --recursive --new-file v2.1.87/linux/arch/i386/boot/compressed/head.S linux/arch/i386/boot/compressed/head.S
--- v2.1.87/linux/arch/i386/boot/compressed/head.S	Mon Dec  1 10:34:10 1997
+++ linux/arch/i386/boot/compressed/head.S	Fri Feb 20 18:28:21 1998
@@ -28,7 +28,6 @@
 .text
 
 #define __ASSEMBLY__
-#include <linux/config.h>
 #include <linux/linkage.h>
 #include <asm/segment.h>
 
diff -u --recursive --new-file v2.1.87/linux/arch/i386/boot/setup.S linux/arch/i386/boot/setup.S
--- v2.1.87/linux/arch/i386/boot/setup.S	Mon Dec 29 10:23:54 1997
+++ linux/arch/i386/boot/setup.S	Thu Feb 19 14:58:40 1998
@@ -410,8 +410,8 @@
 	mov	[68],ebx	! BIOS entry point offset
 	mov	[72],cx		! BIOS 16 bit code segment
 	mov	[74],dx		! BIOS data segment
-	mov	[78],si		! BIOS code segment length
-	mov	[80],di		! BIOS data segment length
+	mov	[78],esi	! BIOS code segment length
+	mov	[82],di		! BIOS data segment length
 	jmp	done_apm_bios
 
 no_32_apm_bios:
diff -u --recursive --new-file v2.1.87/linux/arch/i386/boot/tools/build.c linux/arch/i386/boot/tools/build.c
--- v2.1.87/linux/arch/i386/boot/tools/build.c	Thu May 29 21:53:04 1997
+++ linux/arch/i386/boot/tools/build.c	Fri Feb 20 18:28:21 1998
@@ -33,7 +33,6 @@
 #include <sys/sysmacros.h>
 #include <unistd.h>
 #include <fcntl.h>
-#include <linux/config.h>
 #include <asm/boot.h>
 
 typedef unsigned char byte;
diff -u --recursive --new-file v2.1.87/linux/arch/i386/boot/video.S linux/arch/i386/boot/video.S
--- v2.1.87/linux/arch/i386/boot/video.S	Thu May 15 16:48:01 1997
+++ linux/arch/i386/boot/video.S	Fri Feb 20 18:28:21 1998
@@ -5,6 +5,8 @@
 !	Based on the original setup.S code (C) Linus Torvalds and Mats Anderson
 !
 
+#include <linux/config.h> /* for CONFIG_VIDEO_* */
+
 ! Enable autodetection of SVGA adapters and modes. If you really need this
 ! feature, drop me a mail as I think of removing it some day...
 #undef CONFIG_VIDEO_SVGA
diff -u --recursive --new-file v2.1.87/linux/arch/i386/kernel/irq.c linux/arch/i386/kernel/irq.c
--- v2.1.87/linux/arch/i386/kernel/irq.c	Tue Feb 17 13:12:44 1998
+++ linux/arch/i386/kernel/irq.c	Fri Feb 20 18:28:21 1998
@@ -15,7 +15,6 @@
  * Naturally it's not a 1:1 relation, but there are similarities.
  */
 
-#include <linux/config.h>
 #include <linux/ptrace.h>
 #include <linux/errno.h>
 #include <linux/kernel_stat.h>
diff -u --recursive --new-file v2.1.87/linux/arch/i386/kernel/ptrace.c linux/arch/i386/kernel/ptrace.c
--- v2.1.87/linux/arch/i386/kernel/ptrace.c	Tue Dec 23 20:23:53 1997
+++ linux/arch/i386/kernel/ptrace.c	Fri Feb 20 18:28:21 1998
@@ -2,6 +2,7 @@
 /* By Ross Biro 1/23/92 */
 /* edited by Linus Torvalds */
 
+#include <linux/config.h> /* for CONFIG_MATH_EMULATION */
 #include <linux/head.h>
 #include <linux/kernel.h>
 #include <linux/sched.h>
diff -u --recursive --new-file v2.1.87/linux/arch/i386/kernel/smp.c linux/arch/i386/kernel/smp.c
--- v2.1.87/linux/arch/i386/kernel/smp.c	Tue Feb 17 13:12:44 1998
+++ linux/arch/i386/kernel/smp.c	Thu Feb 19 14:58:40 1998
@@ -878,8 +878,14 @@
 			else
 				printk("Not responding.\n");
 		}
-	}
 	SMP_PRINTK(("CPU has booted.\n"));
+	}
+	else
+	{
+		__cpu_logical_map[cpucount] = -1;
+		cpu_number_map[i] = -1;
+		cpucount--;
+	}
 
 	swapper_pg_dir[0]=maincfg;
 	local_flush_tlb();
@@ -1031,9 +1037,11 @@
 		 *	Make sure we unmap all failed CPUs
 		 */
 		
-		if (cpu_number_map[i] == -1)
+		if (cpu_number_map[i] == -1 && (cpu_present_map & (1 << i))) {
+			printk("CPU #%d not responding. Removing from cpu_present_map.\n",i);
 			cpu_present_map &= ~(1 << i);
-	}
+                }
+        }
 
 	/*
 	 *	Cleanup possible dangling ends...
diff -u --recursive --new-file v2.1.87/linux/arch/i386/kernel/traps.c linux/arch/i386/kernel/traps.c
--- v2.1.87/linux/arch/i386/kernel/traps.c	Sun Dec 21 17:27:18 1997
+++ linux/arch/i386/kernel/traps.c	Fri Feb 20 18:28:21 1998
@@ -15,7 +15,6 @@
 #include <linux/string.h>
 #include <linux/errno.h>
 #include <linux/ptrace.h>
-#include <linux/config.h>
 #include <linux/timer.h>
 #include <linux/mm.h>
 #include <linux/smp.h>
diff -u --recursive --new-file v2.1.87/linux/arch/m68k/amiga/amiga_ksyms.c linux/arch/m68k/amiga/amiga_ksyms.c
--- v2.1.87/linux/arch/m68k/amiga/amiga_ksyms.c	Wed Apr 23 19:01:14 1997
+++ linux/arch/m68k/amiga/amiga_ksyms.c	Fri Feb 20 18:28:21 1998
@@ -1,4 +1,3 @@
-#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/types.h>
 #include <linux/zorro.h>
diff -u --recursive --new-file v2.1.87/linux/arch/m68k/atari/atari_ksyms.c linux/arch/m68k/atari/atari_ksyms.c
--- v2.1.87/linux/arch/m68k/atari/atari_ksyms.c	Tue Feb 17 13:12:44 1998
+++ linux/arch/m68k/atari/atari_ksyms.c	Fri Feb 20 18:28:21 1998
@@ -1,4 +1,3 @@
-#include <linux/config.h>
 #include <linux/module.h>
 #include <asm/ptrace.h>
 #include <asm/traps.h>
diff -u --recursive --new-file v2.1.87/linux/arch/m68k/atari/time.c linux/arch/m68k/atari/time.c
--- v2.1.87/linux/arch/m68k/atari/time.c	Tue Feb 17 13:12:44 1998
+++ linux/arch/m68k/atari/time.c	Fri Feb 20 18:28:21 1998
@@ -10,7 +10,6 @@
  * for more details.
  */
 
-#include <linux/config.h>
 #include <linux/types.h>
 #include <linux/mc146818rtc.h>
 #include <linux/kd.h>
diff -u --recursive --new-file v2.1.87/linux/arch/m68k/kernel/m68k_ksyms.c linux/arch/m68k/kernel/m68k_ksyms.c
--- v2.1.87/linux/arch/m68k/kernel/m68k_ksyms.c	Tue Feb 17 13:12:45 1998
+++ linux/arch/m68k/kernel/m68k_ksyms.c	Fri Feb 20 18:28:21 1998
@@ -1,4 +1,3 @@
-#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/linkage.h>
 #include <linux/sched.h>
diff -u --recursive --new-file v2.1.87/linux/arch/m68k/kernel/time.c linux/arch/m68k/kernel/time.c
--- v2.1.87/linux/arch/m68k/kernel/time.c	Tue Feb 17 13:12:45 1998
+++ linux/arch/m68k/kernel/time.c	Fri Feb 20 18:28:21 1998
@@ -7,6 +7,7 @@
  * Most of the stuff is located in the machine specific files.
  */
 
+#include <linux/config.h> /* CONFIG_HEARTBEAT */
 #include <linux/errno.h>
 #include <linux/sched.h>
 #include <linux/kernel.h>
diff -u --recursive --new-file v2.1.87/linux/arch/m68k/mac/mackeyb.c linux/arch/m68k/mac/mackeyb.c
--- v2.1.87/linux/arch/m68k/mac/mackeyb.c	Tue Feb 17 13:12:45 1998
+++ linux/arch/m68k/mac/mackeyb.c	Fri Feb 20 18:28:21 1998
@@ -10,7 +10,6 @@
  * misc. keyboard stuff (everything not in adb-bus.c or keyb_m68k.c)
  */
 
-#include <linux/config.h>
 #include <linux/types.h>
 #include <linux/mm.h>
 #include <linux/kd.h>
diff -u --recursive --new-file v2.1.87/linux/arch/m68k/mac/via6522.c linux/arch/m68k/mac/via6522.c
--- v2.1.87/linux/arch/m68k/mac/via6522.c	Tue Feb 17 13:12:45 1998
+++ linux/arch/m68k/mac/via6522.c	Fri Feb 20 18:28:21 1998
@@ -6,7 +6,6 @@
  *	via them as are assorted bits and bobs - eg rtc, adb.
  */
 
-#include <linux/config.h>
 #include <linux/types.h>
 #include <linux/kernel.h>
 #include <linux/mm.h>
diff -u --recursive --new-file v2.1.87/linux/arch/m68k/mvme16x/16xints.c linux/arch/m68k/mvme16x/16xints.c
--- v2.1.87/linux/arch/m68k/mvme16x/16xints.c	Tue Feb 17 13:12:45 1998
+++ linux/arch/m68k/mvme16x/16xints.c	Fri Feb 20 18:28:21 1998
@@ -11,7 +11,6 @@
  *
  */
 
-#include <linux/config.h>
 #include <linux/types.h>
 #include <linux/kernel.h>
 #include <linux/errno.h>
diff -u --recursive --new-file v2.1.87/linux/arch/ppc/kernel/idle.c linux/arch/ppc/kernel/idle.c
--- v2.1.87/linux/arch/ppc/kernel/idle.c	Fri Jan 16 20:34:18 1998
+++ linux/arch/ppc/kernel/idle.c	Fri Feb 20 18:28:21 1998
@@ -23,7 +23,6 @@
 #include <linux/unistd.h>
 #include <linux/ptrace.h>
 #include <linux/malloc.h>
-#include <linux/config.h>
 
 #include <asm/pgtable.h>
 #include <asm/uaccess.h>
diff -u --recursive --new-file v2.1.87/linux/arch/ppc/kernel/prep_pci.c linux/arch/ppc/kernel/prep_pci.c
--- v2.1.87/linux/arch/ppc/kernel/prep_pci.c	Mon Jan 12 15:18:13 1998
+++ linux/arch/ppc/kernel/prep_pci.c	Fri Feb 20 18:28:21 1998
@@ -7,7 +7,6 @@
  * The motherboard routes/maps will disappear shortly. -- Cort
  */
 
-#include <linux/config.h>
 #include <linux/types.h>
 #include <linux/bios32.h>
 #include <linux/pci.h>
diff -u --recursive --new-file v2.1.87/linux/arch/ppc/kernel/process.c linux/arch/ppc/kernel/process.c
--- v2.1.87/linux/arch/ppc/kernel/process.c	Mon Jan 12 15:18:13 1998
+++ linux/arch/ppc/kernel/process.c	Fri Feb 20 18:28:21 1998
@@ -30,7 +30,6 @@
 #include <linux/malloc.h>
 #include <linux/user.h>
 #include <linux/elf.h>
-#include <linux/config.h>
 #include <linux/elf.h>
 
 #include <asm/pgtable.h>
diff -u --recursive --new-file v2.1.87/linux/arch/ppc/kernel/residual.c linux/arch/ppc/kernel/residual.c
--- v2.1.87/linux/arch/ppc/kernel/residual.c	Mon Jan 12 15:18:13 1998
+++ linux/arch/ppc/kernel/residual.c	Fri Feb 20 18:28:21 1998
@@ -7,7 +7,6 @@
  * Improved _greatly_ and rewritten by Gabriel Paubert (paubert@iram.es)
  */
 #if 0
-#include <linux/config.h>
 #include <linux/errno.h>
 #include <linux/sched.h>
 #include <linux/kernel.h>
diff -u --recursive --new-file v2.1.87/linux/arch/ppc/kernel/time.c linux/arch/ppc/kernel/time.c
--- v2.1.87/linux/arch/ppc/kernel/time.c	Mon Jan 12 15:18:13 1998
+++ linux/arch/ppc/kernel/time.c	Fri Feb 20 18:28:21 1998
@@ -6,7 +6,6 @@
  * Paul Mackerras' version and mine for PReP and Pmac.
  */
 
-#include <linux/config.h>
 #include <linux/errno.h>
 #include <linux/sched.h>
 #include <linux/kernel.h>
diff -u --recursive --new-file v2.1.87/linux/arch/ppc/mm/extable.c linux/arch/ppc/mm/extable.c
--- v2.1.87/linux/arch/ppc/mm/extable.c	Mon Jan 12 15:18:13 1998
+++ linux/arch/ppc/mm/extable.c	Fri Feb 20 18:28:21 1998
@@ -4,7 +4,6 @@
  * from linux/arch/i386/mm/extable.c
  */
 
-#include <linux/config.h>
 #include <linux/module.h>
 #include <asm/uaccess.h>
 
diff -u --recursive --new-file v2.1.87/linux/arch/sparc/ap1000/apmmu.c linux/arch/sparc/ap1000/apmmu.c
--- v2.1.87/linux/arch/sparc/ap1000/apmmu.c	Fri Jan 16 20:39:23 1998
+++ linux/arch/sparc/ap1000/apmmu.c	Fri Feb 20 18:28:22 1998
@@ -11,7 +11,6 @@
  * based on srmmu.c 
  */
 
-#include <linux/config.h>
 #include <linux/kernel.h>
 #include <linux/mm.h>
 #include <linux/malloc.h>
diff -u --recursive --new-file v2.1.87/linux/arch/sparc/ap1000/bnet.c linux/arch/sparc/ap1000/bnet.c
--- v2.1.87/linux/arch/sparc/ap1000/bnet.c	Thu Feb 12 20:56:04 1998
+++ linux/arch/sparc/ap1000/bnet.c	Fri Feb 20 18:28:22 1998
@@ -8,7 +8,6 @@
 /* routines to control the AP1000 bif interface. This is the interface
    used to talk to the front end processor */
 
-#include <linux/config.h>
 #include <linux/sched.h>
 #include <asm/ap1000/apservice.h>
 #include <asm/ap1000/apreg.h>
diff -u --recursive --new-file v2.1.87/linux/arch/sparc/ap1000/msc.c linux/arch/sparc/ap1000/msc.c
--- v2.1.87/linux/arch/sparc/ap1000/msc.c	Fri Jan 16 20:39:41 1998
+++ linux/arch/sparc/ap1000/msc.c	Fri Feb 20 18:28:22 1998
@@ -10,7 +10,6 @@
  * and Memory Controller (MC+).
  *
  */
-#include <linux/config.h>
 #define _APLIB_
 #include <asm/ap1000/apreg.h>
 #include <linux/mm.h>
diff -u --recursive --new-file v2.1.87/linux/arch/sparc/ap1000/timer.c linux/arch/sparc/ap1000/timer.c
--- v2.1.87/linux/arch/sparc/ap1000/timer.c	Mon Jan 12 15:15:43 1998
+++ linux/arch/sparc/ap1000/timer.c	Fri Feb 20 18:28:22 1998
@@ -7,6 +7,7 @@
   */
 /* routines to control the AP1000 timer chip */   
 
+#include <linux/config.h> /* for CONFIG_PROFILE */
 #include <linux/time.h>
 #include <linux/sched.h>
 #include <linux/interrupt.h>
diff -u --recursive --new-file v2.1.87/linux/arch/sparc/kernel/smp.c linux/arch/sparc/kernel/smp.c
--- v2.1.87/linux/arch/sparc/kernel/smp.c	Thu Feb 12 20:56:04 1998
+++ linux/arch/sparc/kernel/smp.c	Fri Feb 20 18:28:22 1998
@@ -3,6 +3,7 @@
  * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
  */
 
+#include <linux/config.h> /* for CONFIG_PROFILE */
 #include <asm/head.h>
 
 #include <linux/kernel.h>
diff -u --recursive --new-file v2.1.87/linux/drivers/acorn/README linux/drivers/acorn/README
--- v2.1.87/linux/drivers/acorn/README	Wed Dec 31 16:00:00 1969
+++ linux/drivers/acorn/README	Mon Feb 16 15:49:47 1998
@@ -0,0 +1,3 @@
+Drivers for the ACORN "podule" ARM specific bus.
+
+
diff -u --recursive --new-file v2.1.87/linux/drivers/acorn/block/Config.in linux/drivers/acorn/block/Config.in
--- v2.1.87/linux/drivers/acorn/block/Config.in	Wed Dec 31 16:00:00 1969
+++ linux/drivers/acorn/block/Config.in	Mon Feb 16 13:46:44 1998
@@ -0,0 +1,21 @@
+#
+# Block device driver configuration
+#
+mainmenu_option next_comment
+comment 'Acorn-Specific floppy, IDE, and other block devices'
+
+tristate 'Normal floppy disk support' CONFIG_BLK_DEV_FD
+bool '   Support expansion card IDE interfaces' CONFIG_BLK_DEV_IDE_CARDS
+if [ "$CONFIG_BLK_DEV_IDE_CARDS" = "y" ]; then
+  dep_tristate '    ICS IDE interface support' CONFIG_BLK_DEV_IDE_ICSIDE $CONFIG_BLK_DEV_IDE
+  dep_tristate '    RapIDE interface support' CONFIG_BLK_DEV_IDE_RAPIDE $CONFIG_BLK_DEV_IDE
+fi
+
+tristate 'MFM harddisk support' CONFIG_BLK_DEV_MFM
+if [ "$CONFIG_BLK_DEV_MFM" != "n" ]; then
+  bool '   Autodetect hard drive geometry' CONFIG_BLK_DEV_MFM_AUTODETECT
+fi
+
+bool 'ADFS partition support' CONFIG_BLK_DEV_PART
+
+endmenu
diff -u --recursive --new-file v2.1.87/linux/drivers/acorn/block/Makefile linux/drivers/acorn/block/Makefile
--- v2.1.87/linux/drivers/acorn/block/Makefile	Wed Dec 31 16:00:00 1969
+++ linux/drivers/acorn/block/Makefile	Mon Feb 16 13:49:36 1998
@@ -0,0 +1,66 @@
+#
+# Makefile for the Acorn block device drivers.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definition is now inherited from the
+# parent makefile.
+#
+
+L_TARGET := acorn-block.a
+L_OBJS   :=
+M_OBJS   :=
+MOD_LIST_NAME := ACORN_BLOCK_MODULES
+
+ifeq ($(CONFIG_ARCH_ARC),y)
+  ifeq ($(CONFIG_BLK_DEV_FD),y)
+    L_OBJS += fd1772.o fd1772dma.o
+  else
+    ifeq ($(CONFIG_BLK_DEV_FD),m)
+      M_OBJS += fd1772_mod.o
+    endif
+  endif
+endif
+
+ifeq ($(CONFIG_BLK_DEV_IDE_ICSIDE),y)
+  L_OBJS += ide-ics.o
+else
+  ifeq ($(CONFIG_BLK_DEV_IDE_ICSIDE),m)
+    M_OBJS += ide-ics.o
+  endif
+endif
+
+ifeq ($(CONFIG_BLK_DEV_IDE_RAPIDE),y)
+  L_OBJS += ide-rapide.o
+else
+  ifeq ($(CONFIG_BLK_DEV_IDE_RAPIDE),m)
+    M_OBJS += ide-rapide.o
+  endif
+endif
+
+ifeq ($(CONFIG_BLK_DEV_MFM),y)
+  L_OBJS += mfmhd.o mfm.o
+else
+  ifeq ($(CONFIG_BLK_DEV_MFM),m)
+    M_OBJS += mfmhd_mod.o
+  endif
+endif
+
+include $(TOPDIR)/Rules.make
+
+fd1772_mod.o: $(FLOPPY)
+	$(LD) -r -o $@ $(FLOPPY)
+
+mfmhd_mod.o: mfmhd.o mfm.o
+	$(LD) -r -o $@ mfmhd.o mfm.o
+
+%.o: %.S
+ifndef $(CONFIG_BINUTILS_NEW)
+	$(CC) $(CFLAGS) -D__ASSEMBLY__ -E $< | tr ';$$' '\n#' > ..tmp.s
+	$(CC) $(CFLAGS) -c -o $@ ..tmp.s
+	$(RM) ..tmp.s
+else
+	$(CC) $(CFLAGS) -D__ASSEMBLY__ -c -o $@ $<
+endif
diff -u --recursive --new-file v2.1.87/linux/drivers/acorn/block/fd1772.c linux/drivers/acorn/block/fd1772.c
--- v2.1.87/linux/drivers/acorn/block/fd1772.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/acorn/block/fd1772.c	Sun Jan 25 10:02:29 1998
@@ -0,0 +1,1670 @@
+/*
+ *  linux/kernel/arch/arm/drivers/block/fd1772.c
+ *  Based on ataflop.c in the m68k Linux
+ *  Copyright (C) 1993  Greg Harp
+ *  Atari Support by Bjoern Brauel, Roman Hodek
+ *  Archimedes Support by Dave Gilbert (gilbertd@cs.man.ac.uk)
+ *
+ *  Big cleanup Sep 11..14 1994 Roman Hodek:
+ *   - Driver now works interrupt driven
+ *   - Support for two drives; should work, but I cannot test that :-(
+ *   - Reading is done in whole tracks and buffered to speed up things
+ *   - Disk change detection and drive deselecting after motor-off
+ *     similar to TOS
+ *   - Autodetection of disk format (DD/HD); untested yet, because I
+ *     don't have an HD drive :-(
+ *
+ *  Fixes Nov 13 1994 Martin Schaller:
+ *   - Autodetection works now
+ *   - Support for 5 1/4" disks
+ *   - Removed drive type (unknown on atari)
+ *   - Do seeks with 8 Mhz
+ *
+ *  Changes by Andreas Schwab:
+ *   - After errors in multiple read mode try again reading single sectors
+ *  (Feb 1995):
+ *   - Clean up error handling
+ *   - Set blk_size for proper size checking
+ *   - Initialize track register when testing presence of floppy
+ *   - Implement some ioctl's
+ *
+ *  Changes by Torsten Lang:
+ *   - When probing the floppies we should add the FDC1772CMDADD_H flag since
+ *     the FDC1772 will otherwise wait forever when no disk is inserted...
+ *
+ *  Things left to do:
+ *   - Formatting
+ *   - Maybe a better strategy for disk change detection (does anyone
+ *     know one?)
+ *   - There are some strange problems left: The strangest one is
+ *     that, at least on my TT (4+4MB), the first 2 Bytes of the last
+ *     page of the TT-Ram (!) change their contents (some bits get
+ *     set) while a floppy DMA is going on. But there are no accesses
+ *     to these memory locations from the kernel... (I tested that by
+ *     making the page read-only). I cannot explain what's going on...
+ *   - Sometimes the drive-change-detection stops to work. The
+ *     function is still called, but the WP bit always reads as 0...
+ *     Maybe a problem with the status reg mode or a timing problem.
+ *     Note 10/12/94: The change detection now seems to work reliably.
+ *     There is no proof, but I've seen no hang for a long time...
+ *
+ * ARCHIMEDES changes: (gilbertd@cs.man.ac.uk)
+ *     26/12/95 - Changed all names starting with FDC to FDC1772
+ *                Removed all references to clock speed of FDC - we're stuck with 8MHz
+ *                Modified disk_type structure to remove HD formats
+ *
+ *      7/ 1/96 - Wrote FIQ code, removed most remaining atariisms
+ *
+ *     13/ 1/96 - Well I think its read a single sector; but there is a problem
+ *                fd_rwsec_done which is called in FIQ mode starts another transfer
+ *                off (in fd_rwsec) while still in FIQ mode.  Because its still in
+ *                FIQ mode it can't service the DMA and loses data. So need to
+ *                heavily restructure.
+ *     14/ 1/96 - Found that the definitions of the register numbers of the
+ *                FDC were multiplied by 2 in the header for the 16bit words
+ *                of the atari so half the writes were going in the wrong place.
+ *                Also realised that the FIQ entry didn't make any attempt to
+ *                preserve registers or return correctly; now in assembler.
+ *
+ *     11/ 2/96 - Hmm - doesn't work on real machine.  Auto detect doesn't
+ *                and hacking that past seems to wait forever - check motor
+ *                being turned on.
+ *
+ *     17/ 2/96 - still having problems - forcing track to -1 when selecting
+ *                new drives seems to allow it to read first few sectors
+ *                but then we get solid hangs at apparently random places
+ *                which change depending what is happening.
+ *
+ *      9/ 3/96 - Fiddled a lot of stuff around to move to kernel 1.3.35
+ *                A lot of fiddling in DMA stuff. Having problems with it
+ *                constnatly thinking its timeing out. Ah - its timeout
+ *                was set to (6*HZ) rather than jiffies+(6*HZ).  Now giving
+ *                duff data!
+ *
+ *      5/ 4/96 - Made it use the new IOC_ macros rather than *ioc
+ *                Hmm - giving unexpected FIQ and then timeouts
+ *     18/ 8/96 - Ran through indent -kr -i8
+ *                Some changes to disc change detect; don't know how well it
+ *                works.
+ *     24/ 8/96 - Put all the track buffering code back in from the atari
+ *                code - I wonder if it will still work... No :-)
+ *                Still works if I turn off track buffering.
+ *     25/ 8/96 - Changed the timer expires that I'd added back to be 
+ *                jiffies + ....; and it all sprang to life! Got 2.8K/sec
+ *                off a cp -r of a 679K disc (showed 94% cpu usage!)
+ *                (PC gets 14.3K/sec - 0% CPU!) Hmm - hard drive corrupt!
+ *                Also perhaps that compile was with cache off.
+ *                changed cli in fd_readtrack_check to cliIF
+ *                changed vmallocs to kmalloc (whats the difference!!)
+ *                Removed the busy wait loop in do_fd_request and replaced
+ *                by a routine on tq_immediate; only 11% cpu on a dd off the
+ *                raw disc - but the speed is the same.
+ *	1/ 9/96 - Idea (failed!) - set the 'disable spin-up seqeunce'
+ *		  when we read the track if we know the motor is on; didn't
+ *		  help - perhaps we have to do it in stepping as well.
+ *		  Nope. Still doesn't help.
+ *		  Hmm - what seems to be happening is that fd_readtrack_check
+ *		  is never getting called. Its job is to terminate the read
+ *		  just after we think we should have got the data; otherwise
+ *		  the fdc takes 1 second to timeout; which is what's happening
+ *		  Now I can see 'readtrack_timer' being set (which should do the
+ *		  call); but it never seems to be called - hmm!
+ *		  OK - I've moved the check to my tq_immediate code -
+ *		  and it WORKS! 13.95K/second at 19% CPU.
+ *		  I wish I knew why that timer didn't work.....
+ *
+ *     16/11/96 - Fiddled and frigged for 2.0.18
+ */
+
+#include <linux/config.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/fcntl.h>
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/timer.h>
+#include <linux/tqueue.h>
+#include <linux/fd.h>
+#include <linux/fd1772.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/mm.h>
+
+#include <asm/arch/oldlatches.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <asm/dma.h>
+#include <asm/hardware.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/irq-no.h>
+#include <asm/pgtable.h>
+#include <asm/segment.h>
+
+#define MAJOR_NR FLOPPY_MAJOR
+#define FLOPPY_DMA 0
+#include "blk.h"
+
+/* Note: FD_MAX_UNITS could be redefined to 2 for the Atari (with
+ * little additional rework in this file). But I'm not yet sure if
+ * some other code depends on the number of floppies... (It is defined
+ * in a public header!)
+ */
+#if 0
+#undef FD_MAX_UNITS
+#define	FD_MAX_UNITS	2
+#endif
+
+/* Ditto worries for Arc - DAG */
+#define FD_MAX_UNITS 4
+#define TRACKBUFFER 0
+/*#define DEBUG*/
+
+#ifdef DEBUG
+#define DPRINT(a)	printk a
+#else
+#define DPRINT(a)
+#endif
+
+/* Disk types: DD */
+static struct archy_disk_type {
+	const char *name;
+	unsigned spt;		/* sectors per track */
+	unsigned blocks;	/* total number of blocks */
+	unsigned stretch;	/* track doubling ? */
+} disk_type[] = {
+
+	{ "d360", 9, 720, 0 },			/* 360kB diskette */
+	{ "D360", 9, 720, 1 },			/* 360kb in 720kb drive */
+	{ "D720", 9, 1440, 0 },			/* 720kb diskette (DD) */
+	/*{ "D820", 10,1640, 0}, *//* DD disk with 82 tracks/10 sectors 
+	                              - DAG - can't see how type detect can distinguish this
+				      from 720K until it reads block 4 by which time its too late! */
+};
+
+#define	NUM_DISK_TYPES (sizeof(disk_type)/sizeof(*disk_type))
+
+/*
+ * Maximum disk size (in kilobytes). This default is used whenever the
+ * current disk size is unknown.
+ */
+#define MAX_DISK_SIZE 720
+
+static int floppy_sizes[256];
+static int floppy_blocksizes[256] = {0,};
+
+/* current info on each unit */
+static struct archy_floppy_struct {
+	int connected;		/* !=0 : drive is connected */
+	int autoprobe;		/* !=0 : do autoprobe       */
+
+	struct archy_disk_type *disktype;	/* current type of disk */
+
+	int track;		/* current head position or -1
+				   * if unknown */
+	unsigned int steprate;	/* steprate setting */
+	unsigned int wpstat;	/* current state of WP signal
+				   * (for disk change detection) */
+} unit[FD_MAX_UNITS];
+
+/* DAG: On Arc we spin on a flag being cleared by fdc1772_comendhandler which
+   is an assembler routine */
+extern void fdc1772_comendhandler(void);	/* Actually doens't have these parameters - see fd1772.S */
+extern volatile int fdc1772_comendstatus;
+extern volatile int fdc1772_fdc_int_done;
+
+#define FDC1772BASE ((0x210000>>2)|0x80000000)
+
+#define FDC1772_READ(reg) inb(FDC1772BASE+(reg/2))
+
+/* DAG: You wouldn't be silly to ask why FDC1772_WRITE is a function rather
+   than the #def below - well simple - the #def won't compile - and I
+   don't understand why (__outwc not defined) */
+/* NOTE: Reg is 0,2,4,6 as opposed to 0,1,2,3 or 0,4,8,12 to keep compatibility
+   with the ST version of fd1772.h */
+/*#define FDC1772_WRITE(reg,val) outw(val,(reg+FDC1772BASE)); */
+void FDC1772_WRITE(int reg, unsigned char val)
+{
+	if (reg == FDC1772REG_CMD) {
+		DPRINT(("FDC1772_WRITE new command 0x%x @ %d\n", val,jiffies));
+		if (fdc1772_fdc_int_done) {
+			DPRINT(("FDC1772_WRITE: Hmm fdc1772_fdc_int_done true - resetting\n"));
+			fdc1772_fdc_int_done = 0;
+		};
+	};
+	outb(val, (reg / 2) + FDC1772BASE);
+};
+
+#define	MAX_SECTORS	22
+
+unsigned char *DMABuffer;	/* buffer for writes */
+/*static unsigned long PhysDMABuffer; *//* physical address */
+/* DAG: On Arc we just go straight for the DMA buffer */
+#define PhysDMABuffer DMABuffer
+
+#ifdef TRACKBUFFER   
+unsigned char *TrackBuffer;       /* buffer for reads */
+#define PhysTrackBuffer TrackBuffer /* physical address */
+static int BufferDrive, BufferSide, BufferTrack;
+static int read_track;    /* non-zero if we are reading whole tracks */
+  
+#define SECTOR_BUFFER(sec)  (TrackBuffer + ((sec)-1)*512)
+#define IS_BUFFERED(drive,side,track) \
+    (BufferDrive == (drive) && BufferSide == (side) && BufferTrack == (track))
+#endif
+
+/*
+ * These are global variables, as that's the easiest way to give
+ * information to interrupts. They are the data used for the current
+ * request.
+ */
+static int SelectedDrive = 0;
+static int ReqCmd, ReqBlock;
+static int ReqSide, ReqTrack, ReqSector, ReqCnt;
+static int HeadSettleFlag = 0;
+static unsigned char *ReqData, *ReqBuffer;
+static int MotorOn = 0, MotorOffTrys;
+
+/* Synchronization of FDC1772 access. */
+static volatile int fdc_busy = 0;
+static struct wait_queue *fdc_wait = NULL;
+
+
+static unsigned int changed_floppies = 0xff, fake_change = 0;
+#define	CHECK_CHANGE_DELAY	HZ/2
+
+/* DAG - increased to 30*HZ - not sure if this is the correct thing to do */
+#define	FD_MOTOR_OFF_DELAY	(10*HZ)
+#define	FD_MOTOR_OFF_MAXTRY	(10*20)
+
+#define FLOPPY_TIMEOUT		(6*HZ)
+#define RECALIBRATE_ERRORS	4	/* After this many errors the drive
+					 * will be recalibrated. */
+#define MAX_ERRORS			8	/* After this many errors the driver
+						 * will give up. */
+
+
+#define	START_MOTOR_OFF_TIMER(delay)			\
+    do {						\
+        motor_off_timer.expires = jiffies + (delay);		\
+        add_timer( &motor_off_timer );			\
+        MotorOffTrys = 0;				\
+	} while(0)
+
+#define	START_CHECK_CHANGE_TIMER(delay)				\
+    do {							\
+        timer_table[FLOPPY_TIMER].expires = jiffies + (delay);	\
+        timer_active |= (1 << FLOPPY_TIMER);			\
+	} while(0)
+
+#define	START_TIMEOUT()					\
+    do {						\
+        del_timer( &timeout_timer );			\
+        timeout_timer.expires = jiffies + FLOPPY_TIMEOUT;	\
+        add_timer( &timeout_timer );			\
+	} while(0)
+
+#define	STOP_TIMEOUT()					\
+    do {						\
+        del_timer( &timeout_timer );			\
+	} while(0)
+
+#define ENABLE_IRQ() enable_irq(FIQ_FD1772+64);
+
+#define DISABLE_IRQ() disable_irq(FIQ_FD1772+64);
+
+static void fd1772_checkint(void);
+
+struct tq_struct fd1772_tq = 
+{ 0,0, (void *)fd1772_checkint, 0 };
+/*
+ * The driver is trying to determine the correct media format
+ * while Probing is set. fd_rwsec_done() clears it after a
+ * successful access.
+ */
+static int Probing = 0;
+
+/* This flag is set when a dummy seek is necesary to make the WP
+ * status bit accessible.
+ */
+static int NeedSeek = 0;
+
+
+/***************************** Prototypes *****************************/
+
+static void fd_select_side(int side);
+static void fd_select_drive(int drive);
+static void fd_deselect(void);
+static void fd_motor_off_timer(unsigned long dummy);
+static void check_change(void);
+static __inline__ void set_head_settle_flag(void);
+static __inline__ int get_head_settle_flag(void);
+static void floppy_irqconsequencehandler(void);
+static void fd_error(void);
+static void do_fd_action(int drive);
+static void fd_calibrate(void);
+static void fd_calibrate_done(int status);
+static void fd_seek(void);
+static void fd_seek_done(int status);
+static void fd_rwsec(void);
+#ifdef TRACKBUFFER   
+static void fd_readtrack_check( unsigned long dummy );  
+#endif
+static void fd_rwsec_done(int status);
+static void fd_times_out(unsigned long dummy);
+static void finish_fdc(void);
+static void finish_fdc_done(int dummy);
+static void floppy_off(unsigned int nr);
+static __inline__ void copy_buffer(void *from, void *to);
+static void setup_req_params(int drive);
+static void redo_fd_request(void);
+static int fd_ioctl(struct inode *inode, struct file *filp, unsigned int
+		    cmd, unsigned long param);
+static void fd_probe(int drive);
+static int fd_test_drive_present(int drive);
+static void config_types(void);
+static int floppy_open(struct inode *inode, struct file *filp);
+static void floppy_release(struct inode *inode, struct file *filp);
+
+/************************* End of Prototypes **************************/
+
+static struct timer_list motor_off_timer =
+{NULL, NULL, 0, 0, fd_motor_off_timer};
+#ifdef TRACKBUFFER
+static struct timer_list readtrack_timer =
+             { NULL, NULL, 0, 0, fd_readtrack_check };
+#endif
+static struct timer_list timeout_timer =
+{NULL, NULL, 0, 0, fd_times_out};
+
+/* DAG: Haven't got a clue what this is? */
+int stdma_islocked(void)
+{
+	return 0;
+};
+
+/* Select the side to use. */
+
+static void fd_select_side(int side)
+{
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+
+	oldlatch_aupdate(LATCHA_SIDESEL, side ? 0 : LATCHA_SIDESEL);
+	restore_flags(flags);
+}
+
+
+/* Select a drive, update the FDC1772's track register
+ */
+
+static void fd_select_drive(int drive)
+{
+	unsigned long flags;
+
+#ifdef DEBUG
+	printk("fd_select_drive:%d\n", drive);
+#endif
+	/* Hmm - nowhere do we seem to turn the motor on - I'm going to do it here! */
+	oldlatch_aupdate(LATCHA_MOTOR | LATCHA_INUSE, 0);
+
+	if (drive == SelectedDrive)
+		return;
+
+	save_flags(flags);
+	cli();
+	oldlatch_aupdate(LATCHA_FDSELALL, 0xf - (1 << drive));
+	restore_flags(flags);
+
+	/* restore track register to saved value */
+	FDC1772_WRITE(FDC1772REG_TRACK, unit[drive].track);
+	udelay(25);
+
+	SelectedDrive = drive;
+}
+
+
+/* Deselect both drives. */
+
+static void fd_deselect(void)
+{
+	unsigned long flags;
+
+	DPRINT(("fd_deselect\n"));
+
+	save_flags(flags);
+	cli();
+	oldlatch_aupdate(LATCHA_FDSELALL | LATCHA_MOTOR | LATCHA_INUSE, 0xf | LATCHA_MOTOR | LATCHA_INUSE);
+	restore_flags(flags);
+
+	SelectedDrive = -1;
+}
+
+
+/* This timer function deselects the drives when the FDC1772 switched the
+ * motor off. The deselection cannot happen earlier because the FDC1772
+ * counts the index signals, which arrive only if one drive is selected.
+ */
+
+static void fd_motor_off_timer(unsigned long dummy)
+{
+	unsigned long flags;
+	unsigned char status;
+	int delay;
+
+	del_timer(&motor_off_timer);
+
+	if (SelectedDrive < 0)
+		/* no drive selected, needn't deselect anyone */
+		return;
+
+	save_flags(flags);
+	cli();
+
+	if (fdc_busy)		/* was stdma_islocked */
+		goto retry;
+
+	status = FDC1772_READ(FDC1772REG_STATUS);
+
+	if (!(status & 0x80)) {
+		/* motor already turned off by FDC1772 -> deselect drives */
+     /* In actual fact its this deselection which turns the motor off on the
+        Arc, since the motor control is actually on Latch A */
+		DPRINT(("fdc1772: deselecting in fd_motor_off_timer\n"));
+		fd_deselect();
+		MotorOn = 0;
+		restore_flags(flags);
+		return;
+	}
+	/* not yet off, try again */
+
+      retry:
+	restore_flags(flags);
+	/* Test again later; if tested too often, it seems there is no disk
+	 * in the drive and the FDC1772 will leave the motor on forever (or,
+	 * at least until a disk is inserted). So we'll test only twice
+	 * per second from then on...
+	 */
+	delay = (MotorOffTrys < FD_MOTOR_OFF_MAXTRY) ?
+	    (++MotorOffTrys, HZ / 20) : HZ / 2;
+	START_MOTOR_OFF_TIMER(delay);
+}
+
+
+/* This function is repeatedly called to detect disk changes (as good
+ * as possible) and keep track of the current state of the write protection.
+ */
+
+static void check_change(void)
+{
+	static int drive = 0;
+
+	unsigned long flags;
+	int stat;
+
+	if (fdc_busy)
+		return;		/* Don't start poking about if the fdc is busy */
+
+	return;			/* lets just forget it for the mo DAG */
+
+	if (++drive > 1 || !unit[drive].connected)
+		drive = 0;
+
+	save_flags(flags);
+	cli();
+
+	if (!stdma_islocked()) {
+		stat = !!(FDC1772_READ(FDC1772REG_STATUS) & FDC1772STAT_WPROT);
+
+		/* The idea here is that if the write protect line has changed then
+		the disc must have changed */
+		if (stat != unit[drive].wpstat) {
+			DPRINT(("wpstat[%d] = %d\n", drive, stat));
+			unit[drive].wpstat = stat;
+			set_bit(drive, &changed_floppies);
+		}
+	}
+	restore_flags(flags);
+
+	START_CHECK_CHANGE_TIMER(CHECK_CHANGE_DELAY);
+}
+
+
+/* Handling of the Head Settling Flag: This flag should be set after each
+ * seek operation, because we dont't use seeks with verify.
+ */
+
+static __inline__ void set_head_settle_flag(void)
+{
+	HeadSettleFlag = FDC1772CMDADD_E;
+}
+
+static __inline__ int get_head_settle_flag(void)
+{
+	int tmp = HeadSettleFlag;
+	HeadSettleFlag = 0;
+	return (tmp);
+}
+
+
+
+
+/* General Interrupt Handling */
+
+static void (*FloppyIRQHandler) (int status) = NULL;
+
+static void floppy_irqconsequencehandler(void)
+{
+	unsigned char status;
+	void (*handler) (int);
+
+	fdc1772_fdc_int_done = 0;
+
+	handler = FloppyIRQHandler;
+	FloppyIRQHandler = NULL;
+
+	if (handler) {
+		nop();
+		status = (unsigned char) fdc1772_comendstatus;
+		DPRINT(("FDC1772 irq, status = %02x handler = %08lx\n", (unsigned int) status, (unsigned long) handler));
+		handler(status);
+	} else {
+		DPRINT(("FDC1772 irq, no handler status=%02x\n", fdc1772_comendstatus));
+	}
+	DPRINT(("FDC1772 irq: end of floppy_irq\n"));
+}
+
+
+/* Error handling: If some error happened, retry some times, then
+ * recalibrate, then try again, and fail after MAX_ERRORS.
+ */
+
+static void fd_error(void)
+{
+	printk("FDC1772: fd_error\n");
+	/*panic("fd1772: fd_error"); *//* DAG tmp */
+	if (!CURRENT)
+		return;
+	CURRENT->errors++;
+	if (CURRENT->errors >= MAX_ERRORS) {
+		printk("fd%d: too many errors.\n", SelectedDrive);
+		end_request(0);
+	} else if (CURRENT->errors == RECALIBRATE_ERRORS) {
+		printk("fd%d: recalibrating\n", SelectedDrive);
+		if (SelectedDrive != -1)
+			unit[SelectedDrive].track = -1;
+	}
+	redo_fd_request();
+}
+
+
+
+#define	SET_IRQ_HANDLER(proc) do { FloppyIRQHandler = (proc); } while(0)
+
+
+/* do_fd_action() is the general procedure for a fd request: All
+ * required parameter settings (drive select, side select, track
+ * position) are checked and set if needed. For each of these
+ * parameters and the actual reading or writing exist two functions:
+ * one that starts the setting (or skips it if possible) and one
+ * callback for the "done" interrupt. Each done func calls the next
+ * set function to propagate the request down to fd_rwsec_done().
+ */
+
+static void do_fd_action(int drive)
+{
+	DPRINT(("do_fd_action unit[drive].track=%d\n", unit[drive].track));
+
+#ifdef TRACKBUFFER
+  repeat:
+
+  if (IS_BUFFERED( drive, ReqSide, ReqTrack )) {
+    if (ReqCmd == READ) {
+      copy_buffer( SECTOR_BUFFER(ReqSector), ReqData );
+      if (++ReqCnt < CURRENT->current_nr_sectors) {
+        /* read next sector */
+        setup_req_params( drive );
+        goto repeat;
+      }
+      else {
+        /* all sectors finished */
+        CURRENT->nr_sectors -= CURRENT->current_nr_sectors;
+        CURRENT->sector += CURRENT->current_nr_sectors;
+        end_request( 1 );
+        redo_fd_request();
+        return;
+      }
+    }
+    else {
+      /* cmd == WRITE, pay attention to track buffer
+       * consistency! */
+      copy_buffer( ReqData, SECTOR_BUFFER(ReqSector) );
+    }
+  }
+#endif
+
+	if (SelectedDrive != drive) {
+		/*unit[drive].track = -1; DAG */
+		fd_select_drive(drive);
+	};
+
+
+	if (unit[drive].track == -1)
+		fd_calibrate();
+	else if (unit[drive].track != ReqTrack << unit[drive].disktype->stretch)
+		fd_seek();
+	else
+		fd_rwsec();
+}
+
+
+/* Seek to track 0 if the current track is unknown */
+
+static void fd_calibrate(void)
+{
+	DPRINT(("fd_calibrate\n"));
+	if (unit[SelectedDrive].track >= 0) {
+		fd_calibrate_done(0);
+		return;
+	}
+	DPRINT(("fd_calibrate (after track compare)\n"));
+	SET_IRQ_HANDLER(fd_calibrate_done);
+	/* we can't verify, since the speed may be incorrect */
+	FDC1772_WRITE(FDC1772REG_CMD, FDC1772CMD_RESTORE | unit[SelectedDrive].steprate);
+
+	NeedSeek = 1;
+	MotorOn = 1;
+	START_TIMEOUT();
+	/* wait for IRQ */
+}
+
+
+static void fd_calibrate_done(int status)
+{
+	DPRINT(("fd_calibrate_done()\n"));
+	STOP_TIMEOUT();
+
+	/* set the correct speed now */
+	if (status & FDC1772STAT_RECNF) {
+		printk("fd%d: restore failed\n", SelectedDrive);
+		fd_error();
+	} else {
+		unit[SelectedDrive].track = 0;
+		fd_seek();
+	}
+}
+
+
+/* Seek the drive to the requested track. The drive must have been
+ * calibrated at some point before this.
+ */
+
+static void fd_seek(void)
+{
+  unsigned long flags;
+	DPRINT(("fd_seek() to track %d (unit[SelectedDrive].track=%d)\n", ReqTrack,
+		unit[SelectedDrive].track));
+	if (unit[SelectedDrive].track == ReqTrack <<
+	    unit[SelectedDrive].disktype->stretch) {
+		fd_seek_done(0);
+		return;
+	}
+	FDC1772_WRITE(FDC1772REG_DATA, ReqTrack <<
+		      unit[SelectedDrive].disktype->stretch);
+	udelay(25);
+  save_flags(flags);
+  cliIF();
+	SET_IRQ_HANDLER(fd_seek_done);
+	FDC1772_WRITE(FDC1772REG_CMD, FDC1772CMD_SEEK | unit[SelectedDrive].steprate |
+		/* DAG */
+		(MotorOn?FDC1772CMDADD_H:0));
+
+  restore_flags(flags);
+	MotorOn = 1;
+	set_head_settle_flag();
+	START_TIMEOUT();
+	/* wait for IRQ */
+}
+
+
+static void fd_seek_done(int status)
+{
+	DPRINT(("fd_seek_done()\n"));
+	STOP_TIMEOUT();
+
+	/* set the correct speed */
+	if (status & FDC1772STAT_RECNF) {
+		printk("fd%d: seek error (to track %d)\n",
+		       SelectedDrive, ReqTrack);
+		/* we don't know exactly which track we are on now! */
+		unit[SelectedDrive].track = -1;
+		fd_error();
+	} else {
+		unit[SelectedDrive].track = ReqTrack <<
+		    unit[SelectedDrive].disktype->stretch;
+		NeedSeek = 0;
+		fd_rwsec();
+	}
+}
+
+
+/* This does the actual reading/writing after positioning the head
+ * over the correct track.
+ */
+
+#ifdef TRACKBUFFER
+static int MultReadInProgress = 0;
+#endif
+
+
+static void fd_rwsec(void)
+{
+	unsigned long paddr, flags;
+	unsigned int rwflag, old_motoron;
+	unsigned int track;
+
+	DPRINT(("fd_rwsec(), Sec=%d, Access=%c\n", ReqSector, ReqCmd == WRITE ? 'w' : 'r'));
+	if (ReqCmd == WRITE) {
+		/*cache_push( (unsigned long)ReqData, 512 ); */
+		paddr = (unsigned long) ReqData;
+		rwflag = 0x100;
+	} else {
+#ifdef TRACKBUFFER
+    if (read_track)
+      paddr = (unsigned long)PhysTrackBuffer;
+    else
+      paddr =(unsigned long)PhysDMABuffer;
+#else
+    paddr = (unsigned long)PhysDMABuffer;
+#endif
+		rwflag = 0;
+	}
+
+	DPRINT(("fd_rwsec() before sidesel rwflag=%d sec=%d trk=%d\n", rwflag,
+		ReqSector, FDC1772_READ(FDC1772REG_TRACK)));
+	fd_select_side(ReqSide);
+
+	/*DPRINT(("fd_rwsec() before start sector \n")); */
+	/* Start sector of this operation */
+#ifdef TRACKBUFFER
+  FDC1772_WRITE( FDC1772REG_SECTOR, !read_track ? ReqSector : 1 );
+#else
+  FDC1772_WRITE( FDC1772REG_SECTOR, ReqSector );
+#endif
+
+	/* Cheat for track if stretch != 0 */
+	if (unit[SelectedDrive].disktype->stretch) {
+		track = FDC1772_READ(FDC1772REG_TRACK);
+		FDC1772_WRITE(FDC1772REG_TRACK, track >>
+			      unit[SelectedDrive].disktype->stretch);
+	}
+	udelay(25);
+
+	DPRINT(("fd_rwsec() before setup DMA \n"));
+	/* Setup DMA - Heavily modified by DAG */
+	save_flags(flags);
+	cliIF();
+	disable_dma(FLOPPY_DMA);
+	set_dma_mode(FLOPPY_DMA, rwflag ? DMA_MODE_WRITE : DMA_MODE_READ);
+	set_dma_addr(FLOPPY_DMA, (long) paddr);		/* DAG - changed from Atari specific */
+#ifdef TRACKBUFFER
+  set_dma_count(FLOPPY_DMA,(!read_track ? 1 : unit[SelectedDrive].disktype->spt)*512);
+#else
+	set_dma_count(FLOPPY_DMA, 512);		/* Block/sector size - going to have to change */
+#endif
+	SET_IRQ_HANDLER(fd_rwsec_done);
+	/* Turn on dma int */
+	enable_dma(FLOPPY_DMA);
+	/* Now give it something to do */
+	FDC1772_WRITE(FDC1772REG_CMD, (rwflag ? (FDC1772CMD_WRSEC | FDC1772CMDADD_P) : 
+#ifdef TRACKBUFFER
+              (FDC1772CMD_RDSEC | (read_track ? FDC1772CMDADD_M : 0) |
+	      /* Hmm - the idea here is to stop the FDC spinning the disc
+	      up when we know that we already still have it spinning */
+	      (MotorOn?FDC1772CMDADD_H:0))
+#else
+              FDC1772CMD_RDSEC
+#endif
+    ));
+
+	restore_flags(flags);
+	DPRINT(("fd_rwsec() after DMA setup flags=0x%08x\n", flags));
+	/*sti(); *//* DAG - Hmm */
+	/* Hmm - should do something DAG */
+	old_motoron = MotorOn;
+	MotorOn = 1;
+	NeedSeek = 1;
+
+	/* wait for interrupt */
+
+#ifdef TRACKBUFFER
+  if (read_track) {
+    /* If reading a whole track, wait about one disk rotation and
+     * then check if all sectors are read. The FDC will even
+     * search for the first non-existant sector and need 1 sec to
+     * recognise that it isn't present :-(
+     */
+    del_timer( &readtrack_timer );
+    readtrack_timer.function = fd_readtrack_check;
+    readtrack_timer.expires = jiffies + HZ/5 + (old_motoron ? 0 : HZ);
+                              /* 1 rot. + 5 rot.s if motor was off  */
+    DPRINT(("Setting readtrack_timer to %d @ %d\n",readtrack_timer.expires,jiffies));
+    add_timer( &readtrack_timer );
+    MultReadInProgress = 1;
+ }
+#endif
+
+	/*DPRINT(("fd_rwsec() before START_TIMEOUT \n")); */
+	START_TIMEOUT();
+	/*DPRINT(("fd_rwsec() after START_TIMEOUT \n")); */
+}
+
+
+#ifdef TRACKBUFFER
+
+static void fd_readtrack_check( unsigned long dummy )
+
+{ unsigned long flags, addr;
+  extern unsigned char *fdc1772_dataaddr;
+
+  DPRINT(("fd_readtrack_check @ %d\n",jiffies));
+
+  save_flags(flags);
+  cliIF();
+
+  del_timer( &readtrack_timer );
+
+  if (!MultReadInProgress) {
+    /* This prevents a race condition that could arise if the
+     * interrupt is triggered while the calling of this timer
+     * callback function takes place. The IRQ function then has
+     * already cleared 'MultReadInProgress'  when control flow
+     * gets here.
+     */
+        restore_flags(flags);
+    return;
+  }
+
+  /* get the current DMA address */
+  addr=fdc1772_dataaddr; /* DAG - ? */
+  DPRINT(("fd_readtrack_check: addr=%x PhysTrackBuffer=%x\n",addr,PhysTrackBuffer));
+
+  if (addr >= PhysTrackBuffer + unit[SelectedDrive].disktype->spt*512) {
+    /* already read enough data, force an FDC interrupt to stop
+     * the read operation
+     */
+    SET_IRQ_HANDLER( NULL );
+    restore_flags(flags);
+    DPRINT(("fd_readtrack_check(): done\n"));
+    FDC1772_WRITE( FDC1772REG_CMD, FDC1772CMD_FORCI );
+    udelay(25);
+
+    /* No error until now -- the FDC would have interrupted
+     * otherwise!
+     */
+    fd_rwsec_done( 0 );
+  }
+  else {
+    /* not yet finished, wait another tenth rotation */
+    restore_flags(flags);
+    DPRINT(("fd_readtrack_check(): not yet finished\n"));
+    readtrack_timer.expires = jiffies + HZ/5/10;
+    add_timer( &readtrack_timer );
+  }
+}
+
+#endif
+
+static void fd_rwsec_done(int status)
+{
+	unsigned int track;
+
+	DPRINT(("fd_rwsec_done() status=%d @ %d\n", status,jiffies));
+
+#ifdef TRACKBUFFER
+  if (read_track && !MultReadInProgress) return;
+  MultReadInProgress = 0;
+
+  STOP_TIMEOUT();
+
+  if (read_track)
+    del_timer( &readtrack_timer );
+#endif
+
+
+	/* Correct the track if stretch != 0 */
+	if (unit[SelectedDrive].disktype->stretch) {
+		track = FDC1772_READ(FDC1772REG_TRACK);
+		FDC1772_WRITE(FDC1772REG_TRACK, track <<
+			      unit[SelectedDrive].disktype->stretch);
+	}
+	if (ReqCmd == WRITE && (status & FDC1772STAT_WPROT)) {
+		printk("fd%d: is write protected\n", SelectedDrive);
+		goto err_end;
+	}
+	if ((status & FDC1772STAT_RECNF)
+#ifdef TRACKBUFFER
+    /* RECNF is no error after a multiple read when the FDC
+     * searched for a non-existant sector!
+     */
+    && !(read_track &&
+       FDC1772_READ(FDC1772REG_SECTOR) > unit[SelectedDrive].disktype->spt)
+#endif
+    ) {
+		if (Probing) {
+			if (unit[SelectedDrive].disktype > disk_type) {
+				/* try another disk type */
+				unit[SelectedDrive].disktype--;
+				floppy_sizes[SelectedDrive]
+				    = unit[SelectedDrive].disktype->blocks >> 1;
+			} else
+				Probing = 0;
+		} else {
+			/* record not found, but not probing. Maybe stretch wrong ? Restart probing */
+			if (unit[SelectedDrive].autoprobe) {
+				unit[SelectedDrive].disktype = disk_type + NUM_DISK_TYPES - 1;
+				floppy_sizes[SelectedDrive]
+				    = unit[SelectedDrive].disktype->blocks >> 1;
+				Probing = 1;
+			}
+		}
+		if (Probing) {
+			setup_req_params(SelectedDrive);
+#ifdef TRACKBUFFER
+      BufferDrive = -1;
+#endif
+			do_fd_action(SelectedDrive);
+			return;
+		}
+		printk("fd%d: sector %d not found (side %d, track %d)\n",
+		       SelectedDrive, FDC1772_READ(FDC1772REG_SECTOR), ReqSide, ReqTrack);
+		goto err_end;
+	}
+	if (status & FDC1772STAT_CRC) {
+		printk("fd%d: CRC error (side %d, track %d, sector %d)\n",
+		       SelectedDrive, ReqSide, ReqTrack, FDC1772_READ(FDC1772REG_SECTOR));
+		goto err_end;
+	}
+	if (status & FDC1772STAT_LOST) {
+		printk("fd%d: lost data (side %d, track %d, sector %d)\n",
+		       SelectedDrive, ReqSide, ReqTrack, FDC1772_READ(FDC1772REG_SECTOR));
+		goto err_end;
+	}
+	Probing = 0;
+
+	if (ReqCmd == READ) {
+#ifdef TRACKBUFFER
+    if (!read_track)
+      {
+        /*cache_clear (PhysDMABuffer, 512);*/
+        copy_buffer (DMABuffer, ReqData);
+      }
+    else
+      {
+        /*cache_clear (PhysTrackBuffer, MAX_SECTORS * 512);*/
+        BufferDrive = SelectedDrive;
+        BufferSide  = ReqSide;
+        BufferTrack = ReqTrack;
+        copy_buffer (SECTOR_BUFFER (ReqSector), ReqData);
+      }
+#else
+		/*cache_clear( PhysDMABuffer, 512 ); */
+		copy_buffer(DMABuffer, ReqData);
+#endif
+	}
+	if (++ReqCnt < CURRENT->current_nr_sectors) {
+		/* read next sector */
+		setup_req_params(SelectedDrive);
+		do_fd_action(SelectedDrive);
+	} else {
+		/* all sectors finished */
+		CURRENT->nr_sectors -= CURRENT->current_nr_sectors;
+		CURRENT->sector += CURRENT->current_nr_sectors;
+		end_request(1);
+		redo_fd_request();
+	}
+	return;
+
+  err_end:
+#ifdef TRACKBUFFER
+  BufferDrive = -1;
+#endif
+
+	fd_error();
+}
+
+
+static void fd_times_out(unsigned long dummy)
+{
+	SET_IRQ_HANDLER(NULL);
+	/* If the timeout occured while the readtrack_check timer was
+	 * active, we need to cancel it, else bad things will happen */
+	del_timer( &readtrack_timer ); 
+	FDC1772_WRITE(FDC1772REG_CMD, FDC1772CMD_FORCI);
+	udelay(25);
+
+	printk("floppy timeout\n");
+	STOP_TIMEOUT();		/* hmm - should we do this ? */
+	fd_error();
+}
+
+
+/* The (noop) seek operation here is needed to make the WP bit in the
+ * FDC1772 status register accessible for check_change. If the last disk
+ * operation would have been a RDSEC, this bit would always read as 0
+ * no matter what :-( To save time, the seek goes to the track we're
+ * already on.
+ */
+
+static void finish_fdc(void)
+{
+	/* DAG - just try without this dummy seek! */
+	finish_fdc_done(0);
+	return;
+
+	if (!NeedSeek) {
+		finish_fdc_done(0);
+	} else {
+		DPRINT(("finish_fdc: dummy seek started\n"));
+		FDC1772_WRITE(FDC1772REG_DATA, unit[SelectedDrive].track);
+		SET_IRQ_HANDLER(finish_fdc_done);
+		FDC1772_WRITE(FDC1772REG_CMD, FDC1772CMD_SEEK);
+		MotorOn = 1;
+		START_TIMEOUT();
+		/* we must wait for the IRQ here, because the ST-DMA is
+		 * released immediatly afterwards and the interrupt may be
+		 * delivered to the wrong driver.
+		 */
+	}
+}
+
+
+static void finish_fdc_done(int dummy)
+{
+	unsigned long flags;
+
+	DPRINT(("finish_fdc_done entered\n"));
+	STOP_TIMEOUT();
+	NeedSeek = 0;
+
+	if ((timer_active & (1 << FLOPPY_TIMER)) &&
+	    timer_table[FLOPPY_TIMER].expires < jiffies + 5)
+		/* If the check for a disk change is done too early after this
+		 * last seek command, the WP bit still reads wrong :-((
+		 */
+		timer_table[FLOPPY_TIMER].expires = jiffies + 5;
+	else {
+		/*      START_CHECK_CHANGE_TIMER( CHECK_CHANGE_DELAY ); */
+	};
+	del_timer(&motor_off_timer);
+	START_MOTOR_OFF_TIMER(FD_MOTOR_OFF_DELAY);
+
+	save_flags(flags);
+	cli();
+	/* stdma_release(); - not sure if I should do something DAG  */
+	fdc_busy = 0;
+	wake_up(&fdc_wait);
+	restore_flags(flags);
+
+	DPRINT(("finish_fdc() finished\n"));
+}
+
+
+/* Prevent "aliased" accesses. */
+static fd_ref[4] =
+{0, 0, 0, 0};
+static fd_device[4] =
+{0, 0, 0, 0};
+
+/*
+ * Current device number. Taken either from the block header or from the
+ * format request descriptor.
+ */
+#define CURRENT_DEVICE (CURRENT->rq_dev)
+
+/* Current error count. */
+#define CURRENT_ERRORS (CURRENT->errors)
+
+
+/* dummy for blk.h */
+static void floppy_off(unsigned int nr)
+{
+}
+
+
+/* On the old arcs write protect depends on the particular model
+   of machine.  On the A310, R140, and A440 there is a disc changed
+   detect, however on the A4x0/1 range there is not.  There
+   is nothing to tell you which machine your on.
+   At the moment I'm just marking changed always. I've
+   left the Atari's 'change on write protect change' code in this
+   part (but nothing sets it).
+   RiscOS apparently checks the disc serial number etc. to detect changes
+   - but if it sees a disc change line go high (?) it flips to using
+   it. Well  maybe I'll add that in the future (!?)
+*/
+static int check_floppy_change(dev_t dev)
+{
+	unsigned int drive = (dev & 0x03);
+
+	if (MAJOR(dev) != MAJOR_NR) {
+		printk("floppy_changed: not a floppy\n");
+		return 0;
+	}
+	if (test_bit(drive, &fake_change)) {
+		/* simulated change (e.g. after formatting) */
+		return 1;
+	}
+	if (test_bit(drive, &changed_floppies)) {
+		/* surely changed (the WP signal changed at least once) */
+		return 1;
+	}
+	if (unit[drive].wpstat) {
+		/* WP is on -> could be changed: to be sure, buffers should be
+		   * invalidated...
+		 */
+		return 1;
+	}
+	return 1; /* DAG - was 0 */
+}
+
+static int floppy_revalidate(dev_t dev)
+{
+	int drive = dev & 3;
+
+	if (test_bit(drive, &changed_floppies) || test_bit(drive, &fake_change)
+	    || unit[drive].disktype == 0) {
+#ifdef TRACKBUFFER
+      BufferDrive = -1;
+#endif
+		clear_bit(drive, &fake_change);
+		clear_bit(drive, &changed_floppies);
+		unit[drive].disktype = 0;
+	}
+	return 0;
+}
+
+static __inline__ void copy_buffer(void *from, void *to)
+{
+	ulong *p1 = (ulong *) from, *p2 = (ulong *) to;
+	int cnt;
+
+	for (cnt = 512 / 4; cnt; cnt--)
+		*p2++ = *p1++;
+}
+
+
+/* This sets up the global variables describing the current request. */
+
+static void setup_req_params(int drive)
+{
+	int block = ReqBlock + ReqCnt;
+
+	ReqTrack = block / unit[drive].disktype->spt;
+	ReqSector = block - ReqTrack * unit[drive].disktype->spt + 1;
+	ReqSide = ReqTrack & 1;
+	ReqTrack >>= 1;
+	ReqData = ReqBuffer + 512 * ReqCnt;
+
+#ifdef TRACKBUFFER
+  read_track = (ReqCmd == READ && CURRENT_ERRORS == 0);
+#endif
+
+	DPRINT(("Request params: Si=%d Tr=%d Se=%d Data=%08lx\n", ReqSide,
+		ReqTrack, ReqSector, (unsigned long) ReqData));
+}
+
+
+static void redo_fd_request(void)
+{
+	int device, drive, type;
+	struct archy_floppy_struct *floppy;
+
+	DPRINT(("redo_fd_request: CURRENT=%08lx CURRENT->rq_dev=%04x CURRENT->sector=%ld\n",
+		(unsigned long) CURRENT, CURRENT ? CURRENT->rq_dev : 0,
+		CURRENT ? CURRENT->sector : 0));
+
+	if (CURRENT && CURRENT->rq_status == RQ_INACTIVE)
+		goto the_end;
+
+      repeat:
+
+	if (!CURRENT)
+		goto the_end;
+
+	if (MAJOR(CURRENT->rq_dev) != MAJOR_NR)
+		panic(DEVICE_NAME ": request list destroyed");
+
+	if (CURRENT->bh) {
+		if (!buffer_locked(CURRENT->bh))
+			panic(DEVICE_NAME ": block not locked");
+	}
+	device = MINOR(CURRENT_DEVICE);
+	drive = device & 3;
+	type = device >> 2;
+	floppy = &unit[drive];
+
+	if (!floppy->connected) {
+		/* drive not connected */
+		printk("Unknown Device: fd%d\n", drive);
+		end_request(0);
+		goto repeat;
+	}
+	if (type == 0) {
+		if (!floppy->disktype) {
+			Probing = 1;
+			floppy->disktype = disk_type + NUM_DISK_TYPES - 1;
+			floppy_sizes[drive] = floppy->disktype->blocks >> 1;
+			floppy->autoprobe = 1;
+		}
+	} else {
+		/* user supplied disk type */
+		--type;
+		if (type >= NUM_DISK_TYPES) {
+			printk("fd%d: invalid disk format", drive);
+			end_request(0);
+			goto repeat;
+		}
+		floppy->disktype = &disk_type[type];
+		floppy_sizes[drive] = disk_type[type].blocks >> 1;
+		floppy->autoprobe = 0;
+	}
+
+	if (CURRENT->sector + 1 > floppy->disktype->blocks) {
+		end_request(0);
+		goto repeat;
+	}
+	/* stop deselect timer */
+	del_timer(&motor_off_timer);
+
+	ReqCnt = 0;
+	ReqCmd = CURRENT->cmd;
+	ReqBlock = CURRENT->sector;
+	ReqBuffer = CURRENT->buffer;
+	setup_req_params(drive);
+	do_fd_action(drive);
+
+	return;
+
+      the_end:
+	finish_fdc();
+}
+
+static void fd1772_checkint(void)
+{
+  extern int fdc1772_bytestogo;
+
+  /*printk("fd1772_checkint %d\n",fdc1772_fdc_int_done);*/
+  if (fdc1772_fdc_int_done)
+    floppy_irqconsequencehandler();
+  if ((MultReadInProgress) && (fdc1772_bytestogo==0)) fd_readtrack_check(0);
+  if (fdc_busy) {
+    queue_task(&fd1772_tq,&tq_immediate);
+    mark_bh(IMMEDIATE_BH);
+  };
+};
+
+void do_fd_request(void)
+{
+	unsigned long flags;
+
+	DPRINT(("do_fd_request for pid %d\n", current->pid));
+	if (fdc_busy) return;
+	save_flags(flags);
+	cli();
+	while (fdc_busy)
+		sleep_on(&fdc_wait);
+	fdc_busy = 1;
+	ENABLE_IRQ();
+	restore_flags(flags);
+
+	fdc1772_fdc_int_done = 0;
+
+	redo_fd_request();
+
+  queue_task(&fd1772_tq,&tq_immediate);
+  mark_bh(IMMEDIATE_BH);
+}
+
+
+static int invalidate_drive(int rdev)
+{
+	/* invalidate the buffer track to force a reread */
+#ifdef TRACKBUFFER
+  BufferDrive = -1;
+#endif
+
+	set_bit(rdev & 3, &fake_change);
+	check_disk_change(rdev);
+	return 0;
+}
+
+static int fd_ioctl(struct inode *inode, struct file *filp,
+		    unsigned int cmd, unsigned long param)
+{
+#define IOCTL_MODE_BIT 8
+#define OPEN_WRITE_BIT 16
+#define IOCTL_ALLOWED (filp && (filp->f_mode & IOCTL_MODE_BIT))
+
+	int drive, device;
+
+	device = inode->i_rdev;
+	switch (cmd) {
+		RO_IOCTLS(inode->i_rdev, param);
+	}
+	drive = MINOR(device);
+	if (!IOCTL_ALLOWED)
+		return -EPERM;
+	switch (cmd) {
+	case FDFMTBEG:
+		return 0;
+		/* case FDC1772LRPRM:  ??? DAG what does this do?? 
+		   unit[drive].disktype = NULL;
+		   floppy_sizes[drive] = MAX_DISK_SIZE;
+		   return invalidate_drive (device); */
+	case FDFMTEND:
+	case FDFLUSH:
+		return invalidate_drive(drive);
+	}
+	if (!suser())
+		return -EPERM;
+	if (drive < 0 || drive > 3)
+		return -EINVAL;
+	switch (cmd) {
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+
+/* Initialize the 'unit' variable for drive 'drive' */
+
+static void fd_probe(int drive)
+{
+	unit[drive].connected = 0;
+	unit[drive].disktype = NULL;
+
+	if (!fd_test_drive_present(drive))
+		return;
+
+	unit[drive].connected = 1;
+	unit[drive].track = -1; /* If we put the auto detect back in this can go to 0 */
+	unit[drive].steprate = FDC1772STEP_6;
+	MotorOn = 1;		/* from probe restore operation! */
+}
+
+
+/* This function tests the physical presence of a floppy drive (not
+ * whether a disk is inserted). This is done by issuing a restore
+ * command, waiting max. 2 seconds (that should be enough to move the
+ * head across the whole disk) and looking at the state of the "TR00"
+ * signal. This should now be raised if there is a drive connected
+ * (and there is no hardware failure :-) Otherwise, the drive is
+ * declared absent.
+ */
+
+static int fd_test_drive_present(int drive)
+{
+	unsigned long timeout;
+	unsigned char status;
+	int ok;
+
+	printk("fd_test_drive_present %d\n", drive);
+	if (drive > 1)
+		return (0);
+	return (1);		/* Simple hack for the moment - the autodetect doesn't seem to work on arc */
+	fd_select_drive(drive);
+
+	/* disable interrupt temporarily */
+	DISABLE_IRQ();
+	FDC1772_WRITE(FDC1772REG_TRACK, 0x00);	/* was ff00 why? */
+	FDC1772_WRITE(FDC1772REG_CMD, FDC1772CMD_RESTORE | FDC1772CMDADD_H | FDC1772STEP_6);
+
+	/*printk("fd_test_drive_present: Going into timeout loop\n"); */
+	for (ok = 0, timeout = jiffies + 2 * HZ + HZ / 2; jiffies < timeout;) {
+		/*  What does this piece of atariism do? - query for an interrupt? */
+		/*  if (!(mfp.par_dt_reg & 0x20))
+		   break; */
+		/* Well this is my nearest guess - quit when we get an FDC interrupt */
+		if (IOC_FIQSTAT & 2)
+			break;
+	}
+
+	/*printk("fd_test_drive_present: Coming out of timeout loop\n"); */
+	status = FDC1772_READ(FDC1772REG_STATUS);
+	ok = (status & FDC1772STAT_TR00) != 0;
+
+	/*printk("fd_test_drive_present: ok=%d\n",ok); */
+	/* force interrupt to abort restore operation (FDC1772 would try
+	 * about 50 seconds!) */
+	FDC1772_WRITE(FDC1772REG_CMD, FDC1772CMD_FORCI);
+	udelay(500);
+	status = FDC1772_READ(FDC1772REG_STATUS);
+	udelay(20);
+	/*printk("fd_test_drive_present: just before OK code %d\n",ok); */
+
+	if (ok) {
+		/* dummy seek command to make WP bit accessible */
+		FDC1772_WRITE(FDC1772REG_DATA, 0);
+		FDC1772_WRITE(FDC1772REG_CMD, FDC1772CMD_SEEK);
+		printk("fd_test_drive_present: just before wait for int\n");
+		/* DAG: Guess means wait for interrupt */
+		while (!(IOC_FIQSTAT & 2));
+		printk("fd_test_drive_present: just after wait for int\n");
+		status = FDC1772_READ(FDC1772REG_STATUS);
+	}
+	printk("fd_test_drive_present: just before ENABLE_IRQ\n");
+	ENABLE_IRQ();
+	printk("fd_test_drive_present: about to return\n");
+	return (ok);
+}
+
+
+/* Look how many and which kind of drives are connected. If there are
+ * floppies, additionally start the disk-change and motor-off timers.
+ */
+
+static void config_types(void)
+{
+	int drive, cnt = 0;
+
+	printk("Probing floppy drive(s):\n");
+	for (drive = 0; drive < FD_MAX_UNITS; drive++) {
+		fd_probe(drive);
+		if (unit[drive].connected) {
+			printk("fd%d\n", drive);
+			++cnt;
+		}
+	}
+
+	if (FDC1772_READ(FDC1772REG_STATUS) & FDC1772STAT_BUSY) {
+		/* If FDC1772 is still busy from probing, give it another FORCI
+		 * command to abort the operation. If this isn't done, the FDC1772
+		 * will interrupt later and its IRQ line stays low, because
+		 * the status register isn't read. And this will block any
+		 * interrupts on this IRQ line :-(
+		 */
+		FDC1772_WRITE(FDC1772REG_CMD, FDC1772CMD_FORCI);
+		udelay(500);
+		FDC1772_READ(FDC1772REG_STATUS);
+		udelay(20);
+	}
+	if (cnt > 0) {
+		START_MOTOR_OFF_TIMER(FD_MOTOR_OFF_DELAY);
+		if (cnt == 1)
+			fd_select_drive(0);
+		/*START_CHECK_CHANGE_TIMER( CHECK_CHANGE_DELAY ); */
+	}
+}
+
+/*
+ * floppy_open check for aliasing (/dev/fd0 can be the same as
+ * /dev/PS0 etc), and disallows simultaneous access to the same
+ * drive with different device numbers.
+ */
+
+static int floppy_open(struct inode *inode, struct file *filp)
+{
+	int drive;
+	int old_dev;
+
+	if (!filp) {
+		DPRINT(("Weird, open called with filp=0\n"));
+		return -EIO;
+	}
+	drive = MINOR(inode->i_rdev) & 3;
+	if ((MINOR(inode->i_rdev) >> 2) > NUM_DISK_TYPES)
+		return -ENXIO;
+
+	old_dev = fd_device[drive];
+
+	if (fd_ref[drive])
+		if (old_dev != inode->i_rdev)
+			return -EBUSY;
+
+	if (fd_ref[drive] == -1 || (fd_ref[drive] && filp->f_flags & O_EXCL))
+		return -EBUSY;
+
+	if (filp->f_flags & O_EXCL)
+		fd_ref[drive] = -1;
+	else
+		fd_ref[drive]++;
+
+	fd_device[drive] = inode->i_rdev;
+
+	if (old_dev && old_dev != inode->i_rdev)
+		invalidate_buffers(old_dev);
+
+	/* Allow ioctls if we have write-permissions even if read-only open */
+	if (filp->f_mode & 2 || permission(inode, 2) == 0)
+		filp->f_mode |= IOCTL_MODE_BIT;
+	if (filp->f_mode & 2)
+		filp->f_mode |= OPEN_WRITE_BIT;
+
+	if (filp->f_flags & O_NDELAY)
+		return 0;
+
+	if (filp->f_mode & 3) {
+		check_disk_change(inode->i_rdev);
+		if (filp->f_mode & 2) {
+			if (unit[drive].wpstat) {
+				floppy_release(inode, filp);
+				return -EROFS;
+			}
+		}
+	}
+	return 0;
+}
+
+
+static void floppy_release(struct inode *inode, struct file *filp)
+{
+	int drive;
+
+	drive = inode->i_rdev & 3;
+
+	if (!filp || (filp->f_mode & (2 | OPEN_WRITE_BIT)))
+		/* if the file is mounted OR (writable now AND writable at open
+		   time) Linus: Does this cover all cases? */
+		block_fsync(inode, filp);
+
+	if (fd_ref[drive] < 0)
+		fd_ref[drive] = 0;
+	else if (!fd_ref[drive]--) {
+		printk("floppy_release with fd_ref == 0");
+		fd_ref[drive] = 0;
+	}
+}
+
+static struct file_operations floppy_fops =
+{
+	NULL,			/* lseek - default */
+	block_read,		/* read - general block-dev read */
+	block_write,		/* write - general block-dev write */
+	NULL,			/* readdir - bad */
+	NULL,			/* select */
+	fd_ioctl,		/* ioctl */
+	NULL,			/* mmap */
+	floppy_open,		/* open */
+	floppy_release,		/* release */
+	block_fsync,		/* fsync */
+	NULL,			/* fasync */
+	check_floppy_change,	/* media_change */
+	floppy_revalidate,	/* revalidate */
+};
+
+
+int floppy_init(void)
+{
+	int i;
+
+	if (register_blkdev(MAJOR_NR, "fd", &floppy_fops)) {
+		printk("Unable to get major %d for floppy\n", MAJOR_NR);
+		return 1;
+	}
+
+	if (request_dma(FLOPPY_DMA, "fd1772")) {
+		printk("Unable to grab DMA%d for the floppy (1772) driver\n", FLOPPY_DMA);
+		return 1;
+	};
+
+	if (request_dma(FIQ_FD1772, "fd1772 end")) {
+		printk("Unable to grab DMA%d for the floppy (1772) driver\n", FIQ_FD1772);
+		free_dma(FLOPPY_DMA);
+		return 1;
+	};
+	enable_dma(FIQ_FD1772);	/* This inserts a call to our command end routine */
+
+	/* initialize variables */
+	SelectedDrive = -1;
+#ifdef TRACKBUFFER
+  BufferDrive = -1;
+#endif
+
+	/* initialize check_change timer */
+	timer_table[FLOPPY_TIMER].fn = check_change;
+	timer_active &= ~(1 << FLOPPY_TIMER);
+
+
+#ifdef TRACKBUFFER
+  DMABuffer = (char *)kmalloc((MAX_SECTORS+1)*512,GFP_KERNEL); /* Atari uses 512 - I want to eventually cope with 1K sectors */
+  TrackBuffer = DMABuffer + 512;
+#else
+	/* Allocate memory for the DMAbuffer - on the Atari this takes it
+	   out of some special memory... */
+	DMABuffer = (char *) kmalloc(2048);	/* Copes with pretty large sectors */
+#endif
+#ifdef TRACKBUFFER  
+  BufferDrive = BufferSide = BufferTrack = -1;
+#endif
+
+	for (i = 0; i < FD_MAX_UNITS; i++) {
+		unit[i].track = -1;
+	}
+
+	for (i = 0; i < 256; i++)
+		if ((i >> 2) > 0 && (i >> 2) <= NUM_DISK_TYPES)
+			floppy_sizes[i] = disk_type[(i >> 2) - 1].blocks >> 1;
+		else
+			floppy_sizes[i] = MAX_DISK_SIZE;
+
+	blk_size[MAJOR_NR] = floppy_sizes;
+	blksize_size[MAJOR_NR] = floppy_blocksizes;
+	blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
+
+	config_types();
+
+	return 0;
+}
+
+/* Just a dummy at the moment */
+void floppy_setup(char *str, int *ints)
+{
+}
+
+void floppy_eject(void) {
+}
diff -u --recursive --new-file v2.1.87/linux/drivers/acorn/block/ide-ics.c linux/drivers/acorn/block/ide-ics.c
--- v2.1.87/linux/drivers/acorn/block/ide-ics.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/acorn/block/ide-ics.c	Mon Feb 16 14:26:56 1998
@@ -0,0 +1,271 @@
+/*
+ * linux/arch/arm/drivers/block/ide-ics.c
+ *
+ * Copyright (c) 1996,1997 Russell King.
+ *
+ * Changelog:
+ *  08-06-1996	RMK	Created
+ *  12-09-1997	RMK	Added interrupt enable/disable
+ */
+
+#include <linux/module.h>
+#include <linux/ioport.h>
+#include <linux/malloc.h>
+#include <linux/blkdev.h>
+#include <linux/errno.h>
+#include <linux/hdreg.h>
+
+#include <asm/ecard.h>
+#include <asm/io.h>
+
+#include "../../block/ide.h"
+
+/*
+ * Maximum number of interfaces per card
+ */
+#define MAX_IFS	2
+
+#define ICS_IDENT_OFFSET		0x8a0
+
+#define ICS_ARCIN_V5_INTROFFSET		0x001
+#define ICS_ARCIN_V5_IDEOFFSET		0xa00
+#define ICS_ARCIN_V5_IDEALTOFFSET	0xae0
+#define ICS_ARCIN_V5_IDESTEPPING	4
+
+#define ICS_ARCIN_V6_IDEOFFSET_1	0x800
+#define ICS_ARCIN_V6_INTROFFSET_1	0x880
+#define ICS_ARCIN_V6_IDEALTOFFSET_1	0x8e0
+#define ICS_ARCIN_V6_IDEOFFSET_2	0xc00
+#define ICS_ARCIN_V6_INTROFFSET_2	0xc80
+#define ICS_ARCIN_V6_IDEALTOFFSET_2	0xce0
+#define ICS_ARCIN_V6_IDESTEPPING	4
+
+static const card_ids icside_cids[] = {
+	{ MANU_ICS, PROD_ICS_IDE },
+	{ 0xffff, 0xffff }
+};
+
+typedef enum {
+	ics_if_unknown,
+	ics_if_arcin_v5,
+	ics_if_arcin_v6
+} iftype_t;
+
+static struct expansion_card *ec[MAX_ECARDS];
+static int result[MAX_ECARDS][MAX_IFS];
+
+
+/* ---------------- Version 5 PCB Support Functions --------------------- */
+/* Prototype: icside_irqenable_arcin_v5 (struct expansion_card *ec, int irqnr)
+ * Purpose  : enable interrupts from card
+ */
+static void icside_irqenable_arcin_v5 (struct expansion_card *ec, int irqnr)
+{
+	unsigned int memc_port = (unsigned int)ec->irq_data;
+	outb (0, memc_port + ICS_ARCIN_V5_INTROFFSET);
+}
+
+/* Prototype: icside_irqdisable_arcin_v5 (struct expansion_card *ec, int irqnr)
+ * Purpose  : disable interrupts from card
+ */
+static void icside_irqdisable_arcin_v5 (struct expansion_card *ec, int irqnr)
+{
+	unsigned int memc_port = (unsigned int)ec->irq_data;
+	inb (memc_port + ICS_ARCIN_V5_INTROFFSET);
+}
+
+static const expansioncard_ops_t icside_ops_arcin_v5 = {
+	icside_irqenable_arcin_v5,
+	icside_irqdisable_arcin_v5,
+	NULL,
+	NULL
+};
+
+
+/* ---------------- Version 6 PCB Support Functions --------------------- */
+/* Prototype: icside_irqenable_arcin_v6 (struct expansion_card *ec, int irqnr)
+ * Purpose  : enable interrupts from card
+ */
+static void icside_irqenable_arcin_v6 (struct expansion_card *ec, int irqnr)
+{
+	unsigned int ide_base_port = (unsigned int)ec->irq_data;
+	outb (0, ide_base_port + ICS_ARCIN_V6_INTROFFSET_1);
+	outb (0, ide_base_port + ICS_ARCIN_V6_INTROFFSET_2);
+}
+
+/* Prototype: icside_irqdisable_arcin_v6 (struct expansion_card *ec, int irqnr)
+ * Purpose  : disable interrupts from card
+ */
+static void icside_irqdisable_arcin_v6 (struct expansion_card *ec, int irqnr)
+{
+	unsigned int ide_base_port = (unsigned int)ec->irq_data;
+	inb (ide_base_port + ICS_ARCIN_V6_INTROFFSET_1);
+	inb (ide_base_port + ICS_ARCIN_V6_INTROFFSET_2);
+}
+
+static const expansioncard_ops_t icside_ops_arcin_v6 = {
+	icside_irqenable_arcin_v6,
+	icside_irqdisable_arcin_v6,
+	NULL,
+	NULL
+};
+
+
+
+/* Prototype: icside_identifyif (struct expansion_card *ec)
+ * Purpose  : identify IDE interface type
+ * Notes    : checks the description string
+ */
+static iftype_t icside_identifyif (struct expansion_card *ec)
+{
+	unsigned int addr;
+	iftype_t iftype;
+	int id = 0;
+
+	iftype = ics_if_unknown;
+
+	addr = ecard_address (ec, ECARD_IOC, ECARD_FAST) + ICS_IDENT_OFFSET;
+
+	id = inb (addr) & 1;
+	id |= (inb (addr + 1) & 1) << 1;
+	id |= (inb (addr + 2) & 1) << 2;
+	id |= (inb (addr + 3) & 1) << 3;
+
+	switch (id) {
+	case 0: /* A3IN */
+		printk ("icside: A3IN unsupported\n");
+		break;
+
+	case 1: /* A3USER */
+		printk ("icside: A3USER unsupported\n");
+		break;
+
+	case 3:	/* ARCIN V6 */
+		printk ("icside: detected ARCIN V6 in slot %d\n", ec->slot_no);
+		iftype = ics_if_arcin_v6;
+		break;
+
+	case 15:/* ARCIN V5 (no id) */
+		printk ("icside: detected ARCIN V5 in slot %d\n", ec->slot_no);
+		iftype = ics_if_arcin_v5;
+		break;
+
+	default:/* we don't know - complain very loudly */
+		printk ("icside: ***********************************\n");
+		printk ("icside: *** UNKNOWN ICS INTERFACE id=%d ***\n", id);
+		printk ("icside: ***********************************\n");
+		printk ("icside: please report this to: linux@arm.uk.linux.org\n");
+		printk ("icside: defaulting to ARCIN V5\n");
+		iftype = ics_if_arcin_v5;
+		break;
+	}
+
+	return iftype;
+}
+
+/* Prototype: icside_register (struct expansion_card *ec)
+ * Purpose  : register an ICS IDE card with the IDE driver
+ * Notes    : we make sure that interrupts are disabled from the card
+ */
+static inline void icside_register (struct expansion_card *ec, int index)
+{
+	unsigned long port;
+
+	result[index][0] = -1;
+	result[index][1] = -1;
+
+	switch (icside_identifyif (ec)) {
+	case ics_if_unknown:
+	default:
+		printk ("** Warning: ICS IDE Interface unrecognised! **\n");
+		break;
+
+	case ics_if_arcin_v5:
+		port = ecard_address (ec, ECARD_MEMC, 0);
+		ec->irq_data = (void *)port;
+		ec->ops = (expansioncard_ops_t *)&icside_ops_arcin_v5;
+
+		/*
+		 * Be on the safe side - disable interrupts
+		 */
+		inb (port + ICS_ARCIN_V5_INTROFFSET);
+		result[index][0] =
+			ide_register_port (port + ICS_ARCIN_V5_IDEOFFSET,
+					   port + ICS_ARCIN_V5_IDEALTOFFSET,
+					   ICS_ARCIN_V5_IDESTEPPING,
+					   ec->irq);
+		break;
+
+	case ics_if_arcin_v6:
+		port = ecard_address (ec, ECARD_IOC, ECARD_FAST);
+		ec->irq_data = (void *)port;
+		ec->ops = (expansioncard_ops_t *)&icside_ops_arcin_v6;
+
+		/*
+		 * Be on the safe side - disable interrupts
+		 */
+		inb (port + ICS_ARCIN_V6_INTROFFSET_1);
+		inb (port + ICS_ARCIN_V6_INTROFFSET_2);
+		result[index][0] =
+			ide_register_port (port + ICS_ARCIN_V6_IDEOFFSET_1,
+					   port + ICS_ARCIN_V6_IDEALTOFFSET_1,
+					   ICS_ARCIN_V6_IDESTEPPING,
+					   ec->irq);
+		result[index][1] =
+			ide_register_port (port + ICS_ARCIN_V6_IDEOFFSET_2,
+					   port + ICS_ARCIN_V6_IDEALTOFFSET_2,
+					   ICS_ARCIN_V6_IDESTEPPING,
+					   ec->irq);
+		break;
+	}		
+}
+
+int icside_init (void)
+{
+	int i;
+
+	for (i = 0; i < MAX_ECARDS; i++)
+		ec[i] = NULL;
+
+	ecard_startfind ();
+
+	for (i = 0; ; i++) {
+		if ((ec[i] = ecard_find (0, icside_cids)) == NULL)
+			break;
+
+		ecard_claim (ec[i]);
+		icside_register (ec[i], i);
+	}
+
+	for (i = 0; i < MAX_ECARDS; i++)
+		if (ec[i] && result[i][0] < 0 && result[i][1] < 0) {
+			ecard_release (ec[i]);
+			ec[i] = NULL;
+		}
+	return 0;
+}
+
+#ifdef MODULE
+int init_module (void)
+{
+	return icside_init();
+}
+
+void cleanup_module (void)
+{
+	int i;
+
+	for (i = 0; i < MAX_ECARDS; i++)
+		if (ec[i]) {
+			if (result[i][0] >= 0)
+				ide_unregister (result[i][0]);
+
+			if (result[i][1] >= 0)
+				ide_unregister (result[i][1]);
+				
+			ecard_release (ec[i]);
+			ec[i] = NULL;
+		}
+}
+#endif
+
diff -u --recursive --new-file v2.1.87/linux/drivers/acorn/block/ide-rapide.c linux/drivers/acorn/block/ide-rapide.c
--- v2.1.87/linux/drivers/acorn/block/ide-rapide.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/acorn/block/ide-rapide.c	Mon Feb 16 14:27:05 1998
@@ -0,0 +1,78 @@
+/*
+ * linux/arch/arm/drivers/block/ide-ics.c
+ *
+ * Copyright (c) 1996 Russell King.
+ *
+ * Changelog:
+ *  08-06-1996	RMK	Created
+ */
+
+#include <linux/module.h>
+#include <linux/malloc.h>
+#include <linux/blkdev.h>
+#include <linux/errno.h>
+#include <asm/ecard.h>
+
+#include "../../block/ide.h"
+
+static const card_ids rapide_cids[] = {
+	{ 0xffff, 0xffff }
+};
+
+static struct expansion_card *ec[MAX_ECARDS];
+static int result[MAX_ECARDS];
+
+static inline int rapide_register (struct expansion_card *ec)
+{
+	unsigned long port = ecard_address (ec, ECARD_MEMC, 0);
+
+	return ide_register_port (port, port + 0x206, 4, ec->irq);
+}
+
+int rapide_init (void)
+{
+	int i;
+
+	for (i = 0; i < MAX_ECARDS; i++)
+		ec[i] = NULL;
+
+	ecard_startfind ();
+
+	for (i = 0; ; i++) {
+		if ((ec[i] = ecard_find (0, rapide_cids)) == NULL)
+			break;
+
+		ecard_claim (ec[i]);
+		result[i] = rapide_register (ec[i]);
+	}
+	for (i = 0; i < MAX_ECARDS; i++)
+		if (ec[i] && result[i] < 0) {
+			ecard_release (ec[i]);
+			ec[i] = NULL;
+	}
+	return 0;
+}
+
+#ifdef MODULE
+
+int init_module (void)
+{
+	return rapide_init();
+}
+
+void cleanup_module (void)
+{
+	int i;
+
+	for (i = 0; i < MAX_ECARDS; i++)
+		if (ec[i]) {
+			unsigned long port;
+			port = ecard_address (ec[i], ECARD_MEMC, 0);
+
+			ide_unregister_port (port, ec[i]->irq, 16);
+			ecard_release (ec[i]);
+			ec[i] = NULL;
+		}
+}
+#endif
+
diff -u --recursive --new-file v2.1.87/linux/drivers/acorn/block/mfmhd.c linux/drivers/acorn/block/mfmhd.c
--- v2.1.87/linux/drivers/acorn/block/mfmhd.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/acorn/block/mfmhd.c	Mon Jan 26 14:03:39 1998
@@ -0,0 +1,1549 @@
+/*
+ * linux/arch/arm/drivers/block/mfmhd.c
+ *
+ * Copyright (C) 1995, 1996 Russell King, Dave Alan Gilbert (gilbertd@cs.man.ac.uk)
+ *
+ * MFM hard drive code [experimental]
+ */
+
+/*
+ * Change list:
+ *
+ *  3/2/96:DAG: Started a change list :-)
+ *              Set the hardsect_size pointers up since we are running 256 byte
+ *                sectors
+ *              Added DMA code, put it into the rw_intr
+ *              Moved RCAL out of generic interrupt code - don't want to do it
+ *                while DMA'ing - its now in individual handlers.
+ *              Took interrupt handlers off task queue lists and called
+ *                directly - not sure of implications.
+ *
+ * 18/2/96:DAG: Well its reading OK I think, well enough for image file code
+ *              to find the image file; but now I've discovered that I actually
+ *              have to put some code in for image files.
+ *
+ *              Added stuff for image files; seems to work, but I've not
+ *              got a multisegment image file (I don't think!).
+ *              Put in a hack (yep a real hack) for multiple cylinder reads.
+ *              Not convinced its working.
+ *
+ *  5/4/96:DAG: Added asm/hardware.h and use IOC_ macros
+ *              Rewrote dma code in mfm.S (again!) - now takes a word at a time
+ *              from main RAM for speed; still doesn't feel speedy!
+ *
+ * 20/4/96:DAG: After rewriting mfm.S a heck of a lot of times and speeding
+ *              things up, I've finally figured out why its so damn slow.
+ *              Linux is only reading a block at a time, and so you never
+ *              get more than 1K per disc revoloution ~=60K/second.
+ *
+ * 27/4/96:DAG: On Russell's advice I change ll_rw_blk.c to ask it to
+ *              join adjacent blocks together. Everything falls flat on its
+ *              face.
+ *              Four hours of debugging later; I hadn't realised that
+ *              ll_rw_blk would be so generous as to join blocks whose
+ *              results aren't going into consecutive buffers.
+ * 
+ *              OK; severe rehacking of mfm_rw_interrupt; now end_request's
+ *              as soon as its DMA'd each request.  Odd thing is that
+ *              we are sometimes getting interrupts where we are not transferring
+ *              any data; why? Is that what happens when you miss? I doubt
+ *              it; are we too fast? No - its just at command ends. Got 240K/s
+ *              better than before, but RiscOS hits 480K/s
+ *
+ * 25/6/96:RMK: Fixed init code to allow the MFM podule to work.  Increased the
+ *              number of errors for my Miniscribe drive (8425).
+ *
+ * 30/6/96:DAG: Russell suggested that a check drive 0 might turn the LEDs off
+ *              - so in request_done just before it clears Busy it sends a
+ *              check drive 0 - and the LEDs go off!!!!
+ *
+ *              Added test for mainboard controller. - Removes need for separate
+ *              define.
+ *
+ * 13/7/96:DAG: Changed hardware sectore size to 512 in attempt to make
+ *              IM drivers work.
+ * 21/7/96:DAG: Took out old image file stuff (accessing it now produces an IO
+ *              error.)
+ *
+ * 17/8/96:DAG: Ran through indent -kr -i8; evil - all my nice 2 character indents
+ *              gone :-( Hand modified afterwards.
+ *		Took out last remains of the older image map system.
+ *
+ * 22/9/96:DAG:	Changed mfm.S so it will carry on DMA'ing til; BSY is dropped
+ *		Changed mfm_rw_intr so that it doesn't follow the error
+ *		code until BSY is dropped. Nope - still broke. Problem
+ *		may revolve around when it reads the results for the error
+ *		number?
+ *
+ *16/11/96:DAG:	Modified for 2.0.18; request_irq changed
+ *
+ *17/12/96:RMK: Various cleanups, reorganisation, and the changes for new IO system.
+ *		Improved probe for onboard MFM chip - it was hanging on my A5k.
+ *		Added autodetect CHS code such that we don't rely on the presence
+ *		of an ADFS boot block.  Added ioport resource manager calls so
+ *		that we don't clash with already-running hardware (eg. RiscPC Ether
+ *		card slots if someone tries this)!
+ *
+ * 17/1/97:RMK:	Upgraded to 2.1 kernels.
+ */
+
+/*
+ * Possible enhancements:
+ *  Multi-thread the code so that it is possible that while one drive
+ *  is seeking, the other one can be reading data/seeking as well.
+ *  This would be a performance boost with dual drive systems.
+ */
+
+#include <linux/module.h>
+#include <linux/config.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/timer.h>
+#include <linux/tqueue.h>
+#include <linux/mm.h>
+#include <linux/errno.h>
+#include <linux/genhd.h>
+#include <linux/major.h>
+#include <linux/ioport.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/irq-no.h>
+#include <asm/uaccess.h>
+#include <asm/delay.h>
+#include <asm/dma.h>
+#include <asm/hardware.h>
+#include <asm/ecard.h>
+
+#define MFM_DISK_MAJOR	13
+#undef  XT_DISK_MAJOR
+#define XT_DISK_MAJOR	-1
+#define MAJOR_NR	MFM_DISK_MAJOR
+#include "blk.h"
+
+/*
+ * This sort of stuff should be in a header file shared with ide.c, hd.c, xd.c etc
+ */
+#ifndef HDIO_GETGEO
+#define HDIO_GETGEO 0x301
+struct hd_geometry {
+	unsigned char heads;
+	unsigned char sectors;
+	unsigned short cylinders;
+	unsigned long start;
+};
+#endif
+
+
+/*
+ * Configuration section
+ *
+ * This is the maximum number of drives that we accept
+ */
+#define MFM_MAXDRIVES 2
+/*
+ * Linux I/O address of onboard MFM controller or 0 to disable this
+ */
+#define ONBOARD_MFM_ADDRESS ((0x002d0000 >> 2) | 0x80000000)
+/*
+ * Uncomment this to enable debugging in the MFM driver...
+ */
+#ifndef DEBUG
+/*#define DEBUG */
+#endif
+/*
+ * List of card types that we recognise
+ */
+static const card_ids mfm_cids[] = {
+	{ MANU_ACORN, PROD_ACORN_MFM },
+	{ 0xffff, 0xffff }
+};
+/*
+ * End of configuration
+ */
+
+ 
+/*
+ * This structure contains all information to do with a particular physical
+ * device.
+ */
+struct mfm_info {
+	unsigned char sectors;
+	unsigned char heads;
+	unsigned short cylinders;
+	unsigned short lowcurrent;
+	unsigned short precomp;
+#define NO_TRACK -1
+#define NEED_1_RECAL -2
+#define NEED_2_RECAL -3
+		 int cylinder;
+	unsigned int access_count;
+	unsigned int busy;
+	struct {
+		char recal;
+		char report;
+		char abort;
+	} errors;
+} mfm_info[MFM_MAXDRIVES];
+
+#define MFM_DRV_INFO mfm_info[raw_cmd.dev]
+
+static struct hd_struct mfm[MFM_MAXDRIVES << 6];
+static int mfm_sizes[MFM_MAXDRIVES << 6];
+static int mfm_blocksizes[MFM_MAXDRIVES << 6];
+static int mfm_sectsizes[MFM_MAXDRIVES << 6];
+static struct wait_queue *mfm_wait_open = NULL;
+
+/* Stuff from the assembly routines */
+extern unsigned int hdc63463_baseaddress;	/* Controller base address */
+extern unsigned int hdc63463_irqpolladdress;	/* Address to read to test for int */
+extern unsigned int hdc63463_irqpollmask;	/* Mask for irq register */
+extern unsigned int hdc63463_dataptr;	/* Pointer to kernel data space to DMA */
+extern int hdc63463_dataleft;	/* Number of bytes left to transfer */
+
+
+
+
+static int lastspecifieddrive;
+static unsigned Busy;
+
+static unsigned int PartFragRead;	/* The number of sectors which have been read
+					   during a partial read split over two
+					   cylinders.  If 0 it means a partial
+					   read did not occur. */
+
+static unsigned int PartFragRead_RestartBlock;	/* Where to restart on a split access */
+static unsigned int PartFragRead_SectorsLeft;	/* Where to restart on a split access */
+
+static int Sectors256LeftInCurrent;	/* i.e. 256 byte sectors left in current */
+static int SectorsLeftInRequest;	/* i.e. blocks left in the thing mfm_request was called for */
+static int Copy_Sector;		/* The 256 byte sector we are currently at - fragments need to know 
+				   where to take over */
+static char *Copy_buffer;
+
+
+static void mfm_seek(void);
+static void mfm_rerequest(void);
+static void mfm_request(void);
+static int mfm_reread_partitions(kdev_t dev);
+static void mfm_specify (void);
+static void issue_request(int dev, unsigned int block, unsigned int nsect,
+			  struct request *req);
+
+#define mfm_init xd_init
+#define mfm_setup xd_setup
+
+static unsigned int mfm_addr;		/* Controller address */
+static unsigned int mfm_IRQPollLoc;	/* Address to read for IRQ information */
+static unsigned int mfm_irqenable;	/* Podule IRQ enable location */
+static unsigned char mfm_irq;		/* Interrupt number */
+static int mfm_drives = 0;		/* drives available */
+static int mfm_status = 0;		/* interrupt status */
+static int *errors;
+
+static struct rawcmd {
+	unsigned int dev;
+	unsigned int cylinder;
+	unsigned int head;
+	unsigned int sector;
+	unsigned int cmdtype;
+	unsigned int cmdcode;
+	unsigned char cmddata[16];
+	unsigned int cmdlen;
+} raw_cmd;
+
+static unsigned char result[16];
+
+static struct cont {
+	void (*interrupt) (void);	/* interrupt handler */
+	void (*error) (void);	/* error handler */
+	void (*redo) (void);	/* redo handler */
+	void (*done) (int st);	/* done handler */
+} *cont = NULL;
+
+static struct tq_struct mfm_tq = {0, 0, (void (*)(void *)) NULL, 0};
+
+int number_mfm_drives = 1;
+
+/* ------------------------------------------------------------------------------------------ */
+/*
+ * From the HD63463 data sheet from Hitachi Ltd.
+ */
+
+#define MFM_COMMAND (mfm_addr + 0)
+#define MFM_DATAOUT (mfm_addr + 1)
+#define MFM_STATUS  (mfm_addr + 8)
+#define MFM_DATAIN  (mfm_addr + 9)
+
+#define CMD_ABT		0xF0	/* Abort */
+#define CMD_SPC		0xE8	/* Specify */
+#define CMD_TST		0xE0	/* Test */
+#define CMD_RCLB	0xC8	/* Recalibrate */
+#define CMD_SEK		0xC0	/* Seek */
+#define CMD_WFS		0xAB	/* Write Format Skew */
+#define CMD_WFM		0xA3	/* Write Format */
+#define CMD_MTB		0x90	/* Memory to buffer */
+#define CMD_CMPD	0x88	/* Compare data */
+#define CMD_WD		0x87	/* Write data */
+#define CMD_RED		0x70	/* Read erroneous data */
+#define CMD_RIS		0x68	/* Read ID skew */
+#define CMD_FID		0x61	/* Find ID */
+#define CMD_RID		0x60	/* Read ID */
+#define CMD_BTM		0x50	/* Buffer to memory */
+#define CMD_CKD		0x48	/* Check data */
+#define CMD_RD		0x40	/* Read data */
+#define CMD_OPBW	0x38	/* Open buffer write */
+#define CMD_OPBR	0x30	/* Open buffer read */
+#define CMD_CKV		0x28	/* Check drive */
+#define CMD_CKE		0x20	/* Check ECC */
+#define CMD_POD		0x18	/* Polling disable */
+#define CMD_POL		0x10	/* Polling enable */
+#define CMD_RCAL	0x08	/* Recall */
+
+#define STAT_BSY	0x8000	/* Busy */
+#define STAT_CPR	0x4000	/* Command Parameter Rejection */
+#define STAT_CED	0x2000	/* Command end */
+#define STAT_SED	0x1000	/* Seek end */
+#define STAT_DER	0x0800	/* Drive error */
+#define STAT_ABN	0x0400	/* Abnormal end */
+#define STAT_POL	0x0200	/* Polling */
+
+/* ------------------------------------------------------------------------------------------ */
+#ifdef DEBUG
+static void console_printf(const char *fmt,...)
+{
+	static char buffer[2048];	/* Arbitary! */
+	extern void console_print(const char *);
+	unsigned long flags;
+	va_list ap;
+
+	save_flags_cli(flags);
+
+	va_start(ap, fmt);
+	vsprintf(buffer, fmt, ap);
+	console_print(buffer);
+	va_end(fmt);
+
+	restore_flags(flags);
+};	/* console_printf */
+
+#define DBG(x...) console_printf(x)
+#else
+#define DBG(x...)
+#endif
+
+static void print_status(void)
+{
+	char *error;
+	static char *errors[] = {
+         "no error",
+	 "command aborted",
+	 "invalid command",
+	 "parameter error",
+	 "not initialised",
+	 "rejected TEST",
+	 "no useld",
+	 "write fault",
+	 "not ready",
+	 "no scp",
+	 "in seek",
+	 "invalid NCA",
+	 "invalid step rate",
+	 "seek error",
+	 "over run",
+	 "invalid PHA",
+	 "data field EEC error",
+	 "data field CRC error",
+	 "error corrected",
+	 "data field fatal error",
+	 "no data am",
+	 "not hit",
+	 "ID field CRC error",
+	 "time over",
+	 "no ID am",
+	 "not writable"
+	};
+	if (result[1] < 0x65)
+		error = errors[result[1] >> 2];
+	else
+		error = "unknown";
+	printk("(");
+	if (mfm_status & STAT_BSY) printk("BSY ");
+	if (mfm_status & STAT_CPR) printk("CPR ");
+	if (mfm_status & STAT_CED) printk("CED ");
+	if (mfm_status & STAT_SED) printk("SED ");
+	if (mfm_status & STAT_DER) printk("DER ");
+	if (mfm_status & STAT_ABN) printk("ABN ");
+	if (mfm_status & STAT_POL) printk("POL ");
+	printk(") SSB = %X (%s)\n", result[1], error);
+
+}
+
+/* ------------------------------------------------------------------------------------- */
+
+static void issue_command(int command, unsigned char *cmdb, int len)
+{
+	int status;
+#ifdef DEBUG
+	int i;
+	console_printf("issue_command: %02X: ", command);
+	for (i = 0; i < len; i++)
+		console_printf("%02X ", cmdb[i]);
+	console_printf("\n");
+#endif
+
+	do {
+		status = inw(MFM_STATUS);
+	} while (status & (STAT_BSY | STAT_POL));
+	DBG("issue_command: status after pol/bsy loop: %02X:\n ", status >> 8);
+
+	if (status & (STAT_CPR | STAT_CED | STAT_SED | STAT_DER | STAT_ABN)) {
+		outw(CMD_RCAL, MFM_COMMAND);
+		while (inw(MFM_STATUS) & STAT_BSY);
+	}
+	status = inw(MFM_STATUS);
+	DBG("issue_command: status before parameter issue: %02X:\n ", status >> 8);
+
+	while (len > 0) {
+		outw(cmdb[1] | (cmdb[0] << 8), MFM_DATAOUT);
+		len -= 2;
+		cmdb += 2;
+	}
+	status = inw(MFM_STATUS);
+	DBG("issue_command: status before command issue: %02X:\n ", status >> 8);
+
+	outw(command, MFM_COMMAND);
+	status = inw(MFM_STATUS);
+	DBG("issue_command: status immediatly after command issue: %02X:\n ", status >> 8);
+}
+
+static void wait_for_completion(void)
+{
+	while ((mfm_status = inw(MFM_STATUS)) & STAT_BSY);
+}
+
+static void wait_for_command_end(void)
+{
+	int i;
+
+	while (!((mfm_status = inw(MFM_STATUS)) & STAT_CED));
+
+	for (i = 0; i < 16;) {
+		int in;
+		in = inw(MFM_DATAIN);
+		result[i++] = in >> 8;
+		result[i++] = in;
+	}
+	outw (CMD_RCAL, MFM_COMMAND);
+}
+
+/* ------------------------------------------------------------------------------------- */
+
+static void mfm_rw_intr(void)
+{
+	int old_status;		/* Holds status on entry, we read to see if the command just finished */
+#ifdef DEBUG
+	console_printf("mfm_rw_intr...dataleft=%d\n", hdc63463_dataleft);
+	print_status();
+#endif
+
+  /* Now don't handle the error until BSY drops */
+	if ((mfm_status & (STAT_DER | STAT_ABN)) && ((mfm_status&STAT_BSY)==0)) {
+		/* Something has gone wrong - lets try that again */
+		outw(CMD_RCAL, MFM_COMMAND);	/* Clear interrupt condition */
+		if (cont) {
+			DBG("mfm_rw_intr: DER/ABN err\n");
+			cont->error();
+			cont->redo();
+		};
+		return;
+	};
+
+	/* OK so what ever happend its not an error, now I reckon we are left between
+	   a choice of command end or some data which is ready to be collected */
+	/* I think we have to transfer data while the interrupt line is on and its
+	   not any other type of interrupt */
+	if (CURRENT->cmd == WRITE) {
+		extern void hdc63463_writedma(void);
+		if ((hdc63463_dataleft <= 0) && (!(mfm_status & STAT_CED))) {
+			printk("mfm_rw_intr: Apparent DMA write request when no more to DMA\n");
+			if (cont) {
+				cont->error();
+				cont->redo();
+			};
+			return;
+		};
+		hdc63463_writedma();
+	} else {
+		extern void hdc63463_readdma(void);
+		if ((hdc63463_dataleft <= 0) && (!(mfm_status & STAT_CED))) {
+			printk("mfm_rw_intr: Apparent DMA read request when no more to DMA\n");
+			if (cont) {
+				cont->error();
+				cont->redo();
+			};
+			return;
+		};
+		DBG("Going to try read dma..............status=0x%x, buffer=%p\n", mfm_status, hdc63463_dataptr);
+		hdc63463_readdma();
+	};			/* Read */
+
+	if (hdc63463_dataptr != ((unsigned int) Copy_buffer + 256)) {
+		/* If we didn't actually manage to get any data on this interrupt - but why? We got the interrupt */
+		/* Ah - well looking at the status its just when we get command end; so no problem */
+		/*console_printf("mfm: dataptr mismatch. dataptr=0x%08x Copy_buffer+256=0x%08p\n",
+		   hdc63463_dataptr,Copy_buffer+256);
+		   print_status(); */
+	} else {
+		Sectors256LeftInCurrent--;
+		Copy_buffer += 256;
+		Copy_Sector++;
+
+		/* We have come to the end of this request */
+		if (!Sectors256LeftInCurrent) {
+			DBG("mfm: end_request for CURRENT=0x%p CURRENT(sector=%d current_nr_sectors=%d nr_sectors=%d)\n",
+				       CURRENT, CURRENT->sector, CURRENT->current_nr_sectors, CURRENT->nr_sectors);
+
+			CURRENT->nr_sectors -= CURRENT->current_nr_sectors;
+			CURRENT->sector += CURRENT->current_nr_sectors;
+			SectorsLeftInRequest -= CURRENT->current_nr_sectors;
+
+			end_request(1);
+			if (SectorsLeftInRequest) {
+				hdc63463_dataptr = (unsigned int) CURRENT->buffer;
+				Copy_buffer = CURRENT->buffer;
+				Sectors256LeftInCurrent = CURRENT->current_nr_sectors * 2;
+				errors = &(CURRENT->errors);
+				/* These should match the present calculations of the next logical sector
+				   on the device
+				   Copy_Sector=CURRENT->sector*2; */
+
+				if (Copy_Sector != CURRENT->sector * 2)
+#ifdef DEBUG
+					/*console_printf*/printk("mfm: Copy_Sector mismatch. Copy_Sector=%d CURRENT->sector*2=%d\n",
+					Copy_Sector, CURRENT->sector * 2);
+#else
+					printk("mfm: Copy_Sector mismatch! Eek!\n");
+#endif
+			};	/* CURRENT */
+		};	/* Sectors256LeftInCurrent */
+	};
+
+	old_status = mfm_status;
+	mfm_status = inw(MFM_STATUS);
+	if (mfm_status & (STAT_DER | STAT_ABN)) {
+		/* Something has gone wrong - lets try that again */
+		if (cont) {
+			DBG("mfm_rw_intr: DER/ABN error\n");
+			cont->error();
+			cont->redo();
+		};
+		return;
+	};
+
+	/* If this code wasn't entered due to command_end but there is
+	   now a command end we must read the command results out. If it was
+	   entered like this then mfm_interrupt_handler would have done the
+	   job. */
+	if ((!((old_status & (STAT_CPR | STAT_BSY)) == STAT_CPR)) &&
+	    ((mfm_status & (STAT_CPR | STAT_BSY)) == STAT_CPR)) {
+		int len = 0;
+		while (len < 16) {
+			int in;
+			in = inw(MFM_DATAIN);
+			result[len++] = in >> 8;
+			result[len++] = in;
+		};
+	};			/* Result read */
+
+	/*console_printf ("mfm_rw_intr nearexit [%02X]\n", inb(mfm_IRQPollLoc)); */
+
+	/* If end of command move on */
+	if (mfm_status & (STAT_CED)) {
+		outw(CMD_RCAL, MFM_COMMAND);	/* Clear interrupt condition */
+		/* End of command - trigger the next command */
+		if (cont) {
+			cont->done(1);
+		}
+		DBG("mfm_rw_intr: returned from cont->done\n");
+	} else {
+		/* Its going to generate another interrupt */
+		SET_INTR(mfm_rw_intr);
+	};
+}
+
+static void mfm_setup_rw(void)
+{
+	DBG("setting up for rw...\n");
+
+	SET_INTR(mfm_rw_intr);
+	issue_command(raw_cmd.cmdcode, raw_cmd.cmddata, raw_cmd.cmdlen);
+}
+
+static void mfm_recal_intr(void)
+{
+#ifdef DEBUG
+	console_printf("recal intr - status = ");
+	print_status();
+#endif
+	outw(CMD_RCAL, MFM_COMMAND);	/* Clear interrupt condition */
+	if (mfm_status & (STAT_DER | STAT_ABN)) {
+		printk("recal failed\n");
+		MFM_DRV_INFO.cylinder = NEED_2_RECAL;
+		if (cont) {
+			cont->error();
+			cont->redo();
+		}
+		return;
+	}
+	/* Thats seek end - we are finished */
+	if (mfm_status & STAT_SED) {
+		issue_command(CMD_POD, NULL, 0);
+		MFM_DRV_INFO.cylinder = 0;
+		mfm_seek();
+		return;
+	}
+	/* Command end without seek end (see data sheet p.20) for parallel seek
+	   - we have to send a POL command to wait for the seek */
+	if (mfm_status & STAT_CED) {
+		SET_INTR(mfm_recal_intr);
+		issue_command(CMD_POL, NULL, 0);
+		return;
+	}
+	printk("recal: unknown status\n");
+}
+
+static void mfm_seek_intr(void)
+{
+#ifdef DEBUG
+	console_printf("seek intr - status = ");
+	print_status();
+#endif
+	outw(CMD_RCAL, MFM_COMMAND);	/* Clear interrupt condition */
+	if (mfm_status & (STAT_DER | STAT_ABN)) {
+		printk("seek failed\n");
+		MFM_DRV_INFO.cylinder = NEED_2_RECAL;
+		if (cont) {
+			cont->error();
+			cont->redo();
+		}
+		return;
+	}
+	if (mfm_status & STAT_SED) {
+		issue_command(CMD_POD, NULL, 0);
+		MFM_DRV_INFO.cylinder = raw_cmd.cylinder;
+		mfm_seek();
+		return;
+	}
+	if (mfm_status & STAT_CED) {
+		SET_INTR(mfm_seek_intr);
+		issue_command(CMD_POL, NULL, 0);
+		return;
+	}
+	printk("seek: unknown status\n");
+}
+
+/* IDEA2 seems to work better - its what RiscOS sets my
+ * disc to - on its SECOND call to specify!
+ */
+#define IDEA2
+#ifndef IDEA2
+#define SPEC_SL 0x16
+#define SPEC_SH 0xa9		/* Step pulse high=21, Record Length=001 (256 bytes) */
+#else
+#define SPEC_SL 0x00		/* OM2 - SL - step pulse low */
+#define SPEC_SH 0x21		/* Step pulse high=4, Record Length=001 (256 bytes) */
+#endif
+
+static void mfm_setupspecify (int drive, unsigned char *cmdb)
+{
+	cmdb[0]  = 0x1f;		/* OM0 - !SECT,!MOD,!DIF,PADP,ECD,CRCP,CRCI,ACOR */
+	cmdb[1]  = 0xc3;		/* OM1 - DTM,BRST,!CEDM,!SEDM,!DERM,0,AMEX,PSK */
+	cmdb[2]  = SPEC_SL;		/* OM2 - SL - step pulse low */
+	cmdb[3]  = (number_mfm_drives == 1) ? 0x02 : 0x06;	/* 1 or 2 drives */
+	cmdb[4]  = 0xfc | ((mfm_info[drive].cylinders - 1) >> 8);/* RW time over/high part of number of cylinders */
+	cmdb[5]  = mfm_info[drive].cylinders - 1;		/* low part of number of cylinders */
+	cmdb[6]  = mfm_info[drive].heads - 1;			/* Number of heads */
+	cmdb[7]  = mfm_info[drive].sectors - 1;			/* Number of sectors */
+	cmdb[8]  = SPEC_SH;
+	cmdb[9]  = 0x0a;		/* gap length 1 */
+	cmdb[10] = 0x0d;		/* gap length 2 */
+	cmdb[11] = 0x0c;		/* gap length 3 */
+	cmdb[12] = (mfm_info[drive].precomp - 1) >> 8;	/* pre comp cylinder */
+	cmdb[13] = mfm_info[drive].precomp - 1;
+	cmdb[14] = (mfm_info[drive].lowcurrent - 1) >> 8;	/* Low current cylinder */
+	cmdb[15] = mfm_info[drive].lowcurrent - 1;
+}
+
+static void mfm_specify (void)
+{
+	unsigned char cmdb[16];
+
+	DBG("specify...dev=%d lastspecified=%d\n", raw_cmd.dev, lastspecifieddrive);
+	mfm_setupspecify (raw_cmd.dev, cmdb);
+
+	issue_command (CMD_SPC, cmdb, 16);
+	/* Ensure that we will do another specify if we move to the other drive */
+	lastspecifieddrive = raw_cmd.dev;
+	wait_for_completion();
+}
+
+static void mfm_seek(void)
+{
+	unsigned char cmdb[4];
+
+	DBG("seeking...\n");
+	if (MFM_DRV_INFO.cylinder < 0) {
+		SET_INTR(mfm_recal_intr);
+		DBG("mfm_seek: about to call specify\n");
+		mfm_specify ();	/* DAG added this */
+
+		cmdb[0] = raw_cmd.dev + 1;
+		cmdb[1] = 0;
+
+		issue_command(CMD_RCLB, cmdb, 2);
+		return;
+	}
+	if (MFM_DRV_INFO.cylinder != raw_cmd.cylinder) {
+		cmdb[0] = raw_cmd.dev + 1;
+		cmdb[1] = 0;	/* raw_cmd.head; DAG: My data sheet says this should be 0 */
+		cmdb[2] = raw_cmd.cylinder >> 8;
+		cmdb[3] = raw_cmd.cylinder;
+
+		SET_INTR(mfm_seek_intr);
+		issue_command(CMD_SEK, cmdb, 4);
+	} else
+		mfm_setup_rw();
+}
+
+static void mfm_initialise(void)
+{
+	DBG("init...\n");
+	mfm_seek();
+}
+
+static void request_done(int uptodate)
+{
+	DBG("mfm:request_done\n");
+	if (uptodate) {
+		unsigned char block[2] = {0, 0};
+
+		/* Apparently worked - lets check bytes left to DMA */
+		if (hdc63463_dataleft != (PartFragRead_SectorsLeft * 256)) {
+			printk("mfm: request_done - dataleft=%d - should be %d - Eek!\n", hdc63463_dataleft, PartFragRead_SectorsLeft * 256);
+			end_request(0);
+			Busy = 0;
+		};
+		/* Potentially this means that we've done; but we might be doing
+		   a partial access, (over two cylinders) or we may have a number
+		   of fragments in an image file.  First lets deal with partial accesss
+		 */
+		if (PartFragRead) {
+			/* Yep - a partial access */
+
+			/* and issue the remainder */
+			issue_request(MINOR(CURRENT->rq_dev), PartFragRead_RestartBlock, PartFragRead_SectorsLeft, CURRENT);
+			return;
+		}
+
+		/* ah well - perhaps there is another fragment to go */
+
+		/* Increment pointers/counts to start of next fragment */
+		if (SectorsLeftInRequest > 0) printk("mfm: SectorsLeftInRequest>0 - Eek! Shouldn't happen!\n");
+
+		/* No - its the end of the line */
+		/* end_request's should have happened at the end of sector DMAs */
+		/* Turns Drive LEDs off - may slow it down? */
+		if (!CURRENT)
+			issue_command(CMD_CKV, block, 2);
+
+		Busy = 0;
+		DBG("request_done: About to mfm_request\n");
+		/* Next one please */
+		mfm_request();	/* Moved from mfm_rw_intr */
+		DBG("request_done: returned from mfm_request\n");
+	} else {
+		printk("mfm:request_done: update=0\n");
+		end_request(0);
+		Busy = 0;
+	}
+}
+
+static void error_handler(void)
+{
+	printk("error detected... status = ");
+	print_status();
+	(*errors)++;
+	if (*errors > MFM_DRV_INFO.errors.abort)
+		cont->done(0);
+	if (*errors > MFM_DRV_INFO.errors.recal)
+		MFM_DRV_INFO.cylinder = NEED_2_RECAL;
+}
+
+static void rw_interrupt(void)
+{
+	printk("rw_interrupt\n");
+}
+
+static struct cont rw_cont =
+{
+	rw_interrupt,
+	error_handler,
+	mfm_rerequest,
+	request_done
+};
+
+/*
+ * Actually gets round to issuing the request - note everything at this
+ * point is in 256 byte sectors not Linux 512 byte blocks
+ */
+static void issue_request(int dev, unsigned int block, unsigned int nsect,
+			  struct request *req)
+{
+	int track, start_head, start_sector;
+	int sectors_to_next_cyl;
+
+	dev >>= 6;
+
+	track = block / mfm_info[dev].sectors;
+	start_sector = block % mfm_info[dev].sectors;
+	start_head = track % mfm_info[dev].heads;
+
+	/* First get the number of whole tracks which are free before the next
+	   track */
+	sectors_to_next_cyl = (mfm_info[dev].heads - (start_head + 1)) * mfm_info[dev].sectors;
+	/* Then add in the number of sectors left on this track */
+	sectors_to_next_cyl += (mfm_info[dev].sectors - start_sector);
+
+	DBG("issue_request: mfm_info[dev].sectors=%d track=%d\n", mfm_info[dev].sectors, track);
+
+	raw_cmd.dev = dev;
+	raw_cmd.sector = start_sector;
+	raw_cmd.head = start_head;
+	raw_cmd.cylinder = track / mfm_info[dev].heads;
+	raw_cmd.cmdtype = CURRENT->cmd;
+	raw_cmd.cmdcode = CURRENT->cmd == WRITE ? CMD_WD : CMD_RD;
+	raw_cmd.cmddata[0] = dev + 1;	/* DAG: +1 to get US */
+	raw_cmd.cmddata[1] = raw_cmd.head;
+	raw_cmd.cmddata[2] = raw_cmd.cylinder >> 8;
+	raw_cmd.cmddata[3] = raw_cmd.cylinder;
+	raw_cmd.cmddata[4] = raw_cmd.head;
+	raw_cmd.cmddata[5] = raw_cmd.sector;
+
+	/* Was == and worked - how the heck??? */
+	if (lastspecifieddrive != raw_cmd.dev)
+		mfm_specify ();
+
+	if (nsect <= sectors_to_next_cyl) {
+		raw_cmd.cmddata[6] = nsect >> 8;
+		raw_cmd.cmddata[7] = nsect;
+		PartFragRead = 0;	/* All in one */
+		PartFragRead_SectorsLeft = 0;	/* Must set this - used in DMA calcs */
+	} else {
+		raw_cmd.cmddata[6] = sectors_to_next_cyl >> 8;
+		raw_cmd.cmddata[7] = sectors_to_next_cyl;
+		PartFragRead = sectors_to_next_cyl;	/* only do this many this time */
+		PartFragRead_RestartBlock = block + sectors_to_next_cyl;	/* Where to restart from */
+		PartFragRead_SectorsLeft = nsect - sectors_to_next_cyl;
+	}
+	raw_cmd.cmdlen = 8;
+
+	/* Setup DMA pointers */
+	hdc63463_dataptr = (unsigned int) Copy_buffer;
+	hdc63463_dataleft = nsect * 256;	/* Better way? */
+
+	DBG("mfm%c: %sing: CHS=%d/%d/%d, sectors=%d, buffer=0x%08lx (%p)\n",
+	     raw_cmd.dev + 'a', (CURRENT->cmd == READ) ? "read" : "writ",
+		       raw_cmd.cylinder,
+		       raw_cmd.head,
+	    raw_cmd.sector, nsect, (unsigned long) Copy_buffer, CURRENT);
+
+	cont = &rw_cont;
+	errors = &(CURRENT->errors);
+#if 0
+	mfm_tq.routine = (void (*)(void *)) mfm_initialise;
+	queue_task(&mfm_tq, &tq_immediate);
+	mark_bh(IMMEDIATE_BH);
+#else
+	mfm_initialise();
+#endif
+}				/* issue_request */
+
+/*
+ * Called when an error has just happened - need to trick mfm_request
+ * into thinking we weren't busy
+ *
+ * Turn off ints - mfm_request expects them this way
+ */
+static void mfm_rerequest(void)
+{
+	DBG("mfm_rerequest\n");
+	cli();
+	Busy = 0;
+	mfm_request();
+}
+
+static void mfm_request(void)
+{
+	DBG("mfm_request CURRENT=%p Busy=%d\n", CURRENT, Busy);
+
+	if (!CURRENT) {
+		DBG("mfm_request: Exited due to NULL Current 1\n");
+		return;
+	}
+
+	if (CURRENT->rq_status == RQ_INACTIVE) {
+		/* Hmm - seems to be happening a lot on 1.3.45 */
+		/*console_printf("mfm_request: Exited due to INACTIVE Current\n"); */
+		return;
+	}
+
+	/* If we are still processing then return; we will get called again */
+	if (Busy) {
+		/* Again seems to be common in 1.3.45 */
+		/*DBG*/printk("mfm_request: Exiting due to busy\n");
+		return;
+	}
+	Busy = 1;
+
+	while (1) {
+		unsigned int dev, block, nsect;
+
+		DBG("mfm_request: loop start\n");
+		sti();
+
+		DBG("mfm_request: before INIT_REQUEST\n");
+
+		if (!CURRENT) {
+			printk("mfm_request: Exiting due to !CURRENT (pre)\n");
+			CLEAR_INTR;
+			Busy = 0;
+			return;
+		};
+
+		INIT_REQUEST;
+
+		DBG("mfm_request:                 before arg extraction\n");
+
+		dev = MINOR(CURRENT->rq_dev);
+		block = CURRENT->sector;
+		nsect = CURRENT->nr_sectors;
+#ifdef DEBUG
+		/*if ((dev>>6)==1) */ console_printf("mfm_request:                                raw vals: dev=%d (block=512 bytes) block=%d nblocks=%d\n", dev, block, nsect);
+#endif
+		if (dev >= (mfm_drives << 6) ||
+		    block >= mfm[dev].nr_sects || ((block+nsect) > mfm[dev].nr_sects)) {
+			if (dev >= (mfm_drives << 6))
+				printk("mfm: bad minor number: device=%s\n", kdevname(CURRENT->rq_dev));
+			else
+				printk("mfm%c: bad access: block=%d, count=%d, nr_sects=%ld\n", (dev >> 6)+'a',
+				       block, nsect, mfm[dev].nr_sects);
+			printk("mfm: continue 1\n");
+			end_request(0);
+			Busy = 0;
+			continue;
+		}
+
+		block += mfm[dev].start_sect;
+
+		/* DAG: Linux doesn't cope with this - even though it has an array telling
+		   it the hardware block size - silly */
+		block <<= 1;	/* Now in 256 byte sectors */
+		nsect <<= 1;	/* Ditto */
+
+		SectorsLeftInRequest = nsect >> 1;
+		Sectors256LeftInCurrent = CURRENT->current_nr_sectors * 2;
+		Copy_buffer = CURRENT->buffer;
+		Copy_Sector = CURRENT->sector << 1;
+
+		DBG("mfm_request: block after offset=%d\n", block);
+
+		if (CURRENT->cmd != READ && CURRENT->cmd != WRITE) {
+			printk("unknown mfm-command %d\n", CURRENT->cmd);
+			end_request(0);
+			Busy = 0;
+			printk("mfm: continue 4\n");
+			continue;
+		}
+		issue_request(dev, block, nsect, CURRENT);
+
+		break;
+	}
+	DBG("mfm_request: Dropping out bottom\n");
+}
+
+static void do_mfm_request(void)
+{
+	DBG("do_mfm_request: about to mfm_request\n");
+	mfm_request();
+}
+
+static void mfm_interrupt_handler(int unused, void *dev_id, struct pt_regs *regs)
+{
+	void (*handler) (void) = DEVICE_INTR;
+
+	CLEAR_INTR;
+
+	DBG("mfm_interrupt_handler (handler=0x%p)\n", handler);
+
+	mfm_status = inw(MFM_STATUS);
+
+	/* If CPR (Command Parameter Reject) and not busy it means that the command
+	   has some return message to give us */
+	if ((mfm_status & (STAT_CPR | STAT_BSY)) == STAT_CPR) {
+		int len = 0;
+		while (len < 16) {
+			int in;
+			in = inw(MFM_DATAIN);
+			result[len++] = in >> 8;
+			result[len++] = in;
+		}
+	}
+	if (handler) {
+		handler();
+		return;
+	}
+	outw (CMD_RCAL, MFM_COMMAND);	/* Clear interrupt condition */
+	printk ("mfm: unexpected interrupt - status = ");
+	print_status ();
+	while (1);
+}
+
+
+
+
+
+/*
+ * Tell the user about the drive if we decided it exists.  Also,
+ * set the size of the drive.
+ */
+static void mfm_geometry (int drive)
+{
+	if (mfm_info[drive].cylinders)
+		printk ("mfm%c: %dMB CHS=%d/%d/%d LCC=%d RECOMP=%d\n", 'a' + drive,
+			mfm_info[drive].cylinders * mfm_info[drive].heads * mfm_info[drive].sectors / 4096,
+			mfm_info[drive].cylinders, mfm_info[drive].heads, mfm_info[drive].sectors,
+			mfm_info[drive].lowcurrent, mfm_info[drive].precomp);
+	mfm[drive << 6].start_sect = 0;
+	mfm[drive << 6].nr_sects = mfm_info[drive].cylinders * mfm_info[drive].heads * mfm_info[drive].sectors / 2;
+}
+
+#ifdef CONFIG_BLK_DEV_MFM_AUTODETECT
+/*
+ * Attempt to detect a drive and find its geometry.  The drive has already been
+ * specified...
+ *
+ * We first recalibrate the disk, then try to probe sectors, heads and then
+ * cylinders.  NOTE! the cylinder probe may break drives.  The xd disk driver
+ * does something along these lines, so I assume that most drives are up to
+ * this mistreatment...
+ */
+static int mfm_detectdrive (int drive)
+{
+	unsigned int mingeo[3], maxgeo[3];
+	unsigned int attribute, need_recal = 1;
+	unsigned char cmdb[8];
+
+	memset (mingeo, 0, sizeof (mingeo));
+	maxgeo[0] = mfm_info[drive].sectors;
+	maxgeo[1] = mfm_info[drive].heads;
+	maxgeo[2] = mfm_info[drive].cylinders;
+
+	cmdb[0] = drive + 1;
+	cmdb[6] = 0;
+	cmdb[7] = 1;
+	for (attribute = 0; attribute < 3; attribute++) {
+		while (mingeo[attribute] != maxgeo[attribute]) {
+			unsigned int variable;
+
+			variable = (maxgeo[attribute] + mingeo[attribute]) >> 1;
+			cmdb[1] = cmdb[2] = cmdb[3] = cmdb[4] = cmdb[5] = 0;
+
+			if (need_recal) {
+				int tries = 5;
+
+				do {
+					issue_command (CMD_RCLB, cmdb, 2);
+					wait_for_completion ();
+					wait_for_command_end ();
+					if  (result[1] == 0x20)
+						break;
+				} while (result[1] && --tries);
+				if (result[1]) {
+					outw (CMD_RCAL, MFM_COMMAND);
+					return 0;
+				}
+				need_recal = 0;
+			}
+
+			switch (attribute) {
+			case 0:
+				cmdb[5] = variable;
+				issue_command (CMD_CMPD, cmdb, 8);
+				break;
+			case 1:
+				cmdb[1] = variable;
+				cmdb[4] = variable;
+				issue_command (CMD_CMPD, cmdb, 8);
+				break;
+			case 2:
+				cmdb[2] = variable >> 8;
+				cmdb[3] = variable;
+				issue_command (CMD_SEK, cmdb, 4);
+				break;
+			}
+			wait_for_completion ();
+			wait_for_command_end ();
+
+			switch (result[1]) {
+			case 0x00:
+			case 0x50:
+				mingeo[attribute] = variable + 1;
+				break;
+
+			case 0x20:
+				outw (CMD_RCAL, MFM_COMMAND);
+				return 0;
+
+			case 0x24:
+				need_recal = 1;
+			default:
+				maxgeo[attribute] = variable;
+				break;
+			}
+		}
+	}
+	mfm_info[drive].cylinders  = mingeo[2];
+	mfm_info[drive].lowcurrent = mingeo[2];
+	mfm_info[drive].precomp    = mingeo[2] / 2;
+	mfm_info[drive].heads 	   = mingeo[1];
+	mfm_info[drive].sectors	   = mingeo[0];
+	outw (CMD_RCAL, MFM_COMMAND);
+	return 1;
+}
+#endif
+
+/*
+ * Initialise all drive information for this controller.
+ */
+static int mfm_initdrives(void)
+{
+	int drive;
+
+	if (number_mfm_drives > MFM_MAXDRIVES) {
+		number_mfm_drives = MFM_MAXDRIVES;
+		printk("No. of ADFS MFM drives is greater than MFM_MAXDRIVES - you can't have that many!\n");
+	}
+
+	for (drive = 0; drive < number_mfm_drives; drive++) {
+		mfm_info[drive].lowcurrent = 1;
+		mfm_info[drive].precomp    = 1;
+		mfm_info[drive].cylinder   = -1;
+		mfm_info[drive].errors.recal  = 0;
+		mfm_info[drive].errors.report = 0;
+		mfm_info[drive].errors.abort  = 4;
+
+#ifdef CONFIG_BLK_DEV_MFM_AUTODETECT
+		mfm_info[drive].cylinders  = 1024;
+		mfm_info[drive].heads	   = 8;
+		mfm_info[drive].sectors	   = 64;
+		{
+			unsigned char cmdb[16];
+
+			mfm_setupspecify (drive, cmdb);
+			cmdb[1] &= ~0x81;
+			issue_command (CMD_SPC, cmdb, 16);
+			wait_for_completion ();
+			if (!mfm_detectdrive (drive)) {
+				mfm_info[drive].cylinders = 0;
+				mfm_info[drive].heads     = 0;
+				mfm_info[drive].sectors   = 0;
+			}
+			cmdb[0] = cmdb[1] = 0;
+			issue_command (CMD_CKV, cmdb, 2);
+		}
+#else
+		mfm_info[drive].cylinders  = 1;	/* its going to have to figure it out from the partition info */
+		mfm_info[drive].heads      = 4;
+		mfm_info[drive].sectors    = 32;
+#endif
+	}
+	return number_mfm_drives;
+}
+
+
+
+/*
+ * The 'front' end of the mfm driver follows...
+ */
+
+static int mfm_ioctl(struct inode *inode, struct file *file, u_int cmd, u_long arg)
+{
+	struct hd_geometry *geo = (struct hd_geometry *) arg;
+	kdev_t dev;
+	int device, major, minor, err;
+
+	if (!inode || !(dev = inode->i_rdev))
+		return -EINVAL;
+
+	major = MAJOR(dev);
+	minor = MINOR(dev);
+
+	device = DEVICE_NR(MINOR(inode->i_rdev)), err;
+	if (device >= mfm_drives)
+		return -EINVAL;
+
+	switch (cmd) {
+	case HDIO_GETGEO:
+		if (!arg)
+			return -EINVAL;
+		if (put_user (mfm_info[device].heads, &geo->heads))
+			return -EFAULT;
+		if (put_user (mfm_info[device].sectors, &geo->sectors))
+			return -EFAULT;
+		if (put_user (mfm_info[device].cylinders, &geo->cylinders))
+			return -EFAULT;
+		if (put_user (mfm[minor].start_sect, &geo->start))
+			return -EFAULT;
+		return 0;
+
+	case BLKFLSBUF:
+		if (!suser())
+			return -EACCES;
+		fsync_dev(dev);
+		invalidate_buffers(dev);
+		return 0;
+
+	case BLKRASET:
+		if (!suser())
+			return -EACCES;
+		if (arg > 0xff)
+			return -EINVAL;
+		read_ahead[major] = arg;
+		return 0;
+
+	case BLKRAGET:
+		return put_user(read_ahead[major], (long *)arg);
+
+	case BLKGETSIZE:
+		return put_user (mfm[minor].nr_sects, (long *)arg);
+
+	case BLKFRASET:
+		if (!suser())
+			return -EACCES;
+		max_readahead[major][minor] = arg;
+		return 0;
+
+	case BLKFRAGET:
+		return put_user(max_readahead[major][minor], (long *) arg);
+
+	case BLKSECTGET:
+		return put_user(max_sectors[major][minor], (long *) arg);
+
+	case BLKRRPART:
+		if (!suser())
+			return -EACCES;
+		return mfm_reread_partitions(dev);
+
+	RO_IOCTLS(dev, arg);
+
+	default:
+		return -EINVAL;
+	}
+}
+
+static int mfm_open(struct inode *inode, struct file *file)
+{
+	int dev = DEVICE_NR(MINOR(inode->i_rdev));
+
+	if (dev >= mfm_drives)
+		return -ENODEV;
+
+	MOD_INC_USE_COUNT;
+	while (mfm_info[dev].busy)
+		sleep_on (&mfm_wait_open);
+
+	mfm_info[dev].access_count++;
+	return 0;
+}
+
+/*
+ * Releasing a block device means we sync() it, so that it can safely
+ * be forgotten about...
+ */
+static int mfm_release(struct inode *inode, struct file *file)
+{
+	fsync_dev(inode->i_rdev);
+	mfm_info[DEVICE_NR(MINOR(inode->i_rdev))].access_count--;
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+/*
+ * This is to handle various kernel command line parameters
+ * specific to this driver.
+ */
+void mfm_setup(char *str, int *ints)
+{
+	return;
+}
+
+/*
+ * Set the CHS from the ADFS boot block if it is present.  This is not ideal
+ * since if there are any non-ADFS partitions on the disk, this won't work!
+ * Hence, I want to get rid of this...
+ */
+void xd_set_geometry(kdev_t dev, unsigned char secsptrack, unsigned char heads,
+		     unsigned long discsize, unsigned int secsize)
+{
+	int drive = MINOR(dev) >> 6;
+
+	if (mfm_info[drive].cylinders == 1) {
+		mfm_info[drive].sectors = secsptrack;
+		mfm_info[drive].heads = heads;
+		mfm_info[drive].cylinders = discsize / (secsptrack * heads * secsize);
+
+		if ((heads < 1) || (mfm_info[drive].cylinders > 1024)) {
+			printk("mfm%c: Insane disc shape! Setting to 512/4/32\n",'a' + (dev >> 6));
+
+			/* These values are fairly arbitary, but are there so that if your
+			 * lucky you can pick apart your disc to find out what is going on -
+			 * I reckon these figures won't hurt MOST drives
+			 */
+			mfm_info[drive].sectors = 32;
+			mfm_info[drive].heads = 4;
+			mfm_info[drive].cylinders = 512;
+		}
+		if (raw_cmd.dev == drive)
+			mfm_specify ();
+		mfm_geometry (drive);
+	}
+}
+
+static void mfm_geninit (struct gendisk *gdev);
+
+static struct gendisk mfm_gendisk = {
+	MAJOR_NR,		/* Major number */
+	"mfm",			/* Major name */
+	6,			/* Bits to shift to get real from partition */
+	1 << 6,			/* Number of partitions per real */
+	MFM_MAXDRIVES,		/* maximum number of real */
+	mfm_geninit,		/* init function */
+	mfm,			/* hd struct */
+	mfm_sizes,		/* block sizes */
+	0,			/* number */
+	(void *) mfm_info,	/* internal */
+	NULL			/* next */
+};
+
+static void mfm_geninit (struct gendisk *gdev)
+{
+	int i;
+
+	mfm_drives = mfm_initdrives();
+
+	printk("mfm: detected %d hard drive%s\n", mfm_drives, mfm_drives == 1 ? "" : "s");
+	gdev->nr_real = mfm_drives;
+
+	for (i = 0; i < mfm_drives; i++)
+		mfm_geometry (i);
+
+	if (request_irq(mfm_irq, mfm_interrupt_handler, SA_INTERRUPT, "MFM harddisk", NULL))
+		printk("mfm: unable to get IRQ%d\n", mfm_irq);
+
+	if (mfm_irqenable)
+		outw(0x80, mfm_irqenable);	/* Required to enable IRQs from MFM podule */
+
+	for (i = 0; i < (MFM_MAXDRIVES << 6); i++) {
+		mfm_blocksizes[i] = 1024;	/* Can't increase this - if you do all hell breaks loose */
+		mfm_sectsizes[i] = 512;
+	}
+	blksize_size[MAJOR_NR] = mfm_blocksizes;
+	hardsect_size[MAJOR_NR] = mfm_sectsizes;
+}
+
+static struct file_operations mfm_fops =
+{
+	NULL,			/* lseek - default */
+	block_read,		/* read - general block-dev read */
+	block_write,		/* write - general block-dev write */
+	NULL,			/* readdir - bad */
+	NULL,			/* poll */
+	mfm_ioctl,		/* ioctl */
+	NULL,			/* mmap */
+	mfm_open,		/* open */
+	mfm_release,		/* release */
+	block_fsync,		/* fsync */
+	NULL,			/* fasync */
+	NULL,			/* check_media_change */
+	NULL			/* revalidate */
+};
+
+
+static struct expansion_card *ecs;
+
+/*
+ * See if there is a controller at the address presently at mfm_addr
+ *
+ * We check to see if the controller is busy - if it is, we abort it first,
+ * and check that the chip is no longer busy after at least 180 clock cycles.
+ * We then issue a command and check that the BSY or CPR bits are set.
+ */
+static int mfm_probecontroller (unsigned int mfm_addr)
+{
+	if (check_region (mfm_addr, 10))
+		return 0;
+
+	if (inw (MFM_STATUS) & STAT_BSY) {
+		outw (CMD_ABT, MFM_COMMAND);
+		udelay (50);
+		if (inw (MFM_STATUS) & STAT_BSY)
+			return 0;
+	}
+
+	if (inw (MFM_STATUS) & STAT_CED)
+		outw (CMD_RCAL, MFM_COMMAND);
+
+	outw (CMD_SEK, MFM_COMMAND);
+
+	if (inw (MFM_STATUS) & (STAT_BSY | STAT_CPR)) {
+		unsigned int count = 2000;
+		while (inw (MFM_STATUS) & STAT_BSY) {
+			udelay (500);
+			if (!--count)
+				return 0;
+		}
+
+		outw (CMD_RCAL, MFM_COMMAND);
+	}
+	return 1;
+}
+
+/*
+ * Look for a MFM controller - first check the motherboard, then the podules
+ * The podules have an extra interrupt enable that needs to be played with
+ *
+ * The HDC is accessed at MEDIUM IOC speeds.
+ */
+int mfm_init (void)
+{
+	unsigned char irqmask;
+
+	if (register_blkdev(MAJOR_NR, "mfm", &mfm_fops)) {
+		printk("mfm_init: unable to get major number %d\n", MAJOR_NR);
+		return -1;
+	}
+
+	if (mfm_probecontroller(ONBOARD_MFM_ADDRESS)) {
+		mfm_addr	= ONBOARD_MFM_ADDRESS;
+		mfm_IRQPollLoc	= IOC_IRQSTATB;
+		mfm_irqenable	= 0;
+		mfm_irq		= IRQ_HARDDISK;
+		irqmask		= 0x08;			/* IL3 pin */
+	} else {
+		ecs = ecard_find(0, mfm_cids);
+		if (!ecs) {
+			mfm_addr = 0;
+			return -1;
+		}
+
+		mfm_addr	= ecard_address(ecs, ECARD_IOC, ECARD_MEDIUM) + 0x800;
+		mfm_IRQPollLoc	= mfm_addr + 0x400;
+		mfm_irqenable	= mfm_IRQPollLoc;
+		mfm_irq		= ecs->irq;
+		irqmask		= 0x08;
+
+		ecard_claim(ecs);
+	}
+
+	printk("mfm: found at address %08X, interrupt %d\n", mfm_addr, mfm_irq);
+	request_region (mfm_addr, 10, "mfm");
+
+	/* Stuff for the assembler routines to get to */
+	hdc63463_baseaddress	= ioaddr(mfm_addr);
+	hdc63463_irqpolladdress	= ioaddr(mfm_IRQPollLoc);
+	hdc63463_irqpollmask	= irqmask;
+
+	blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
+	read_ahead[MAJOR_NR] = 8;	/* 8 sector (4kB?) read ahread */
+
+#ifndef MODULE
+	mfm_gendisk.next = gendisk_head;
+	gendisk_head = &mfm_gendisk;
+#endif
+
+	Busy = 0;
+	lastspecifieddrive = -1;
+
+	return 0;
+}
+
+/*
+ * This routine is called to flush all partitions and partition tables
+ * for a changed MFM disk, and then re-read the new partition table.
+ * If we are revalidating due to an ioctl, we have USAGE == 1.
+ */
+static int mfm_reread_partitions(kdev_t dev)
+{
+	unsigned int start, i, maxp, target = DEVICE_NR(MINOR(dev));
+	unsigned long flags;
+
+	save_flags_cli(flags);
+	if (mfm_info[target].busy || mfm_info[target].access_count > 1) {
+		restore_flags (flags);
+		return -EBUSY;
+	}
+	mfm_info[target].busy = 1;
+	restore_flags (flags);
+
+	maxp = mfm_gendisk.max_p;
+	start = target << mfm_gendisk.minor_shift;
+
+	for (i = maxp - 1; i >= 0; i--) {
+		int minor = start + i;
+		kdev_t devi = MKDEV(MAJOR_NR, minor);
+		struct super_block *sb = get_super(devi);
+
+		sync_dev (devi);
+		if (sb)
+			invalidate_inodes (sb);
+		invalidate_buffers (devi);
+
+		mfm_gendisk.part[minor].start_sect = 0;
+		mfm_gendisk.part[minor].nr_sects = 0;
+	}
+
+	mfm_gendisk.part[start].nr_sects = mfm_info[target].heads *
+	    mfm_info[target].cylinders * mfm_info[target].sectors / 2;
+
+	resetup_one_dev(&mfm_gendisk, target);
+
+	mfm_info[target].busy = 0;
+	wake_up (&mfm_wait_open);
+	return 0;
+}
+
+#ifdef MODULE
+int init_module(void)
+{
+	int ret;
+	ret = mfm_init();
+	if (!ret)
+		mfm_geninit(&mfm_gendisk);
+	return ret;
+}
+
+void cleanup_module(void)
+{
+	if (ecs && mfm_irqenable)
+		outw (0, mfm_irqenable);	/* Required to enable IRQs from MFM podule */
+	free_irq(mfm_irq, NULL);
+	unregister_blkdev(MAJOR_NR, "mfm");
+	if (ecs)
+		ecard_release(ecs);
+	if (mfm_addr)
+		release_region(mfm_addr, 10);
+}
+#endif
diff -u --recursive --new-file v2.1.87/linux/drivers/acorn/net/Config.in linux/drivers/acorn/net/Config.in
--- v2.1.87/linux/drivers/acorn/net/Config.in	Wed Dec 31 16:00:00 1969
+++ linux/drivers/acorn/net/Config.in	Mon Feb 16 15:49:47 1998
@@ -0,0 +1,7 @@
+#
+# Acorn Network device configuration
+#  These are for Acorn's Expansion card network interfaces
+#
+tristate 'Acorn Ether1 (82586) support' CONFIG_ARM_ETHER1
+tristate 'Acorn/ANT Ether3 (NQ8005) support' CONFIG_ARM_ETHER3
+tristate 'I-cubed EtherH (NS8390) support' CONFIG_ARM_ETHERH
diff -u --recursive --new-file v2.1.87/linux/drivers/acorn/net/Makefile linux/drivers/acorn/net/Makefile
--- v2.1.87/linux/drivers/acorn/net/Makefile	Wed Dec 31 16:00:00 1969
+++ linux/drivers/acorn/net/Makefile	Mon Feb 16 15:49:47 1998
@@ -0,0 +1,35 @@
+# File: drivers/acorn/net/Makefile
+#
+# Makefile for the Acorn ethercard network device drivers
+#
+
+L_TARGET := acorn-net.a
+L_OBJS   := net-probe.o
+M_OBJS   :=
+MOD_LIST_NAME := ACORN_NET_MODULES
+
+ifeq ($(CONFIG_ARM_ETHER1),y)
+  L_OBJS += ether1.o
+else
+  ifeq ($(CONFIG_ARM_ETHER1),m)
+    M_OBJS += ether1.o
+  endif
+endif
+
+ifeq ($(CONFIG_ARM_ETHER3),y)
+  L_OBJS += ether3.o
+else
+  ifeq ($(CONFIG_ARM_ETHER3),m)
+    M_OBJS += ether3.o
+  endif
+endif
+
+ifeq ($(CONFIG_ARM_ETHERH),y)
+  L_OBJS += etherh.o
+else
+  ifeq ($(CONFIG_ARM_ETHERH),m)
+    M_OBJS += etherh.o
+  endif
+endif
+
+include $(TOPDIR)/Rules.make
diff -u --recursive --new-file v2.1.87/linux/drivers/acorn/net/ether1.c linux/drivers/acorn/net/ether1.c
--- v2.1.87/linux/drivers/acorn/net/ether1.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/acorn/net/ether1.c	Mon Feb 16 15:49:47 1998
@@ -0,0 +1,1241 @@
+/*
+ * linux/arch/arm/drivers/net/ether1.c
+ *
+ * (C) Copyright 1996,1997,1998 Russell King
+ *
+ * Acorn ether1 driver (82586 chip)
+ *  for Acorn machines
+ */
+
+/*
+ * We basically keep two queues in the cards memory - one for transmit
+ * and one for receive.  Each has a head and a tail.  The head is where
+ * we/the chip adds packets to be transmitted/received, and the tail
+ * is where the transmitter has got to/where the receiver will stop.
+ * Both of these queues are circular, and since the chip is running
+ * all the time, we have to be careful when we modify the pointers etc
+ * so that the buffer memory contents is valid all the time.
+ */
+
+/*
+ * Change log:
+ * 1.00	RMK			Released
+ * 1.01	RMK	19/03/1996	Transfers the last odd byte onto/off of the card now.
+ * 1.02	RMK	25/05/1997	Added code to restart RU if it goes not ready
+ * 1.03	RMK	14/09/1997	Cleaned up the handling of a reset during the TX interrupt.
+ *				Should prevent lockup.
+ * 1.04 RMK	17/09/1997	Added more info when initialsation of chip goes wrong.
+ *				TDR now only reports failure when chip reports non-zero
+ *				TDR time-distance.
+ * 1.05	RMK	31/12/1997	Removed calls to dev_tint for 2.1
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/in.h>
+#include <linux/malloc.h>
+#include <linux/string.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <linux/errno.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+#include <asm/ecard.h>
+
+#define __ETHER1_C
+#include "ether1.h"
+
+static unsigned int net_debug = NET_DEBUG;
+
+#define 	struct ether1_priv *priv = (struct ether1_priv *)dev->priv \
+	struct ether1_priv *priv = (struct ether1_priv *)dev->priv
+
+#define BUFFER_SIZE	0x10000
+#define TX_AREA_START	0x00100
+#define TX_AREA_END	0x05000
+#define RX_AREA_START	0x05000
+#define RX_AREA_END	0x0fc00
+
+#define tx_done(dev) 0
+/* ------------------------------------------------------------------------- */
+static char *version = "ether1 ethernet driver (c) 1995 Russell King v1.05\n";
+
+#define BUS_16 16
+#define BUS_8  8
+
+static const card_ids ether1_cids[] = {
+	{ MANU_ACORN, PROD_ACORN_ETHER1 },
+	{ 0xffff, 0xffff }
+};
+
+/* ------------------------------------------------------------------------- */
+
+#define DISABLEIRQS 1
+#define NORMALIRQS  0
+
+#define ether1_inw(dev, addr, type, offset, svflgs) ether1_inw_p (dev, addr + (int)(&((type *)0)->offset), svflgs)
+#define ether1_outw(dev, val, addr, type, offset, svflgs) ether1_outw_p (dev, val, addr + (int)(&((type *)0)->offset), svflgs)
+
+static inline unsigned short ether1_inw_p (struct device *dev, int addr, int svflgs)
+{
+	unsigned long flags;
+	unsigned short ret;
+
+	if (svflgs) {
+		save_flags_cli (flags);
+	}
+	outb (addr >> 12, REG_PAGE);
+	ret = inw (ETHER1_RAM + ((addr & 4095) >> 1));
+	if (svflgs)
+		restore_flags (flags);
+	return ret;
+}
+
+static inline void ether1_outw_p (struct device *dev, unsigned short val, int addr, int svflgs)
+{
+	unsigned long flags;
+
+	if (svflgs) {
+		save_flags_cli (flags);
+	}
+	outb (addr >> 12, REG_PAGE);
+	outw (val, ETHER1_RAM + ((addr & 4095) >> 1));
+	if (svflgs)
+		restore_flags (flags);
+}
+
+/*
+ * Some inline assembler to allow fast transfers on to/off of the card.
+ * Since this driver depends on some features presented by the ARM
+ * specific architecture, and that you can't configure this driver
+ * without specifiing ARM mode, this is not a problem.
+ *
+ * This routine is essentially an optimised memcpy from the card's
+ * onboard RAM to kernel memory.
+ */
+ 
+static inline void *ether1_inswb (unsigned int addr, void *data, unsigned int len)
+{
+	int used;
+
+	addr = IO_BASE + (addr << 2);
+
+	__asm__ __volatile__(
+		"subs	%3, %3, #2
+		bmi	2f
+1:		ldr	%0, [%1], #4
+		strb	%0, [%2], #1
+		mov	%0, %0, lsr #8
+		strb	%0, [%2], #1
+		subs	%3, %3, #2
+		bmi	2f
+		ldr	%0, [%1], #4
+		strb	%0, [%2], #1
+		mov	%0, %0, lsr #8
+		strb	%0, [%2], #1
+		subs	%3, %3, #2
+		bmi	2f
+		ldr	%0, [%1], #4
+		strb	%0, [%2], #1
+		mov	%0, %0, lsr #8
+		strb	%0, [%2], #1
+		subs	%3, %3, #2
+		bmi	2f
+		ldr	%0, [%1], #4
+		strb	%0, [%2], #1
+		mov	%0, %0, lsr #8
+		strb	%0, [%2], #1
+		subs	%3, %3, #2
+		bpl	1b
+2:		adds	%3, %3, #1
+		ldreqb	%0, [%1]
+		streqb	%0, [%2]"
+		: "=&r" (used), "=&r" (addr), "=&r" (data), "=&r" (len)
+		:                "1"  (addr), "2"   (data), "3"   (len));
+
+	return data;
+}
+
+static inline void *ether1_outswb (unsigned int addr, void *data, unsigned int len)
+{
+	int used;
+
+	addr = IO_BASE + (addr << 2);
+
+	__asm__ __volatile__(
+		"subs	%3, %3, #2
+		bmi	2f
+1:		ldr	%0, [%2], #2
+		mov	%0, %0, lsl #16
+		orr	%0, %0, %0, lsr #16
+		str	%0, [%1], #4
+		subs	%3, %3, #2
+		bmi	2f
+		ldr	%0, [%2], #2
+		mov	%0, %0, lsl #16
+		orr	%0, %0, %0, lsr #16
+		str	%0, [%1], #4
+		subs	%3, %3, #2
+		bmi	2f
+		ldr	%0, [%2], #2
+		mov	%0, %0, lsl #16
+		orr	%0, %0, %0, lsr #16
+		str	%0, [%1], #4
+		subs	%3, %3, #2
+		bmi	2f
+		ldr	%0, [%2], #2
+		mov	%0, %0, lsl #16
+		orr	%0, %0, %0, lsr #16
+		str	%0, [%1], #4
+		subs	%3, %3, #2
+		bpl	1b
+2:		adds	%3, %3, #1
+		ldreqb	%0, [%2]
+		streqb	%0, [%1]"
+		: "=&r" (used), "=&r" (addr), "=&r" (data), "=&r" (len)
+		:                "1"  (addr), "2"   (data), "3"   (len));
+
+	return data;
+}
+
+
+static void ether1_writebuffer (struct device *dev, void *data, unsigned int start, unsigned int length)
+{
+	unsigned int page, thislen, offset;
+	offset = start & 4095;
+
+	for (page = start >> 12; length; page++) 
+	{
+		outb (page, REG_PAGE);
+		if (offset + length > 4096) 
+		{
+		    length -= 4096 - offset;
+		    thislen = 4096 - offset;
+		}
+		else
+		{
+			thislen = length;
+			length = 0;
+		}
+
+		data = ether1_outswb (ETHER1_RAM + (offset >> 1), data, thislen);
+		offset = 0;
+	}
+}
+
+static void ether1_readbuffer (struct device *dev, void *data, unsigned int start, unsigned int length)
+{
+	unsigned int page, thislen, offset;
+
+	offset = start & 4095;
+
+	for (page = start >> 12; length; page++) 
+	{
+		outb (page, REG_PAGE);
+		if (offset + length > 4096) 
+		{
+			length -= 4096 - offset;
+			thislen = 4096 - offset;
+		}
+		else
+		{
+	 		thislen = length;
+			length = 0;
+		}
+
+		data = ether1_inswb (ETHER1_RAM + (offset >> 1), data, thislen);
+		offset = 0;
+	}
+}
+
+static int ether1_ramtest (struct device *dev, unsigned char byte)
+{
+	unsigned char *buffer = kmalloc (BUFFER_SIZE, GFP_KERNEL);
+	int i, ret = BUFFER_SIZE;
+	int max_errors = 15;
+	int bad = -1;
+	int bad_start = 0;
+
+	if (!buffer)
+		return 1;
+
+	memset (buffer, byte, BUFFER_SIZE);
+	ether1_writebuffer (dev, buffer, 0, BUFFER_SIZE);
+	memset (buffer, byte ^ 0xff, BUFFER_SIZE);
+	ether1_readbuffer (dev, buffer, 0, BUFFER_SIZE);
+
+	for (i = 0; i < BUFFER_SIZE; i++) 
+	{
+		if (buffer[i] != byte) 
+		{
+			if (max_errors >= 0 && bad != buffer[i]) 
+			{
+				if (bad != -1)
+					printk ("\n");
+				printk (KERN_CRIT "%s: RAM failed with (%02X instead of %02X) at 0x%04X",
+					dev->name, buffer[i], byte, i);
+				ret = -ENODEV;
+				max_errors --;
+				bad = buffer[i];
+				bad_start = i;
+			}
+		}
+		else
+		{
+			if (bad != -1) 
+			{
+				if (bad_start == i - 1)
+					printk ("\n");
+				else
+					printk (" - 0x%04X\n", i - 1);
+				bad = -1;
+			}
+		}
+	}
+
+	if (bad != -1)
+		printk (" - 0x%04X\n", BUFFER_SIZE);
+	kfree (buffer);
+
+	return ret;
+}
+
+static int ether1_reset (struct device *dev)
+{
+	outb (CTRL_RST|CTRL_ACK, REG_CONTROL);
+	return BUS_16;
+}
+
+static int ether1_init_2 (struct device *dev)
+{
+	int i;
+	dev->mem_start = 0;
+
+	i = ether1_ramtest (dev, 0x5a);
+
+	if (i > 0)
+		i = ether1_ramtest (dev, 0x1e);
+
+	if (i <= 0)
+    		return -ENODEV;
+
+	dev->mem_end = i;
+	return 0;
+}
+
+/*
+ * These are the structures that are loaded into the ether RAM card to
+ * initialise the 82586
+ */
+
+/* at 0x0100 */
+
+#define NOP_ADDR	(TX_AREA_START)
+#define NOP_SIZE	(0x06)
+
+static nop_t  init_nop  = 
+{
+	0,
+	CMD_NOP,
+	NOP_ADDR
+};
+
+/* at 0x003a */
+#define TDR_ADDR	(0x003a)
+#define TDR_SIZE	(0x08)
+static tdr_t  init_tdr	= {
+	0,
+	CMD_TDR | CMD_INTR,
+	NOP_ADDR,
+	0
+};
+
+/* at 0x002e */
+#define MC_ADDR		(0x002e)
+#define MC_SIZE		(0x0c)
+static mc_t   init_mc   = 
+{
+	0,
+	CMD_SETMULTICAST,
+	TDR_ADDR,
+	0,
+	{ { 0, } }
+};
+
+/* at 0x0022 */
+#define SA_ADDR		(0x0022)
+#define SA_SIZE		(0x0c)
+static sa_t   init_sa   = {
+	0,
+	CMD_SETADDRESS,
+	MC_ADDR,
+	{ 0, }
+};
+
+/* at 0x0010 */
+#define CFG_ADDR	(0x0010)
+#define CFG_SIZE	(0x12)
+static cfg_t  init_cfg  = {
+	0,
+	CMD_CONFIG,
+	SA_ADDR,
+	8,
+	8,
+	CFG8_SRDY,
+	CFG9_PREAMB8 | CFG9_ADDRLENBUF | CFG9_ADDRLEN(6),
+	0,
+	0x60,
+	0,
+	CFG13_RETRY(15) | CFG13_SLOTH(2),
+	0,
+};
+
+/* at 0x0000 */
+#define SCB_ADDR	(0x0000)
+#define SCB_SIZE	(0x10)
+static scb_t  init_scb  = {
+	0,
+	SCB_CMDACKRNR | SCB_CMDACKCNA | SCB_CMDACKFR | SCB_CMDACKCX,
+	CFG_ADDR,
+	RX_AREA_START,
+	0,
+	0,
+	0,
+	0
+};
+
+/* at 0xffee */
+#define ISCP_ADDR	(0xffee)
+#define ISCP_SIZE	(0x08)
+static iscp_t init_iscp = {
+	1,
+	SCB_ADDR,
+	0x0000,
+	0x0000
+};
+
+/* at 0xfff6 */
+#define SCP_ADDR	(0xfff6)
+#define SCP_SIZE	(0x0a)
+static scp_t  init_scp  = {
+	SCP_SY_16BBUS,
+	{ 0, 0 },
+	ISCP_ADDR,
+	0
+};
+
+#define RFD_SIZE	(0x16)
+static rfd_t  init_rfd	= {
+	0,
+	0,
+	0,
+	0,
+	{ 0, },
+	{ 0, },
+	0
+};
+
+#define RBD_SIZE	(0x0a)
+static rbd_t  init_rbd	= {	
+	0,
+	0,
+	0,
+	0,
+	ETH_FRAME_LEN + 8
+};
+
+#define TX_SIZE		(0x08)
+#define TBD_SIZE	(0x08)
+
+static int ether1_init_for_open (struct device *dev)
+{
+	struct ether1_priv *priv = (struct ether1_priv *)dev->priv;
+	int i, status, addr, next, next2;
+	int failures = 0;
+
+	outb (CTRL_RST|CTRL_ACK, REG_CONTROL);
+
+	for (i = 0; i < 6; i++)
+		init_sa.sa_addr[i] = dev->dev_addr[i];
+
+	/* load data structures into ether1 RAM */
+	ether1_writebuffer (dev, &init_scp,  SCP_ADDR,  SCP_SIZE);
+	ether1_writebuffer (dev, &init_iscp, ISCP_ADDR, ISCP_SIZE);
+	ether1_writebuffer (dev, &init_scb,  SCB_ADDR,  SCB_SIZE);
+	ether1_writebuffer (dev, &init_cfg,  CFG_ADDR,  CFG_SIZE);
+	ether1_writebuffer (dev, &init_sa,   SA_ADDR,   SA_SIZE);
+	ether1_writebuffer (dev, &init_mc,   MC_ADDR,   MC_SIZE);
+	ether1_writebuffer (dev, &init_tdr,  TDR_ADDR,  TDR_SIZE);
+	ether1_writebuffer (dev, &init_nop,  NOP_ADDR,  NOP_SIZE);
+
+	if (ether1_inw (dev, CFG_ADDR, cfg_t, cfg_command, NORMALIRQS) != CMD_CONFIG) 
+	{
+		printk (KERN_ERR "%s: detected either RAM fault or compiler bug\n",
+			dev->name);
+		return 1;
+	}
+
+	/*
+	 * setup circularly linked list of { rfd, rbd, buffer }, with
+	 * all rfds circularly linked, rbds circularly linked.
+	 * First rfd is linked to scp, first rbd is linked to first
+	 * rfd.  Last rbd has a suspend command.
+	 */
+	
+	addr = RX_AREA_START;
+
+	do 
+	{
+		next = addr + RFD_SIZE + RBD_SIZE + ETH_FRAME_LEN + 10;
+		next2 = next + RFD_SIZE + RBD_SIZE + ETH_FRAME_LEN + 10;
+
+		if (next2 >= RX_AREA_END) 
+		{
+			next = RX_AREA_START;
+			init_rfd.rfd_command = RFD_CMDEL | RFD_CMDSUSPEND;
+			priv->rx_tail = addr;
+		}
+		else
+			init_rfd.rfd_command = 0;
+		if (addr == RX_AREA_START)
+			init_rfd.rfd_rbdoffset = addr + RFD_SIZE;
+		else
+			init_rfd.rfd_rbdoffset = 0;
+		init_rfd.rfd_link = next;
+		init_rbd.rbd_link = next + RFD_SIZE;
+		init_rbd.rbd_bufl = addr + RFD_SIZE + RBD_SIZE;
+
+		ether1_writebuffer (dev, &init_rfd, addr, RFD_SIZE);
+		ether1_writebuffer (dev, &init_rbd, addr + RFD_SIZE, RBD_SIZE);
+		addr = next;
+	}
+	while (next2 < RX_AREA_END);
+
+	priv->tx_link = NOP_ADDR;
+	priv->tx_head = NOP_ADDR + NOP_SIZE;
+	priv->tx_tail = TDR_ADDR;
+	priv->rx_head = RX_AREA_START;
+
+	/* release reset & give 586 a prod */
+	priv->resetting = 1;
+	priv->initialising = 1;
+	outb (CTRL_RST, REG_CONTROL);
+	outb (0, REG_CONTROL);
+	outb (CTRL_CA, REG_CONTROL);
+
+	/* 586 should now unset iscp.busy */
+	i = jiffies + HZ/2;
+	while (ether1_inw (dev, ISCP_ADDR, iscp_t, iscp_busy, DISABLEIRQS) == 1) 
+	{
+		if (jiffies > i) 
+		{
+			printk (KERN_WARNING "%s: can't initialise 82586: iscp is busy\n", dev->name);
+			return 1;
+		}
+	}
+
+	/* check status of commands that we issued */
+	i += HZ/10;
+	while (((status = ether1_inw (dev, CFG_ADDR, cfg_t, cfg_status, DISABLEIRQS)) & STAT_COMPLETE) == 0) 
+	{
+		if (jiffies-i<0)
+			break;
+	}
+
+	if ((status & (STAT_COMPLETE | STAT_OK)) != (STAT_COMPLETE | STAT_OK)) 
+	{
+		printk (KERN_WARNING "%s: can't initialise 82586: config status %04X\n", dev->name, status);
+		printk (KERN_DEBUG "%s: SCB=[STS=%04X CMD=%04X CBL=%04X RFA=%04X]\n", dev->name,
+		ether1_inw (dev, SCB_ADDR, scb_t, scb_status, NORMALIRQS),
+		ether1_inw (dev, SCB_ADDR, scb_t, scb_command, NORMALIRQS),
+		ether1_inw (dev, SCB_ADDR, scb_t, scb_cbl_offset, NORMALIRQS),
+		ether1_inw (dev, SCB_ADDR, scb_t, scb_rfa_offset, NORMALIRQS));
+		failures += 1;
+	}
+
+	i += HZ/10;
+	while (((status = ether1_inw (dev, SA_ADDR, sa_t, sa_status, DISABLEIRQS)) & STAT_COMPLETE) == 0) 
+	{
+		if (jiffies-i<0)
+			break;
+	}
+
+	if ((status & (STAT_COMPLETE | STAT_OK)) != (STAT_COMPLETE | STAT_OK)) 
+	{
+		printk (KERN_WARNING "%s: can't initialise 82586: set address status %04X\n", dev->name, status);
+		printk (KERN_DEBUG "%s: SCB=[STS=%04X CMD=%04X CBL=%04X RFA=%04X]\n", dev->name,
+			ether1_inw (dev, SCB_ADDR, scb_t, scb_status, NORMALIRQS),
+			ether1_inw (dev, SCB_ADDR, scb_t, scb_command, NORMALIRQS),
+			ether1_inw (dev, SCB_ADDR, scb_t, scb_cbl_offset, NORMALIRQS),
+			ether1_inw (dev, SCB_ADDR, scb_t, scb_rfa_offset, NORMALIRQS));
+		failures += 1;
+	}
+
+	i += HZ/10;
+	while (((status = ether1_inw (dev, MC_ADDR, mc_t, mc_status, DISABLEIRQS)) & STAT_COMPLETE) == 0) 
+	{
+		if (jiffies-i < 0)
+			break;
+	}
+
+	if ((status & (STAT_COMPLETE | STAT_OK)) != (STAT_COMPLETE | STAT_OK)) 
+	{
+		printk (KERN_WARNING "%s: can't initialise 82586: set multicast status %04X\n", dev->name, status);
+		printk (KERN_DEBUG "%s: SCB=[STS=%04X CMD=%04X CBL=%04X RFA=%04X]\n", dev->name,
+			ether1_inw (dev, SCB_ADDR, scb_t, scb_status, NORMALIRQS),
+			ether1_inw (dev, SCB_ADDR, scb_t, scb_command, NORMALIRQS),
+			ether1_inw (dev, SCB_ADDR, scb_t, scb_cbl_offset, NORMALIRQS),
+			ether1_inw (dev, SCB_ADDR, scb_t, scb_rfa_offset, NORMALIRQS));
+		failures += 1;
+	}
+
+	i += HZ;
+	while (((status = ether1_inw (dev, TDR_ADDR, tdr_t, tdr_status, DISABLEIRQS)) & STAT_COMPLETE) == 0) 
+	{
+		if (jiffies-i <0)
+			break;
+	}
+
+	if ((status & (STAT_COMPLETE | STAT_OK)) != (STAT_COMPLETE | STAT_OK)) 
+	{
+		printk (KERN_WARNING "%s: can't tdr (ignored)\n", dev->name);
+		printk (KERN_DEBUG "%s: SCB=[STS=%04X CMD=%04X CBL=%04X RFA=%04X]\n", dev->name,
+			ether1_inw (dev, SCB_ADDR, scb_t, scb_status, NORMALIRQS),
+			ether1_inw (dev, SCB_ADDR, scb_t, scb_command, NORMALIRQS),
+			ether1_inw (dev, SCB_ADDR, scb_t, scb_cbl_offset, NORMALIRQS),
+			ether1_inw (dev, SCB_ADDR, scb_t, scb_rfa_offset, NORMALIRQS));
+	}
+	else 
+	{
+		status = ether1_inw (dev, TDR_ADDR, tdr_t, tdr_result, DISABLEIRQS);
+		if (status & TDR_XCVRPROB)
+			printk (KERN_WARNING "%s: i/f failed tdr: transceiver problem\n", dev->name);
+		else if ((status & (TDR_SHORT|TDR_OPEN)) && (status & TDR_TIME)) 
+		{
+#ifdef FANCY
+			printk (KERN_WARNING "%s: i/f failed tdr: cable %s %d.%d us away\n", dev->name,
+				status & TDR_SHORT ? "short" : "open", (status & TDR_TIME) / 10,
+				(status & TDR_TIME) % 10);
+#else	
+			printk (KERN_WARNING "%s: i/f failed tdr: cable %s %d clks away\n", dev->name,
+				status & TDR_SHORT ? "short" : "open", (status & TDR_TIME));
+#endif
+		}
+	}
+
+	if (failures)
+		ether1_reset (dev);
+	return failures ? 1 : 0;
+}
+
+static int ether1_probe1 (struct device *dev)
+{
+	static unsigned int version_printed = 0;
+	struct ether1_priv *priv;
+	int i;
+
+	if (!dev->priv)
+		dev->priv = kmalloc (sizeof (struct ether1_priv), GFP_KERNEL);
+
+	if (!dev->priv)
+    		return 1;
+
+	priv = (struct ether1_priv *)dev->priv;
+	memset (priv, 0, sizeof (struct ether1_priv));
+
+	if ((priv->bus_type = ether1_reset (dev)) == 0) 
+	{
+		kfree (dev->priv);
+		return 1;
+	}
+
+	if (net_debug && version_printed++ == 0)
+		printk (KERN_INFO "%s", version);
+
+	printk (KERN_INFO "%s: ether1 found [%d, %04lx, %d]", dev->name, priv->bus_type,
+		dev->base_addr, dev->irq);
+
+	request_region (dev->base_addr, 16, "ether1");
+	request_region (dev->base_addr + 0x800, 4096, "ether1(ram)");
+
+	for (i = 0; i < 6; i++)
+		printk (i==0?" %02x":i==5?":%02x\n":":%02x", dev->dev_addr[i]);
+
+	if (ether1_init_2 (dev)) 
+	{
+		kfree (dev->priv);
+		return 1;
+	}
+
+	dev->open	    = ether1_open;
+	dev->stop	    = ether1_close;
+	dev->hard_start_xmit= ether1_sendpacket;
+	dev->get_stats	    = ether1_getstats;
+	dev->set_multicast_list = ether1_setmulticastlist;
+
+	/* Fill in the fields of the device structure with ethernet values */
+	ether_setup (dev);
+
+#ifndef CLAIM_IRQ_AT_OPEN
+	if (request_irq (dev->irq, ether1_interrupt, 0, "ether1", dev)) 
+	{
+		kfree (dev->priv);
+		return -EAGAIN;
+	}
+#endif
+	return 0;
+}	
+    
+/* ------------------------------------------------------------------------- */
+
+static void ether1_addr (struct device *dev)
+{
+	int i;
+    
+	for (i = 0; i < 6; i++)
+		dev->dev_addr[i] = inb (IDPROM_ADDRESS + i);
+}
+
+int ether1_probe (struct device *dev)
+{
+#ifndef MODULE
+	struct expansion_card *ec;
+
+	if (!dev)
+		return ENODEV;
+
+	ecard_startfind ();
+	if ((ec = ecard_find (0, ether1_cids)) == NULL)
+		return ENODEV;
+
+	dev->base_addr = ecard_address (ec, ECARD_IOC, ECARD_FAST);
+	dev->irq       = ec->irq;
+
+	ecard_claim (ec);
+#endif
+	ether1_addr (dev);
+
+	if (ether1_probe1 (dev) == 0)
+		return 0;
+	return ENODEV;
+}
+
+/* ------------------------------------------------------------------------- */
+
+static int ether1_txalloc (struct device *dev, int size)
+{
+    	struct ether1_priv *priv = (struct ether1_priv *)dev->priv;
+	int start, tail;
+
+	size = (size + 1) & ~1;
+	tail = priv->tx_tail;
+
+	if (priv->tx_head + size > TX_AREA_END) 
+	{
+		if (tail > priv->tx_head)
+			return -1;
+		start = TX_AREA_START;
+		if (start + size > tail)
+			return -1;
+		priv->tx_head = start + size;
+	}
+	else
+	{
+		if (priv->tx_head < tail && (priv->tx_head + size) > tail)
+			return -1;
+		start = priv->tx_head;
+		priv->tx_head += size;
+	}
+	return start;
+}
+
+static void ether1_restart (struct device *dev, char *reason)
+{
+    	struct ether1_priv *priv = (struct ether1_priv *)dev->priv;
+	priv->stats.tx_errors ++;
+
+	if (reason)
+		printk (KERN_WARNING "%s: %s - resetting device\n", dev->name, reason);
+	else
+		printk (" - resetting device\n");
+
+	ether1_reset (dev);
+
+	dev->start = 0;
+	dev->tbusy = 0;
+
+	if (ether1_init_for_open (dev))
+		printk (KERN_ERR "%s: unable to restart interface\n", dev->name);
+
+	dev->start = 1;
+}
+
+static int ether1_open (struct device *dev)
+{
+    	struct ether1_priv *priv = (struct ether1_priv *)dev->priv;
+#ifdef CLAIM_IRQ_AT_OPEN
+	if (request_irq (dev->irq, ether1_interrupt, 0, "ether1", dev))
+		return -EAGAIN;
+#endif
+	MOD_INC_USE_COUNT;
+
+	memset (&priv->stats, 0, sizeof (struct enet_statistics));
+
+	if (ether1_init_for_open (dev)) 
+	{
+#ifdef CLAIM_IRQ_AT_OPEN
+		free_irq (dev->irq, dev);
+#endif
+		MOD_DEC_USE_COUNT;
+		return -EAGAIN;
+	}
+
+	dev->tbusy = 0;
+	dev->interrupt = 0;
+	dev->start = 1;
+
+	return 0;
+}
+
+static int ether1_sendpacket (struct sk_buff *skb, struct device *dev)
+{
+    	struct ether1_priv *priv = (struct ether1_priv *)dev->priv;
+
+	if (priv->restart)
+		ether1_restart (dev, NULL);
+
+	if (dev->tbusy) 
+	{
+		/*
+		 * If we get here, some higher level has decided that we are broken.
+		 * There should really be a "kick me" function call instead.
+		 */
+		int tickssofar = jiffies - dev->trans_start;
+
+		if (tickssofar < 5)
+			return 1;
+
+		/* Try to restart the adapter. */
+		ether1_restart (dev, "transmit timeout, network cable problem?");
+		dev->trans_start = jiffies;
+	}
+
+	/*
+	 * Block a timer-based transmit from overlapping.  This could better be
+	 * done with atomic_swap(1, dev->tbusy), but set_bit() works as well.
+	 */
+	 
+	if (test_and_set_bit (0, (void *)&dev->tbusy) != 0)
+		printk (KERN_WARNING "%s: transmitter access conflict.\n", dev->name);
+	else 
+	{
+		int len = (ETH_ZLEN < skb->len) ? skb->len : ETH_ZLEN;
+		int tmp, tst, nopaddr, txaddr, tbdaddr, dataddr;
+		unsigned long flags;
+		tx_t tx;
+		tbd_t tbd;
+		nop_t nop;
+
+		/*
+		 * insert packet followed by a nop
+		 */
+		txaddr = ether1_txalloc (dev, TX_SIZE);
+		tbdaddr = ether1_txalloc (dev, TBD_SIZE);
+		dataddr = ether1_txalloc (dev, len);
+		nopaddr = ether1_txalloc (dev, NOP_SIZE);
+
+		tx.tx_status = 0;
+		tx.tx_command = CMD_TX | CMD_INTR;
+		tx.tx_link = nopaddr;
+		tx.tx_tbdoffset = tbdaddr;
+		tbd.tbd_opts = TBD_EOL | len;
+		tbd.tbd_link = I82586_NULL;
+		tbd.tbd_bufl = dataddr;
+		tbd.tbd_bufh = 0;
+		nop.nop_status = 0;
+		nop.nop_command = CMD_NOP;
+		nop.nop_link = nopaddr;
+
+		save_flags_cli (flags);
+		ether1_writebuffer (dev, &tx, txaddr, TX_SIZE);
+		ether1_writebuffer (dev, &tbd, tbdaddr, TBD_SIZE);
+		ether1_writebuffer (dev, skb->data, dataddr, len);
+		ether1_writebuffer (dev, &nop, nopaddr, NOP_SIZE);
+		tmp = priv->tx_link;
+		priv->tx_link = nopaddr;
+
+		/* now reset the previous nop pointer */
+		ether1_outw (dev, txaddr, tmp, nop_t, nop_link, NORMALIRQS);
+
+		restore_flags (flags);
+
+		/* handle transmit */
+		dev->trans_start = jiffies;
+
+		/* check to see if we have room for a full sized ether frame */
+		tmp = priv->tx_head;
+		tst = ether1_txalloc (dev, TX_SIZE + TBD_SIZE + NOP_SIZE + ETH_FRAME_LEN);
+		priv->tx_head = tmp;
+		if (tst != -1)
+			dev->tbusy = 0;
+	}
+	dev_kfree_skb (skb, FREE_WRITE);
+
+	return 0;
+}
+
+static void ether1_xmit_done (struct device *dev)
+{
+    	struct ether1_priv *priv = (struct ether1_priv *)dev->priv;
+	nop_t nop;
+	int caddr, tst;
+
+	caddr = priv->tx_tail;
+
+again:
+	ether1_readbuffer (dev, &nop, caddr, NOP_SIZE);
+
+	switch (nop.nop_command & CMD_MASK) 
+	{
+		case CMD_TDR:
+			/* special case */
+			if (ether1_inw (dev, SCB_ADDR, scb_t, scb_cbl_offset, NORMALIRQS) != (unsigned short)I82586_NULL) 
+			{
+				ether1_outw (dev, SCB_CMDCUCSTART | SCB_CMDRXSTART, SCB_ADDR, scb_t,
+				scb_command, NORMALIRQS);
+				outb (CTRL_CA, REG_CONTROL);
+			}
+			priv->tx_tail = NOP_ADDR;
+			return;
+
+		case CMD_NOP:
+			if (nop.nop_link == caddr)
+			{
+				if (priv->initialising == 0)
+					printk (KERN_WARNING "%s: strange command complete with no tx command!\n", dev->name);
+				else
+	        			priv->initialising = 0;
+				return;
+			}
+			if (caddr == nop.nop_link)
+				return;
+			caddr = nop.nop_link;
+			goto again;
+
+		case CMD_TX:
+			if (nop.nop_status & STAT_COMPLETE)
+				break;
+			printk (KERN_ERR "%s: strange command complete without completed command\n", dev->name);
+			priv->restart = 1;
+			return;
+
+		default:
+			printk (KERN_WARNING "%s: strange command %d complete! (offset %04X)", dev->name,
+				nop.nop_command & CMD_MASK, caddr);
+			priv->restart = 1;
+			return;
+	}
+
+	while (nop.nop_status & STAT_COMPLETE) 
+	{
+		if (nop.nop_status & STAT_OK) 
+		{
+			priv->stats.tx_packets ++;
+			priv->stats.collisions += (nop.nop_status & STAT_COLLISIONS);
+		}
+		else
+		{
+			priv->stats.tx_errors ++;
+			if (nop.nop_status & STAT_COLLAFTERTX)
+				priv->stats.collisions ++;
+			if (nop.nop_status & STAT_NOCARRIER)
+				priv->stats.tx_carrier_errors ++;
+			if (nop.nop_status & STAT_TXLOSTCTS)
+				printk (KERN_WARNING "%s: cts lost\n", dev->name);
+			if (nop.nop_status & STAT_TXSLOWDMA)
+				priv->stats.tx_fifo_errors ++;
+			if (nop.nop_status & STAT_COLLEXCESSIVE)
+				priv->stats.collisions += 16;
+		}
+		if (nop.nop_link == caddr) 
+		{
+			printk (KERN_ERR "%s: tx buffer chaining error: tx command points to itself\n", dev->name);
+			break;
+		}
+
+		caddr = nop.nop_link;
+		ether1_readbuffer (dev, &nop, caddr, NOP_SIZE);
+		if ((nop.nop_command & CMD_MASK) != CMD_NOP) 
+		{
+			printk (KERN_ERR "%s: tx buffer chaining error: no nop after tx command\n", dev->name);
+			break;
+		}
+
+		if (caddr == nop.nop_link)
+			break;
+
+		caddr = nop.nop_link;
+		ether1_readbuffer (dev, &nop, caddr, NOP_SIZE);
+		if ((nop.nop_command & CMD_MASK) != CMD_TX) 
+		{
+			printk (KERN_ERR "%s: tx buffer chaining error: no tx command after nop\n", dev->name);
+			break;
+		}
+	}
+	priv->tx_tail = caddr;
+
+	caddr = priv->tx_head;
+	tst = ether1_txalloc (dev, TX_SIZE + TBD_SIZE + NOP_SIZE + ETH_FRAME_LEN);
+	priv->tx_head = caddr;
+	if (tst != -1)
+		dev->tbusy = 0;
+    
+	mark_bh (NET_BH);
+}
+
+static void ether1_recv_done (struct device *dev)
+{
+    	struct ether1_priv *priv = (struct ether1_priv *)dev->priv;
+	int status;
+	int nexttail, rbdaddr;
+	rbd_t rbd;
+
+	do
+	{
+		status = ether1_inw (dev, priv->rx_head, rfd_t, rfd_status, NORMALIRQS);
+		if ((status & RFD_COMPLETE) == 0)
+			break;
+
+		rbdaddr = ether1_inw (dev, priv->rx_head, rfd_t, rfd_rbdoffset, NORMALIRQS);
+		ether1_readbuffer (dev, &rbd, rbdaddr, RBD_SIZE);
+
+		if ((rbd.rbd_status & (RBD_EOF | RBD_ACNTVALID)) == (RBD_EOF | RBD_ACNTVALID)) 
+		{
+			int length = rbd.rbd_status & RBD_ACNT;
+			struct sk_buff *skb;
+
+			length = (length + 1) & ~1;
+			skb = dev_alloc_skb (length + 2);
+
+			if (skb) 
+			{
+				skb->dev = dev;
+				skb_reserve (skb, 2);
+
+				ether1_readbuffer (dev, skb_put (skb, length), rbd.rbd_bufl, length);
+
+				skb->protocol = eth_type_trans (skb, dev);
+				netif_rx (skb);
+				priv->stats.rx_packets ++;
+			}
+			else
+				priv->stats.rx_dropped ++;
+		}
+		else 
+		{
+			printk (KERN_WARNING "%s: %s\n", dev->name,
+				(rbd.rbd_status & RBD_EOF) ? "oversized packet" : "acnt not valid");
+			priv->stats.rx_dropped ++;
+		}
+
+		nexttail = ether1_inw (dev, priv->rx_tail, rfd_t, rfd_link, NORMALIRQS);
+		/* nexttail should be rx_head */
+		if (nexttail != priv->rx_head)
+			printk (KERN_ERR "%s: receiver buffer chaining error (%04X != %04X)\n",
+				dev->name, nexttail, priv->rx_head);
+		ether1_outw (dev, RFD_CMDEL | RFD_CMDSUSPEND, nexttail, rfd_t, rfd_command, NORMALIRQS);
+		ether1_outw (dev, 0, priv->rx_tail, rfd_t, rfd_command, NORMALIRQS);
+		ether1_outw (dev, 0, priv->rx_tail, rfd_t, rfd_status, NORMALIRQS);
+		ether1_outw (dev, 0, priv->rx_tail, rfd_t, rfd_rbdoffset, NORMALIRQS);
+	
+		priv->rx_tail = nexttail;
+		priv->rx_head = ether1_inw (dev, priv->rx_head, rfd_t, rfd_link, NORMALIRQS);
+	}
+	while (1);
+}
+
+static void ether1_interrupt (int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct device *dev = (struct device *)dev_id;
+    	struct ether1_priv *priv = (struct ether1_priv *)dev->priv;
+	int status;
+
+	dev->interrupt = 1;
+
+	status = ether1_inw (dev, SCB_ADDR, scb_t, scb_status, NORMALIRQS);
+
+	if (status) 
+	{
+		ether1_outw (dev, status & (SCB_STRNR | SCB_STCNA | SCB_STFR | SCB_STCX),
+			SCB_ADDR, scb_t, scb_command, NORMALIRQS);
+		outb (CTRL_CA | CTRL_ACK, REG_CONTROL);
+		if (status & SCB_STCX)
+			ether1_xmit_done (dev);
+		if (status & SCB_STCNA) 
+		{
+			if (priv->resetting == 0)
+				printk (KERN_WARNING "%s: CU went not ready ???\n", dev->name);
+			else
+				priv->resetting += 1;
+			if (ether1_inw (dev, SCB_ADDR, scb_t, scb_cbl_offset, NORMALIRQS) != (unsigned short)I82586_NULL) 
+			{
+				ether1_outw (dev, SCB_CMDCUCSTART, SCB_ADDR, scb_t, scb_command, NORMALIRQS);
+				outb (CTRL_CA, REG_CONTROL);
+			}
+			if (priv->resetting == 2)
+				priv->resetting = 0;
+		}
+		if (status & SCB_STFR)
+			ether1_recv_done (dev);
+
+		if (status & SCB_STRNR) 
+		{
+			if (ether1_inw (dev, SCB_ADDR, scb_t, scb_status, NORMALIRQS) & SCB_STRXSUSP) 
+			{
+				printk (KERN_WARNING "%s: RU went not ready: RU suspended\n", dev->name);
+				ether1_outw (dev, SCB_CMDRXRESUME, SCB_ADDR, scb_t, scb_command, NORMALIRQS);
+				outb (CTRL_CA, REG_CONTROL);
+				priv->stats.rx_dropped ++;	/* we suspended due to lack of buffer space */
+			}
+			else
+				printk (KERN_WARNING "%s: RU went not ready: %04X\n", dev->name,
+			ether1_inw (dev, SCB_ADDR, scb_t, scb_status, NORMALIRQS));
+			printk (KERN_WARNING "RU ptr = %04X\n", ether1_inw (dev, SCB_ADDR, scb_t, scb_rfa_offset,NORMALIRQS));
+		}
+	}
+	else
+        	outb (CTRL_ACK, REG_CONTROL);
+
+	dev->interrupt = 0;
+}
+
+static int ether1_close (struct device *dev)
+{
+#ifdef CLAIM_IRQ_AT_OPEN
+	free_irq (dev->irq, dev);
+#endif
+
+	ether1_reset (dev);	
+	dev->start = 0;
+	dev->tbusy = 0;
+	MOD_DEC_USE_COUNT;
+
+	return 0;
+}
+
+static struct enet_statistics *ether1_getstats (struct device *dev)
+{
+    	struct ether1_priv *priv = (struct ether1_priv *)dev->priv;
+	return &priv->stats;
+}
+
+
+/*
+ * Set or clear the multicast filter for this adaptor.
+ * num_addrs == -1	Promiscuous mode, receive all packets.
+ * num_addrs == 0	Normal mode, clear multicast list.
+ * num_addrs > 0	Multicast mode, receive normal and MC packets, and do
+ *			best-effort filtering.
+ */
+
+static void ether1_setmulticastlist (struct device *dev)
+{
+}
+
+/* ------------------------------------------------------------------------- */
+
+#ifdef MODULE
+
+static char ethernames[MAX_ECARDS][9];
+static struct device *my_ethers[MAX_ECARDS];
+static struct expansion_card *ec[MAX_ECARDS];
+
+int init_module (void)
+{
+	int i;
+
+	for (i = 0; i < MAX_ECARDS; i++) 
+	{
+		my_ethers[i] = NULL;
+		ec[i] = NULL;
+		strcpy (ethernames[i], "        ");
+	}
+
+	i = 0;
+
+	ecard_startfind();
+
+	do 
+	{
+		if ((ec[i] = ecard_find(0, ether1_cids)) == NULL)
+			break;
+
+		my_ethers[i] = (struct device *)kmalloc (sizeof (struct device), GFP_KERNEL);
+		memset (my_ethers[i], 0, sizeof (struct device));
+
+		my_ethers[i]->irq = ec[i]->irq;
+		my_ethers[i]->base_addr = ecard_address (ec[i], ECARD_IOC, ECARD_FAST);
+		my_ethers[i]->init = ether1_probe;
+		my_ethers[i]->name = ethernames[i];
+
+		ecard_claim (ec[i]);
+
+		if (register_netdev (my_ethers[i]) != 0) 
+		{
+			for (i = 0; i < 4; i++) 
+	    		{
+				if (my_ethers[i]) 
+				{
+					kfree (my_ethers[i]);
+					my_ethers[i] = NULL;
+				}
+				if (ec[i])
+				{
+					ecard_release (ec[i]);
+					ec[i] = NULL;
+				}
+			}
+			return -EIO;
+		}
+		i++;
+	}
+	while (i < MAX_ECARDS);
+
+	return i != 0 ? 0 : -ENODEV;
+}
+
+void cleanup_module (void)
+{
+	int i;
+
+	for (i = 0; i < MAX_ECARDS; i++) 
+	{
+		if (my_ethers[i]) 
+		{
+			unregister_netdev (my_ethers[i]);
+			release_region (my_ethers[i]->base_addr, 16);
+			release_region (my_ethers[i]->base_addr + 0x800, 4096);
+#ifndef CLAIM_IRQ_AT_OPEN
+			free_irq (my_ethers[i]->irq, my_ethers[i]);
+#endif
+			my_ethers[i] = NULL;
+		}
+		if (ec[i]) 
+		{
+			ecard_release (ec[i]);
+			ec[i] = NULL;
+		}
+	}
+}
+#endif /* MODULE */
diff -u --recursive --new-file v2.1.87/linux/drivers/acorn/net/ether1.h linux/drivers/acorn/net/ether1.h
--- v2.1.87/linux/drivers/acorn/net/ether1.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/acorn/net/ether1.h	Mon Feb 16 15:49:47 1998
@@ -0,0 +1,281 @@
+/*
+ * linux/arch/arm/drivers/net/ether1.h
+ *
+ * network driver for Acorn Ether1 cards.
+ *
+ * (c) 1996 Russell King
+ */
+
+#ifndef _LINUX_ether1_H
+#define _LINUX_ether1_H
+
+#ifdef __ETHER1_C
+/* use 0 for production, 1 for verification, >2 for debug */
+#ifndef NET_DEBUG
+#define NET_DEBUG 0
+#endif
+
+/* Page register */
+#define REG_PAGE	(dev->base_addr + 0x00)
+
+/* Control register */
+#define REG_CONTROL	(dev->base_addr + 0x01)
+#define CTRL_RST	0x01
+#define CTRL_LOOPBACK	0x02
+#define CTRL_CA		0x04
+#define CTRL_ACK	0x08
+
+#define ETHER1_RAM	(dev->base_addr + 0x800)
+
+/* HW address */
+#define IDPROM_ADDRESS	(dev->base_addr + 0x09)
+
+struct ether1_priv {
+	struct enet_statistics stats;
+	unsigned int tx_link;
+	unsigned int tx_head;
+	volatile unsigned int tx_tail;
+	volatile unsigned int rx_head;
+	volatile unsigned int rx_tail;
+	unsigned char bus_type;
+	unsigned char resetting;
+	unsigned char initialising : 1;
+	unsigned char restart      : 1;
+};
+
+static int ether1_open (struct device *dev);
+static int ether1_sendpacket (struct sk_buff *skb, struct device *dev);
+static void ether1_interrupt (int irq, void *dev_id, struct pt_regs *regs);
+static int ether1_close (struct device *dev);
+static struct enet_statistics *ether1_getstats (struct device *dev);
+static void ether1_setmulticastlist (struct device *dev);
+
+#define I82586_NULL (-1)
+
+typedef struct { /* tdr */
+	unsigned short tdr_status;
+	unsigned short tdr_command;
+	unsigned short tdr_link;
+	unsigned short tdr_result;
+#define TDR_TIME	(0x7ff)
+#define TDR_SHORT	(1 << 12)
+#define TDR_OPEN	(1 << 13)
+#define TDR_XCVRPROB	(1 << 14)
+#define TDR_LNKOK	(1 << 15)
+} tdr_t;
+
+typedef struct { /* transmit */
+	unsigned short tx_status;
+	unsigned short tx_command;
+	unsigned short tx_link;
+	unsigned short tx_tbdoffset;
+} tx_t;
+
+typedef struct { /* tbd */
+	unsigned short tbd_opts;
+#define TBD_CNT		(0x3fff)
+#define TBD_EOL		(1 << 15)
+	unsigned short tbd_link;
+	unsigned short tbd_bufl;
+	unsigned short tbd_bufh;
+} tbd_t;
+
+typedef struct { /* rfd */
+	unsigned short rfd_status;
+#define RFD_NOEOF	(1 << 6)
+#define RFD_FRAMESHORT	(1 << 7)
+#define RFD_DMAOVRN	(1 << 8)
+#define RFD_NORESOURCES	(1 << 9)
+#define RFD_ALIGNERROR	(1 << 10)
+#define RFD_CRCERROR	(1 << 11)
+#define RFD_OK		(1 << 13)
+#define RFD_FDCONSUMED	(1 << 14)
+#define RFD_COMPLETE	(1 << 15)
+	unsigned short rfd_command;
+#define RFD_CMDSUSPEND	(1 << 14)
+#define RFD_CMDEL	(1 << 15)
+	unsigned short rfd_link;
+	unsigned short rfd_rbdoffset;
+	unsigned char  rfd_dest[6];
+	unsigned char  rfd_src[6];
+	unsigned short rfd_len;
+} rfd_t;
+
+typedef struct { /* rbd */
+	unsigned short rbd_status;
+#define RBD_ACNT	(0x3fff)
+#define RBD_ACNTVALID	(1 << 14)
+#define RBD_EOF		(1 << 15)
+	unsigned short rbd_link;
+	unsigned short rbd_bufl;
+	unsigned short rbd_bufh;
+	unsigned short rbd_len;
+} rbd_t;
+
+typedef struct { /* nop */
+	unsigned short nop_status;
+	unsigned short nop_command;
+	unsigned short nop_link;
+} nop_t;
+
+typedef struct { /* set multicast */
+	unsigned short mc_status;
+	unsigned short mc_command;
+	unsigned short mc_link;
+	unsigned short mc_cnt;
+	unsigned char  mc_addrs[1][6];
+} mc_t;
+
+typedef struct { /* set address */
+	unsigned short sa_status;
+	unsigned short sa_command;
+	unsigned short sa_link;
+	unsigned char  sa_addr[6];
+} sa_t;
+
+typedef struct { /* config command */
+	unsigned short cfg_status;
+	unsigned short cfg_command;
+	unsigned short cfg_link;
+	unsigned char  cfg_bytecnt;	/* size foll data: 4 - 12		 */
+	unsigned char  cfg_fifolim;	/* FIFO threshold			 */
+	unsigned char  cfg_byte8;
+#define CFG8_SRDY	(1 << 6)
+#define CFG8_SAVEBADF	(1 << 7)
+	unsigned char  cfg_byte9;
+#define CFG9_ADDRLEN(x)	(x)
+#define CFG9_ADDRLENBUF	(1 << 3)
+#define CFG9_PREAMB2	(0 << 4)
+#define CFG9_PREAMB4	(1 << 4)
+#define CFG9_PREAMB8	(2 << 4)
+#define CFG9_PREAMB16	(3 << 4)
+#define CFG9_ILOOPBACK	(1 << 6)
+#define CFG9_ELOOPBACK	(1 << 7)
+	unsigned char  cfg_byte10;
+#define CFG10_LINPRI(x)	(x)
+#define CFG10_ACR(x)	(x << 4)
+#define CFG10_BOFMET	(1 << 7)
+	unsigned char  cfg_ifs;
+	unsigned char  cfg_slotl;
+	unsigned char  cfg_byte13;
+#define CFG13_SLOTH(x)	(x)
+#define CFG13_RETRY(x)	(x << 4)
+	unsigned char  cfg_byte14;
+#define CFG14_PROMISC	(1 << 0)
+#define CFG14_DISBRD	(1 << 1)
+#define CFG14_MANCH	(1 << 2)
+#define CFG14_TNCRS	(1 << 3)
+#define CFG14_NOCRC	(1 << 4)
+#define CFG14_CRC16	(1 << 5)
+#define CFG14_BTSTF	(1 << 6)
+#define CFG14_FLGPAD	(1 << 7)
+	unsigned char  cfg_byte15;
+#define CFG15_CSTF(x)	(x)
+#define CFG15_ICSS	(1 << 3)
+#define CFG15_CDTF(x)	(x << 4)
+#define CFG15_ICDS	(1 << 7)
+	unsigned short cfg_minfrmlen;
+} cfg_t;
+
+typedef struct { /* scb */
+	unsigned short scb_status;	/* status of 82586			*/
+#define SCB_STRXMASK		(7 << 4)	/* Receive unit status		*/
+#define SCB_STRXIDLE		(0 << 4)	/* Idle				*/
+#define SCB_STRXSUSP		(1 << 4)	/* Suspended			*/
+#define SCB_STRXNRES		(2 << 4)	/* No resources			*/
+#define SCB_STRXRDY		(4 << 4)	/* Ready			*/
+#define SCB_STCUMASK		(7 << 8)	/* Command unit status		*/
+#define SCB_STCUIDLE		(0 << 8)	/* Idle				*/
+#define SCB_STCUSUSP		(1 << 8)	/* Suspended			*/
+#define SCB_STCUACTV		(2 << 8)	/* Active			*/
+#define SCB_STRNR		(1 << 12)	/* Receive unit not ready	*/
+#define SCB_STCNA		(1 << 13)	/* Command unit not ready	*/
+#define SCB_STFR		(1 << 14)	/* Frame received		*/
+#define SCB_STCX		(1 << 15)	/* Command completed		*/
+	unsigned short scb_command;	/* Next command				*/
+#define SCB_CMDRXSTART		(1 << 4)	/* Start (at rfa_offset)	*/
+#define SCB_CMDRXRESUME		(2 << 4)	/* Resume reception		*/
+#define SCB_CMDRXSUSPEND	(3 << 4)	/* Suspend reception		*/
+#define SCB_CMDRXABORT		(4 << 4)	/* Abort reception		*/
+#define SCB_CMDCUCSTART		(1 << 8)	/* Start (at cbl_offset)	*/
+#define SCB_CMDCUCRESUME	(2 << 8)	/* Resume execution		*/
+#define SCB_CMDCUCSUSPEND	(3 << 8)	/* Suspend execution		*/
+#define SCB_CMDCUCABORT		(4 << 8)	/* Abort execution		*/
+#define SCB_CMDACKRNR		(1 << 12)	/* Ack RU not ready		*/
+#define SCB_CMDACKCNA		(1 << 13)	/* Ack CU not ready		*/
+#define SCB_CMDACKFR		(1 << 14)	/* Ack Frame received		*/
+#define SCB_CMDACKCX		(1 << 15)	/* Ack Command complete		*/
+	unsigned short scb_cbl_offset;	/* Offset of first command unit		*/
+	unsigned short scb_rfa_offset;	/* Offset of first receive frame area	*/
+	unsigned short scb_crc_errors;	/* Properly aligned frame with CRC error*/
+	unsigned short scb_aln_errors;	/* Misaligned frames			*/
+	unsigned short scb_rsc_errors;	/* Frames lost due to no space		*/
+	unsigned short scb_ovn_errors;	/* Frames lost due to slow bus		*/
+} scb_t;
+
+typedef struct { /* iscp */
+	unsigned short iscp_busy;	/* set by CPU before CA			*/
+	unsigned short iscp_offset;	/* offset of SCB			*/
+	unsigned short iscp_basel;	/* base of SCB				*/
+	unsigned short iscp_baseh;
+} iscp_t;
+
+    /* this address must be 0xfff6 */
+typedef struct { /* scp */
+	unsigned short scp_sysbus;	/* bus size */
+#define SCP_SY_16BBUS	0x00
+#define SCP_SY_8BBUS	0x01
+	unsigned short scp_junk[2];	/* junk */
+	unsigned short scp_iscpl;	/* lower 16 bits of iscp */
+	unsigned short scp_iscph;	/* upper 16 bits of iscp */
+} scp_t;
+
+/* commands */
+#define CMD_NOP			0
+#define CMD_SETADDRESS		1
+#define CMD_CONFIG		2
+#define CMD_SETMULTICAST	3
+#define CMD_TX			4
+#define CMD_TDR			5
+#define CMD_DUMP		6
+#define CMD_DIAGNOSE		7
+
+#define CMD_MASK		7
+
+#define CMD_INTR		(1 << 13)
+#define CMD_SUSP		(1 << 14)
+#define CMD_EOL			(1 << 15)
+
+#define STAT_COLLISIONS		(15)
+#define STAT_COLLEXCESSIVE	(1 << 5)
+#define STAT_COLLAFTERTX	(1 << 6)
+#define STAT_TXDEFERRED		(1 << 7)
+#define STAT_TXSLOWDMA		(1 << 8)
+#define STAT_TXLOSTCTS		(1 << 9)
+#define STAT_NOCARRIER		(1 << 10)
+#define STAT_FAIL		(1 << 11)
+#define STAT_ABORTED		(1 << 12)
+#define STAT_OK			(1 << 13)
+#define STAT_BUSY		(1 << 14)
+#define STAT_COMPLETE		(1 << 15)
+#endif
+#endif
+
+/*
+ * Ether1 card definitions:
+ *
+ * FAST accesses:
+ *	+0	Page register
+ * 			16 pages
+ *	+4	Control
+ *			'1' = reset
+ *			'2' = loopback
+ *			'4' = CA
+ *			'8' = int ack
+ *
+ * RAM at address + 0x2000
+ * Pod. Prod id = 3
+ * Words after ID block [base + 8 words]
+ *	+0 pcb issue (0x0c and 0xf3 invalid)
+ *	+1 - +6 eth hw address
+ */
diff -u --recursive --new-file v2.1.87/linux/drivers/acorn/net/ether3.c linux/drivers/acorn/net/ether3.c
--- v2.1.87/linux/drivers/acorn/net/ether3.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/acorn/net/ether3.c	Mon Feb 16 15:49:48 1998
@@ -0,0 +1,896 @@
+/*
+ * linux/drivers/net/ether3.c
+ *
+ * SEEQ nq8005 ethernet driver for Acorn/ANT Ether3 card
+ *  for Acorn machines
+ *
+ * By Russell King, with some suggestions from borris@ant.co.uk
+ *
+ * Changelog:
+ * 1.04 RMK     29/02/1996      Won't pass packets that are from our ethernet
+ *                              address up to the higher levels - they're
+ *                              silently ignored.  I/F can now be put into
+ *                              multicast mode.  Receiver routine optimised.
+ * 1.05 RMK     30/02/1996      Now claims interrupt at open when part of
+ *                              the kernel rather than when a module.
+ * 1.06 RMK     02/03/1996      Various code cleanups
+ * 1.07 RMK     13/10/1996      Optimised interrupt routine and transmit
+ *                              routines.
+ * 1.08 RMK     14/10/1996      Fixed problem with too many packets,
+ *                              prevented the kernel message about dropped
+ *                              packets appearing too many times a second.
+ *                              Now does not disable all IRQs, only the IRQ
+ *                              used by this card.
+ * 1.09 RMK     10/11/1996      Only enables TX irq when buffer space is low,
+ *                              but we still service the TX queue if we get a
+ *                              RX interrupt.
+ * 1.10 RMK     15/07/1997      Fixed autoprobing of NQ8004.
+ * 1.11 RMK     16/11/1997      Fixed autoprobing of NQ8005A.
+ * 1.12 RMK     31/12/1997      Removed reference to dev_tint for Linux 2.1.
+ *
+ * TODO:
+ *  When we detect a fatal error on the interface, we should restart it.
+ */
+
+static char *version = "ether3 ethernet driver (c) 1995-1998 R.M.King v1.12\n";
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/in.h>
+#include <linux/malloc.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <asm/ecard.h>
+#include <asm/delay.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+
+#include "ether3.h"
+
+#ifndef MODULE
+#define CLAIM_IRQ_AT_OPEN
+#endif
+
+static unsigned int net_debug = NET_DEBUG;
+
+static const card_ids ether3_cids[] =
+{
+	{MANU_ANT2, PROD_ANT_ETHER3},
+	{MANU_ANT, PROD_ANT_ETHER3},
+	{MANU_ANT, PROD_ANT_ETHERB},	/* trial - will etherb work? */
+	{0xffff, 0xffff}
+};
+
+static void ether3_setmulticastlist(struct device *dev);
+static int ether3_rx(struct device *dev, struct dev_priv *priv, unsigned int maxcnt);
+static void ether3_tx(struct device *dev, struct dev_priv *priv);
+
+extern int inswb(int reg, void *buffer, int len);
+extern int outswb(int reg, void *buffer, int len);
+
+#define 	struct dev_priv *priv = (struct dev_priv *)dev->priv \
+	struct dev_priv *priv = (struct dev_priv *)dev->priv
+
+#define BUS_16		2
+#define BUS_8		1
+#define BUS_UNKNOWN	0
+
+/*
+ * I'm not sure what address we should default to if the internal one
+ * is corrupted...
+ */
+
+unsigned char def_eth_addr[6] =
+{0x00, 0x01, 0x02, 0x03, 0x04, 0x05};
+
+/* --------------------------------------------------------------------------- */
+
+typedef enum {
+	buffer_write,
+	buffer_read
+} buffer_rw_t;
+
+static int ether3_setbuffer(struct device *dev, buffer_rw_t read, int start)
+{
+	struct dev_priv *priv = (struct dev_priv *) dev->priv;
+	int timeout = 1000;
+
+	outw(priv->regs.config1 | CFG1_LOCBUFMEM, REG_CONFIG1);
+	outw(priv->regs.command | CMD_FIFOWRITE, REG_COMMAND);
+	while ((inw(REG_STATUS) & STAT_FIFOEMPTY) == 0) {
+		if (!timeout--) {
+			printk(KERN_ERR "%s: setbuffer broken\n", dev->name);
+			priv->broken = 1;
+			return 1;
+		}
+		udelay(1);
+	}
+	if (read == buffer_read) {
+		outw(start, REG_DMAADDR);
+		outw(priv->regs.command | CMD_FIFOREAD, REG_COMMAND);
+	} else {
+		outw(priv->regs.command | CMD_FIFOWRITE, REG_COMMAND);
+		outw(start, REG_DMAADDR);
+	}
+	return 0;
+}
+
+/*
+ * write data to the buffer memory
+ */
+#define ether3_writebuffer(dev,data,length)			\
+	outswb (REG_BUFWIN, (data), (length))
+
+#define ether3_writeword(dev,data)				\
+	outw ((data), REG_BUFWIN)
+
+#define ether3_writelong(dev,data)	{			\
+	unsigned long reg_bufwin = REG_BUFWIN;			\
+	outw ((data), reg_bufwin);				\
+	outw ((data) >> 16, reg_bufwin);			\
+}
+
+/*
+ * read data from the buffer memory
+ */
+#define ether3_readbuffer(dev,data,length)			\
+	inswb (REG_BUFWIN, (data), (length))
+
+#define ether3_readword(dev)					\
+	inw (REG_BUFWIN)
+
+#define ether3_readlong(dev)	 				\
+	inw (REG_BUFWIN) | (inw (REG_BUFWIN) << 16)
+
+/*
+ * Switch LED off...
+ */
+static void ether3_ledoff(unsigned long data)
+{
+	struct device *dev = (struct device *) data;
+	struct dev_priv *priv = (struct dev_priv *) dev->priv;
+	outw(priv->regs.config2 |= CFG2_CTRLO, REG_CONFIG2);
+}
+
+/*
+ * switch LED on...
+ */
+static inline void ether3_ledon(struct device *dev, struct dev_priv *priv)
+{
+	del_timer(&priv->timer);
+	priv->timer.expires = jiffies + HZ / 50;	/* leave on for 1/50th second */
+	priv->timer.data = (unsigned long) dev;
+	priv->timer.function = ether3_ledoff;
+	add_timer(&priv->timer);
+	if (priv->regs.config2 & CFG2_CTRLO)
+		outw(priv->regs.config2 &= ~CFG2_CTRLO, REG_CONFIG2);
+}
+
+/*
+ * Read the ethernet address string from the on board rom.
+ * This is an ascii string!!!
+ */
+static void ether3_addr(char *addr, struct expansion_card *ec)
+{
+	struct in_chunk_dir cd;
+	char *s;
+
+	if (ecard_readchunk(&cd, ec, 0xf5, 0) && (s = strchr(cd.d.string, '('))) {
+		int i;
+		for (i = 0; i < 6; i++) {
+			addr[i] = simple_strtoul(s + 1, &s, 0x10);
+			if (*s != (i == 5 ? ')' : ':'))
+				break;
+		}
+		if (i == 6)
+			return;
+	}
+	memcpy(addr, def_eth_addr, 6);
+}
+
+/* --------------------------------------------------------------------------- */
+
+static int ether3_ramtest(struct device *dev, unsigned char byte)
+{
+	unsigned char *buffer = kmalloc(RX_END, GFP_KERNEL);
+	int i, ret = 0;
+	int max_errors = 4;
+	int bad = -1;
+
+	if (!buffer)
+		return 1;
+
+	memset(buffer, byte, RX_END);
+	ether3_setbuffer(dev, buffer_write, 0);
+	ether3_writebuffer(dev, buffer, TX_END);
+	ether3_setbuffer(dev, buffer_write, RX_START);
+	ether3_writebuffer(dev, buffer + RX_START, RX_LEN);
+	memset(buffer, byte ^ 0xff, RX_END);
+	ether3_setbuffer(dev, buffer_read, 0);
+	ether3_readbuffer(dev, buffer, TX_END);
+	ether3_setbuffer(dev, buffer_read, RX_START);
+	ether3_readbuffer(dev, buffer + RX_START, RX_LEN);
+
+	for (i = 0; i < RX_END; i++) {
+		if (buffer[i] != byte) {
+			if (max_errors > 0 && bad != buffer[i]) {
+				printk("%s: RAM failed with (%02X instead of %02X) at 0x%04X",
+				       dev->name, buffer[i], byte, i);
+				ret = 2;
+				max_errors--;
+				bad = i;
+			}
+		} else {
+			if (bad != -1) {
+				if (bad != i - 1)
+					printk(" - 0x%04X", i - 1);
+				printk("\n");
+				bad = -1;
+			}
+		}
+	}
+	if (bad != -1)
+		printk(" - 0xffff\n");
+	kfree(buffer);
+
+	return ret;
+}
+
+/* ------------------------------------------------------------------------------- */
+
+static int ether3_init_2(struct device *dev)
+{
+	struct dev_priv *priv = (struct dev_priv *) dev->priv;
+	int i;
+
+	priv->regs.config1 = CFG1_RECVCOMPSTAT0 | CFG1_DMABURST8;
+	priv->regs.config2 = CFG2_CTRLO | CFG2_RECVCRC | CFG2_ERRENCRC;
+	priv->regs.command = 0;
+	/*
+	 * Set up our hardware address
+	 */
+	outw(priv->regs.config1 | CFG1_BUFSELSTAT0, REG_CONFIG1);
+	for (i = 0; i < 6; i++)
+		outb(dev->dev_addr[i], REG_BUFWIN);
+
+	if (dev->flags & IFF_PROMISC)
+		priv->regs.config1 |= CFG1_RECVPROMISC;
+	else if (dev->flags & IFF_MULTICAST)
+		priv->regs.config1 |= CFG1_RECVSPECBRMULTI;
+	else
+		priv->regs.config1 |= CFG1_RECVSPECBROAD;
+
+	/*
+	 * There is a problem with the NQ8005 in that it occasionally losses the
+	 * last two bytes.  To get round this problem, we receive the CRC as well.
+	 * That way, if we do loose the last two, then it doesn't matter
+	 */
+	outw(priv->regs.config1 | CFG1_TRANSEND, REG_CONFIG1);
+	outw((TX_END >> 8) - 1, REG_BUFWIN);
+	outw(priv->rx_head, REG_RECVPTR);
+	outw(0, REG_TRANSMITPTR);
+	outw(priv->rx_head >> 8, REG_RECVEND);
+	outw(priv->regs.config2, REG_CONFIG2);
+	outw(priv->regs.config1 | CFG1_LOCBUFMEM, REG_CONFIG1);
+	outw(priv->regs.command, REG_COMMAND);
+
+	i = ether3_ramtest(dev, 0x5A);
+	if (i)
+		return i;
+	i = ether3_ramtest(dev, 0x1E);
+	if (i)
+		return i;
+
+	ether3_setbuffer(dev, buffer_write, 0);
+	ether3_writelong(dev, 0);
+	return 0;
+}
+
+static void ether3_init_for_open(struct device *dev)
+{
+	struct dev_priv *priv = (struct dev_priv *) dev->priv;
+	int i;
+
+	memset(&priv->stats, 0, sizeof(struct enet_statistics));
+
+	priv->regs.command = 0;
+	outw(CMD_RXOFF | CMD_TXOFF, REG_COMMAND);
+	while (inw(REG_STATUS) & (STAT_RXON | STAT_TXON));
+
+	outw(priv->regs.config1 | CFG1_BUFSELSTAT0, REG_CONFIG1);
+	for (i = 0; i < 6; i++)
+		outb(dev->dev_addr[i], REG_BUFWIN);
+
+	priv->tx_used = 0;
+	priv->tx_head = 0;
+	priv->tx_tail = 0;
+	priv->regs.config2 |= CFG2_CTRLO;
+	priv->rx_head = RX_START;
+
+	outw(priv->regs.config1 | CFG1_TRANSEND, REG_CONFIG1);
+	outw((TX_END >> 8) - 1, REG_BUFWIN);
+	outw(priv->rx_head, REG_RECVPTR);
+	outw(priv->rx_head >> 8, REG_RECVEND);
+	outw(0, REG_TRANSMITPTR);
+	outw(priv->regs.config2, REG_CONFIG2);
+	outw(priv->regs.config1 | CFG1_LOCBUFMEM, REG_CONFIG1);
+
+	ether3_setbuffer(dev, buffer_write, 0);
+	ether3_writelong(dev, 0);
+
+	priv->regs.command = CMD_ENINTRX;
+	outw(priv->regs.command | CMD_RXON, REG_COMMAND);
+}
+
+/*
+ * This is the real probe routine.
+ */
+static int ether3_probe1(struct device *dev)
+{
+	static unsigned version_printed = 0;
+	struct dev_priv *priv;
+	unsigned int i, bus_type, error = ENODEV;
+
+	if (net_debug && version_printed++ == 0)
+		printk(version);
+
+	if (!dev->priv) {
+		dev->priv = kmalloc(sizeof(struct dev_priv), GFP_KERNEL);
+		if (!dev->priv)
+			return -ENOMEM;
+	}
+	priv = (struct dev_priv *) dev->priv;
+	memset(priv, 0, sizeof(struct dev_priv));
+
+	request_region(dev->base_addr, 128, "ether3");
+
+	/* Reset card...
+	 */
+	outb(0x80, REG_CONFIG2 + 1);
+	bus_type = BUS_UNKNOWN;
+	udelay(4);
+
+	/* Test using Receive Pointer (16-bit register) to find out
+	 * how the ether3 is connected to the bus...
+	 */
+	outb(0, REG_RECVPTR);
+	outb(1, REG_RECVPTR + 1);
+
+	if (inb(REG_RECVPTR + 1) == 1) {
+		if (inb(REG_RECVPTR) == 0)
+			bus_type = BUS_8;
+		else if (inw(REG_RECVPTR) == 0x101)
+			bus_type = BUS_16;
+	}
+	switch (bus_type) {
+	case BUS_UNKNOWN:
+		printk(KERN_ERR "%s: unable to identify podule bus width\n", dev->name);
+		goto failed;
+
+	case BUS_8:
+		printk(KERN_ERR "%s: ether3 found, but is an unsupported 8-bit card\n", dev->name);
+		goto failed;
+
+	default:
+		break;
+	}
+
+	printk("%s: ether3 found at %lx, IRQ%d, ether address ", dev->name, dev->base_addr, dev->irq);
+	for (i = 0; i < 6; i++)
+		printk(i == 5 ? "%2.2x\n" : "%2.2x:", dev->dev_addr[i]);
+
+	if (!ether3_init_2(dev)) {
+		dev->open = ether3_open;
+		dev->stop = ether3_close;
+		dev->hard_start_xmit = ether3_sendpacket;
+		dev->get_stats = ether3_getstats;
+		dev->set_multicast_list = ether3_setmulticastlist;
+
+		/* Fill in the fields of the device structure with ethernet values. */
+		ether_setup(dev);
+#ifndef CLAIM_IRQ_AT_OPEN
+		if (request_irq(dev->irq, ether3_interrupt, 0, "ether3", dev))
+			error = EAGAIN;
+		else
+#endif
+			return 0;
+	}
+      failed:
+	kfree(dev->priv);
+	dev->priv = NULL;
+	release_region(dev->base_addr, 128);
+	return error;
+}
+
+#ifndef MODULE
+int ether3_probe(struct device *dev)
+{
+	struct expansion_card *ec;
+
+	if (!dev)
+		return ENODEV;
+
+	ecard_startfind();
+
+	if ((ec = ecard_find(0, ether3_cids)) == NULL)
+		return ENODEV;
+
+	dev->base_addr = ecard_address(ec, ECARD_MEMC, 0);
+	dev->irq = ec->irq;
+
+	ecard_claim(ec);
+
+	ether3_addr(dev->dev_addr, ec);
+	return ether3_probe1(dev);
+}
+#endif
+
+/*
+ * Open/initialize the board.  This is called (in the current kernel)
+ * sometime after booting when the 'ifconfig' program is run.
+ *
+ * This routine should set everything up anew at each open, even
+ * registers that "should" only need to be set once at boot, so that
+ * there is non-reboot way to recover if something goes wrong.
+ */
+static int ether3_open(struct device *dev)
+{
+	ether3_init_for_open(dev);
+
+	MOD_INC_USE_COUNT;
+
+#ifdef CLAIM_IRQ_AT_OPEN
+	if (request_irq(dev->irq, ether3_interrupt, 0, "ether3", dev)) {
+		MOD_DEC_USE_COUNT;
+		return -EAGAIN;
+	}
+#endif
+
+	dev->tbusy = 0;
+	dev->interrupt = 0;
+	dev->start = 1;
+	return 0;
+}
+
+/*
+ * The inverse routine to ether3_open().
+ */
+static int ether3_close(struct device *dev)
+{
+	struct dev_priv *priv = (struct dev_priv *) dev->priv;
+
+	dev->tbusy = 1;
+	dev->start = 0;
+
+	disable_irq(dev->irq);
+
+	outw(CMD_RXOFF | CMD_TXOFF, REG_COMMAND);
+	priv->regs.command = 0;
+	while (inw(REG_STATUS) & (STAT_RXON | STAT_TXON));
+	outb(0x80, REG_CONFIG2 + 1);
+	outw(0, REG_COMMAND);
+
+	enable_irq(dev->irq);
+#ifdef CLAIM_IRQ_AT_OPEN
+	free_irq(dev->irq, dev);
+#endif
+
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+/*
+ * Get the current statistics.        This may be called with the card open or
+ * closed.
+ */
+static struct enet_statistics *ether3_getstats(struct device *dev)
+{
+	struct dev_priv *priv = (struct dev_priv *) dev->priv;
+	return &priv->stats;
+}
+
+/*
+ * Set or clear promiscuous/multicast mode filter for this adaptor.
+ *
+ * We don't attempt any packet filtering.  The card may have a SEEQ 8004
+ * in which does not have the other ethernet address registers present...
+ */
+static void ether3_setmulticastlist(struct device *dev)
+{
+	struct dev_priv *priv = (struct dev_priv *) dev->priv;
+
+	priv->regs.config1 &= ~CFG1_RECVPROMISC;
+
+	if (dev->flags & IFF_PROMISC) {
+		/* promiscuous mode */
+		priv->regs.config1 |= CFG1_RECVPROMISC;
+	} else if (dev->flags & IFF_ALLMULTI) {
+		priv->regs.config1 |= CFG1_RECVSPECBRMULTI;
+	} else
+		priv->regs.config1 |= CFG1_RECVSPECBROAD;
+
+	outw(priv->regs.config1 | CFG1_LOCBUFMEM, REG_CONFIG1);
+}
+
+/*
+ * Transmit a packet
+ */
+static int ether3_sendpacket(struct sk_buff *skb, struct device *dev)
+{
+	struct dev_priv *priv = (struct dev_priv *) dev->priv;
+      retry:
+	if (!dev->tbusy) {
+		/* Block a timer-based transmit from overlapping.  This could better be
+		 * done with atomic_swap(1, dev->tbusy), but set_bit() works as well.
+		 */
+		if (!test_and_set_bit(0, (void *) &dev->tbusy)) {
+			unsigned long flags;
+			unsigned int length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
+			unsigned int ptr, nextptr;
+
+			length = (length + 1) & ~1;
+
+			if (priv->broken) {
+				dev_kfree_skb(skb, FREE_WRITE);
+				priv->stats.tx_dropped++;
+				dev->tbusy = 0;
+				return 0;
+			}
+			ptr = priv->tx_head;
+			nextptr = ptr + 0x600;
+			if (nextptr >= TX_END)
+				nextptr = 0;
+			if (nextptr == priv->tx_tail)
+				return 1;	/* unable to queue */
+			priv->tx_head = nextptr;
+
+			save_flags_cli(flags);
+			ether3_setbuffer(dev, buffer_write, nextptr);
+			ether3_writelong(dev, 0);
+			ether3_setbuffer(dev, buffer_write, ptr + 4);
+			ether3_writebuffer(dev, skb->data, length);
+			ether3_writeword(dev, htons(nextptr));
+			ether3_writeword(dev, (TXHDR_TRANSMIT | TXHDR_CHAINCONTINUE) >> 16);
+			ether3_setbuffer(dev, buffer_write, ptr);
+#define TXHDR_FLAGS (TXHDR_TRANSMIT|TXHDR_CHAINCONTINUE|TXHDR_DATAFOLLOWS|TXHDR_ENSUCCESS)
+			ether3_writeword(dev, htons(ptr + length + 4));
+			ether3_writeword(dev, (TXHDR_FLAGS >> 16));
+			ether3_ledon(dev, priv);
+			priv->tx_used++;
+			if (priv->tx_used < MAX_TX_BUFFERED)
+				dev->tbusy = 0;
+			if (priv->tx_used >= (MAX_TX_BUFFERED * 3 / 4)) {
+				priv->regs.command |= CMD_ENINTTX;
+				outw(priv->regs.command, REG_COMMAND);
+			}
+			restore_flags(flags);
+
+			dev->trans_start = jiffies;
+			dev_kfree_skb(skb, FREE_WRITE);
+			if (!(inw(REG_STATUS) & STAT_TXON)) {
+				outw(ptr, REG_TRANSMITPTR);
+				outw(priv->regs.command | CMD_TXON, REG_COMMAND);
+			}
+			return 0;
+		} else {
+			printk("%s: transmitter access conflict.\n", dev->name);
+			return 1;
+		}
+	} else {
+		/* If we get here, some higher level has decided we are broken.
+		 * There should really be a "kick me" function call instead.
+		 */
+		int tickssofar = jiffies - dev->trans_start;
+		if (tickssofar < 5)
+			return 1;
+		del_timer(&priv->timer);
+		printk("%s: transmit timed out, network cable problem?\n", dev->name);
+		dev->tbusy = 0;
+		priv->regs.config2 |= CFG2_CTRLO;
+		outw(priv->regs.config2, REG_CONFIG2);
+		dev->trans_start = jiffies;
+		goto retry;
+	}
+}
+
+static void ether3_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct device *dev = (struct device *) dev_id;
+	struct dev_priv *priv;
+	unsigned int status;
+
+#if NET_DEBUG > 1
+	if (net_debug & DEBUG_INT)
+		printk("eth3irq: %d ", irq);
+#endif
+
+	priv = (struct dev_priv *) dev->priv;
+
+	dev->interrupt = 1;
+	status = inw(REG_STATUS);
+	/*
+	 * Dispite we disable the TX interrupt when the packet buffer is
+	 * mostly empty, if we happen to get a RX interrupt, we might as
+	 * well handle the TX packets as well.
+	 */
+	if (status & STAT_INTTX) {	/* Packets transmitted */
+		outw(CMD_ACKINTTX | priv->regs.command, REG_COMMAND);
+		ether3_tx(dev, priv);
+	}
+	status = inw(REG_STATUS);
+	if (status & STAT_INTRX && ether3_rx(dev, priv, 12)) {	/* Got packet(s). */
+		/*
+		 * We only acknowledge the interrupt if we have received all packets
+		 * in the buffer or else we run out of memory. This is to allow the
+		 * bh routines to run.
+		 */
+		outw(CMD_ACKINTRX | priv->regs.command, REG_COMMAND);
+		/*
+		 * Receive again if some have become available - we may have cleared
+		 * a pending IRQ
+		 */
+		ether3_rx(dev, priv, 4);
+	}
+	dev->interrupt = 0;
+
+#if NET_DEBUG > 1
+	if (net_debug & DEBUG_INT)
+		printk("done\n");
+#endif
+}
+
+/*
+ * If we have a good packet(s), get it/them out of the buffers.
+ */
+static int ether3_rx(struct device *dev, struct dev_priv *priv, unsigned int maxcnt)
+{
+	unsigned int next_ptr = priv->rx_head, received = 0;
+	ether3_ledon(dev, priv);
+
+	do {
+		unsigned int this_ptr, status;
+		unsigned char addrs[16];
+
+		/*
+		 * read the first 16 bytes from the buffer.
+		 * This contains the status bytes etc and ethernet addresses,
+		 * and we also check the source ethernet address to see if
+		 * it originated from us.
+		 */
+		{
+			unsigned int temp_ptr;
+			ether3_setbuffer(dev, buffer_read, next_ptr);
+			temp_ptr = ether3_readword(dev);
+			status = ether3_readword(dev);
+			if (!(status & RXSTAT_DONE) || !temp_ptr)
+				break;
+
+			this_ptr = next_ptr + 4;
+			next_ptr = ntohs(temp_ptr);
+		}
+		ether3_setbuffer(dev, buffer_read, this_ptr);
+		ether3_readbuffer(dev, addrs + 2, 12);
+
+		/*
+		 * ignore our own packets...
+		 */
+		if (!(*(unsigned long *) &dev->dev_addr[0] ^ *(unsigned long *) &addrs[2 + 6]) &&
+		    !(*(unsigned short *) &dev->dev_addr[4] ^ *(unsigned short *) &addrs[2 + 10])) {
+			maxcnt++;	/* compensate for loopedback packet */
+			outw(next_ptr >> 8, REG_RECVEND);
+		} else if (!(status & (RXSTAT_OVERSIZE | RXSTAT_CRCERROR | RXSTAT_DRIBBLEERROR | RXSTAT_SHORTPACKET))) {
+			unsigned int length = next_ptr - this_ptr;
+			struct sk_buff *skb;
+
+			if (next_ptr <= this_ptr)
+				length += RX_END - RX_START;
+
+			skb = dev_alloc_skb(length + 2);
+			if (skb) {
+				unsigned char *buf;
+
+				skb->dev = dev;
+				skb_reserve(skb, 2);
+				buf = skb_put(skb, length);
+				ether3_readbuffer(dev, buf + 12, length - 12);
+				outw(next_ptr >> 8, REG_RECVEND);
+				*(unsigned short *) (buf + 0) = *(unsigned short *) (addrs + 2);
+				*(unsigned long *) (buf + 2) = *(unsigned long *) (addrs + 4);
+				*(unsigned long *) (buf + 6) = *(unsigned long *) (addrs + 8);
+				*(unsigned short *) (buf + 10) = *(unsigned short *) (addrs + 12);
+				skb->protocol = eth_type_trans(skb, dev);
+				netif_rx(skb);
+				received++;
+			} else
+				goto dropping;
+		} else {
+			struct enet_statistics *stats = &priv->stats;
+			outw(next_ptr >> 8, REG_RECVEND);
+			if (status & RXSTAT_OVERSIZE)
+				stats->rx_length_errors++;
+			if (status & RXSTAT_CRCERROR)
+				stats->rx_crc_errors++;
+			if (status & RXSTAT_DRIBBLEERROR)
+				stats->rx_fifo_errors++;
+			if (status & RXSTAT_SHORTPACKET)
+				stats->rx_length_errors++;
+			stats->rx_errors++;
+		}
+	}
+	while (--maxcnt);
+
+      done:
+	priv->stats.rx_packets += received;
+	priv->rx_head = next_ptr;
+	/*
+	 * If rx went off line, then that means that the buffer may be full.  We
+	 * have dropped at least one packet.
+	 */
+	if (!(inw(REG_STATUS) & STAT_RXON)) {
+		priv->stats.rx_dropped++;
+		outw(next_ptr, REG_RECVPTR);
+		outw(priv->regs.command | CMD_RXON, REG_COMMAND);
+	}
+	return maxcnt;
+
+      dropping:{
+		static unsigned long last_warned;
+
+		outw(next_ptr >> 8, REG_RECVEND);
+		/*
+		 * Don't print this message too many times...
+		 */
+		if (jiffies - last_warned > 30 * HZ) {
+			last_warned = jiffies;
+			printk("%s: memory squeeze, dropping packet.\n", dev->name);
+		}
+		priv->stats.rx_dropped++;
+		goto done;
+	}
+}
+
+/*
+ * Update stats for the transmitted packet(s)
+ */
+static void ether3_tx(struct device *dev, struct dev_priv *priv)
+{
+	unsigned int tx_tail = priv->tx_tail;
+
+	do {
+		unsigned long status;
+		/*
+		 * Read the packet header
+		 */
+		ether3_setbuffer(dev, buffer_read, tx_tail);
+		status = ether3_readlong(dev);
+
+		/*
+		 * Check to see if this packet has been transmitted
+		 */
+		if (!(status & TXSTAT_DONE) || !(status & TXHDR_TRANSMIT))
+			break;
+
+		/*
+		 * Update errors
+		 */
+		if (!(status & (TXSTAT_BABBLED | TXSTAT_16COLLISIONS)))
+			priv->stats.tx_packets++;
+		else {
+			priv->stats.tx_errors++;
+			if (status & TXSTAT_16COLLISIONS)
+				priv->stats.collisions += 16;
+			if (status & TXSTAT_BABBLED)
+				priv->stats.tx_fifo_errors++;
+		}
+
+		/*
+		 * Get next packet address
+		 */
+		tx_tail += 0x600;
+		if (tx_tail >= TX_END)
+			tx_tail = 0;
+
+		if (priv->tx_used)
+			priv->tx_used--;
+	} while (1);
+
+	if (priv->tx_tail != tx_tail) {
+		priv->tx_tail = tx_tail;
+		if (priv->tx_used <= MAX_TX_BUFFERED) {
+			dev->tbusy = 0;
+			mark_bh(NET_BH);	/* Inform upper layers. */
+		}
+	}
+	priv->regs.command &= ~CMD_ENINTTX;
+	outw(priv->regs.command, REG_COMMAND);
+}
+
+#ifdef MODULE
+
+char ethernames[MAX_ECARDS][9];
+
+static struct device *my_ethers[MAX_ECARDS];
+static struct expansion_card *ec[MAX_ECARDS];
+
+int init_module(void)
+{
+	int i;
+
+	for (i = 0; i < MAX_ECARDS; i++) {
+		my_ethers[i] = NULL;
+		ec[i] = NULL;
+		strcpy(ethernames[i], "        ");
+	}
+
+	i = 0;
+
+	ecard_startfind();
+
+	do {
+		if ((ec[i] = ecard_find(0, ether3_cids)) == NULL)
+			break;
+
+		my_ethers[i] = (struct device *) kmalloc(sizeof(struct device), GFP_KERNEL);
+		memset(my_ethers[i], 0, sizeof(struct device));
+
+		my_ethers[i]->irq = ec[i]->irq;
+		my_ethers[i]->base_addr = ecard_address(ec[i], ECARD_MEMC, 0);
+		my_ethers[i]->init = ether3_probe1;
+		my_ethers[i]->name = ethernames[i];
+
+		ether3_addr(my_ethers[i]->dev_addr, ec[i]);
+
+		ecard_claim(ec[i]);
+
+		if (register_netdev(my_ethers[i]) != 0) {
+			for (i = 0; i < 4; i++) {
+				if (my_ethers[i]) {
+					kfree(my_ethers[i]);
+					my_ethers[i] = NULL;
+				}
+				if (ec[i]) {
+					ecard_release(ec[i]);
+					ec[i] = NULL;
+				}
+			}
+			return -EIO;
+		}
+		i++;
+	}
+	while (i < MAX_ECARDS);
+
+	return i != 0 ? 0 : -ENODEV;
+}
+
+void cleanup_module(void)
+{
+	if (MOD_IN_USE) {
+		printk("ether3: device busy, remove delayed\n");
+	} else {
+		int i;
+		for (i = 0; i < MAX_ECARDS; i++) {
+			if (my_ethers[i]) {
+				release_region(my_ethers[i]->base_addr, 128);
+				unregister_netdev(my_ethers[i]);
+				my_ethers[i] = NULL;
+			}
+			if (ec[i]) {
+				ecard_release(ec[i]);
+				ec[i] = NULL;
+			}
+		}
+	}
+}
+#endif				/* MODULE */
diff -u --recursive --new-file v2.1.87/linux/drivers/acorn/net/ether3.h linux/drivers/acorn/net/ether3.h
--- v2.1.87/linux/drivers/acorn/net/ether3.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/acorn/net/ether3.h	Mon Feb 16 15:49:48 1998
@@ -0,0 +1,169 @@
+/*
+ * linux/drivers/net/ether3.h
+ *
+ * network driver for Acorn/ANT Ether3 cards
+ */
+
+#ifndef _LINUX_ether3_H
+#define _LINUX_ether3_H
+
+/* use 0 for production, 1 for verification, >2 for debug. debug flags: */
+#define DEBUG_TX	 2
+#define DEBUG_RX	 4
+#define DEBUG_INT	 8
+#define DEBUG_IC	16
+#ifndef NET_DEBUG
+#define NET_DEBUG 	0
+#endif
+
+/* Command register definitions & bits */
+#define REG_COMMAND		(dev->base_addr + 0x00)
+#define CMD_ENINTDMA		0x0001
+#define CMD_ENINTRX		0x0002
+#define CMD_ENINTTX		0x0004
+#define CMD_ENINTBUFWIN		0x0008
+#define CMD_ACKINTDMA		0x0010
+#define CMD_ACKINTRX		0x0020
+#define CMD_ACKINTTX		0x0040
+#define CMD_ACKINTBUFWIN	0x0080
+#define CMD_DMAON		0x0100
+#define CMD_RXON		0x0200
+#define CMD_TXON		0x0400
+#define CMD_DMAOFF		0x0800
+#define CMD_RXOFF		0x1000
+#define CMD_TXOFF		0x2000
+#define CMD_FIFOREAD		0x4000
+#define CMD_FIFOWRITE		0x8000
+
+/* status register */
+#define REG_STATUS		(dev->base_addr + 0x00)
+#define STAT_ENINTSTAT		0x0001
+#define STAT_ENINTRX		0x0002
+#define STAT_ENINTTX		0x0004
+#define STAT_ENINTBUFWIN	0x0008
+#define STAT_INTDMA		0x0010
+#define STAT_INTRX		0x0020
+#define STAT_INTTX		0x0040
+#define STAT_INTBUFWIN		0x0080
+#define STAT_DMAON		0x0100
+#define STAT_RXON		0x0200
+#define STAT_TXON		0x0400
+#define STAT_FIFOFULL		0x2000
+#define STAT_FIFOEMPTY		0x4000
+#define STAT_FIFODIR		0x8000
+
+/* configuration register 1 */
+#define REG_CONFIG1		(dev->base_addr + 0x10)
+#define CFG1_BUFSELSTAT0	0x0000
+#define CFG1_BUFSELSTAT1	0x0001
+#define CFG1_BUFSELSTAT2	0x0002
+#define CFG1_BUFSELSTAT3	0x0003
+#define CFG1_BUFSELSTAT4	0x0004
+#define CFG1_BUFSELSTAT5	0x0005
+#define CFG1_ADDRPROM		0x0006
+#define CFG1_TRANSEND		0x0007
+#define CFG1_LOCBUFMEM		0x0008
+#define CFG1_INTVECTOR		0x0009
+#define CFG1_DMABURSTCONT	0x0000
+#define CFG1_DMABURST800NS	0x0010
+#define CFG1_DMABURST1600NS	0x0020
+#define CFG1_DMABURST3200NS	0x0030
+#define CFG1_DMABURST1		0x0000
+#define CFG1_DMABURST4		0x0040
+#define CFG1_DMABURST8		0x0080
+#define CFG1_DMABURST16		0x00C0
+#define CFG1_RECVCOMPSTAT0	0x0100
+#define CFG1_RECVCOMPSTAT1	0x0200
+#define CFG1_RECVCOMPSTAT2	0x0400
+#define CFG1_RECVCOMPSTAT3	0x0800
+#define CFG1_RECVCOMPSTAT4	0x1000
+#define CFG1_RECVCOMPSTAT5	0x2000
+#define CFG1_RECVSPECONLY	0x0000
+#define CFG1_RECVSPECBROAD	0x4000
+#define CFG1_RECVSPECBRMULTI	0x8000
+#define CFG1_RECVPROMISC	0xC000
+
+/* configuration register 2 */
+#define REG_CONFIG2		(dev->base_addr + 0x20)
+#define CFG2_BYTESWAP		0x0001
+#define CFG2_ERRENCRC		0x0008
+#define CFG2_ERRENDRIBBLE	0x0010
+#define CFG2_ERRSHORTFRAME	0x0020
+#define CFG2_SLOTSELECT		0x0040
+#define CFG2_PREAMSELECT	0x0080
+#define CFG2_ADDRLENGTH		0x0100
+#define CFG2_RECVCRC		0x0200
+#define CFG2_XMITNOCRC		0x0400
+#define CFG2_LOOPBACK		0x0800
+#define CFG2_CTRLO		0x1000
+#define CFG2_RESET		0x8000
+
+#define REG_RECVEND		(dev->base_addr + 0x30)
+
+#define REG_BUFWIN		(dev->base_addr + 0x40)
+
+#define REG_RECVPTR		(dev->base_addr + 0x50)
+
+#define REG_TRANSMITPTR		(dev->base_addr + 0x60)
+
+#define REG_DMAADDR		(dev->base_addr + 0x70)
+
+/*
+ * Cards transmit/receive headers
+ */
+#define TX_NEXT			(0xffff)
+#define TXHDR_ENBABBLEINT	(1 << 16)
+#define TXHDR_ENCOLLISIONINT	(1 << 17)
+#define TXHDR_EN16COLLISION	(1 << 18)
+#define TXHDR_ENSUCCESS		(1 << 19)
+#define TXHDR_DATAFOLLOWS	(1 << 21)
+#define TXHDR_CHAINCONTINUE	(1 << 22)
+#define TXHDR_TRANSMIT		(1 << 23)
+#define TXSTAT_BABBLED		(1 << 24)
+#define TXSTAT_COLLISION	(1 << 25)
+#define TXSTAT_16COLLISIONS	(1 << 26)
+#define TXSTAT_DONE		(1 << 31)
+
+#define RX_NEXT			(0xffff)
+#define RXHDR_CHAINCONTINUE	(1 << 6)
+#define RXHDR_RECEIVE		(1 << 7)
+#define RXSTAT_OVERSIZE		(1 << 8)
+#define RXSTAT_CRCERROR		(1 << 9)
+#define RXSTAT_DRIBBLEERROR	(1 << 10)
+#define RXSTAT_SHORTPACKET	(1 << 11)
+#define RXSTAT_DONE		(1 << 15)
+
+
+#define TX_END		0x6000
+#define RX_START	0x6000
+#define RX_LEN		0xA000
+#define RX_END		0x10000
+/* must be a power of 2 and greater than MAX_TX_BUFFERED */
+#define MAX_TXED	16
+#define MAX_TX_BUFFERED	10
+
+struct dev_priv {
+    struct {
+	unsigned int command;
+	unsigned int config1;
+	unsigned int config2;
+    } regs;
+    unsigned int tx_head;		/* address to insert next packet	 */
+    unsigned int tx_tail;		/* address of transmitting packet	 */
+    unsigned int tx_used;		/* number of 'slots' used		 */
+    unsigned int rx_head;		/* address to fetch next packet from	 */
+    struct enet_statistics stats;
+    struct timer_list timer;
+    int broken;				/* 0 = ok, 1 = something went wrong	 */
+};
+
+extern int	ether3_probe (struct device *dev);
+static int	ether3_probe1 (struct device *dev);
+static int	ether3_open (struct device *dev);
+static int	ether3_sendpacket (struct sk_buff *skb, struct device *dev);
+static void	ether3_interrupt (int irq, void *dev_id, struct pt_regs *regs);
+static int	ether3_close (struct device *dev);
+static struct enet_statistics *ether3_getstats (struct device *dev);
+static void	ether3_setmulticastlist (struct device *dev);
+
+#endif
diff -u --recursive --new-file v2.1.87/linux/drivers/acorn/net/etherh.c linux/drivers/acorn/net/etherh.c
--- v2.1.87/linux/drivers/acorn/net/etherh.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/acorn/net/etherh.c	Mon Feb 16 15:49:48 1998
@@ -0,0 +1,538 @@
+/*
+ * linux/drivers/net/etherh.c
+ *
+ * NS8390 ANT etherh specific driver
+ *  For Acorn machines
+ *
+ * By Russell King.
+ *
+ * Changelog:
+ *  08-Dec-1996	RMK	1.00	Created
+ *		RMK	1.03	Added support for EtherLan500 cards
+ *  23-Nov-1997	RMK	1.04	Added media autodetection
+ *
+ * Insmod Module Parameters
+ * ------------------------
+ *   io=<io_base>
+ *   irq=<irqno>
+ *   xcvr=<0|1> 0 = 10bT, 1=10b2 (Lan600/600A only)
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/in.h>
+#include <linux/malloc.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <asm/ecard.h>
+#include <asm/delay.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+
+#include "8390.h"
+
+#define NET_DEBUG  0
+#define DEBUG_INIT 2
+
+static unsigned int net_debug = NET_DEBUG;
+static const card_ids etherh_cids[] = {
+	{ MANU_I3, PROD_I3_ETHERLAN500 },
+	{ MANU_I3, PROD_I3_ETHERLAN600 },
+	{ MANU_I3, PROD_I3_ETHERLAN600A },
+	{ 0xffff, 0xffff }
+};
+
+static char *version = "etherh [500/600/600A] ethernet driver (c) 1997 R.M.King v1.04\n";
+
+#define ETHERH500_DATAPORT	0x200	/* MEMC */
+#define ETHERH500_NS8390	0x000	/* MEMC */
+#define ETHERH500_CTRLPORT	0x200	/* IOC  */
+
+#define ETHERH600_DATAPORT	16	/* MEMC */
+#define ETHERH600_NS8390	0x200	/* MEMC */
+#define ETHERH600_CTRLPORT	0x080	/* MEMC */
+
+#define ETHERH_CP_IE		1
+#define ETHERH_CP_IF		2
+
+#define ETHERH_TX_START_PAGE	1
+#define ETHERH_STOP_PAGE	0x7f
+
+/* --------------------------------------------------------------------------- */
+
+/*
+ * Read the ethernet address string from the on board rom.
+ * This is an ascii string...
+ */
+static int
+etherh_addr (char *addr, struct expansion_card *ec)
+{
+	struct in_chunk_dir cd;
+	char *s;
+	
+	if (ecard_readchunk(&cd, ec, 0xf5, 0) && (s = strchr(cd.d.string, '('))) {
+		int i;
+		for (i = 0; i < 6; i++) {
+			addr[i] = simple_strtoul(s + 1, &s, 0x10);
+			if (*s != (i == 5? ')' : ':'))
+				break;
+		}
+		if (i == 6)
+			return 0;
+	}
+	return ENODEV;
+}
+
+/*
+ * Reset the 8390 (hard reset)
+ */
+static void
+etherh_reset (struct device *dev)
+{
+	unsigned int addr = dev->base_addr;
+	int crb;
+
+	if (dev->mem_end == PROD_I3_ETHERLAN600 || dev->mem_end == PROD_I3_ETHERLAN600A) {
+		crb = inb (addr + EN0_RCNTHI) & 0xf8;
+		outb (ei_status.interface_num ? crb | 1: crb, addr+EN0_RCNTHI);
+	}
+}
+
+/*
+ * Write a block of data out to the 8390
+ */
+static void
+etherh_block_output (struct device *dev, int count, const unsigned char *buf, int start_page)
+{
+	unsigned int addr, dma_addr;
+	unsigned long dma_start;
+	
+	if (ei_status.dmaing) {
+		printk ("%s: DMAing conflict in etherh_block_input: "
+			" DMAstat %d irqlock %d intr %d\n", dev->name,
+			ei_status.dmaing, ei_status.irqlock, dev->interrupt);
+		return;
+	}
+
+	ei_status.dmaing |= 1;
+
+	addr = dev->base_addr;
+	dma_addr = dev->mem_start;
+
+	count = (count + 1) & ~1;
+	outb (E8390_NODMA | E8390_PAGE0 | E8390_START, addr + E8390_CMD);
+
+	outb (0x42, addr + EN0_RCNTLO);
+	outb (0x00, addr + EN0_RCNTHI);
+	outb (0x42, addr + EN0_RSARLO);
+	outb (0x00, addr + EN0_RSARHI);
+	outb (E8390_RREAD | E8390_START, addr + E8390_CMD);
+
+	udelay (1);
+
+	outb (ENISR_RDC, addr + EN0_ISR);
+	outb (count, addr + EN0_RCNTLO);
+	outb (count >> 8, addr + EN0_RCNTHI);
+	outb (0, addr + EN0_RSARLO);
+	outb (start_page, addr + EN0_RSARHI);
+	outb (E8390_RWRITE | E8390_START, addr + E8390_CMD);
+
+	if (ei_status.word16)
+		outsw (dma_addr, buf, count >> 1);
+#ifdef BIT8
+	else
+		outsb (dma_addr, buf, count);
+#endif
+
+	dma_start = jiffies;
+
+	while ((inb (addr + EN0_ISR) & ENISR_RDC) == 0)
+		if (jiffies - dma_start > 2*HZ/100) { /* 20ms */
+			printk ("%s: timeout waiting for TX RDC\n", dev->name);
+			etherh_reset (dev);
+			NS8390_init (dev, 1);
+			break;
+		}
+
+	outb (ENISR_RDC, addr + EN0_ISR);
+	ei_status.dmaing &= ~1;
+}
+
+/*
+ * Read a block of data from the 8390
+ */
+static void
+etherh_block_input (struct device *dev, int count, struct sk_buff *skb, int ring_offset)
+{
+	unsigned int addr, dma_addr;
+	unsigned char *buf;
+
+	if (ei_status.dmaing) {
+		printk ("%s: DMAing conflict in etherh_block_input: "
+			" DMAstat %d irqlock %d intr %d\n", dev->name,
+			ei_status.dmaing, ei_status.irqlock, dev->interrupt);
+		return;
+	}
+
+	ei_status.dmaing |= 1;
+
+	addr = dev->base_addr;
+	dma_addr = dev->mem_start;
+
+	buf = skb->data;
+	outb (E8390_NODMA | E8390_PAGE0 | E8390_START, addr + E8390_CMD);
+	outb (count, addr + EN0_RCNTLO);
+	outb (count >> 8, addr + EN0_RCNTHI);
+	outb (ring_offset, addr + EN0_RSARLO);
+	outb (ring_offset >> 8, addr + EN0_RSARHI);
+	outb (E8390_RREAD | E8390_START, addr + E8390_CMD);
+
+	if (ei_status.word16) {
+		insw (dma_addr, buf, count >> 1);
+		if (count & 1)
+			buf[count - 1] = inb (dma_addr);
+	}
+#ifdef BIT8
+	else
+		insb (dma_addr, buf, count);
+#endif
+
+	outb (ENISR_RDC, addr + EN0_ISR);
+	ei_status.dmaing &= ~1;
+}
+
+/*
+ * Read a header from the 8390
+ */
+static void
+etherh_get_header (struct device *dev, struct e8390_pkt_hdr *hdr, int ring_page)
+{
+	unsigned int addr, dma_addr;
+
+	if (ei_status.dmaing) {
+		printk ("%s: DMAing conflict in etherh_get_header: "
+			" DMAstat %d irqlock %d intr %d\n", dev->name,
+			ei_status.dmaing, ei_status.irqlock, dev->interrupt);
+		return;
+	}
+
+	ei_status.dmaing |= 1;
+
+	addr = dev->base_addr;
+	dma_addr = dev->mem_start;
+
+	outb (E8390_NODMA | E8390_PAGE0 | E8390_START, addr + E8390_CMD);
+	outb (sizeof (*hdr), addr + EN0_RCNTLO);
+	outb (0, addr + EN0_RCNTHI);
+	outb (0, addr + EN0_RSARLO);
+	outb (ring_page, addr + EN0_RSARHI);
+	outb (E8390_RREAD | E8390_START, addr + E8390_CMD);
+
+	if (ei_status.word16)
+		insw (dma_addr, hdr, sizeof (*hdr) >> 1);
+#ifdef BIT8
+	else
+		insb (dma_addr, hdr, sizeof (*hdr));
+#endif
+
+	outb (ENISR_RDC, addr + EN0_ISR);
+	ei_status.dmaing &= ~1;
+}
+
+/*
+ * Open/initialize the board.  This is called (in the current kernel)
+ * sometime after booting when the 'ifconfig' program is run.
+ *
+ * This routine should set everything up anew at each open, even
+ * registers that "should" only need to be set once at boot, so that
+ * there is non-reboot way to recover if something goes wrong.
+ */
+static int
+etherh_open(struct device *dev)
+{
+	unsigned int addr = dev->base_addr;
+	int crb;
+
+	MOD_INC_USE_COUNT;
+
+	if (request_irq(dev->irq, ei_interrupt, 0, "etherh", dev)) {
+		MOD_DEC_USE_COUNT;
+		return -EAGAIN;
+	}
+
+	if (dev->mem_end == PROD_I3_ETHERLAN600 || dev->mem_end == PROD_I3_ETHERLAN600A) {
+		crb = inb (addr + EN0_RCNTHI) & 0xf8;
+		outb (ei_status.interface_num ? crb | 1: crb, addr+EN0_RCNTHI);
+	}
+
+	ei_open (dev);
+	return 0;
+}
+
+/*
+ * The inverse routine to etherh_open().
+ */
+static int
+etherh_close(struct device *dev)
+{
+	ei_close (dev);
+	free_irq (dev->irq, dev);
+
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+/*
+ * This is the real probe routine.
+ */
+static int
+etherh_probe1(struct device *dev)
+{
+	static int version_printed;
+	unsigned int addr, i, reg0, tmp;
+	char *dev_type;
+
+	addr = dev->base_addr;
+
+	if (net_debug && version_printed++ == 0)
+		printk(version);
+
+	switch (dev->mem_end) {
+	case PROD_I3_ETHERLAN500:
+		dev_type = "500 ";
+		break;
+	case PROD_I3_ETHERLAN600:
+		dev_type = "600 ";
+		break;
+	case PROD_I3_ETHERLAN600A:
+		dev_type = "600A ";
+		break;
+	default:
+		dev_type = "";
+	}
+
+	reg0 = inb (addr);
+	if (reg0 == 0xff) {
+		if (net_debug & DEBUG_INIT)
+			printk ("%s: etherh error: NS8390 command register wrong\n", dev->name);
+		return -ENODEV;
+	}
+
+ 	outb (E8390_NODMA | E8390_PAGE1 | E8390_STOP, addr + E8390_CMD);
+	tmp = inb (addr + 13);
+	outb (0xff, addr + 13);
+	outb (E8390_NODMA | E8390_PAGE0, addr + E8390_CMD);
+	inb (addr + EN0_COUNTER0);
+	if (inb (addr + EN0_COUNTER0) != 0) {
+		if (net_debug & DEBUG_INIT)
+			printk ("%s: etherh error: NS8390 not found\n", dev->name);
+		outb (reg0, addr);
+		outb (tmp, addr + 13);
+		return -ENODEV;
+	}
+
+	if (ethdev_init (dev))
+		return -ENOMEM;
+
+	request_region (addr, 16, "etherh");
+
+	printk("%s: etherh %sfound at %lx, IRQ%d, ether address ",
+		dev->name, dev_type, dev->base_addr, dev->irq);
+
+	for (i = 0; i < 6; i++)
+		printk (i == 5 ? "%2.2x\n" : "%2.2x:", dev->dev_addr[i]);
+
+	ei_status.name = "etherh";
+	ei_status.word16 = 1;
+	ei_status.interface_num = dev->if_port & 1;
+	ei_status.tx_start_page = ETHERH_TX_START_PAGE;
+	ei_status.rx_start_page = ei_status.tx_start_page + TX_PAGES;
+	ei_status.stop_page = ETHERH_STOP_PAGE;
+	ei_status.reset_8390 = etherh_reset;
+	ei_status.block_input = etherh_block_input;
+	ei_status.block_output = etherh_block_output;
+	ei_status.get_8390_hdr = etherh_get_header;
+	dev->open = etherh_open;
+	dev->stop = etherh_close;
+
+	NS8390_init (dev, 0);
+	return 0;
+}
+
+static void etherh_irq_enable (ecard_t *ec, int irqnr)
+{
+	unsigned int ctrl_addr = (unsigned int)ec->irq_data;
+	outb (inb (ctrl_addr) | ETHERH_CP_IE, ctrl_addr);
+}
+
+static void etherh_irq_disable (ecard_t *ec, int irqnr)
+{
+	unsigned int ctrl_addr = (unsigned int)ec->irq_data;
+	outb (inb (ctrl_addr) & ~ETHERH_CP_IE, ctrl_addr);
+}
+
+static expansioncard_ops_t etherh_ops = {
+	etherh_irq_enable,
+	etherh_irq_disable,
+	NULL,
+	NULL
+};
+
+static void etherh_initdev (ecard_t *ec, struct device *dev)
+{
+	ecard_claim (ec);
+	
+	dev->irq = ec->irq;
+	dev->mem_end = ec->cld.product;
+
+	switch (ec->cld.product) {
+	case PROD_I3_ETHERLAN500:
+		dev->base_addr = ecard_address (ec, ECARD_MEMC, 0) + ETHERH500_NS8390;
+		dev->mem_start = dev->base_addr + ETHERH500_DATAPORT;
+		ec->irq_data   = (void *)ecard_address (ec, ECARD_IOC, ECARD_FAST)
+				  + ETHERH500_CTRLPORT;
+		break;
+
+	case PROD_I3_ETHERLAN600:
+	case PROD_I3_ETHERLAN600A:
+		dev->base_addr = ecard_address (ec, ECARD_MEMC, 0) + ETHERH600_NS8390;
+		dev->mem_start = dev->base_addr + ETHERH600_DATAPORT;
+		ec->irq_data = (void *)(dev->base_addr + ETHERH600_CTRLPORT);
+		break;
+
+	default:
+		printk ("%s: etherh error: unknown card type\n", dev->name);
+	}
+	ec->ops = &etherh_ops;
+
+	etherh_addr (dev->dev_addr, ec);
+}
+
+#ifndef MODULE
+int
+etherh_probe(struct device *dev)
+{
+	if (!dev)
+		return ENODEV;
+
+	ecard_startfind ();
+
+	if (!dev->base_addr) {
+		struct expansion_card *ec;
+
+		if ((ec = ecard_find (0, etherh_cids)) == NULL)
+			return ENODEV;
+
+		etherh_initdev (ec, dev);
+	}
+	return etherh_probe1 (dev);
+}
+#endif
+
+#ifdef MODULE
+#define MAX_ETHERH_CARDS 2
+
+static int io[MAX_ETHERH_CARDS];
+static int irq[MAX_ETHERH_CARDS];
+static int xcvr[MAX_ETHERH_CARDS] = { 1, 1 };
+static char ethernames[MAX_ETHERH_CARDS][9];
+static struct device *my_ethers[MAX_ETHERH_CARDS];
+static struct expansion_card *ec[MAX_ETHERH_CARDS];
+
+int
+init_module(void)
+{
+	struct device *dev = NULL;
+	struct expansion_card *boguscards[MAX_ETHERH_CARDS];
+	int i, found = 0;
+
+	for (i = 0; i < MAX_ETHERH_CARDS; i++) {
+		my_ethers[i] = NULL;
+		boguscards[i] = NULL;
+		ec[i] = NULL;
+		strcpy (ethernames[i], "        ");
+	}
+
+	ecard_startfind();
+
+	for (i = 0; i < MAX_ETHERH_CARDS; i++) {
+		if (!dev)
+			dev = (struct device *)kmalloc (sizeof (struct device), GFP_KERNEL);
+		if (dev)
+			memset (dev, 0, sizeof (struct device));
+
+		if (!io[i]) {
+			if ((ec[i] = ecard_find (0, etherh_cids)) == NULL)
+				continue;
+			if (!dev)
+				return -ENOMEM;
+
+			etherh_initdev (ec[i], dev);
+		} else {
+			ec[i] = NULL;
+			if (!dev)
+				return -ENOMEM;
+			dev->base_addr = io[i];
+			dev->irq = irq[i];
+		}
+
+		dev->init = etherh_probe1;
+		dev->name = ethernames[i];
+		dev->if_port = xcvr[i];
+
+		my_ethers[i] = dev;
+
+		if (register_netdev (my_ethers[i]) != 0) {
+			printk (KERN_WARNING "No etherh card found at %08lX\n", dev->base_addr);
+			if (ec[i]) {
+				boguscards[i] = ec[i];
+				ec[i] = NULL;
+			}
+			continue;
+		}
+		found ++;
+		dev = NULL;
+	}
+	if (dev)
+		kfree (dev);
+	for (i = 0; i < MAX_ETHERH_CARDS; i++)
+		if (boguscards[i]) {
+			boguscards[i]->ops = NULL;
+			ecard_release (boguscards[i]);
+		}
+	if (!found)
+		return -ENODEV;
+	return 0;
+}
+
+void
+cleanup_module(void)
+{
+	int i;
+	for (i = 0; i < MAX_ETHERH_CARDS; i++) {
+		if (my_ethers[i]) {
+			unregister_netdev(my_ethers[i]);
+			release_region (my_ethers[i]->base_addr, 16);
+			kfree (my_ethers[i]);
+			my_ethers[i] = NULL;
+		}
+		if (ec[i]) {
+			ec[i]->ops = NULL;
+			ecard_release(ec[i]);
+			ec[i] = NULL;
+		}
+	}
+}
+#endif /* MODULE */
diff -u --recursive --new-file v2.1.87/linux/drivers/acorn/net/net-probe.c linux/drivers/acorn/net/net-probe.c
--- v2.1.87/linux/drivers/acorn/net/net-probe.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/acorn/net/net-probe.c	Mon Feb 16 15:49:48 1998
@@ -0,0 +1,31 @@
+/*
+ * Acorn specific net device driver probe routine
+ *
+ * Copyright (C) 1998 Russell King
+ */
+#include <linux/config.h>
+#include <linux/netdevice.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+
+extern int ether1_probe (struct device *dev);
+extern int ether3_probe (struct device *dev);
+extern int etherh_probe (struct device *dev);
+
+__initfunc(int acorn_ethif_probe(struct device *dev))
+{
+	if (1
+#ifdef CONFIG_ARM_ETHERH
+        && etherh_probe (dev)
+#endif
+#ifdef CONFIG_ARM_ETHER3
+        && ether3_probe (dev)
+#endif
+#ifdef CONFIG_ARM_ETHER1
+        && ether1_probe (dev)
+#endif
+	&& 1) {
+		return 1;
+	}
+	return 0;
+}
diff -u --recursive --new-file v2.1.87/linux/drivers/acorn/scsi/Config.in linux/drivers/acorn/scsi/Config.in
--- v2.1.87/linux/drivers/acorn/scsi/Config.in	Wed Dec 31 16:00:00 1969
+++ linux/drivers/acorn/scsi/Config.in	Tue Feb  3 12:57:43 1998
@@ -0,0 +1,21 @@
+#
+# SCSI driver configuration for Acorn
+#
+dep_tristate 'Acorn SCSI card (aka30) support' CONFIG_SCSI_ACORNSCSI_3 $CONFIG_SCSI
+if [ "$CONFIG_SCSI_ACORNSCSI_3" != "n" ]; then
+  bool '  Support SCSI 2 Tagged queueing' CONFIG_SCSI_ACORNSCSI_TAGGED_QUEUE
+  bool '  Support SCSI 2 Synchronous Transfers' CONFIG_SCSI_ACORNSCSI_SYNC
+fi
+if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+  dep_tristate 'CumanaSCSI II support (Experimental)' CONFIG_SCSI_CUMANA_2 $CONFIG_SCSI
+  dep_tristate 'PowerTec support (Experimental)' CONFIG_SCSI_POWERTECSCSI $CONFIG_SCSI
+
+  comment 'The following drives are not fully supported'
+
+  dep_tristate 'CumanaSCSI I support' CONFIG_SCSI_CUMANA_1 $CONFIG_SCSI
+  if [ "$CONFIG_ARCH_ARC" = "y" -o "$CONFIG_ARCH_A5K" = "y" ]; then
+    dep_tristate 'EcoScsi support' CONFIG_SCSI_ECOSCSI $CONFIG_SCSI
+  fi
+  dep_tristate 'Oak SCSI support' CONFIG_SCSI_OAK1 $CONFIG_SCSI
+fi
+
diff -u --recursive --new-file v2.1.87/linux/drivers/acorn/scsi/Makefile linux/drivers/acorn/scsi/Makefile
--- v2.1.87/linux/drivers/acorn/scsi/Makefile	Wed Dec 31 16:00:00 1969
+++ linux/drivers/acorn/scsi/Makefile	Sat Feb  7 07:18:17 1998
@@ -0,0 +1,103 @@
+#
+# Makefile for drivers/acorn/scsi
+#
+
+L_TARGET := acorn-scsi.a
+L_OBJS   :=
+LX_OBJS  :=
+M_OBJS   :=
+MX_OBJS  :=
+MOD_LIST_NAME := ACORN_SCSI_MODULES
+
+CONFIG_QUEUE_BUILTIN :=
+CONFIG_FAS216_BUILTIN :=
+CONFIG_QUEUE_MODULE :=
+CONFIG_FAS216_MODULE :=
+
+ifeq ($(CONFIG_SCSI_ACORNSCSI_3),y)
+  L_OBJS += acornscsi.o acornscsi-io.o
+  CONFIG_QUEUE_BUILTIN=y
+else
+  ifeq ($(CONFIG_SCSI_ACORNSCSI_3),m)
+    M_OBJS += acornscsi_mod.o
+    CONFIG_QUEUE_MODULE=y
+  endif
+endif
+
+ifeq ($(CONFIG_SCSI_CUMANA_1),y)
+  L_OBJS += cumana_1.o
+else
+  ifeq ($(CONFIG_SCSI_CUMANA_1),m)
+    M_OBJS += cumana_1.o
+  endif
+endif
+
+ifeq ($(CONFIG_SCSI_CUMANA_2),y)
+  L_OBJS += cumana_2.o
+  CONFIG_QUEUE_BUILTIN=y
+  CONFIG_FAS216_BUILTIN=y
+else
+  ifeq ($(CONFIG_SCSI_CUMANA_2),m)
+    M_OBJS += cumana_2.o
+    CONFIG_QUEUE_MODULE=y
+    CONFIG_FAS216_MODULE=y
+  endif
+endif
+
+ifeq ($(CONFIG_SCSI_ECOSCSI),y)
+  L_OBJS += ecoscsi.o
+else
+  ifeq ($(CONFIG_SCSI_ECOSCSI),m)
+    M_OBJS += ecoscsi.o
+  endif
+endif
+
+ifeq ($(CONFIG_SCSI_OAK1),y)
+  L_OBJS += oak.o
+else
+  ifeq ($(CONFIG_SCSI_OAK1),m)
+    M_OBJS += oak.o
+  endif
+endif
+
+ifeq ($(CONFIG_SCSI_POWERTECSCSI),y)
+  L_OBJS += powertec.o
+  CONFIG_QUEUE_BUILTIN=y
+  CONFIG_FAS216_BUILTIN=y
+else
+  ifeq ($(CONFIG_SCSI_POWERTECSCSI),m)
+    M_OBJS += powertec.o
+    CONFIG_QUEUE_MODULE=y
+    CONFIG_FAS216_MODULE=y
+  endif
+endif
+
+ifeq ($(CONFIG_QUEUE_BUILTIN),y)
+  LX_OBJS += queue.o msgqueue.o
+else
+  ifeq ($(CONFIG_QUEUE_MODULE),y)
+    MX_OBJS += queue.o msgqueue.o
+  endif
+endif
+
+ifeq ($(CONFIG_FAS216_BUILTIN),y)
+  LX_OBJS += fas216.o
+else
+  ifeq ($(CONFIG_FAS216_MODULE),y)
+    MX_OBJS += fas216.o
+  endif
+endif
+
+include $(TOPDIR)/Rules.make
+
+acornscsi_mod.o: acornscsi.o acornscsi-io.o
+	$(LD) $(LD_RFLAG) -r -o $@ acornscsi.o acornscsi-io.o
+
+%.o: %.S
+ifndef $(CONFIG_BINUTILS_NEW)
+	$(CC) $(CFLAGS) -D__ASSEMBLY__ -E $< | tr ';$$' '\n#' > ..tmp.$<.s
+	$(CC) $(CFLAGS:-pipe=) -c -o $@ ..tmp.$<.s
+	$(RM) ..tmp.$<.s
+else
+	$(CC) $(CFLAGS) -D__ASSEMBLY__ -c -o $@ $<
+endif
diff -u --recursive --new-file v2.1.87/linux/drivers/acorn/scsi/acornscsi-io.S linux/drivers/acorn/scsi/acornscsi-io.S
--- v2.1.87/linux/drivers/acorn/scsi/acornscsi-io.S	Wed Dec 31 16:00:00 1969
+++ linux/drivers/acorn/scsi/acornscsi-io.S	Sun Jan 11 16:45:53 1998
@@ -0,0 +1,139 @@
+@  linux/arch/arm/drivers/scsi/acornscsi-io.S: Acorn SCSI card IO
+#include <linux/linkage.h>
+
+#include <asm/assembler.h>
+#include <asm/hardware.h>
+
+#if (IO_BASE == (PCIO_BASE & 0xff000000))
+#define ADDR(off,reg)						\
+		tst	off, $0x80000000			;\
+		mov	reg, $IO_BASE				;\
+		orreq	reg, reg, $(PCIO_BASE & 0x00ff0000)
+#else
+#define ADDR(off,reg)						\
+		tst	off, $0x80000000			;\
+		movne	reg, $IO_BASE				;\
+		moveq	reg, $(PCIO_BASE & 0xff000000)		;\
+		orreq	reg, reg, $(PCIO_BASE & 0x00ff0000)
+#endif
+
+@ Purpose: transfer a block of data from the acorn scsi card to memory
+@ Proto  : void acornscsi_in(unsigned int addr_start, char *buffer, int length)
+@ Returns: nothing
+
+		.align
+ENTRY(__acornscsi_in)
+		stmfd	sp!, {r4 - r7, lr}
+		bic	r0, r0, #3
+		mov	lr, #0xff
+		orr	lr, lr, #0xff00
+acornscsi_in16lp:
+		subs	r2, r2, #16
+		bmi	acornscsi_in8
+		ldmia	r0!, {r3, r4, r5, r6}
+		and	r3, r3, lr
+		orr	r3, r3, r4, lsl #16
+		and 	r4, r5, lr
+		orr	r4, r4, r6, lsl #16
+		ldmia	r0!, {r5, r6, r7, ip}
+		and	r5, r5, lr
+		orr	r5, r5, r6, lsl #16
+		and	r6, r7, lr
+		orr	r6, r6, ip, lsl #16
+		stmia	r1!, {r3 - r6}
+		bne	acornscsi_in16lp
+		LOADREGS(fd, sp!, {r4 - r7, pc})
+
+acornscsi_in8:	adds	r2, r2, #8
+		bmi	acornscsi_in4
+		ldmia	r0!, {r3, r4, r5, r6}
+		and	r3, r3, lr
+		orr	r3, r3, r4, lsl #16
+		and	r4, r5, lr
+		orr	r4, r4, r6, lsl #16
+		stmia	r1!, {r3 - r4}
+		LOADREGS(eqfd, sp!, {r4 - r7, pc})
+		sub	r2, r2, #8
+
+acornscsi_in4:	adds	r2, r2, #4
+		bmi	acornscsi_in2
+		ldmia	r0!, {r3, r4}
+		and	r3, r3, lr
+		orr	r3, r3, r4, lsl #16
+		str	r3, [r1], #4
+		LOADREGS(eqfd, sp!, {r4 - r7, pc})
+		sub	r2, r2, #4
+
+acornscsi_in2:	adds	r2, r2, #2
+		ldr	r3, [r0], #4
+		and	r3, r3, lr
+		strb	r3, [r1], #1
+		mov	r3, r3, lsr #8
+		strplb	r3, [r1], #1
+		LOADREGS(fd, sp!, {r4 - r7, pc})
+
+@ Purpose: transfer a block of data from memory to the acorn scsi card
+@ Proto  : void acornscsi_in(unsigned int addr_start, char *buffer, int length)
+@ Returns: nothing
+
+ENTRY(__acornscsi_out)
+		stmfd	sp!, {r4 - r6, lr}
+		bic	r0, r0, #3
+acornscsi_out16lp:
+		subs	r2, r2, #16
+		bmi	acornscsi_out8
+		ldmia	r1!, {r4, r6, ip, lr}
+		mov	r3, r4, lsl #16
+		orr	r3, r3, r3, lsr #16
+		mov	r4, r4, lsr #16
+		orr	r4, r4, r4, lsl #16
+		mov	r5, r6, lsl #16
+		orr	r5, r5, r5, lsr #16
+		mov	r6, r6, lsr #16
+		orr	r6, r6, r6, lsl #16
+		stmia	r0!, {r3, r4, r5, r6}
+		mov	r3, ip, lsl #16
+		orr	r3, r3, r3, lsr #16
+		mov	r4, ip, lsr #16
+		orr	r4, r4, r4, lsl #16
+		mov	ip, lr, lsl #16
+		orr	ip, ip, ip, lsr #16
+		mov	lr, lr, lsr #16
+		orr	lr, lr, lr, lsl #16
+		stmia	r0!, {r3, r4, ip, lr}
+		bne	acornscsi_out16lp
+		LOADREGS(fd, sp!, {r4 - r6, pc})
+
+acornscsi_out8:	adds	r2, r2, #8
+		bmi	acornscsi_out4
+		ldmia	r1!, {r4, r6}
+		mov	r3, r4, lsl #16
+		orr	r3, r3, r3, lsr #16
+		mov	r4, r4, lsr #16
+		orr	r4, r4, r4, lsl #16
+		mov	r5, r6, lsl #16
+		orr	r5, r5, r5, lsr #16
+		mov	r6, r6, lsr #16
+		orr	r6, r6, r6, lsl #16
+		stmia	r0!, {r3, r4, r5, r6}
+		LOADREGS(eqfd, sp!, {r4 - r6, pc})
+
+		sub	r2, r2, #8
+acornscsi_out4:	adds	r2, r2, #4
+		bmi	acornscsi_out2
+		ldr	r4, [r1], #4
+		mov	r3, r4, lsl #16
+		orr	r3, r3, r3, lsr #16
+		mov	r4, r4, lsr #16
+		orr	r4, r4, r4, lsl #16
+		stmia	r0!, {r3, r4}
+		LOADREGS(eqfd, sp!, {r4 - r6, pc})
+
+		sub	r2, r2, #4
+acornscsi_out2:	adds	r2, r2, #2
+		ldr	r3, [r1], #2
+		strb	r3, [r0], #1
+		mov	r3, r3, lsr #8
+		strplb	r3, [r0], #1
+		LOADREGS(fd, sp!, {r4 - r6, pc})
+
diff -u --recursive --new-file v2.1.87/linux/drivers/acorn/scsi/acornscsi.c linux/drivers/acorn/scsi/acornscsi.c
--- v2.1.87/linux/drivers/acorn/scsi/acornscsi.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/acorn/scsi/acornscsi.c	Fri Feb  6 15:55:44 1998
@@ -0,0 +1,2870 @@
+/*
+ * linux/arch/arm/drivers/scsi/acornscsi.c
+ *
+ *  Acorn SCSI 3 driver
+ *  By R.M.King.
+ *
+ * Abandoned using the Select and Transfer command since there were
+ * some nasty races between our software and the target devices that
+ * were not easy to solve, and the device errata had a lot of entries
+ * for this command, some of them quite nasty...
+ *
+ * Changelog:
+ *  26-Sep-1997	RMK	Re-jigged to use the queue module.
+ *			Re-coded state machine to be based on driver
+ *			state not scsi state.  Should be easier to debug.
+ *			Added acornscsi_release to clean up properly.
+ *			Updated proc/scsi reporting.
+ *  05-Oct-1997	RMK	Implemented writing to SCSI devices.
+ *  06-Oct-1997	RMK	Corrected small (non-serious) bug with the connect/
+ *			reconnect race condition causing a warning message.
+ *  12-Oct-1997	RMK	Added catch for re-entering interrupt routine.
+ *  15-Oct-1997	RMK	Improved handling of commands.
+ */
+#define DEBUG_NO_WRITE	1
+#define DEBUG_QUEUES	2
+#define DEBUG_DMA	4
+#define DEBUG_ABORT	8
+#define DEBUG_DISCON	16
+#define DEBUG_CONNECT	32
+#define DEBUG_PHASES	64
+#define DEBUG_WRITE	128
+#define DEBUG_LINK	256
+#define DEBUG_MESSAGES	512
+#define DEBUG_RESET	1024
+#define DEBUG_ALL	(DEBUG_RESET|DEBUG_MESSAGES|DEBUG_LINK|DEBUG_WRITE|\
+			 DEBUG_PHASES|DEBUG_CONNECT|DEBUG_DISCON|DEBUG_ABORT|\
+			 DEBUG_DMA|DEBUG_QUEUES|DEBUG_NO_WRITE)
+
+/* DRIVER CONFIGURATION
+ *
+ * SCSI-II Tagged queue support.
+ *
+ * I don't have any SCSI devices that support it, so it is totally untested
+ * (except to make sure that it doesn't interfere with any non-tagging
+ * devices).  It is not fully implemented either - what happens when a
+ * tagging device reconnects???
+ *
+ * You can tell if you have a device that supports tagged queueing my
+ * cating (eg) /proc/scsi/acornscsi/0 and see if the SCSI revision is reported
+ * as '2 TAG'.
+ *
+ * Also note that CONFIG_SCSI_ACORNSCSI_TAGGED_QUEUE is normally set in the config
+ * scripts, but disabled here.  Once debugged, remove the #undef, otherwise to debug,
+ * comment out the undef.
+ */
+#undef CONFIG_SCSI_ACORNSCSI_TAGGED_QUEUE
+/*
+ * SCSI-II Linked command support.
+ *
+ * The higher level code doesn't support linked commands yet, and so the option
+ * is undef'd here.
+ */
+#undef CONFIG_SCSI_ACORNSCSI_LINK
+/*
+ * SCSI-II Synchronous transfer support.
+ *
+ * Tried and tested...
+ *
+ * SDTR_SIZE	  - maximum number of un-acknowledged bytes (0 = off, 12 = max)
+ * SDTR_PERIOD	  - period of REQ signal (min=125, max=1020)
+ * DEFAULT_PERIOD - default REQ period.
+ */
+#define SDTR_SIZE	12
+#define SDTR_PERIOD	125
+#define DEFAULT_PERIOD	500
+
+/*
+ * Debugging information
+ *
+ * DEBUG	  - bit mask from list above
+ * DEBUG_TARGET   - is defined to the target number if you want to debug
+ *		    a specific target. [only recon/write/dma].
+ */
+#define DEBUG (DEBUG_RESET|DEBUG_WRITE|DEBUG_NO_WRITE)
+/* only allow writing to SCSI device 0 */
+#define NO_WRITE 0xFE
+/*#define DEBUG_TARGET 2*/
+/*
+ * Select timeout time (in 10ms units)
+ *
+ * This is the timeout used between the start of selection and the WD33C93
+ * chip deciding that the device isn't responding.
+ */
+#define TIMEOUT_TIME 10
+/*
+ * Define this if you want to have verbose explaination of SCSI
+ * status/messages.
+ */
+#undef CONFIG_ACORNSCSI_CONSTANTS
+/*
+ * Define this if you want to use the on board DMAC [don't remove this option]
+ * If not set, then use PIO mode (not currently supported).
+ */
+#define USE_DMAC
+/*
+ * List of devices that the driver will recognise
+ */
+#define ACORNSCSI_LIST { MANU_ACORN, PROD_ACORN_SCSI }
+/*
+ * ====================================================================================
+ */
+
+#ifdef DEBUG_TARGET
+#define DBG(cmd,xxx...) \
+  if (cmd->target == DEBUG_TARGET) { \
+    xxx; \
+  }
+#else
+#define DBG(cmd,xxx...) xxx
+#endif
+
+#ifndef STRINGIFY
+#define STRINGIFY(x) #x
+#endif
+#define STR(x) STRINGIFY(x)
+#define NO_WRITE_STR STR(NO_WRITE)
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/signal.h>
+#include <linux/errno.h>
+#include <linux/proc_fs.h>
+#include <linux/stat.h>
+#include <linux/ioport.h>
+#include <linux/blk.h>
+#include <asm/bitops.h>
+#include <asm/delay.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/ecard.h>
+
+#include "../../scsi/scsi.h"
+#include "../../scsi/hosts.h"
+#include "../../scsi/constants.h"
+#include "acornscsi.h"
+
+#define VER_MAJOR 2
+#define VER_MINOR 0
+#define VER_PATCH 5
+
+#ifndef ABORT_TAG
+#define ABORT_TAG 0xd
+#else
+#error "Yippee!  ABORT TAG is now defined!  Remove this error!"
+#endif
+
+#ifndef NO_IRQ
+#define NO_IRQ 255
+#endif
+
+#ifdef CONFIG_SCSI_ACORNSCSI_LINK
+#error SCSI2 LINKed commands not supported (yet)!
+#endif
+
+#ifdef USE_DMAC
+/*
+ * DMAC setup parameters
+ */ 
+#define INIT_DEVCON0	(DEVCON0_RQL|DEVCON0_EXW|DEVCON0_CMP)
+#define INIT_DEVCON1	(DEVCON1_BHLD)
+#define DMAC_READ	(MODECON_READ)
+#define DMAC_WRITE	(MODECON_WRITE)
+#define INIT_SBICDMA	(CTRL_DMABURST)
+
+/*
+ * Size of on-board DMA buffer
+ */
+#define DMAC_BUFFER_SIZE	65536
+#endif
+
+/*
+ * This is used to dump the previous states of the SBIC
+ */
+static struct status_entry {
+	unsigned long	when;
+	unsigned char	ssr;
+	unsigned char	ph;
+	unsigned char	irq;
+	unsigned char	unused;
+} status[9][16];
+static unsigned char status_ptr[9];
+
+#define ADD_STATUS(_q,_ssr,_ph,_irq) \
+({							\
+	status[(_q)][status_ptr[(_q)]].when = jiffies;	\
+	status[(_q)][status_ptr[(_q)]].ssr  = (_ssr);	\
+	status[(_q)][status_ptr[(_q)]].ph   = (_ph);	\
+	status[(_q)][status_ptr[(_q)]].irq  = (_irq);	\
+	status_ptr[(_q)] = (status_ptr[(_q)] + 1) & 15;	\
+})
+
+unsigned int sdtr_period = SDTR_PERIOD;
+unsigned int sdtr_size   = SDTR_SIZE;
+
+static struct proc_dir_entry proc_scsi_acornscsi = {
+	PROC_SCSI_EATA, 9, "acornscsi", S_IFDIR | S_IRUGO | S_IXUGO, 2
+};
+
+static void acornscsi_done (AS_Host *host, Scsi_Cmnd **SCpntp, unsigned int result);
+static int acornscsi_reconnect_finish (AS_Host *host);
+static void acornscsi_dma_cleanup (AS_Host *host);
+static void acornscsi_abortcmd (AS_Host *host, unsigned char tag);
+
+/* ====================================================================================
+ * Miscellaneous
+ */
+
+static inline void
+sbic_arm_write (unsigned int io_port, int reg, int value)
+{
+    outb_t (reg, io_port);
+    outb_t (value, io_port + 4);
+}
+
+#define sbic_arm_writenext(io,val) \
+	outb_t ((val), (io) + 4)
+
+static inline
+int sbic_arm_read (unsigned int io_port, int reg)
+{
+    if(reg == ASR)
+	   return inl_t(io_port) & 255;
+    outb_t (reg, io_port);
+    return inl_t(io_port + 4) & 255;
+}
+
+#define sbic_arm_readnext(io) \
+	inb_t((io) + 4)
+
+#ifdef USE_DMAC
+#define dmac_read(io_port,reg) \
+	inb ((io_port) + (reg))
+
+#define dmac_write(io_port,reg,value) \
+	({ outb ((value), (io_port) + (reg)); })
+
+#define dmac_clearintr(io_port) \
+	({ outb (0, (io_port)); })
+
+static inline
+unsigned int dmac_address (unsigned int io_port)
+{
+    return dmac_read (io_port, TXADRHI) << 16 |
+	   dmac_read (io_port, TXADRMD) << 8 |
+	   dmac_read (io_port, TXADRLO);
+}
+#endif
+
+static
+unsigned long acornscsi_sbic_xfcount (AS_Host *host)
+{
+    unsigned long length;
+
+    length = sbic_arm_read (host->scsi.io_port, TRANSCNTH) << 16;
+    length |= sbic_arm_readnext (host->scsi.io_port) << 8;
+    length |= sbic_arm_readnext (host->scsi.io_port);
+
+    return length;
+}
+
+static
+int acornscsi_sbic_issuecmd (AS_Host *host, int command)
+{
+    int asr;
+
+    do {
+	asr = sbic_arm_read (host->scsi.io_port, ASR);
+    } while (asr & ASR_CIP);
+
+    sbic_arm_write (host->scsi.io_port, CMND, command);
+
+    return 0;
+}
+
+static void
+acornscsi_csdelay (unsigned int cs)
+{
+    unsigned long target_jiffies, flags;
+
+    target_jiffies = jiffies + 1 + cs * HZ / 100;
+
+    save_flags (flags);
+    sti ();
+
+    while (jiffies < target_jiffies) barrier();
+
+    restore_flags (flags);
+}
+
+static
+void acornscsi_resetcard (AS_Host *host)
+{
+    unsigned int i;
+
+    /* assert reset line */
+    host->card.page_reg = 0x80;
+    outb (host->card.page_reg, host->card.io_page);
+
+    /* wait 3 cs.  SCSI standard says 25ms. */
+    acornscsi_csdelay (3);
+
+    host->card.page_reg = 0;
+    outb (host->card.page_reg, host->card.io_page);
+
+    /*
+     * Should get a reset from the card
+     */
+    while (!(inb (host->card.io_intr) & 8));
+    sbic_arm_read (host->scsi.io_port, ASR);
+    sbic_arm_read (host->scsi.io_port, SSR);
+
+    /* setup sbic - WD33C93A */
+    sbic_arm_write (host->scsi.io_port, OWNID, OWNID_EAF | host->host->this_id);
+    sbic_arm_write (host->scsi.io_port, CMND, CMND_RESET);
+
+    /*
+     * Command should cause a reset interrupt
+     */
+    while (!(inb (host->card.io_intr) & 8));
+    sbic_arm_read (host->scsi.io_port, ASR);
+    if (sbic_arm_read (host->scsi.io_port, SSR) != 0x01)
+	printk (KERN_CRIT "scsi%d: WD33C93A didn't give enhanced reset interrupt\n",
+		host->host->host_no);
+
+    sbic_arm_write (host->scsi.io_port, CTRL, INIT_SBICDMA | CTRL_IDI);
+    sbic_arm_write (host->scsi.io_port, TIMEOUT, TIMEOUT_TIME);
+    sbic_arm_write (host->scsi.io_port, SYNCHTRANSFER, SYNCHTRANSFER_2DBA);
+    sbic_arm_write (host->scsi.io_port, SOURCEID, SOURCEID_ER | SOURCEID_DSP);
+
+    host->card.page_reg = 0x40;
+    outb (host->card.page_reg, host->card.io_page);
+
+#ifdef USE_DMAC
+    /* setup dmac - uPC71071 */
+    dmac_write (host->dma.io_port, INIT, 0);
+    dmac_write (host->dma.io_port, INIT, INIT_8BIT);
+    dmac_write (host->dma.io_port, CHANNEL, CHANNEL_0);
+    dmac_write (host->dma.io_port, DEVCON0, INIT_DEVCON0);
+    dmac_write (host->dma.io_port, DEVCON1, INIT_DEVCON1);
+#else
+    dmac_write (host->dma.io_port, INIT, 0);
+#endif
+
+    host->SCpnt = NULL;
+    host->scsi.phase = PHASE_IDLE;
+    host->scsi.disconnectable = 0;
+
+    for (i = 0; i < 8; i++) {
+	host->busyluns[i] = 0;
+	host->device[i].sync_state = SYNC_NEGOCIATE;
+	host->device[i].disconnect_ok = 1;
+    }
+
+    /* wait 25 cs.  SCSI standard says 250ms. */
+    acornscsi_csdelay (25);
+}
+
+/*=============================================================================================
+ * Utility routines (eg. debug)
+ */
+#ifdef CONFIG_ACORNSCSI_CONSTANTS
+static char *acornscsi_interrupttype[] = {
+  "rst",  "suc",  "p/a",  "3",
+  "term", "5",	  "6",	  "7",
+  "serv", "9",	  "a",	  "b",
+  "c",	  "d",	  "e",	  "f"
+};
+
+static signed char acornscsi_map[] = {
+  0,  1, -1, -1,  -1, -1, -1, -1,  -1, -1, -1, -1,  -1, -1, -1, -1,
+ -1,  2, -1, -1,  -1, -1,  3, -1,   4,	5,  6,	7,   8,  9, 10, 11,
+ 12, 13, 14, -1,  -1, -1, -1, -1,   4,	5,  6,	7,   8,  9, 10, 11,
+ -1, -1, -1, -1,  -1, -1, -1, -1,  -1, -1, -1, -1,  -1, -1, -1, -1,
+ 15, 16, 17, 18,  19, -1, -1, 20,   4,	5,  6,	7,   8,  9, 10, 11,
+ -1, -1, -1, -1,  -1, -1, -1, -1,  -1, -1, -1, -1,  -1, -1, -1, -1,
+ -1, -1, -1, -1,  -1, -1, -1, -1,  -1, -1, -1, -1,  -1, -1, -1, -1,
+ -1, -1, -1, -1,  -1, -1, -1, -1,  -1, -1, -1, -1,  -1, -1, -1, -1,
+ 21, 22, -1, -1,  -1, 23, -1, -1,   4,	5,  6,	7,   8,  9, 10, 11,
+ -1, -1, -1, -1,  -1, -1, -1, -1,  -1, -1, -1, -1,  -1, -1, -1, -1,
+ -1, -1, -1, -1,  -1, -1, -1, -1,  -1, -1, -1, -1,  -1, -1, -1, -1,
+ -1, -1, -1, -1,  -1, -1, -1, -1,  -1, -1, -1, -1,  -1, -1, -1, -1,
+ -1, -1, -1, -1,  -1, -1, -1, -1,  -1, -1, -1, -1,  -1, -1, -1, -1,
+ -1, -1, -1, -1,  -1, -1, -1, -1,  -1, -1, -1, -1,  -1, -1, -1, -1,
+ -1, -1, -1, -1,  -1, -1, -1, -1,  -1, -1, -1, -1,  -1, -1, -1, -1,
+ -1, -1, -1, -1,  -1, -1, -1, -1,  -1, -1, -1, -1,  -1, -1, -1, -1
+};      
+
+static char *acornscsi_interruptcode[] = {
+    /* 0 */
+    "reset - normal mode",	/* 00 */
+    "reset - advanced mode",	/* 01 */
+
+    /* 2 */
+    "sel",			/* 11 */
+    "sel+xfer", 		/* 16 */
+    "data-out", 		/* 18 */
+    "data-in",			/* 19 */
+    "cmd",			/* 1A */
+    "stat",			/* 1B */
+    "??-out",			/* 1C */
+    "??-in",			/* 1D */
+    "msg-out",			/* 1E */
+    "msg-in",			/* 1F */
+
+    /* 12 */
+    "/ACK asserted",		/* 20 */
+    "save-data-ptr",		/* 21 */
+    "{re}sel",			/* 22 */
+
+    /* 15 */
+    "inv cmd",			/* 40 */
+    "unexpected disconnect",	/* 41 */
+    "sel timeout",		/* 42 */
+    "P err",			/* 43 */
+    "P err+ATN",		/* 44 */
+    "bad status byte",		/* 47 */
+
+    /* 21 */
+    "resel, no id",		/* 80 */
+    "resel",			/* 81 */
+    "discon",			/* 85 */
+};
+
+static
+void print_scsi_status (unsigned int ssr)
+{
+    if (acornscsi_map[ssr] != -1)
+	printk ("%s:%s",
+		acornscsi_interrupttype[(ssr >> 4)],
+		acornscsi_interruptcode[acornscsi_map[ssr]]);
+    else
+	printk ("%X:%X", ssr >> 4, ssr & 0x0f);    
+}    
+#endif
+
+static
+void print_sbic_status (int asr, int ssr, int cmdphase)
+{
+#ifdef CONFIG_ACORNSCSI_CONSTANTS
+    printk ("sbic: %c%c%c%c%c%c ",
+	    asr & ASR_INT ? 'I' : 'i',
+	    asr & ASR_LCI ? 'L' : 'l',
+	    asr & ASR_BSY ? 'B' : 'b',
+	    asr & ASR_CIP ? 'C' : 'c',
+	    asr & ASR_PE  ? 'P' : 'p',
+	    asr & ASR_DBR ? 'D' : 'd');
+    printk ("scsi: ");
+    print_scsi_status (ssr);
+    printk (" ph %02X\n", cmdphase);
+#else
+    printk ("sbic: %02X scsi: %X:%X ph: %02X\n",
+	    asr, (ssr & 0xf0)>>4, ssr & 0x0f, cmdphase);
+#endif
+}
+
+static
+void acornscsi_dumplog (AS_Host *host, int target)
+{
+    unsigned int prev;
+    do {
+	signed int statptr;
+
+	printk ("%c:", target == 8 ? 'H' : ('0' + target));
+	statptr = status_ptr[target] - 10;
+
+	if (statptr < 0)
+	    statptr += 16;
+
+	prev = status[target][statptr].when;
+
+	for (; statptr != status_ptr[target]; statptr = (statptr + 1) & 15) {
+	    if (status[target][statptr].when) {
+#ifdef CONFIG_ACORNSCSI_CONSTANTS
+		printk ("%c%02X:S=",
+			status[target][statptr].irq ? '-' : ' ',
+			status[target][statptr].ph);
+		print_scsi_status (status[target][statptr].ssr);
+#else
+		printk ("%c%02X:%02X",
+			status[target][statptr].irq ? '-' : ' ',
+			status[target][statptr].ph,
+			status[target][statptr].ssr);
+#endif
+		printk ("+%02ld",
+			(status[target][statptr].when - prev) < 100 ?
+				(status[target][statptr].when - prev) : 99);
+		prev = status[target][statptr].when;
+	    }
+	}
+	printk ("\n");
+	if (target == 8)
+	    break;
+	target = 8;
+    } while (1);
+}
+
+static
+char acornscsi_target (AS_Host *host)
+{
+	if (host->SCpnt)
+		return '0' + host->SCpnt->target;
+	return 'H';
+}
+
+#ifdef USE_DMAC
+static
+void acornscsi_dumpdma (AS_Host *host, char *where)
+{
+	unsigned int mode, addr, len;
+
+	mode = dmac_read (host->dma.io_port, MODECON);
+	addr = dmac_address (host->dma.io_port);
+	len  = dmac_read (host->dma.io_port, TXCNTHI) << 8 |
+	       dmac_read (host->dma.io_port, TXCNTLO);
+
+	printk ("scsi%d: %s: DMAC %02x @%06x+%04x msk %02x, ",
+		host->host->host_no, where,
+		mode, addr, (len + 1) & 0xffff,
+		dmac_read (host->dma.io_port, MASKREG));
+
+	printk ("DMA @%06x, ", host->dma.start_addr);
+	printk ("BH @%p +%04x, ", host->scsi.SCp.ptr,
+		host->scsi.SCp.this_residual);
+	printk ("DT @+%04x ST @+%04x", host->dma.transferred,
+		host->scsi.SCp.have_data_in);
+	printk ("\n");
+}
+#endif
+
+/*
+ * Prototype: cmdtype_t acornscsi_cmdtype (int command)
+ * Purpose  : differentiate READ from WRITE from other commands
+ * Params   : command - command to interpret
+ * Returns  : CMD_READ	- command reads data,
+ *	      CMD_WRITE - command writes data,
+ *	      CMD_MISC	- everything else
+ */
+static inline
+cmdtype_t acornscsi_cmdtype (int command)
+{
+    switch (command) {
+    case WRITE_6:  case WRITE_10:  case WRITE_12:
+	return CMD_WRITE;
+    case READ_6:   case READ_10:   case READ_12:
+	return CMD_READ;
+    default:
+	return CMD_MISC;
+    }
+}
+
+/*
+ * Prototype: int acornscsi_datadirection (int command)
+ * Purpose  : differentiate between commands that have a DATA IN phase
+ *	      and a DATA OUT phase
+ * Params   : command - command to interpret
+ * Returns  : DATADIR_OUT - data out phase expected
+ *	      DATADIR_IN  - data in phase expected
+ */
+static
+datadir_t acornscsi_datadirection (int command)
+{
+    switch (command) {
+    case CHANGE_DEFINITION:	case COMPARE:		case COPY:
+    case COPY_VERIFY:		case LOG_SELECT:	case MODE_SELECT:
+    case MODE_SELECT_10:	case SEND_DIAGNOSTIC:	case WRITE_BUFFER:
+    case FORMAT_UNIT:		case REASSIGN_BLOCKS:	case RESERVE:
+    case SEARCH_EQUAL:		case SEARCH_HIGH:	case SEARCH_LOW:
+    case WRITE_6:		case WRITE_10:		case WRITE_VERIFY:
+    case UPDATE_BLOCK:		case WRITE_LONG:	case WRITE_SAME:
+    case SEARCH_HIGH_12:	case SEARCH_EQUAL_12:	case SEARCH_LOW_12:
+    case WRITE_12:		case WRITE_VERIFY_12:	case SET_WINDOW:
+    case MEDIUM_SCAN:		case SEND_VOLUME_TAG:	case 0xea:
+	return DATADIR_OUT;
+    default:
+	return DATADIR_IN;
+    }
+}
+
+/*
+ * Purpose  : provide values for synchronous transfers with 33C93.
+ * Copyright: Copyright (c) 1996 John Shifflett, GeoLog Consulting
+ *	Modified by Russell King for 8MHz WD33C93A
+ */
+static struct sync_xfer_tbl {
+    unsigned int period_ns;
+    unsigned char reg_value;
+} sync_xfer_table[] = {
+    {	1, 0x20 },    { 249, 0x20 },	{ 374, 0x30 },
+    { 499, 0x40 },    { 624, 0x50 },	{ 749, 0x60 },
+    { 874, 0x70 },    { 999, 0x00 },	{   0,	  0 }
+};
+
+/*
+ * Prototype: int acornscsi_getperiod (unsigned char syncxfer)
+ * Purpose  : period for the synchronous transfer setting
+ * Params   : syncxfer SYNCXFER register value
+ * Returns  : period in ns.
+ */
+static
+int acornscsi_getperiod (unsigned char syncxfer)
+{
+    int i;
+
+    syncxfer &= 0xf0;
+    if (syncxfer == 0x10)
+	syncxfer = 0;
+
+    for (i = 1; sync_xfer_table[i].period_ns; i++)
+	if (syncxfer == sync_xfer_table[i].reg_value)
+	    return sync_xfer_table[i].period_ns;
+    return 0;
+}
+
+/*
+ * Prototype: int round_period (unsigned int period)
+ * Purpose  : return index into above table for a required REQ period
+ * Params   : period - time (ns) for REQ
+ * Returns  : table index
+ * Copyright: Copyright (c) 1996 John Shifflett, GeoLog Consulting
+ */
+static inline
+int round_period (unsigned int period)
+{
+    int i;
+
+    for (i = 1; sync_xfer_table[i].period_ns; i++) {
+	if ((period <= sync_xfer_table[i].period_ns) &&
+	    (period > sync_xfer_table[i - 1].period_ns))
+	    return i;
+    }
+    return 7;
+}
+
+/*
+ * Prototype: unsigned char calc_sync_xfer (unsigned int period, unsigned int offset)
+ * Purpose  : calculate value for 33c93s SYNC register
+ * Params   : period - time (ns) for REQ
+ *	      offset - offset in bytes between REQ/ACK
+ * Returns  : value for SYNC register
+ * Copyright: Copyright (c) 1996 John Shifflett, GeoLog Consulting
+ */
+static
+unsigned char calc_sync_xfer (unsigned int period, unsigned int offset)
+{
+    return sync_xfer_table[round_period(period)].reg_value |
+		((offset < SDTR_SIZE) ? offset : SDTR_SIZE);
+}
+
+/* ====================================================================================
+ * Command functions
+ */
+/*
+ * Function: acornscsi_kick (AS_Host *host)
+ * Purpose : kick next command to interface
+ * Params  : host - host to send command to
+ * Returns : INTR_IDLE if idle, otherwise INTR_PROCESSING
+ * Notes   : interrupts are always disabled!
+ */
+static
+intr_ret_t acornscsi_kick (AS_Host *host)
+{
+    int from_queue = 0;
+    Scsi_Cmnd *SCpnt;
+
+    /* first check to see if a command is waiting to be executed */
+    SCpnt = host->origSCpnt;
+    host->origSCpnt = NULL;
+
+    /* retrieve next command */
+    if (!SCpnt) {
+	SCpnt = queue_remove_exclude (&host->queues.issue, host->busyluns);
+	if (!SCpnt)
+	    return INTR_IDLE;
+
+	from_queue = 1;
+    }
+
+    if (host->scsi.disconnectable && host->SCpnt) {
+	queue_add_cmd_tail (&host->queues.disconnected, host->SCpnt);
+	host->scsi.disconnectable = 0;
+#if (DEBUG & (DEBUG_QUEUES|DEBUG_DISCON))
+	DBG(host->SCpnt, printk ("scsi%d.%c: moved command to disconnected queue\n",
+		host->host->host_no, acornscsi_target (host)));
+#endif
+	host->SCpnt = NULL;
+    }
+
+    /*
+     * If we have an interrupt pending, then we may have been reselected.
+     * In this case, we don't want to write to the registers
+     */
+    if (!(sbic_arm_read (host->scsi.io_port, ASR) & (ASR_INT|ASR_BSY|ASR_CIP))) {
+	sbic_arm_write (host->scsi.io_port, DESTID, SCpnt->target);
+	sbic_arm_write (host->scsi.io_port, CMND, CMND_SELWITHATN);
+    }
+
+    /*
+     * claim host busy - all of these must happen atomically wrt
+     * our interrupt routine.  Failure means command loss.
+     */
+    host->scsi.phase = PHASE_CONNECTING;
+    host->SCpnt = SCpnt;
+    host->scsi.SCp = SCpnt->SCp;
+    host->dma.xfer_setup = 0;
+    host->dma.xfer_required = 0;
+
+#if (DEBUG & (DEBUG_ABORT|DEBUG_CONNECT))
+    DBG(SCpnt,printk ("scsi%d.%c: starting cmd %02X\n",
+	    host->host->host_no, '0' + SCpnt->target,
+	    SCpnt->cmnd[0]));
+#endif
+
+    if (from_queue) {
+#ifdef CONFIG_SCSI_ACORNSCSI_TAGGED_QUEUE
+	/*
+	 * tagged queueing - allocate a new tag to this command
+	 */
+	if (SCpnt->device->tagged_queue) {
+	    SCpnt->device->current_tag += 1;
+	    if (SCpnt->device->current_tag == 0)
+		SCpnt->device->current_tag = 1;
+	    SCpnt->tag = SCpnt->device->current_tag;
+	} else
+#endif
+	    set_bit (SCpnt->target * 8 + SCpnt->lun, host->busyluns);
+
+	host->stats.removes += 1;
+
+	switch (acornscsi_cmdtype (SCpnt->cmnd[0])) {
+	case CMD_WRITE:
+	    host->stats.writes += 1;
+	    break;
+	case CMD_READ:
+	    host->stats.reads += 1;
+	    break;
+	case CMD_MISC:
+	    host->stats.miscs += 1;
+	    break;
+	}
+    }
+
+    return INTR_PROCESSING;
+}    
+
+/*
+ * Function: void acornscsi_done (AS_Host *host, Scsi_Cmnd **SCpntp, unsigned int result)
+ * Purpose : complete processing for command
+ * Params  : host   - interface that completed
+ *	     result - driver byte of result
+ */
+static
+void acornscsi_done (AS_Host *host, Scsi_Cmnd **SCpntp, unsigned int result)
+{
+    Scsi_Cmnd *SCpnt = *SCpntp;
+
+    /* clean up */
+    sbic_arm_write (host->scsi.io_port, SOURCEID, SOURCEID_ER | SOURCEID_DSP);
+
+    host->stats.fins += 1;
+
+    if (SCpnt) {
+	*SCpntp = NULL;
+
+	acornscsi_dma_cleanup (host);
+
+	SCpnt->result = result << 16 | host->scsi.SCp.Message << 8 | host->scsi.SCp.Status;
+
+	/*
+	 * In theory, this should not happen.  In practice, it seems to.
+	 * Only trigger an error if the device attempts to report all happy
+	 * but with untransferred buffers...  If we don't do something, then
+	 * data loss will occur.  Should we check SCpnt->underflow here?
+	 * It doesn't appear to be set to something meaningful by the higher
+	 * levels all the time.
+	 */
+	if (host->scsi.SCp.ptr && result == DID_OK &&
+		acornscsi_cmdtype (SCpnt->cmnd[0]) != CMD_MISC) {
+	    switch (status_byte (SCpnt->result)) {
+	    case CHECK_CONDITION:
+	    case COMMAND_TERMINATED:
+	    case BUSY:
+	    case QUEUE_FULL:
+	    case RESERVATION_CONFLICT:
+		break;
+
+	    default:
+		printk (KERN_ERR "scsi%d.H: incomplete data transfer detected: result=%08X command=",
+			host->host->host_no, SCpnt->result);
+		print_command (SCpnt->cmnd);
+		acornscsi_dumpdma (host, "done");
+	 	acornscsi_dumplog (host, SCpnt->target);
+		SCpnt->result &= 0xffff;
+		SCpnt->result |= DID_ERROR << 16;
+	    }
+	}
+
+	if (!SCpnt->scsi_done)
+	    panic ("scsi%d.H: null scsi_done function in acornscsi_done", host->host->host_no);
+
+	clear_bit (SCpnt->target * 8 + SCpnt->lun, host->busyluns);
+
+	SCpnt->scsi_done (SCpnt);
+    } else
+	printk ("scsi%d: null command in acornscsi_done", host->host->host_no);
+
+    host->scsi.phase = PHASE_IDLE;
+}
+
+/* ====================================================================================
+ * DMA routines
+ */
+/*
+ * Purpose  : update SCSI Data Pointer
+ * Notes    : this will only be one SG entry or less
+ */
+static
+void acornscsi_data_updateptr (AS_Host *host, Scsi_Pointer *SCp, unsigned int length)
+{
+    SCp->ptr += length;
+    SCp->this_residual -= length;
+
+    if (!SCp->this_residual) {
+	if (SCp->buffers_residual) {
+	    SCp->buffer++;
+	    SCp->buffers_residual--;
+	    SCp->ptr = (char *)SCp->buffer->address;
+	    SCp->this_residual = SCp->buffer->length;
+	} else
+	    SCp->ptr = NULL;
+    }
+}
+
+/*
+ * Prototype: void acornscsi_data_read (AS_Host *host, char *ptr,
+ *				unsigned int start_addr, unsigned int length)
+ * Purpose  : read data from DMA RAM
+ * Params   : host - host to transfer from
+ *	      ptr  - DRAM address
+ *	      start_addr - host mem address
+ *	      length - number of bytes to transfer
+ * Notes    : this will only be one SG entry or less
+ */
+static
+void acornscsi_data_read (AS_Host *host, char *ptr,
+				 unsigned int start_addr, unsigned int length)
+{
+    extern void __acornscsi_in (int port, char *buf, int len);
+    unsigned int page, offset, len = length;
+
+    page = (start_addr >> 12);
+    offset = start_addr & ((1 << 12) - 1);
+
+    outb ((page & 0x3f) | host->card.page_reg, host->card.io_page);
+
+    while (len > 0) {
+	unsigned int this_len;
+
+	if (len + offset > (1 << 12))
+	    this_len = (1 << 12) - offset;
+	else
+	    this_len = len;
+
+	__acornscsi_in (host->card.io_ram + (offset << 1), ptr, this_len);
+
+	offset += this_len;
+	ptr += this_len;
+	len -= this_len;
+
+	if (offset == (1 << 12)) {
+	    offset = 0;
+	    page ++;
+	    outb ((page & 0x3f) | host->card.page_reg, host->card.io_page);
+	}
+    }
+    outb (host->card.page_reg, host->card.io_page);
+}
+
+/*
+ * Prototype: void acornscsi_data_write (AS_Host *host, char *ptr,
+ *				unsigned int start_addr, unsigned int length)
+ * Purpose  : write data to DMA RAM
+ * Params   : host - host to transfer from
+ *	      ptr  - DRAM address
+ *	      start_addr - host mem address
+ *	      length - number of bytes to transfer
+ * Notes    : this will only be one SG entry or less
+ */
+static
+void acornscsi_data_write (AS_Host *host, char *ptr,
+				 unsigned int start_addr, unsigned int length)
+{
+    extern void __acornscsi_out (int port, char *buf, int len);
+    unsigned int page, offset, len = length;
+
+    page = (start_addr >> 12);
+    offset = start_addr & ((1 << 12) - 1);
+
+    outb ((page & 0x3f) | host->card.page_reg, host->card.io_page);
+
+    while (len > 0) {
+	unsigned int this_len;
+
+	if (len + offset > (1 << 12))
+	    this_len = (1 << 12) - offset;
+	else
+	    this_len = len;
+
+	__acornscsi_out (host->card.io_ram + (offset << 1), ptr, this_len);
+
+	offset += this_len;
+	ptr += this_len;
+	len -= this_len;
+
+	if (offset == (1 << 12)) {
+	    offset = 0;
+	    page ++;
+	    outb ((page & 0x3f) | host->card.page_reg, host->card.io_page);
+	}
+    }
+    outb (host->card.page_reg, host->card.io_page);
+}
+
+/* =========================================================================================
+ * On-board DMA routines
+ */
+#ifdef USE_DMAC
+/*
+ * Prototype: void acornscsi_dmastop (AS_Host *host)
+ * Purpose  : stop all DMA
+ * Params   : host - host on which to stop DMA
+ * Notes    : This is called when leaving DATA IN/OUT phase,
+ *	      or when interface is RESET
+ */
+static inline
+void acornscsi_dma_stop (AS_Host *host)
+{
+    dmac_write (host->dma.io_port, MASKREG, MASK_ON);
+    dmac_clearintr (host->dma.io_intr_clear);
+
+#if (DEBUG & DEBUG_DMA)
+    DBG(host->SCpnt, acornscsi_dumpdma (host, "stop"));
+#endif
+}
+
+/*
+ * Function: void acornscsi_dma_setup (AS_Host *host, dmadir_t direction)
+ * Purpose : setup DMA controller for data transfer
+ * Params  : host - host to setup
+ *	     direction - data transfer direction
+ * Notes   : This is called when entering DATA I/O phase, not
+ *	     while we're in a DATA I/O phase
+ */
+static
+void acornscsi_dma_setup (AS_Host *host, dmadir_t direction)
+{
+    unsigned int address, length, mode;
+
+    host->dma.direction = direction;
+
+    dmac_write (host->dma.io_port, MASKREG, MASK_ON);
+
+    if (direction == DMA_OUT) {
+#if (DEBUG & DEBUG_NO_WRITE)
+	if (NO_WRITE & (1 << host->SCpnt->target)) {
+	    printk (KERN_CRIT "scsi%d.%c: I can't handle DMA_OUT!\n",
+		    host->host->host_no, acornscsi_target (host));
+	    return;
+	}
+#endif
+	mode = DMAC_WRITE;
+    } else
+	mode = DMAC_READ;
+
+    /*
+     * Allocate some buffer space, limited to half the buffer size
+     */
+    length = min (host->scsi.SCp.this_residual, DMAC_BUFFER_SIZE / 2);
+    if (length) {
+	host->dma.start_addr = address = host->dma.free_addr;
+	host->dma.free_addr = (host->dma.free_addr + length) &
+				(DMAC_BUFFER_SIZE - 1);
+
+	/*
+	 * Transfer data to DMA memory
+	 */
+	if (direction == DMA_OUT)
+	    acornscsi_data_write (host, host->scsi.SCp.ptr, host->dma.start_addr,
+				length);
+
+	length -= 1;
+	dmac_write (host->dma.io_port, TXCNTLO, length);
+	dmac_write (host->dma.io_port, TXCNTHI, length >> 8);
+	dmac_write (host->dma.io_port, TXADRLO, address);
+	dmac_write (host->dma.io_port, TXADRMD, address >> 8);
+	dmac_write (host->dma.io_port, TXADRHI, 0);
+	dmac_write (host->dma.io_port, MODECON, mode);
+	dmac_write (host->dma.io_port, MASKREG, MASK_OFF);
+
+#if (DEBUG & DEBUG_DMA)
+	DBG(host->SCpnt, acornscsi_dumpdma (host, "strt"));
+#endif
+	host->dma.xfer_setup = 1;
+    }
+}
+
+/*
+ * Function: void acornscsi_dma_cleanup (AS_Host *host)
+ * Purpose : ensure that all DMA transfers are up-to-date & host->scsi.SCp is correct
+ * Params  : host - host to finish
+ * Notes   : This is called when a command is:
+ *		terminating, RESTORE_POINTERS, SAVE_POINTERS, DISCONECT
+ *	   : This must not return until all transfers are completed.
+ */
+static
+void acornscsi_dma_cleanup (AS_Host *host)
+{
+    dmac_write (host->dma.io_port, MASKREG, MASK_ON);
+    dmac_clearintr (host->dma.io_intr_clear);
+
+    /*
+     * Check for a pending transfer
+     */
+    if (host->dma.xfer_required) {
+	host->dma.xfer_required = 0;
+	if (host->dma.direction == DMA_IN)
+	    acornscsi_data_read (host, host->dma.xfer_ptr,
+				 host->dma.xfer_start, host->dma.xfer_length);
+    }
+
+    /*
+     * Has a transfer been setup?
+     */
+    if (host->dma.xfer_setup) {
+	unsigned int transferred;
+
+	host->dma.xfer_setup = 0;
+
+#if (DEBUG & DEBUG_DMA)
+	DBG(host->SCpnt, acornscsi_dumpdma (host, "clup"));
+#endif
+
+	/*
+	 * Calculate number of bytes transferred from DMA.
+	 */
+	transferred = dmac_address (host->dma.io_port) - host->dma.start_addr;
+	host->dma.transferred += transferred;
+
+	if (host->dma.direction == DMA_IN)
+	    acornscsi_data_read (host, host->scsi.SCp.ptr,
+				 host->dma.start_addr, transferred);
+
+	/*
+	 * Update SCSI pointers
+	 */
+	acornscsi_data_updateptr (host, &host->scsi.SCp, transferred);
+    }
+}
+
+/*
+ * Function: void acornscsi_dmacintr (AS_Host *host)
+ * Purpose : handle interrupts from DMAC device
+ * Params  : host - host to process
+ * Notes   : If reading, we schedule the read to main memory &
+ *	     allow the transfer to continue.
+ *	   : If writing, we fill the onboard DMA memory from main
+ *	     memory.
+ *	   : Called whenever DMAC finished it's current transfer.
+ */
+static
+void acornscsi_dma_intr (AS_Host *host)
+{
+    unsigned int address, length, transferred;
+
+#if (DEBUG & DEBUG_DMA)
+    DBG(host->SCpnt, acornscsi_dumpdma (host, "inti"));
+#endif
+
+    dmac_write (host->dma.io_port, MASKREG, MASK_ON);
+    dmac_clearintr (host->dma.io_intr_clear);
+
+    /*
+     * Calculate amount transferred via DMA
+     */
+    transferred = dmac_address (host->dma.io_port) - host->dma.start_addr;
+    host->dma.transferred += transferred;
+
+    /*
+     * Schedule DMA transfer off board
+     */
+    if (host->dma.direction == DMA_IN) {
+	host->dma.xfer_start = host->dma.start_addr;
+	host->dma.xfer_length = transferred;
+	host->dma.xfer_ptr = host->scsi.SCp.ptr;
+	host->dma.xfer_required = 1;
+    }
+
+    acornscsi_data_updateptr (host, &host->scsi.SCp, transferred);
+
+    /*
+     * Allocate some buffer space, limited to half the on-board RAM size
+     */
+    length = min (host->scsi.SCp.this_residual, DMAC_BUFFER_SIZE / 2);
+    if (length) {
+	host->dma.start_addr = address = host->dma.free_addr;
+	host->dma.free_addr = (host->dma.free_addr + length) &
+				(DMAC_BUFFER_SIZE - 1);
+
+	/*
+	 * Transfer data to DMA memory
+	 */
+	if (host->dma.direction == DMA_OUT)
+	    acornscsi_data_write (host, host->scsi.SCp.ptr, host->dma.start_addr,
+				length);
+
+	length -= 1;
+	dmac_write (host->dma.io_port, TXCNTLO, length);
+	dmac_write (host->dma.io_port, TXCNTHI, length >> 8);
+	dmac_write (host->dma.io_port, TXADRLO, address);
+	dmac_write (host->dma.io_port, TXADRMD, address >> 8);
+	dmac_write (host->dma.io_port, TXADRHI, 0);
+	dmac_write (host->dma.io_port, MASKREG, MASK_OFF);
+
+#if (DEBUG & DEBUG_DMA)
+	DBG(host->SCpnt, acornscsi_dumpdma (host, "into"));
+#endif
+    } else {
+	host->dma.xfer_setup = 0;
+#if 0
+	/*
+	 * If the interface still wants more, then this is an error.
+	 * We give it another byte, but we also attempt to raise an
+	 * attention condition.  We continue giving one byte until
+	 * the device recognises the attention.
+	 */
+	if (dmac_read (host->dma.io_port, STATUS) & STATUS_RQ0) {
+	    acornscsi_abortcmd (host, host->SCpnt->tag);
+
+	    dmac_write (host->dma.io_port, TXCNTLO, 0);
+	    dmac_write (host->dma.io_port, TXCNTHI, 0);
+	    dmac_write (host->dma.io_port, TXADRLO, 0);
+	    dmac_write (host->dma.io_port, TXADRMD, 0);
+	    dmac_write (host->dma.io_port, TXADRHI, 0);
+	    dmac_write (host->dma.io_port, MASKREG, MASK_OFF);
+	}
+#endif
+    }
+}
+
+/*
+ * Function: void acornscsi_dma_xfer (AS_Host *host)
+ * Purpose : transfer data between AcornSCSI and memory
+ * Params  : host - host to process
+ */
+static
+void acornscsi_dma_xfer (AS_Host *host)
+{
+    host->dma.xfer_required = 0;
+
+    if (host->dma.direction == DMA_IN)
+	acornscsi_data_read (host, host->dma.xfer_ptr,
+				host->dma.xfer_start, host->dma.xfer_length);
+}
+
+/*
+ * Function: void acornscsi_dma_adjust (AS_Host *host)
+ * Purpose : adjust DMA pointers & count for bytes transfered to
+ *	     SBIC but not SCSI bus.
+ * Params  : host - host to adjust DMA count for
+ */
+static
+void acornscsi_dma_adjust (AS_Host *host)
+{
+    if (host->dma.xfer_setup) {
+	signed long transferred;
+#if (DEBUG & (DEBUG_DMA|DEBUG_WRITE))
+	DBG(host->SCpnt, acornscsi_dumpdma (host, "adji"));
+#endif
+	/*
+	 * Calculate correct DMA address - DMA is ahead of SCSI bus while
+	 * writing.
+	 *  host->scsi.SCp.have_data_in is the number of bytes
+	 *  actually transferred to/from the SCSI bus.
+	 *  host->dma.transferred is the number of bytes transferred
+	 *  over DMA since host->dma.start_addr was last set.
+	 *
+	 * real_dma_addr = host->dma.start_addr + host->scsi.SCp.have_data_in
+	 *		   - host->dma.transferred
+	 */
+	transferred = host->scsi.SCp.have_data_in - host->dma.transferred;
+	if (transferred < 0)
+	    printk ("scsi%d.%c: Ack! DMA write correction %ld < 0!\n",
+		    host->host->host_no, acornscsi_target (host), transferred);
+	else if (transferred == 0)
+	    host->dma.xfer_setup = 0;
+	else {
+	    transferred += host->dma.start_addr;
+	    dmac_write (host->dma.io_port, TXADRLO, transferred);
+	    dmac_write (host->dma.io_port, TXADRMD, transferred >> 8);
+	    dmac_write (host->dma.io_port, TXADRHI, transferred >> 16);
+#if (DEBUG & (DEBUG_DMA|DEBUG_WRITE))
+	    DBG(host->SCpnt, acornscsi_dumpdma (host, "adjo"));
+#endif
+	}
+    }
+}
+#endif
+
+/* =========================================================================================
+ * Data I/O
+ */
+/*
+ * Function: void acornscsi_sendcommand (AS_Host *host)
+ * Purpose : send a command to a target
+ * Params  : host - host which is connected to target
+ */
+static
+void acornscsi_sendcommand (AS_Host *host)
+{
+    Scsi_Cmnd *SCpnt = host->SCpnt;
+    unsigned int asr;
+    unsigned char *cmdptr, *cmdend;
+
+    sbic_arm_write (host->scsi.io_port, TRANSCNTH, 0);
+    sbic_arm_writenext (host->scsi.io_port, 0);
+    sbic_arm_writenext (host->scsi.io_port, SCpnt->cmd_len - host->scsi.SCp.sent_command);
+    acornscsi_sbic_issuecmd (host, CMND_XFERINFO);
+
+    cmdptr = SCpnt->cmnd + host->scsi.SCp.sent_command;
+    cmdend = SCpnt->cmnd + SCpnt->cmd_len;
+
+    while (cmdptr < cmdend) {
+	asr = sbic_arm_read (host->scsi.io_port, ASR);
+	if (asr & ASR_DBR)
+	    sbic_arm_write (host->scsi.io_port, DATA, *cmdptr++);
+	else if (asr & ASR_INT)
+	    break;
+    }
+    if (cmdptr >= cmdend)
+	host->scsi.SCp.sent_command = cmdptr - SCpnt->cmnd;
+    host->scsi.phase = PHASE_COMMAND;
+}
+
+static
+void acornscsi_sendmessage (AS_Host *host)
+{
+    unsigned int message_length = msgqueue_msglength (&host->scsi.msgs);
+    int msglen;
+    char *msg;
+
+#if (DEBUG & DEBUG_MESSAGES)
+    printk ("scsi%d.%c: sending message ",
+	    host->host->host_no, acornscsi_target (host));
+#endif
+
+    switch (message_length) {
+    case 0:
+	acornscsi_sbic_issuecmd (host, CMND_XFERINFO | CMND_SBT);
+	while ((sbic_arm_read (host->scsi.io_port, ASR) & ASR_DBR) == 0);
+	sbic_arm_write (host->scsi.io_port, DATA, NOP);
+	host->scsi.last_message = NOP;
+#if (DEBUG & DEBUG_MESSAGES)
+	printk ("NOP");
+#endif
+	break;
+
+    case 1:
+	acornscsi_sbic_issuecmd (host, CMND_XFERINFO | CMND_SBT);
+	msg = msgqueue_getnextmsg (&host->scsi.msgs, &msglen);
+	while ((sbic_arm_read (host->scsi.io_port, ASR) & ASR_DBR) == 0);
+	sbic_arm_write (host->scsi.io_port, DATA, msg[0]);
+	host->scsi.last_message = msg[0];
+#if (DEBUG & DEBUG_MESSAGES)
+	print_msg (msg);
+#endif
+	break;
+
+    default:
+	/*
+	 * ANSI standard says: (SCSI-2 Rev 10c Sect 5.6.14)
+	 * 'When a target sends this (MESSAGE_REJECT) message, it
+	 *  shall change to MESSAGE IN phase and send this message
+	 *  prior to requesting additional message bytes from the
+	 *  initiator.  This provides an interlock so that the
+	 *  initiator can determine which message byte is rejected.
+	 */
+	sbic_arm_write (host->scsi.io_port, TRANSCNTH, 0);
+	sbic_arm_writenext (host->scsi.io_port, 0);
+	sbic_arm_writenext (host->scsi.io_port, message_length);
+	acornscsi_sbic_issuecmd (host, CMND_XFERINFO);
+
+	while ((msg = msgqueue_getnextmsg (&host->scsi.msgs, &msglen)) != NULL) {
+	    unsigned int asr, i;
+#if (DEBUG & DEBUG_MESSAGES)
+	    print_msg (msg);
+#endif
+	    for (i = 0; i < msglen;) {
+		asr = sbic_arm_read (host->scsi.io_port, ASR);
+		if (asr & ASR_DBR)
+		    sbic_arm_write (host->scsi.io_port, DATA, msg[i++]);
+		if (asr & ASR_INT)
+		    break;
+	    }
+	    host->scsi.last_message = msg[0];
+	    if (msg[0] == EXTENDED_MESSAGE)
+		host->scsi.last_message |= msg[2] << 8;
+	    if (asr & ASR_INT)
+		break;
+	}
+	break;
+    }
+#if (DEBUG & DEBUG_MESSAGES)
+    printk ("\n");
+#endif
+}
+
+/*
+ * Function: void acornscsi_readstatusbyte (AS_Host *host)
+ * Purpose : Read status byte from connected target
+ * Params  : host - host connected to target
+ */
+static
+void acornscsi_readstatusbyte (AS_Host *host)
+{
+    acornscsi_sbic_issuecmd (host, CMND_XFERINFO|CMND_SBT);
+    while ((sbic_arm_read (host->scsi.io_port, ASR) & ASR_DBR) == 0);
+
+    host->scsi.SCp.Status = sbic_arm_read (host->scsi.io_port, DATA);
+}
+
+/*
+ * Function: unsigned char acornscsi_readmessagebyte (AS_Host *host)
+ * Purpose : Read one message byte from connected target
+ * Params  : host - host connected to target
+ */
+static
+unsigned char acornscsi_readmessagebyte (AS_Host *host)
+{
+    unsigned char message;
+
+    acornscsi_sbic_issuecmd (host, CMND_XFERINFO | CMND_SBT);
+    while ((sbic_arm_read (host->scsi.io_port, ASR) & ASR_DBR) == 0);
+
+    message = sbic_arm_read (host->scsi.io_port, DATA);
+
+    /* wait for MSGIN-XFER-PAUSED */
+    while ((sbic_arm_read (host->scsi.io_port, ASR) & ASR_INT) == 0);
+    sbic_arm_read (host->scsi.io_port, SSR);
+
+    return message;
+}
+
+/*
+ * Function: void acornscsi_message (AS_Host *host)
+ * Purpose : Read complete message from connected target & action message
+ * Params  : host - host connected to target
+ */
+static
+void acornscsi_message (AS_Host *host)
+{
+    unsigned char message[16];
+    unsigned int msgidx = 0, msglen = 1;
+
+    do {
+	message[msgidx] = acornscsi_readmessagebyte (host);
+
+	switch (msgidx) {
+	case 0:
+	    if (message[0] == EXTENDED_MESSAGE ||
+		(message[0] >= 0x20 && message[0] <= 0x2f))
+		msglen = 2;
+	    break;
+
+	case 1:
+	    if (message[0] == EXTENDED_MESSAGE)
+		msglen += message[msgidx];
+	    break;
+	}
+	msgidx += 1;
+	if (msgidx < msglen) {
+	    acornscsi_sbic_issuecmd (host, CMND_NEGATEACK);
+
+	    /* wait for next msg-in */
+	    while ((sbic_arm_read (host->scsi.io_port, ASR) & ASR_INT) == 0);
+	    sbic_arm_read (host->scsi.io_port, SSR);
+	}
+    } while (msgidx < msglen);
+
+#if (DEBUG & DEBUG_MESSAGES)
+    printk (KERN_DEBUG "scsi%d.%c: message in: ",
+	    host->host->host_no, acornscsi_target (host));
+    print_msg (message);
+    printk ("\n");
+#endif
+
+    if (host->scsi.phase == PHASE_RECONNECTED) {
+	/*
+	 * ANSI standard says: (Section SCSI-2 Rev. 10c Sect 5.6.17)
+	 * 'Whenever a target reconnects to an initiator to continue
+	 *  a tagged I/O process, the SIMPLE QUEUE TAG message shall
+	 *  be sent immediately following the IDENTIFY message...'
+	 */
+	if (message[0] == SIMPLE_QUEUE_TAG)
+	    host->scsi.reconnected.tag = message[1];
+	if (acornscsi_reconnect_finish (host))
+	    host->scsi.phase = PHASE_MSGIN;
+    }
+
+    switch (message[0]) {
+    case ABORT:
+    case ABORT_TAG:
+    case COMMAND_COMPLETE:
+	if (host->scsi.phase != PHASE_STATUSIN)
+	    printk (KERN_ERR "scsi%d.%c: command complete following non-status in phase?\n",
+		    host->host->host_no, acornscsi_target (host));
+	host->scsi.phase = PHASE_DONE;
+	host->scsi.SCp.Message = message[0];
+	break;
+
+    case SAVE_POINTERS:
+	/*
+	 * ANSI standard says: (Section SCSI-2 Rev. 10c Sect 5.6.20)
+	 * 'The SAVE DATA POINTER message is sent from a target to
+	 *  direct the initiator to copy the active data pointer to
+	 *  the saved data pointer for the current I/O process.
+	 */
+	acornscsi_dma_cleanup (host);
+	host->SCpnt->SCp = host->scsi.SCp;
+	host->SCpnt->SCp.sent_command = 0;
+	host->scsi.phase = PHASE_MSGIN;
+	break;
+
+    case RESTORE_POINTERS:
+	/*
+	 * ANSI standard says: (Section SCSI-2 Rev. 10c Sect 5.6.19)
+	 * 'The RESTORE POINTERS message is sent from a target to
+	 *  direct the initiator to copy the most recently saved
+	 *  command, data, and status pointers for the I/O process
+	 *  to the corresponding active pointers.  The command and
+	 *  status pointers shall be restored to the beginning of
+	 *  the present command and status areas.'
+	 */
+	acornscsi_dma_cleanup (host);
+	host->scsi.SCp = host->SCpnt->SCp;
+	host->scsi.phase = PHASE_MSGIN;
+	break;
+
+    case DISCONNECT:
+	/*
+	 * ANSI standard says: (Section SCSI-2 Rev. 10c Sect 6.4.2)
+	 * 'On those occasions when an error or exception condition occurs
+	 *  and the target elects to repeat the information transfer, the
+	 *  target may repeat the transfer either issuing a RESTORE POINTERS
+	 *  message or by disconnecting without issuing a SAVE POINTERS
+	 *  message.  When reconnection is completed, the most recent
+	 *  saved pointer values are restored.'
+	 */
+	acornscsi_dma_cleanup (host);
+	host->scsi.phase = PHASE_DISCONNECT;
+	break;
+
+    case MESSAGE_REJECT:
+#if 0 /* this isn't needed any more */
+	/*
+	 * If we were negociating sync transfer, we don't yet know if
+	 * this REJECT is for the sync transfer or for the tagged queue/wide
+	 * transfer.  Re-initiate sync transfer negociation now, and if
+	 * we got a REJECT in response to SDTR, then it'll be set to DONE.
+	 */
+	if (host->device[host->SCpnt->target].sync_state == SYNC_SENT_REQUEST)
+	    host->device[host->SCpnt->target].sync_state = SYNC_NEGOCIATE;
+#endif
+
+	/*
+	 * If we have any messages waiting to go out, then assert ATN now
+	 */
+	if (msgqueue_msglength (&host->scsi.msgs))
+	    acornscsi_sbic_issuecmd (host, CMND_ASSERTATN);
+
+	switch (host->scsi.last_message) {
+#ifdef CONFIG_SCSI_ACORNSCSI_TAGGED_QUEUE
+	case HEAD_OF_QUEUE_TAG:
+	case ORDERED_QUEUE_TAG:
+	case SIMPLE_QUEUE_TAG:
+	    /*
+	     * ANSI standard says: (Section SCSI-2 Rev. 10c Sect 5.6.17)
+	     *  If a target does not implement tagged queuing and a queue tag
+	     *  message is received, it shall respond with a MESSAGE REJECT
+	     *  message and accept the I/O process as if it were untagged.
+	     */
+	    printk (KERN_NOTICE "scsi%d.%c: disabling tagged queueing\n",
+		    host->host->host_no, acornscsi_target (host));
+	    host->SCpnt->device->tagged_queue = 0;
+	    set_bit (host->SCpnt->target * 8 + host->SCpnt->lun, &host->busyluns);
+	    break;
+#endif
+	case EXTENDED_MESSAGE | (EXTENDED_SDTR << 8):
+	    /*
+	     * Target can't handle synchronous transfers
+	     */
+	    printk (KERN_NOTICE "scsi%d.%c: Using asynchronous transfer\n",
+		    host->host->host_no, acornscsi_target (host));
+	    host->device[host->SCpnt->target].sync_xfer = SYNCHTRANSFER_2DBA;
+	    host->device[host->SCpnt->target].sync_state = SYNC_ASYNCHRONOUS;
+	    sbic_arm_write (host->scsi.io_port, SYNCHTRANSFER, host->device[host->SCpnt->target].sync_xfer);
+	    break;
+
+	default:
+	    break;
+	}
+	break;
+
+    case QUEUE_FULL:
+	/* TODO: target queue is full */
+	break;
+
+    case SIMPLE_QUEUE_TAG:
+	/* tag queue reconnect... message[1] = queue tag.  Print something to indicate something happened! */
+	printk ("scsi%d.%c: reconnect queue tag %02X\n",
+		host->host->host_no, acornscsi_target (host),
+		message[1]);
+	break;
+
+    case EXTENDED_MESSAGE:
+	switch (message[2]) {
+#ifdef CONFIG_SCSI_ACORNSCSI_SYNC
+	case EXTENDED_SDTR:
+	    if (host->device[host->SCpnt->target].sync_state == SYNC_SENT_REQUEST) {
+		/*
+		 * We requested synchronous transfers.  This isn't quite right...
+		 * We can only say if this succeeded if we proceed on to execute the
+		 * command from this message.  If we get a MESSAGE PARITY ERROR,
+		 * and the target retries fail, then we fallback to asynchronous mode
+		 */
+		host->device[host->SCpnt->target].sync_state = SYNC_COMPLETED;
+		printk (KERN_NOTICE "scsi%d.%c: Using synchronous transfer, offset %d, %d ns\n",
+			host->host->host_no, acornscsi_target(host),
+			message[4], message[3] * 4);
+		host->device[host->SCpnt->target].sync_xfer =
+			calc_sync_xfer (message[3] * 4, message[4]);
+	    } else {
+		unsigned char period, length;
+		/*
+		 * Target requested synchronous transfers.  The agreement is only
+		 * to be in operation AFTER the target leaves message out phase.
+		 */
+		acornscsi_sbic_issuecmd (host, CMND_ASSERTATN);
+		period = max (message[3], sdtr_period / 4);
+		length = min (message[4], sdtr_size);
+		msgqueue_addmsg (&host->scsi.msgs, 5, EXTENDED_MESSAGE, 3,
+				 EXTENDED_SDTR, period, length);
+		host->device[host->SCpnt->target].sync_xfer =
+			calc_sync_xfer (period * 4, length);
+	    }
+	    sbic_arm_write (host->scsi.io_port, SYNCHTRANSFER, host->device[host->SCpnt->target].sync_xfer);
+	    break;
+#else
+	    /* We do not accept synchronous transfers.  Respond with a
+	     * MESSAGE_REJECT.
+	     */
+#endif
+
+	case EXTENDED_WDTR:
+	    /* The WD33C93A is only 8-bit.  We respond with a MESSAGE_REJECT
+	     * to a wide data transfer request.
+	     */
+	default:
+	    acornscsi_sbic_issuecmd (host, CMND_ASSERTATN);
+	    msgqueue_flush (&host->scsi.msgs);
+	    msgqueue_addmsg (&host->scsi.msgs, 1, MESSAGE_REJECT);
+	    break;
+	}
+	break;
+
+#ifdef CONFIG_SCSI_ACORNSCSI_LINK
+    case LINKED_CMD_COMPLETE:
+    case LINKED_FLG_CMD_COMPLETE:
+	/*
+	 * We don't support linked commands yet
+	 */
+	if (0) {
+#if (DEBUG & DEBUG_LINK)
+	    printk (KERN_DEBUG "scsi%d.%c: lun %d tag %d linked command complete\n",
+		    host->host->host_no, acornscsi_target(host), host->SCpnt->tag);
+#endif
+	    /*
+	     * A linked command should only terminate with one of these messages
+	     * if there are more linked commands available.
+	     */
+	    if (!host->SCpnt->next_link) {
+		printk (KERN_WARNING "scsi%d.%c: lun %d tag %d linked command complete, but no next_link\n",
+			instance->host_no, acornscsi_target (host), host->SCpnt->tag);
+		acornscsi_sbic_issuecmd (host, CMND_ASSERTATN);
+		msgqueue_addmsg (&host->scsi.msgs, 1, ABORT);
+	    } else {
+		Scsi_Cmnd *SCpnt = host->SCpnt;
+
+		acornscsi_dma_cleanup (host);
+
+		host->SCpnt = host->SCpnt->next_link;
+		host->SCpnt->tag = SCpnt->tag;
+		SCpnt->result = DID_OK | host->scsi.SCp.Message << 8 | host->Scsi.SCp.Status;
+		SCpnt->done (SCpnt);
+
+		/* initialise host->SCpnt->SCp */
+	    }
+	    break;
+	}
+#endif
+
+    default: /* reject message */
+	printk (KERN_ERR "scsi%d.%c: unrecognised message %02X, rejecting\n",
+		host->host->host_no, acornscsi_target (host),
+		message[0]);
+	acornscsi_sbic_issuecmd (host, CMND_ASSERTATN);
+	msgqueue_flush (&host->scsi.msgs);
+	msgqueue_addmsg (&host->scsi.msgs, 1, MESSAGE_REJECT);
+	host->scsi.phase = PHASE_MSGIN;
+	break;
+    }
+    acornscsi_sbic_issuecmd (host, CMND_NEGATEACK);
+}
+
+/*
+ * Function: int acornscsi_buildmessages (AS_Host *host)
+ * Purpose : build the connection messages for a host
+ * Params  : host - host to add messages to
+ */
+static
+void acornscsi_buildmessages (AS_Host *host)
+{
+#if 0
+    /* does the device need resetting? */
+    if (cmd_reset) {
+	msgqueue_addmsg (&host->scsi.msgs, 1, BUS_DEVICE_RESET);
+	return;
+    }
+#endif
+
+    msgqueue_addmsg (&host->scsi.msgs, 1,
+		     IDENTIFY(host->device[host->SCpnt->target].disconnect_ok,
+			     host->SCpnt->lun));
+
+#if 0
+    /* does the device need the current command aborted */
+    if (cmd_aborted) {
+	acornscsi_abortcmd (host->SCpnt->tag);
+	return;
+    }
+#endif
+
+#ifdef CONFIG_SCSI_ACORNSCSI_TAGGED_QUEUE
+    if (host->SCpnt->tag) {
+	unsigned int tag_type;
+
+	if (host->SCpnt->cmnd[0] == REQUEST_SENSE ||
+	    host->SCpnt->cmnd[0] == TEST_UNIT_READY ||
+	    host->SCpnt->cmnd[0] == INQUIRY)
+	    tag_type = HEAD_OF_QUEUE_TAG;
+	else
+	    tag_type = SIMPLE_QUEUE_TAG;
+	msgqueue_addmsg (&host->scsi.msgs, 2, tag_type, host->SCpnt->tag);
+    }
+#endif
+
+#ifdef CONFIG_SCSI_ACORNSCSI_SYNC
+    if (host->device[host->SCpnt->target].sync_state == SYNC_NEGOCIATE) {
+	host->device[host->SCpnt->target].sync_state = SYNC_SENT_REQUEST;
+	msgqueue_addmsg (&host->scsi.msgs, 5,
+			 EXTENDED_MESSAGE, 3, EXTENDED_SDTR,
+			 sdtr_period / 4, sdtr_size);
+    }
+#endif
+}
+
+/*
+ * Function: int acornscsi_starttransfer (AS_Host *host)
+ * Purpose : transfer data to/from connected target
+ * Params  : host - host to which target is connected
+ * Returns : 0 if failure
+ */
+static
+int acornscsi_starttransfer (AS_Host *host)
+{
+    int residual;
+
+    if (!host->scsi.SCp.ptr /*&& host->scsi.SCp.this_residual*/) {
+	printk (KERN_ERR "scsi%d.%c: null buffer passed to acornscsi_starttransfer\n",
+		host->host->host_no, acornscsi_target (host));
+	return 0;
+    }
+
+    residual = host->SCpnt->request_bufflen - host->scsi.SCp.have_data_in;
+
+    sbic_arm_write (host->scsi.io_port, SYNCHTRANSFER, host->device[host->SCpnt->target].sync_xfer);
+    sbic_arm_writenext (host->scsi.io_port, residual >> 16);
+    sbic_arm_writenext (host->scsi.io_port, residual >> 8);
+    sbic_arm_writenext (host->scsi.io_port, residual);
+    acornscsi_sbic_issuecmd (host, CMND_XFERINFO);
+    return 1;
+}
+
+/* =========================================================================================
+ * Connection & Disconnection
+ */
+/*
+ * Function : acornscsi_reconnect (AS_Host *host)
+ * Purpose  : reconnect a previously disconnected command
+ * Params   : host - host specific data
+ * Remarks  : SCSI spec says:
+ *		'The set of active pointers is restored from the set
+ *		 of saved pointers upon reconnection of the I/O process'
+ */
+static
+int acornscsi_reconnect (AS_Host *host)
+{
+    unsigned int target, lun, ok = 0;
+
+    target = sbic_arm_read (host->scsi.io_port, SOURCEID);
+
+    if (!(target & 8))
+	printk (KERN_ERR "scsi%d: invalid source id after reselection "
+		"- device fault?\n",
+		host->host->host_no);
+
+    target &= 7;
+
+    if (host->SCpnt && !host->scsi.disconnectable) {
+	printk (KERN_ERR "scsi%d.%d: reconnected while command in "
+		"progress to target %d?\n",
+		host->host->host_no, target, host->SCpnt->target);
+	host->SCpnt = NULL;
+    }
+
+    lun = sbic_arm_read (host->scsi.io_port, DATA) & 7;
+
+    host->scsi.reconnected.target = target;
+    host->scsi.reconnected.lun = lun;
+    host->scsi.reconnected.tag = 0;
+
+    if (host->scsi.disconnectable && host->SCpnt &&
+	host->SCpnt->target == target && host->SCpnt->lun == lun)
+	ok = 1;
+
+    if (!ok && queue_probetgtlun (&host->queues.disconnected, target, lun))
+	ok = 1;
+
+    ADD_STATUS(target, 0x81, host->scsi.phase, 0);
+
+    if (ok) {
+	host->scsi.phase = PHASE_RECONNECTED;
+    } else {
+	/* this doesn't seem to work */
+	printk (KERN_ERR "scsi%d.%c: reselected with no command "
+		"to reconnect with\n",
+		host->host->host_no, '0' + target);
+	acornscsi_dumplog (host, target);
+	acornscsi_sbic_issuecmd (host, CMND_ASSERTATN);
+	msgqueue_addmsg (&host->scsi.msgs, 1, ABORT);
+	host->scsi.phase = PHASE_ABORTED;
+    }
+    acornscsi_sbic_issuecmd (host, CMND_NEGATEACK);
+    return !ok;
+}
+
+/*
+ * Function: int acornscsi_reconect_finish (AS_Host *host)
+ * Purpose : finish reconnecting a command
+ * Params  : host - host to complete
+ * Returns : 0 if failed
+ */
+static
+int acornscsi_reconnect_finish (AS_Host *host)
+{
+    if (host->scsi.disconnectable && host->SCpnt) {
+	host->scsi.disconnectable = 0;
+	if (host->SCpnt->target == host->scsi.reconnected.target &&
+	    host->SCpnt->lun	== host->scsi.reconnected.lun &&
+	    host->SCpnt->tag	== host->scsi.reconnected.tag) {
+#if (DEBUG & (DEBUG_QUEUES|DEBUG_DISCON))
+	    DBG(host->SCpnt, printk ("scsi%d.%c: reconnected",
+		    host->host->host_no, acornscsi_target (host)));
+#endif
+	} else {
+	    queue_add_cmd_tail (&host->queues.disconnected, host->SCpnt);
+#if (DEBUG & (DEBUG_QUEUES|DEBUG_DISCON))
+	    DBG(host->SCpnt, printk ("scsi%d.%c: had to move command "
+		    "to disconnected queue\n",
+		    host->host->host_no, acornscsi_target (host)));
+#endif
+	    host->SCpnt = NULL;
+	}
+    }
+    if (!host->SCpnt) {
+	host->SCpnt = queue_remove_tgtluntag (&host->queues.disconnected,
+				host->scsi.reconnected.target,
+				host->scsi.reconnected.lun,
+				host->scsi.reconnected.tag);
+#if (DEBUG & (DEBUG_QUEUES|DEBUG_DISCON))
+	DBG(host->SCpnt, printk ("scsi%d.%c: had to get command",
+		host->host->host_no, acornscsi_target (host)));
+#endif
+    }
+
+    if (!host->SCpnt) {
+	acornscsi_abortcmd (host, host->scsi.reconnected.tag);
+	host->scsi.phase = PHASE_ABORTED;
+    } else {
+	/*
+	 * Restore data pointer from SAVED pointers.
+	 */
+	host->scsi.SCp = host->SCpnt->SCp;
+#if (DEBUG & (DEBUG_QUEUES|DEBUG_DISCON))
+	printk (", data pointers: [%p, %X]",
+		host->scsi.SCp.ptr, host->scsi.SCp.this_residual);
+#endif
+    }
+#if (DEBUG & (DEBUG_QUEUES|DEBUG_DISCON))
+    printk ("\n");
+#endif
+
+    host->dma.transferred = host->scsi.SCp.have_data_in;
+
+    return host->SCpnt != NULL;
+}
+
+/*
+ * Function: void acornscsi_disconnect_unexpected (AS_Host *host)
+ * Purpose : handle an unexpected disconnect
+ * Params  : host - host on which disconnect occurred
+ */
+static
+void acornscsi_disconnect_unexpected (AS_Host *host)
+{
+    printk (KERN_ERR "scsi%d.%c: unexpected disconnect\n",
+	    host->host->host_no, acornscsi_target (host));
+#if (DEBUG & DEBUG_ABORT)
+    acornscsi_dumplog (host, 8);
+#endif
+
+    acornscsi_done (host, &host->SCpnt, DID_ABORT);
+}
+
+/*
+ * Function: void acornscsi_abortcmd (AS_host *host, unsigned char tag)
+ * Purpose : abort a currently executing command
+ * Params  : host - host with connected command to abort
+ *	     tag  - tag to abort
+ */
+static
+void acornscsi_abortcmd (AS_Host *host, unsigned char tag)
+{
+    sbic_arm_write (host->scsi.io_port, CMND, CMND_ASSERTATN);
+
+    msgqueue_flush (&host->scsi.msgs);
+#ifdef CONFIG_SCSI_ACORNSCSI_TAGGED_QUEUE
+    if (tag)
+	msgqueue_addmsg (&host->scsi.msgs, 2, ABORT_TAG, tag);
+    else
+#endif
+	msgqueue_addmsg (&host->scsi.msgs, 1, ABORT);
+}
+
+/* ==========================================================================================
+ * Interrupt routines.
+ */
+/*
+ * Function: int acornscsi_sbicintr (AS_Host *host)
+ * Purpose : handle interrupts from SCSI device
+ * Params  : host - host to process
+ * Returns : INTR_PROCESS if expecting another SBIC interrupt
+ *	     INTR_IDLE if no interrupt
+ *	     INTR_NEXT_COMMAND if we have finished processing the command
+ */
+static
+intr_ret_t acornscsi_sbicintr (AS_Host *host, int in_irq)
+{
+    unsigned int asr, ssr;
+
+    asr = sbic_arm_read (host->scsi.io_port, ASR);
+    if (!(asr & ASR_INT))
+	return INTR_IDLE;
+
+    ssr = sbic_arm_read (host->scsi.io_port, SSR);
+
+#if (DEBUG & DEBUG_PHASES)
+    print_sbic_status(asr, ssr, host->scsi.phase);
+#endif
+
+    ADD_STATUS(8, ssr, host->scsi.phase, in_irq);
+
+    if (host->SCpnt && !host->scsi.disconnectable)
+	ADD_STATUS(host->SCpnt->target, ssr, host->scsi.phase, in_irq);
+
+    switch (ssr) {
+    case 0x00:				/* reset state - not advanced			*/
+	printk (KERN_ERR "scsi%d: reset in standard mode but wanted advanced mode.\n",
+		host->host->host_no);
+	/* setup sbic - WD33C93A */
+	sbic_arm_write (host->scsi.io_port, OWNID, OWNID_EAF | host->host->this_id);
+	sbic_arm_write (host->scsi.io_port, CMND, CMND_RESET);
+	return INTR_IDLE;
+
+    case 0x01:				/* reset state - advanced			*/
+	sbic_arm_write (host->scsi.io_port, CTRL, INIT_SBICDMA | CTRL_IDI);
+	sbic_arm_write (host->scsi.io_port, TIMEOUT, TIMEOUT_TIME);
+	sbic_arm_write (host->scsi.io_port, SYNCHTRANSFER, SYNCHTRANSFER_2DBA);
+	sbic_arm_write (host->scsi.io_port, SOURCEID, SOURCEID_ER | SOURCEID_DSP);
+	msgqueue_flush (&host->scsi.msgs);
+	return INTR_IDLE;
+
+    case 0x41:				/* unexpected disconnect aborted command	*/
+	acornscsi_disconnect_unexpected (host);
+	return INTR_NEXT_COMMAND;
+    }
+
+    switch (host->scsi.phase) {
+    case PHASE_CONNECTING:		/* STATE: command removed from issue queue	*/
+	switch (ssr) {
+	case 0x11:			/* -> PHASE_CONNECTED				*/
+	    /* BUS FREE -> SELECTION */
+	    host->scsi.phase = PHASE_CONNECTED;
+	    msgqueue_flush (&host->scsi.msgs);
+	    host->dma.transferred = host->scsi.SCp.have_data_in;
+	    /* 33C93 gives next interrupt indicating bus phase */
+	    asr = sbic_arm_read (host->scsi.io_port, ASR);
+	    if (!(asr & ASR_INT))
+		break;
+	    ssr = sbic_arm_read (host->scsi.io_port, SSR);
+	    ADD_STATUS(8, ssr, host->scsi.phase, 1);
+	    ADD_STATUS(host->SCpnt->target, ssr, host->scsi.phase, 1);
+	    goto connected;
+	    
+	case 0x42:			/* select timed out				*/
+					/* -> PHASE_IDLE				*/
+	    acornscsi_done (host, &host->SCpnt, DID_NO_CONNECT);
+	    return INTR_NEXT_COMMAND;
+
+	case 0x81:			/* -> PHASE_RECONNECTED or PHASE_ABORTED	*/
+	    /* BUS FREE -> RESELECTION */
+	    host->origSCpnt = host->SCpnt;
+	    host->SCpnt = NULL;
+	    msgqueue_flush (&host->scsi.msgs);
+	    acornscsi_reconnect (host);
+	    break;
+
+	default:
+	    printk (KERN_ERR "scsi%d.%c: PHASE_CONNECTING, SSR %02X?\n",
+		    host->host->host_no, acornscsi_target (host), ssr);
+	    acornscsi_dumplog (host, host->SCpnt ? host->SCpnt->target : 8);
+	    acornscsi_abortcmd (host, host->SCpnt->tag);
+	}
+	return INTR_PROCESSING;
+
+    connected:
+    case PHASE_CONNECTED:		/* STATE: device selected ok			*/
+	switch (ssr) {
+#ifdef NONSTANDARD
+	case 0x8a:			/* -> PHASE_COMMAND, PHASE_COMMANDPAUSED	*/
+	    /* SELECTION -> COMMAND */
+	    acornscsi_sendcommand (host);
+	    break;
+
+	case 0x8b:			/* -> PHASE_STATUS				*/
+	    /* SELECTION -> STATUS */
+	    acornscsi_readstatusbyte (host);
+	    host->scsi.phase = PHASE_STATUSIN;
+	    break;
+#endif
+
+	case 0x8e:			/* -> PHASE_MSGOUT				*/
+	    /* SELECTION ->MESSAGE OUT */
+	    host->scsi.phase = PHASE_MSGOUT;
+	    acornscsi_buildmessages (host);
+	    acornscsi_sendmessage (host);
+	    break;
+
+	/* these should not happen */
+	case 0x85:			/* target disconnected				*/
+	    acornscsi_done (host, &host->SCpnt, DID_ERROR);
+	    break;
+
+	default:
+	    printk (KERN_ERR "scsi%d.%c: PHASE_CONNECTED, SSR %02X?\n",
+		    host->host->host_no, acornscsi_target (host), ssr);
+	    acornscsi_dumplog (host, host->SCpnt ? host->SCpnt->target : 8);
+	    acornscsi_abortcmd (host, host->SCpnt->tag);
+	}
+	return INTR_PROCESSING;
+
+    case PHASE_MSGOUT:			/* STATE: connected & sent IDENTIFY message	*/
+	/*
+	 * SCSI standard says th at a MESSAGE OUT phases can be followed by a DATA phase
+	 */
+	switch (ssr) {
+	case 0x8a:
+	case 0x1a:			/* -> PHASE_COMMAND, PHASE_COMMANDPAUSED	*/
+	    /* MESSAGE OUT -> COMMAND */
+	    acornscsi_sendcommand (host);
+	    break;
+
+	case 0x1b:			/* -> PHASE_STATUS				*/
+	    /* MESSAGE OUT -> STATUS */
+	    acornscsi_readstatusbyte (host);
+	    host->scsi.phase = PHASE_STATUSIN;
+	    break;
+
+	case 0x8e:			/* -> PHASE_MSGOUT				*/
+	    /* MESSAGE_OUT(MESSAGE_IN) ->MESSAGE OUT */
+	    acornscsi_sendmessage (host);
+	    break;
+
+        case 0x4f:
+	case 0x1f:			/* -> PHASE_MSGIN, PHASE_DISCONNECT		*/
+	    /* MESSAGE OUT -> MESSAGE IN */
+	    acornscsi_message (host);
+	    break;
+
+	default:
+	    printk (KERN_ERR "scsi%d.%c: PHASE_MSGOUT, SSR %02X?\n",
+		    host->host->host_no, acornscsi_target (host), ssr);
+	    acornscsi_dumplog (host, host->SCpnt ? host->SCpnt->target : 8);
+	}
+	return INTR_PROCESSING;
+
+    case PHASE_COMMAND: 		/* STATE: connected & command sent		*/
+	switch (ssr) {
+	case 0x18:			/* -> PHASE_DATAOUT				*/
+	    /* COMMAND -> DATA OUT */
+	    if (host->scsi.SCp.sent_command != host->SCpnt->cmd_len)
+		acornscsi_abortcmd (host, host->SCpnt->tag);
+	    acornscsi_dma_setup (host, DMA_OUT);
+	    if (!acornscsi_starttransfer (host))
+		acornscsi_abortcmd (host, host->SCpnt->tag);
+	    host->scsi.phase = PHASE_DATAOUT;
+	    return INTR_IDLE;
+
+	case 0x19:			/* -> PHASE_DATAIN				*/
+	    /* COMMAND -> DATA IN */
+	    if (host->scsi.SCp.sent_command != host->SCpnt->cmd_len)
+		acornscsi_abortcmd (host, host->SCpnt->tag);
+	    acornscsi_dma_setup (host, DMA_IN);
+	    if (!acornscsi_starttransfer (host))
+		acornscsi_abortcmd (host, host->SCpnt->tag);
+	    host->scsi.phase = PHASE_DATAIN;
+	    return INTR_IDLE;
+
+	case 0x1b:			/* -> PHASE_STATUS				*/
+	    /* COMMAND -> STATUS */
+	    acornscsi_readstatusbyte (host);
+	    host->scsi.phase = PHASE_STATUSIN;
+	    break;
+
+	case 0x1e:			/* -> PHASE_MSGOUT				*/
+	    /* COMMAND -> MESSAGE OUT */
+	    acornscsi_sendmessage (host);
+	    break;
+
+	case 0x1f:			/* -> PHASE_MSGIN, PHASE_DISCONNECT		*/
+	    /* COMMAND -> MESSAGE IN */
+	    acornscsi_message (host);
+	    break;
+
+	default:
+	    printk (KERN_ERR "scsi%d.%c: PHASE_COMMAND, SSR %02X?\n",
+		    host->host->host_no, acornscsi_target (host), ssr);
+	    acornscsi_dumplog (host, host->SCpnt ? host->SCpnt->target : 8);
+	}
+	return INTR_PROCESSING;
+
+    case PHASE_DISCONNECT:		/* STATE: connected, received DISCONNECT msg	*/
+	if (ssr == 0x85) {		/* -> PHASE_IDLE				*/
+	    host->scsi.disconnectable = 1;
+	    host->scsi.reconnected.tag = 0;
+	    host->scsi.phase = PHASE_IDLE;
+	    host->stats.disconnects += 1;
+	} else {
+	    printk (KERN_ERR "scsi%d.%c: PHASE_DISCONNECT, SSR %02X instead of disconnect?\n",
+		    host->host->host_no, acornscsi_target (host), ssr);
+	    acornscsi_dumplog (host, host->SCpnt ? host->SCpnt->target : 8);
+	}
+	return INTR_NEXT_COMMAND;
+
+    case PHASE_IDLE:			/* STATE: disconnected				*/
+	if (ssr == 0x81)		/* -> PHASE_RECONNECTED or PHASE_ABORTED	*/
+	    acornscsi_reconnect (host);
+	else {
+	    printk (KERN_ERR "scsi%d.%c: PHASE_IDLE, SSR %02X while idle?\n",
+		    host->host->host_no, acornscsi_target (host), ssr);
+	    acornscsi_dumplog (host, host->SCpnt ? host->SCpnt->target : 8);
+	}
+	return INTR_PROCESSING;
+
+    case PHASE_RECONNECTED:		/* STATE: device reconnected to initiator	*/
+	/*
+	 * Command reconnected - if MESGIN, get message - it may be
+	 * the tag.  If not, get command out of disconnected queue
+	 */
+	/*
+	 * If we reconnected and we're not in MESSAGE IN phase after IDENTIFY,
+	 * reconnect I_T_L command
+	 */
+	if (ssr != 0x8f && !acornscsi_reconnect_finish (host))
+	    return INTR_IDLE;
+	ADD_STATUS(host->SCpnt->target, ssr, host->scsi.phase, in_irq);
+	switch (ssr) {
+	case 0x88:			/* data out phase				*/
+					/* -> PHASE_DATAOUT				*/
+	    /* MESSAGE IN -> DATA OUT */
+	    acornscsi_dma_setup (host, DMA_OUT);
+	    if (!acornscsi_starttransfer (host))
+		acornscsi_abortcmd (host, host->SCpnt->tag);
+	    host->scsi.phase = PHASE_DATAOUT;
+	    return INTR_IDLE;
+
+	case 0x89:			/* data in phase				*/
+					/* -> PHASE_DATAIN				*/
+	    /* MESSAGE IN -> DATA IN */
+	    acornscsi_dma_setup (host, DMA_IN);
+	    if (!acornscsi_starttransfer (host))
+		acornscsi_abortcmd (host, host->SCpnt->tag);
+	    host->scsi.phase = PHASE_DATAIN;
+	    return INTR_IDLE;
+
+	case 0x8a:			/* command out					*/
+	    /* MESSAGE IN -> COMMAND */
+	    acornscsi_sendcommand (host);/* -> PHASE_COMMAND, PHASE_COMMANDPAUSED	*/
+	    break;
+
+	case 0x8b:			/* status in					*/
+					/* -> PHASE_STATUSIN				*/
+	    /* MESSAGE IN -> STATUS */
+	    acornscsi_readstatusbyte (host);
+	    host->scsi.phase = PHASE_STATUSIN;
+	    break;
+
+	case 0x8e:			/* message out					*/
+					/* -> PHASE_MSGOUT				*/
+	    /* MESSAGE IN -> MESSAGE OUT */
+	    acornscsi_sendmessage (host);
+	    break;
+
+	case 0x8f:			/* message in					*/
+	    acornscsi_message (host);	/* -> PHASE_MSGIN, PHASE_DISCONNECT		*/
+	    break;
+
+	default:
+	    printk (KERN_ERR "scsi%d.%c: PHASE_RECONNECTED, SSR %02X after reconnect?\n",
+		    host->host->host_no, acornscsi_target (host), ssr);
+	    acornscsi_dumplog (host, host->SCpnt ? host->SCpnt->target : 8);
+	}
+	return INTR_PROCESSING;
+
+    case PHASE_DATAIN:			/* STATE: transferred data in			*/
+	/*
+	 * This is simple - if we disconnect then the DMA address & count is
+	 * correct.
+	 */
+	switch (ssr) {
+	case 0x19:			/* -> PHASE_DATAIN				*/
+	    acornscsi_abortcmd (host, host->SCpnt->tag);
+	    return INTR_IDLE;
+
+	case 0x4b:			/* -> PHASE_STATUSIN				*/
+	case 0x1b:			/* -> PHASE_STATUSIN				*/
+	    /* DATA IN -> STATUS */
+	    host->scsi.SCp.have_data_in = host->SCpnt->request_bufflen -
+					  acornscsi_sbic_xfcount (host);
+	    acornscsi_dma_stop (host);
+	    acornscsi_readstatusbyte (host);
+	    host->scsi.phase = PHASE_STATUSIN;
+	    break;
+
+	case 0x1e:			/* -> PHASE_MSGOUT				*/
+	case 0x4e:			/* -> PHASE_MSGOUT				*/
+	    /* DATA IN -> MESSAGE OUT */
+	    host->scsi.SCp.have_data_in = host->SCpnt->request_bufflen -
+					  acornscsi_sbic_xfcount (host);
+	    acornscsi_dma_stop (host);
+	    acornscsi_sendmessage (host);
+	    break;
+
+	case 0x1f:			/* message in					*/
+	case 0x4f:			/* message in					*/
+	    /* DATA IN -> MESSAGE IN */
+	    host->scsi.SCp.have_data_in = host->SCpnt->request_bufflen -
+					  acornscsi_sbic_xfcount (host);
+	    acornscsi_dma_stop (host);
+	    acornscsi_message (host);	/* -> PHASE_MSGIN, PHASE_DISCONNECT		*/
+	    break;
+
+	default:
+	    printk (KERN_ERR "scsi%d.%c: PHASE_DATAIN, SSR %02X?\n",
+		    host->host->host_no, acornscsi_target (host), ssr);
+	    acornscsi_dumplog (host, host->SCpnt ? host->SCpnt->target : 8);
+	}
+	return INTR_PROCESSING;
+
+    case PHASE_DATAOUT: 		/* STATE: transferred data out			*/
+	/*
+	 * This is more complicated - if we disconnect, the DMA could be 12
+	 * bytes ahead of us.  We need to correct this.
+	 */
+	switch (ssr) {
+	case 0x18:			/* -> PHASE_DATAOUT				*/
+	    acornscsi_abortcmd (host, host->SCpnt->tag);
+	    return INTR_IDLE;
+
+	case 0x4b:			/* -> PHASE_STATUSIN				*/
+	case 0x1b:			/* -> PHASE_STATUSIN				*/
+	    /* DATA OUT -> STATUS */
+	    host->scsi.SCp.have_data_in = host->SCpnt->request_bufflen -
+					  acornscsi_sbic_xfcount (host);
+	    acornscsi_dma_stop (host);
+	    acornscsi_dma_adjust (host);
+	    acornscsi_readstatusbyte (host);
+	    host->scsi.phase = PHASE_STATUSIN;
+	    break;
+
+	case 0x1e:			/* -> PHASE_MSGOUT				*/
+	case 0x4e:			/* -> PHASE_MSGOUT				*/
+	    /* DATA OUT -> MESSAGE OUT */
+	    host->scsi.SCp.have_data_in = host->SCpnt->request_bufflen -
+					  acornscsi_sbic_xfcount (host);
+	    acornscsi_dma_stop (host);
+	    acornscsi_dma_adjust (host);
+	    acornscsi_sendmessage (host);
+	    break;
+
+	case 0x1f:			/* message in					*/
+	case 0x4f:			/* message in					*/
+	    /* DATA OUT -> MESSAGE IN */
+	    host->scsi.SCp.have_data_in = host->SCpnt->request_bufflen -
+					  acornscsi_sbic_xfcount (host);
+	    acornscsi_dma_stop (host);
+	    acornscsi_dma_adjust (host);
+	    acornscsi_message (host);	/* -> PHASE_MSGIN, PHASE_DISCONNECT		*/
+	    break;
+
+	default:
+	    printk (KERN_ERR "scsi%d.%c: PHASE_DATAOUT, SSR %02X?\n",
+		    host->host->host_no, acornscsi_target (host), ssr);
+	    acornscsi_dumplog (host, host->SCpnt ? host->SCpnt->target : 8);
+	}
+	return INTR_PROCESSING;
+
+    case PHASE_STATUSIN:		/* STATE: status in complete			*/
+	if (ssr == 0x1f)		/* -> PHASE_MSGIN, PHASE_DONE, PHASE_DISCONNECT */
+	    /* STATUS -> MESSAGE IN */
+	    acornscsi_message (host);
+	else if (ssr == 0x1e)		/* -> PHASE_MSGOUT				*/
+	    /* STATUS -> MESSAGE OUT */
+	    acornscsi_sendmessage (host);
+	else {
+	    printk (KERN_ERR "scsi%d.%c: PHASE_STATUSIN, SSR %02X instead of MESSAGE_IN?\n",
+		    host->host->host_no, acornscsi_target (host), ssr);
+	    acornscsi_dumplog (host, host->SCpnt ? host->SCpnt->target : 8);
+	}
+	return INTR_PROCESSING;
+
+    case PHASE_MSGIN:			/* STATE: message in				*/
+	switch (ssr) {
+	case 0x1e:			/* -> PHASE_MSGOUT				*/
+	case 0x4e:			/* -> PHASE_MSGOUT				*/
+	    /* MESSAGE IN -> MESSAGE OUT */
+	    acornscsi_sendmessage (host);
+	    break;
+
+	case 0x1f:			/* -> PHASE_MSGIN, PHASE_DONE, PHASE_DISCONNECT */
+	case 0x2f:
+	case 0x4f:
+	case 0x8f:
+	    acornscsi_message (host);
+	    break;
+
+	default:
+	    printk (KERN_ERR "scsi%d.%c: PHASE_MSGIN, SSR %02X after message in?\n",
+		    host->host->host_no, acornscsi_target (host), ssr);
+	    acornscsi_dumplog (host, host->SCpnt ? host->SCpnt->target : 8);
+	}
+	return INTR_PROCESSING;
+
+    case PHASE_DONE:			/* STATE: received status & message		*/
+	switch (ssr) {
+	case 0x85:			/* -> PHASE_IDLE				*/
+	    acornscsi_done (host, &host->SCpnt, DID_OK);
+	    return INTR_NEXT_COMMAND;
+
+	case 0x8e:
+	    acornscsi_sendmessage (host);
+	    break;
+
+	default:
+	    printk (KERN_ERR "scsi%d.%c: PHASE_DONE, SSR %02X instead of disconnect?\n",
+		    host->host->host_no, acornscsi_target (host), ssr);
+	    acornscsi_dumplog (host, host->SCpnt ? host->SCpnt->target : 8);
+	}
+	return INTR_PROCESSING;
+
+    case PHASE_ABORTED:
+	switch (ssr) {
+	case 0x85:
+	    acornscsi_done (host, &host->SCpnt, DID_ABORT);
+	    return INTR_NEXT_COMMAND;
+
+	case 0x1e:
+	case 0x2e:
+	case 0x4e:
+	case 0x8e:
+	    acornscsi_sendmessage (host);
+	    break;
+
+	default:
+	    printk (KERN_ERR "scsi%d.%c: PHASE_ABORTED, SSR %02X?\n",
+		    host->host->host_no, acornscsi_target (host), ssr);
+	    acornscsi_dumplog (host, host->SCpnt ? host->SCpnt->target : 8);
+	}
+	return INTR_PROCESSING;
+
+    default:
+	printk (KERN_ERR "scsi%d.%c: unknown driver phase %d\n",
+		host->host->host_no, acornscsi_target (host), ssr);
+	acornscsi_dumplog (host, host->SCpnt ? host->SCpnt->target : 8);
+    }
+    return INTR_PROCESSING;
+}
+
+/*
+ * Prototype: void acornscsi_intr (int irq, void *dev_id, struct pt_regs *regs)
+ * Purpose  : handle interrupts from Acorn SCSI card
+ * Params   : irq    - interrupt number
+ *	      dev_id - device specific data (AS_Host structure)
+ *	      regs   - processor registers when interrupt occurred
+ */
+static
+void acornscsi_intr (int irq, void *dev_id, struct pt_regs *regs)
+{
+    AS_Host *host = (AS_Host *)dev_id;
+    intr_ret_t ret;
+    int iostatus;
+    int in_irq = 0;
+
+    if (host->scsi.interrupt)
+	printk ("scsi%d: interrupt re-entered\n", host->host->host_no);
+    host->scsi.interrupt = 1;
+
+    do {
+	ret = INTR_IDLE;
+
+	iostatus = inb (host->card.io_intr);
+
+	if (iostatus & 2) {
+	    acornscsi_dma_intr (host);
+	    iostatus = inb (host->card.io_intr);
+	}
+
+	if (iostatus & 8)
+	    ret = acornscsi_sbicintr (host, in_irq);
+
+	/*
+	 * If we have a transfer pending, start it.
+	 * Only start it if the interface has already started transferring
+	 * it's data
+	 */
+	if (host->dma.xfer_required)
+	    acornscsi_dma_xfer (host);
+
+	if (ret == INTR_NEXT_COMMAND)
+	    ret = acornscsi_kick (host);
+
+	in_irq = 1;
+    } while (ret != INTR_IDLE);
+
+    host->scsi.interrupt = 0;
+}
+
+/*=============================================================================================
+ * Interfaces between interrupt handler and rest of scsi code
+ */
+
+/*
+ * Function : acornscsi_queuecmd (Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *))
+ * Purpose  : queues a SCSI command
+ * Params   : cmd  - SCSI command
+ *	      done - function called on completion, with pointer to command descriptor
+ * Returns  : 0, or < 0 on error.
+ */
+int acornscsi_queuecmd (Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *))
+{
+    AS_Host *host = (AS_Host *)SCpnt->host->hostdata;
+
+    if (!done) {
+	/* there should be some way of rejecting errors like this without panicing... */
+	panic ("scsi%d: queuecommand called with NULL done function [cmd=%p]",
+		SCpnt->host->host_no, SCpnt);
+	return -EINVAL;
+    }
+
+#if (DEBUG & DEBUG_NO_WRITE)
+    if (acornscsi_cmdtype (SCpnt->cmnd[0]) == CMD_WRITE && (NO_WRITE & (1 << SCpnt->target))) {
+	printk (KERN_CRIT "scsi%d.%c: WRITE attempted with NO_WRITE flag set\n",
+	    SCpnt->host->host_no, '0' + SCpnt->target);
+	SCpnt->result = DID_NO_CONNECT << 16;
+	done (SCpnt);
+	return 0;
+    }
+#endif
+
+    SCpnt->scsi_done = done;
+    SCpnt->host_scribble = NULL;
+    SCpnt->result = 0;
+    SCpnt->tag = 0;
+    SCpnt->SCp.phase = (int)acornscsi_datadirection (SCpnt->cmnd[0]);
+    SCpnt->SCp.sent_command = 0;
+    SCpnt->SCp.have_data_in = 0;
+    SCpnt->SCp.Status = 0;
+    SCpnt->SCp.Message = 0;
+
+    if (SCpnt->use_sg) {
+	SCpnt->SCp.buffer = (struct scatterlist *) SCpnt->buffer;
+	SCpnt->SCp.buffers_residual = SCpnt->use_sg - 1;
+	SCpnt->SCp.ptr = (char *) SCpnt->SCp.buffer->address;
+	SCpnt->SCp.this_residual = SCpnt->SCp.buffer->length;
+    } else {
+	SCpnt->SCp.buffer = NULL;
+	SCpnt->SCp.buffers_residual = 0;
+	SCpnt->SCp.ptr = (char *) SCpnt->request_buffer;
+	SCpnt->SCp.this_residual = SCpnt->request_bufflen;
+    }
+
+    host->stats.queues += 1;
+
+    {
+	unsigned long flags;
+
+	if (!queue_add_cmd_ordered (&host->queues.issue, SCpnt)) {
+	    SCpnt->result = DID_ERROR << 16;
+	    done (SCpnt);
+	    return 0;
+	}
+	save_flags_cli (flags);
+	if (host->scsi.phase == PHASE_IDLE)
+	    acornscsi_kick (host);
+	restore_flags (flags);
+    }
+    return 0;
+}
+
+/*
+ * Prototype: void acornscsi_reportstatus (Scsi_Cmnd **SCpntp1, Scsi_Cmnd **SCpntp2, int result)
+ * Purpose  : pass a result to *SCpntp1, and check if *SCpntp1 = *SCpntp2
+ * Params   : SCpntp1 - pointer to command to return
+ *	      SCpntp2 - pointer to command to check
+ *	      result  - result to pass back to mid-level done function
+ * Returns  : *SCpntp2 = NULL if *SCpntp1 is the same command structure as *SCpntp2.
+ */
+static inline
+void acornscsi_reportstatus (Scsi_Cmnd **SCpntp1, Scsi_Cmnd **SCpntp2, int result)
+{
+    Scsi_Cmnd *SCpnt = *SCpntp1;
+
+    if (SCpnt) {
+	*SCpntp1 = NULL;
+
+	SCpnt->result = result;
+	SCpnt->scsi_done (SCpnt);
+    }
+
+    if (SCpnt == *SCpntp2)
+	*SCpntp2 = NULL;
+}
+
+/*
+ * Prototype: int acornscsi_abort (Scsi_Cmnd *SCpnt)
+ * Purpose  : abort a command on this host
+ * Params   : SCpnt - command to abort
+ * Returns  : one of SCSI_ABORT_ macros
+ */
+int acornscsi_abort (Scsi_Cmnd *SCpnt)
+{
+    AS_Host *host = (AS_Host *) SCpnt->host->hostdata;
+    int result = SCSI_ABORT_NOT_RUNNING;
+
+    host->stats.aborts += 1;
+
+#if (DEBUG & DEBUG_ABORT)
+    {
+	int asr, ssr;
+	asr = sbic_arm_read (host->scsi.io_port, ASR);
+	ssr = sbic_arm_read (host->scsi.io_port, SSR);
+
+	printk (KERN_WARNING "acornscsi_abort: ");
+	print_sbic_status(asr, ssr, host->scsi.phase);
+	acornscsi_dumplog (host, SCpnt->target);
+    }
+#endif
+
+    if (queue_removecmd (&host->queues.issue, SCpnt)) {
+	SCpnt->result = DID_ABORT << 16;
+	SCpnt->scsi_done (SCpnt);
+#if (DEBUG & DEBUG_ABORT)
+	printk ("scsi%d: command on issue queue\n", host->host->host_no);
+#endif
+	result = SCSI_ABORT_SUCCESS;
+    } else if (queue_cmdonqueue (&host->queues.disconnected, SCpnt)) {
+	printk ("scsi%d: command on disconnected queue\n", host->host->host_no);
+	result = SCSI_ABORT_SNOOZE;
+    } else if (host->SCpnt == SCpnt) {
+	acornscsi_abortcmd (host, host->SCpnt->tag);
+	printk ("scsi%d: command executing\n", host->host->host_no);
+	result = SCSI_ABORT_SNOOZE;
+    } else if (host->origSCpnt == SCpnt) {
+	host->origSCpnt = NULL;
+	SCpnt->result = DID_ABORT << 16;
+	SCpnt->scsi_done (SCpnt);
+#if (DEBUG & DEBUG_ABORT)
+	printk ("scsi%d: command waiting for execution\n", host->host->host_no);
+#endif
+	result = SCSI_ABORT_SUCCESS;
+    }
+
+    if (result == SCSI_ABORT_NOT_RUNNING) {
+	printk ("scsi%d: abort(): command not running\n", host->host->host_no);
+	acornscsi_dumplog (host, SCpnt->target);
+#if (DEBUG & DEBUG_ABORT)
+	result = SCSI_ABORT_SNOOZE;
+#endif
+    }
+    return result;
+}
+
+/*
+ * Prototype: int acornscsi_reset (Scsi_Cmnd *SCpnt, unsigned int reset_flags)
+ * Purpose  : reset a command on this host/reset this host
+ * Params   : SCpnt  - command causing reset
+ *	      result - what type of reset to perform
+ * Returns  : one of SCSI_RESET_ macros
+ */
+int acornscsi_reset (Scsi_Cmnd *SCpnt, unsigned int reset_flags)
+{
+    AS_Host *host = (AS_Host *)SCpnt->host->hostdata;
+    Scsi_Cmnd *SCptr;
+    
+    host->stats.resets += 1;
+
+#if (DEBUG & DEBUG_RESET)
+    {
+	int asr, ssr;
+
+	asr = sbic_arm_read (host->scsi.io_port, ASR);
+	ssr = sbic_arm_read (host->scsi.io_port, SSR);
+
+	printk (KERN_WARNING "acornscsi_reset: ");
+	print_sbic_status(asr, ssr, host->scsi.phase);
+	acornscsi_dumplog (host, SCpnt->target);
+    }
+#endif
+
+    acornscsi_dma_stop (host);
+
+    SCptr = host->SCpnt;
+
+    /*
+     * do hard reset.  This resets all devices on this host, and so we
+     * must set the reset status on all commands.
+     */
+    acornscsi_resetcard (host);
+
+    /*
+     * report reset on commands current connected/disconnected
+     */
+    acornscsi_reportstatus (&host->SCpnt, &SCptr, DID_RESET);
+
+    while ((SCptr = queue_remove (&host->queues.disconnected)) != NULL)
+	acornscsi_reportstatus (&SCptr, &SCpnt, DID_RESET);
+
+    if (SCpnt) {
+	SCpnt->result = DID_RESET << 16;
+	SCpnt->scsi_done (SCpnt);
+    }
+while (1);
+    return SCSI_RESET_BUS_RESET | SCSI_RESET_HOST_RESET | SCSI_RESET_SUCCESS;
+}
+
+/*==============================================================================================
+ * initialisation & miscellaneous support
+ */
+static struct expansion_card *ecs[MAX_ECARDS];
+
+/*
+ * Prototype: void acornscsi_init (AS_Host *host)
+ * Purpose  : initialise the AS_Host structure for one interface & setup hardware
+ * Params   : host - host to setup
+ */
+static
+void acornscsi_init (AS_Host *host)
+{
+    memset (&host->stats, 0, sizeof (host->stats));
+    queue_initialise (&host->queues.issue);
+    queue_initialise (&host->queues.disconnected);
+    msgqueue_initialise (&host->scsi.msgs);
+
+    acornscsi_resetcard (host);
+}
+
+int acornscsi_detect(Scsi_Host_Template * tpnt)
+{
+    static const card_ids acornscsi_cids[] = { ACORNSCSI_LIST, { 0xffff, 0xffff } };
+    int i, count = 0;
+    struct Scsi_Host *instance;
+    AS_Host *host;
+
+    tpnt->proc_dir = &proc_scsi_acornscsi;
+
+    for (i = 0; i < MAX_ECARDS; i++)
+	ecs[i] = NULL;
+
+    ecard_startfind ();
+
+    while(1) {
+	ecs[count] = ecard_find(0, acornscsi_cids);
+	if (!ecs[count])
+	    break;
+
+	if (ecs[count]->irq == 0xff) {
+	    printk ("scsi: WD33C93 does not have IRQ enabled - ignoring\n");
+	    continue;
+	}
+
+	ecard_claim(ecs[count]); /* Must claim here - card produces irq on reset */
+
+	instance = scsi_register (tpnt, sizeof(AS_Host));
+	host = (AS_Host *)instance->hostdata;
+
+	instance->io_port = ecard_address (ecs[count], ECARD_MEMC, 0);
+	instance->irq = ecs[count]->irq;
+
+	host->host		= instance;
+	host->scsi.io_port	= ioaddr (instance->io_port + 0x800);
+	host->scsi.irq		= instance->irq;
+	host->card.io_intr	= POD_SPACE(instance->io_port) + 0x800;
+	host->card.io_page	= POD_SPACE(instance->io_port) + 0xc00;
+	host->card.io_ram	= ioaddr (instance->io_port);
+	host->dma.io_port	= instance->io_port + 0xc00;
+	host->dma.io_intr_clear = POD_SPACE(instance->io_port) + 0x800;
+
+	request_region (instance->io_port + 0x800,  2, "acornscsi(sbic)");
+	request_region (host->card.io_intr,  1, "acornscsi(intr)");
+	request_region (host->card.io_page,  1, "acornscsi(page)");
+#ifdef USE_DMAC
+	request_region (host->dma.io_port, 256, "acornscsi(dmac)");
+#endif
+	request_region (instance->io_port, 2048, "acornscsi(ram)");
+
+	if (request_irq(host->scsi.irq, acornscsi_intr, SA_INTERRUPT, "acornscsi", host)) {
+	    printk(KERN_CRIT "scsi%d: IRQ%d not free, interrupts disabled\n",
+		instance->host_no, host->scsi.irq);
+	    host->scsi.irq = NO_IRQ;
+	}
+
+	acornscsi_init (host);
+
+	++count;
+    }
+    return count;
+}
+
+/*
+ * Function: int acornscsi_release (struct Scsi_Host *host)
+ * Purpose : release all resources used by this adapter
+ * Params  : host - driver structure to release
+ * Returns : nothing of any consequence
+ */
+int acornscsi_release (struct Scsi_Host *instance)
+{
+    AS_Host *host = (AS_Host *)instance->hostdata;
+    int i;
+
+    /*
+     * Put card into RESET state
+     */
+    outb (0x80, host->card.io_page);
+
+    if (host->scsi.irq != NO_IRQ)
+	free_irq (host->scsi.irq, host);
+
+    release_region (instance->io_port + 0x800, 2);
+    release_region (host->card.io_intr, 1);
+    release_region (host->card.io_page, 1);
+    release_region (host->dma.io_port, 256);
+    release_region (instance->io_port, 2048);
+
+    for (i = 0; i < MAX_ECARDS; i++)
+	if (ecs[i] && instance->io_port == ecard_address (ecs[i], ECARD_MEMC, 0))
+	    ecard_release (ecs[i]);
+
+    msgqueue_free (&host->scsi.msgs);
+    queue_free (&host->queues.disconnected);
+    queue_free (&host->queues.issue);
+
+    return 0;
+}
+
+/*
+ * Function: char *acornscsi_info (struct Scsi_Host *host)
+ * Purpose : return a string describing this interface
+ * Params  : host - host to give information on
+ * Returns : a constant string
+ */
+const
+char *acornscsi_info(struct Scsi_Host *host)
+{
+    static char string[100], *p;
+
+    p = string;
+    
+    p += sprintf (string, "%s at port %X irq %d v%d.%d.%d"
+#ifdef CONFIG_SCSI_ACORNSCSI_SYNC
+    " SYNC"
+#endif
+#ifdef CONFIG_SCSI_ACORNSCSI_TAGGED_QUEUE
+    " TAG"
+#endif
+#ifdef CONFIG_SCSI_ACORNSCSI_LINK
+    " LINK"
+#endif
+#if (DEBUG & DEBUG_NO_WRITE)
+    " NOWRITE ("NO_WRITE_STR")"
+#endif
+		, host->hostt->name, host->io_port, host->irq,
+		VER_MAJOR, VER_MINOR, VER_PATCH);
+    return string;
+}
+
+int acornscsi_proc_info(char *buffer, char **start, off_t offset,
+			int length, int host_no, int inout)
+{
+    int pos, begin = 0, devidx;
+    struct Scsi_Host *instance = scsi_hostlist;
+    Scsi_Device *scd;
+    AS_Host *host;
+    char *p = buffer;
+
+    for (instance = scsi_hostlist;
+	    instance && instance->host_no != host_no;
+		instance = instance->next);
+
+    if (inout == 1 || !instance)
+	return -EINVAL;
+
+    host  = (AS_Host *)instance->hostdata;
+    
+    p += sprintf (p, "AcornSCSI driver v%d.%d.%d"
+#ifdef CONFIG_SCSI_ACORNSCSI_SYNC
+    " SYNC"
+#endif
+#ifdef CONFIG_SCSI_ACORNSCSI_TAGGED_QUEUE
+    " TAG"
+#endif
+#ifdef CONFIG_SCSI_ACORNSCSI_LINK
+    " LINK"
+#endif
+#if (DEBUG & DEBUG_NO_WRITE)
+    " NOWRITE ("NO_WRITE_STR")"
+#endif
+		"\n\n", VER_MAJOR, VER_MINOR, VER_PATCH);
+
+    p += sprintf (p,	"SBIC: WD33C93A  Address: %08X  IRQ : %d\n",
+			host->scsi.io_port, host->scsi.irq);
+#ifdef USE_DMAC
+    p += sprintf (p,	"DMAC: uPC71071  Address: %08X  IRQ : %d\n\n",
+			host->dma.io_port, host->scsi.irq);
+#endif
+
+    p += sprintf (p,	"Statistics:\n",
+			"Queued commands: %-10d    Issued commands: %-10d\n"
+			"Done commands  : %-10d    Reads          : %-10d\n"
+			"Writes         : %-10d    Others         : %-10d\n"
+			"Disconnects    : %-10d    Aborts         : %-10d\n"
+			"Resets         : %-10d\n\nLast phases:",
+			host->stats.queues,		host->stats.removes,
+			host->stats.fins,		host->stats.reads,
+			host->stats.writes,		host->stats.miscs,
+			host->stats.disconnects,	host->stats.aborts,
+			host->stats.resets);
+
+    for (devidx = 0; devidx < 9; devidx ++) {
+	unsigned int statptr, prev;
+
+	p += sprintf (p, "\n%c:", devidx == 8 ? 'H' : ('0' + devidx));
+	statptr = status_ptr[devidx] - 10;
+
+	if ((signed int)statptr < 0)
+	    statptr += 16;
+
+	prev = status[devidx][statptr].when;
+
+	for (; statptr != status_ptr[devidx]; statptr = (statptr + 1) & 15) {
+	    if (status[devidx][statptr].when) {
+		p += sprintf (p, "%c%02X:%02X+%2ld",
+			status[devidx][statptr].irq ? '-' : ' ',
+			status[devidx][statptr].ph,
+			status[devidx][statptr].ssr,
+			(status[devidx][statptr].when - prev) < 100 ?
+				(status[devidx][statptr].when - prev) : 99);
+		prev = status[devidx][statptr].when;
+	    }
+	}
+    }
+
+    p += sprintf (p, "\nAttached devices:%s\n", instance->host_queue ? "" : " none");
+
+    for (scd = instance->host_queue; scd; scd = scd->next) {
+	int len;
+
+	proc_print_scsidevice (scd, p, &len, 0);
+	p += len;
+
+	p += sprintf (p, "Extensions: ");
+
+	if (scd->tagged_supported)
+	    p += sprintf (p, "TAG %sabled [%d] ",
+			  scd->tagged_queue ? "en" : "dis", scd->current_tag);
+	p += sprintf (p, "\nTransfers: ");
+	if (host->device[scd->id].sync_xfer & 15)
+	    p += sprintf (p, "sync, offset %d, %d ns\n",
+			  host->device[scd->id].sync_xfer & 15,
+			  acornscsi_getperiod (host->device[scd->id].sync_xfer));
+	else
+	    p += sprintf (p, "async\n");
+
+	pos = p - buffer;
+	if (pos + begin < offset) {
+	    begin += pos;
+	    p = buffer;
+	}
+	pos = p - buffer;
+	if (pos + begin > offset + length)
+	    break;
+    }
+
+    pos = p - buffer;
+
+    *start = buffer + (offset - begin);
+    pos -= offset - begin;
+
+    if (pos > length)
+	pos = length;
+
+    return pos;
+}
+
+#ifdef MODULE
+
+Scsi_Host_Template driver_template = ACORNSCSI_3;
+
+#include "../../scsi/scsi_module.c"
+#endif
diff -u --recursive --new-file v2.1.87/linux/drivers/acorn/scsi/acornscsi.h linux/drivers/acorn/scsi/acornscsi.h
--- v2.1.87/linux/drivers/acorn/scsi/acornscsi.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/acorn/scsi/acornscsi.h	Sun Dec 28 09:06:25 1997
@@ -0,0 +1,378 @@
+#ifndef ACORNSCSI_H
+#define ACORNSCSI_H
+
+#ifndef ASM
+extern int acornscsi_detect (Scsi_Host_Template *);
+extern int acornscsi_release (struct Scsi_Host *);
+extern const char *acornscsi_info (struct Scsi_Host *);
+extern int acornscsi_queuecmd (Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
+extern int acornscsi_abort (Scsi_Cmnd *);
+extern int acornscsi_reset (Scsi_Cmnd *, unsigned int);
+extern int acornscsi_proc_info (char *, char **, off_t, int, int, int);
+extern int acornscsi_biosparam (Disk *, kdev_t, int []);
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+#ifndef CMD_PER_LUN
+#define CMD_PER_LUN 2
+#endif
+
+#ifndef CAN_QUEUE
+#define CAN_QUEUE 16
+#endif
+
+#ifndef PROC_SCSI_AKA30
+#include "linux/proc_fs.h"
+#define PROC_SCSI_AKA30 PROC_SCSI_EATA
+#endif
+
+#include <scsi/scsicam.h>
+
+#define ACORNSCSI_3 {							\
+proc_info:		acornscsi_proc_info,				\
+name:			"AcornSCSI",					\
+detect:			acornscsi_detect,				\
+release:		acornscsi_release,	/* Release */		\
+info:			acornscsi_info,					\
+queuecommand:		acornscsi_queuecmd,				\
+abort:			acornscsi_abort,				\
+reset:			acornscsi_reset,				\
+bios_param:		scsicam_bios_param,				\
+can_queue:		CAN_QUEUE,		/* can_queue */		\
+this_id:		7,			/* this id */		\
+sg_tablesize:		SG_ALL,			/* sg_tablesize */	\
+cmd_per_lun:		CMD_PER_LUN,		/* cmd_per_lun */	\
+unchecked_isa_dma:	0,			/* unchecked isa dma */	\
+use_clustering:		DISABLE_CLUSTERING				\
+	}
+
+#ifndef HOSTS_C
+
+/* SBIC registers */
+#define OWNID			0
+#define OWNID_FS1		(1<<7)
+#define OWNID_FS2		(1<<6)
+#define OWNID_EHP		(1<<4)
+#define OWNID_EAF		(1<<3)
+
+#define CTRL			1
+#define CTRL_DMAMODE		(1<<7)
+#define CTRL_DMADBAMODE		(1<<6)
+#define CTRL_DMABURST		(1<<5)
+#define CTRL_DMAPOLLED		0
+#define CTRL_HHP		(1<<4)
+#define CTRL_EDI		(1<<3)
+#define CTRL_IDI		(1<<2)
+#define CTRL_HA			(1<<1)
+#define CTRL_HSP		(1<<0)
+
+#define TIMEOUT			2
+#define TOTSECTS		3
+#define TOTHEADS		4
+#define TOTCYLH			5
+#define TOTCYLL			6
+#define LOGADDRH		7
+#define LOGADDRM2		8
+#define LOGADDRM1		9
+#define LOGADDRL		10
+#define SECTORNUM		11
+#define HEADNUM			12
+#define CYLH			13
+#define CYLL			14
+#define TARGETLUN		15
+#define TARGETLUN_TLV		(1<<7)
+#define TARGETLUN_DOK		(1<<6)
+
+#define CMNDPHASE		16
+#define SYNCHTRANSFER		17
+#define SYNCHTRANSFER_OF0	0x00
+#define SYNCHTRANSFER_OF1	0x01
+#define SYNCHTRANSFER_OF2	0x02
+#define SYNCHTRANSFER_OF3	0x03
+#define SYNCHTRANSFER_OF4	0x04
+#define SYNCHTRANSFER_OF5	0x05
+#define SYNCHTRANSFER_OF6	0x06
+#define SYNCHTRANSFER_OF7	0x07
+#define SYNCHTRANSFER_OF8	0x08
+#define SYNCHTRANSFER_OF9	0x09
+#define SYNCHTRANSFER_OF10	0x0A
+#define SYNCHTRANSFER_OF11	0x0B
+#define SYNCHTRANSFER_OF12	0x0C
+#define SYNCHTRANSFER_8DBA	0x00
+#define SYNCHTRANSFER_2DBA	0x20
+#define SYNCHTRANSFER_3DBA	0x30
+#define SYNCHTRANSFER_4DBA	0x40
+#define SYNCHTRANSFER_5DBA	0x50
+#define SYNCHTRANSFER_6DBA	0x60
+#define SYNCHTRANSFER_7DBA	0x70
+
+#define TRANSCNTH		18
+#define TRANSCNTM		19
+#define TRANSCNTL		20
+#define DESTID			21
+#define DESTID_SCC		(1<<7)
+#define DESTID_DPD		(1<<6)
+
+#define SOURCEID		22
+#define SOURCEID_ER		(1<<7)
+#define SOURCEID_ES		(1<<6)
+#define SOURCEID_DSP		(1<<5)
+#define SOURCEID_SIV		(1<<4)
+
+#define SSR			23
+#define CMND			24
+#define CMND_RESET		0x00
+#define CMND_ABORT		0x01
+#define CMND_ASSERTATN		0x02
+#define CMND_NEGATEACK		0x03
+#define CMND_DISCONNECT		0x04
+#define CMND_RESELECT		0x05
+#define CMND_SELWITHATN		0x06
+#define CMND_SELECT		0x07
+#define CMND_SELECTATNTRANSFER	0x08
+#define CMND_SELECTTRANSFER	0x09
+#define CMND_RESELECTRXDATA	0x0A
+#define CMND_RESELECTTXDATA	0x0B
+#define CMND_WAITFORSELRECV	0x0C
+#define CMND_SENDSTATCMD	0x0D
+#define CMND_SENDDISCONNECT	0x0E
+#define CMND_SETIDI		0x0F
+#define CMND_RECEIVECMD		0x10
+#define CMND_RECEIVEDTA		0x11
+#define CMND_RECEIVEMSG		0x12
+#define CMND_RECEIVEUSP		0x13
+#define CMND_SENDCMD		0x14
+#define CMND_SENDDATA		0x15
+#define CMND_SENDMSG		0x16
+#define CMND_SENDUSP		0x17
+#define CMND_TRANSLATEADDR	0x18
+#define CMND_XFERINFO		0x20
+#define CMND_SBT		(1<<7)
+
+#define DATA			25
+#define ASR			26
+#define ASR_INT			(1<<7)
+#define ASR_LCI			(1<<6)
+#define ASR_BSY			(1<<5)
+#define ASR_CIP			(1<<4)
+#define ASR_PE			(1<<1)
+#define ASR_DBR			(1<<0)
+
+/* DMAC registers */
+#define INIT			0x00
+#define INIT_8BIT		(1)
+
+#define CHANNEL			0x80
+#define CHANNEL_0		0x00
+#define CHANNEL_1		0x01
+#define CHANNEL_2		0x02
+#define CHANNEL_3		0x03
+
+#define TXCNTLO			0x01
+#define TXCNTHI			0x81
+#define TXADRLO			0x02
+#define TXADRMD			0x82
+#define TXADRHI			0x03
+
+#define DEVCON0			0x04
+#define DEVCON0_AKL		(1<<7)
+#define DEVCON0_RQL		(1<<6)
+#define DEVCON0_EXW		(1<<5)
+#define DEVCON0_ROT		(1<<4)
+#define DEVCON0_CMP		(1<<3)
+#define DEVCON0_DDMA		(1<<2)
+#define DEVCON0_AHLD		(1<<1)
+#define DEVCON0_MTM		(1<<0)
+
+#define DEVCON1			0x84
+#define DEVCON1_WEV		(1<<1)
+#define DEVCON1_BHLD		(1<<0)
+
+#define MODECON			0x05
+#define MODECON_WOED		0x01
+#define MODECON_VERIFY		0x00
+#define MODECON_READ		0x04
+#define MODECON_WRITE		0x08
+#define MODECON_AUTOINIT	0x10
+#define MODECON_ADDRDIR		0x20
+#define MODECON_DEMAND		0x00
+#define MODECON_SINGLE		0x40
+#define MODECON_BLOCK		0x80
+#define MODECON_CASCADE		0xC0
+
+#define STATUS			0x85
+#define STATUS_TC0		(1<<0)
+#define STATUS_RQ0		(1<<4)
+
+#define TEMPLO			0x06
+#define TEMPHI			0x86
+#define REQREG			0x07
+#define MASKREG			0x87
+#define MASKREG_M0		0x01
+#define MASKREG_M1		0x02
+#define MASKREG_M2		0x04
+#define MASKREG_M3		0x08
+
+/* miscellaneous internal variables */
+
+#define POD_SPACE(x)	((x) + 0xd0000)
+#define MASK_ON		(MASKREG_M3|MASKREG_M2|MASKREG_M1|MASKREG_M0)
+#define MASK_OFF	(MASKREG_M3|MASKREG_M2|MASKREG_M1)
+
+#define min(x,y) ((x) < (y) ? (x) : (y))
+#define max(x,y) ((x) < (y) ? (y) : (x))
+
+/*
+ * SCSI driver phases
+ */
+typedef enum {
+    PHASE_IDLE,					/* we're not planning on doing anything	 */
+    PHASE_CONNECTING,				/* connecting to a target		 */
+    PHASE_CONNECTED,				/* connected to a target		 */
+    PHASE_MSGOUT,				/* message out to device		 */
+    PHASE_RECONNECTED,				/* reconnected				 */
+    PHASE_COMMANDPAUSED,			/* command partly sent			 */
+    PHASE_COMMAND,				/* command all sent			 */
+    PHASE_DATAOUT,				/* data out to device			 */
+    PHASE_DATAIN,				/* data in from device			 */
+    PHASE_STATUSIN,				/* status in from device		 */
+    PHASE_MSGIN,				/* message in from device		 */
+    PHASE_DONE,					/* finished				 */
+    PHASE_ABORTED,				/* aborted				 */
+    PHASE_DISCONNECT,				/* disconnecting			 */
+} phase_t;
+
+/*
+ * After interrupt, what to do now
+ */
+typedef enum {
+    INTR_IDLE,					/* not expecting another IRQ		 */
+    INTR_NEXT_COMMAND,				/* start next command			 */
+    INTR_PROCESSING,				/* interrupt routine still processing	 */
+} intr_ret_t;
+
+/*
+ * DMA direction
+ */
+typedef enum {
+    DMA_OUT,					/* DMA from memory to chip		*/
+    DMA_IN					/* DMA from chip to memory		*/
+} dmadir_t;
+
+/*
+ * Synchronous transfer state
+ */
+typedef enum {					/* Synchronous transfer state		*/
+    SYNC_ASYNCHRONOUS,				/* don't negociate synchronous transfers*/
+    SYNC_NEGOCIATE,				/* start negociation			*/
+    SYNC_SENT_REQUEST,				/* sent SDTR message			*/
+    SYNC_COMPLETED,				/* received SDTR reply			*/
+} syncxfer_t;
+
+/*
+ * Command type
+ */
+typedef enum {					/* command type				*/
+    CMD_READ,					/* READ_6, READ_10, READ_12		*/
+    CMD_WRITE,					/* WRITE_6, WRITE_10, WRITE_12		*/
+    CMD_MISC,					/* Others				*/
+} cmdtype_t;
+
+/*
+ * Data phase direction
+ */
+typedef enum {					/* Data direction			*/
+    DATADIR_IN,					/* Data in phase expected		*/
+    DATADIR_OUT					/* Data out phase expected		*/
+} datadir_t;
+
+#include "queue.h"
+#include "msgqueue.h"
+
+/*
+ * AcornSCSI host specific data
+ */
+typedef struct acornscsi_hostdata {
+    /* miscellaneous */
+    struct Scsi_Host	*host;			/* host					*/
+    Scsi_Cmnd		*SCpnt;			/* currently processing command		*/
+    Scsi_Cmnd		*origSCpnt;		/* original connecting command		*/
+
+    /* driver information */
+    struct {
+	unsigned int	io_port;		/* base address of WD33C93		*/
+	unsigned char	irq;			/* interrupt				*/
+	phase_t		phase;			/* current phase			*/
+
+	struct {
+	    unsigned char	target;		/* reconnected target			*/
+	    unsigned char	lun;		/* reconnected lun			*/
+	    unsigned char	tag;		/* reconnected tag			*/
+	} reconnected;
+
+	Scsi_Pointer	SCp;			/* current commands data pointer	*/
+
+	MsgQueue_t	msgs;
+
+	unsigned short	last_message;		/* last message to be sent		*/
+	unsigned char	disconnectable:1;	/* this command can be disconnected	*/
+	unsigned char	interrupt:1;		/* interrupt active			*/
+    } scsi;
+
+    /* statistics information */
+    struct {
+	unsigned int	queues;
+	unsigned int	removes;
+	unsigned int	fins;
+	unsigned int	reads;
+	unsigned int	writes;
+	unsigned int	miscs;
+	unsigned int	disconnects;
+	unsigned int	aborts;
+	unsigned int	resets;
+    } stats;
+
+    /* queue handling */
+    struct {
+	Queue_t		issue;			/* issue queue				*/
+	Queue_t		disconnected;		/* disconnected command queue		*/
+    } queues;
+
+    /* per-device info */
+    struct {
+	unsigned char	sync_xfer;		/* synchronous transfer (SBIC value)	*/
+	syncxfer_t	sync_state;		/* sync xfer negociation state		*/
+	unsigned char	disconnect_ok:1;	/* device can disconnect		*/
+    } device[8];
+    unsigned char	busyluns[8];		/* array of bits indicating LUNs busy	*/
+
+    /* DMA info */
+    struct {
+	unsigned int	io_port;		/* base address of DMA controller	*/
+	unsigned int	io_intr_clear;		/* address of DMA interrupt clear	*/
+	unsigned int	free_addr;		/* next free address			*/
+	unsigned int	start_addr;		/* start address of current transfer	*/
+	dmadir_t	direction;		/* dma direction			*/
+	unsigned int	transferred;		/* number of bytes transferred		*/
+	unsigned int	xfer_start;		/* scheduled DMA transfer start		*/
+	unsigned int	xfer_length;		/* scheduled DMA transfer length	*/
+	char		*xfer_ptr;		/* pointer to area			*/
+	unsigned char	xfer_required:1;	/* set if we need to transfer something	*/
+	unsigned char	xfer_setup:1;		/* set if DMA is setup			*/
+    } dma;
+
+    /* card info */
+    struct {
+	unsigned int	io_intr;		/* base address of interrupt id reg	*/
+	unsigned int	io_page;		/* base address of page reg		*/
+	unsigned int	io_ram;			/* base address of RAM access		*/
+	unsigned char	page_reg;		/* current setting of page reg		*/
+    } card;
+} AS_Host;
+
+#endif /* ndef HOSTS_C */
+
+#endif /* ndef ASM */
+#endif /* ACORNSCSI_H */
diff -u --recursive --new-file v2.1.87/linux/drivers/acorn/scsi/cumana_1.c linux/drivers/acorn/scsi/cumana_1.c
--- v2.1.87/linux/drivers/acorn/scsi/cumana_1.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/acorn/scsi/cumana_1.c	Fri Feb  6 16:22:47 1998
@@ -0,0 +1,361 @@
+#define AUTOSENSE
+#define PSEUDO_DMA
+
+/*
+ * Generic Generic NCR5380 driver
+ *
+ * Copyright 1995, Russell King
+ *
+ * ALPHA RELEASE 1.
+ *
+ * For more information, please consult
+ *
+ * NCR 5380 Family
+ * SCSI Protocol Controller
+ * Databook
+ *
+ * NCR Microelectronics
+ * 1635 Aeroplaza Drive
+ * Colorado Springs, CO 80916
+ * 1+ (719) 578-3400
+ * 1+ (800) 334-5454
+ */
+
+
+/*
+ * Options :
+ *
+ * PARITY - enable parity checking.  Not supported.
+ *
+ * SCSI2 - enable support for SCSI-II tagged queueing.  Untested.
+ *
+ * USLEEP - enable support for devices that don't disconnect.  Untested.
+ */
+
+/*
+ * $Log: cumana_NCR5380.c,v $
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+#include <linux/blk.h>
+
+#include <asm/ecard.h>
+#include <asm/io.h>
+#include <asm/system.h>
+
+#include "../../scsi/scsi.h"
+#include "../../scsi/hosts.h"
+#include "cumana_1.h"
+#include "../../scsi/NCR5380.h"
+#include "../../scsi/constants.h"
+
+static const card_ids cumanascsi_cids[] = {
+	{ MANU_CUMANA, PROD_CUMANA_SCSI_1 },
+	{ 0xffff, 0xffff }
+};
+
+static struct proc_dir_entry proc_scsi_cumana1 = {
+	PROC_SCSI_T128, 12, "CumanaSCSI-1", S_IFDIR | S_IRUGO, S_IXUGO, 2
+};
+
+/*
+ * Function : cumanascsi_setup(char *str, int *ints)
+ *
+ * Purpose : LILO command line initialization of the overrides array,
+ *
+ * Inputs : str - unused, ints - array of integer parameters with ints[0]
+ *	equal to the number of ints.
+ *
+ */
+
+void cumanascsi_setup(char *str, int *ints) {
+}
+
+#define CUMANA_ADDRESS(card) (ecard_address((card), ECARD_IOC, ECARD_SLOW) + 0x800)
+#define CUMANA_IRQ(card)     ((card)->irq)
+/*
+ * Function : int cumanascsi_detect(Scsi_Host_Template * tpnt)
+ *
+ * Purpose : initializes cumana NCR5380 driver based on the
+ *	command line / compile time port and irq definitions.
+ *
+ * Inputs : tpnt - template for this SCSI adapter.
+ *
+ * Returns : 1 if a host adapter was found, 0 if not.
+ *
+ */
+static struct expansion_card *ecs[4];
+ 
+int cumanascsi_detect(Scsi_Host_Template * tpnt)
+{
+    int count = 0;
+    struct Scsi_Host *instance;
+
+    tpnt->proc_dir = &proc_scsi_cumana1;
+
+    memset (ecs, 0, sizeof (ecs));
+
+    while(1) {
+    	if((ecs[count] = ecard_find(0, cumanascsi_cids)) == NULL)
+    		break;
+
+        instance = scsi_register (tpnt, sizeof(struct NCR5380_hostdata));
+        instance->io_port = CUMANA_ADDRESS(ecs[count]);
+	instance->irq = CUMANA_IRQ(ecs[count]);
+
+	NCR5380_init(instance, 0);
+	ecard_claim(ecs[count]);
+
+	instance->n_io_port = 255;
+	request_region (instance->io_port, instance->n_io_port, "CumanaSCSI-1");
+
+        ((struct NCR5380_hostdata *)instance->hostdata)->ctrl = 0;
+        outb(0x00, instance->io_port - 577);
+
+	if (instance->irq != IRQ_NONE)
+	    if (request_irq(instance->irq, cumanascsi_intr, SA_INTERRUPT, "CumanaSCSI-1", NULL)) {
+		printk("scsi%d: IRQ%d not free, interrupts disabled\n",
+		    instance->host_no, instance->irq);
+		instance->irq = IRQ_NONE;
+	    }
+
+	if (instance->irq == IRQ_NONE) {
+	    printk("scsi%d: interrupts not enabled. for better interactive performance,\n", instance->host_no);
+	    printk("scsi%d: please jumper the board for a free IRQ.\n", instance->host_no);
+	}
+
+	printk("scsi%d: at port %X irq", instance->host_no, instance->io_port);
+	if (instance->irq == IRQ_NONE)
+	    printk ("s disabled");
+	else
+	    printk (" %d", instance->irq);
+	printk(" options CAN_QUEUE=%d CMD_PER_LUN=%d release=%d",
+	    CAN_QUEUE, CMD_PER_LUN, CUMANASCSI_PUBLIC_RELEASE);
+	printk("\nscsi%d:", instance->host_no);
+	NCR5380_print_options(instance);
+	printk("\n");
+
+	++count;
+    }
+    return count;
+}
+
+int cumanascsi_release (struct Scsi_Host *shpnt)
+{
+	int i;
+
+	if (shpnt->irq != IRQ_NONE)
+		free_irq (shpnt->irq, NULL);
+	if (shpnt->io_port)
+		release_region (shpnt->io_port, shpnt->n_io_port);
+
+	for (i = 0; i < 4; i++)
+		if (shpnt->io_port == CUMANA_ADDRESS(ecs[i]))
+			ecard_release (ecs[i]);
+	return 0;
+}
+
+const char * cumanascsi_info (struct Scsi_Host *spnt) {
+    return "";
+}
+
+#ifdef NOT_EFFICIENT
+#define CTRL(p,v)     outb(*ctrl = (v), (p) - 577)
+#define STAT(p)       inb((p)+1)
+#define IN(p)         inb((p))
+#define OUT(v,p)      outb((v), (p))
+#else
+#define CTRL(p,v)	(p[-2308] = (*ctrl = (v)))
+#define STAT(p)		(p[4])
+#define IN(p)		(*(p))
+#define IN2(p)		((unsigned short)(*(volatile unsigned long *)(p)))
+#define OUT(v,p)	(*(p) = (v))
+#define OUT2(v,p)	(*((volatile unsigned long *)(p)) = (v))
+#endif
+#define L(v)		(((v)<<16)|((v) & 0x0000ffff))
+#define H(v)		(((v)>>16)|((v) & 0xffff0000))
+
+static inline int NCR5380_pwrite(struct Scsi_Host *instance, unsigned char *addr,
+              int len)
+{
+  int *ctrl = &((struct NCR5380_hostdata *)instance->hostdata)->ctrl;
+  int oldctrl = *ctrl;
+  unsigned long *laddr;
+#ifdef NOT_EFFICIENT
+  int iobase = instance->io_port;
+  int dma_io = iobase & ~(0x3C0000>>2);
+#else
+  volatile unsigned char *iobase = (unsigned char *)ioaddr(instance->io_port);
+  volatile unsigned char *dma_io = (unsigned char *)((int)iobase & ~0x3C0000);
+#endif
+
+  if(!len) return 0;
+
+  CTRL(iobase, 0x02);
+  laddr = (unsigned long *)addr;
+  while(len >= 32)
+  {
+    int status;
+    unsigned long v;
+    status = STAT(iobase);
+    if(status & 0x80)
+      goto end;
+    if(!(status & 0x40))
+      continue;
+    v=*laddr++; OUT2(L(v),dma_io); OUT2(H(v),dma_io);
+    v=*laddr++; OUT2(L(v),dma_io); OUT2(H(v),dma_io);
+    v=*laddr++; OUT2(L(v),dma_io); OUT2(H(v),dma_io);
+    v=*laddr++; OUT2(L(v),dma_io); OUT2(H(v),dma_io);
+    v=*laddr++; OUT2(L(v),dma_io); OUT2(H(v),dma_io);
+    v=*laddr++; OUT2(L(v),dma_io); OUT2(H(v),dma_io);
+    v=*laddr++; OUT2(L(v),dma_io); OUT2(H(v),dma_io);
+    v=*laddr++; OUT2(L(v),dma_io); OUT2(H(v),dma_io);
+    len -= 32;
+    if(len == 0)
+      break;
+  }
+
+  addr = (unsigned char *)laddr;
+  CTRL(iobase, 0x12);
+  while(len > 0)
+  {
+    int status;
+    status = STAT(iobase);
+    if(status & 0x80)
+      goto end;
+    if(status & 0x40)
+    {
+      OUT(*addr++, dma_io);
+      if(--len == 0)
+        break;
+    }
+
+    status = STAT(iobase);
+    if(status & 0x80)
+      goto end;
+    if(status & 0x40)
+    {
+      OUT(*addr++, dma_io);
+      if(--len == 0)
+        break;
+    }
+  }
+end:
+  CTRL(iobase, oldctrl|0x40);
+  return len;
+}
+
+static inline int NCR5380_pread(struct Scsi_Host *instance, unsigned char *addr,
+              int len)
+{
+  int *ctrl = &((struct NCR5380_hostdata *)instance->hostdata)->ctrl;
+  int oldctrl = *ctrl;
+  unsigned long *laddr;
+#ifdef NOT_EFFICIENT
+  int iobase = instance->io_port;
+  int dma_io = iobase & ~(0x3C0000>>2);
+#else
+  volatile unsigned char *iobase = (unsigned char *)ioaddr(instance->io_port);
+  volatile unsigned char *dma_io = (unsigned char *)((int)iobase & ~0x3C0000);
+#endif
+
+  if(!len) return 0;
+
+  CTRL(iobase, 0x00);
+  laddr = (unsigned long *)addr;
+  while(len >= 32)
+  {
+    int status;
+    status = STAT(iobase);
+    if(status & 0x80)
+      goto end;
+    if(!(status & 0x40))
+      continue;
+    *laddr++ = IN2(dma_io)|(IN2(dma_io)<<16);
+    *laddr++ = IN2(dma_io)|(IN2(dma_io)<<16);
+    *laddr++ = IN2(dma_io)|(IN2(dma_io)<<16);
+    *laddr++ = IN2(dma_io)|(IN2(dma_io)<<16);
+    *laddr++ = IN2(dma_io)|(IN2(dma_io)<<16);
+    *laddr++ = IN2(dma_io)|(IN2(dma_io)<<16);
+    *laddr++ = IN2(dma_io)|(IN2(dma_io)<<16);
+    *laddr++ = IN2(dma_io)|(IN2(dma_io)<<16);
+    len -= 32;
+    if(len == 0)
+      break;
+  }
+
+  addr = (unsigned char *)laddr;
+  CTRL(iobase, 0x10);
+  while(len > 0)
+  {
+    int status;
+    status = STAT(iobase);
+    if(status & 0x80)
+      goto end;
+    if(status & 0x40)
+    {
+      *addr++ = IN(dma_io);
+      if(--len == 0)
+        break;
+    }
+
+    status = STAT(iobase);
+    if(status & 0x80)
+      goto end;
+    if(status & 0x40)
+    {
+      *addr++ = IN(dma_io);
+      if(--len == 0)
+        break;
+    }
+  }
+end:
+  CTRL(iobase, oldctrl|0x40);
+  return len;
+}
+
+#undef STAT
+#undef CTRL
+#undef IN
+#undef OUT
+
+#define CTRL(p,v) outb(*ctrl = (v), (p) - 577)
+
+static char cumanascsi_read(struct Scsi_Host *instance, int reg)
+{
+  int iobase = instance->io_port;
+  int i;
+  int *ctrl = &((struct NCR5380_hostdata *)instance->hostdata)->ctrl;
+
+  CTRL(iobase, 0);
+  i = inb(iobase + 64 + reg);
+  CTRL(iobase, 0x40);
+
+  return i;
+}
+
+static void cumanascsi_write(struct Scsi_Host *instance, int reg, int value)
+{
+  int iobase = instance->io_port;
+  int *ctrl = &((struct NCR5380_hostdata *)instance->hostdata)->ctrl;
+
+  CTRL(iobase, 0);
+  outb(value, iobase + 64 + reg);
+  CTRL(iobase, 0x40);
+}
+
+#undef CTRL
+
+#include "../../scsi/NCR5380.c"
+
+#ifdef MODULE
+
+Scsi_Host_Template driver_template = CUMANA_NCR5380;
+
+#include "../../scsi/scsi_module.c"
+#endif
diff -u --recursive --new-file v2.1.87/linux/drivers/acorn/scsi/cumana_1.h linux/drivers/acorn/scsi/cumana_1.h
--- v2.1.87/linux/drivers/acorn/scsi/cumana_1.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/acorn/scsi/cumana_1.h	Sun Dec 28 09:15:22 1997
@@ -0,0 +1,102 @@
+/*
+ * Cumana Generic NCR5380 driver defines
+ *
+ * Copyright 1993, Drew Eckhardt
+ *	Visionary Computing
+ *	(Unix and Linux consulting and custom programming)
+ *	drew@colorado.edu
+ *      +1 (303) 440-4894
+ *
+ * ALPHA RELEASE 1.
+ *
+ * For more information, please consult
+ *
+ * NCR 5380 Family
+ * SCSI Protocol Controller
+ * Databook
+ *
+ * NCR Microelectronics
+ * 1635 Aeroplaza Drive
+ * Colorado Springs, CO 80916
+ * 1+ (719) 578-3400
+ * 1+ (800) 334-5454
+ */
+
+/*
+ * $Log: cumana_NCR5380.h,v $
+ */
+
+#ifndef CUMANA_NCR5380_H
+#define CUMANA_NCR5380_H
+
+#define CUMANASCSI_PUBLIC_RELEASE 1
+
+
+#ifndef ASM
+int cumanascsi_abort (Scsi_Cmnd *);
+int cumanascsi_detect (Scsi_Host_Template *);
+int cumanascsi_release (struct Scsi_Host *);
+const char *cumanascsi_info (struct Scsi_Host *);
+int cumanascsi_reset(Scsi_Cmnd *, unsigned int);
+int cumanascsi_queue_command (Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
+int cumanascsi_proc_info (char *buffer, char **start, off_t offset,
+			int length, int hostno, int inout);
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+#ifndef CMD_PER_LUN
+#define CMD_PER_LUN 2
+#endif
+
+#ifndef CAN_QUEUE
+#define CAN_QUEUE 16
+#endif
+
+#include <scsi/scsicam.h>
+
+#define CUMANA_NCR5380 {						\
+name:			"Cumana 16-bit SCSI",				\
+detect:			cumanascsi_detect,				\
+release:		cumanascsi_release,	/* Release */		\
+info:			cumanascsi_info,				\
+queuecommand:		cumanascsi_queue_command,			\
+abort:			cumanascsi_abort,			 	\
+reset:			cumanascsi_reset,				\
+bios_param:		scsicam_bios_param,	/* biosparam */		\
+can_queue:		CAN_QUEUE,		/* can queue */		\
+this_id:		7,			/* id */		\
+sg_tablesize:		SG_ALL,			/* sg_tablesize */	\
+cmd_per_lun:		CMD_PER_LUN,		/* cmd per lun */	\
+unchecked_isa_dma:	0,			/* unchecked_isa_dma */	\
+use_clustering:		DISABLE_CLUSTERING				\
+	}
+
+#ifndef HOSTS_C
+
+#define NCR5380_implementation_fields \
+    int port, ctrl
+
+#define NCR5380_local_declare() \
+        struct Scsi_Host *_instance
+
+#define NCR5380_setup(instance) \
+        _instance = instance
+
+#define NCR5380_read(reg) cumanascsi_read(_instance, reg)
+#define NCR5380_write(reg, value) cumanascsi_write(_instance, reg, value)
+
+#define NCR5380_intr cumanascsi_intr
+#define NCR5380_queue_command cumanascsi_queue_command
+#define NCR5380_abort cumanascsi_abort
+#define NCR5380_reset cumanascsi_reset
+#define NCR5380_proc_info cumanascsi_proc_info
+
+#define BOARD_NORMAL	0
+#define BOARD_NCR53C400	1
+
+#endif /* ndef HOSTS_C */
+#endif /* ndef ASM */
+#endif /* CUMANA_NCR5380_H */
+
diff -u --recursive --new-file v2.1.87/linux/drivers/acorn/scsi/cumana_2.c linux/drivers/acorn/scsi/cumana_2.c
--- v2.1.87/linux/drivers/acorn/scsi/cumana_2.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/acorn/scsi/cumana_2.c	Sun Feb 15 08:58:11 1998
@@ -0,0 +1,378 @@
+/*
+ * linux/arch/arm/drivers/scsi/cumana_2.c
+ *
+ * Copyright (C) 1997,1998 Russell King
+ *
+ * This driver is based on experimentation.  Hence, it may have made
+ * assumptions about the particular card that I have available, and
+ * may not be reliable!
+ *
+ * Changelog:
+ *  30-08-1997	RMK	0.0.0	Created, READONLY version
+ *  22-01-1998	RMK	0.0.1	Updated to 2.1.80
+ */
+
+#include <linux/module.h>
+#include <linux/blk.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/proc_fs.h>
+#include <linux/unistd.h>
+#include <linux/stat.h>
+
+#include <asm/delay.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/ecard.h>
+
+#include "../../scsi/sd.h"
+#include "../../scsi/hosts.h"
+#include "cumana_2.h"
+#include "fas216.h"
+
+/* Hmm - this should go somewhere else */
+#define BUS_ADDR(x) ((((unsigned long)(x)) << 2) + IO_BASE)
+
+/* Configuration */
+#define XTALFREQ		40
+#define INT_POLARITY		CTRL_INT_HIGH
+
+/*
+ * List of devices that the driver will recognise
+ */
+#define CUMANASCSI2_LIST	{ MANU_CUMANA, PROD_CUMANA_SCSI_2 }
+
+/*
+ * Version
+ */
+#define VER_MAJOR	0
+#define VER_MINOR	0
+#define VER_PATCH	1
+
+static struct expansion_card *ecs[MAX_ECARDS];
+
+static struct proc_dir_entry proc_scsi_cumanascsi_2 = {
+	PROC_SCSI_QLOGICFAS, 6, "cumanascs2",
+	S_IFDIR | S_IRUGO | S_IXUGO, 2
+};
+
+/*
+ * Function: void cumanascsi_2_intr (int irq, void *dev_id, struct pt_regs *regs)
+ * Purpose : handle interrupts from Cumana SCSI 2 card
+ * Params  : irq - interrupt number
+ *	     dev_id - user-defined (Scsi_Host structure)
+ *	     regs - processor registers at interrupt
+ */
+static void cumanascsi_2_intr (int irq, void *dev_id, struct pt_regs *regs)
+{
+    struct Scsi_Host *instance = (struct Scsi_Host *)dev_id;
+
+    fas216_intr (instance);
+}
+
+/*
+ * Function: int cumanascsi_2_dma_setup (instance, SCpnt, direction)
+ * Purpose : initialises DMA/PIO
+ * Params  : instance  - host
+ *	     SCpnt     - command
+ *	     direction - DMA on to/off of card
+ * Returns : 0 if we should not set CMD_WITHDMA for transfer info command
+ */
+static fasdmatype_t cumanascsi_2_dma_setup (struct Scsi_Host *instance, Scsi_Pointer *SCp, fasdmadir_t direction)
+{
+    /*
+     * We don't do DMA
+     */
+    return fasdma_pseudo;
+}
+
+/*
+ * Function: int cumanascsi_2_dma_pseudo (instance, SCpnt, direction, transfer)
+ * Purpose : handles pseudo DMA
+ * Params  : instance  - host
+ *	     SCpnt     - command
+ *	     direction - DMA on to/off of card
+ *	     transfer  - minimum number of bytes we expect to transfer
+ * Returns : bytes transfered
+ */
+static int
+cumanascsi_2_dma_pseudo (struct Scsi_Host *instance, Scsi_Pointer *SCp,
+			 fasdmadir_t direction, int transfer)
+{
+    CumanaScsi2_Info *info = (CumanaScsi2_Info *)instance->hostdata;
+    unsigned int length;
+    unsigned char *addr;
+
+    length = SCp->this_residual;
+    addr = SCp->ptr;
+
+    if (direction == DMA_OUT)
+#if 0
+	while (length > 1) {
+	    unsigned long word;
+
+
+	    if (inb (REG0_STATUS(&info->info)) & STATUS_INT)
+		goto end;
+
+	    if (!(inb (info->cstatus) & CSTATUS_DRQ))
+		continue;
+
+	    word = *addr | (*addr + 1) << 8;
+	    outw (info->dmaarea);
+	    addr += 2;
+	    length -= 2;
+	}
+#else
+	printk ("PSEUDO_OUT???\n");
+#endif
+    else {
+	if (transfer && (transfer & 255)) {
+	    while (length >= 256) {
+		if (inb (REG0_STATUS(&info->info)) & STATUS_INT)
+		    goto end;
+	    
+		if (!(inb (info->cstatus) & CSTATUS_DRQ))
+		    continue;
+
+		insw (info->dmaarea, addr, 256 >> 1);
+		addr += 256;
+		length -= 256;
+	    }
+	}
+
+	while (length > 0) {
+	    unsigned long word;
+
+	    if (inb (REG0_STATUS(&info->info)) & STATUS_INT)
+		goto end;
+	    
+	    if (!(inb (info->cstatus) & CSTATUS_DRQ))
+		continue;
+
+	    word = inw (info->dmaarea);
+	    *addr++ = word;
+	    if (--length > 0) {
+		*addr++ = word >> 8;
+		length --;
+	    }
+	}
+    }
+
+end:
+    return SCp->this_residual - length;
+}
+
+/*
+ * Function: int cumanascsi_2_dma_stop (instance, SCpnt)
+ * Purpose : stops DMA/PIO
+ * Params  : instance  - host
+ *	     SCpnt     - command
+ */
+static void cumanascsi_2_dma_stop (struct Scsi_Host *instance, Scsi_Pointer *SCp)
+{
+    /*
+     * no DMA to stop
+     */
+}
+
+/*
+ * Function: int cumanascsi_2_detect (Scsi_Host_Template * tpnt)
+ * Purpose : initialises Cumana SCSI 2 driver
+ * Params  : tpnt - template for this SCSI adapter
+ * Returns : >0 if host found, 0 otherwise.
+ */
+int cumanascsi_2_detect (Scsi_Host_Template *tpnt)
+{
+    static const card_ids cumanascsi_2_cids[] = { CUMANASCSI2_LIST, { 0xffff, 0xffff} };
+    int count = 0;
+    struct Scsi_Host *instance;
+  
+    tpnt->proc_dir = &proc_scsi_cumanascsi_2;
+    memset (ecs, 0, sizeof (ecs));
+
+    ecard_startfind ();
+
+    while (1) {
+    	CumanaScsi2_Info *info;
+
+	ecs[count] = ecard_find (0, cumanascsi_2_cids);
+	if (!ecs[count])
+	    break;
+
+	ecard_claim (ecs[count]);
+
+	instance = scsi_register (tpnt, sizeof (CumanaScsi2_Info));
+	if (!instance) {
+	    ecard_release (ecs[count]);
+	    break;
+	}
+
+	instance->io_port = ecard_address (ecs[count], ECARD_MEMC, 0);
+	instance->irq = ecs[count]->irq;
+
+	ecs[count]->irqaddr = (unsigned char *)BUS_ADDR(instance->io_port);
+	ecs[count]->irqmask = CSTATUS_IRQ;
+
+	request_region (instance->io_port      ,  1, "cumanascsi2-stat");
+	request_region (instance->io_port + 128, 64, "cumanascsi2-dma");
+	request_region (instance->io_port + 192, 16, "cumanascsi2-fas");
+	if (request_irq (instance->irq, cumanascsi_2_intr, SA_INTERRUPT, "cumanascsi2", instance)) {
+	    printk ("scsi%d: IRQ%d not free, interrupts disabled\n",
+		      instance->host_no, instance->irq);
+	}
+
+	info = (CumanaScsi2_Info *)instance->hostdata;
+	info->info.scsi.io_port	= instance->io_port + 192;
+	info->info.scsi.irq	= instance->irq;
+	info->info.ifcfg.clockrate = XTALFREQ;
+	info->info.ifcfg.select_timeout = 255;
+	info->info.dma.setup	= cumanascsi_2_dma_setup;
+	info->info.dma.pseudo	= cumanascsi_2_dma_pseudo;
+	info->info.dma.stop	= cumanascsi_2_dma_stop;
+	info->dmaarea		= instance->io_port + 128;
+	info->cstatus		= instance->io_port;
+
+	fas216_init (instance);
+	++count;
+    }
+    return count;
+}
+
+/*
+ * Function: int cumanascsi_2_release (struct Scsi_Host * host)
+ * Purpose : releases all resources used by this adapter
+ * Params  : host - driver host structure to return info for.
+ * Returns : nothing
+ */
+int cumanascsi_2_release (struct Scsi_Host *instance)
+{
+    int i;
+
+    fas216_release (instance);
+
+    if (instance->irq != 255)
+	free_irq (instance->irq, instance);
+    release_region (instance->io_port, 1);
+    release_region (instance->io_port + 128, 32);
+    release_region (instance->io_port + 192, 16);
+
+    for (i = 0; i < MAX_ECARDS; i++)
+	if (ecs[i] && instance->io_port == ecard_address (ecs[i], ECARD_MEMC, 0))
+		ecard_release (ecs[i]);
+    return 0;
+}
+
+/*
+ * Function: const char *cumanascsi_2_info (struct Scsi_Host * host)
+ * Purpose : returns a descriptive string about this interface,
+ * Params  : host - driver host structure to return info for.
+ * Returns : pointer to a static buffer containing null terminated string.
+ */
+const char *cumanascsi_2_info (struct Scsi_Host *host)
+{
+    CumanaScsi2_Info *info = (CumanaScsi2_Info *)host->hostdata;
+    static char string[100], *p;
+
+    p = string;
+    p += sprintf (string, "%s at port %X irq %d v%d.%d.%d scsi %s",
+		  host->hostt->name, host->io_port, host->irq,
+		  VER_MAJOR, VER_MINOR, VER_PATCH,
+		  info->info.scsi.type);
+
+    return string;
+}
+
+/*
+ * Function: int cumanascsi_2_proc_info (char *buffer, char **start, off_t offset,
+ *					 int length, int host_no, int inout)
+ * Purpose : Return information about the driver to a user process accessing
+ *	     the /proc filesystem.
+ * Params  : buffer - a buffer to write information to
+ *	     start  - a pointer into this buffer set by this routine to the start
+ *		      of the required information.
+ *	     offset - offset into information that we have read upto.
+ *	     length - length of buffer
+ *	     host_no - host number to return information for
+ *	     inout  - 0 for reading, 1 for writing.
+ * Returns : length of data written to buffer.
+ */
+int cumanascsi_2_proc_info (char *buffer, char **start, off_t offset,
+			    int length, int host_no, int inout)
+{
+    int pos, begin;
+    struct Scsi_Host *host = scsi_hostlist;
+    CumanaScsi2_Info *info;
+    Scsi_Device *scd;
+
+    while (host) {
+	if (host->host_no == host_no)
+	    break;
+	host = host->next;
+    }
+    if (!host)
+	return 0;
+
+    info = (CumanaScsi2_Info *)host->hostdata;
+    if (inout == 1)
+      return -EINVAL;
+
+    begin = 0;
+    pos = sprintf (buffer,
+			"Cumana SCSI II driver version %d.%d.%d\n",
+			VER_MAJOR, VER_MINOR, VER_PATCH);
+    pos += sprintf (buffer + pos,
+			"Address: %08X          IRQ : %d\n"
+			"FAS    : %s\n\n"
+			"Statistics:\n",
+			host->io_port, host->irq, info->info.scsi.type);
+
+    pos += sprintf (buffer+pos,
+			"Queued commands: %-10ld   Issued commands: %-10ld\n"
+			"Done commands  : %-10ld   Reads          : %-10ld\n"
+			"Writes         : %-10ld   Others         : %-10ld\n"
+			"Disconnects    : %-10ld   Aborts         : %-10ld\n"
+			"Resets         : %-10ld\n",
+			 info->info.stats.queues,      info->info.stats.removes,
+			 info->info.stats.fins,        info->info.stats.reads,
+			 info->info.stats.writes,      info->info.stats.miscs,
+			 info->info.stats.disconnects, info->info.stats.aborts,
+			 info->info.stats.resets);
+
+    pos += sprintf (buffer+pos, "\nAttached devices:%s\n", host->host_queue ? "" : " none");
+
+    for (scd = host->host_queue; scd; scd = scd->next) {
+	int len;
+
+	proc_print_scsidevice (scd, buffer, &len, pos);
+	pos += len;
+	pos += sprintf (buffer+pos, "Extensions: ");
+	if (scd->tagged_supported)
+	    pos += sprintf (buffer+pos, "TAG %sabled [%d] ",
+			    scd->tagged_queue ? "en" : "dis",
+			    scd->current_tag);
+	pos += sprintf (buffer+pos, "\n");
+
+	if (pos + begin < offset) {
+	    begin += pos;
+	    pos = 0;
+	}
+	if (pos + begin > offset + length)
+	    break;
+    }
+
+    *start = buffer + (offset - begin);
+    pos -= offset - begin;
+    if (pos > length)
+	pos = length;
+
+    return pos;
+}
+
+#ifdef MODULE
+Scsi_Host_Template driver_template = CUMANASCSI_2;
+
+#include "../../scsi/scsi_module.c"
+#endif
diff -u --recursive --new-file v2.1.87/linux/drivers/acorn/scsi/cumana_2.h linux/drivers/acorn/scsi/cumana_2.h
--- v2.1.87/linux/drivers/acorn/scsi/cumana_2.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/acorn/scsi/cumana_2.h	Sun Feb  8 14:08:52 1998
@@ -0,0 +1,73 @@
+/*
+ * Cumana SCSI II driver
+ *
+ * Copyright (C) 1997 Russell King
+ */
+#ifndef CUMANA_2_H
+#define CUMANA_2_H
+
+extern int cumanascsi_2_detect (Scsi_Host_Template *);
+extern int cumanascsi_2_release (struct Scsi_Host *);
+extern const char *cumanascsi_2_info (struct Scsi_Host *);
+extern int cumanascsi_2_proc_info (char *buffer, char **start, off_t offset,
+					int length, int hostno, int inout);
+
+#ifndef NULL
+#define NULL ((void *)0)
+#endif
+
+#ifndef CAN_QUEUE
+/*
+ * Default queue size
+ */
+#define CAN_QUEUE	1
+#endif
+
+#ifndef SCSI_ID
+/*
+ * Default SCSI host ID
+ */
+#define SCSI_ID		7
+#endif
+
+#include <scsi/scsicam.h>
+
+#ifndef HOSTS_C
+#include "fas216.h"
+#endif
+
+#define CUMANASCSI_2 {							\
+proc_info:		cumanascsi_2_proc_info,				\
+name:			"Cumana SCSI II",				\
+detect:			cumanascsi_2_detect,	/* detect		*/	\
+release:		cumanascsi_2_release,	/* release		*/	\
+info:			cumanascsi_2_info,	/* info			*/	\
+command:		fas216_command,		/* command		*/	\
+queuecommand:		fas216_queue_command,	/* queuecommand		*/	\
+abort:			fas216_abort,		/* abort		*/	\
+reset:			fas216_reset,		/* reset		*/	\
+bios_param:		scsicam_bios_param,	/* biosparam		*/	\
+can_queue:		CAN_QUEUE,		/* can queue		*/	\
+this_id:		SCSI_ID,		/* scsi host id		*/	\
+sg_tablesize:		SG_ALL,			/* sg_tablesize		*/	\
+cmd_per_lun:		CAN_QUEUE,		/* cmd per lun		*/	\
+unchecked_isa_dma:	0,			/* unchecked isa dma	*/	\
+use_clustering:		DISABLE_CLUSTERING					\
+	}
+
+#ifndef HOSTS_C
+
+typedef struct {
+    FAS216_Info info;
+
+    /* other info... */
+    unsigned int	cstatus;	/* card status register	*/
+    unsigned int	dmaarea;	/* Pseudo DMA area	*/
+} CumanaScsi2_Info;
+
+#define CSTATUS_IRQ	(1 << 0)
+#define CSTATUS_DRQ	(1 << 1)
+
+#endif /* HOSTS_C */
+
+#endif /* CUMANASCSI_2_H */
diff -u --recursive --new-file v2.1.87/linux/drivers/acorn/scsi/ecoscsi.c linux/drivers/acorn/scsi/ecoscsi.c
--- v2.1.87/linux/drivers/acorn/scsi/ecoscsi.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/acorn/scsi/ecoscsi.c	Fri Feb  6 16:24:45 1998
@@ -0,0 +1,240 @@
+#define AUTOSENSE
+/* #define PSEUDO_DMA */
+
+/*
+ * EcoSCSI Generic NCR5380 driver
+ *
+ * Copyright 1995, Russell King
+ *
+ * ALPHA RELEASE 1.
+ *
+ * For more information, please consult
+ *
+ * NCR 5380 Family
+ * SCSI Protocol Controller
+ * Databook
+ *
+ * NCR Microelectronics
+ * 1635 Aeroplaza Drive
+ * Colorado Springs, CO 80916
+ * 1+ (719) 578-3400
+ * 1+ (800) 334-5454
+ */
+
+/*
+ * Options :
+ *
+ * PARITY - enable parity checking.  Not supported.
+ *
+ * SCSI2 - enable support for SCSI-II tagged queueing.  Untested.
+ *
+ * USLEEP - enable support for devices that don't disconnect.  Untested.
+ */
+
+/*
+ * $Log: ecoscsi_NCR5380.c,v $
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+#include <linux/blk.h>
+
+#include <asm/io.h>
+#include <asm/system.h>
+
+#include "../../scsi/scsi.h"
+#include "../../scsi/hosts.h"
+#include "ecoscsi.h"
+#include "../../scsi/NCR5380.h"
+#include "../../scsi/constants.h"
+
+static struct proc_dir_entry proc_scsi_ecoscsi = {
+	PROC_SCSI_GENERIC_NCR5380, 7, "ecoscsi", S_IFDIR | S_IRUGO | S_IXUGO, 2
+};
+
+static char ecoscsi_read(struct Scsi_Host *instance, int reg)
+{
+  int iobase = instance->io_port;
+  outb(reg | 8, iobase);
+  return inb(iobase + 1);
+}
+
+static void ecoscsi_write(struct Scsi_Host *instance, int reg, int value)
+{
+  int iobase = instance->io_port;
+  outb(reg | 8, iobase);
+  outb(value, iobase + 1);
+}
+
+/*
+ * Function : ecoscsi_setup(char *str, int *ints)
+ *
+ * Purpose : LILO command line initialization of the overrides array,
+ *
+ * Inputs : str - unused, ints - array of integer parameters with ints[0]
+ *	equal to the number of ints.
+ *
+ */
+
+void ecoscsi_setup(char *str, int *ints) {
+}
+
+/*
+ * Function : int ecoscsi_detect(Scsi_Host_Template * tpnt)
+ *
+ * Purpose : initializes ecoscsi NCR5380 driver based on the
+ *	command line / compile time port and irq definitions.
+ *
+ * Inputs : tpnt - template for this SCSI adapter.
+ *
+ * Returns : 1 if a host adapter was found, 0 if not.
+ *
+ */
+
+int ecoscsi_detect(Scsi_Host_Template * tpnt)
+{
+    struct Scsi_Host *instance;
+
+    tpnt->proc_dir = &proc_scsi_ecoscsi;
+
+    instance = scsi_register (tpnt, sizeof(struct NCR5380_hostdata));
+    instance->io_port = 0x80ce8000;
+    instance->n_io_port = 144;
+    instance->irq = IRQ_NONE;
+
+    if (check_region (instance->io_port, instance->n_io_port)) {
+	scsi_unregister (instance);
+	return 0;
+    }
+
+    ecoscsi_write (instance, MODE_REG, 0x20);		/* Is it really SCSI? */
+    if (ecoscsi_read (instance, MODE_REG) != 0x20) {	/* Write to a reg.    */
+        scsi_unregister(instance);
+        return 0;					/* and try to read    */
+    }
+    ecoscsi_write( instance, MODE_REG, 0x00 );		/* it back.	      */
+    if (ecoscsi_read (instance, MODE_REG) != 0x00) {
+        scsi_unregister(instance);
+        return 0;
+    }
+
+    NCR5380_init(instance, 0);
+    request_region (instance->io_port, instance->n_io_port, "ecoscsi");
+
+    if (instance->irq != IRQ_NONE)
+	if (request_irq(instance->irq, ecoscsi_intr, SA_INTERRUPT, "ecoscsi", NULL)) {
+	    printk("scsi%d: IRQ%d not free, interrupts disabled\n",
+	    instance->host_no, instance->irq);
+	    instance->irq = IRQ_NONE;
+	}
+
+    if (instance->irq != IRQ_NONE) {
+  	printk("scsi%d: eek! Interrupts enabled, but I don't think\n", instance->host_no);
+	printk("scsi%d: that the board had an interrupt!\n", instance->host_no);
+    }
+
+    printk("scsi%d: at port %X irq", instance->host_no, instance->io_port);
+    if (instance->irq == IRQ_NONE)
+	printk ("s disabled");
+    else
+        printk (" %d", instance->irq);
+    printk(" options CAN_QUEUE=%d CMD_PER_LUN=%d release=%d",
+        CAN_QUEUE, CMD_PER_LUN, ECOSCSI_PUBLIC_RELEASE);
+    printk("\nscsi%d:", instance->host_no);
+    NCR5380_print_options(instance);
+    printk("\n");
+    return 1;
+}
+
+int ecoscsi_release (struct Scsi_Host *shpnt)
+{
+	if (shpnt->irq != IRQ_NONE)
+		free_irq (shpnt->irq, NULL);
+	if (shpnt->io_port)
+		release_region (shpnt->io_port, shpnt->n_io_port);
+	return 0;
+}
+
+const char * ecoscsi_info (struct Scsi_Host *spnt) {
+    return "";
+}
+
+#if 0
+#define STAT(p) inw(p + 144)
+
+static inline int NCR5380_pwrite(struct Scsi_Host *instance, unsigned char *addr,
+              int len)
+{
+  int iobase = instance->io_port;
+printk("writing %p len %d\n",addr, len);
+  if(!len) return -1;
+
+  while(1)
+  {
+    int status;
+    while(((status = STAT(iobase)) & 0x100)==0);
+  }
+}
+
+static inline int NCR5380_pread(struct Scsi_Host *instance, unsigned char *addr,
+              int len)
+{
+  int iobase = instance->io_port;
+  int iobase2= instance->io_port + 0x100;
+  unsigned char *start = addr;
+  int s;
+printk("reading %p len %d\n",addr, len);
+  outb(inb(iobase + 128), iobase + 135);
+  while(len > 0)
+  {
+    int status,b,i, timeout;
+    timeout = 0x07FFFFFF;
+    while(((status = STAT(iobase)) & 0x100)==0)
+    {
+      timeout--;
+      if(status & 0x200 || !timeout)
+      {
+        printk("status = %p\n",status);
+        outb(0, iobase + 135);
+        return 1;
+      }
+    }
+    if(len >= 128)
+    {
+      for(i=0; i<64; i++)
+      {
+        b = inw(iobase + 136);
+        *addr++ = b;
+        *addr++ = b>>8;
+      }
+      len -= 128;
+    }
+    else
+    {
+      b = inw(iobase + 136);
+      *addr ++ = b;
+      len -= 1;
+      if(len)
+        *addr ++ = b>>8;
+      len -= 1;
+    }
+  }
+  outb(0, iobase + 135);
+  printk("first bytes = %02X %02X %02X %20X %02X %02X %02X\n",*start, start[1], start[2], start[3], start[4], start[5], start[6]);
+  return 1;
+}
+#endif
+#undef STAT
+
+#include "../../scsi/NCR5380.c"
+
+#ifdef MODULE
+
+Scsi_Host_Template driver_template = ECOSCSI_NCR5380;
+
+#include "../../scsi/scsi_module.c"
+#endif
diff -u --recursive --new-file v2.1.87/linux/drivers/acorn/scsi/ecoscsi.h linux/drivers/acorn/scsi/ecoscsi.h
--- v2.1.87/linux/drivers/acorn/scsi/ecoscsi.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/acorn/scsi/ecoscsi.h	Sun Dec 28 09:18:59 1997
@@ -0,0 +1,93 @@
+/*
+ * Cumana Generic NCR5380 driver defines
+ *
+ * Copyright 1995, Russell King
+ *
+ * ALPHA RELEASE 1.
+ *
+ * For more information, please consult
+ *
+ * NCR 5380 Family
+ * SCSI Protocol Controller
+ * Databook
+ *
+ * NCR Microelectronics
+ * 1635 Aeroplaza Drive
+ * Colorado Springs, CO 80916
+ * 1+ (719) 578-3400
+ * 1+ (800) 334-5454
+ */
+
+/*
+ * $Log: ecoscsi_NCR5380.h,v $
+ */
+
+#ifndef ECOSCSI_NCR5380_H
+#define ECOSCSI_NCR5380_H
+
+#define ECOSCSI_PUBLIC_RELEASE 1
+
+
+#ifndef ASM
+int ecoscsi_abort (Scsi_Cmnd *);
+int ecoscsi_detect (Scsi_Host_Template *);
+int ecoscsi_release (struct Scsi_Host *);
+const char *ecoscsi_info (struct Scsi_Host *);
+int ecoscsi_reset(Scsi_Cmnd *, unsigned int);
+int ecoscsi_queue_command (Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
+int ecoscsi_proc_info (char *buffer, char **start, off_t offset,
+			int length, int hostno, int inout);
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+#ifndef CMD_PER_LUN
+#define CMD_PER_LUN 2
+#endif
+
+#ifndef CAN_QUEUE
+#define CAN_QUEUE 16
+#endif
+
+#define ECOSCSI_NCR5380 {					\
+name:		"Serial Port EcoSCSI NCR5380",			\
+detect:		ecoscsi_detect,					\
+release:	ecoscsi_release,				\
+info:		ecoscsi_info,					\
+queuecommand:	ecoscsi_queue_command,				\
+abort:		ecoscsi_abort, 					\
+reset:		ecoscsi_reset,					\
+can_queue:	CAN_QUEUE,		/* can queue */		\
+this_id:	7,			/* id */		\
+sg_tablesize:	SG_ALL,						\
+cmd_per_lun:	CMD_PER_LUN,		/* cmd per lun */	\
+use_clustering:	DISABLE_CLUSTERING				\
+	}
+
+#ifndef HOSTS_C
+#define NCR5380_implementation_fields \
+    int port, ctrl
+
+#define NCR5380_local_declare() \
+        struct Scsi_Host *_instance
+
+#define NCR5380_setup(instance) \
+        _instance = instance
+
+#define NCR5380_read(reg) ecoscsi_read(_instance, reg)
+#define NCR5380_write(reg, value) ecoscsi_write(_instance, reg, value)
+
+#define NCR5380_intr ecoscsi_intr
+#define NCR5380_queue_command ecoscsi_queue_command
+#define NCR5380_abort ecoscsi_abort
+#define NCR5380_reset ecoscsi_reset
+#define NCR5380_proc_info ecoscsi_proc_info
+
+#define BOARD_NORMAL	0
+#define BOARD_NCR53C400	1
+
+#endif /* ndef HOSTS_C */
+#endif /* ndef ASM */
+#endif /* ECOSCSI_NCR5380_H */
+
diff -u --recursive --new-file v2.1.87/linux/drivers/acorn/scsi/fas216.c linux/drivers/acorn/scsi/fas216.c
--- v2.1.87/linux/drivers/acorn/scsi/fas216.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/acorn/scsi/fas216.c	Wed Feb 18 14:14:47 1998
@@ -0,0 +1,1575 @@
+/*
+ * linux/arch/arm/drivers/scsi/fas216.c
+ *
+ * Copyright (C) 1997 Russell King
+ *
+ * Based in information in qlogicfas.c by Tom Zerucha, Michael Griffith, and
+ * other sources.
+ *
+ * This is a generic driver.  To use it, have a look at cumana_2.c.  You
+ * should define your own structure that overlays FAS216_Info, eg:
+ * struct my_host_data {
+ *    FAS216_Info info;
+ *    ... my host specific data ...
+ * };
+ *
+ * Changelog:
+ *  30-08-1997	RMK	Created
+ *  14-09-1997	RMK	Started disconnect support
+ *  08-02-1998	RMK	Corrected real DMA support
+ *  15-02-1998	RMK	Started sync xfer support
+ */
+
+#include <linux/module.h>
+#include <linux/blk.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/proc_fs.h>
+#include <linux/unistd.h>
+#include <linux/stat.h>
+
+#include <asm/delay.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/ecard.h>
+
+#define FAS216_C
+
+#include "scsi.h"
+#include "hosts.h"
+#include "fas216.h"
+
+#define VER_MAJOR	0
+#define VER_MINOR	0
+#define VER_PATCH	2
+
+#undef NO_DISCONNECTS
+#undef DEBUG_CONNECT
+#undef DEBUG_BUSSERVICE
+#undef DEBUG_FUNCTIONDONE
+#undef DEBUG_MESSAGES
+
+static char *fas216_bus_phase (int stat)
+{
+	static char *phases[] = {
+		"DATA OUT", "DATA IN",
+		"COMMAND", "STATUS",
+		"MISC OUT", "MISC IN",
+		"MESG OUT", "MESG IN"
+	};
+
+	return phases[stat & STAT_BUSMASK];
+}
+
+static char fas216_target (FAS216_Info *info)
+{
+	if (info->SCpnt)
+		return '0' + info->SCpnt->target;
+	else
+		return 'H';
+}
+
+static void fas216_done (FAS216_Info *info, unsigned int result);
+
+/* Function: int fas216_clockrate (unsigned int clock)
+ * Purpose : calculate correct value to be written into clock conversion
+ *	     factor register.
+ * Params  : clock - clock speed in MHz
+ * Returns : CLKF_ value
+ */
+static int fas216_clockrate (int clock)
+{
+	if (clock <= 10 || clock > 40) {
+		printk(KERN_CRIT
+		       "fas216: invalid clock rate: check your driver!\n");
+		clock = -1;
+	} else
+		clock = ((clock - 1) / 5 + 1) & 7;
+
+	return clock;
+}
+
+/* Function: int fas216_syncperiod(FAS216_Info *info, int ns)
+ * Purpose : Calculate value to be loaded into the STP register
+ *           for a given period in ns
+ * Params  : info - state structure for interface connected to device
+ *         : ns   - period in ns (between subsequent bytes)
+ * Returns : Value suitable for REG_STP
+ */
+static int fas216_syncperiod(FAS216_Info *info, int ns)
+{
+	int value = (info->ifcfg.clockrate * ns) / 1000;
+
+	if (value < 4)
+		value = 4;
+	else if value > 35)
+		value = 35;
+
+	return value & 31;
+}
+
+/* Function: void fas216_updateptrs (FAS216_Info *info, int bytes_transferred)
+ * Purpose : update data pointers after transfer suspended/paused
+ * Params  : info              - interface's local pointer to update
+ *           bytes_transferred - number of bytes transferred
+ */
+static void
+fas216_updateptrs (FAS216_Info *info, int bytes_transferred)
+{
+	unsigned char *ptr = info->scsi.SCp.ptr;
+	unsigned int residual = info->scsi.SCp.this_residual;
+
+	info->SCpnt->request_bufflen -= bytes_transferred;
+
+	while (residual <= bytes_transferred && bytes_transferred) {
+		/* We have used up this buffer */
+		bytes_transferred -= residual;
+		if (info->scsi.SCp.buffers_residual) {
+			info->scsi.SCp.buffer++;
+			info->scsi.SCp.buffers_residual--;
+			ptr = (unsigned char *)info->scsi.SCp.buffer->address;
+			residual = info->scsi.SCp.buffer->length;
+		} else {
+			ptr = NULL;
+			residual = 0;
+		}
+	}
+
+	residual -= bytes_transferred;
+	ptr += bytes_transferred;
+
+	info->scsi.SCp.ptr = ptr;
+	info->scsi.SCp.this_residual = residual;
+}
+
+/* Function: void fas216_pio (FAS216_Info *info, fasdmadir_t direction)
+ * Purpose : transfer data off of/on to card using programmed IO
+ * Params  : info      - interface to transfer data to/from
+ *           direction - direction to transfer data (DMA_OUT/DMA_IN)
+ * Notes   : this is incredibly slow
+ */
+static void
+fas216_pio (FAS216_Info *info, fasdmadir_t direction)
+{
+	unsigned int length = info->scsi.SCp.this_residual;
+	char *ptr = info->scsi.SCp.ptr;
+
+	if (direction == DMA_OUT) {
+	    	while (length > 0) {
+			if ((inb(REG_CFIS(info)) & CFIS_CF) < 8) {
+				outb(*ptr++, REG_FF(info));
+				length -= 1;
+			} else if (inb(REG_STAT(info)) & STAT_INT)
+				break;
+		}
+	} else {
+	    	while (length > 0) {
+			if ((inb(REG_CFIS(info)) & CFIS_CF) != 0) {
+				*ptr++ = inb(REG_FF(info));
+				length -= 1;
+			} else if (inb(REG_STAT(info)) & STAT_INT)
+				break;
+		}
+	}
+
+	if (length == 0) {
+		if (info->scsi.SCp.buffers_residual) {
+			info->scsi.SCp.buffer++;
+			info->scsi.SCp.buffers_residual--;
+			ptr = (unsigned char *)info->scsi.SCp.buffer->address;
+			length = info->scsi.SCp.buffer->length;
+		} else {
+			ptr = NULL;
+			length = 0;
+		}
+	}
+
+	info->scsi.SCp.ptr = ptr;
+	info->scsi.SCp.this_residual = length;
+}
+
+/* Function: void fas216_starttransfer(FAS216_Info *info,
+ *				       fasdmadir_t direction)
+ * Purpose : Start a DMA/PIO transfer off of/on to card
+ * Params  : info      - interface from which device disconnected from
+ *           direction - transfer direction (DMA_OUT/DMA_IN)
+ */
+static void
+fas216_starttransfer(FAS216_Info *info, fasdmadir_t direction)
+{
+	fasdmatype_t dmatype;
+
+	info->scsi.phase = (direction == DMA_OUT) ?
+				PHASE_DATAOUT : PHASE_DATAIN;
+
+	if (info->dma.transfer_type == fasdma_real_block ||
+	    info->dma.transfer_type == fasdma_real_all) {
+		unsigned long total, residual;
+
+		if (info->dma.transfer_type == fasdma_real_block)
+			total = info->scsi.SCp.this_residual;
+		else
+			total = info->SCpnt->request_bufflen;
+
+		residual = (inb(REG_CFIS(info)) & CFIS_CF) +
+			    inb(REG_CTCL(info)) +
+			    (inb(REG_CTCM(info)) << 8) +
+			    (inb(REG_CTCH(info)) << 16);
+		fas216_updateptrs (info, total - residual);
+		info->dma.transfer_type = fasdma_none;
+	}
+
+	if (!info->scsi.SCp.ptr) {
+		printk ("scsi%d.%c: null buffer passed to "
+			"fas216_starttransfer\n", info->host->host_no,
+			fas216_target (info));
+		return;
+	}
+
+	dmatype = fasdma_none;
+	if (info->dma.setup)
+		dmatype = info->dma.setup(info->host, &info->scsi.SCp,
+					  direction);
+
+	info->dma.transfer_type = dmatype;
+
+	switch (dmatype) {
+	case fasdma_none:
+		outb(info->scsi.SCp.this_residual, REG_STCL(info));
+		outb(info->scsi.SCp.this_residual >> 8, REG_STCM(info));
+		outb(info->scsi.SCp.this_residual >> 16, REG_STCH(info));
+		outb(CMD_NOP | CMD_WITHDMA, REG_CMD(info));
+		outb(CMD_TRANSFERINFO, REG_CMD(info));
+		fas216_pio (info, direction);
+		break;
+
+	case fasdma_pseudo: {
+		int transferred;
+
+		outb(info->scsi.SCp.this_residual, REG_STCL(info));
+		outb(info->scsi.SCp.this_residual >> 8, REG_STCM(info));
+		outb(info->scsi.SCp.this_residual >> 16, REG_STCH(info));
+		outb(CMD_NOP | CMD_WITHDMA, REG_CMD(info));
+		outb(CMD_TRANSFERINFO | CMD_WITHDMA, REG_CMD(info));
+
+		transferred =
+			info->dma.pseudo(info->host, &info->scsi.SCp,
+					 direction, info->SCpnt->transfersize);
+
+		fas216_updateptrs (info, transferred);
+		}
+		break;
+
+	case fasdma_real_block:
+		outb(info->scsi.SCp.this_residual, REG_STCL(info));
+		outb(info->scsi.SCp.this_residual >> 8, REG_STCM(info));
+		outb(info->scsi.SCp.this_residual >> 16, REG_STCH(info));
+		outb(CMD_NOP | CMD_WITHDMA, REG_CMD(info));
+
+		outb(CMD_TRANSFERINFO | CMD_WITHDMA, REG_CMD(info));
+		break;
+
+	case fasdma_real_all:
+		outb(info->SCpnt->request_bufflen, REG_STCL(info));
+		outb(info->SCpnt->request_bufflen >> 8, REG_STCM(info));
+		outb(info->SCpnt->request_bufflen >> 16, REG_STCH(info));
+		outb(CMD_NOP | CMD_WITHDMA, REG_CMD(info));
+
+		outb(CMD_TRANSFERINFO | CMD_WITHDMA, REG_CMD(info));
+		break;
+	}
+}
+
+/* Function: void fas216_stoptransfer (FAS216_Info *info)
+ * Purpose : Stop a DMA transfer onto / off of the card
+ * Params  : info      - interface from which device disconnected from
+ */
+static void
+fas216_stoptransfer (FAS216_Info *info)
+{
+	if (info->dma.transfer_type == fasdma_real_block ||
+	    info->dma.transfer_type == fasdma_real_all) {
+		unsigned long total, residual;
+
+		if (info->dma.stop)
+			info->dma.stop (info->host, &info->scsi.SCp);
+
+		if (info->dma.transfer_type == fasdma_real_block)
+			total = info->scsi.SCp.this_residual;
+		else
+			total = info->SCpnt->request_bufflen;
+
+		residual = (inb(REG_CFIS(info)) & CFIS_CF) +
+			    inb(REG_CTCL(info)) +
+			    (inb(REG_CTCM(info)) << 8) +
+			    (inb(REG_CTCH(info)) << 16);
+		fas216_updateptrs (info, total - residual);
+
+		info->dma.transfer_type = fasdma_none;
+	}
+}
+
+/* Function: void fas216_disconnected_intr (FAS216_Info *info)
+ * Purpose : handle device disconnection
+ * Params  : info - interface from which device disconnected from
+ */
+static void
+fas216_disconnect_intr (FAS216_Info *info)
+{
+#ifdef DEBUG_CONNECT
+	printk("scsi%d.%c: disconnect phase=%02X\n", info->host->host_no,
+		fas216_target (info), info->scsi.phase);
+#endif
+	msgqueue_flush (&info->scsi.msgs);
+
+	switch (info->scsi.phase) {
+	case PHASE_SELECTION:			/* while selecting - no target		*/
+		fas216_done (info, DID_NO_CONNECT);
+		break;
+
+	case PHASE_DISCONNECT:			/* message in - disconnecting		*/
+		outb(CMD_ENABLESEL, REG_CMD(info));
+		info->scsi.disconnectable = 1;
+		info->scsi.reconnected.tag = 0;
+		info->scsi.phase = PHASE_IDLE;
+		info->stats.disconnects += 1;
+		break;
+
+	case PHASE_DONE:			/* at end of command - complete		*/
+		fas216_done (info, DID_OK);
+		break;
+
+	case PHASE_AFTERMSGOUT:			/* message out - possible ABORT message	*/
+		if (info->scsi.last_message == ABORT) {
+			info->scsi.aborting = 0;
+			fas216_done (info, DID_ABORT);
+			break;
+		}
+
+	default:				/* huh?					*/
+		printk(KERN_ERR "scsi%d.%c: unexpected disconnect in phase %d\n",
+			info->host->host_no, fas216_target (info), info->scsi.phase);
+		fas216_stoptransfer(info);
+		fas216_done (info, DID_ERROR);
+		break;
+	}
+}
+
+/* Function: void fas216_reselected_intr (FAS216_Info *info)
+ * Purpose : Start reconnection of a device
+ * Params  : info - interface which was reselected
+ */
+static void
+fas216_reselected_intr (FAS216_Info *info)
+{
+    unsigned char target, identify_msg, ok;
+
+	if (info->scsi.phase == PHASE_SELECTION && info->SCpnt) {
+		Scsi_Cmnd *SCpnt = info->SCpnt;
+
+		info->origSCpnt = SCpnt;
+		info->SCpnt = NULL;
+
+		if (info->device[SCpnt->target].negstate == syncneg_sent)
+			info->device[SCpnt->target].negstate = syncneg_start;
+	}
+
+#ifdef DEBUG_CONNECT
+	printk("scsi%d.%c: reconnect phase=%02X\n", info->host->host_no,
+		fas216_target (info), info->scsi.phase);
+#endif
+
+    msgqueue_flush (&info->scsi.msgs);
+
+    if ((inb(REG_CFIS(info)) & CFIS_CF) != 2) {
+	printk (KERN_ERR "scsi%d.H: incorrect number of bytes after reselect\n",
+		info->host->host_no);
+	outb(CMD_SETATN, REG_CMD(info));
+	msgqueue_addmsg (&info->scsi.msgs, 1, MESSAGE_REJECT);
+	info->scsi.phase = PHASE_MSGOUT;
+	outb(CMD_MSGACCEPTED, REG_CMD(info));
+	return;
+    }
+
+    target = inb(REG_FF(info));
+    identify_msg = inb(REG_FF(info));
+
+    ok = 1;
+    if (!(target & (1 << info->host->this_id))) {
+	printk (KERN_ERR "scsi%d.H: invalid host id on reselect\n", info->host->host_no);
+	ok = 0;
+    }
+
+    if (!(identify_msg & 0x80)) {
+	printk (KERN_ERR "scsi%d.H: no IDENTIFY message on reselect, got msg %02X\n",
+		info->host->host_no, identify_msg);
+	ok = 0;
+    }
+
+    if (!ok) {
+	/*
+	 * Something went wrong - abort the command on
+	 * the target.  Should this be INITIATOR_ERROR ?
+	 */
+	outb(CMD_SETATN, REG_CMD(info));
+	msgqueue_addmsg (&info->scsi.msgs, 1, ABORT);
+	info->scsi.phase = PHASE_MSGOUT;
+	outb(CMD_MSGACCEPTED, REG_CMD(info));
+	return;
+    }
+
+    target &= ~(1 << info->host->this_id);
+    switch (target) {
+    case   1:  target = 0; break;
+    case   2:  target = 1; break;
+    case   4:  target = 2; break;
+    case   8:  target = 3; break;
+    case  16:  target = 4; break;
+    case  32:  target = 5; break;
+    case  64:  target = 6; break;
+    case 128:  target = 7; break;
+    default:   target = info->host->this_id; break;
+    }
+
+    identify_msg &= 7;
+    info->scsi.reconnected.target = target;
+    info->scsi.reconnected.lun    = identify_msg;
+    info->scsi.reconnected.tag    = 0;
+
+    ok = 0;
+    if (info->scsi.disconnectable && info->SCpnt &&
+	  info->SCpnt->target == target && info->SCpnt->lun == identify_msg)
+	ok = 1;
+
+    if (!ok && queue_probetgtlun (&info->queues.disconnected, target, identify_msg))
+	ok = 1;
+
+    if (ok) {
+	info->scsi.phase = PHASE_RECONNECTED;
+	outb(target, REG_SDID(info));
+    } else {
+	/*
+	 * Our command structure not found - abort the command on the target
+	 * Should this be INITIATOR_ERROR ?
+	 */
+	outb(CMD_SETATN, REG_CMD(info));
+	msgqueue_addmsg (&info->scsi.msgs, 1, ABORT);
+	info->scsi.phase = PHASE_MSGOUT;
+    }
+    outb(CMD_MSGACCEPTED, REG_CMD(info));
+}
+
+/* Function: void fas216_finish_reconnect (FAS216_Info *info)
+ * Purpose : finish reconnection sequence for device
+ * Params  : info - interface which caused function done interrupt
+ */
+static void
+fas216_finish_reconnect (FAS216_Info *info)
+{
+#ifdef DEBUG_CONNECT
+printk ("Connected: %1X %1X %02X, reconnected: %1X %1X %02X\n",
+	info->SCpnt->target, info->SCpnt->lun, info->SCpnt->tag,
+	info->scsi.reconnected.target, info->scsi.reconnected.lun,
+	info->scsi.reconnected.tag);
+#endif
+
+    if (info->scsi.disconnectable && info->SCpnt) {
+	info->scsi.disconnectable = 0;
+	if (info->SCpnt->target == info->scsi.reconnected.target &&
+	    info->SCpnt->lun    == info->scsi.reconnected.lun &&
+	    info->SCpnt->tag    == info->scsi.reconnected.tag) {
+#ifdef DEBUG_CONNECT
+	    printk ("scsi%d.%c: reconnected",
+		    info->host->host_no, fas216_target (info));
+#endif
+	} else {
+	    queue_add_cmd_tail (&info->queues.disconnected, info->SCpnt);
+#ifdef DEBUG_CONNECT
+	    printk ("scsi%d.%c: had to move command to disconnected queue\n",
+			info->host->host_no, fas216_target (info));
+#endif
+	    info->SCpnt = NULL;
+	}
+    }
+    if (!info->SCpnt) {
+	info->SCpnt = queue_remove_tgtluntag (&info->queues.disconnected,
+				info->scsi.reconnected.target,
+				info->scsi.reconnected.lun,
+				info->scsi.reconnected.tag);
+#ifdef DEBUG_CONNECT
+	printk ("scsi%d.%c: had to get command",
+		info->host->host_no, fas216_target (info));
+#endif
+    }
+    if (!info->SCpnt) {
+	outb(CMD_SETATN, REG_CMD(info));
+	msgqueue_addmsg (&info->scsi.msgs, 1, ABORT);
+	info->scsi.phase = PHASE_MSGOUT;
+	info->scsi.aborting = 1;
+    } else {
+	/*
+	 * Restore data pointer from SAVED data pointer
+	 */
+	info->scsi.SCp = info->SCpnt->SCp;
+#ifdef DEBUG_CONNECT
+	printk (", data pointers: [%p, %X]",
+		info->scsi.SCp.ptr, info->scsi.SCp.this_residual);
+#endif
+    }
+#ifdef DEBUG_CONNECT
+    printk ("\n");
+#endif
+}
+
+/* Function: void fas216_message (FAS216_Info *info)
+ * Purpose : handle a function done interrupt from FAS216 chip
+ * Params  : info - interface which caused function done interrupt
+ */
+static void fas216_message (FAS216_Info *info)
+{
+    unsigned char message[16];
+    unsigned int msglen = 1;
+
+    message[0] = inb(REG_FF(info));
+
+    if (message[0] == EXTENDED_MESSAGE) {
+	message[1] = inb(REG_FF(info));
+
+	for (msglen = 2; msglen < message[1]; msglen++)
+	    message[msglen] = inb(REG_FF(info));
+    }
+
+#ifdef DEBUG_MESSAGES
+    {
+	int i;
+
+	printk ("scsi%d.%c: message in: ",
+		info->host->host_no, fas216_target (info));
+	for (i = 0; i < msglen; i++)
+	    printk ("%02X ", message[i]);
+	printk ("\n");
+    }
+#endif
+    if (info->scsi.phase == PHASE_RECONNECTED) {
+	if (message[0] == SIMPLE_QUEUE_TAG)
+	    info->scsi.reconnected.tag = message[1];
+	fas216_finish_reconnect (info);
+	info->scsi.phase = PHASE_MSGIN;
+    }	
+
+    switch (message[0]) {
+    case COMMAND_COMPLETE:
+	printk ("fas216: command complete with no status in MESSAGE_IN?\n");
+	break;
+
+    case SAVE_POINTERS:
+	/*
+	 * Save current data pointer to SAVED data pointer
+	 */
+	info->SCpnt->SCp = info->scsi.SCp;
+#if defined (DEBUG_MESSAGES) || defined (DEBUG_CONNECT)
+	printk ("scsi%d.%c: save data pointers: [%p, %X]\n",
+		info->host->host_no, fas216_target (info),
+		info->scsi.SCp.ptr, info->scsi.SCp.this_residual);
+#endif
+	break;
+
+    case RESTORE_POINTERS:
+	/*
+	 * Restore current data pointer from SAVED data pointer
+	 */
+	info->scsi.SCp = info->SCpnt->SCp;
+#if defined (DEBUG_MESSAGES) || defined (DEBUG_CONNECT)
+	printk ("scsi%d.%c: restore data pointers: [%p, %X]\n",
+		info->host->host_no, fas216_target (info),
+		info->scsi.SCp.ptr, info->scsi.SCp.this_residual);
+#endif
+	break;
+
+    case DISCONNECT:
+	info->scsi.phase = PHASE_DISCONNECT;
+	break;
+
+    case MESSAGE_REJECT:
+	printk ("scsi%d.%c: reject, last message %04X\n",
+		info->host->host_no, fas216_target (info),
+		info->scsi.last_message);
+	break;
+
+    case SIMPLE_QUEUE_TAG:
+	/* handled above */
+	printk ("scsi%d.%c: reconnect queue tag %02X\n",
+		info->host->host_no, fas216_target (info),
+		message[1]);
+	break;
+
+    case EXTENDED_MESSAGE:
+	switch (message[2]) {
+	case EXTENDED_SDTR:	/* Sync transfer negociation request/reply */
+
+	case EXTENDED_WDTR:	/* Wide transfer negociation request/reply */
+		/* We don't do wide transfers - reject message */
+	default:
+		printk("scsi%d.%c: unrecognised extended message %02X, rejecting\n",
+			info->host->host_no, fas216_target (info),
+			message[2]);
+		msgqueue_flush (&info->scsi.msgs);
+		outb(CMD_SETATN, REG_CMD(info));
+		msgqueue_addmsg (&info->scsi.msgs, 1, MESSAGE_REJECT);
+		info->scsi.phase = PHASE_MSGOUT;
+		break;
+	}
+	break;
+
+    default:
+	printk ("scsi%d.%c: unrecognised message %02X, rejecting\n",
+		info->host->host_no, fas216_target (info),
+		message[0]);
+	msgqueue_flush (&info->scsi.msgs);
+	outb(CMD_SETATN, REG_CMD(info));
+	msgqueue_addmsg (&info->scsi.msgs, 1, MESSAGE_REJECT);
+	info->scsi.phase = PHASE_MSGOUT;
+	break;
+    }
+    outb(CMD_MSGACCEPTED, REG_CMD(info));
+}
+
+/* Function: void fas216_busservice_intr (FAS216_Info *info, unsigned int stat, unsigned int ssr)
+ * Purpose : handle a bus service interrupt from FAS216 chip
+ * Params  : info - interface which caused bus service interrupt
+ *           stat - Status register contents
+ *           ssr  - SCSI Status register contents
+ */
+static void fas216_busservice_intr (FAS216_Info *info, unsigned int stat, unsigned int ssr)
+{
+    int i;
+#ifdef DEBUG_BUSSERVICE
+    printk("scsi%d.%c: bus service: stat=%02X ssr=%02X phase=%02X\n",
+	   info->host->host_no, fas216_target(info), stat, ssr, info->scsi.phase);
+#endif
+    switch (ssr & IS_BITS) {
+    case IS_COMPLETE:			/* last action completed		*/
+	outb(CMD_NOP, REG_CMD(info));
+
+	switch (info->scsi.phase) {
+	case PHASE_SELECTION:		/* while selecting - selected target	*/
+	    switch (stat & STAT_BUSMASK) {
+	    case STAT_DATAOUT:	/* data out phase			*/
+		fas216_starttransfer (info, DMA_OUT);
+		break;
+
+	    case STAT_DATAIN:		/* data in phase			*/
+		fas216_starttransfer (info, DMA_IN);
+		break;
+
+	    case STAT_STATUS:		/* status phase				*/
+		info->scsi.phase = PHASE_STATUS;
+		outb(CMD_INITCMDCOMPLETE, REG_CMD(info));
+		break;
+
+	    case STAT_MESGIN:		/* message in phase			*/
+		info->scsi.phase = PHASE_MSGIN;
+		outb(CMD_TRANSFERINFO, REG_CMD(info));
+		break;
+
+	    default:			/* other				*/
+		printk ("scsi%d.%c: bus phase %s after connect?\n",
+			info->host->host_no, fas216_target (info),
+			fas216_bus_phase (stat));
+		break;
+	    }
+	    break;
+
+	case PHASE_DATAIN:		/* while transfering data in		*/
+	    switch (stat & STAT_BUSMASK) {
+	    case STAT_DATAIN:		/* continue data in phase		*/
+		fas216_starttransfer (info, DMA_IN);
+		break;
+
+	    case STAT_STATUS:
+		fas216_stoptransfer(info);
+		info->scsi.phase = PHASE_STATUS;
+		outb(CMD_INITCMDCOMPLETE, REG_CMD(info));
+		break;
+
+	    case STAT_MESGIN:		/* message in phase			*/
+		fas216_stoptransfer(info);
+		info->scsi.phase = PHASE_MSGIN;
+		outb(CMD_TRANSFERINFO, REG_CMD(info));
+		break;
+
+	    default:
+		printk ("scsi%d.%c: bus phase %s after data in?\n",
+			info->host->host_no, fas216_target (info),
+			fas216_bus_phase (stat));
+	    }
+	    break;
+
+	case PHASE_DATAOUT:		/* while transfering data out		*/
+	    switch (stat & STAT_BUSMASK) {
+	    case STAT_DATAOUT:
+		fas216_starttransfer (info, DMA_OUT);
+		break;
+
+	    case STAT_STATUS:
+		fas216_stoptransfer(info);
+		info->scsi.phase = PHASE_STATUS;
+		outb(CMD_FLUSHFIFO, REG_CMD(info));
+		outb(CMD_INITCMDCOMPLETE, REG_CMD(info));
+		break;
+
+	    case STAT_MESGIN:		/* message in phase			*/
+		fas216_stoptransfer(info);
+		info->scsi.phase = PHASE_MSGIN;
+		outb(CMD_FLUSHFIFO, REG_CMD(info));
+		outb(CMD_TRANSFERINFO, REG_CMD(info));
+		break;
+
+	    default:
+		printk ("scsi%d.%c: bus phase %s after data out?\n",
+			info->host->host_no, fas216_target (info),
+			fas216_bus_phase (stat));
+	    }
+	    break;
+
+	case PHASE_RECONNECTED:		/* newly reconnected device		*/
+	    /*
+	     * Command reconnected - if MESGIN, get message - it may be
+	     * the tag.  If not, get command out of the disconnected queue
+	     */
+	    switch (stat & STAT_BUSMASK) {
+	    case STAT_MESGIN:
+		outb(CMD_TRANSFERINFO, REG_CMD(info));
+		break;
+
+	    case STAT_STATUS:
+		fas216_finish_reconnect (info);
+		info->scsi.phase = PHASE_STATUS;
+		outb(CMD_INITCMDCOMPLETE, REG_CMD(info));
+		break;
+
+	    case STAT_DATAOUT:		/* data out phase			*/
+		fas216_finish_reconnect (info);
+		fas216_starttransfer (info, DMA_OUT);
+		break;
+
+	    case STAT_DATAIN:		/* data in phase			*/
+		fas216_finish_reconnect (info);
+		fas216_starttransfer (info, DMA_IN);
+		break;
+
+	    default:
+		printk ("scsi%d.%c: bus phase %s after reconnect?\n",
+			info->host->host_no, fas216_target (info),
+			fas216_bus_phase (stat));
+	    }
+	    break;
+
+	case PHASE_MSGIN:
+	    switch (stat & STAT_BUSMASK) {
+	    case STAT_MESGIN:
+		outb(CMD_TRANSFERINFO, REG_CMD(info));
+		break;
+
+	    default:
+		printk ("scsi%d.%c: bus phase %s after message in?\n",
+			info->host->host_no, fas216_target (info),
+			fas216_bus_phase (stat));
+	    }
+	    break;
+
+	case PHASE_MSGOUT:
+	    if ((stat & STAT_BUSMASK) != STAT_MESGOUT) {
+		printk ("scsi%d.%c: didn't manage MESSAGE OUT phase\n",
+			info->host->host_no, fas216_target (info));
+	    } else {
+		unsigned int msglen;
+
+		msglen = msgqueue_msglength (&info->scsi.msgs);
+
+		outb(CMD_FLUSHFIFO, REG_CMD(info));
+
+		if (msglen == 0)
+		    outb(NOP, REG_FF(info));
+		else {
+		    char *msg;
+
+		    while ((msg = msgqueue_getnextmsg (&info->scsi.msgs, &msglen)) != NULL) {
+			for (i = 0; i < msglen; i++)
+			    outb(msg[i], REG_FF(info));
+		    }
+		}
+		outb(CMD_TRANSFERINFO, REG_CMD(info));
+		info->scsi.phase = PHASE_AFTERMSGOUT;
+	    }
+	    break;
+
+	case PHASE_AFTERMSGOUT:
+	    switch (stat & STAT_BUSMASK) {
+	    case STAT_MESGIN:
+		info->scsi.phase = PHASE_MSGIN;
+		outb(CMD_TRANSFERINFO, REG_CMD(info));
+		break;
+
+	    default:
+		printk ("scsi%d.%c: bus phase %s after message out\n",
+			info->host->host_no, fas216_target (info),
+			fas216_bus_phase (stat));
+	    }
+	    break;
+
+	case PHASE_DISCONNECT:
+	    printk ("scsi%d.%c: disconnect message received, but bus service %s?\n",
+		    info->host->host_no, fas216_target (info),
+		    fas216_bus_phase (stat));
+	    outb(CMD_SETATN, REG_CMD(info));
+	    msgqueue_addmsg (&info->scsi.msgs, 1, ABORT);
+	    info->scsi.phase = PHASE_MSGOUT;
+	    info->scsi.aborting = 1;
+	    outb(CMD_TRANSFERINFO, REG_CMD(info));
+	    break;
+
+	default:
+	    printk ("scsi%d.%c: internal phase %d for bus service?"
+		    "  What do I do with this?\n",
+		    info->host->host_no, fas216_target (info),
+		    info->scsi.phase);
+	}
+	break;
+
+    default:
+	printk ("scsi%d.%c: bus service at step %d?\n",
+		info->host->host_no, fas216_target (info),
+		ssr & IS_BITS);
+    }
+}
+
+/* Function: void fas216_funcdone_intr (FAS216_Info *info, unsigned int stat, unsigned int ssr)
+ * Purpose : handle a function done interrupt from FAS216 chip
+ * Params  : info - interface which caused function done interrupt
+ *           stat - Status register contents
+ *           ssr  - SCSI Status register contents
+ */
+static void fas216_funcdone_intr (FAS216_Info *info, unsigned int stat, unsigned int ssr)
+{
+    int status, message;
+#ifdef DEBUG_FUNCTIONDONE
+    printk("scsi%d.%c: function done: stat=%X ssr=%X phase=%02X\n",
+	   info->host->host_no, fas216_target(info), stat, ssr, info->scsi.phase);
+#endif
+    switch (info->scsi.phase) {
+    case PHASE_STATUS:			/* status phase - read status and msg	*/
+	status = inb(REG_FF(info));
+	message = inb(REG_FF(info));
+	info->scsi.SCp.Message = message;
+	info->scsi.SCp.Status = status;
+	info->scsi.phase = PHASE_DONE;
+	outb(CMD_MSGACCEPTED, REG_CMD(info));
+	break;
+
+    case PHASE_IDLE:			/* reselected?				*/
+    case PHASE_MSGIN:			/* message in phase			*/
+    case PHASE_RECONNECTED:		/* reconnected command			*/
+	if ((stat & STAT_BUSMASK) == STAT_MESGIN) {
+	    fas216_message (info);
+	    break;
+	}
+
+    default:
+	printk ("scsi%d.%c: internal phase %d for function done?"
+		"  What do I do with this?\n",
+		info->host->host_no, fas216_target (info),
+		info->scsi.phase);
+    }
+}
+
+/* Function: void fas216_intr (struct Scsi_Host *instance)
+ * Purpose : handle interrupts from the interface to progress a command
+ * Params  : instance - interface to service
+ */
+void fas216_intr (struct Scsi_Host *instance)
+{
+    FAS216_Info *info = (FAS216_Info *)instance->hostdata;
+    unsigned char isr, ssr, stat;
+
+    stat = inb(REG_STAT(info));
+    ssr = inb(REG_IS(info));
+    isr = inb(REG_INST(info));
+
+    if (isr & INST_BUSRESET)
+	printk ("scsi%d.H: fas216: bus reset detected\n", instance->host_no);
+    else if (isr & INST_ILLEGALCMD)
+	printk (KERN_CRIT "scsi%d.H: illegal command given\n", instance->host_no);
+    else if (isr & INST_DISCONNECT)
+	fas216_disconnect_intr (info);
+    else if (isr & INST_RESELECTED)		/* reselected			*/
+	fas216_reselected_intr (info);
+    else if (isr & INST_BUSSERVICE)		/* bus service request		*/
+	fas216_busservice_intr (info, stat, ssr);
+    else if (isr & INST_FUNCDONE)		/* function done		*/
+	fas216_funcdone_intr (info, stat, ssr);
+    else
+    	printk ("scsi%d.%c: unknown interrupt received:"
+		" phase %d isr %02X ssr %02X stat %02X\n",
+		instance->host_no, fas216_target (info),
+		info->scsi.phase, isr, ssr, stat);
+}
+
+/* Function: void fas216_kick (FAS216_Info *info)
+ * Purpose : kick a command to the interface - interface should be idle
+ * Params  : info - our host interface to kick
+ * Notes   : Interrupts are always disabled!
+ */
+static void fas216_kick (FAS216_Info *info)
+{
+	Scsi_Cmnd *SCpnt;
+	int i, msglen, from_queue = 0;
+
+	if (info->origSCpnt) {
+		SCpnt = info->origSCpnt;
+		info->origSCpnt = NULL;
+	} else
+		SCpnt = NULL;
+
+	/* retrieve next command */
+	if (!SCpnt) {
+		SCpnt = queue_remove_exclude(&info->queues.issue, info->busyluns);
+		from_queue = 1;
+	}
+
+	if (!SCpnt) /* no command pending - just exit */
+		return;
+
+	if (info->scsi.disconnectable && info->SCpnt) {
+		queue_add_cmd_tail (&info->queues.disconnected, info->SCpnt);
+		info->scsi.disconnectable = 0;
+		info->SCpnt = NULL;
+		printk("scsi%d.%c: moved command to disconnected queue\n",
+			info->host->host_no, fas216_target (info));
+	}
+
+	/*
+	 * tagged queuing - allocate a new tag to this command
+	 */
+	if (SCpnt->device->tagged_queue && SCpnt->cmnd[0] != REQUEST_SENSE) {
+		SCpnt->device->current_tag += 1;
+		if (SCpnt->device->current_tag == 0)
+		    SCpnt->device->current_tag = 1;
+		SCpnt->tag = SCpnt->device->current_tag;
+	}
+
+	/*
+	 * claim host busy
+	 */
+	info->scsi.phase = PHASE_SELECTION;
+	info->SCpnt = SCpnt;
+	info->scsi.SCp = SCpnt->SCp;
+	info->dma.transfer_type = fasdma_none;
+
+#ifdef DEBUG_CONNECT
+	printk("scsi%d.%c: starting cmd %02X",
+		info->host->host_no, '0' + SCpnt->target,
+		SCpnt->cmnd[0]);
+#endif
+
+	if (from_queue) {
+#ifdef SCSI2_TAG
+		if (SCpnt->device->tagged_queue && SCpnt->cmnd[0] != REQUEST_SENSE) {
+		    SCpnt->device->current_tag += 1;
+			if (SCpnt->device->current_tag == 0)
+			    SCpnt->device->current_tag = 1;
+				SCpnt->tag = SCpnt->device->current_tag;
+		} else
+#endif
+			set_bit(SCpnt->target * 8 + SCpnt->lun, info->busyluns);
+
+		info->stats.removes += 1;
+		switch (SCpnt->cmnd[0]) {
+		case WRITE_6:
+		case WRITE_10:
+		case WRITE_12:
+			info->stats.writes += 1;
+			break;
+		case READ_6:
+		case READ_10:
+		case READ_12:
+			info->stats.reads += 1;
+			break;
+		default:
+			info->stats.miscs += 1;
+			break;
+		}
+	}
+
+	/* build outgoing message bytes */
+	msgqueue_flush (&info->scsi.msgs);
+	if (info->device[SCpnt->target].disconnect_ok)
+		msgqueue_addmsg(&info->scsi.msgs, 1, IDENTIFY(1, SCpnt->lun));
+	else
+		msgqueue_addmsg(&info->scsi.msgs, 1, IDENTIFY(0, SCpnt->lun));
+
+	/* add tag message if required */
+	if (SCpnt->tag)
+		msgqueue_addmsg(&info->scsi.msgs, 2, SIMPLE_QUEUE_TAG, SCpnt->tag);
+
+	/* add synchronous negociation */
+	if (info->device[SCpnt->target].negstate == syncneg_start) {
+		info->device[SCpnt->target].negstate = syncneg_sent;
+		msgqueue_addmsg(&info->scsi.msgs, 5,
+				EXTENDED_MESSAGE, 3, EXTENDED_SDTR,
+				1000 / info->ifcfg.clockrate,
+				info->ifcfg.sync_max_depth);
+	}
+
+	/* following what the ESP driver says */
+	outb(0, REG_STCL(info));
+	outb(0, REG_STCM(info));
+	outb(0, REG_STCH(info));
+	outb(CMD_NOP | CMD_WITHDMA, REG_CMD(info));
+
+	/* flush FIFO */
+	outb(CMD_FLUSHFIFO, REG_CMD(info));
+
+	/* load bus-id and timeout */
+	outb(BUSID(SCpnt->target), REG_SDID(info));
+	outb(info->ifcfg.select_timeout, REG_STIM(info));
+
+	/* synchronous transfers */
+	outb(info->device[SCpnt->target].sof, REG_SOF(info));
+	outb(info->device[SCpnt->target].stp, REG_STP(info));
+
+	msglen = msgqueue_msglength (&info->scsi.msgs);
+
+	if (msglen == 1 || msglen == 3) {
+		char *msg;
+
+		/* load message bytes */
+		while ((msg = msgqueue_getnextmsg(&info->scsi.msgs, &msglen)) != NULL) {
+			for (i = 0; i < msglen; i++)
+				outb(msg[i], REG_FF(info));
+		}
+
+		/* load command */
+		for (i = 0; i < SCpnt->cmd_len; i++)
+			outb(SCpnt->cmnd[i], REG_FF(info));
+
+		if (msglen == 1)
+			outb(CMD_SELECTATN, REG_CMD(info));
+		else
+			outb(CMD_SELECTATN3, REG_CMD(info));
+	} else {
+		outb(CMD_SELECTATNSTOP, REG_CMD(info));
+	}
+
+#ifdef DEBUG_CONNECT
+	printk(", data pointers [%p, %X]\n",
+		info->scsi.SCp.ptr, info->scsi.SCp.this_residual);
+#endif
+	/* should now get either DISCONNECT or (FUNCTION DONE with BUS SERVICE) intr */
+}
+
+/* Function: void fas216_done (FAS216_Info *info, unsigned int result)
+ * Purpose : complete processing for command
+ * Params  : info   - interface that completed
+ *	     result - driver byte of result
+ */
+static void fas216_done (FAS216_Info *info, unsigned int result)
+{
+    Scsi_Cmnd *SCpnt = info->SCpnt;
+
+    if (info->scsi.aborting) {
+	printk ("scsi%d.%c: uncaught abort - returning DID_ABORT\n",
+		info->host->host_no, fas216_target (info));
+	result = DID_ABORT;
+	info->scsi.aborting = 0;
+    }
+
+    info->stats.fins += 1;
+
+    if (SCpnt) {
+    	info->scsi.phase = PHASE_IDLE;
+	info->SCpnt = NULL;
+
+	SCpnt->result = result << 16 | info->scsi.SCp.Message << 8 |
+				info->scsi.SCp.Status;
+
+	/*
+	 * In theory, this should not happen, but just in case it does.
+	 */
+	if (info->scsi.SCp.ptr && result == DID_OK) {
+	    switch (status_byte (SCpnt->result)) {
+	    case CHECK_CONDITION:
+	    case COMMAND_TERMINATED:
+	    case BUSY:
+	    case QUEUE_FULL:
+	    case RESERVATION_CONFLICT:
+		break;
+
+	    default:
+		printk (KERN_ERR "scsi%d.H: incomplete data transfer "
+			"detected: result=%08X command=",
+			info->host->host_no, SCpnt->result);
+		print_command (SCpnt->cmnd);
+	    }
+	}
+#ifdef DEBUG_CONNECT
+	printk ("scsi%d.%c: scsi command (%p) complete, result=%08X\n",
+		info->host->host_no, fas216_target (info),
+		SCpnt, SCpnt->result);
+#endif
+
+	if (!SCpnt->scsi_done)
+	    panic ("scsi%d.H: null scsi_done function in fas216_done", info->host->host_no);
+
+	clear_bit (SCpnt->target * 8 + SCpnt->lun, info->busyluns);
+
+	SCpnt->scsi_done (SCpnt);
+    } else
+	panic ("scsi%d.H: null command in fas216_done", info->host->host_no);
+
+    if (info->scsi.irq != NO_IRQ)
+	fas216_kick (info);
+}
+
+/* Function: int fas216_queue_command (Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *))
+ * Purpose : queue a command for adapter to process.
+ * Params  : SCpnt - Command to queue
+ *	     done  - done function to call once command is complete
+ * Returns : 0 - success, else error
+ */
+int fas216_queue_command (Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *))
+{
+	FAS216_Info *info = (FAS216_Info *)SCpnt->host->hostdata;
+
+#ifdef DEBUG_CONNECT
+	printk("scsi%d.%c: received queuable command (%p) %02X\n",
+		SCpnt->host->host_no, '0' + SCpnt->target,
+		SCpnt, SCpnt->cmnd[0]);
+#endif
+
+	SCpnt->scsi_done = done;
+	SCpnt->host_scribble = NULL;
+	SCpnt->result = 0;
+	SCpnt->SCp.Message = 0;
+	SCpnt->SCp.Status = 0;
+
+	if (SCpnt->use_sg) {
+		unsigned long len = 0;
+		int buf;
+
+		SCpnt->SCp.buffer = (struct scatterlist *) SCpnt->buffer;
+		SCpnt->SCp.buffers_residual = SCpnt->use_sg - 1;
+		SCpnt->SCp.ptr = (char *) SCpnt->SCp.buffer->address;
+		SCpnt->SCp.this_residual = SCpnt->SCp.buffer->length;
+		/*
+		 * Calculate correct buffer length
+		 */
+		for (buf = 0; buf <= SCpnt->SCp.buffers_residual; buf++)
+			len += SCpnt->SCp.buffer[buf].length;
+		SCpnt->request_bufflen = len;
+	} else {
+		SCpnt->SCp.buffer = NULL;
+		SCpnt->SCp.buffers_residual = 0;
+		SCpnt->SCp.ptr = (unsigned char *)SCpnt->request_buffer;
+		SCpnt->SCp.this_residual = SCpnt->request_bufflen;
+	}
+
+	info->stats.queues += 1;
+	SCpnt->tag = 0;
+
+	if (info->scsi.irq != NO_IRQ) {
+		unsigned long flags;
+
+		/* add command into execute queue and let it complete under
+		 * the drivers interrupts.
+		 */
+		if (!queue_add_cmd_ordered (&info->queues.issue, SCpnt)) {
+			SCpnt->result = DID_ERROR << 16;
+			done (SCpnt);
+		}
+		save_flags_cli (flags);
+		if (!info->SCpnt || info->scsi.disconnectable)
+			fas216_kick (info);
+		restore_flags (flags);
+	} else {
+		/* no interrupts to rely on - we'll have to handle the
+		 * command ourselves.  For now, we give up.
+		 */
+		SCpnt->result = DID_ERROR << 16;
+		done (SCpnt);
+	}
+	return 0;
+}
+
+/* Function: void fas216_internal_done (Scsi_Cmnd *SCpnt)
+ * Purpose : trigger restart of a waiting thread in fas216_command
+ * Params  : SCpnt - Command to wake
+ */
+static void fas216_internal_done (Scsi_Cmnd *SCpnt)
+{
+	FAS216_Info *info = (FAS216_Info *)SCpnt->host->hostdata;
+
+	info->internal_done = 1;
+}
+
+/* Function: int fas216_command (Scsi_Cmnd *SCpnt)
+ * Purpose : queue a command for adapter to process.
+ * Params  : SCpnt - Command to queue
+ * Returns : scsi result code
+ */
+int fas216_command (Scsi_Cmnd *SCpnt)
+{
+	FAS216_Info *info = (FAS216_Info *)SCpnt->host->hostdata;
+	unsigned long flags;
+
+	info->internal_done = 0;
+	fas216_queue_command (SCpnt, fas216_internal_done);
+
+	/*
+	 * This wastes time, since we can't return until the command is
+	 * complete. We can't seep either since we may get re-entered!
+	 * However, we must re-enable interrupts, or else we'll be
+	 * waiting forever.
+	 */
+	save_flags (flags);
+	sti ();
+
+	while (!info->internal_done)
+		barrier ();
+
+	restore_flags (flags);
+
+	return SCpnt->result;
+}
+
+/* Prototype: void fas216_reportstatus(Scsi_Cmnd **SCpntp1,
+ *				       Scsi_Cmnd **SCpntp2, int result)
+ * Purpose  : pass a result to *SCpntp1, and check if *SCpntp1 = *SCpntp2
+ * Params   : SCpntp1 - pointer to command to return
+ *	      SCpntp2 - pointer to command to check
+ *	      result  - result to pass back to mid-level done function
+ * Returns  : *SCpntp2 = NULL if *SCpntp1 is the same command
+ *	      structure as *SCpntp2.
+ */
+static void fas216_reportstatus(Scsi_Cmnd **SCpntp1, Scsi_Cmnd **SCpntp2,
+				int result)
+{
+	Scsi_Cmnd *SCpnt = *SCpntp1;
+
+	if (SCpnt) {
+		*SCpntp1 = NULL;
+
+		SCpnt->result = result;
+		SCpnt->scsi_done (SCpnt);
+	}
+
+	if (SCpnt == *SCpntp2)
+		*SCpntp2 = NULL;
+}
+
+/* Function: int fas216_abort (Scsi_Cmnd *SCpnt)
+ * Purpose : abort a command if something horrible happens.
+ * Params  : SCpnt - Command that is believed to be causing a problem.
+ * Returns : one of SCSI_ABORT_ macros.
+ */
+int fas216_abort (Scsi_Cmnd *SCpnt)
+{
+	FAS216_Info *info = (FAS216_Info *)SCpnt->host->hostdata;
+	int result = SCSI_ABORT_SNOOZE;
+
+	info->stats.aborts += 1;
+
+	printk(KERN_WARNING "scsi%d: fas216_abort: ", info->host->host_no);
+
+	do {
+		/* If command is waiting in the issue queue, then we can
+		 * simply remove the command and return abort status
+		 */
+		if (queue_removecmd (&info->queues.issue, SCpnt)) {
+			SCpnt->result = DID_ABORT << 16;
+			SCpnt->scsi_done (SCpnt);
+			printk ("command on issue queue");
+			result = SCSI_ABORT_SUCCESS;
+			break;
+		}
+
+		/* If the command is on the disconencted queue, we need to
+		 * reconnect to the device
+		 */
+		if (queue_cmdonqueue (&info->queues.disconnected, SCpnt))
+			printk ("command on disconnected queue");
+
+		/* If the command is connected, we need to flag that the
+		 * command needs to be aborted
+		 */
+		if (info->SCpnt == SCpnt)
+			printk ("command executing");
+
+		/* If the command is pending for execution, then again
+		 * this is simple - we remove it and report abort status
+		 */
+		if (info->origSCpnt == SCpnt) {
+			info->origSCpnt = NULL;
+			SCpnt->result = DID_ABORT << 16;
+			SCpnt->scsi_done (SCpnt);
+			printk ("command waiting for execution");
+			result = SCSI_ABORT_SUCCESS;
+			break;
+		}
+	} while (0);
+
+	printk ("\n");
+
+	return result;
+}
+
+/* Function: void fas216_reset_state(FAS216_Info *info)
+ * Purpose : Initialise driver internal state
+ * Params  : info - state to initialise
+ */
+static void fas216_reset_state(FAS216_Info *info)
+{
+	int i;
+
+	/*
+	 * Clear out all stale info in our state structure
+	 */
+	memset (info->busyluns, 0, sizeof (info->busyluns));
+	msgqueue_flush(&info->scsi.msgs);
+	info->scsi.reconnected.target = 0;
+	info->scsi.reconnected.lun = 0;
+	info->scsi.reconnected.tag = 0;
+	info->scsi.disconnectable = 0;
+	info->scsi.last_message = 0;
+	info->scsi.aborting = 0;
+	info->scsi.phase = PHASE_IDLE;
+
+	for (i = 0; i < 8; i++) {
+#ifndef NO_DISCONNECTS
+		info->device[i].disconnect_ok = 1;
+#else
+		info->device[i].disconnect_ok = 0;
+#endif
+		info->device[i].stp = fas216_syncperiod(info->ifcfg.asyncperiod);
+		info->device[i].sof = 0;
+#ifdef SCSI2SYNC
+		info->device[i].negstate = syncneg_start;
+#else
+		info->device[i].negstate = syncneg_complete;
+#endif
+	}
+}
+
+/* Function: void fas216_init_chip(FAS216_Info *info)
+ * Purpose : Initialise FAS216 state after reset
+ * Params  : info - state structure for interface
+ */
+static void fas216_init_chip(FAS216_Info *info)
+{
+	outb(fas216_clockrate(info->ifcfg.clockrate), REG_CLKF(info));
+	outb(info->scsi.cfg[0], REG_CNTL1(info));
+	outb(info->scsi.cfg[1], REG_CNTL2(info));
+	outb(info->scsi.cfg[2], REG_CNTL3(info));
+	outb(info->ifcfg.select_timeout, REG_STIM(info));
+	outb(0, REG_SOF(info));
+	outb(fas216_syncperiod(info->ifcfg.asyncperiod), REG_STP(info));
+	outb(info->scsi.cfg[0], REG_CNTL1(info));
+}
+
+/* Function: int fas216_reset (Scsi_Cmnd *SCpnt, unsigned int reset_flags)
+ * Purpose : resets the adapter if something horrible happens.
+ * Params  : SCpnt - Command that is believed to be causing a problem.
+ *	     reset_flags - flags indicating reset type that is believed
+ *           to be required.
+ * Returns : one of SCSI_RESET_ macros, or'd with the SCSI_RESET_*_RESET
+ *           macros.
+ */
+int fas216_reset (Scsi_Cmnd *SCpnt, unsigned int reset_flags)
+{
+	FAS216_Info *info = (FAS216_Info *)SCpnt->host->hostdata;
+	Scsi_Cmnd *SCptr;
+	int result = 0;
+
+	info->stats.resets += 1;
+
+	printk(KERN_WARNING "scsi%d: fas216_reset: ", info->host->host_no);
+
+	outb(info->scsi.cfg[3], REG_CNTL3(info));
+
+	fas216_stoptransfer(info);
+	fas216_reset_state(info);
+
+	if (reset_flags & SCSI_RESET_SUGGEST_HOST_RESET) {
+		outb(CMD_RESETCHIP, REG_CMD(info));
+		outb(CMD_NOP, REG_CMD(info));
+		result |= SCSI_RESET_HOST_RESET;
+	}
+
+	if (reset_flags & SCSI_RESET_SUGGEST_BUS_RESET) {
+	    	outb(CMD_RESETSCSI, REG_CMD(info));
+    		outb(CMD_NOP, REG_CMD(info));
+	    	result |= SCSI_RESET_BUS_RESET;
+	}
+
+	if (!(reset_flags &
+	      (SCSI_RESET_SUGGEST_BUS_RESET|SCSI_RESET_SUGGEST_HOST_RESET))) {
+		outb(CMD_RESETCHIP, REG_CMD(info));
+		outb(CMD_NOP, REG_CMD(info));
+		outb(CMD_RESETSCSI, REG_CMD(info));
+		result |= SCSI_RESET_HOST_RESET | SCSI_RESET_BUS_RESET;
+	}
+
+	if (result & SCSI_RESET_HOST_RESET)
+		fas216_init_chip(info);
+
+	/*
+	 * Signal all commands in progress have been reset
+	 */
+	fas216_reportstatus (&info->SCpnt, &SCpnt, DID_RESET << 16);
+
+	while ((SCptr = queue_remove (&info->queues.disconnected)) != NULL)
+		fas216_reportstatus (&SCptr, &SCpnt, DID_RESET << 16);
+
+	if (SCpnt) {
+		/*
+		 * Command not found on disconnected queue, nor currently
+		 * executing command - check pending commands
+		 */
+		if (info->origSCpnt == SCpnt)
+			info->origSCpnt = NULL;
+
+		queue_removecmd(&info->queues.issue, SCpnt);
+
+		SCpnt->result = DID_RESET << 16;
+		SCpnt->scsi_done (SCpnt);
+	}
+
+	printk ("\n");
+
+	return result | SCSI_RESET_SUCCESS;
+}
+
+/* Function: int fas216_init (struct Scsi_Host *instance)
+ * Purpose : initialise FAS/NCR/AMD SCSI ic.
+ * Params  : instance - a driver-specific filled-out structure
+ * Returns : 0 on success
+ */
+int fas216_init (struct Scsi_Host *instance)
+{
+	FAS216_Info *info = (FAS216_Info *)instance->hostdata;
+	unsigned long flags;
+	int target_jiffies;
+
+	info->host = instance;
+	info->scsi.cfg[0] = instance->this_id;
+	info->scsi.cfg[1] = CNTL2_ENF | CNTL2_S2FE;
+	info->scsi.cfg[2] = CNTL3_ADDIDCHK | CNTL3_G2CB | CNTL3_FASTSCSI | CNTL3_FASTCLK;
+	info->scsi.type = "unknown";
+	info->SCpnt = NULL;
+	fas216_reset_state(info);
+
+	memset (&info->stats, 0, sizeof (info->stats));
+
+	msgqueue_initialise (&info->scsi.msgs);
+
+	if (!queue_initialise (&info->queues.issue))
+		return 1;
+
+	if (!queue_initialise (&info->queues.disconnected)) {
+		queue_free (&info->queues.issue);
+		return 1;
+	}
+
+	outb(0, REG_CNTL3(info));
+	outb(CNTL2_S2FE, REG_CNTL2(info));
+
+	if ((inb(REG_CNTL2(info)) & (~0xe0)) != CNTL2_S2FE) {
+		info->scsi.type = "NCR53C90";
+	} else {
+		outb(0, REG_CNTL2(info));
+		outb(0, REG_CNTL3(info));
+		outb(5, REG_CNTL3(info));
+		if (inb(REG_CNTL3(info)) != 5) {
+			info->scsi.type = "NCR53C90A";
+		} else {
+			outb(0, REG_CNTL3(info));
+			info->scsi.type = "NCR53C9x";
+		}
+	}
+
+
+	outb(CNTL3_IDENABLE, REG_CNTL3(info));
+	outb(0, REG_CNTL3(info));
+
+	outb(CMD_RESETCHIP, REG_CMD(info));
+	outb(CMD_WITHDMA | CMD_NOP, REG_CMD(info));
+	outb(CNTL2_ENF, REG_CNTL2(info));
+	outb(CMD_RESETCHIP, REG_CMD(info));
+	switch (inb(REG1_ID(info))) {
+	case 12:
+		info->scsi.type = "Am53CF94";
+		break;
+	default:
+		break;
+	}
+
+	udelay (300);
+
+	/* now for the real initialisation */
+	fas216_init_chip(info);
+
+	outb(info->scsi.cfg[0] | CNTL1_DISR, REG_CNTL1(info));
+	outb(CMD_RESETSCSI, REG_CMD(info));
+
+	/* scsi standard says 250ms */
+	target_jiffies = jiffies + (25 * HZ) / 100;
+	save_flags (flags);
+	sti ();
+
+	while (jiffies < target_jiffies) barrier ();
+
+	restore_flags (flags);
+
+	outb(info->scsi.cfg[0], REG_CNTL1(info));
+	inb(REG_INST(info));
+
+	return 0;
+}
+
+/* Function: int fas216_release (struct Scsi_Host *instance)
+ * Purpose : release all resources and put everything to bed for
+ *           FAS/NCR/AMD SCSI ic.
+ * Params  : instance - a driver-specific filled-out structure
+ * Returns : 0 on success
+ */
+int fas216_release (struct Scsi_Host *instance)
+{
+	FAS216_Info *info = (FAS216_Info *)instance->hostdata;
+
+	outb(CMD_RESETCHIP, REG_CMD(info));
+	queue_free (&info->queues.disconnected);
+	queue_free (&info->queues.issue);
+
+	return 0;
+}
+
+EXPORT_SYMBOL(fas216_init);
+EXPORT_SYMBOL(fas216_abort);
+EXPORT_SYMBOL(fas216_reset);
+EXPORT_SYMBOL(fas216_queue_command);
+EXPORT_SYMBOL(fas216_command);
+EXPORT_SYMBOL(fas216_intr);
+EXPORT_SYMBOL(fas216_release);
+
+#ifdef MODULE
+int init_module (void)
+{
+	return 0;
+}
+
+void cleanup_module (void)
+{
+}
+#endif
diff -u --recursive --new-file v2.1.87/linux/drivers/acorn/scsi/fas216.h linux/drivers/acorn/scsi/fas216.h
--- v2.1.87/linux/drivers/acorn/scsi/fas216.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/acorn/scsi/fas216.h	Wed Feb 18 14:13:52 1998
@@ -0,0 +1,357 @@
+/*
+ * FAS216 generic driver
+ *
+ * Copyright (C) 1997-1998 Russell King
+ *
+ * NOTE! This file should be viewed using a console with
+ * >100 character width (since it uses 8-space tabs)
+ * (it used to fit in 80-columns with 4 space)
+ */
+#ifndef FAS216_H
+#define FAS216_H
+
+#ifndef NO_IRQ
+#define NO_IRQ 255
+#endif
+
+#include "queue.h"
+#include "msgqueue.h"
+
+/* FAS register definitions */
+
+/* transfer count low */
+#define REG_CTCL(x)		((x)->scsi.io_port)
+#define REG_STCL(x)		((x)->scsi.io_port)
+
+/* transfer count medium */
+#define REG_CTCM(x)		((x)->scsi.io_port + (1 << (x)->scsi.io_shift))
+#define REG_STCM(x)		((x)->scsi.io_port + (1 << (x)->scsi.io_shift))
+
+/* fifo data */
+#define REG_FF(x)		((x)->scsi.io_port + (2 << (x)->scsi.io_shift))
+
+/* command */
+#define REG_CMD(x)		((x)->scsi.io_port + (3 << (x)->scsi.io_shift))
+#define CMD_NOP			0x00
+#define CMD_FLUSHFIFO		0x01
+#define CMD_RESETCHIP		0x02
+#define CMD_RESETSCSI		0x03
+
+#define CMD_TRANSFERINFO	0x10
+#define CMD_INITCMDCOMPLETE	0x11
+#define CMD_MSGACCEPTED		0x12
+#define CMD_SETATN		0x1a
+#define CMD_RSETATN		0x1b
+
+#define CMD_SELECTWOATN		0x41
+#define CMD_SELECTATN		0x42
+#define CMD_SELECTATNSTOP	0x43
+#define CMD_ENABLESEL		0x44
+#define CMD_DISABLESEL		0x45
+#define CMD_SELECTATN3		0x46
+#define CMD_RESEL3		0x47
+
+#define CMD_WITHDMA		0x80
+
+/* status register (read) */
+#define REG_STAT(x)		((x)->scsi.io_port + (4 << (x)->scsi.io_shift))
+#define STAT_IO		(1 << 0)				/* IO phase		*/
+#define STAT_CD		(1 << 1)				/* CD phase		*/
+#define STAT_MSG		(1 << 2)			/* MSG phase		*/
+#define STAT_TRANSFERDONE	(1 << 3)			/* Transfer completed	*/
+#define STAT_TRANSFERCNTZ	(1 << 4)			/* Transfer counter is zero */
+#define STAT_PARITYERROR	(1 << 5)			/* Parity error		*/
+#define STAT_REALBAD		(1 << 6)			/* Something bad	*/
+#define STAT_INT		(1 << 7)			/* Interrupt		*/
+
+#define STAT_BUSMASK		(STAT_MSG|STAT_CD|STAT_IO)
+#define STAT_DATAOUT		(0)				/* Data out		*/
+#define STAT_DATAIN		(STAT_IO)			/* Data in		*/
+#define STAT_COMMAND		(STAT_CD)			/* Command out		*/
+#define STAT_STATUS		(STAT_CD|STAT_IO)		/* Status In		*/
+#define STAT_MESGOUT		(STAT_MSG|STAT_CD)		/* Message out		*/
+#define STAT_MESGIN		(STAT_MSG|STAT_CD|STAT_IO)	/* Message In		*/
+
+/* bus ID for select / reselect */
+#define REG_SDID(x)		((x)->scsi.io_port + (4 << (x)->scsi.io_shift))
+#define BUSID(target)		((target) & 7)
+
+/* Interrupt status register (read) */
+#define REG_INST(x)		((x)->scsi.io_port + (5 << (x)->scsi.io_shift))
+#define INST_SELWOATN		(1 << 0)			/* Select w/o ATN	*/
+#define INST_SELATN		(1 << 1)			/* Select w/ATN		*/
+#define INST_RESELECTED		(1 << 2)			/* Reselected		*/
+#define INST_FUNCDONE		(1 << 3)			/* Function done	*/
+#define INST_BUSSERVICE		(1 << 4)			/* Bus service		*/
+#define INST_DISCONNECT		(1 << 5)			/* Disconnect		*/
+#define INST_ILLEGALCMD		(1 << 6)			/* Illegal command	*/
+#define INST_BUSRESET		(1 << 7)			/* SCSI Bus reset	*/
+
+/* Timeout register (write) */
+#define REG_STIM(x)		((x)->scsi.io_port + (5 << (x)->scsi.io_shift))
+
+/* Sequence step register (read) */
+#define REG_IS(x)		((x)->scsi.io_port + (6 << (x)->scsi.io_shift))
+#define IS_BITS			0x07
+#define IS_SELARB		0x00				/* Select & Arb ok	*/
+#define IS_MSGBYTESENT		0x01				/* One byte message sent*/
+#define IS_NOTCOMMAND		0x02				/* Not in command state	*/
+#define IS_EARLYPHASE		0x03				/* Early phase change	*/
+#define IS_COMPLETE		0x04				/* Command ok		*/
+
+/* Transfer period step (write) */
+#define REG_STP(x)		((x)->scsi.io_port + (6 << (x)->scsi.io_shift))
+
+/* Synchronous Offset (write) */
+#define REG_SOF(x)		((x)->scsi.io_port + (7 << (x)->scsi.io_shift))
+
+/* Fifo state register (read) */
+#define REG_CFIS(x)		((x)->scsi.io_port + (7 << (x)->scsi.io_shift))
+#define CFIS_CF			0x1f				/* Num bytes in FIFO	*/
+#define CFIS_IS			0xe0				/* Step			*/
+
+/* config register 1 */
+#define REG_CNTL1(x)		((x)->scsi.io_port + (8 << (x)->scsi.io_shift))
+#define CNTL1_CID		(7 << 0)			/* Chip ID			*/
+#define CNTL1_STE		(1 << 3)			/* Self test enable		*/
+#define CNTL1_PERE		(1 << 4)			/* Parity enable reporting en.	*/
+#define CNTL1_PTE		(1 << 5)			/* Parity test enable		*/
+#define CNTL1_DISR		(1 << 6)			/* Disable Irq on SCSI reset	*/
+#define CNTL1_ETM		(1 << 7)			/* Extended Timing Mode		*/
+
+/* Clock conversion factor (read) */
+#define REG_CLKF(x)		((x)->scsi.io_port + (9 << (x)->scsi.io_shift))
+#define CLKF_F37MHZ		0x00				/* 35.01 - 40 MHz		*/
+#define CLKF_F10MHZ		0x02				/* 10 MHz			*/
+#define CLKF_F12MHZ		0x03				/* 10.01 - 15 MHz		*/
+#define CLKF_F17MHZ		0x04				/* 15.01 - 20 MHz		*/
+#define CLKF_F22MHZ		0x05				/* 20.01 - 25 MHz		*/
+#define CLKF_F27MHZ		0x06				/* 25.01 - 30 MHz		*/
+#define CLKF_F32MHZ		0x07				/* 30.01 - 35 MHz		*/
+
+/* Chip test register (write) */
+#define REG0_FTM(x)		((x)->scsi.io_port + (10 << (x)->scsi.io_shift))
+#define TEST_FTM		0x01				/* Force target mode		*/
+#define TEST_FIM		0x02				/* Force initiator mode		*/
+#define TEST_FHI		0x04				/* Force high impedance mode	*/
+
+/* Configuration register 2 (read/write) */
+#define REG_CNTL2(x)		((x)->scsi.io_port + (11 << (x)->scsi.io_shift))
+#define CNTL2_PGDP		(1 << 0)			/* Pass Th/Generate Data Parity	*/
+#define CNTL2_PGRP		(1 << 1)			/* Pass Th/Generate Reg Parity	*/
+#define CNTL2_ACDPE		(1 << 2)			/* Abort on Cmd/Data Parity Err	*/
+#define CNTL2_S2FE		(1 << 3)			/* SCSI2 Features Enable	*/
+#define CNTL2_TSDR		(1 << 4)			/* Tristate DREQ		*/
+#define CNTL2_SBO		(1 << 5)			/* Select Byte Order		*/
+#define CNTL2_ENF		(1 << 6)			/* Enable features		*/
+#define CNTL2_DAE		(1 << 7)			/* Data Alignment Enable	*/
+
+/* Configuration register 3 (read/write) */
+#define REG_CNTL3(x)		((x)->scsi.io_port + (12 << (x)->scsi.io_shift))
+#define CNTL3_BS8		(1 << 0)			/* Burst size 8			*/
+#define CNTL3_MDM		(1 << 1)			/* Modify DMA mode		*/
+#define CNTL3_LBTM		(1 << 2)			/* Last Byte Transfer mode	*/
+#define CNTL3_FASTCLK		(1 << 3)			/* Fast SCSI clocking		*/
+#define CNTL3_FASTSCSI		(1 << 4)			/* Fast SCSI			*/
+#define CNTL3_G2CB		(1 << 5)			/* Group2 SCSI support		*/
+#define CNTL3_QTAG		(1 << 6)			/* Enable 3 byte msgs		*/
+#define CNTL3_ADIDCHK		(1 << 7)			/* Additional ID check		*/
+
+/* High transfer count (read/write) */
+#define REG_CTCH(x)		((x)->scsi.io_port + (14 << (x)->scsi.io_shift))
+#define REG_STCH(x)		((x)->scsi.io_port + (14 << (x)->scsi.io_shift))
+
+/* ID reigster (read only) */
+#define REG1_ID(x)		((x)->scsi.io_port + (14 << (x)->scsi.io_shift))
+
+/* Data alignment */
+#define REG0_DAL(x)		((x)->scsi.io_port + (15 << (x)->scsi.io_shift))
+
+typedef enum {
+	PHASE_IDLE,					/* we're not planning on doing anything	*/
+	PHASE_SELECTION,				/* selecting a device			*/
+	PHASE_RECONNECTED,				/* reconnected				*/
+	PHASE_DATAOUT,					/* data out to device			*/
+	PHASE_DATAIN,					/* data in from device			*/
+	PHASE_MSGIN,					/* message in from device		*/
+	PHASE_MSGOUT,					/* message out to device		*/
+	PHASE_AFTERMSGOUT,				/* after message out phase		*/
+	PHASE_STATUS,					/* status from device			*/
+	PHASE_DISCONNECT,				/* disconnecting from bus		*/
+	PHASE_DONE					/* Command complete			*/
+} phase_t;
+
+typedef enum {
+	DMA_OUT,					/* DMA from memory to chip		*/
+	DMA_IN						/* DMA from chip to memory		*/
+} fasdmadir_t;
+
+typedef enum {
+	fasdma_none,					/* No dma				*/
+	fasdma_pseudo,					/* Pseudo DMA				*/
+	fasdma_real_block,				/* Real DMA, on block by block basis	*/
+	fasdma_real_all					/* Real DMA, on request by request	*/
+} fasdmatype_t;
+
+typedef enum {
+	syncneg_start,					/* Negociate with device for Sync xfers	*/
+	syncneg_sent,					/* Sync Xfer negociation sent		*/
+	syncnsg_complete				/* Sync Xfer complete			*/
+} syncneg_t;
+
+typedef struct {
+	struct Scsi_Host	*host;			/* host					*/
+	Scsi_Cmnd		*SCpnt;			/* currently processing command		*/
+	Scsi_Cmnd		*origSCpnt;		/* original connecting command		*/
+
+	/* driver information */
+	struct {
+		unsigned int	io_port;		/* base address of FAS216		*/
+		unsigned int	io_shift;		/* shift to adjust reg offsets by	*/
+		unsigned char	irq;			/* interrupt				*/
+		unsigned char	cfg[4];			/* configuration registers		*/
+		const char	*type;			/* chip type				*/
+		phase_t		phase;			/* current phase			*/
+
+		struct {
+			unsigned char	target;		/* reconnected target			*/
+			unsigned char	lun;		/* reconnected lun			*/
+			unsigned char	tag;		/* reconnected tag			*/
+		} reconnected;
+
+		Scsi_Pointer	SCp;			/* current commands data pointer	*/
+
+		MsgQueue_t	msgs;			/* message queue for connected device	*/
+
+		unsigned short	last_message;		/* last message to be sent		*/
+
+		unsigned char	disconnectable:1;	/* this command can be disconnected	*/
+		unsigned char	aborting:1;		/* aborting command			*/
+	} scsi;
+
+	/* statistics information */
+	struct {
+		unsigned int	queues;
+		unsigned int	removes;
+		unsigned int	fins;
+		unsigned int	reads;
+		unsigned int	writes;
+		unsigned int	miscs;
+		unsigned int	disconnects;
+		unsigned int	aborts;
+		unsigned int	resets;
+	} stats;
+
+	/* configuration information */
+	struct {
+		unsigned char	clockrate;		/* clock rate of FAS device (MHz)	*/
+		unsigned char	select_timeout;		/* timeout (R5)				*/
+		unsigned int	asyncperiod;		/* Async transfer period (ns)		*/
+		unsigned char	sync_max_depth;		/* Synchronous xfer max fifo depth	*/
+	} ifcfg;
+
+	/* queue handling */
+	struct {
+	    	Queue_t		issue;			/* issue queue				*/
+    		Queue_t		disconnected;		/* disconnected command queue		*/
+	} queues;
+
+	/* per-device info */
+	struct {
+		unsigned char	disconnect_ok:1;	/* device can disconnect		*/
+		unsigned char	stp;			/* synchronous transfer period		*/
+		unsigned char	sof;			/* synchronous offset register		*/
+		syncneg_t	negstate;		/* synchronous transfer mode		*/
+	} device[8];
+	unsigned char	busyluns[8];			/* array of bits indicating LUNs busy	*/
+
+	/* dma */
+	struct {
+		fasdmatype_t	transfer_type;		/* current type of DMA transfer		*/
+		fasdmatype_t	(*setup) (struct Scsi_Host *host, Scsi_Pointer *SCp, fasdmadir_t direction);
+		int		(*pseudo)(struct Scsi_Host *host, Scsi_Pointer *SCp, fasdmadir_t direction, int transfer);
+		void		(*stop)  (struct Scsi_Host *host, Scsi_Pointer *SCp);
+	} dma;
+
+	/* miscellaneous */
+	int			internal_done;		/* flag to indicate request done */
+} FAS216_Info;
+
+/*
+ * Function: int fas216_init (struct Scsi_Host *instance)
+ *
+ * Purpose : initialise FAS/NCR/AMD SCSI ic.
+ *
+ * Params  : instance - a driver-specific filled-out structure
+ *
+ * Returns : 0 on success
+ */
+extern int fas216_init (struct Scsi_Host *instance);
+
+/*
+ * Function: int fas216_abort (Scsi_Cmnd *SCpnt)
+ *
+ * Purpose : abort a command if something horrible happens.
+ *
+ * Params  : SCpnt - Command that is believed to be causing a problem.
+ *
+ * Returns : one of SCSI_ABORT_ macros.
+ */
+extern int fas216_abort (Scsi_Cmnd *);
+
+/*
+ * Function: int fas216_reset (Scsi_Cmnd *SCpnt, unsigned int reset_flags)
+ *
+ * Purpose : resets the adapter if something horrible happens.
+ *
+ * Params  : SCpnt - Command that is believed to be causing a problem.
+ *	     reset_flags - flags indicating reset type that is believed to be required.
+ *
+ * Returns : one of SCSI_RESET_ macros, or'd with the SCSI_RESET_*_RESET macros.
+ */
+extern int fas216_reset (Scsi_Cmnd *, unsigned int);
+
+/*
+ * Function: int fas216_queue_command (Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *))
+ *
+ * Purpose : queue a command for adapter to process.
+ *
+ * Params  : SCpnt - Command to queue
+ *	     done  - done function to call once command is complete
+ *
+ * Returns : 0 - success, else error
+ */
+extern int fas216_queue_command (Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
+
+/*
+ * Function: int fas216_command (Scsi_Cmnd *SCpnt)
+ *
+ * Purpose : queue a command for adapter to process.
+ *
+ * Params  : SCpnt - Command to queue
+ *
+ * Returns : scsi result code
+ */
+extern int fas216_command (Scsi_Cmnd *);
+
+/*
+ * Function: void fas216_intr (struct Scsi_Host *instance)
+ *
+ * Purpose : handle interrupts from the interface to progress a command
+ *
+ * Params  : instance - interface to service
+ */
+extern void fas216_intr (struct Scsi_Host *instance);
+
+/*
+ * Function: int fas216_release (struct Scsi_Host *instance)
+ *
+ * Purpose : release all resources and put everything to bed for FAS/NCR/AMD SCSI ic.
+ *
+ * Params  : instance - a driver-specific filled-out structure
+ *
+ * Returns : 0 on success
+ */
+extern int fas216_release (struct Scsi_Host *instance);
+
+#endif /* FAS216_H */
diff -u --recursive --new-file v2.1.87/linux/drivers/acorn/scsi/msgqueue.c linux/drivers/acorn/scsi/msgqueue.c
--- v2.1.87/linux/drivers/acorn/scsi/msgqueue.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/acorn/scsi/msgqueue.c	Sat Feb  7 07:17:46 1998
@@ -0,0 +1,175 @@
+/*
+ * drivers/acorn/scsi/msgqueue.c: message queue handling
+ *
+ * Copyright (C) 1997,8 Russell King
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/stddef.h>
+
+#include "msgqueue.h"
+
+/*
+ * Function: struct msgqueue_entry *mqe_alloc (MsgQueue_t *msgq)
+ * Purpose : Allocate a message queue entry
+ * Params  : msgq - message queue to claim entry for
+ * Returns : message queue entry or NULL.
+ */
+static struct msgqueue_entry *mqe_alloc (MsgQueue_t *msgq)
+{
+	struct msgqueue_entry *mq;
+
+	if ((mq = msgq->free) != NULL)
+		msgq->free = mq->next;
+
+	return mq;
+}
+
+/*
+ * Function: void mqe_free (MsgQueue_t *msgq, struct msgqueue_entry *mq)
+ * Purpose : free a message queue entry
+ * Params  : msgq - message queue to free entry from
+ *	     mq   - message queue entry to free
+ */
+static void mqe_free (MsgQueue_t *msgq, struct msgqueue_entry *mq)
+{
+	if (mq) {
+		mq->next = msgq->free;
+		msgq->free = mq;
+	}
+}
+
+/*
+ * Function: void msgqueue_initialise (MsgQueue_t *msgq)
+ * Purpose : initialise a message queue
+ * Params  : msgq - queue to initialise
+ */
+void msgqueue_initialise (MsgQueue_t *msgq)
+{
+	int i;
+
+	msgq->qe = NULL;
+	msgq->free = &msgq->entries[0];
+
+	for (i = 0; i < NR_MESSAGES; i++)
+		msgq->entries[i].next = &msgq->entries[i + 1];
+
+	msgq->entries[NR_MESSAGES - 1].next = NULL;
+}
+
+
+/*
+ * Function: void msgqueue_free (MsgQueue_t *msgq)
+ * Purpose : free a queue
+ * Params  : msgq - queue to free
+ */
+void msgqueue_free (MsgQueue_t *msgq)
+{
+}
+
+/*
+ * Function: int msgqueue_msglength (MsgQueue_t *msgq)
+ * Purpose : calculate the total length of all messages on the message queue
+ * Params  : msgq - queue to examine
+ * Returns : number of bytes of messages in queue
+ */
+int msgqueue_msglength (MsgQueue_t *msgq)
+{
+	struct msgqueue_entry *mq = msgq->qe;
+	int length = 0;
+
+	for (mq = msgq->qe; mq; mq = mq->next)
+		length += mq->length;
+
+	return length;
+}
+
+/*
+ * Function: char *msgqueue_getnextmsg (MsgQueue_t *msgq, int *length)
+ * Purpose : return a message & its length
+ * Params  : msgq   - queue to obtain message from
+ *	     length - pointer to int for message length
+ * Returns : pointer to message string
+ */
+char *msgqueue_getnextmsg (MsgQueue_t *msgq, int *length)
+{
+	struct msgqueue_entry *mq;
+
+	if ((mq = msgq->qe) != NULL) {
+		msgq->qe = mq->next;
+		mqe_free (msgq, mq);
+		*length = mq->length;
+	}
+
+	return mq ? mq->msg : NULL;
+}
+
+/*
+ * Function: int msgqueue_addmsg (MsgQueue_t *msgq, int length, ...)
+ * Purpose : add a message onto a message queue
+ * Params  : msgq   - queue to add message on
+ *	     length - length of message
+ *	     ...    - message bytes
+ * Returns : != 0 if successful
+ */
+int msgqueue_addmsg (MsgQueue_t *msgq, int length, ...)
+{
+	struct msgqueue_entry *mq = mqe_alloc (msgq);
+	va_list ap;
+
+	if (mq) {
+		struct msgqueue_entry **mqp;
+		int i;
+
+		va_start (ap, length);
+		for (i = 0; i < length; i++)
+			mq->msg[i] = va_arg (ap, unsigned char);
+		va_end (ap);
+
+		mq->length = length;
+		mq->next = NULL;
+
+		mqp = &msgq->qe;
+		while (*mqp)
+			mqp = &(*mqp)->next;
+
+		*mqp = mq;
+	}
+
+	return mq != NULL;
+}
+
+/*
+ * Function: void msgqueue_flush (MsgQueue_t *msgq)
+ * Purpose : flush all messages from message queue
+ * Params  : msgq - queue to flush
+ */
+void msgqueue_flush (MsgQueue_t *msgq)
+{
+	struct msgqueue_entry *mq, *mqnext;
+
+	for (mq = msgq->qe; mq; mq = mqnext) {
+		mqnext = mq->next;
+		mqe_free (msgq, mq);
+	}
+	msgq->qe = NULL;
+}
+
+EXPORT_SYMBOL(msgqueue_initialise);
+EXPORT_SYMBOL(msgqueue_free);
+EXPORT_SYMBOL(msgqueue_msglength);
+EXPORT_SYMBOL(msgqueue_getnextmsg);
+EXPORT_SYMBOL(msgqueue_addmsg);
+EXPORT_SYMBOL(msgqueue_flush);
+
+#ifdef MODULE
+int init_module (void)
+{
+	return 0;
+}
+
+void cleanup_module (void)
+{
+}
+#endif
diff -u --recursive --new-file v2.1.87/linux/drivers/acorn/scsi/msgqueue.h linux/drivers/acorn/scsi/msgqueue.h
--- v2.1.87/linux/drivers/acorn/scsi/msgqueue.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/acorn/scsi/msgqueue.h	Sat Oct  4 09:14:47 1997
@@ -0,0 +1,71 @@
+/*
+ * msgqueue.h: message queue handling
+ *
+ * (c) 1997 Russell King
+ */
+#ifndef MSGQUEUE_H
+#define MSGQUEUE_H
+
+struct msgqueue_entry {
+    char msg[8];
+    int length;
+    struct msgqueue_entry *next;
+};
+
+#define NR_MESSAGES 4
+
+typedef struct {
+    struct msgqueue_entry *qe;
+    struct msgqueue_entry *free;
+    struct msgqueue_entry entries[NR_MESSAGES];
+} MsgQueue_t;
+
+/*
+ * Function: void msgqueue_initialise (MsgQueue_t *msgq)
+ * Purpose : initialise a message queue
+ * Params  : msgq - queue to initialise
+ */
+extern void msgqueue_initialise (MsgQueue_t *msgq);
+
+/*
+ * Function: void msgqueue_free (MsgQueue_t *msgq)
+ * Purpose : free a queue
+ * Params  : msgq - queue to free
+ */
+extern void msgqueue_free (MsgQueue_t *msgq);
+
+/*
+ * Function: int msgqueue_msglength (MsgQueue_t *msgq)
+ * Purpose : calculate the total length of all messages on the message queue
+ * Params  : msgq - queue to examine
+ * Returns : number of bytes of messages in queue
+ */
+extern int msgqueue_msglength (MsgQueue_t *msgq);
+
+/*
+ * Function: char *msgqueue_getnextmsg (MsgQueue_t *msgq, int *length)
+ * Purpose : return a message & its length
+ * Params  : msgq   - queue to obtain message from
+ *	     length - pointer to int for message length
+ * Returns : pointer to message string
+ */
+extern char *msgqueue_getnextmsg (MsgQueue_t *msgq, int *length);
+
+/*
+ * Function: int msgqueue_addmsg (MsgQueue_t *msgq, int length, ...)
+ * Purpose : add a message onto a message queue
+ * Params  : msgq   - queue to add message on
+ *	     length - length of message
+ *	     ...    - message bytes
+ * Returns : != 0 if successful
+ */
+extern int msgqueue_addmsg (MsgQueue_t *msgq, int length, ...);
+
+/*
+ * Function: void msgqueue_flush (MsgQueue_t *msgq)
+ * Purpose : flush all messages from message queue
+ * Params  : msgq - queue to flush
+ */
+extern void msgqueue_flush (MsgQueue_t *msgq);
+
+#endif
diff -u --recursive --new-file v2.1.87/linux/drivers/acorn/scsi/oak.c linux/drivers/acorn/scsi/oak.c
--- v2.1.87/linux/drivers/acorn/scsi/oak.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/acorn/scsi/oak.c	Fri Feb  6 16:26:57 1998
@@ -0,0 +1,228 @@
+#define AUTOSENSE
+/*#define PSEUDO_DMA*/
+
+/*
+ * Oak Generic NCR5380 driver
+ *
+ * Copyright 1995, Russell King
+ *
+ * ALPHA RELEASE 1.
+ *
+ * For more information, please consult
+ *
+ * NCR 5380 Family
+ * SCSI Protocol Controller
+ * Databook
+ *
+ * NCR Microelectronics
+ * 1635 Aeroplaza Drive
+ * Colorado Springs, CO 80916
+ * 1+ (719) 578-3400
+ * 1+ (800) 334-5454
+ */
+
+/*
+ * Options :
+ *
+ * PARITY - enable parity checking.  Not supported.
+ *
+ * SCSI2 - enable support for SCSI-II tagged queueing.  Untested.
+ *
+ * USLEEP - enable support for devices that don't disconnect.  Untested.
+ */
+
+/*
+ * $Log: oak.c,v $
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/ioport.h>
+#include <linux/blk.h>
+#include <linux/init.h>
+
+#include <asm/ecard.h>
+#include <asm/io.h>
+#include <asm/system.h>
+
+#include "../../scsi/scsi.h"
+#include "../../scsi/hosts.h"
+#include "oak.h"
+#include "../../scsi/NCR5380.h"
+#include "../../scsi/constants.h"
+
+#undef START_DMA_INITIATOR_RECEIVE_REG
+#define START_DMA_INITIATOR_RECEIVE_REG (7 + 128)
+
+static const card_ids oakscsi_cids[] = {
+	{ MANU_OAK, PROD_OAK_SCSI },
+	{ 0xffff, 0xffff }
+};
+
+static struct proc_dir_entry proc_scsi_oakscsi = {
+	PROC_SCSI_PAS16, 7, "oakscsi", S_IFDIR | S_IRUGO | S_IXUGO, 2
+};
+
+#define OAK_ADDRESS(card) (ecard_address((card), ECARD_MEMC, 0))
+#define OAK_IRQ(card)	  (IRQ_NONE)
+/*
+ * Function : int oakscsi_detect(Scsi_Host_Template * tpnt)
+ *
+ * Purpose : initializes oak NCR5380 driver based on the
+ *	command line / compile time port and irq definitions.
+ *
+ * Inputs : tpnt - template for this SCSI adapter.
+ *
+ * Returns : 1 if a host adapter was found, 0 if not.
+ *
+ */
+static struct expansion_card *ecs[4];
+
+int oakscsi_detect(Scsi_Host_Template * tpnt)
+{
+    int count = 0;
+    struct Scsi_Host *instance;
+
+    tpnt->proc_dir = &proc_scsi_oakscsi;
+
+    memset (ecs, 0, sizeof (ecs));
+
+    ecard_startfind ();
+
+    while(1) {
+        if ((ecs[count] = ecard_find(0, oakscsi_cids)) == NULL)
+            break;
+
+        instance = scsi_register (tpnt, sizeof(struct NCR5380_hostdata));
+        instance->io_port = OAK_ADDRESS(ecs[count]);
+        instance->irq = OAK_IRQ(ecs[count]);
+
+	NCR5380_init(instance, 0);
+	ecard_claim(ecs[count]);
+
+	instance->n_io_port = 255;
+	request_region (instance->io_port, instance->n_io_port, "Oak SCSI");
+
+	if (instance->irq != IRQ_NONE)
+	    if (request_irq(instance->irq, oakscsi_intr, SA_INTERRUPT, "Oak SCSI", NULL)) {
+		printk("scsi%d: IRQ%d not free, interrupts disabled\n",
+		    instance->host_no, instance->irq);
+		instance->irq = IRQ_NONE;
+	    }
+
+	if (instance->irq != IRQ_NONE) {
+	    printk("scsi%d: eek! Interrupts enabled, but I don't think\n", instance->host_no);
+	    printk("scsi%d: that the board had an interrupt!\n", instance->host_no);
+	}
+
+	printk("scsi%d: at port %X irq", instance->host_no, instance->io_port);
+	if (instance->irq == IRQ_NONE)
+	    printk ("s disabled");
+	else
+	    printk (" %d", instance->irq);
+	printk(" options CAN_QUEUE=%d  CMD_PER_LUN=%d release=%d",
+	    CAN_QUEUE, CMD_PER_LUN, OAKSCSI_PUBLIC_RELEASE);
+	printk("\nscsi%d:", instance->host_no);
+	NCR5380_print_options(instance);
+	printk("\n");
+
+	++count;
+    }
+#ifdef MODULE
+    if(count == 0)
+        printk("No oak scsi devices found\n");
+#endif
+    return count;
+}
+
+int oakscsi_release (struct Scsi_Host *shpnt)
+{
+	int i;
+
+	if (shpnt->irq != IRQ_NONE)
+		free_irq (shpnt->irq, NULL);
+	if (shpnt->io_port)
+		release_region (shpnt->io_port, shpnt->n_io_port);
+
+	for (i = 0; i < 4; i++)
+		if (shpnt->io_port == OAK_ADDRESS(ecs[i]))
+			ecard_release (ecs[i]);
+	return 0;
+}
+
+const char * oakscsi_info (struct Scsi_Host *spnt) {
+    return "";
+}
+
+#define STAT(p)   inw(p + 144)
+extern void inswb(int from, void *to, int len);
+
+static inline int NCR5380_pwrite(struct Scsi_Host *instance, unsigned char *addr,
+              int len)
+{
+  int iobase = instance->io_port;
+printk("writing %p len %d\n",addr, len);
+  if(!len) return -1;
+
+  while(1)
+  {
+    int status;
+    while(((status = STAT(iobase)) & 0x100)==0);
+  }
+}
+
+static inline int NCR5380_pread(struct Scsi_Host *instance, unsigned char *addr,
+              int len)
+{
+  int iobase = instance->io_port;
+printk("reading %p len %d\n", addr, len);
+  while(len > 0)
+  {
+    int status, timeout;
+    unsigned long b;
+    
+    timeout = 0x01FFFFFF;
+    
+    while(((status = STAT(iobase)) & 0x100)==0)
+    {
+      timeout--;
+      if(status & 0x200 || !timeout)
+      {
+        printk("status = %08X\n",status);
+        return 1;
+      }
+    }
+    if(len >= 128)
+    {
+      inswb(iobase + 136, addr, 128);
+      addr += 128;
+      len -= 128;
+    }
+    else
+    {
+      b = (unsigned long) inw(iobase + 136);
+      *addr ++ = b;
+      len -= 1;
+      if(len)
+        *addr ++ = b>>8;
+      len -= 1;
+    }
+  }
+  return 0;
+}
+
+#define oakscsi_read(instance,reg)	(inb((instance)->io_port + (reg)))
+#define oakscsi_write(instance,reg,val)	(outb((val), (instance)->io_port + (reg)))
+
+#undef STAT
+
+#include "../../scsi/NCR5380.c"
+
+#ifdef MODULE
+
+Scsi_Host_Template driver_template = OAK_NCR5380;
+
+#include "../../scsi/scsi_module.c"
+#endif
diff -u --recursive --new-file v2.1.87/linux/drivers/acorn/scsi/oak.h linux/drivers/acorn/scsi/oak.h
--- v2.1.87/linux/drivers/acorn/scsi/oak.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/acorn/scsi/oak.h	Sun Dec 28 09:20:10 1997
@@ -0,0 +1,97 @@
+/*
+ * Cumana Generic NCR5380 driver defines
+ *
+ * Copyright 1993, Drew Eckhardt
+ *	Visionary Computing
+ *	(Unix and Linux consulting and custom programming)
+ *	drew@colorado.edu
+ *      +1 (303) 440-4894
+ *
+ * ALPHA RELEASE 1.
+ *
+ * For more information, please consult
+ *
+ * NCR 5380 Family
+ * SCSI Protocol Controller
+ * Databook
+ *
+ * NCR Microelectronics
+ * 1635 Aeroplaza Drive
+ * Colorado Springs, CO 80916
+ * 1+ (719) 578-3400
+ * 1+ (800) 334-5454
+ */
+
+/*
+ * $Log: oak_NCR5380.h,v $
+ */
+
+#ifndef OAK_NCR5380_H
+#define OAK_NCR5380_H
+
+#define OAKSCSI_PUBLIC_RELEASE 1
+
+
+#ifndef ASM
+int oakscsi_abort(Scsi_Cmnd *);
+int oakscsi_detect(Scsi_Host_Template *);
+int oakscsi_release(struct Scsi_Host *);
+const char *oakscsi_info(struct Scsi_Host *);
+int oakscsi_reset(Scsi_Cmnd *, unsigned int);
+int oakscsi_queue_command(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
+int oakscsi_proc_info (char *buffer, char **start, off_t off,
+		int length, int hostno, int inout);
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+#ifndef CMD_PER_LUN
+#define CMD_PER_LUN 2
+#endif
+
+#ifndef CAN_QUEUE
+#define CAN_QUEUE 16
+#endif
+
+#define OAK_NCR5380 {						\
+proc_info:	oakscsi_proc_info,				\
+name:		"Oak 16-bit SCSI",				\
+detect:		oakscsi_detect,					\
+release:	oakscsi_release,	/* Release */		\
+info:		oakscsi_info,					\
+queuecommand:	oakscsi_queue_command,				\
+abort:		oakscsi_abort, 					\
+reset:		oakscsi_reset,					\
+can_queue:	CAN_QUEUE,		/* can queue */		\
+this_id:	7,			/* id */		\
+sg_tablesize:	SG_ALL,			/* sg_tablesize */	\
+cmd_per_lun:	CMD_PER_LUN,		/* cmd per lun */	\
+use_clustering:	DISABLE_CLUSTERING				\
+	}
+
+#ifndef HOSTS_C
+#define NCR5380_implementation_fields \
+	int port, ctrl
+
+#define NCR5380_local_declare() \
+        struct Scsi_Host *_instance
+
+#define NCR5380_setup(instance) \
+        _instance = instance
+
+#define NCR5380_read(reg)		oakscsi_read(_instance, reg)
+#define NCR5380_write(reg, value)	oakscsi_write(_instance, reg, value)
+#define NCR5380_intr			oakscsi_intr
+#define NCR5380_queue_command		oakscsi_queue_command
+#define NCR5380_abort			oakscsi_abort
+#define NCR5380_reset			oakscsi_reset
+#define NCR5380_proc_info		oakscsi_proc_info
+
+#define BOARD_NORMAL	0
+#define BOARD_NCR53C400	1
+
+#endif /* else def HOSTS_C */
+#endif /* ndef ASM */
+#endif /* OAK_NCR5380_H */
+
diff -u --recursive --new-file v2.1.87/linux/drivers/acorn/scsi/powertec.c linux/drivers/acorn/scsi/powertec.c
--- v2.1.87/linux/drivers/acorn/scsi/powertec.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/acorn/scsi/powertec.c	Wed Feb 18 14:55:07 1998
@@ -0,0 +1,410 @@
+/*
+ * linux/arch/arm/drivers/scsi/powertec.c
+ *
+ * Copyright (C) 1997 Russell King
+ *
+ * This driver is based on experimentation.  Hence, it may have made
+ * assumptions about the particular card that I have available, and
+ * may not be reliable!
+ *
+ * Changelog:
+ *  01-10-1997	RMK	Created, READONLY version
+ *  15-02-1998	RMK	Added DMA support and hardware definitions
+ */
+
+#include <linux/module.h>
+#include <linux/blk.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/proc_fs.h>
+#include <linux/unistd.h>
+#include <linux/stat.h>
+
+#include <asm/delay.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/dma.h>
+#include <asm/ecard.h>
+#include <asm/pgtable.h>
+
+#include "../../scsi/sd.h"
+#include "../../scsi/hosts.h"
+#include "powertec.h"
+
+/* Configuration */
+#define POWERTEC_XTALFREQ	40
+#define POWERTEC_ASYNC_PERIOD	200
+#define POWERTEC_SYNC_DEPTH	16
+
+/*
+ * List of devices that the driver will recognise
+ */
+#define POWERTECSCSI_LIST	{ MANU_ALSYSTEMS, PROD_ALSYS_SCSIATAPI }
+
+#define POWERTEC_FAS216_OFFSET	0xc00
+#define POWERTEC_FAS216_SHIFT	4
+#define POWERTEC_INTR_STATUS	0x800
+#define POWERTEC_INTR_BIT	0x80
+#define POWERTEC_INTR_CONTROL	0x407
+#define POWERTEC_INTR_ENABLE	1
+#define POWERTEC_INTR_DISABLE	0
+
+/*
+ * Version
+ */
+#define VER_MAJOR	0
+#define VER_MINOR	0
+#define VER_PATCH	1
+
+static struct expansion_card *ecs[MAX_ECARDS];
+
+static struct proc_dir_entry proc_scsi_powertec = {
+	PROC_SCSI_QLOGICISP, 8, "powertec",
+	S_IFDIR | S_IRUGO | S_IXUGO, 2
+};
+
+/* Function: void powertecscsi_irqenable(ec, irqnr)
+ * Purpose : Enable interrupts on powertec SCSI card
+ * Params  : ec    - expansion card structure
+ *         : irqnr - interrupt number
+ */
+static void
+powertecscsi_irqenable(struct expansion_card *ec, int irqnr)
+{
+	unsigned int port = (unsigned int)ec->irq_data;
+	outb(POWERTEC_INTR_ENABLE, port);
+}
+
+/* Function: void powertecscsi_irqdisable(ec, irqnr)
+ * Purpose : Disable interrupts on powertec SCSI card
+ * Params  : ec    - expansion card structure
+ *         : irqnr - interrupt number
+ */
+static void
+powertecscsi_irqdisable(struct expansion_card *ec, int irqnr)
+{
+	unsigned int port = (unsigned int)ec->irq_data;
+	outb(POWERTEC_INTR_DISABLE, port);
+}
+
+static const expansioncard_ops_t powertecscsi_ops = {
+	powertecscsi_irqenable,
+	powertecscsi_irqdisable,
+	NULL,
+	NULL
+};
+
+/* Function: void powertecscsi_intr(int irq, void *dev_id,
+ *				    struct pt_regs *regs)
+ * Purpose : handle interrupts from Powertec SCSI card
+ * Params  : irq - interrupt number
+ *	     dev_id - user-defined (Scsi_Host structure)
+ *	     regs - processor registers at interrupt
+ */
+static void
+powertecscsi_intr(int irq, void *dev_id, struct pt_regs *regs)
+{
+    struct Scsi_Host *instance = (struct Scsi_Host *)dev_id;
+
+    fas216_intr(instance);
+}
+
+static void
+powertecscsi_invalidate(char *addr, long len, fasdmadir_t direction)
+{
+	unsigned int page;
+
+	if (direction == DMA_OUT) {
+		for (page = (unsigned int) addr; len > 0;
+		     page += PAGE_SIZE, len -= PAGE_SIZE)
+			flush_page_to_ram(page);
+	} else
+		flush_cache_range(current->mm, (unsigned long)addr,
+				  (unsigned long)addr + len);
+}
+
+/* Function: fasdmatype_t powertecscsi_dma_setup(instance, SCpnt, direction)
+ * Purpose : initialises DMA/PIO
+ * Params  : instance  - host
+ *	     SCpnt     - command
+ *	     direction - DMA on to/off of card
+ * Returns : type of transfer to be performed
+ */
+static fasdmatype_t
+powertecscsi_dma_setup(struct Scsi_Host *instance, Scsi_Pointer *SCp,
+		       fasdmadir_t direction)
+{
+	if (instance->dma_channel != NO_DMA && SCp->this_residual >= 512) {
+		int buf;
+static		dmasg_t dmasg[256];
+
+		for (buf = 1; buf <= SCp->buffers_residual; buf++) {
+			dmasg[buf].address = __virt_to_bus(
+				(unsigned long)SCp->buffer[buf].address);
+			dmasg[buf].length = SCp->buffer[buf].length;
+
+			powertecscsi_invalidate(SCp->buffer[buf].address,
+						SCp->buffer[buf].length,
+						direction);
+		}
+
+		dmasg[0].address = __virt_to_phys((unsigned long)SCp->ptr);
+		dmasg[0].length = SCp->this_residual;
+		powertecscsi_invalidate(SCp->ptr,
+					SCp->this_residual, direction);
+
+		disable_dma(instance->dma_channel);
+		set_dma_sg(instance->dma_channel, dmasg, buf);
+		set_dma_mode(instance->dma_channel,
+			     direction == DMA_OUT ? DMA_MODE_WRITE :
+						    DMA_MODE_READ);
+		enable_dma(instance->dma_channel);
+		return fasdma_real_all;
+	}
+	/*
+	 * We don't do DMA, we only do slow PIO
+	 */
+	return fasdma_none;
+}
+
+/* Function: int powertecscsi_dma_stop(instance, SCpnt)
+ * Purpose : stops DMA/PIO
+ * Params  : instance  - host
+ *	     SCpnt     - command
+ */
+static void
+powertecscsi_dma_stop(struct Scsi_Host *instance, Scsi_Pointer *SCp)
+{
+	if (instance->dma_channel != NO_DMA)
+		disable_dma(instance->dma_channel);
+}
+
+/* Function: int powertecscsi_detect(Scsi_Host_Template * tpnt)
+ * Purpose : initialises PowerTec SCSI driver
+ * Params  : tpnt - template for this SCSI adapter
+ * Returns : >0 if host found, 0 otherwise.
+ */
+int
+powertecscsi_detect(Scsi_Host_Template *tpnt)
+{
+	static const card_ids powertecscsi_cids[] =
+			{ POWERTECSCSI_LIST, { 0xffff, 0xffff} };
+	int count = 0;
+	struct Scsi_Host *instance;
+  
+	tpnt->proc_dir = &proc_scsi_powertec;
+	memset(ecs, 0, sizeof (ecs));
+
+	ecard_startfind();
+
+	while(1) {
+	    	PowerTecScsi_Info *info;
+
+		ecs[count] = ecard_find(0, powertecscsi_cids);
+		if (!ecs[count])
+			break;
+
+		ecard_claim(ecs[count]);
+
+		instance = scsi_register(tpnt, sizeof (PowerTecScsi_Info));
+		if (!instance) {
+			ecard_release(ecs[count]);
+			break;
+		}
+
+		instance->io_port = ecard_address(ecs[count], ECARD_IOC, 0);
+		instance->irq = ecs[count]->irq;
+
+		ecs[count]->irqaddr = (unsigned char *)
+			    ioaddr(instance->io_port + POWERTEC_INTR_STATUS);
+		ecs[count]->irqmask = POWERTEC_INTR_BIT;
+		ecs[count]->irq_data = (void *)
+			    (instance->io_port + POWERTEC_INTR_CONTROL);
+		ecs[count]->ops = (expansioncard_ops_t *)&powertecscsi_ops;
+
+		request_region(instance->io_port + POWERTEC_FAS216_OFFSET,
+				16 << POWERTEC_FAS216_SHIFT, "powertec2-fas");
+
+		if (request_irq(instance->irq, powertecscsi_intr,
+				SA_INTERRUPT, "powertec", instance)) {
+			printk("scsi%d: IRQ%d not free, interrupts disabled\n",
+			       instance->host_no, instance->irq);
+			instance->irq = NO_IRQ;
+		}
+
+		info = (PowerTecScsi_Info *)instance->hostdata;
+
+		instance->dma_channel = 3; /* slot 1 */
+		if (request_dma(instance->dma_channel, "powertec")) {
+			printk("scsi%d: DMA%d not free, DMA disabled\n",
+			       instance->host_no, instance->dma_channel);
+			instance->dma_channel = NO_DMA;
+		}
+
+		info->info.scsi.io_port	=
+				instance->io_port + POWERTEC_FAS216_OFFSET;
+		info->info.scsi.io_shift= POWERTEC_FAS216_SHIFT;
+		info->info.scsi.irq	= instance->irq;
+		info->info.ifcfg.clockrate = POWERTEC_XTALFREQ;
+		info->info.ifcfg.select_timeout = 255;
+		info->info.ifcfg.asyncperiod = POWERTEC_ASYNC_PERIOD;
+		info->info.ifcfg.sync_max_depth = POWERTEC_SYNC_DEPTH;
+		info->info.dma.setup	= powertecscsi_dma_setup;
+		info->info.dma.pseudo	= NULL;
+		info->info.dma.stop	= powertecscsi_dma_stop;
+
+		fas216_init(instance);
+		++count;
+	}
+	return count;
+}
+
+/* Function: int powertecscsi_release(struct Scsi_Host * host)
+ * Purpose : releases all resources used by this adapter
+ * Params  : host - driver host structure to return info for.
+ * Returns : nothing
+ */
+int powertecscsi_release(struct Scsi_Host *instance)
+{
+	int i;
+
+	fas216_release(instance);
+
+	if (instance->irq != NO_IRQ)
+		free_irq(instance->irq, instance);
+	if (instance->dma_channel != NO_DMA)
+		free_dma(instance->dma_channel);
+	release_region(instance->io_port + POWERTEC_FAS216_OFFSET,
+		       16 << POWERTEC_FAS216_SHIFT);
+
+	for (i = 0; i < MAX_ECARDS; i++)
+		if (ecs[i] &&
+		    instance->io_port == ecard_address(ecs[i], ECARD_IOC, 0))
+			ecard_release(ecs[i]);
+	return 0;
+}
+
+/* Function: const char *powertecscsi_info(struct Scsi_Host * host)
+ * Purpose : returns a descriptive string about this interface,
+ * Params  : host - driver host structure to return info for.
+ * Returns : pointer to a static buffer containing null terminated string.
+ */
+const char *powertecscsi_info(struct Scsi_Host *host)
+{
+	PowerTecScsi_Info *info = (PowerTecScsi_Info *)host->hostdata;
+	static char string[100], *p;
+
+	p = string;
+	p += sprintf(string, "%s at port %X ",
+		     host->hostt->name, host->io_port);
+
+	if (host->irq != NO_IRQ)
+		p += sprintf(p, "irq %d ", host->irq);
+	else
+		p += sprintf(p, "NO IRQ ");
+
+	if (host->dma_channel != NO_DMA)
+		p += sprintf(p, "dma %d ", host->dma_channel);
+	else
+		p += sprintf(p, "NO DMA ");
+
+	p += sprintf(p, "v%d.%d.%d scsi %s",
+		     VER_MAJOR, VER_MINOR, VER_PATCH,
+		     info->info.scsi.type);
+
+	return string;
+}
+
+/* Function: int powertecscsi_proc_info(char *buffer, char **start, off_t offset,
+ *					int length, int host_no, int inout)
+ * Purpose : Return information about the driver to a user process accessing
+ *	     the /proc filesystem.
+ * Params  : buffer - a buffer to write information to
+ *	     start  - a pointer into this buffer set by this routine to the start
+ *		      of the required information.
+ *	     offset - offset into information that we have read upto.
+ *	     length - length of buffer
+ *	     host_no - host number to return information for
+ *	     inout  - 0 for reading, 1 for writing.
+ * Returns : length of data written to buffer.
+ */
+int powertecscsi_proc_info(char *buffer, char **start, off_t offset,
+			    int length, int host_no, int inout)
+{
+	int pos, begin;
+	struct Scsi_Host *host = scsi_hostlist;
+	PowerTecScsi_Info *info;
+	Scsi_Device *scd;
+
+	while (host) {
+		if (host->host_no == host_no)
+			break;
+		host = host->next;
+	}
+	if (!host)
+		return 0;
+
+	info = (PowerTecScsi_Info *)host->hostdata;
+	if (inout == 1)
+		return -EINVAL;
+
+	begin = 0;
+	pos = sprintf(buffer,
+			"PowerTec SCSI driver version %d.%d.%d\n",
+			VER_MAJOR, VER_MINOR, VER_PATCH);
+	pos += sprintf(buffer + pos,
+			"Address: %08X    IRQ : %d     DMA : %d\n"
+			"FAS    : %s\n\n"
+			"Statistics:\n",
+			host->io_port, host->irq, host->dma_channel,
+			info->info.scsi.type);
+
+	pos += sprintf(buffer+pos,
+			"Queued commands: %-10d   Issued commands: %-10d\n"
+			"Done commands  : %-10d   Reads          : %-10d\n"
+			"Writes         : %-10d   Others         : %-10d\n"
+			"Disconnects    : %-10d   Aborts         : %-10d\n"
+			"Resets         : %-10d\n",
+			info->info.stats.queues,      info->info.stats.removes,
+			info->info.stats.fins,        info->info.stats.reads,
+			info->info.stats.writes,      info->info.stats.miscs,
+			info->info.stats.disconnects, info->info.stats.aborts,
+			info->info.stats.resets);
+
+	pos += sprintf (buffer+pos, "\nAttached devices:%s\n", host->host_queue ? "" : " none");
+
+	for (scd = host->host_queue; scd; scd = scd->next) {
+		int len;
+
+		proc_print_scsidevice (scd, buffer, &len, pos);
+		pos += len;
+		pos += sprintf (buffer+pos, "Extensions: ");
+		if (scd->tagged_supported)
+			pos += sprintf (buffer+pos, "TAG %sabled [%d] ",
+				    scd->tagged_queue ? "en" : "dis",
+				    scd->current_tag);
+		pos += sprintf (buffer+pos, "\n");
+
+		if (pos + begin < offset) {
+			begin += pos;
+			pos = 0;
+		}
+		if (pos + begin > offset + length)
+			break;
+	}
+
+	*start = buffer + (offset - begin);
+	pos -= offset - begin;
+	if (pos > length)
+		pos = length;
+
+	return pos;
+}
+
+#ifdef MODULE
+Scsi_Host_Template driver_template = POWERTECSCSI;
+
+#include "../../scsi/scsi_module.c"
+#endif
diff -u --recursive --new-file v2.1.87/linux/drivers/acorn/scsi/powertec.h linux/drivers/acorn/scsi/powertec.h
--- v2.1.87/linux/drivers/acorn/scsi/powertec.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/acorn/scsi/powertec.h	Sun Feb 15 07:56:11 1998
@@ -0,0 +1,69 @@
+/*
+ * PowerTec SCSI driver
+ *
+ * Copyright (C) 1997 Russell King
+ */
+#ifndef POWERTECSCSI_H
+#define POWERTECSCSI_H
+
+extern int powertecscsi_detect (Scsi_Host_Template *);
+extern int powertecscsi_release (struct Scsi_Host *);
+extern const char *powertecscsi_info (struct Scsi_Host *);
+extern int powertecscsi_proc_info (char *buffer, char **start, off_t offset,
+					int length, int hostno, int inout);
+
+#ifndef NULL
+#define NULL ((void *)0)
+#endif
+
+#ifndef CAN_QUEUE
+/*
+ * Default queue size
+ */
+#define CAN_QUEUE	1
+#endif
+
+#ifndef CMD_PER_LUN
+#define CMD_PER_LUN	1
+#endif
+
+#ifndef SCSI_ID
+/*
+ * Default SCSI host ID
+ */
+#define SCSI_ID		7
+#endif
+
+#include <scsi/scsicam.h>
+
+#include "fas216.h"
+
+#define POWERTECSCSI {							\
+proc_info:	powertecscsi_proc_info,					\
+name:		"PowerTec SCSI",					\
+detect:		powertecscsi_detect,	/* detect		*/	\
+release:	powertecscsi_release,	/* release		*/	\
+info:		powertecscsi_info,	/* info			*/	\
+command:	fas216_command,		/* command		*/	\
+queuecommand:	fas216_queue_command,	/* queuecommand		*/	\
+abort:		fas216_abort,		/* abort		*/	\
+reset:		fas216_reset,		/* reset		*/	\
+bios_param:	scsicam_bios_param,	/* biosparam		*/	\
+can_queue:	CAN_QUEUE,		/* can queue		*/	\
+this_id:	SCSI_ID,		/* scsi host id		*/	\
+sg_tablesize:	SG_ALL,			/* sg_tablesize		*/	\
+cmd_per_lun:	CMD_PER_LUN,		/* cmd per lun		*/	\
+use_clustering:	ENABLE_CLUSTERING					\
+	}
+
+#ifndef HOSTS_C
+
+typedef struct {
+    FAS216_Info info;
+
+    /* other info... */
+} PowerTecScsi_Info;
+
+#endif /* HOSTS_C */
+
+#endif /* POWERTECSCSI_H */
diff -u --recursive --new-file v2.1.87/linux/drivers/acorn/scsi/queue.c linux/drivers/acorn/scsi/queue.c
--- v2.1.87/linux/drivers/acorn/scsi/queue.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/acorn/scsi/queue.c	Sat Feb  7 07:15:19 1998
@@ -0,0 +1,432 @@
+/*
+ * queue.c: queue handling primitives
+ *
+ * (c) 1997 Russell King
+ *
+ * Changelog:
+ *  15-Sep-1997 RMK	Created.
+ *  11-Oct-1997	RMK	Corrected problem with queue_remove_exclude
+ *			not updating internal linked list properly
+ *			(was causing commands to go missing).
+ */
+
+#define SECTOR_SIZE 512
+
+#include <linux/module.h>
+#include <linux/blk.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/malloc.h>
+
+#include "../../scsi/scsi.h"
+
+typedef struct queue_entry {
+	struct queue_entry *next;
+	struct queue_entry *prev;
+	unsigned long	   magic;
+	Scsi_Cmnd	   *SCpnt;
+} QE_t;
+
+#define QUEUE_MAGIC_FREE	0xf7e1c9a3
+#define QUEUE_MAGIC_USED	0xf7e1cc33
+
+#include "queue.h"
+
+/*
+ * Function: void queue_initialise (Queue_t *queue)
+ * Purpose : initialise a queue
+ * Params  : queue - queue to initialise
+ */
+int queue_initialise (Queue_t *queue)
+{
+	unsigned int nqueues;
+	QE_t *q;
+
+	queue->alloc = queue->free = q = (QE_t *) kmalloc (SECTOR_SIZE, GFP_KERNEL);
+	if (q) {
+		nqueues = SECTOR_SIZE / sizeof (QE_t);
+
+		for (; nqueues; q++, nqueues--) {
+			q->next = q + 1;
+			q->prev = NULL;
+			q->magic = QUEUE_MAGIC_FREE;
+			q->SCpnt = NULL;
+		}
+		q->next = NULL;
+	}
+
+	return q != NULL;
+}
+
+/*
+ * Function: void queue_free (Queue_t *queue)
+ * Purpose : free a queue
+ * Params  : queue - queue to free
+ */
+void queue_free (Queue_t *queue)
+{
+	if (queue->alloc)
+		kfree (queue->alloc);
+}
+     
+
+/*
+ * Function: int queue_add_cmd_ordered (Queue_t *queue, Scsi_Cmnd *SCpnt)
+ * Purpose : Add a new command onto a queue, adding REQUEST_SENSE to head.
+ * Params  : queue - destination queue
+ *	     SCpnt - command to add
+ * Returns : 0 on error, !0 on success
+ */
+int queue_add_cmd_ordered (Queue_t *queue, Scsi_Cmnd *SCpnt)
+{
+	unsigned long flags;
+	QE_t *q;
+
+	save_flags_cli (flags);
+	q = queue->free;
+	if (q)
+		queue->free = q->next;
+
+	if (q) {
+		if (q->magic != QUEUE_MAGIC_FREE) {
+			restore_flags (flags);
+			panic ("scsi queues corrupted - queue entry not free");
+		}
+
+		q->magic = QUEUE_MAGIC_USED;
+		q->SCpnt = SCpnt;
+
+		if (SCpnt->cmnd[0] == REQUEST_SENSE) { /* request_sense gets put on the queue head */
+			if (queue->head) {
+				q->prev = NULL;
+				q->next = queue->head;
+				queue->head->prev = q;
+				queue->head = q;
+			} else {
+			    	q->next = q->prev = NULL;
+			    	queue->head = queue->tail = q;
+			}
+		} else {				/* others get put on the tail */
+			if (queue->tail) {
+				q->next = NULL;
+				q->prev = queue->tail;
+				queue->tail->next = q;
+				queue->tail = q;
+			} else {
+				q->next = q->prev = NULL;
+				queue->head = queue->tail = q;
+			}
+		}    
+	}
+	restore_flags (flags);
+
+	return q != NULL;
+}
+
+/*
+ * Function: int queue_add_cmd_tail (Queue_t *queue, Scsi_Cmnd *SCpnt)
+ * Purpose : Add a new command onto a queue, adding onto tail of list
+ * Params  : queue - destination queue
+ *	     SCpnt - command to add
+ * Returns : 0 on error, !0 on success
+ */
+int queue_add_cmd_tail (Queue_t *queue, Scsi_Cmnd *SCpnt)
+{
+	unsigned long flags;
+	QE_t *q;
+
+	save_flags_cli (flags);
+	q = queue->free;
+	if (q)
+		queue->free = q->next;
+
+	if (q) {
+		if (q->magic != QUEUE_MAGIC_FREE) {
+			restore_flags (flags);
+			panic ("scsi queues corrupted - queue entry not free");
+		}
+
+		q->magic = QUEUE_MAGIC_USED;
+		q->SCpnt = SCpnt;
+
+		if (queue->tail) {
+			q->next = NULL;
+			q->prev = queue->tail;
+			queue->tail->next = q;
+			queue->tail = q;
+		} else {
+			q->next = q->prev = NULL;
+			queue->head = queue->tail = q;
+		}    
+	}
+	restore_flags (flags);
+
+	return q != NULL;
+}
+
+/*
+ * Function: Scsi_Cmnd *queue_remove_exclude (queue, exclude)
+ * Purpose : remove a SCSI command from a queue
+ * Params  : queue   - queue to remove command from
+ *	     exclude - bit array of target&lun which is busy
+ * Returns : Scsi_Cmnd if successful (and a reference), or NULL if no command available
+ */
+Scsi_Cmnd *queue_remove_exclude (Queue_t *queue, unsigned char *exclude)
+{
+	unsigned long flags;
+	Scsi_Cmnd *SCpnt;
+	QE_t *q, *prev;
+
+	save_flags_cli (flags);
+	for (q = queue->head, prev = NULL; q; q = q->next) {
+		if (exclude && !test_bit (q->SCpnt->target * 8 + q->SCpnt->lun, exclude))
+			break;
+		prev = q;
+	}
+
+	if (q) {
+		if (q->magic != QUEUE_MAGIC_USED) {
+			restore_flags (flags);
+			panic ("q_remove_exclude: scsi queues corrupted - queue entry not used");
+		}
+		if (q->prev != prev)
+			panic ("q_remove_exclude: scsi queues corrupted - q->prev != prev");
+
+		if (!prev) {
+			queue->head = q->next;
+			if (queue->head)
+				queue->head->prev = NULL;
+			else
+				queue->tail = NULL;
+		} else {
+			prev->next = q->next;
+			if (prev->next)
+				prev->next->prev = prev;
+			else
+				queue->tail = prev;
+		}
+
+		SCpnt = q->SCpnt;
+
+		q->next = queue->free;
+		queue->free = q;
+		q->magic = QUEUE_MAGIC_FREE;
+	} else
+		SCpnt = NULL;
+
+	restore_flags (flags);
+
+	return SCpnt;
+}
+
+/*
+ * Function: Scsi_Cmnd *queue_remove (queue)
+ * Purpose : removes first SCSI command from a queue
+ * Params  : queue   - queue to remove command from
+ * Returns : Scsi_Cmnd if successful (and a reference), or NULL if no command available
+ */
+Scsi_Cmnd *queue_remove (Queue_t *queue)
+{
+	unsigned long flags;
+	Scsi_Cmnd *SCpnt;
+	QE_t *q;
+
+	save_flags_cli (flags);
+	q = queue->head;
+	if (q) {
+		queue->head = q->next;
+		if (queue->head)
+			queue->head->prev = NULL;
+		else
+			queue->tail = NULL;
+
+		if (q->magic != QUEUE_MAGIC_USED) {
+			restore_flags (flags);
+			panic ("scsi queues corrupted - queue entry not used");
+		}
+
+		SCpnt = q->SCpnt;
+
+		q->next = queue->free;
+		queue->free = q;
+		q->magic = QUEUE_MAGIC_FREE;
+	} else
+		SCpnt = NULL;
+
+	restore_flags (flags);
+
+	return SCpnt;
+}
+
+/*
+ * Function: Scsi_Cmnd *queue_remove_tgtluntag (queue, target, lun, tag)
+ * Purpose : remove a SCSI command from the queue for a specified target/lun/tag
+ * Params  : queue  - queue to remove command from
+ *	     target - target that we want
+ *	     lun    - lun on device
+ *	     tag    - tag on device
+ * Returns : Scsi_Cmnd if successful, or NULL if no command satisfies requirements
+ */
+Scsi_Cmnd *queue_remove_tgtluntag (Queue_t *queue, int target, int lun, int tag)
+{
+	unsigned long flags;
+	Scsi_Cmnd *SCpnt;
+	QE_t *q, *prev;
+
+	save_flags_cli (flags);
+	for (q = queue->head, prev = NULL; q; q = q->next) {
+		if (q->SCpnt->target == target &&
+		    q->SCpnt->lun == lun &&
+		    q->SCpnt->tag == tag)
+			break;
+
+		prev = q;
+	}
+
+	if (q) {
+		if (q->magic != QUEUE_MAGIC_USED) {
+			restore_flags (flags);
+			panic ("q_remove_tgtluntag: scsi queues corrupted - queue entry not used");
+		}
+		if (q->prev != prev)
+			panic ("q_remove_tgtluntag: scsi queues corrupted - q->prev != prev");
+
+		if (!prev) {
+			queue->head = q->next;
+			if (queue->head)
+				queue->head->prev = NULL;
+			else
+				queue->tail = NULL;
+		} else {
+		 	prev->next = q->next;
+			if (prev->next)
+				prev->next->prev = prev;
+			else
+				queue->tail = prev;
+		}
+
+		SCpnt = q->SCpnt;
+
+		q->magic = QUEUE_MAGIC_FREE;
+		q->next = queue->free;
+		queue->free = q;
+	} else
+		SCpnt = NULL;
+
+	restore_flags (flags);
+
+	return SCpnt;
+}
+
+/*
+ * Function: int queue_probetgtlun (queue, target, lun)
+ * Purpose : check to see if we have a command in the queue for the specified
+ *	     target/lun.
+ * Params  : queue  - queue to look in
+ *	     target - target we want to probe
+ *	     lun    - lun on target
+ * Returns : 0 if not found, != 0 if found
+ */
+int queue_probetgtlun (Queue_t *queue, int target, int lun)
+{
+	QE_t *q;
+
+	for (q = queue->head; q; q = q->next)
+		if (q->SCpnt->target == target &&
+		    q->SCpnt->lun == lun)
+			break;
+
+	return q != NULL;
+}
+
+/*
+ * Function: int queue_cmdonqueue (queue, SCpnt)
+ * Purpose : check to see if we have a command on the queue
+ * Params  : queue - queue to look in
+ *	     SCpnt - command to find
+ * Returns : 0 if not found, != 0 if found
+ */
+int queue_cmdonqueue (Queue_t *queue, Scsi_Cmnd *SCpnt)
+{
+	QE_t *q;
+
+	for (q = queue->head; q; q = q->next)
+		if (q->SCpnt == SCpnt)
+			break;
+
+	return q != NULL;
+}
+
+/*
+ * Function: int queue_removecmd (Queue_t *queue, Scsi_Cmnd *SCpnt)
+ * Purpose : remove a specific command from the queues
+ * Params  : queue - queue to look in
+ *	     SCpnt - command to find
+ * Returns : 0 if not found
+ */
+int queue_removecmd (Queue_t *queue, Scsi_Cmnd *SCpnt)
+{
+	unsigned long flags;
+	QE_t *q, *prev;
+
+	save_flags_cli (flags);
+	for (q = queue->head, prev = NULL; q; q = q->next) {
+		if (q->SCpnt == SCpnt)
+			break;
+
+		prev = q;
+	}
+
+	if (q) {
+		if (q->magic != QUEUE_MAGIC_USED) {
+			restore_flags (flags);
+			panic ("q_removecmd: scsi queues corrupted - queue entry not used");
+		}
+		if (q->prev != prev)
+			panic ("q_removecmd: scsi queues corrupted - q->prev != prev");
+
+		if (!prev) {
+			queue->head = q->next;
+			if (queue->head)
+				queue->head->prev = NULL;
+			else
+				queue->tail = NULL;
+		} else {
+			prev->next = q->next;
+			if (prev->next)
+				prev->next->prev = prev;
+			else
+				queue->tail = prev;
+		}
+
+		q->magic = QUEUE_MAGIC_FREE;
+		q->next = queue->free;
+		queue->free = q;
+	}
+
+	restore_flags (flags);
+
+	return q != NULL;
+}
+
+EXPORT_SYMBOL(queue_initialise);
+EXPORT_SYMBOL(queue_free);
+EXPORT_SYMBOL(queue_remove);
+EXPORT_SYMBOL(queue_remove_exclude);
+EXPORT_SYMBOL(queue_add_cmd_ordered);
+EXPORT_SYMBOL(queue_add_cmd_tail);
+EXPORT_SYMBOL(queue_remove_tgtluntag);
+EXPORT_SYMBOL(queue_probetgtlun);
+EXPORT_SYMBOL(queue_cmdonqueue);
+EXPORT_SYMBOL(queue_removecmd);
+
+#ifdef MODULE
+int init_module (void)
+{
+	return 0;
+}
+
+void cleanup_module (void)
+{
+}
+#endif
diff -u --recursive --new-file v2.1.87/linux/drivers/acorn/scsi/queue.h linux/drivers/acorn/scsi/queue.h
--- v2.1.87/linux/drivers/acorn/scsi/queue.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/acorn/scsi/queue.h	Thu Sep 25 14:35:09 1997
@@ -0,0 +1,106 @@
+/*
+ * queue.h: queue handling
+ *
+ * (c) 1997 Russell King
+ */
+#ifndef QUEUE_H
+#define QUEUE_H
+
+typedef struct {
+	struct queue_entry *head;		/* head of queue */
+	struct queue_entry *tail;		/* tail of queue */
+	struct queue_entry *free;		/* free list */
+	void *alloc;				/* start of allocated mem */
+} Queue_t;
+
+/*
+ * Function: void queue_initialise (Queue_t *queue)
+ * Purpose : initialise a queue
+ * Params  : queue - queue to initialise
+ */
+extern int queue_initialise (Queue_t *queue);
+
+/*
+ * Function: void queue_free (Queue_t *queue)
+ * Purpose : free a queue
+ * Params  : queue - queue to free
+ */
+extern void queue_free (Queue_t *queue);
+
+/*
+ * Function: Scsi_Cmnd *queue_remove (queue)
+ * Purpose : removes first SCSI command from a queue
+ * Params  : queue   - queue to remove command from
+ * Returns : Scsi_Cmnd if successful (and a reference), or NULL if no command available
+ */
+extern Scsi_Cmnd *queue_remove (Queue_t *queue);
+
+/*
+ * Function: Scsi_Cmnd *queue_remove_exclude_ref (queue, exclude, ref)
+ * Purpose : remove a SCSI command from a queue
+ * Params  : queue   - queue to remove command from
+ *	     exclude - array of busy LUNs
+ *	     ref     - a reference that can be used to put the command back
+ * Returns : Scsi_Cmnd if successful (and a reference), or NULL if no command available
+ */
+extern Scsi_Cmnd *queue_remove_exclude (Queue_t *queue, unsigned char *exclude);
+
+/*
+ * Function: int queue_add_cmd_ordered (Queue_t *queue, Scsi_Cmnd *SCpnt)
+ * Purpose : Add a new command onto a queue, queueing REQUEST_SENSE first
+ * Params  : queue - destination queue
+ *	     SCpnt - command to add
+ * Returns : 0 on error, !0 on success
+ */
+extern int queue_add_cmd_ordered (Queue_t *queue, Scsi_Cmnd *SCpnt);
+
+/*
+ * Function: int queue_add_cmd_tail (Queue_t *queue, Scsi_Cmnd *SCpnt)
+ * Purpose : Add a new command onto a queue, queueing at end of list
+ * Params  : queue - destination queue
+ *	     SCpnt - command to add
+ * Returns : 0 on error, !0 on success
+ */
+extern int queue_add_cmd_tail (Queue_t *queue, Scsi_Cmnd *SCpnt);
+
+/*
+ * Function: Scsi_Cmnd *queue_remove_tgtluntag (queue, target, lun, tag)
+ * Purpose : remove a SCSI command from the queue for a specified target/lun/tag
+ * Params  : queue  - queue to remove command from
+ *	     target - target that we want
+ *	     lun    - lun on device
+ *	     tag    - tag on device
+ * Returns : Scsi_Cmnd if successful, or NULL if no command satisfies requirements
+ */
+extern Scsi_Cmnd *queue_remove_tgtluntag (Queue_t *queue, int target, int lun, int tag);
+
+/*
+ * Function: int queue_probetgtlun (queue, target, lun)
+ * Purpose : check to see if we have a command in the queue for the specified
+ *	     target/lun.
+ * Params  : queue  - queue to look in
+ *	     target - target we want to probe
+ *	     lun    - lun on target
+ * Returns : 0 if not found, != 0 if found
+ */
+extern int queue_probetgtlun (Queue_t *queue, int target, int lun);
+
+/*
+ * Function: int queue_cmdonqueue (queue, SCpnt)
+ * Purpose : check to see if we have a command on the queue
+ * Params  : queue - queue to look in
+ *	     SCpnt - command to find
+ * Returns : 0 if not found, != 0 if found
+ */
+int queue_cmdonqueue (Queue_t *queue, Scsi_Cmnd *SCpnt);
+
+/*
+ * Function: int queue_removecmd (Queue_t *queue, Scsi_Cmnd *SCpnt)
+ * Purpose : remove a specific command from the queues
+ * Params  : queue - queue to look in
+ *	     SCpnt - command to find
+ * Returns : 0 if not found
+ */
+int queue_removecmd (Queue_t *queue, Scsi_Cmnd *SCpnt);
+
+#endif /* QUEUE_H */
diff -u --recursive --new-file v2.1.87/linux/drivers/ap1000/ddv.c linux/drivers/ap1000/ddv.c
--- v2.1.87/linux/drivers/ap1000/ddv.c	Sun Jan  4 10:40:15 1998
+++ linux/drivers/ap1000/ddv.c	Fri Feb 20 18:28:22 1998
@@ -14,7 +14,6 @@
  */
 
 #include <linux/errno.h>
-#include <linux/config.h>
 #include <linux/types.h>
 #include <linux/fs.h>
 #include <linux/ext2_fs.h>
diff -u --recursive --new-file v2.1.87/linux/drivers/block/acsi.c linux/drivers/block/acsi.c
--- v2.1.87/linux/drivers/block/acsi.c	Fri Jan 16 20:35:26 1998
+++ linux/drivers/block/acsi.c	Fri Feb 20 17:55:45 1998
@@ -59,8 +59,11 @@
 #include <linux/major.h>
 #include <linux/blk.h>
 #include <linux/malloc.h>
-
 #include <linux/interrupt.h>
+#include <scsi/scsi.h> /* for SCSI_IOCTL_GET_IDLUN */
+typedef void Scsi_Device; /* hack to avoid including scsi.h */
+#include <scsi/scsi_ioctl.h>
+#include <linux/hdreg.h> /* for HDIO_GETGEO */
 
 #include <asm/setup.h>
 #include <asm/pgtable.h>
@@ -70,7 +73,7 @@
 #include <asm/atariints.h>
 #include <asm/atari_acsi.h>
 #include <asm/atari_stdma.h>
-#include <asm/atari_rootsec.h>
+#include <asm/atari_stram.h>
 
 
 #define DEBUG
@@ -386,7 +389,7 @@
 
 #ifdef CONFIG_ATARI_SLM
 
-extern void attach_slm( int target, int lun );
+extern int attach_slm( int target, int lun );
 extern int slm_init( void );
 
 #endif
@@ -1121,12 +1124,30 @@
 	if (dev >= NDevices)
 		return -EINVAL;
 	switch (cmd) {
-		/* I left out the GETGEO cmd; This doesn't make much sense for
-		 * ACSI disks...
-		 */
+	  case HDIO_GETGEO:
+		/* HDIO_GETGEO is supported more for getting the partition's start
+		 * sector... */
+	  { struct hd_geometry *geo = (struct hd_geometry *)arg;
+	    /* just fake some geometry here, it's nonsense anyway; to make it
+		 * easy, use Adaptec's usual 64/32 mapping */
+	    put_user( 64, &geo->heads );
+	    put_user( 32, &geo->sectors );
+	    put_user( acsi_info[dev].size >> 11, &geo->cylinders );
+		put_user( acsi_part[MINOR(inode->i_rdev)].start_sect, &geo->start );
+		return 0;
+	  }
+		
+	  case SCSI_IOCTL_GET_IDLUN:
+		/* SCSI compatible GET_IDLUN call to get target's ID and LUN number */
+		put_user( acsi_info[dev].target | (acsi_info[dev].lun << 8),
+				  &((Scsi_Idlun *) arg)->dev_id );
+		put_user( 0, &((Scsi_Idlun *) arg)->host_unique_id );
+		return 0;
+		
 	  case BLKGETSIZE:   /* Return device size */
 		return put_user(acsi_part[MINOR(inode->i_rdev)].nr_sects,
 				(long *) arg);
+		
 	  case BLKFLSBUF:
 		if(!suser())  return -EACCES;
 		if(!inode->i_rdev) return -EINVAL;
@@ -1784,8 +1805,8 @@
 		return -EBUSY;
 	}
 
-	if (!(acsi_buffer = (char *)__get_free_pages(GFP_KERNEL | GFP_DMA,
-						     ACSI_BUFFER_ORDER))) {
+	if (!(acsi_buffer =
+		  (char *)atari_stram_alloc( ACSI_BUFFER_SIZE, NULL, "acsi" ))) {
 		printk( KERN_ERR "Unable to get ACSI ST-Ram buffer.\n" );
 		unregister_blkdev( MAJOR_NR, "ad" );
 		return -ENOMEM;
@@ -1824,14 +1845,17 @@
 
 	del_timer( &acsi_timer );
 	blk_dev[MAJOR_NR].request_fn = 0;
-	free_pages( (unsigned long)acsi_buffer, ACSI_BUFFER_ORDER );
+	atari_stram_free( acsi_buffer );
 
 	if (unregister_blkdev( MAJOR_NR, "ad" ) != 0)
 		printk( KERN_ERR "acsi: cleanup_module failed\n");
+	
 	for (gdp = &gendisk_head; *gdp; gdp = &((*gdp)->next))
 		if (*gdp == &acsi_gendisk)
 			break;
-	if (*gdp)
+	if (!*gdp)
+		printk( KERN_ERR "acsi: entry in disk chain missing!\n" );
+	else
 		*gdp = (*gdp)->next;
 }
 #endif
@@ -1861,7 +1885,7 @@
 
 static int revalidate_acsidisk( int dev, int maxusage )
 {
-	int device, major;
+	int device;
 	struct gendisk * gdev;
 	int max_p, start, i;
 	struct acsi_info_struct *aip;
@@ -1880,14 +1904,19 @@
 
 	max_p = gdev->max_p;
 	start = device << gdev->minor_shift;
-	major = MAJOR_NR << 8;
 
 	for( i = max_p - 1; i >= 0 ; i-- ) {
-		sync_dev( major | start | i );
-		invalidate_inodes( major | start | i );
-		invalidate_buffers( major | start | i );
+		if (gdev->part[start + i].nr_sects != 0) {
+			kdev_t devp = MKDEV(MAJOR_NR, start + i);
+			struct super_block *sb = get_super(devp);
+
+			fsync_dev(devp);
+			if (sb)
+				invalidate_inodes(sb);
+			invalidate_buffers(devp);
+			gdev->part[start + i].nr_sects = 0;
+		}
 		gdev->part[start+i].start_sect = 0;
-		gdev->part[start+i].nr_sects = 0;
 	};
 
 	stdma_lock( NULL, NULL );
diff -u --recursive --new-file v2.1.87/linux/drivers/block/acsi_slm.c linux/drivers/block/acsi_slm.c
--- v2.1.87/linux/drivers/block/acsi_slm.c	Sat Sep 20 14:51:54 1997
+++ linux/drivers/block/acsi_slm.c	Fri Feb 20 17:55:45 1998
@@ -73,6 +73,7 @@
 #include <asm/atariints.h>
 #include <asm/atari_acsi.h>
 #include <asm/atari_stdma.h>
+#include <asm/atari_stram.h>
 #include <asm/atari_SLM.h>
 
 
@@ -252,15 +253,15 @@
 
 static char *slm_errstr( int stat );
 static int slm_getstats( char *buffer, int device );
-static long slm_read( struct inode *node, struct file* file, char *buf,
-                      unsigned long count );
+static ssize_t slm_read( struct file* file, char *buf, size_t count, loff_t
+                         *ppos );
 static void start_print( int device );
 static void slm_interrupt(int irc, void *data, struct pt_regs *fp);
 static void slm_test_ready( unsigned long dummy );
 static void set_dma_addr( unsigned long paddr );
 static unsigned long get_dma_addr( void );
-static long slm_write( struct inode *node, struct file *file, const char *buf,
-                       unsigned long count );
+static ssize_t slm_write( struct file *file, const char *buf, size_t count,
+                          loff_t *ppos );
 static int slm_ioctl( struct inode *inode, struct file *file, unsigned int
                       cmd, unsigned long arg );
 static int slm_open( struct inode *inode, struct file *file );
@@ -372,10 +373,12 @@
 }
 
 
-static long slm_read( struct inode *node, struct file* file,
-					  char *buf, unsigned long count )
+static ssize_t slm_read( struct file *file, char *buf, size_t count,
+						 loff_t *ppos )
 
-{	unsigned long page;
+{
+	struct inode *node = file->f_dentry->d_inode;
+	unsigned long page;
 	int length;
 	int end;
 
@@ -625,10 +628,12 @@
 }
 
 
-static long slm_write( struct inode *node, struct file *file,
-					   const char *buf, unsigned long count )
+static ssize_t slm_write( struct file *file, const char *buf, size_t count,
+						  loff_t *ppos )
 
-{	int		device = MINOR( node->i_rdev );
+{
+	struct inode *node = file->f_dentry->d_inode;
+	int		device = MINOR( node->i_rdev );
 	int		n, filled, w, h;
 
 	while( SLMState == PRINTING ||
@@ -1005,7 +1010,7 @@
 		return -EBUSY;
 	}
 	
-	if (!(SLMBuffer = kmalloc( SLM_BUFFER_SIZE,  GFP_KERNEL | GFP_DMA))) {
+	if (!(SLMBuffer = atari_stram_alloc( SLM_BUFFER_SIZE, NULL, "SLM" ))) {
 		printk( KERN_ERR "Unable to get SLM ST-Ram buffer.\n" );
 		unregister_chrdev( MAJOR_NR, "slm" );
 		return -ENOMEM;
@@ -1037,5 +1042,6 @@
 {
 	if (unregister_chrdev( MAJOR_NR, "slm" ) != 0)
 		printk( KERN_ERR "acsi_slm: cleanup_module failed\n");
+	atari_stram_free( SLMBuffer );
 }
 #endif
diff -u --recursive --new-file v2.1.87/linux/drivers/block/amiflop.c linux/drivers/block/amiflop.c
--- v2.1.87/linux/drivers/block/amiflop.c	Tue May 13 22:41:04 1997
+++ linux/drivers/block/amiflop.c	Fri Feb 20 17:55:45 1998
@@ -219,8 +219,8 @@
 static int probing = 0;
 
 /* Prevent "aliased" accesses. */
-static fd_ref[4] = { 0,0,0,0 };
-static fd_device[4] = { 0,0,0,0 };
+static int fd_ref[4] = { 0,0,0,0 };
+static int fd_device[4] = { 0,0,0,0 };
 
 /*
  * Current device number. Taken either from the block header or from the
@@ -678,7 +678,7 @@
 static void amiga_write(int disk, unsigned long raw, unsigned char *data,
 			int track)
 {
-	int cnt;
+	unsigned int cnt;
 	unsigned long *ptr = (unsigned long *)raw;
 
 	disk&=3;
@@ -1376,8 +1376,8 @@
 
 static void redo_fd_request(void)
 {
-	unsigned int block, track, sector;
-	int device, drive, cnt;
+	unsigned int cnt, block, track, sector;
+	int device, drive;
 	struct amiga_floppy_struct *floppy;
 	char *data;
 	unsigned long flags;
@@ -1511,7 +1511,7 @@
 {
 	int drive = inode->i_rdev & 3;
 	static struct floppy_struct getprm;
-	int error;
+	struct super_block * sb;
 	unsigned long flags;
 
 	switch(cmd){
@@ -1522,9 +1522,9 @@
 		loc.sectors = unit[drive].sects;
 		loc.cylinders = unit[drive].type->tracks;
 		loc.start = 0;
-		if ((error = copy_to_user((void *)param, (void *)&loc,
-					  sizeof(struct hd_geometry))))
-			return error;
+		if (copy_to_user((void *)param, (void *)&loc,
+				 sizeof(struct hd_geometry)))
+			return -EFAULT;
 		break;
 	}
 	case FDFMTBEG:
@@ -1566,7 +1566,9 @@
 		break;
 	case FDFMTEND:
 		floppy_off(drive);
-		invalidate_inodes(inode->i_rdev);
+		sb = get_super(inode->i_rdev);
+		if (sb)
+			invalidate_inodes(sb);
 		invalidate_buffers(inode->i_rdev);
 		break;
 	case FDGETPRM:
@@ -1575,15 +1577,13 @@
 		getprm.head=unit[drive].type->heads;
 		getprm.sect=unit[drive].sects;
 		getprm.size=unit[drive].blocks;
-		if ((error = copy_to_user((void *)param,
-					  (void *)&getprm,
-					  sizeof(struct floppy_struct))))
-			return error;
+		if (copy_to_user((void *)param,
+				 (void *)&getprm,
+				 sizeof(struct floppy_struct)))
+			return -EFAULT;
 	    break;
 	case BLKGETSIZE:
-		if (put_user(unit[drive].blocks,(long *)param))
-			return -EFAULT;
-		break;
+		return put_user(unit[drive].blocks,(long *)param);
 	case FDSETPRM:
 	case FDDEFPRM:
 		return -EINVAL;
@@ -1600,10 +1600,9 @@
 		break;
 #ifdef RAW_IOCTL
 	case IOCTL_RAW_TRACK:
-		error = copy_to_user((void *)param, raw_buf,
-				     unit[drive].type->read_size);
-		if (error)
-			return error;
+		if (copy_to_user((void *)param, raw_buf,
+				 unit[drive].type->read_size))
+			return -EFAULT;
 		else
 			return unit[drive].type->read_size;
 #endif
@@ -1691,6 +1690,7 @@
 	if (type >= num_dr_types) {
 		printk(KERN_WARNING "fd_probe: unsupported drive type %08lx found\n",
 		       code);
+		unit[drive].type = &drive_types[num_dr_types-1]; /* FD_NODRIVE */
 		return;
 	}
 
@@ -1795,9 +1795,12 @@
 static int floppy_release(struct inode * inode, struct file * filp)
 {
   unsigned long flags;
+  struct super_block * sb;
 
   fsync_dev(inode->i_rdev);
-  invalidate_inodes(inode->i_rdev);
+  sb = get_super(inode->i_rdev);
+  if (sb)
+	  invalidate_inodes(sb);
   invalidate_buffers(inode->i_rdev);
   save_flags (flags);
   cli();
@@ -1819,10 +1822,10 @@
   return 0;
 }
 
-void amiga_floppy_setup (char *str, int *ints)
+__initfunc(void amiga_floppy_setup (char *str, int *ints))
 {
-printk ("amiflop: Setting default df0 to %x\n", ints[1]);
-fd_def_df0 = ints[1];
+	printk ("amiflop: Setting default df0 to %x\n", ints[1]);
+	fd_def_df0 = ints[1];
 }
 
 static struct file_operations floppy_fops = {
diff -u --recursive --new-file v2.1.87/linux/drivers/block/ataflop.c linux/drivers/block/ataflop.c
--- v2.1.87/linux/drivers/block/ataflop.c	Sun Sep 21 10:46:52 1997
+++ linux/drivers/block/ataflop.c	Fri Feb 20 17:55:45 1998
@@ -89,6 +89,7 @@
 #include <asm/atarihw.h>
 #include <asm/atariints.h>
 #include <asm/atari_stdma.h>
+#include <asm/atari_stram.h>
 
 #define MAJOR_NR FLOPPY_MAJOR
 #include <linux/blk.h>
@@ -278,7 +279,6 @@
 	15*512, 30*512, 60*512
 };
 
-#define	MAX_SECTORS	(MaxSectors[DriveType])
 #define	BUFFER_SIZE	(BufferSize[DriveType])
 
 unsigned char *DMABuffer;			  /* buffer for writes */
@@ -395,6 +395,7 @@
 static void fd_rwsec( void );
 static void fd_readtrack_check( unsigned long dummy );
 static void fd_rwsec_done( int status );
+static void fd_rwsec_done1(int status);
 static void fd_writetrack( void );
 static void fd_writetrack_done( int status );
 static void fd_times_out( unsigned long dummy );
@@ -459,6 +460,7 @@
 	sound_ym.rd_data_reg_sel = 14; /* Select PSG Port A */
 	tmp = sound_ym.rd_data_reg_sel;
 	sound_ym.wd_data = (tmp | DSKDRVNONE) & ~(drive == 0 ? DSKDRV0 : DSKDRV1);
+	atari_dont_touch_floppy_select = 1;
 	restore_flags(flags);
 
 	/* restore track register to saved value */
@@ -482,8 +484,12 @@
 
 	save_flags(flags);
 	cli(); /* protect against various other ints mucking around with the PSG */
+	atari_dont_touch_floppy_select = 0;
 	sound_ym.rd_data_reg_sel=14;	/* Select PSG Port A */
-	sound_ym.wd_data = sound_ym.rd_data_reg_sel | 7; /* no drives selected */
+	sound_ym.wd_data = (sound_ym.rd_data_reg_sel |
+			    (MACH_IS_FALCON ? 3 : 7)); /* no drives selected */
+	/* On Falcon, the drive B select line is used on the printer port, so
+	 * leave it alone... */
 	SelectedDrive = -1;
 	restore_flags(flags);
 }
@@ -977,11 +983,12 @@
 		 * search for the first non-existent sector and need 1 sec to
 		 * recognise that it isn't present :-(
 		 */
+		del_timer (&readtrack_timer);
 		readtrack_timer.expires =
 		  jiffies + HZ/5 + (old_motoron ? 0 : HZ);
 		       /* 1 rot. + 5 rot.s if motor was off  */
-		add_timer( &readtrack_timer );
 		MultReadInProgress = 1;
+		add_timer( &readtrack_timer );
 	}
 	START_TIMEOUT();
 }
@@ -1028,6 +1035,7 @@
 		 * the read operation
 		 */
 		SET_IRQ_HANDLER( NULL );
+		MultReadInProgress = 0;
 		restore_flags(flags);
 		DPRINT(("fd_readtrack_check(): done\n"));
 		FDC_WRITE( FDCREG_CMD, FDCCMD_FORCI );
@@ -1036,7 +1044,7 @@
 		/* No error until now -- the FDC would have interrupted
 		 * otherwise!
 		 */
-		fd_rwsec_done( 0 );
+		fd_rwsec_done1(0);
 	}
 	else {
 		/* not yet finished, wait another tenth rotation */
@@ -1050,19 +1058,23 @@
 
 static void fd_rwsec_done( int status )
 {
-	unsigned int track;
-
 	DPRINT(("fd_rwsec_done()\n"));
 
-	STOP_TIMEOUT();
-	
 	if (read_track) {
+		del_timer(&readtrack_timer);
 		if (!MultReadInProgress)
 			return;
 		MultReadInProgress = 0;
-		del_timer( &readtrack_timer );
 	}
+	fd_rwsec_done1(status);
+}
 
+static void fd_rwsec_done1(int status)
+{
+	unsigned int track;
+
+	STOP_TIMEOUT();
+	
 	/* Correct the track if stretch != 0 */
 	if (SUDT->stretch) {
 		track = FDC_READ( FDCREG_TRACK);
@@ -1147,7 +1159,7 @@
 			if (!ATARIHW_PRESENT( EXTD_DMA ))
 				copy_buffer (addr, ReqData);
 		} else {
-			dma_cache_maintenance( PhysTrackBuffer, MAX_SECTORS * 512, 0 );
+			dma_cache_maintenance( PhysTrackBuffer, MaxSectors[DriveType] * 512, 0 );
 			BufferDrive = SelectedDrive;
 			BufferSide  = ReqSide;
 			BufferTrack = ReqTrack;
@@ -1802,7 +1814,7 @@
 		UD.steprate = FDCSTEP_12;
 		break;
 	default: /* should be -1 for "not set by user" */
-		if (ATARIHW_PRESENT( FDCSPEED ) || is_medusa)
+		if (ATARIHW_PRESENT( FDCSPEED ) || MACH_IS_MEDUSA)
 			UD.steprate = FDCSTEP_3;
 		else
 			UD.steprate = FDCSTEP_6;
@@ -1827,7 +1839,7 @@
 	unsigned char status;
 	int ok;
 	
-	if (drive > 1) return( 0 );
+	if (drive >= (MACH_IS_FALCON ? 1 : 2)) return( 0 );
 	fd_select_drive( drive );
 
 	/* disable interrupt temporarily */
@@ -2019,6 +2031,14 @@
 {
 	int i;
 
+	if (!MACH_IS_ATARI)
+		/* Amiga, Mac, ... don't have Atari-compatible floppy :-) */
+		return -ENXIO;
+
+	if (MACH_IS_HADES)
+		/* Hades doesn't have Atari-compatible floppy */
+		return -ENXIO;
+
 	if (register_blkdev(MAJOR_NR,"fd",&floppy_fops)) {
 		printk(KERN_ERR "Unable to get major %d for floppy\n",MAJOR_NR);
 		return -EBUSY;
@@ -2029,7 +2049,7 @@
 		   track buffering off for all Medusas, though it
 		   could be used with ones that have a counter
 		   card. But the test is too hard :-( */
-		UseTrackbuffer = !is_medusa;
+		UseTrackbuffer = !MACH_IS_MEDUSA;
 
 	/* initialize variables */
 	SelectedDrive = -1;
@@ -2039,7 +2059,7 @@
 	timer_table[FLOPPY_TIMER].fn = check_change;
 	timer_active &= ~(1 << FLOPPY_TIMER);
 
-	DMABuffer = kmalloc(BUFFER_SIZE + 512, GFP_KERNEL | GFP_DMA);
+	DMABuffer = atari_stram_alloc( BUFFER_SIZE+512, NULL, "ataflop" );
 	if (!DMABuffer) {
 		printk(KERN_ERR "atari_floppy_init: cannot get dma buffer\n");
 		unregister_blkdev(MAJOR_NR, "fd");
@@ -2071,6 +2091,7 @@
 	       UseTrackbuffer ? "" : "no ");
 	config_types();
 
+	(void)do_floppy; /* avoid warning about unused variable */
 	return 0;
 }
 
@@ -2118,7 +2139,7 @@
 	blk_dev[MAJOR_NR].request_fn = 0;
 	timer_active &= ~(1 << FLOPPY_TIMER);
 	timer_table[FLOPPY_TIMER].fn = 0;
-	kfree (DMABuffer);
+	atari_stram_free( DMABuffer );
 }
 #endif
 
diff -u --recursive --new-file v2.1.87/linux/drivers/block/ide-cd.c linux/drivers/block/ide-cd.c
--- v2.1.87/linux/drivers/block/ide-cd.c	Fri Jan 30 11:28:06 1998
+++ linux/drivers/block/ide-cd.c	Fri Feb 20 18:28:22 1998
@@ -194,7 +194,6 @@
 
 #define IDECD_VERSION "4.10"
 
-#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/types.h>
 #include <linux/kernel.h>
diff -u --recursive --new-file v2.1.87/linux/drivers/block/ide-disk.c linux/drivers/block/ide-disk.c
--- v2.1.87/linux/drivers/block/ide-disk.c	Sat Jan 10 10:42:55 1998
+++ linux/drivers/block/ide-disk.c	Fri Feb 20 12:58:07 1998
@@ -105,12 +105,15 @@
 	int i;
 	unsigned int msect, nsect;
 	struct request *rq;
+	unsigned long flags;
 
 	if (!OK_STAT(stat=GET_STAT(),DATA_READY,BAD_R_STAT)) {
 		ide_error(drive, "read_intr", stat);
 		return;
 	}
 	msect = drive->mult_count;
+	
+	spin_lock_irqsave(&io_request_lock,flags);
 read_next:
 	rq = HWGROUP(drive)->rq;
 	if (msect) {
@@ -119,6 +122,12 @@
 		msect -= nsect;
 	} else
 		nsect = 1;
+	/*
+	 * PIO input can take longish times, so we drop the spinlock.
+	 * On SMP, bad things might happen if syscall level code adds
+	 * a new request while we do this PIO, so we just freeze all
+	 * request queue handling while doing the PIO. FIXME
+	 */
 	idedisk_input_data(drive, rq->buffer, nsect * SECTOR_WORDS);
 #ifdef DEBUG
 	printk("%s:  read: sectors(%ld-%ld), buffer=0x%08lx, remaining=%ld\n",
@@ -136,6 +145,7 @@
 			goto read_next;
 		ide_set_handler (drive, &read_intr, WAIT_CMD);
 	}
+	spin_unlock_irqrestore(&io_request_lock,flags);
 }
 
 /*
@@ -147,7 +157,10 @@
 	int i;
 	ide_hwgroup_t *hwgroup = HWGROUP(drive);
 	struct request *rq = hwgroup->rq;
+	unsigned long flags;
+	int error = 0;
 
+	spin_lock_irqsave(&io_request_lock,flags);
 	if (OK_STAT(stat=GET_STAT(),DRIVE_READY,drive->bad_wstat)) {
 #ifdef DEBUG
 		printk("%s: write: sector %ld, buffer=0x%08lx, remaining=%ld\n",
@@ -166,10 +179,16 @@
 				idedisk_output_data (drive, rq->buffer, SECTOR_WORDS);
 				ide_set_handler (drive, &write_intr, WAIT_CMD);
 			}
-			return;
+			goto out;
 		}
-	}
-	ide_error(drive, "write_intr", stat);
+	} else
+		error = 1;
+
+out:
+	spin_unlock_irqrestore(&io_request_lock,flags);
+
+	if (error)
+		ide_error(drive, "write_intr", stat);
 }
 
 /*
@@ -217,13 +236,16 @@
 	int i;
 	ide_hwgroup_t *hwgroup = HWGROUP(drive);
 	struct request *rq = &hwgroup->wrq;
+	unsigned long flags;
+	int error = 0;
 
+	spin_lock_irqsave(&io_request_lock,flags);
 	if (OK_STAT(stat=GET_STAT(),DRIVE_READY,drive->bad_wstat)) {
 		if (stat & DRQ_STAT) {
 			if (rq->nr_sectors) {
 				ide_multwrite(drive, drive->mult_count);
 				ide_set_handler (drive, &multwrite_intr, WAIT_CMD);
-				return;
+				goto out;
 			}
 		} else {
 			if (!rq->nr_sectors) {	/* all done? */
@@ -232,11 +254,17 @@
 					i -= rq->current_nr_sectors;
 					ide_end_request(1, hwgroup);
 				}
-				return;
+				goto out;
 			}
 		}
-	}
-	ide_error(drive, "multwrite_intr", stat);
+	} else
+		error = 1;
+
+out:
+	spin_unlock_irqrestore(&io_request_lock,flags);
+
+	if (error)
+		ide_error(drive, "multwrite_intr", stat);
 }
 
 /*
@@ -354,7 +382,7 @@
 			return;
 		}
 		if (!drive->unmask)
-			cli();
+			__cli();
 		if (drive->mult_count) {
 			HWGROUP(drive)->wrq = *rq; /* scratchpad */
 			ide_set_handler (drive, &multwrite_intr, WAIT_CMD);
diff -u --recursive --new-file v2.1.87/linux/drivers/block/ide.c linux/drivers/block/ide.c
--- v2.1.87/linux/drivers/block/ide.c	Fri Jan 30 15:50:57 1998
+++ linux/drivers/block/ide.c	Fri Feb 20 12:58:07 1998
@@ -170,13 +170,13 @@
 	unsigned long t, flags;
 	int i;
 
-	save_flags(flags);
-	cli();
+	__save_flags(flags);
+	__cli();
 	t = jiffies * 11932;
     	outb_p(0, 0x43);
 	i = inb_p(0x40);
 	i |= inb(0x40) << 8;
-	restore_flags(flags);
+	__restore_flags(flags);
 	return (t - i);
 }
 #endif /* DISK_RECOVERY_TIME */
@@ -314,11 +314,11 @@
 #if SUPPORT_VLB_SYNC
 		if (io_32bit & 2) {
 			unsigned long flags;
-			save_flags(flags);
-			cli();
+			__save_flags(flags);
+			__cli();
 			do_vlb_sync(IDE_NSECTOR_REG);
 			insl(IDE_DATA_REG, buffer, wcount);
-			restore_flags(flags);
+			__restore_flags(flags);
 		} else
 #endif /* SUPPORT_VLB_SYNC */
 			insl(IDE_DATA_REG, buffer, wcount);
@@ -347,11 +347,11 @@
 #if SUPPORT_VLB_SYNC
 		if (io_32bit & 2) {
 			unsigned long flags;
-			save_flags(flags);
-			cli();
+			__save_flags(flags);
+			__cli();
 			do_vlb_sync(IDE_NSECTOR_REG);
 			outsl(IDE_DATA_REG, buffer, wcount);
-			restore_flags(flags);
+			__restore_flags(flags);
 		} else
 #endif /* SUPPORT_VLB_SYNC */
 			outsl(IDE_DATA_REG, buffer, wcount);
@@ -573,8 +573,8 @@
 	ide_hwif_t *hwif = HWIF(drive);
 	ide_hwgroup_t *hwgroup = HWGROUP(drive);
 
-	save_flags(flags);
-	cli();		/* Why ? */
+	__save_flags(flags);
+	__cli();		/* Why ? */
 
 	/* For an ATAPI device, first try an ATAPI SRST. */
 	if (drive->media != ide_disk && !do_not_try_atapi) {
@@ -584,7 +584,7 @@
 		OUT_BYTE (WIN_SRST, IDE_COMMAND_REG);
 		hwgroup->poll_timeout = jiffies + WAIT_WORSTCASE;
 		ide_set_handler (drive, &atapi_reset_pollfunc, HZ/20);
-		restore_flags (flags);
+		__restore_flags (flags);
 		return;
 	}
 
@@ -612,7 +612,7 @@
 	ide_set_handler (drive, &reset_pollfunc, HZ/20);
 #endif	/* OK_TO_RESET_CONTROLLER */
 
-	restore_flags (flags);
+	__restore_flags (flags);
 }
 
 /*
@@ -640,15 +640,15 @@
 			args[2] = IN_BYTE(IDE_NSECTOR_REG);
 		}
 	}
-	save_flags(flags);
-	cli();
+	__save_flags(flags);
+	__cli();
 	drive->queue = rq->next;
 	blk_dev[MAJOR(rq->rq_dev)].current_request = NULL;
 	HWGROUP(drive)->rq = NULL;
 	rq->rq_status = RQ_INACTIVE;
 	if (rq->sem != NULL)
 		up(rq->sem);
-	restore_flags(flags);
+	__restore_flags(flags);
 }
 
 /*
@@ -659,8 +659,8 @@
 	unsigned long flags;
 	byte err = 0;
 
-	save_flags (flags);
-	ide_sti();
+	__save_flags (flags);
+	/* ide_sti(); HACK */
 	printk("%s: %s: status=0x%02x", drive->name, msg, stat);
 #if FANCY_STATUS_DUMPS
 	printk(" { ");
@@ -713,7 +713,7 @@
 #endif	/* FANCY_STATUS_DUMPS */
 		printk("\n");
 	}
-	restore_flags (flags);
+	__restore_flags (flags);
 	return err;
 }
 
@@ -814,7 +814,7 @@
 	byte stat = GET_STAT();
 	int retries = 10;
 
-	ide_sti();
+	/* ide_sti(); HACK */
 	if ((stat & DRQ_STAT) && args && args[3]) {
 		byte io_32bit = drive->io_32bit;
 		drive->io_32bit = 0;
@@ -872,17 +872,17 @@
 
 	udelay(1);	/* spec allows drive 400ns to assert "BUSY" */
 	if ((stat = GET_STAT()) & BUSY_STAT) {
-		save_flags(flags);
-		ide_sti();
+		__save_flags(flags);
+		/* ide_sti(); HACK */
 		timeout += jiffies;
 		while ((stat = GET_STAT()) & BUSY_STAT) {
 			if (0 < (signed long)(jiffies - timeout)) {
-				restore_flags(flags);
+				__restore_flags(flags);
 				ide_error(drive, "status timeout", stat);
 				return 1;
 			}
 		}
-		restore_flags(flags);
+		__restore_flags(flags);
 	}
 	udelay(1);	/* allow status to settle, then read it again */
 	if (OK_STAT((stat = GET_STAT()), good, bad))
@@ -932,7 +932,7 @@
 	struct request *rq = drive->queue;
 	unsigned int minor = MINOR(rq->rq_dev), unit = minor >> PARTN_BITS;
 
-	ide_sti();
+	/* ide_sti(); HACK */
 #ifdef DEBUG
 	printk("%s: do_request: current=0x%08lx\n", hwif->name, (unsigned long) rq);
 #endif
@@ -1093,7 +1093,7 @@
  */
 void ide_do_request (ide_hwgroup_t *hwgroup)
 {
-	cli();	/* paranoia */
+	__cli();	/* paranoia */
 	if (hwgroup->handler != NULL) {
 		printk("%s: EEeekk!! handler not NULL in ide_do_request()\n", hwgroup->hwif->name);
 		return;
@@ -1108,7 +1108,7 @@
 			blk_dev[hwif->major].current_request = hwgroup->rq = drive->queue;
 			drive->service_start = jiffies;
 			do_request(hwgroup, hwif, drive);
-			cli();
+			__cli();
 		} else {
 			ide_leave_hwgroup(hwgroup);	/* no work left for this hwgroup */
 			return;
@@ -1157,27 +1157,27 @@
 	}
 }
 
-void do_ide0_request (void)	/* invoked with cli() */
+void do_ide0_request (void)	/* invoked with __cli() */
 {
 	do_hwgroup_request (ide_hwifs[0].hwgroup);
 }
 
 #if MAX_HWIFS > 1
-void do_ide1_request (void)	/* invoked with cli() */
+void do_ide1_request (void)	/* invoked with __cli() */
 {
 	do_hwgroup_request (ide_hwifs[1].hwgroup);
 }
 #endif /* MAX_HWIFS > 1 */
 
 #if MAX_HWIFS > 2
-void do_ide2_request (void)	/* invoked with cli() */
+void do_ide2_request (void)	/* invoked with __cli() */
 {
 	do_hwgroup_request (ide_hwifs[2].hwgroup);
 }
 #endif /* MAX_HWIFS > 2 */
 
 #if MAX_HWIFS > 3
-void do_ide3_request (void)	/* invoked with cli() */
+void do_ide3_request (void)	/* invoked with __cli() */
 {
 	do_hwgroup_request (ide_hwifs[3].hwgroup);
 }
@@ -1190,8 +1190,8 @@
 	ide_handler_t *handler;
 	unsigned long flags;
 
-	save_flags(flags);
-	cli();
+	__save_flags(flags);
+	__cli();
 
 	if ((handler = hwgroup->handler) != NULL) {
 		hwgroup->handler = NULL;
@@ -1202,7 +1202,7 @@
 				(void) hwgroup->hwif->dmaproc (ide_dma_end, drive);
 			ide_error(drive, "irq timeout", GET_STAT());
 		}
-		cli();
+		__cli();
 		if (hwgroup->handler == NULL) {
 			set_recovery_timer(HWIF(drive));
 			drive->service_time = jiffies - drive->service_start;
@@ -1210,7 +1210,7 @@
 		}
 	} else
 		do_hwgroup_request (hwgroup);
-	restore_flags(flags);
+	__restore_flags(flags);
 }
 
 /*
@@ -1223,7 +1223,7 @@
  * drive enters "idle", "standby", or "sleep" mode, so if the status looks
  * "good", we just ignore the interrupt completely.
  *
- * This routine assumes cli() is in effect when called.
+ * This routine assumes __cli() is in effect when called.
  *
  * If an unexpected interrupt happens on irq15 while we are handling irq14
  * and if the two interfaces are "serialized" (CMD640), then it looks like
@@ -1261,10 +1261,11 @@
 }
 
 /*
- * entry point for all interrupts, caller does cli() for us
+ * entry point for all interrupts, caller does __cli() for us
  */
 void ide_intr (int irq, void *dev_id, struct pt_regs *regs)
 {
+	unsigned long flags;
 	ide_hwgroup_t *hwgroup = dev_id;
 	ide_hwif_t *hwif = hwgroup->hwif;
 	ide_handler_t *handler;
@@ -1290,19 +1291,21 @@
 #endif	/* temporary */
 		hwgroup->handler = NULL;
 		del_timer(&(hwgroup->timer));
-		if (drive->unmask)
-			ide_sti();
+		/* if (drive->unmask)
+			ide_sti(); HACK */
 		handler(drive);
-		cli();	/* this is necessary, as next rq may be different irq */
+		/* this is necessary, as next rq may be different irq */
+		spin_lock_irqsave(&io_request_lock,flags);
 		if (hwgroup->handler == NULL) {
 			set_recovery_timer(HWIF(drive));
 			drive->service_time = jiffies - drive->service_start;
 			ide_do_request(hwgroup);
 		}
+		spin_unlock_irqrestore(&io_request_lock,flags);
 	} else {
 		unexpected_intr(irq, hwgroup);
 	}
-	cli();
+	__cli();
 	hwif = hwgroup->hwif;
 	do {
 		if (hwif->irq != irq) enable_irq(hwif->irq);
@@ -1390,8 +1393,8 @@
 	if (action == ide_wait)
 		rq->sem = &sem;
 
-	save_flags(flags);
-	cli();
+	__save_flags(flags);
+	__cli();
 	cur_rq = drive->queue;
 
 	if (cur_rq == NULL || action == ide_preempt) {
@@ -1409,11 +1412,11 @@
 	}
 	if (!hwgroup->active) {
 		do_hwgroup_request(hwgroup);
-		cli();
+		__cli();
 	}
 	if (action == ide_wait  && rq->rq_status != RQ_INACTIVE)
 		down(&sem);	/* wait for it to be serviced */
-	restore_flags(flags);
+	__restore_flags(flags);
 	return rq->errors ? -EIO : 0;	/* return -EIO if errors */
 }
 
@@ -1435,15 +1438,15 @@
 		return -ENODEV;
 	major = MAJOR(i_rdev);
 	minor = drive->select.b.unit << PARTN_BITS;
-	save_flags(flags);
-	cli();
+	__save_flags(flags);
+	__cli();
 	if (drive->busy || (drive->usage > 1)) {
-		restore_flags(flags);
+		__restore_flags(flags);
 		return -EBUSY;
 	};
 	drive->busy = 1;
 	MOD_INC_USE_COUNT;
-	restore_flags(flags);
+	__restore_flags(flags);
 
 	for (p = 0; p < (1<<PARTN_BITS); ++p) {
 		if (drive->part[p].nr_sects > 0) {
@@ -1589,8 +1592,8 @@
 
 	if (index >= MAX_HWIFS)
 		return;
-	save_flags(flags);
-	cli();
+	__save_flags(flags);
+	__cli();
 	hwif = &ide_hwifs[index];
 	if (!hwif->present)
 		goto abort;
@@ -1680,7 +1683,7 @@
 	}
 	init_hwif_data (index);	/* restore hwif data to pristine status */
 abort:
-	restore_flags(flags);
+	__restore_flags(flags);
 }
 
 int ide_register (int arg1, int arg2, int irq)
@@ -1825,8 +1828,8 @@
 		return -EPERM;
 	if (val < setting->min || val > setting->max)
 		return -EINVAL;
-	save_flags(flags);
-	cli();
+	__save_flags(flags);
+	__cli();
 	if (setting->set)
 		rc = setting->set(drive, val);
 	else switch (setting->data_type) {
@@ -1845,7 +1848,7 @@
 				*p = val;
 			break;
 	}
-	restore_flags(flags);
+	__restore_flags(flags);
 	return rc;
 }
 
@@ -2697,16 +2700,16 @@
 {
 	unsigned long flags;
 	
-	save_flags(flags);
-	cli();
+	__save_flags(flags);
+	__cli();
 	if (version != IDE_SUBDRIVER_VERSION || !drive->present || drive->driver != NULL ||
 	    drive->busy || drive->usage) {
-	    	restore_flags(flags);
+	    	__restore_flags(flags);
 		return 1;
 	}
 	drive->driver = driver;
 	setup_driver_defaults(drive);
-	restore_flags(flags);
+	__restore_flags(flags);
 	if (drive->autotune != 2) {
 		if (driver->supports_dma && HWIF(drive)->dmaproc != NULL)
 			(void) (HWIF(drive)->dmaproc(ide_dma_check, drive));
@@ -2723,17 +2726,17 @@
 {
 	unsigned long flags;
 	
-	save_flags(flags);
-	cli();
+	__save_flags(flags);
+	__cli();
 	if (drive->usage || drive->busy || drive->driver == NULL || DRIVER(drive)->busy) {
-		restore_flags(flags);
+		__restore_flags(flags);
 		return 1;
 	}
 	ide_remove_proc_entries(drive, DRIVER(drive)->proc);
 	ide_remove_proc_entries(drive, generic_subdriver_entries);
 	auto_remove_settings(drive);
 	drive->driver = NULL;
-	restore_flags(flags);
+	__restore_flags(flags);
 	return 0;
 }
 
diff -u --recursive --new-file v2.1.87/linux/drivers/block/ll_rw_blk.c linux/drivers/block/ll_rw_blk.c
--- v2.1.87/linux/drivers/block/ll_rw_blk.c	Tue Feb 17 13:12:46 1998
+++ linux/drivers/block/ll_rw_blk.c	Fri Feb 20 15:42:05 1998
@@ -22,6 +22,9 @@
 #include <asm/io.h>
 #include <linux/blk.h>
 
+#define ATOMIC_ON()	do { } while (0)
+#define ATOMIC_OFF()	do { } while (0)
+
 /*
  * The request-struct contains all necessary data
  * to load a nr of sectors into memory
@@ -37,7 +40,7 @@
 /*
  * Protect the request list against multiple users..
  */
-spinlock_t current_lock = SPIN_LOCK_UNLOCKED;
+spinlock_t io_request_lock = SPIN_LOCK_UNLOCKED;
 
 /*
  * used to wait on when there are no free requests
@@ -340,6 +343,7 @@
 	unsigned int sector, count;
 	struct request * req;
 	int rw_ahead, max_req, max_sectors;
+	unsigned long flags;
 
 	count = bh->b_size >> 9;
 	sector = bh->b_rsector;
@@ -407,13 +411,20 @@
 	 * Try to coalesce the new request with old requests
 	 */
 	max_sectors = get_max_sectors(bh->b_rdev);
-	cli();
+
+	/*
+	 * Now we acquire the request spinlock, we have to be mega careful
+	 * not to schedule or do something nonatomic
+	 */
+	spin_lock_irqsave(&io_request_lock,flags);
+	ATOMIC_ON();
+
 	req = *get_queue(bh->b_rdev);
 	if (!req) {
 		/* MD and loop can't handle plugging without deadlocking */
 		if (major != MD_MAJOR && major != LOOP_MAJOR && 
 		    major != DDV_MAJOR)
-			plug_device(blk_dev + major);
+			plug_device(blk_dev + major); /* is atomic */
 	} else switch (major) {
 	     case IDE0_MAJOR:	/* same as HD_MAJOR */
 	     case IDE1_MAJOR:
@@ -467,14 +478,18 @@
 				continue;
 
 			mark_buffer_clean(bh);
-		    	sti();
+			ATOMIC_OFF();
+			spin_unlock_irqrestore(&io_request_lock,flags);
 		    	return;
+
 		} while ((req = req->next) != NULL);
 	}
 
 /* find an unused request. */
 	req = get_request(max_req, bh->b_rdev);
-	sti();
+
+	ATOMIC_OFF();
+	spin_unlock_irqrestore(&io_request_lock,flags);
 
 /* if no request available: if rw_ahead, forget it; otherwise try again blocking.. */
 	if (!req) {
@@ -646,9 +661,12 @@
 			if (j == 0) {
 				req[j] = get_request_wait(max_req, rdev);
 			} else {
-				cli();
+				unsigned long flags;
+				spin_lock_irqsave(&io_request_lock,flags);
+				ATOMIC_ON();
 				req[j] = get_request(max_req, rdev);
-				sti();
+				ATOMIC_OFF();
+				spin_unlock_irqrestore(&io_request_lock,flags);
 				if (req[j] == NULL)
 					break;
 			}
diff -u --recursive --new-file v2.1.87/linux/drivers/block/rz1000.c linux/drivers/block/rz1000.c
--- v2.1.87/linux/drivers/block/rz1000.c	Wed Dec 17 11:11:51 1997
+++ linux/drivers/block/rz1000.c	Fri Feb 20 18:28:22 1998
@@ -15,6 +15,7 @@
 
 #undef REALLY_SLOW_IO		/* most systems can safely undef this */
 
+#include <linux/config.h> /* for CONFIG_BLK_DEV_IDEPCI */
 #include <linux/types.h>
 #include <linux/kernel.h>
 #include <linux/delay.h>
diff -u --recursive --new-file v2.1.87/linux/drivers/char/amigamouse.c linux/drivers/char/amigamouse.c
--- v2.1.87/linux/drivers/char/amigamouse.c	Sat Sep 13 11:07:27 1997
+++ linux/drivers/char/amigamouse.c	Fri Feb 20 17:55:45 1998
@@ -44,17 +44,17 @@
 #include <linux/random.h>
 #include <linux/poll.h>
 #include <linux/init.h>
+#include <linux/busmouse.h>
 
 #include <asm/setup.h>
 #include <asm/system.h>
 #include <asm/uaccess.h>
 #include <asm/irq.h>
-#include <asm/amigamouse.h>
 #include <asm/amigahw.h>
 #include <asm/amigaints.h>
 
-#define MSE_INT_ON()	mouseint_allowed = 1
-#define MSE_INT_OFF()	mouseint_allowed = 0
+#define AMI_MSE_INT_ON()	mouseint_allowed = 1
+#define AMI_MSE_INT_OFF()	mouseint_allowed = 0
 
 
 static struct mouse_status mouse;
@@ -72,7 +72,7 @@
 
 	if(!mouseint_allowed)
 		return;
-	MSE_INT_OFF();
+	AMI_MSE_INT_OFF();
 
 	/*
 	 *  This routine assumes, just like Kickstart, that the mouse
@@ -156,7 +156,7 @@
 	  if (mouse.fasyncptr)
 	      kill_fasync(mouse.fasyncptr, SIGIO);
 	}
-	MSE_INT_ON();
+	AMI_MSE_INT_ON();
 }
 
 static int fasync_mouse(struct file *filp, int on)
@@ -178,7 +178,7 @@
 	if (--mouse.active)
 		return 0;
 	free_irq(IRQ_AMIGA_VERTB, mouse_interrupt);
-	MSE_INT_OFF();
+	AMI_MSE_INT_OFF();
 	MOD_DEC_USE_COUNT;
 	return 0;
 }
@@ -211,7 +211,7 @@
 	mouse.buttons = 0x87;
 	mouse.active = 1;
 	MOD_INC_USE_COUNT;
-	MSE_INT_ON();
+	AMI_MSE_INT_ON();
 	return 0;
 }
 
@@ -219,8 +219,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;
 }
@@ -229,18 +229,15 @@
  * 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;
 	int dy;
 	unsigned char buttons; 
 
 	if (count < 3)
 		return -EINVAL;
-	if ((r = verify_area(VERIFY_WRITE, buffer, count)))
-		return r;
 	if (!mouse.ready)
 		return -EAGAIN;
 
@@ -251,7 +248,7 @@
 	 * so paging in put_user() does not effect mouse tracking.
 	 */
 
-	MSE_INT_OFF();
+	AMI_MSE_INT_OFF();
 	dx = mouse.dx;
 	dy = mouse.dy;
 	if (dx < -127)
@@ -268,14 +265,17 @@
 	mouse.dx -= dx;
 	mouse.dy -= dy;
 	mouse.ready = 0;
-	MSE_INT_ON();
+	AMI_MSE_INT_ON();
 
-	put_user(buttons | 0x80, buffer);
-	put_user((char)dx, buffer + 1);
-	put_user((char)dy, buffer + 2);
-	for (r = 3; r < count; r++)
-	    put_user(0x00, buffer + r);
-	return r;
+	if ((put_user(buttons | 0x80, buffer++)) ||
+	    put_user((char)dx, buffer++) ||
+	    put_user((char)dy, buffer++))
+		return -EINVAL;
+
+	if (count > 3)
+		if (clear_user(buffer, count - 3))
+			return -EFAULT;
+	return count;
 }
 
 /*
@@ -315,7 +315,7 @@
 
 	custom.joytest = 0;	/* reset counters */
 
-	MSE_INT_OFF();
+	AMI_MSE_INT_OFF();
 
 	mouse.active = 0;
 	mouse.ready = 0;
diff -u --recursive --new-file v2.1.87/linux/drivers/char/apm_bios.c linux/drivers/char/apm_bios.c
--- v2.1.87/linux/drivers/char/apm_bios.c	Thu Feb 12 20:56:05 1998
+++ linux/drivers/char/apm_bios.c	Thu Feb 19 14:58:40 1998
@@ -141,6 +141,8 @@
  * U: ACER 486DX4/75: uses dseg 0040, in violation of APM specification
  *                    [Confirmed by BIOS disassembly]
  * P: Toshiba 1950S: battery life information only gets updated after resume
+ * P: Midwest Micro Soundbook Elite DX2/66 monochrome: screen blanking
+ * 	broken in BIOS [Reported by Garst R. Reese <reese@isn.net>]
  *
  * Legend: U = unusable with APM patches
  *         P = partially usable with APM patches
@@ -279,9 +281,15 @@
 	: "a" (0x530a), "b" (1) \
 	APM_BIOS_CALL_END
 
-#define APM_GET_EVENT(event, error)	\
+#define APM_GET_BATTERY_STATUS(which, bx, cx, dx, si, error) \
 	APM_BIOS_CALL(al) \
-	: "=a" (error), "=b" (event) \
+	: "=a" (error), "=b" (bx), "=c" (cx), "=d" (dx), "=S" (si) \
+	: "a" (0x530a), "b" (0x8000 | (which)) \
+	APM_BIOS_CALL_END
+
+#define APM_GET_EVENT(event, info, error)	\
+	APM_BIOS_CALL(al) \
+	: "=a" (error), "=b" (event), "=c" (info) \
 	: "a" (0x530b) \
 	APM_BIOS_CALL_END
 
@@ -356,7 +364,8 @@
 	"critical suspend",
 	"user standby",
 	"user suspend",
-	"system standby resume"
+	"system standby resume",
+	"capabilities change"
 };
 #define NR_APM_EVENT_NAME	\
 		(sizeof(apm_event_name) / sizeof(apm_event_name[0]))
@@ -406,6 +415,8 @@
 	{ APM_BAD_DEVICE,	"Unrecognized device ID" },
 	{ APM_BAD_PARAM,	"Parameter out of range" },
 	{ APM_NOT_ENGAGED,	"Interface not engaged" },
+	{ APM_BAD_FUNCTION,     "Function not supported" },
+	{ APM_RESUME_DISABLED,	"Resume timer disabled" },
 	{ APM_BAD_STATE,	"Unable to enter requested state" },
 /* N/A	{ APM_NO_EVENTS,	"No events pending" }, */
 	{ APM_NOT_PRESENT,	"No APM present" }
@@ -423,13 +434,15 @@
 	return APM_SUCCESS;
 }
 
-static int apm_get_event(apm_event_t *event)
+static int apm_get_event(apm_event_t *event, apm_eventinfo_t *info)
 {
 	u_short	error;
 
-	APM_GET_EVENT(*event, error);
+	APM_GET_EVENT(*event, *info, error);
 	if (error & 0xff)
 		return (error >> 8);
+	if (apm_bios_info.version < 0x0102)
+		*info = ~0; /* indicate info not valid */
 	return APM_SUCCESS;
 }
 
@@ -481,6 +494,24 @@
 	return APM_SUCCESS;
 }
 
+static int apm_get_battery_status(u_short which, 
+				  u_short *bat, u_short *life, u_short *nbat)
+{
+	u_short status, error;
+
+	if (apm_bios_info.version < 0x0102) {
+		/* pretend we only have one battery. */
+		if (which!=1) return APM_BAD_DEVICE;
+		*nbat = 1;
+		return apm_get_power_status(&status, bat, life);
+	}
+
+	APM_GET_BATTERY_STATUS(which, status, *bat, *life, *nbat, error);
+	if (error & 0xff)
+		return (error >> 8);
+	return APM_SUCCESS;
+}
+
 static inline int apm_engage_power_management(u_short device)
 {
 	u_short	error;
@@ -652,10 +683,12 @@
 {
 	int		error;
 	apm_event_t	event;
+	apm_eventinfo_t	info;
 
 	static int notified = 0;
 
-	error = apm_get_event(&event);
+	/* we don't use the eventinfo */
+	error = apm_get_event(&event, &info);
 	if (error == APM_SUCCESS)
 		return event;
 
@@ -718,6 +751,7 @@
 
 		case APM_LOW_BATTERY:
 		case APM_POWER_STATUS_CHANGE:
+		case APM_CAPABILITY_CHANGE:
 			send_event(event, 0, NULL);
 			break;
 
@@ -1106,12 +1140,17 @@
 	if (apm_bios_info.version == 0x001)
 		apm_bios_info.version = 0x100;
 
+	/* BIOS < 1.2 doesn't set cseg_16_len */
+	if (apm_bios_info.version < 0x102)
+		apm_bios_info.cseg_16_len = 0xFFFF; /* 64k */
+
 	printk(KERN_INFO "    Entry %x:%lx cseg16 %x dseg %x",
 	       apm_bios_info.cseg, apm_bios_info.offset,
 	       apm_bios_info.cseg_16, apm_bios_info.dseg);
 	if (apm_bios_info.version > 0x100)
-		printk(" cseg len %x, dseg len %x",
-		       apm_bios_info.cseg_len, apm_bios_info.dseg_len);
+		printk(" cseg len %x, cseg16 len %x, dseg len %x",
+		       apm_bios_info.cseg_len, apm_bios_info.cseg_16_len,
+		       apm_bios_info.dseg_len);
 	printk("\n");
 
 	/*
@@ -1146,19 +1185,25 @@
 		set_limit(gdt[APM_DS >> 3], 64 * 1024);
 #else
 		set_limit(gdt[APM_CS >> 3], apm_bios_info.cseg_len);
-		set_limit(gdt[APM_CS_16 >> 3], 64 * 1024);
+		set_limit(gdt[APM_CS_16 >> 3], apm_bios_info.cseg_16_len);
 		set_limit(gdt[APM_DS >> 3], apm_bios_info.dseg_len);
 #endif
-		apm_bios_info.version = 0x0101;
+		/* The APM 1.2 docs state that the apm_driver_version
+		 * call can fail if we try to connect as 1.2 to a 1.1 bios.
+		 */
+		apm_bios_info.version = 0x0102;
 		error = apm_driver_version(&apm_bios_info.version);
-		if (error != 0)
+		if (error != 0) { /* Fall back to an APM 1.1 connection. */
+			apm_bios_info.version = 0x0101;
+			error = apm_driver_version(&apm_bios_info.version);
+		}
+		if (error != 0) /* Fall back to an APM 1.0 connection. */
 			apm_bios_info.version = 0x100;
 		else {
 			apm_engage_power_management(0x0001);
 			printk( "    Connection version %d.%d\n",
 				(apm_bios_info.version >> 8) & 0xff,
 				apm_bios_info.version & 0xff );
-			apm_bios_info.version = 0x0101;
 		}
 	}
 
diff -u --recursive --new-file v2.1.87/linux/drivers/char/atarimouse.c linux/drivers/char/atarimouse.c
--- v2.1.87/linux/drivers/char/atarimouse.c	Sat Sep 13 11:07:27 1997
+++ linux/drivers/char/atarimouse.c	Fri Feb 20 17:55:45 1998
@@ -90,14 +90,14 @@
     return 0;
 }
 
-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;
 }
 
-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 dx, dy, buttons;
 
diff -u --recursive --new-file v2.1.87/linux/drivers/char/bttv.c linux/drivers/char/bttv.c
--- v2.1.87/linux/drivers/char/bttv.c	Tue Feb 17 13:12:46 1998
+++ linux/drivers/char/bttv.c	Fri Feb 20 18:28:22 1998
@@ -24,7 +24,6 @@
 
 #include <linux/module.h>
 #include <linux/bios32.h>
-#include <linux/config.h>
 #include <linux/delay.h>
 #include <linux/errno.h>
 #include <linux/fs.h>
@@ -1684,7 +1683,7 @@
 		return -1;
 	if (!(btv->risc_even=(dword *) kmalloc(RISCMEM_LEN/2, GFP_KERNEL)))
 		return -1;
-	if (!(btv->risc_jmp =(dword *) kmalloc(1024, GFP_KERNEL)))
+	if (!(btv->risc_jmp =(dword *) kmalloc(2048, GFP_KERNEL)))
 		return -1;
 	btv->vbi_odd=btv->risc_jmp+12;
 	btv->vbi_even=btv->vbi_odd+256;
diff -u --recursive --new-file v2.1.87/linux/drivers/char/bw-qcam.c linux/drivers/char/bw-qcam.c
--- v2.1.87/linux/drivers/char/bw-qcam.c	Fri Jan 23 18:10:31 1998
+++ linux/drivers/char/bw-qcam.c	Fri Feb 20 18:28:22 1998
@@ -43,7 +43,6 @@
 ******************************************************************/
 
 #include <linux/module.h>
-#include <linux/config.h>
 #include <linux/delay.h>
 #include <linux/errno.h>
 #include <linux/fs.h>
diff -u --recursive --new-file v2.1.87/linux/drivers/char/c-qcam.c linux/drivers/char/c-qcam.c
--- v2.1.87/linux/drivers/char/c-qcam.c	Fri Jan 23 18:10:31 1998
+++ linux/drivers/char/c-qcam.c	Fri Feb 20 18:28:22 1998
@@ -9,7 +9,6 @@
 
 
 #include <linux/module.h>
-#include <linux/config.h>
 #include <linux/delay.h>
 #include <linux/errno.h>
 #include <linux/fs.h>
diff -u --recursive --new-file v2.1.87/linux/drivers/char/dsp56k.c linux/drivers/char/dsp56k.c
--- v2.1.87/linux/drivers/char/dsp56k.c	Mon Jan 19 13:43:25 1998
+++ linux/drivers/char/dsp56k.c	Fri Feb 20 18:28:22 1998
@@ -23,7 +23,6 @@
  * for more details.
  */
 
-#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/version.h>
 #include <linux/malloc.h>	/* for kmalloc() and kfree() */
@@ -207,9 +206,10 @@
 	return 0;
 }
 
-static long dsp56k_read(struct inode *inode, struct file *file,
-			char *buf, unsigned long count)
+static ssize_t dsp56k_read(struct file *file, char *buf, size_t count,
+			   loff_t *ppos)
 {
+	struct inode *inode = file->f_dentry->d_inode;
 	int dev = MINOR(inode->i_rdev) & 0x0f;
 
 	switch(dev)
@@ -269,9 +269,10 @@
 	}
 }
 
-static long dsp56k_write(struct inode *inode, struct file *file,
-			 const char *buf, unsigned long count)
+static ssize_t dsp56k_write(struct file *file, const char *buf, size_t count,
+			    loff_t *ppos)
 {
+	struct inode *inode = file->f_dentry->d_inode;
 	int dev = MINOR(inode->i_rdev) & 0x0f;
 
 	switch(dev)
diff -u --recursive --new-file v2.1.87/linux/drivers/char/ftape/compressor/zftape-compress.c linux/drivers/char/ftape/compressor/zftape-compress.c
--- v2.1.87/linux/drivers/char/ftape/compressor/zftape-compress.c	Tue Nov 25 14:45:27 1997
+++ linux/drivers/char/ftape/compressor/zftape-compress.c	Fri Feb 20 18:28:22 1998
@@ -31,7 +31,6 @@
  char zftc_rev[] = "$Revision: 1.1.6.1 $";
  char zftc_dat[] = "$Date: 1997/11/16 15:15:56 $";
 
-#include <linux/config.h>
 #include <linux/errno.h>
 #include <linux/mm.h>
 #include <linux/module.h>
diff -u --recursive --new-file v2.1.87/linux/drivers/char/ftape/lowlevel/fdc-io.c linux/drivers/char/ftape/lowlevel/fdc-io.c
--- v2.1.87/linux/drivers/char/ftape/lowlevel/fdc-io.c	Fri Jan 30 11:28:07 1998
+++ linux/drivers/char/ftape/lowlevel/fdc-io.c	Fri Feb 20 18:28:22 1998
@@ -26,6 +26,7 @@
  *      Linux.
  */
 
+#include <linux/config.h> /* for CONFIG_FT_* */
 #include <linux/errno.h>
 #include <linux/sched.h>
 #include <linux/ioport.h>
diff -u --recursive --new-file v2.1.87/linux/drivers/char/ftape/zftape/zftape-rw.c linux/drivers/char/ftape/zftape/zftape-rw.c
--- v2.1.87/linux/drivers/char/ftape/zftape/zftape-rw.c	Tue Nov 25 14:45:28 1997
+++ linux/drivers/char/ftape/zftape/zftape-rw.c	Fri Feb 20 18:28:22 1998
@@ -24,6 +24,7 @@
  *      zftape.
  */
 
+#include <linux/config.h> /* for CONFIG_ZFT_DFLT_BLK_SZ */
 #include <linux/errno.h>
 #include <linux/mm.h>
 #include <asm/segment.h>
diff -u --recursive --new-file v2.1.87/linux/drivers/char/ftape/zftape/zftape-rw.h linux/drivers/char/ftape/zftape/zftape-rw.h
--- v2.1.87/linux/drivers/char/ftape/zftape/zftape-rw.h	Tue Nov 25 14:45:28 1997
+++ linux/drivers/char/ftape/zftape/zftape-rw.h	Fri Feb 20 18:28:22 1998
@@ -28,6 +28,7 @@
  *
  */
 
+#include <linux/config.h> /* for CONFIG_ZFT_DFLT_BLK_SZ */
 #include "../zftape/zftape-buffers.h"
 
 #define SEGMENTS_PER_TAPE  (ft_segments_per_track * ft_tracks_per_tape)
diff -u --recursive --new-file v2.1.87/linux/drivers/char/ftape/zftape/zftape_syms.c linux/drivers/char/ftape/zftape/zftape_syms.c
--- v2.1.87/linux/drivers/char/ftape/zftape/zftape_syms.c	Tue Nov 25 14:45:28 1997
+++ linux/drivers/char/ftape/zftape/zftape_syms.c	Fri Feb 20 18:28:22 1998
@@ -24,7 +24,6 @@
  *      the ftape floppy tape driver exports 
  */		 
 
-#include <linux/config.h>
 #define __NO_VERSION__
 #include <linux/module.h>
 
diff -u --recursive --new-file v2.1.87/linux/drivers/char/hfmodem/main.c linux/drivers/char/hfmodem/main.c
--- v2.1.87/linux/drivers/char/hfmodem/main.c	Sun Nov 30 10:30:19 1997
+++ linux/drivers/char/hfmodem/main.c	Fri Feb 20 18:28:22 1998
@@ -31,6 +31,7 @@
 /*****************************************************************************/
       
 
+#include <linux/config.h> /* for CONFIG_HFMODEM_WSS and CONFIG_HFMODEM_SBC */
 #include <linux/module.h>
 
 #include <linux/kernel.h>
diff -u --recursive --new-file v2.1.87/linux/drivers/char/joystick.c linux/drivers/char/joystick.c
--- v2.1.87/linux/drivers/char/joystick.c	Mon Dec  1 11:05:41 1997
+++ linux/drivers/char/joystick.c	Fri Feb 20 18:28:22 1998
@@ -10,7 +10,6 @@
  *  and credits.
  */
 
-#include <linux/config.h>
 #include <linux/init.h>
 #include <linux/module.h>
 #include <linux/ioport.h>
diff -u --recursive --new-file v2.1.87/linux/drivers/char/pc110pad.c linux/drivers/char/pc110pad.c
--- v2.1.87/linux/drivers/char/pc110pad.c	Sat Nov 29 10:33:19 1997
+++ linux/drivers/char/pc110pad.c	Fri Feb 20 18:28:22 1998
@@ -16,7 +16,6 @@
  *	0.4 1997-11-09 Alan Cox <alan@cymru.net> - Single Unix VFS API changes
  */
 
-#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/busmouse.h>
diff -u --recursive --new-file v2.1.87/linux/drivers/char/pms.c linux/drivers/char/pms.c
--- v2.1.87/linux/drivers/char/pms.c	Fri Jan 23 18:10:31 1998
+++ linux/drivers/char/pms.c	Fri Feb 20 18:28:22 1998
@@ -15,7 +15,6 @@
  */
 
 #include <linux/module.h>
-#include <linux/config.h>
 #include <linux/delay.h>
 #include <linux/errno.h>
 #include <linux/fs.h>
diff -u --recursive --new-file v2.1.87/linux/drivers/char/rocket.c linux/drivers/char/rocket.c
--- v2.1.87/linux/drivers/char/rocket.c	Thu Feb 12 20:56:06 1998
+++ linux/drivers/char/rocket.c	Fri Feb 20 18:28:22 1998
@@ -55,12 +55,10 @@
 #include <linux/modversions.h>
 #endif
 #include <linux/module.h>
-#include <linux/config.h>
 #else /* !NEW_MODULES */
 #ifdef MODVERSIONS
 #define MODULE
 #endif
-#include <linux/config.h>
 #include <linux/module.h>
 #endif /* NEW_MODULES */
 
diff -u --recursive --new-file v2.1.87/linux/drivers/char/selection.c linux/drivers/char/selection.c
--- v2.1.87/linux/drivers/char/selection.c	Mon Jun 16 16:35:55 1997
+++ linux/drivers/char/selection.c	Fri Feb 20 18:28:22 1998
@@ -11,7 +11,6 @@
  * Now that /dev/vcs exists, most of this can disappear again.
  */
 
-#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/tty.h>
 #include <linux/sched.h>
diff -u --recursive --new-file v2.1.87/linux/drivers/isdn/isdn_syms.c linux/drivers/isdn/isdn_syms.c
--- v2.1.87/linux/drivers/isdn/isdn_syms.c	Thu Feb 27 10:57:30 1997
+++ linux/drivers/isdn/isdn_syms.c	Fri Feb 20 18:28:22 1998
@@ -21,7 +21,6 @@
  * Added GPL-Header, Id and Log
  *
  */
-#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/version.h>
 
diff -u --recursive --new-file v2.1.87/linux/drivers/macintosh/aty.c linux/drivers/macintosh/aty.c
--- v2.1.87/linux/drivers/macintosh/aty.c	Mon Jan 12 15:18:13 1998
+++ linux/drivers/macintosh/aty.c	Fri Feb 20 18:28:22 1998
@@ -11,6 +11,7 @@
  * as published by the Free Software Foundation; either version
  * 2 of the License, or (at your option) any later version.
  */
+#include <linux/config.h> /* for CONFIG_CHIP_ID and CONFIG_STAT0 */
 #include <linux/kernel.h>
 #include <linux/sched.h>
 #include <linux/delay.h>
diff -u --recursive --new-file v2.1.87/linux/drivers/misc/parport_ax.c linux/drivers/misc/parport_ax.c
--- v2.1.87/linux/drivers/misc/parport_ax.c	Fri Jan 30 11:28:07 1998
+++ linux/drivers/misc/parport_ax.c	Fri Feb 20 18:28:22 1998
@@ -12,7 +12,6 @@
  */
 
 #include <linux/module.h>
-#include <linux/config.h>
 #include <linux/delay.h>
 #include <linux/errno.h>
 #include <linux/ioport.h>
diff -u --recursive --new-file v2.1.87/linux/drivers/misc/parport_pc.c linux/drivers/misc/parport_pc.c
--- v2.1.87/linux/drivers/misc/parport_pc.c	Thu Feb 12 20:56:07 1998
+++ linux/drivers/misc/parport_pc.c	Fri Feb 20 18:28:22 1998
@@ -39,7 +39,6 @@
 #include <asm/dma.h>
 
 #include <linux/module.h>
-#include <linux/config.h>
 #include <linux/delay.h>
 #include <linux/errno.h>
 #include <linux/interrupt.h>
diff -u --recursive --new-file v2.1.87/linux/drivers/net/3c503.c linux/drivers/net/3c503.c
--- v2.1.87/linux/drivers/net/3c503.c	Sun Jan  4 10:55:08 1998
+++ linux/drivers/net/3c503.c	Thu Feb 19 14:58:40 1998
@@ -675,10 +675,10 @@
 		struct device *dev = &dev_el2[this_dev];
 		if (dev->priv != NULL) {
 			/* NB: el2_close() handles free_irq */
+			unregister_netdev(dev);
 			kfree(dev->priv);
 			dev->priv = NULL;
 			release_region(dev->base_addr, EL2_IO_EXTENT);
-			unregister_netdev(dev);
 		}
 	}
 }
diff -u --recursive --new-file v2.1.87/linux/drivers/net/3c505.c linux/drivers/net/3c505.c
--- v2.1.87/linux/drivers/net/3c505.c	Thu Feb 12 20:56:07 1998
+++ linux/drivers/net/3c505.c	Thu Feb 19 14:58:40 1998
@@ -1694,10 +1694,10 @@
 	for (this_dev = 0; this_dev < ELP_MAX_CARDS; this_dev++) {
 		struct device *dev = &dev_3c505[this_dev];
 		if (dev->priv != NULL) {
+			unregister_netdev(dev);
 			kfree(dev->priv);
 			dev->priv = NULL;
 			release_region(dev->base_addr, ELP_IO_EXTENT);
-			unregister_netdev(dev);
 		}
 	}
 }
diff -u --recursive --new-file v2.1.87/linux/drivers/net/3c509.c linux/drivers/net/3c509.c
--- v2.1.87/linux/drivers/net/3c509.c	Thu Feb 12 20:56:07 1998
+++ linux/drivers/net/3c509.c	Fri Feb 20 18:28:22 1998
@@ -34,7 +34,7 @@
 
 #include <linux/module.h>
 
-#include <linux/config.h>
+#include <linux/config.h>	/* for CONFIG_MCA */
 #include <linux/kernel.h>
 #include <linux/sched.h>
 #include <linux/string.h>
@@ -47,7 +47,6 @@
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
 #include <linux/skbuff.h>
-#include <linux/config.h>	/* for CONFIG_MCA */
 #include <linux/delay.h>	/* for udelay() */
 #include <linux/init.h>
 
@@ -864,11 +863,11 @@
 	for (this_dev = 0; this_dev < MAX_3C_CARDS; this_dev++) {
 		struct device *dev = &dev_3c509[this_dev];
 		if (dev->priv != NULL) {
+			unregister_netdev(dev);
 			kfree_s(dev->priv,sizeof(struct el3_private));
 			dev->priv = NULL;
 			free_irq(dev->irq, dev);
 			release_region(dev->base_addr, EL3_IO_EXTENT);
-			unregister_netdev(dev);
 		}
 	}
 }
diff -u --recursive --new-file v2.1.87/linux/drivers/net/3c59x.c linux/drivers/net/3c59x.c
--- v2.1.87/linux/drivers/net/3c59x.c	Thu Feb 12 20:56:07 1998
+++ linux/drivers/net/3c59x.c	Fri Feb 20 18:28:22 1998
@@ -44,7 +44,6 @@
 #define RX_RING_SIZE	32
 #define PKT_BUF_SZ		1536			/* Size of each temporary Rx buffer.*/
 
-#include <linux/config.h>
 #ifdef MODULE
 #ifdef MODVERSIONS
 #include <linux/modversions.h>
diff -u --recursive --new-file v2.1.87/linux/drivers/net/Makefile linux/drivers/net/Makefile
--- v2.1.87/linux/drivers/net/Makefile	Fri Jan 30 15:50:57 1998
+++ linux/drivers/net/Makefile	Fri Feb 20 18:24:17 1998
@@ -92,6 +92,22 @@
   endif
 endif
 
+ifeq ($(CONFIG_ARM_AM79C961A),y)
+L_OBJS += am79c961a.o
+else
+  ifeq ($(CONFIG_ARM_AM79C961A),m)
+  M_OBJS += am79c961a.o
+  endif
+endif
+
+ifeq ($(CONFIG_ARM_ETHERH),y)
+CONFIG_8390_BUILTIN = y
+else
+  ifeq ($(CONFIG_ARM_ETHERH),m)
+  CONFIG_8390_MODULE = y
+  endif
+endif
+
 
 ifeq ($(CONFIG_WD80x3),y)
 L_OBJS += wd.o
diff -u --recursive --new-file v2.1.87/linux/drivers/net/Space.c linux/drivers/net/Space.c
--- v2.1.87/linux/drivers/net/Space.c	Tue Dec  9 09:49:58 1997
+++ linux/drivers/net/Space.c	Fri Feb 20 18:24:17 1998
@@ -277,6 +277,12 @@
 #ifdef CONFIG_MIPS_JAZZ_SONIC
 	&& sonic_probe(dev)
 #endif	
+#ifdef CONFIG_ARCH_ACORN
+	&& acorn_ethif_probe(dev)
+#endif
+#ifdef CONFIG_ARM_AM79C961A
+	&& am79c961_probe(dev)
+#endif
 	&& 1 ) {
 	return 1;	/* -ENODEV or -EAGAIN would be more accurate. */
     }
diff -u --recursive --new-file v2.1.87/linux/drivers/net/ac3200.c linux/drivers/net/ac3200.c
--- v2.1.87/linux/drivers/net/ac3200.c	Mon Nov  3 09:29:30 1997
+++ linux/drivers/net/ac3200.c	Thu Feb 19 14:58:40 1998
@@ -366,12 +366,12 @@
 	for (this_dev = 0; this_dev < MAX_AC32_CARDS; this_dev++) {
 		struct device *dev = &dev_ac32[this_dev];
 		if (dev->priv != NULL) {
+			unregister_netdev(dev);
 			kfree(dev->priv);
 			dev->priv = NULL;
 			/* Someday free_irq may be in ac_close_card() */
 			free_irq(dev->irq, dev);
 			release_region(dev->base_addr, AC_IO_EXTENT);
-			unregister_netdev(dev);
 		}
 	}
 }
diff -u --recursive --new-file v2.1.87/linux/drivers/net/am79c961a.c linux/drivers/net/am79c961a.c
--- v2.1.87/linux/drivers/net/am79c961a.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/am79c961a.c	Mon Feb 16 15:50:21 1998
@@ -0,0 +1,638 @@
+/*
+ *	linux/drivers/net/am79c961.c
+ *
+ *	Derived from various things including skeleton.c
+ *
+ *	R.M.King 1995.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/in.h>
+#include <linux/malloc.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <asm/ecard.h>
+#include <asm/delay.h>
+
+#define TX_BUFFERS 15
+#define RX_BUFFERS 25
+
+#include "am79c961a.h"
+
+static unsigned int net_debug = NET_DEBUG;
+static void am79c961_setmulticastlist (struct device *dev);
+
+static char *version = "am79c961 ethernet driver (c) 1995 R.M.King v0.00\n";
+
+static void write_rreg (unsigned long base, unsigned int reg, unsigned short val)
+{
+    __asm__("
+	strh	%1, [%2]	@ NET_RAP
+	strh	%0, [%2, #-4]	@ NET_RDP
+	" : : "r" (val), "r" (reg), "r" (0xf0000464));
+}
+
+static inline void
+write_ireg (unsigned long base, unsigned int reg, unsigned short val)
+{
+    __asm__("
+	strh	%1, [%2]	@ NET_RAP
+	strh	%0, [%2, #8]	@ NET_RDP
+	" : : "r" (val), "r" (reg), "r" (0xf0000464));
+}
+
+#define am_writeword(dev,off,val)\
+    __asm__("\
+	strh	%0, [%1]\
+	" : : "r" ((val) & 0xffff), "r" (0xe0000000 + ((off) << 1)));
+
+static inline void am_writebuffer(struct device *dev, unsigned int offset, unsigned char *buf, unsigned int length)
+{
+	offset = 0xe0000000 + (offset << 1);
+	length = (length + 1) & ~1;
+	if ((int)buf & 2) {
+		__asm__ __volatile__("
+			strh	%2, [%0], #4
+		" : "=&r" (offset) : "0" (offset), "r" (buf[0] | (buf[1] << 8)));
+		buf += 2;
+		length -= 2;
+	}
+	while (length > 8) 
+	{
+		unsigned int tmp, tmp2;
+		__asm__ __volatile__("
+			ldmia	%1!, {%2, %3}
+			strh	%2, [%0], #4
+			mov	%2, %2, lsr #16
+			strh	%2, [%0], #4
+			strh	%3, [%0], #4
+			mov	%3, %3, lsr #16
+			strh	%3, [%0], #4
+		" : "=&r" (offset), "=&r" (buf), "=r" (tmp), "=r" (tmp2)
+		  : "0" (offset), "1" (buf));
+		length -= 8;
+	}
+	while (length > 0) {
+		__asm__ __volatile__("
+			strh	%2, [%0], #4
+		" : "=&r" (offset) : "0" (offset), "r" (buf[0] | (buf[1] << 8)));
+		buf += 2;
+		length -= 2;
+	}
+}
+
+static inline unsigned short
+read_rreg (unsigned int base_addr, unsigned int reg)
+{
+	unsigned short v;
+	__asm__("
+		strh	%1, [%2]	@ NET_RAP
+		ldrh	%0, [%2, #-4]	@ NET_IDP
+		" : "=r" (v): "r" (reg), "r" (0xf0000464));
+	return v;
+}
+
+static inline unsigned short am_readword (struct device *dev, unsigned long off)
+{
+	unsigned long address = 0xe0000000 + (off << 1);
+	unsigned short val;
+
+	__asm__("
+		ldrh	%0, [%1]
+		" : "=r" (val): "r" (address));
+	return val;
+}
+
+static inline void am_readbuffer(struct device *dev, unsigned int offset, unsigned char *buf, unsigned int length)
+{
+	offset = 0xe0000000 + (offset << 1);
+	length = (length + 1) & ~1;
+	if ((int)buf & 2) {
+		unsigned int tmp;
+		__asm__ __volatile__("
+			ldrh	%2, [%0], #4
+			strb	%2, [%1], #1
+			mov	%2, %2, lsr #8
+			strb	%2, [%1], #1
+		" : "=&r" (offset), "=&r" (buf), "=r" (tmp): "0" (offset), "1" (buf));
+		length -= 2;
+	}
+	while (length > 8) {
+		unsigned int tmp, tmp2, tmp3;
+		__asm__ __volatile__("
+			ldrh	%2, [%0], #4
+			ldrh	%3, [%0], #4
+			orr	%2, %2, %3, lsl #16
+			ldrh	%3, [%0], #4
+			ldrh	%4, [%0], #4
+			orr	%3, %3, %4, lsl #16
+			stmia	%1!, {%2, %3}
+		" : "=&r" (offset), "=&r" (buf), "=r" (tmp), "=r" (tmp2), "=r" (tmp3)
+		  : "0" (offset), "1" (buf));
+		length -= 8;
+	}
+	while (length > 0) {
+		unsigned int tmp;
+		__asm__ __volatile__("
+			ldrh	%2, [%0], #4
+			strb	%2, [%1], #1
+			mov	%2, %2, lsr #8
+			strb	%2, [%1], #1
+		" : "=&r" (offset), "=&r" (buf), "=r" (tmp) : "0" (offset), "1" (buf));
+		length -= 2;
+	}
+}
+
+/*
+ *	From here on is mostly non ARM specific. Watch the fact it knows
+ *	the chip can hit all memory (kmalloc).
+ */
+
+static int am79c961_ramtest(struct device *dev, unsigned int val)
+{
+	unsigned char *buffer = kmalloc (65536, GFP_KERNEL);
+	int i, error = 0, errorcount = 0;
+
+	if (!buffer)
+		return 0;
+	memset (buffer, val, 65536);
+	am_writebuffer(dev, 0, buffer, 65536);
+	memset (buffer, val ^ 255, 65536);
+	am_readbuffer(dev, 0, buffer, 65536);
+	for (i = 0; i < 65536; i++) {
+		if (buffer[i] != val && !error) {
+			printk ("%s: buffer error (%02X %02X) %05X - ", dev->name, val, buffer[i], i);
+			error = 1;
+			errorcount ++;
+		} else if (error && buffer[i] == val) {
+			printk ("%05X\n", i);
+			error = 0;
+		}
+	}
+	if (error)
+		printk ("10000\n");
+	kfree (buffer);
+	return errorcount;
+}
+
+static void am79c961_init_for_open(struct device *dev)
+{
+    	struct dev_priv *priv = (struct dev_priv *)dev->priv;
+	unsigned long hdr_addr, first_free_addr;
+	unsigned long flags;
+	unsigned char *p;
+	int i;
+
+	save_flags_cli (flags);
+
+	write_ireg (dev->base_addr, 2, 0x4000); /* autoselect media */
+	write_rreg (dev->base_addr, CSR0, CSR0_BABL|CSR0_CERR|CSR0_MISS|CSR0_MERR|CSR0_TINT|CSR0_RINT|CSR0_STOP);
+
+	restore_flags (flags);
+
+	first_free_addr = RX_BUFFERS * 8 + TX_BUFFERS * 8 + 16;
+	hdr_addr = 0;
+
+	priv->rxhead = 0;
+	priv->rxtail = 0;
+	priv->rxhdr = hdr_addr;
+
+	for (i = 0; i < RX_BUFFERS; i++) 
+	{
+    		priv->rxbuffer[i] = first_free_addr;
+		am_writeword (dev, hdr_addr, first_free_addr);
+		am_writeword (dev, hdr_addr + 2, RMD_OWN);
+		am_writeword (dev, hdr_addr + 4, (-1600));
+		am_writeword (dev, hdr_addr + 6, 0);
+		first_free_addr += 1600;
+		hdr_addr += 8;
+	}
+	priv->txhead = 0;
+	priv->txtail = 0;
+	priv->txhdr = hdr_addr;
+	for (i = 0; i < TX_BUFFERS; i++) 
+	{
+    		priv->txbuffer[i] = first_free_addr;
+		am_writeword (dev, hdr_addr, first_free_addr);
+		am_writeword (dev, hdr_addr + 2, 0);
+		am_writeword (dev, hdr_addr + 4, 0);
+		am_writeword (dev, hdr_addr + 6, 0);
+		first_free_addr += 1600;
+		hdr_addr += 8;
+	}
+
+	for (i = LADRL; i <= LADRH; i++)
+		write_rreg (dev->base_addr, i, 0);
+
+	for (i = PADRL, p = dev->dev_addr; i <= PADRH; i++, p += 2)
+		write_rreg (dev->base_addr, i, p[0] | (p[1] << 8));
+
+	i = MODE_PORT0;
+	if (dev->flags & IFF_PROMISC)
+		i |= MODE_PROMISC;
+
+	write_rreg (dev->base_addr, MODE, i);
+	write_rreg (dev->base_addr, BASERXL, priv->rxhdr);
+	write_rreg (dev->base_addr, BASERXH, 0);
+	write_rreg (dev->base_addr, BASETXL, priv->txhdr);
+	write_rreg (dev->base_addr, BASERXH, 0);
+	write_rreg (dev->base_addr, POLLINT, 0);	
+	write_rreg (dev->base_addr, SIZERXR, -RX_BUFFERS);
+	write_rreg (dev->base_addr, SIZETXR, -TX_BUFFERS);
+	write_rreg (dev->base_addr, CSR0, CSR0_STOP);
+	write_rreg (dev->base_addr, CSR3, CSR3_IDONM|CSR3_BABLM);
+	write_rreg (dev->base_addr, CSR0, CSR0_IENA|CSR0_STRT);
+}
+
+static int am79c961_init(struct device *dev)
+{
+	unsigned long flags;
+
+	am79c961_ramtest(dev, 0x66);
+	am79c961_ramtest(dev, 0x99);
+
+	save_flags_cli (flags);
+
+	write_ireg (dev->base_addr, 2, 0x4000); /* autoselect media */
+	write_rreg (dev->base_addr, CSR0, CSR0_STOP);
+	write_rreg (dev->base_addr, CSR3, CSR3_MASKALL);
+
+	restore_flags (flags);
+
+	return 0;
+}
+
+/*
+ * This is the real probe routine.
+ */
+
+static int am79c961_probe1(struct device *dev)
+{
+	static unsigned version_printed = 0;
+	struct dev_priv *priv;
+	int i;
+
+	if (!dev->priv) 
+	{
+		dev->priv = kmalloc (sizeof (struct dev_priv), GFP_KERNEL);
+		if (!dev->priv)
+			return -ENOMEM;
+	}
+
+	priv = (struct dev_priv *) dev->priv;
+	memset (priv, 0, sizeof(struct dev_priv));
+
+	/*
+	 * The PNP initialisation should have been done by the ether bootp loader.
+	 */
+	 
+	inb ((dev->base_addr + NET_RESET) >> 1);	/* reset the device */
+
+	udelay (5);
+
+	if (inb (dev->base_addr >> 1) != 0x08 ||
+		inb ((dev->base_addr >> 1) + 1) != 00 ||
+		inb ((dev->base_addr >> 1) + 2) != 0x2b) 
+	{
+		kfree (dev->priv);
+		dev->priv = NULL;
+		return -ENODEV;
+	 }
+
+	 /*
+	 * Ok, we've found a valid hw ID
+	 */
+
+	if (net_debug  &&  version_printed++ == 0)
+		printk (KERN_INFO "%s", version);
+
+	printk(KERN_INFO "%s: am79c961 found [%04lx, %d] ", dev->name, dev->base_addr, dev->irq);
+	request_region (dev->base_addr, 0x18, "am79c961");
+
+	/* Retrive and print the ethernet address. */
+	for (i = 0; i < 6; i++) 
+	{
+    		dev->dev_addr[i] = inb ((dev->base_addr >> 1) + i) & 0xff;
+		printk (i == 5 ? "%02x\n" : "%02x:", dev->dev_addr[i]);
+	}
+
+	if (am79c961_init(dev)) 
+	{
+		kfree (dev->priv);
+		dev->priv = NULL;
+		return -ENODEV;
+	}
+
+	dev->open = am79c961_open;
+	dev->stop = am79c961_close;
+	dev->hard_start_xmit = am79c961_sendpacket;
+	dev->get_stats = am79c961_getstats;
+	dev->set_multicast_list = am79c961_setmulticastlist;
+
+	/* Fill in the fields of the device structure with ethernet values. */
+	ether_setup(dev);
+	return 0;
+}
+
+int am79c961_probe(struct device *dev)
+{
+	static int initialised = 0;
+
+	if (initialised)
+		return -ENODEV;
+	initialised = 1;
+
+	dev->base_addr = 0x220;
+	dev->irq = 3;
+
+	return am79c961_probe1(dev);
+}
+
+/*
+ * Open/initialize the board.  This is called (in the current kernel)
+ * sometime after booting when the 'ifconfig' program is run.
+ *
+ * This routine should set everything up anew at each open, even
+ * registers that "should" only need to be set once at boot, so that
+ * there is non-reboot way to recover if something goes wrong.
+ */
+
+static int am79c961_open(struct device *dev)
+{
+    	struct dev_priv *priv = (struct dev_priv *)dev->priv;
+
+	MOD_INC_USE_COUNT;
+
+	memset (&priv->stats, 0, sizeof (priv->stats));
+
+	if (request_irq(dev->irq, am79c961_interrupt, 0, "am79c961", dev))
+		return -EAGAIN;
+
+	am79c961_init_for_open(dev);
+	dev->tbusy = 0;
+	dev->interrupt = 0;
+	dev->start = 1;
+	return 0;
+}
+
+/*
+ * The inverse routine to am79c961_open().
+ */
+ 
+static int am79c961_close(struct device *dev)
+{
+	dev->tbusy = 1;
+	dev->start = 0;
+
+	am79c961_init(dev);
+	free_irq (dev->irq, dev);
+
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+/*
+ * Get the current statistics.	This may be called with the card open or
+ * closed.
+ */
+
+static struct enet_statistics *am79c961_getstats (struct device *dev)
+{
+    	struct dev_priv *priv = (struct dev_priv *)dev->priv;
+	return &priv->stats;
+}
+
+/*
+ * Set or clear promiscuous/multicast mode filter for this adaptor.
+ *
+ * We don't attempt any packet filtering.  The card may have a SEEQ 8004
+ * in which does not have the other ethernet address registers present...
+ */
+
+static void am79c961_setmulticastlist (struct device *dev)
+{
+	unsigned long flags;
+	int i;
+
+	dev->flags &= ~IFF_ALLMULTI;
+
+	i = MODE_PORT0;
+	if (dev->flags & IFF_PROMISC)
+		i |= MODE_PROMISC;
+
+	save_flags_cli (flags);
+	write_rreg (dev->base_addr, MODE, i);
+	restore_flags (flags);
+}
+
+/*
+ *	Transmit a packet
+ */
+
+static int am79c961_sendpacket(struct sk_buff *skb, struct device *dev)
+{
+	struct dev_priv *priv = (struct dev_priv *)dev->priv;
+
+	if (!dev->tbusy) 
+	{
+again:
+		if (!test_and_set_bit(0, (void*)&dev->tbusy)) 
+		{
+			unsigned int length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
+			unsigned int hdraddr, bufaddr;
+			unsigned long flags;
+
+			hdraddr = priv->txhdr + (priv->txhead << 3);
+			bufaddr = priv->txbuffer[priv->txhead];
+			priv->txhead ++;
+			if (priv->txhead >= TX_BUFFERS)
+				priv->txhead = 0;
+
+			am_writebuffer (dev, bufaddr, skb->data, length);
+			am_writeword (dev, hdraddr + 4, -length);
+			am_writeword (dev, hdraddr + 2, TMD_OWN|TMD_STP|TMD_ENP);
+
+			save_flags_cli (flags);
+			write_rreg (dev->base_addr, CSR0, CSR0_TDMD|CSR0_IENA);
+			dev->trans_start = jiffies;
+			restore_flags (flags);
+
+			if (!(am_readword (dev, priv->txhdr + (priv->txhead << 3) + 2) & TMD_OWN))
+				dev->tbusy = 0;
+			dev_kfree_skb (skb, FREE_WRITE);
+			return 0;
+		}
+		else
+		{
+			printk(KERN_ERR "%s: Transmitter access conflict.\n", dev->name);
+			return 1;
+		}
+	}
+	else 
+	{
+		int tickssofar = jiffies - dev->trans_start;
+		if (tickssofar < 5)
+			return 1;
+		printk (KERN_WARNING "%s: transmit timed out, network cable problem?\n", dev->name);
+		/* Try to restart the adaptor. */
+		dev->tbusy = 0;
+		dev->trans_start = jiffies;
+		goto again;
+	}
+}
+
+static void am79c961_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct device *dev = (struct device *)dev_id;
+	struct dev_priv *priv = (struct dev_priv *)dev->priv;
+	unsigned int status;
+
+#if NET_DEBUG > 1
+	if(net_debug & DEBUG_INT)
+		printk(KERN_DEBUG "am79c961irq: %d ", irq);
+#endif
+
+	dev->interrupt = 1;
+	status = read_rreg (dev->base_addr, CSR0);
+	write_rreg (dev->base_addr, CSR0, status & (CSR0_TINT|CSR0_RINT|CSR0_MISS|CSR0_IENA));
+
+	if (status & CSR0_RINT) /* Got a packet(s). */
+		am79c961_rx (dev, priv);
+	if (status & CSR0_TINT) /* Packets transmitted */
+		am79c961_tx (dev, priv);
+	if (status & CSR0_MISS)
+		priv->stats.rx_dropped ++;
+
+	dev->interrupt = 0;
+
+#if NET_DEBUG > 1
+	if(net_debug & DEBUG_INT)
+		printk("done\n");
+#endif
+}
+
+/*
+ * If we have a good packet(s), get it/them out of the buffers.
+ */
+
+static void am79c961_rx(struct device *dev, struct dev_priv *priv)
+{
+	unsigned long hdraddr;
+	unsigned long pktaddr;
+
+	do 
+	{
+		unsigned long status;
+		struct sk_buff *skb;
+		int len;
+
+		hdraddr = priv->rxhdr + (priv->rxtail << 3);
+		pktaddr = priv->rxbuffer[priv->rxtail];
+
+		status = am_readword (dev, hdraddr + 2);
+		if (status & RMD_OWN) /* do we own it? */
+			break;
+
+		priv->rxtail ++;
+		if (priv->rxtail >= RX_BUFFERS)
+			priv->rxtail = 0;
+
+		if ((status & (RMD_ERR|RMD_STP|RMD_ENP)) != (RMD_STP|RMD_ENP)) 
+		{
+			am_writeword (dev, hdraddr + 2, RMD_OWN);
+			priv->stats.rx_errors ++;
+			if (status & RMD_ERR) 
+			{
+				if (status & RMD_FRAM)
+					priv->stats.rx_frame_errors ++;
+				if (status & RMD_CRC)
+					priv->stats.rx_crc_errors ++;
+			}
+			else if (status & RMD_STP)
+				priv->stats.rx_length_errors ++;
+			continue;
+		}
+
+		len = am_readword (dev, hdraddr + 6);
+		skb = dev_alloc_skb (len + 2);
+
+		if (skb) {
+			unsigned char *buf;
+
+			skb->dev = dev;
+			skb_reserve (skb, 2);
+			buf = skb_put (skb, len);
+
+			am_readbuffer (dev, pktaddr, buf, len);
+			am_writeword (dev, hdraddr + 2, RMD_OWN);
+			skb->protocol = eth_type_trans(skb, dev);
+			netif_rx (skb);
+			priv->stats.rx_packets ++;
+		} else {
+			am_writeword (dev, hdraddr + 2, RMD_OWN);
+			printk (KERN_WARNING "%s: memory squeeze, dropping packet.\n", dev->name);
+			priv->stats.rx_dropped ++;
+			break;
+		}
+	} while (1);
+}
+
+/*
+ * Update stats for the transmitted packet
+ */
+
+static void am79c961_tx(struct device *dev, struct dev_priv *priv)
+{
+	do {
+		unsigned long hdraddr;
+		unsigned long status;
+
+		hdraddr = priv->txhdr + (priv->txtail << 3);
+		status = am_readword (dev, hdraddr + 2);
+		if (status & TMD_OWN)
+			break;
+
+		priv->txtail ++;
+		if (priv->txtail >= TX_BUFFERS)
+			priv->txtail = 0;
+
+		if (status & TMD_ERR) {
+			unsigned long status2;
+
+			priv->stats.tx_errors ++;
+
+			status2 = am_readword (dev, hdraddr + 6);
+			am_writeword (dev, hdraddr + 6, 0);
+
+			if (status2 & TST_RTRY)
+				priv->stats.collisions += 1;
+			if (status2 & TST_LCOL)
+				priv->stats.tx_window_errors ++;
+			if (status2 & TST_LCAR)
+				priv->stats.tx_carrier_errors ++;
+			if (status2 & TST_UFLO)
+				priv->stats.tx_fifo_errors ++;
+			continue;
+		}
+		priv->stats.tx_packets ++;
+	} while (priv->txtail != priv->txhead);
+
+	dev->tbusy = 0;
+	mark_bh (NET_BH);
+}
+
diff -u --recursive --new-file v2.1.87/linux/drivers/net/am79c961a.h linux/drivers/net/am79c961a.h
--- v2.1.87/linux/drivers/net/am79c961a.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/am79c961a.h	Mon Feb 16 15:50:21 1998
@@ -0,0 +1,128 @@
+/*
+ * linux/drivers/net/am79c961.h
+ */
+
+#ifndef _LINUX_am79c961a_H
+#define _LINUX_am79c961a_H
+
+/* use 0 for production, 1 for verification, >2 for debug. debug flags: */
+#define DEBUG_TX	 2
+#define DEBUG_RX	 4
+#define DEBUG_INT	 8
+#define DEBUG_IC	16
+#ifndef NET_DEBUG
+#define NET_DEBUG 	0
+#endif
+
+#define NET_UID		0
+#define NET_RDP		0x10
+#define NET_RAP		0x12
+#define NET_RESET	0x14
+#define NET_IDP		0x16
+
+/*
+ * RAP registers
+ */
+#define CSR0		0
+#define CSR0_INIT	0x0001
+#define CSR0_STRT	0x0002
+#define CSR0_STOP	0x0004
+#define CSR0_TDMD	0x0008
+#define CSR0_TXON	0x0010
+#define CSR0_RXON	0x0020
+#define CSR0_IENA	0x0040
+#define CSR0_INTR	0x0080
+#define CSR0_IDON	0x0100
+#define CSR0_TINT	0x0200
+#define CSR0_RINT	0x0400
+#define CSR0_MERR	0x0800
+#define CSR0_MISS	0x1000
+#define CSR0_CERR	0x2000
+#define CSR0_BABL	0x4000
+#define CSR0_ERR	0x8000
+
+#define CSR3		3
+#define CSR3_EMBA	0x0008
+#define CSR3_DXMT2PD	0x0010
+#define CSR3_LAPPEN	0x0020
+#define CSR3_IDONM	0x0100
+#define CSR3_TINTM	0x0200
+#define CSR3_RINTM	0x0400
+#define CSR3_MERRM	0x0800
+#define CSR3_MISSM	0x1000
+#define CSR3_BABLM	0x4000
+#define CSR3_MASKALL	0x5F00
+
+#define LADRL		8
+#define LADRM1		9
+#define LADRM2		10
+#define LADRH		11
+#define PADRL		12
+#define PADRM		13
+#define PADRH		14
+
+#define MODE		15
+#define MODE_DISRX	0x0001
+#define MODE_DISTX	0x0002
+#define MODE_LOOP	0x0004
+#define MODE_DTCRC	0x0008
+#define MODE_COLL	0x0010
+#define MODE_DRETRY	0x0020
+#define MODE_INTLOOP	0x0040
+#define MODE_PORT0	0x0080
+#define MODE_DRXPA	0x2000
+#define MODE_DRXBA	0x4000
+#define MODE_PROMISC	0x8000
+
+#define BASERXL		24
+#define BASERXH		25
+#define BASETXL		30
+#define BASETXH		31
+
+#define POLLINT		47
+
+#define SIZERXR		76
+#define SIZETXR		78
+
+#define RMD_ENP		0x0100
+#define RMD_STP		0x0200
+#define RMD_CRC		0x0800
+#define RMD_FRAM	0x2000
+#define RMD_ERR		0x4000
+#define RMD_OWN		0x8000
+
+#define TMD_ENP		0x0100
+#define TMD_STP		0x0200
+#define TMD_MORE	0x1000
+#define TMD_ERR		0x4000
+#define TMD_OWN		0x8000
+
+#define TST_RTRY	0x0200
+#define TST_LCAR	0x0400
+#define TST_LCOL	0x1000
+#define TST_UFLO	0x4000
+
+struct dev_priv {
+    struct enet_statistics stats;
+    unsigned long	rxbuffer[RX_BUFFERS];
+    unsigned long	txbuffer[TX_BUFFERS];
+    unsigned char	txhead;
+    unsigned char	txtail;
+    unsigned char	rxhead;
+    unsigned char	rxtail;
+    unsigned long	rxhdr;
+    unsigned long	txhdr;
+};
+
+extern int	am79c961_probe (struct device *dev);
+static int	am79c961_probe1 (struct device *dev);
+static int	am79c961_open (struct device *dev);
+static int	am79c961_sendpacket (struct sk_buff *skb, struct device *dev);
+static void	am79c961_interrupt (int irq, void *dev_id, struct pt_regs *regs);
+static void	am79c961_rx (struct device *dev, struct dev_priv *priv);
+static void	am79c961_tx (struct device *dev, struct dev_priv *priv);
+static int	am79c961_close (struct device *dev);
+static struct enet_statistics *am79c961_getstats (struct device *dev);
+static void	am79c961_setmulticastlist (struct device *dev);
+
+#endif
diff -u --recursive --new-file v2.1.87/linux/drivers/net/arc-rimi.c linux/drivers/net/arc-rimi.c
--- v2.1.87/linux/drivers/net/arc-rimi.c	Thu Feb 12 20:56:07 1998
+++ linux/drivers/net/arc-rimi.c	Fri Feb 20 18:28:22 1998
@@ -26,7 +26,6 @@
 
 
 #include <linux/module.h>
-#include <linux/config.h>
 #include <linux/version.h>
 
 #include <linux/kernel.h>
diff -u --recursive --new-file v2.1.87/linux/drivers/net/atarilance.c linux/drivers/net/atarilance.c
--- v2.1.87/linux/drivers/net/atarilance.c	Thu Feb 12 20:56:07 1998
+++ linux/drivers/net/atarilance.c	Fri Feb 20 17:55:45 1998
@@ -819,6 +819,7 @@
 	head->flag = TMD1_OWN_CHIP | TMD1_ENP | TMD1_STP;
 	dev_kfree_skb( skb );
 	lp->cur_tx++;
+	lp->stats.tx_bytes += skb->len;
 	while( lp->cur_tx >= TX_RING_SIZE && lp->dirty_tx >= TX_RING_SIZE ) {
 		lp->cur_tx -= TX_RING_SIZE;
 		lp->dirty_tx -= TX_RING_SIZE;
@@ -1027,6 +1028,7 @@
 				skb->protocol = eth_type_trans( skb, dev );
 				netif_rx( skb );
 				lp->stats.rx_packets++;
+				lp->stats.rx_bytes += skb->len;
 			}
 		}
 
diff -u --recursive --new-file v2.1.87/linux/drivers/net/com20020.c linux/drivers/net/com20020.c
--- v2.1.87/linux/drivers/net/com20020.c	Thu Feb 12 20:56:07 1998
+++ linux/drivers/net/com20020.c	Fri Feb 20 18:28:22 1998
@@ -28,7 +28,6 @@
 
 
 #include <linux/module.h>
-#include <linux/config.h>
 #include <linux/version.h>
 
 #include <linux/kernel.h>
diff -u --recursive --new-file v2.1.87/linux/drivers/net/com90io.c linux/drivers/net/com90io.c
--- v2.1.87/linux/drivers/net/com90io.c	Thu Feb 12 20:56:07 1998
+++ linux/drivers/net/com90io.c	Fri Feb 20 18:28:22 1998
@@ -28,7 +28,6 @@
 
 
 #include <linux/module.h>
-#include <linux/config.h>
 #include <linux/version.h>
 
 #include <linux/kernel.h>
diff -u --recursive --new-file v2.1.87/linux/drivers/net/com90xx.c linux/drivers/net/com90xx.c
--- v2.1.87/linux/drivers/net/com90xx.c	Thu Feb 12 20:56:07 1998
+++ linux/drivers/net/com90xx.c	Fri Feb 20 18:28:22 1998
@@ -26,7 +26,6 @@
 
 
 #include <linux/module.h>
-#include <linux/config.h>
 #include <linux/version.h>
 
 #include <linux/kernel.h>
diff -u --recursive --new-file v2.1.87/linux/drivers/net/cops.c linux/drivers/net/cops.c
--- v2.1.87/linux/drivers/net/cops.c	Thu Feb 12 20:56:07 1998
+++ linux/drivers/net/cops.c	Fri Feb 20 18:28:22 1998
@@ -47,7 +47,6 @@
 #include <linux/version.h>
 #endif
 
-#include <linux/config.h>
 #include <linux/kernel.h>
 #include <linux/sched.h>
 #include <linux/types.h>
diff -u --recursive --new-file v2.1.87/linux/drivers/net/cs89x0.c linux/drivers/net/cs89x0.c
--- v2.1.87/linux/drivers/net/cs89x0.c	Thu Feb 12 20:56:07 1998
+++ linux/drivers/net/cs89x0.c	Fri Feb 20 18:28:22 1998
@@ -44,7 +44,6 @@
 
 /* Always include 'config.h' first in case the user wants to turn on
    or override something. */
-#include <linux/config.h>
 #ifdef MODULE
 #include <linux/module.h>
 #include <linux/version.h>
@@ -1078,11 +1077,11 @@
 
         if (dev_cs89x0.priv != NULL) {
                 /* Free up the private structure, or leak memory :-)  */
+                unregister_netdev(&dev_cs89x0);
                 kfree(dev_cs89x0.priv);
                 dev_cs89x0.priv = NULL;	/* gets re-allocated by cs89x0_probe1 */
                 /* If we don't do this, we can't re-insmod it later. */
                 release_region(dev_cs89x0.base_addr, NETCARD_IO_EXTENT);
-                unregister_netdev(&dev_cs89x0);
         }
 }
 #endif /* MODULE */
diff -u --recursive --new-file v2.1.87/linux/drivers/net/dlci.c linux/drivers/net/dlci.c
--- v2.1.87/linux/drivers/net/dlci.c	Thu Feb 12 20:56:08 1998
+++ linux/drivers/net/dlci.c	Fri Feb 20 18:28:22 1998
@@ -28,6 +28,7 @@
  *		2 of the License, or (at your option) any later version.
  */
 
+#include <linux/config.h> /* for CONFIG_DLCI_COUNT */
 #include <linux/module.h>
 
 #include <linux/kernel.h>
diff -u --recursive --new-file v2.1.87/linux/drivers/net/e2100.c linux/drivers/net/e2100.c
--- v2.1.87/linux/drivers/net/e2100.c	Sun Jan  4 10:55:08 1998
+++ linux/drivers/net/e2100.c	Thu Feb 19 14:58:40 1998
@@ -441,10 +441,10 @@
 		struct device *dev = &dev_e21[this_dev];
 		if (dev->priv != NULL) {
 			/* NB: e21_close() handles free_irq */
+			unregister_netdev(dev);
 			kfree(dev->priv);
 			dev->priv = NULL;
 			release_region(dev->base_addr, E21_IO_EXTENT);
-			unregister_netdev(dev);
 		}
 	}
 }
diff -u --recursive --new-file v2.1.87/linux/drivers/net/eepro100.c linux/drivers/net/eepro100.c
--- v2.1.87/linux/drivers/net/eepro100.c	Thu Feb 12 20:56:08 1998
+++ linux/drivers/net/eepro100.c	Fri Feb 20 18:28:22 1998
@@ -38,7 +38,6 @@
 /* Maximum events (Rx packets, etc.) to handle at each interrupt. */
 static int max_interrupt_work = 200;
 
-#include <linux/config.h>
 #ifdef MODULE
 #ifdef MODVERSIONS
 #include <linux/modversions.h>
diff -u --recursive --new-file v2.1.87/linux/drivers/net/eexpress.c linux/drivers/net/eexpress.c
--- v2.1.87/linux/drivers/net/eexpress.c	Thu Feb 12 20:56:08 1998
+++ linux/drivers/net/eexpress.c	Thu Feb 19 14:58:40 1998
@@ -1558,10 +1558,10 @@
 	for (this_dev = 0; this_dev < EEXP_MAX_CARDS; this_dev++) {
 		struct device *dev = &dev_eexp[this_dev];
 		if (dev->priv != NULL) {
+			unregister_netdev(dev);
 			kfree(dev->priv);
 			dev->priv = NULL;
 			release_region(dev->base_addr, EEXP_IO_EXTENT);
-			unregister_netdev(dev);
 		}
 	}
 }
diff -u --recursive --new-file v2.1.87/linux/drivers/net/es3210.c linux/drivers/net/es3210.c
--- v2.1.87/linux/drivers/net/es3210.c	Sun Jan  4 10:55:08 1998
+++ linux/drivers/net/es3210.c	Thu Feb 19 14:58:40 1998
@@ -430,11 +430,11 @@
 	for (this_dev = 0; this_dev < MAX_ES_CARDS; this_dev++) {
 		struct device *dev = &dev_es3210[this_dev];
 		if (dev->priv != NULL) {
+			unregister_netdev(dev);
 			kfree(dev->priv);
 			dev->priv = NULL;
 			free_irq(dev->irq, dev);
 			release_region(dev->base_addr, ES_IO_EXTENT);
-			unregister_netdev(dev);
 		}
 	}
 }
diff -u --recursive --new-file v2.1.87/linux/drivers/net/ewrk3.c linux/drivers/net/ewrk3.c
--- v2.1.87/linux/drivers/net/ewrk3.c	Thu Feb 12 20:56:08 1998
+++ linux/drivers/net/ewrk3.c	Thu Feb 19 14:58:40 1998
@@ -1904,13 +1904,13 @@
 
 void cleanup_module(void)
 {
+	unregister_netdev(&thisEthwrk);
 	if (thisEthwrk.priv) {
 		kfree(thisEthwrk.priv);
 		thisEthwrk.priv = NULL;
 	}
 	thisEthwrk.irq = 0;
 
-	unregister_netdev(&thisEthwrk);
 	release_region(thisEthwrk.base_addr, EWRK3_TOTAL_SIZE);
 }
 #endif				/* MODULE */
diff -u --recursive --new-file v2.1.87/linux/drivers/net/hp-plus.c linux/drivers/net/hp-plus.c
--- v2.1.87/linux/drivers/net/hp-plus.c	Sun Jan  4 10:55:08 1998
+++ linux/drivers/net/hp-plus.c	Thu Feb 19 14:58:40 1998
@@ -466,10 +466,10 @@
 		if (dev->priv != NULL) {
 			/* NB: hpp_close() handles free_irq */
 			int ioaddr = dev->base_addr - NIC_OFFSET;
+			unregister_netdev(dev);
 			kfree(dev->priv);
 			dev->priv = NULL;
 			release_region(ioaddr, HP_IO_EXTENT);
-			unregister_netdev(dev);
 		}
 	}
 }
diff -u --recursive --new-file v2.1.87/linux/drivers/net/hp.c linux/drivers/net/hp.c
--- v2.1.87/linux/drivers/net/hp.c	Thu Jan 15 14:33:06 1998
+++ linux/drivers/net/hp.c	Thu Feb 19 14:58:40 1998
@@ -433,11 +433,11 @@
 		struct device *dev = &dev_hp[this_dev];
 		if (dev->priv != NULL) {
 			int ioaddr = dev->base_addr - NIC_OFFSET;
+			unregister_netdev(dev);
 			kfree(dev->priv);
 			dev->priv = NULL;
 			free_irq(dev->irq, dev);
 			release_region(ioaddr, HP_IO_EXTENT);
-			unregister_netdev(dev);
 		}
 	}
 }
diff -u --recursive --new-file v2.1.87/linux/drivers/net/lapbether.c linux/drivers/net/lapbether.c
--- v2.1.87/linux/drivers/net/lapbether.c	Thu Feb 12 20:56:08 1998
+++ linux/drivers/net/lapbether.c	Fri Feb 20 18:28:22 1998
@@ -18,7 +18,6 @@
  *	LAPBETH 001	Jonathan Naylor		Cloned from bpqether.c
  */
 
-#include <linux/config.h>
 #include <linux/errno.h>
 #include <linux/types.h>
 #include <linux/socket.h>
diff -u --recursive --new-file v2.1.87/linux/drivers/net/ltpc.c linux/drivers/net/ltpc.c
--- v2.1.87/linux/drivers/net/ltpc.c	Thu Feb 12 20:56:08 1998
+++ linux/drivers/net/ltpc.c	Fri Feb 20 18:28:22 1998
@@ -180,7 +180,6 @@
 #define DEBUG_UPPER 2
 #define DEBUG_LOWER 4
 
-#include <linux/config.h> /* for CONFIG_MAX_16M */
 
 #ifdef MODULE
 #include <linux/module.h>
diff -u --recursive --new-file v2.1.87/linux/drivers/net/ne.c linux/drivers/net/ne.c
--- v2.1.87/linux/drivers/net/ne.c	Fri Jan 30 11:28:07 1998
+++ linux/drivers/net/ne.c	Thu Feb 19 14:58:40 1998
@@ -782,11 +782,11 @@
 	for (this_dev = 0; this_dev < MAX_NE_CARDS; this_dev++) {
 		struct device *dev = &dev_ne[this_dev];
 		if (dev->priv != NULL) {
+			unregister_netdev(dev);
 			kfree(dev->priv);
 			dev->priv = NULL;
 			free_irq(dev->irq, dev);
 			release_region(dev->base_addr, NE_IO_EXTENT);
-			unregister_netdev(dev);
 		}
 	}
 }
diff -u --recursive --new-file v2.1.87/linux/drivers/net/ni52.c linux/drivers/net/ni52.c
--- v2.1.87/linux/drivers/net/ni52.c	Thu Feb 12 20:56:09 1998
+++ linux/drivers/net/ni52.c	Thu Feb 19 14:58:40 1998
@@ -1338,9 +1338,9 @@
 void cleanup_module(void)
 {
 	release_region(dev_ni52.base_addr, NI52_TOTAL_SIZE);
+	unregister_netdev(&dev_ni52);
 	kfree(dev_ni52.priv);
 	dev_ni52.priv = NULL;
-	unregister_netdev(&dev_ni52);
 }
 #endif /* MODULE */
 
diff -u --recursive --new-file v2.1.87/linux/drivers/net/ni65.c linux/drivers/net/ni65.c
--- v2.1.87/linux/drivers/net/ni65.c	Thu Feb 12 20:56:09 1998
+++ linux/drivers/net/ni65.c	Thu Feb 19 14:58:40 1998
@@ -1202,10 +1202,10 @@
 	}
 	disable_dma(dev_ni65.dma);
 	free_dma(dev_ni65.dma);
+	unregister_netdev(&dev_ni65);
 	release_region(dev_ni65.base_addr,cards[p->cardno].total_size);
 	ni65_free_buffer(p);
 	dev_ni65.priv = NULL;
-	unregister_netdev(&dev_ni65);
 }
 #endif /* MODULE */
 
diff -u --recursive --new-file v2.1.87/linux/drivers/net/plip.c linux/drivers/net/plip.c
--- v2.1.87/linux/drivers/net/plip.c	Thu Feb 12 20:56:09 1998
+++ linux/drivers/net/plip.c	Fri Feb 20 18:28:22 1998
@@ -80,7 +80,6 @@
 */
 
 #include <linux/module.h>
-#include <linux/config.h>
 #include <linux/kernel.h>
 #include <linux/sched.h>
 #include <linux/types.h>
diff -u --recursive --new-file v2.1.87/linux/drivers/net/ppp.c linux/drivers/net/ppp.c
--- v2.1.87/linux/drivers/net/ppp.c	Thu Feb 12 20:56:09 1998
+++ linux/drivers/net/ppp.c	Fri Feb 20 18:28:22 1998
@@ -50,6 +50,7 @@
 
 /* $Id: ppp.c,v 1.14 1997/11/27 06:04:45 paulus Exp $ */
 
+#include <linux/config.h> /* for CONFIG_KERNELD */
 #include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/sched.h>
diff -u --recursive --new-file v2.1.87/linux/drivers/net/sdla.c linux/drivers/net/sdla.c
--- v2.1.87/linux/drivers/net/sdla.c	Sat Nov 29 10:33:20 1997
+++ linux/drivers/net/sdla.c	Fri Feb 20 18:28:22 1998
@@ -32,6 +32,7 @@
  *		2 of the License, or (at your option) any later version.
  */
 
+#include <linux/config.h> /* for CONFIG_DLCI_MAX */
 #include <linux/module.h>
 
 #include <linux/kernel.h>
diff -u --recursive --new-file v2.1.87/linux/drivers/net/sdla_ppp.c linux/drivers/net/sdla_ppp.c
--- v2.1.87/linux/drivers/net/sdla_ppp.c	Thu Feb 12 20:56:09 1998
+++ linux/drivers/net/sdla_ppp.c	Fri Feb 20 18:28:22 1998
@@ -59,6 +59,7 @@
 #error	This code MUST be compiled as a kernel module!
 #endif
 
+#include <linux/config.h>	/* CONFIG_SANGOMA_MANAGER */
 #include <linux/kernel.h>	/* printk(), and other useful stuff */
 #include <linux/stddef.h>	/* offsetof(), etc. */
 #include <linux/errno.h>	/* return codes */
diff -u --recursive --new-file v2.1.87/linux/drivers/net/sdladrv.c linux/drivers/net/sdladrv.c
--- v2.1.87/linux/drivers/net/sdladrv.c	Wed Apr 23 19:01:20 1997
+++ linux/drivers/net/sdladrv.c	Fri Feb 20 18:28:22 1998
@@ -81,7 +81,6 @@
 
 #if	defined(_LINUX_)	/****** Linux *******************************/
 
-#include <linux/config.h>	/* OS configuration options */
 #include <linux/kernel.h>	/* printk(), and other useful stuff */
 #include <linux/stddef.h>	/* offsetof(), etc. */
 #include <linux/errno.h>	/* return codes */
diff -u --recursive --new-file v2.1.87/linux/drivers/net/seeq8005.c linux/drivers/net/seeq8005.c
--- v2.1.87/linux/drivers/net/seeq8005.c	Thu Feb 12 20:56:09 1998
+++ linux/drivers/net/seeq8005.c	Thu Feb 19 14:58:40 1998
@@ -209,10 +209,7 @@
 #endif
 
 	outw( SEEQCFG2_RESET, SEEQ_CFG2);				/* reset the card */
-	SLOW_DOWN_IO;							/* have to wait 4us after a reset - should be fixed */
-	SLOW_DOWN_IO;
-	SLOW_DOWN_IO;
-	SLOW_DOWN_IO;
+	udelay(5);
 	outw( SEEQCMD_SET_ALL_OFF, SEEQ_CMD);
 	
 	if (net_debug) {
@@ -626,10 +623,7 @@
 	int i;
 	
 	outw(SEEQCFG2_RESET, SEEQ_CFG2);	/* reset device */
-	SLOW_DOWN_IO;				/* have to wait 4us after a reset - should be fixed */
-	SLOW_DOWN_IO;
-	SLOW_DOWN_IO;
-	SLOW_DOWN_IO;
+	udelay(5);
 	
 	outw( SEEQCMD_FIFO_WRITE | SEEQCMD_SET_ALL_OFF, SEEQ_CMD);
 	outw( 0, SEEQ_DMAAR);			/* load start address into both low and high byte */
@@ -638,7 +632,7 @@
 	
 	for(i=0;i<6;i++) {			/* set Station address */
 		outb(dev->dev_addr[i], SEEQ_BUFFER);
-		SLOW_DOWN_IO;
+		udelay(2);
 	}
 	
 	outw( SEEQCFG1_BUFFER_TEA, SEEQ_CFG1);	/* set xmit end area pointer to 16K */
diff -u --recursive --new-file v2.1.87/linux/drivers/net/slip.c linux/drivers/net/slip.c
--- v2.1.87/linux/drivers/net/slip.c	Thu Feb 12 20:56:09 1998
+++ linux/drivers/net/slip.c	Thu Feb 19 14:58:40 1998
@@ -969,11 +969,12 @@
 
 	switch(cmd) {
 	 case SIOCGIFNAME:
-		err = verify_area(VERIFY_WRITE, arg, strlen(sl->dev->name) + 1);
-		if (err)  {
-			return err;
-		}
-		copy_to_user(arg, sl->dev->name, strlen(sl->dev->name) + 1);
+		/* Please, do not put this line under copy_to_user,
+		   it breaks my old poor gcc on alpha --ANK
+		 */
+		tmp = strlen(sl->dev->name) + 1;
+		if (copy_to_user(arg, sl->dev->name, tmp) < 0)
+			return -EFAULT;
 		return 0;
 
 	case SIOCGIFENCAP:
diff -u --recursive --new-file v2.1.87/linux/drivers/net/smc-mca.c linux/drivers/net/smc-mca.c
--- v2.1.87/linux/drivers/net/smc-mca.c	Mon Nov  3 09:29:31 1997
+++ linux/drivers/net/smc-mca.c	Thu Feb 19 14:58:40 1998
@@ -367,10 +367,10 @@
         	{
 			/* NB: ultra_close_card() does free_irq */
 			int ioaddr = dev->base_addr - ULTRA_NIC_OFFSET;
+			unregister_netdev(dev);
 			kfree(dev->priv);
 			dev->priv = NULL;
 			release_region(ioaddr, ULTRA_IO_EXTENT);
-			unregister_netdev(dev);
 		}
 	}
 }
diff -u --recursive --new-file v2.1.87/linux/drivers/net/smc-ultra.c linux/drivers/net/smc-ultra.c
--- v2.1.87/linux/drivers/net/smc-ultra.c	Sun Jan  4 10:55:08 1998
+++ linux/drivers/net/smc-ultra.c	Thu Feb 19 14:58:40 1998
@@ -475,10 +475,10 @@
 		if (dev->priv != NULL) {
 			/* NB: ultra_close_card() does free_irq */
 			int ioaddr = dev->base_addr - ULTRA_NIC_OFFSET;
+			unregister_netdev(dev);
 			kfree(dev->priv);
 			dev->priv = NULL;
 			release_region(ioaddr, ULTRA_IO_EXTENT);
-			unregister_netdev(dev);
 		}
 	}
 }
diff -u --recursive --new-file v2.1.87/linux/drivers/net/wd.c linux/drivers/net/wd.c
--- v2.1.87/linux/drivers/net/wd.c	Sun Jan  4 10:55:09 1998
+++ linux/drivers/net/wd.c	Thu Feb 19 14:58:40 1998
@@ -497,11 +497,11 @@
 		struct device *dev = &dev_wd[this_dev];
 		if (dev->priv != NULL) {
 			int ioaddr = dev->base_addr - WD_NIC_OFFSET;
+			unregister_netdev(dev);
 			kfree(dev->priv);
 			dev->priv = NULL;
 			free_irq(dev->irq, dev);
 			release_region(ioaddr, WD_IO_EXTENT);
-			unregister_netdev(dev);
 		}
 	}
 }
diff -u --recursive --new-file v2.1.87/linux/drivers/net/x25_asy.c linux/drivers/net/x25_asy.c
--- v2.1.87/linux/drivers/net/x25_asy.c	Thu Feb 12 20:56:09 1998
+++ linux/drivers/net/x25_asy.c	Fri Feb 20 18:28:22 1998
@@ -11,7 +11,6 @@
  *	checksum routines from ppp.c
  */
 
-#include <linux/config.h>
 #include <linux/module.h>
 
 #include <asm/system.h>
diff -u --recursive --new-file v2.1.87/linux/drivers/pci/proc.c linux/drivers/pci/proc.c
--- v2.1.87/linux/drivers/pci/proc.c	Fri Jan 30 11:28:08 1998
+++ linux/drivers/pci/proc.c	Fri Feb 20 18:28:22 1998
@@ -6,7 +6,6 @@
  *	Copyright (c) 1997 Martin Mares <mj@atrey.karlin.mff.cuni.cz>
  */
 
-#include <linux/config.h>
 #include <linux/types.h>
 #include <linux/kernel.h>
 #include <linux/bios32.h>
diff -u --recursive --new-file v2.1.87/linux/drivers/sbus/audio/amd7930.c linux/drivers/sbus/audio/amd7930.c
--- v2.1.87/linux/drivers/sbus/audio/amd7930.c	Mon Jan 12 15:15:45 1998
+++ linux/drivers/sbus/audio/amd7930.c	Fri Feb 20 18:28:22 1998
@@ -15,7 +15,6 @@
  * databook which has all the programming information and gain tables.
  */
 
-#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/sched.h>
diff -u --recursive --new-file v2.1.87/linux/drivers/sbus/audio/cs4231.c linux/drivers/sbus/audio/cs4231.c
--- v2.1.87/linux/drivers/sbus/audio/cs4231.c	Mon Jan 12 15:15:45 1998
+++ linux/drivers/sbus/audio/cs4231.c	Fri Feb 20 18:28:22 1998
@@ -8,7 +8,6 @@
  * sun4m machines.
  */
 
-#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/sched.h>
diff -u --recursive --new-file v2.1.87/linux/drivers/sbus/char/mach64.c linux/drivers/sbus/char/mach64.c
--- v2.1.87/linux/drivers/sbus/char/mach64.c	Mon Jan 12 15:15:45 1998
+++ linux/drivers/sbus/char/mach64.c	Fri Feb 20 18:28:22 1998
@@ -8,6 +8,7 @@
  * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu)
  */
 
+#include <linux/config.h> /* for CONFIG_CHIP_ID */
 #include <linux/kernel.h>
 #include <linux/types.h>
 #include <linux/pci.h>
diff -u --recursive --new-file v2.1.87/linux/drivers/scsi/53c7xx.c linux/drivers/scsi/53c7xx.c
--- v2.1.87/linux/drivers/scsi/53c7xx.c	Sat May 24 09:10:24 1997
+++ linux/drivers/scsi/53c7xx.c	Fri Feb 20 18:28:22 1998
@@ -100,8 +100,6 @@
  *  the fourth byte from 50 to 25.
  */
 
-#include <linux/config.h>
-
 /*
  * Sponsored by 
  *	iX Multiuser Multitasking Magazine
@@ -233,7 +231,6 @@
 #endif
 
 #include <linux/config.h>
-
 #include <asm/dma.h>
 #include <asm/io.h>
 #include <asm/system.h>
diff -u --recursive --new-file v2.1.87/linux/drivers/scsi/gdth_proc.c linux/drivers/scsi/gdth_proc.c
--- v2.1.87/linux/drivers/scsi/gdth_proc.c	Tue Nov  4 09:17:30 1997
+++ linux/drivers/scsi/gdth_proc.c	Fri Feb 20 18:55:44 1998
@@ -620,12 +620,12 @@
     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;
+        if (timer_table[GDTH_TIMER].expires == 0) {
+            timer_table[GDTH_TIMER].expires = jiffies + timeout;
+            timer_active |= 1 << GDTH_TIMER;
         } else {
-            if (jiffies + timeout < timer_table[SCSI_TIMER].expires)
-                timer_table[SCSI_TIMER].expires = jiffies + timeout;
+            if (jiffies + timeout < timer_table[GDTH_TIMER].expires)
+                timer_table[GDTH_TIMER].expires = jiffies + timeout;
         }
     }
 
diff -u --recursive --new-file v2.1.87/linux/drivers/scsi/hosts.h linux/drivers/scsi/hosts.h
--- v2.1.87/linux/drivers/scsi/hosts.h	Tue Feb 17 13:12:47 1998
+++ linux/drivers/scsi/hosts.h	Fri Feb 20 19:13:25 1998
@@ -377,7 +377,7 @@
      * alignment to a 4-byte boundary.
      */
     unsigned long hostdata[0]  /* Used for storage of host specific stuff */
-        __attribute__ ((aligned (4)));
+        __attribute__ ((aligned (sizeof(unsigned long))));
 };
 
 extern struct Scsi_Host * scsi_hostlist;
diff -u --recursive --new-file v2.1.87/linux/drivers/scsi/ppa.h linux/drivers/scsi/ppa.h
--- v2.1.87/linux/drivers/scsi/ppa.h	Fri Feb  6 15:33:47 1998
+++ linux/drivers/scsi/ppa.h	Fri Feb 20 19:13:32 1998
@@ -15,6 +15,7 @@
 /* Use the following to enable certain chipset support
  * Default is PEDANTIC = 3
  */
+#include <linux/config.h> /* for CONFIG_SCSI_PPA_HAVE_PEDANTIC */
 #ifndef CONFIG_SCSI_PPA_HAVE_PEDANTIC
 #define CONFIG_SCSI_PPA_HAVE_PEDANTIC	3
 #endif
diff -u --recursive --new-file v2.1.87/linux/drivers/scsi/scsi.c linux/drivers/scsi/scsi.c
--- v2.1.87/linux/drivers/scsi/scsi.c	Fri Jan 23 18:10:32 1998
+++ linux/drivers/scsi/scsi.c	Fri Feb 20 18:58:26 1998
@@ -51,6 +51,7 @@
 #include <asm/system.h>
 #include <asm/irq.h>
 #include <asm/dma.h>
+#include <asm/spinlock.h>
 
 #include "scsi.h"
 #include "hosts.h"
@@ -122,6 +123,8 @@
                                                    12, 12, 10, 10 };
 static unsigned long      serial_number = 0;
 static Scsi_Cmnd        * scsi_bh_queue_head = NULL;
+static Scsi_Cmnd	* scsi_bh_queue_tail = NULL;
+static spinlock_t	  scsi_bh_queue_spin = SPIN_LOCK_UNLOCKED;
 static FreeSectorBitmap * dma_malloc_freelist = NULL;
 static int                need_isa_bounce_buffers;
 static unsigned int       dma_sectors = 0;
@@ -1523,7 +1526,6 @@
 scsi_done (Scsi_Cmnd * SCpnt)
 {
   unsigned long      flags;
-  Scsi_Cmnd        * SCswap;
 
   /*
    * We don't have to worry about this one timing out any more.
@@ -1551,35 +1553,20 @@
    * If it was NULL before, then everything is fine, and we are done
    * (this is the normal case).  If it was not NULL, then we block interrupts,
    * and link them together.
+   * We need a spinlock here, or compare and exchange if we can reorder incoming
+   * Scsi_Cmnds, as it happens pretty often scsi_done is called multiple times
+   * before bh is serviced. -jj
    */
-  
-  SCswap = (Scsi_Cmnd *) xchg(&scsi_bh_queue_head, SCpnt);
-  if( SCswap != NULL )
-  {
-      /*
-       * If we assume that the interrupt handler doesn't dawdle, then it is safe to
-       * say that we should come in here extremely rarely.  Under very heavy load,
-       * the requests might not be removed from the list fast enough so that we
-       * *do* end up stacking them, and that would be bad.
-       */
-      save_flags(flags);
-      cli();
-      
-      /*
-       * See if the pointer is NULL - it might have been serviced already
-       */
-      if( scsi_bh_queue_head == NULL )
-      {
-          scsi_bh_queue_head = SCswap;
-      }
-      else
-      {
-          SCswap->bh_next = scsi_bh_queue_head;
-          scsi_bh_queue_head = SCswap;
-      }
-      
-      restore_flags(flags);
+
+  spin_lock_irqsave(&scsi_bh_queue_spin, flags);
+  if (!scsi_bh_queue_head) {
+  	scsi_bh_queue_head = SCpnt;
+  	scsi_bh_queue_tail = SCpnt;
+  } else {
+  	scsi_bh_queue_tail->bh_next = SCpnt;
+  	scsi_bh_queue_tail = SCpnt;
   }
+  spin_unlock_irqrestore(&scsi_bh_queue_spin, flags);
 
   /*
    * Mark the bottom half handler to be run.
@@ -1603,7 +1590,7 @@
   Scsi_Cmnd        * SCpnt;
   Scsi_Cmnd        * SCnext;
   static atomic_t    recursion_depth;
-
+  unsigned long      flags;
   
   while(1==1)
   {
@@ -1622,15 +1609,17 @@
       }
       
       /*
-       * This is an atomic operation - swap the pointer with a NULL pointer
+       * We need to hold the spinlock, so that nobody is tampering with the queue. -jj
        * We will process everything we find in the list here.
        */
-      SCpnt = xchg(&scsi_bh_queue_head, NULL);
+      
+      spin_lock_irqsave(&scsi_bh_queue_spin, flags);
+      SCpnt = scsi_bh_queue_head;
+      scsi_bh_queue_head = NULL;
+      spin_unlock_irqrestore(&scsi_bh_queue_spin, flags);
       
       if( SCpnt == NULL )
-      {
           return;
-      }
       
       atomic_inc(&recursion_depth);
       
diff -u --recursive --new-file v2.1.87/linux/drivers/scsi/scsi.h linux/drivers/scsi/scsi.h
--- v2.1.87/linux/drivers/scsi/scsi.h	Tue Feb 17 13:12:47 1998
+++ linux/drivers/scsi/scsi.h	Fri Feb 20 19:13:25 1998
@@ -15,6 +15,8 @@
 #ifndef _SCSI_H
 #define _SCSI_H
 
+#include <linux/config.h> /* for CONFIG_SCSI_LOGGING */
+
 /*
  * Some of the public constants are being moved to this file.
  * We include it here so that what came from where is transparent.
@@ -685,7 +687,7 @@
 #define INIT_SCSI_REQUEST       \
     if (!CURRENT) {             \
 	CLEAR_INTR;             \
-	spin_unlock_irqrestore(&current_lock,flags);	\
+	spin_unlock_irqrestore(&io_request_lock,flags);	\
 	return;                 \
     }                           \
     if (MAJOR(CURRENT->rq_dev) != MAJOR_NR)           \
diff -u --recursive --new-file v2.1.87/linux/drivers/scsi/scsi_obsolete.c linux/drivers/scsi/scsi_obsolete.c
--- v2.1.87/linux/drivers/scsi/scsi_obsolete.c	Thu Jan  8 20:35:43 1998
+++ linux/drivers/scsi/scsi_obsolete.c	Fri Feb 20 18:28:22 1998
@@ -47,6 +47,7 @@
  * driver uses the new code this *ENTIRE* file will be nuked.
  */
 
+#include <linux/config.h> /* for CONFIG_KERNELD */
 #define __NO_VERSION__
 #include <linux/module.h>
 
diff -u --recursive --new-file v2.1.87/linux/drivers/scsi/scsi_proc.c linux/drivers/scsi/scsi_proc.c
--- v2.1.87/linux/drivers/scsi/scsi_proc.c	Sun Dec 21 17:04:49 1997
+++ linux/drivers/scsi/scsi_proc.c	Fri Feb 20 18:28:22 1998
@@ -16,6 +16,7 @@
  * Michael A. Griffith <grif@acm.org>
  */
 
+#include <linux/config.h> /* for CONFIG_PROC_FS */
 #define __NO_VERSION__
 #include <linux/module.h>
 
@@ -291,7 +292,7 @@
     *size = y; 
     return;
 }
-#endif /* CONFIG_SCSI_PROC */
+#endif /* CONFIG_PROC_FS */
 
 /*
  * Overrides for Emacs so that we get a uniform tabbing style.
diff -u --recursive --new-file v2.1.87/linux/drivers/scsi/sd.c linux/drivers/scsi/sd.c
--- v2.1.87/linux/drivers/scsi/sd.c	Tue Feb 17 13:12:47 1998
+++ linux/drivers/scsi/sd.c	Fri Feb 20 12:58:56 1998
@@ -517,10 +517,10 @@
     int flag = 0;
 
     while (1==1){
-	spin_lock_irqsave(&current_lock, flags);
+	spin_lock_irqsave(&io_request_lock, flags);
 
 	if (CURRENT != NULL && CURRENT->rq_status == RQ_INACTIVE) {
-            spin_unlock_irqrestore(&current_lock, flags);
+            spin_unlock_irqrestore(&io_request_lock, flags);
 	    return;
 	}
 
@@ -535,7 +535,7 @@
          */
         if( SDev->host->in_recovery )
           {
-            spin_unlock_irqrestore(&current_lock, flags);
+            spin_unlock_irqrestore(&io_request_lock, flags);
             return;
           }
 
@@ -555,7 +555,7 @@
 	     */
 	    if( SDev->removable && !in_interrupt() )
 	    {
-                spin_unlock(&current_lock);
+                spin_unlock_irqrestore(&io_request_lock, flags);
                 scsi_ioctl(SDev, SCSI_IOCTL_DOORLOCK, 0);
 		/* scsi_ioctl may allow CURRENT to change, so start over. */
 		SDev->was_reset = 0;
@@ -587,7 +587,7 @@
 	 * Using a "sti()" gets rid of the latency problems but causes
 	 * race conditions and crashes.
 	 */
-        spin_unlock_irqrestore(&current_lock, flags);
+        spin_unlock_irqrestore(&io_request_lock, flags);
 
 	/* This is a performance enhancement. We dig down into the request
 	 * list and try to find a queueable request (i.e. device not busy,
@@ -605,7 +605,7 @@
 	if (!SCpnt && sd_template.nr_dev > 1){
 	    struct request *req1;
 	    req1 = NULL;
-	    spin_lock_irqsave(&current_lock, flags);
+	    spin_lock_irqsave(&io_request_lock, flags);
 	    req = CURRENT;
 	    while(req){
 		SCpnt = scsi_request_queueable(req,
@@ -620,7 +620,7 @@
 		else
 		    req1->next = req->next;
 	    }
-            spin_unlock_irqrestore(&current_lock, flags);
+            spin_unlock_irqrestore(&io_request_lock, flags);
 	}
 
 	if (!SCpnt) return; /* Could not find anything to do */
diff -u --recursive --new-file v2.1.87/linux/drivers/scsi/sr.c linux/drivers/scsi/sr.c
--- v2.1.87/linux/drivers/scsi/sr.c	Tue Feb 17 13:12:47 1998
+++ linux/drivers/scsi/sr.c	Fri Feb 20 18:28:22 1998
@@ -32,7 +32,6 @@
 #include <linux/errno.h>
 #include <linux/cdrom.h>
 #include <linux/interrupt.h>
-#include <linux/config.h>
 #include <asm/system.h>
 #include <asm/io.h>
 
@@ -432,10 +431,10 @@
     int flag = 0;
 
     while (1==1){
-	save_flags(flags);
-	cli();
+    	spin_lock_irqsave(&io_request_lock, flags);
+
 	if (CURRENT != NULL && CURRENT->rq_status == RQ_INACTIVE) {
-	    restore_flags(flags);
+	    spin_unlock_irqrestore(&io_request_lock, flags);
 	    return;
 	};
 
@@ -451,6 +450,7 @@
          */
         if( SDev->host->in_recovery )
           {
+	    spin_unlock_irqrestore(&io_request_lock, flags);
             return;
           }
 
@@ -469,7 +469,11 @@
  	     */
  	    if( SDev->removable && !in_interrupt() )
  	    {
+		spin_unlock_irqrestore(&io_request_lock, flags);
 		scsi_ioctl(SDev, SCSI_IOCTL_DOORLOCK, 0);
+		/* scsi_ioctl may allow CURRENT to change, so start over. *
+		SDev->was_reset = 0;
+		continue;
  	    }
  	    SDev->was_reset = 0;
 	}
@@ -489,7 +493,7 @@
 	    SCpnt = scsi_allocate_device(&CURRENT,
 				    scsi_CDs[DEVICE_NR(CURRENT->rq_dev)].device, 0);
 	else SCpnt = NULL;
-	restore_flags(flags);
+	spin_unlock_irqrestore(&io_request_lock, flags);
 
 	/* This is a performance enhancement.  We dig down into the request list and
 	 * try to find a queueable request (i.e. device not busy, and host able to
@@ -501,8 +505,7 @@
 	if (!SCpnt && sr_template.nr_dev > 1){
 	    struct request *req1;
 	    req1 = NULL;
-	    save_flags(flags);
-	    cli();
+	    spin_lock_irqsave(&io_request_lock, flags);
 	    req = CURRENT;
 	    while(req){
 		SCpnt = scsi_request_queueable(req,
@@ -510,15 +513,15 @@
 		if(SCpnt) break;
 		req1 = req;
 		req = req->next;
-	    };
+	    }
 	    if (SCpnt && req->rq_status == RQ_INACTIVE) {
 		if (req == CURRENT)
 		    CURRENT = CURRENT->next;
 		else
 		    req1->next = req->next;
-	    };
-	    restore_flags(flags);
-	};
+	    }
+	    spin_unlock_irqrestore(&io_request_lock, flags);
+	}
 
 	if (!SCpnt)
 	    return; /* Could not find anything to do */
@@ -527,7 +530,7 @@
 
 	/* Queue command */
 	requeue_sr_request(SCpnt);
-    };  /* While */
+    }  /* While */
 }
 
 void requeue_sr_request (Scsi_Cmnd * SCpnt)
diff -u --recursive --new-file v2.1.87/linux/drivers/sgi/char/sgiserial.c linux/drivers/sgi/char/sgiserial.c
--- v2.1.87/linux/drivers/sgi/char/sgiserial.c	Wed Dec 10 10:31:11 1997
+++ linux/drivers/sgi/char/sgiserial.c	Fri Feb 20 18:28:22 1998
@@ -3,6 +3,7 @@
  * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com)
  */
 
+#include <linux/config.h> /* for CONFIG_REMOTE_DEBUG */
 #include <linux/errno.h>
 #include <linux/signal.h>
 #include <linux/sched.h>
diff -u --recursive --new-file v2.1.87/linux/drivers/sound/Defines linux/drivers/sound/Defines
--- v2.1.87/linux/drivers/sound/Defines	Tue Jan 13 21:12:57 1998
+++ linux/drivers/sound/Defines	Thu Feb 19 14:46:14 1998
@@ -129,18 +129,6 @@
 endif
 endif
 
-ifdef CONFIG_GUS
-ifneq ($(CONFIG_GUSHW),Y)
-CONFIG_GUSHW=y
-endif
-endif
-
-ifdef CONFIG_SSCAPE
-ifneq ($(CONFIG_SSCAPEHW),Y)
-CONFIG_SSCAPEHW=y
-endif
-endif
-
 ifdef CONFIG_PAS
 ifneq ($(CONFIG_SEQUENCER),Y)
 CONFIG_SEQUENCER=y
diff -u --recursive --new-file v2.1.87/linux/drivers/sound/ad1848.c linux/drivers/sound/ad1848.c
--- v2.1.87/linux/drivers/sound/ad1848.c	Tue Jan  6 19:32:46 1998
+++ linux/drivers/sound/ad1848.c	Thu Feb 19 14:46:14 1998
@@ -1716,7 +1716,7 @@
 	char dev_name[100];
 	int e;
 
-	ad1848_info    *devc = &adev_info[nr_ad1848_devs];
+	ad1848_info  *devc = &adev_info[nr_ad1848_devs];
 
 	ad1848_port_info *portc = NULL;
 
diff -u --recursive --new-file v2.1.87/linux/drivers/sound/audio.c linux/drivers/sound/audio.c
--- v2.1.87/linux/drivers/sound/audio.c	Fri Jan 30 11:28:08 1998
+++ linux/drivers/sound/audio.c	Thu Feb 19 14:46:14 1998
@@ -13,9 +13,15 @@
  */
 /*
  * Thomas Sailer   : ioctl code reworked (vmalloc/vfree removed)
+ * Thomas Sailer   : moved several static variables into struct audio_operations
+ *                   (which is grossly misnamed btw.) because they have the same
+ *                   lifetime as the rest in there and dynamic allocation saves
+ *                   12k or so
  */
 
 #include <linux/config.h>
+#include <linux/stddef.h>
+#include <linux/kerneld.h>
 
 #include "sound_config.h"
 
@@ -26,51 +32,40 @@
 #define NEUTRAL8	0x80
 #define NEUTRAL16	0x00
 
-static int      audio_mode[MAX_AUDIO_DEV];
-static int      dev_nblock[MAX_AUDIO_DEV];	/* 1 if in nonblocking mode */
 
-#define		AM_NONE		0
-#define		AM_WRITE	OPEN_WRITE
-#define 	AM_READ		OPEN_READ
 int             dma_ioctl(int dev, unsigned int cmd, caddr_t arg);
 
-
-static int      local_format[MAX_AUDIO_DEV], audio_format[MAX_AUDIO_DEV];
-static int      local_conversion[MAX_AUDIO_DEV];
-
-#define CNV_MU_LAW	0x00000001
-
 static int set_format(int dev, int fmt)
 {
 	if (fmt != AFMT_QUERY)
 	{
-		local_conversion[dev] = 0;
+		audio_devs[dev]->local_conversion = 0;
 
 		if (!(audio_devs[dev]->format_mask & fmt))	/* Not supported */
 		{
 			if (fmt == AFMT_MU_LAW)
 			{
 				fmt = AFMT_U8;
-				local_conversion[dev] = CNV_MU_LAW;
+				audio_devs[dev]->local_conversion = CNV_MU_LAW;
 			}
 			else
 				fmt = AFMT_U8;	/* This is always supported */
 		}
-		audio_format[dev] = audio_devs[dev]->d->set_bits(dev, fmt);
-		local_format[dev] = fmt;
+		audio_devs[dev]->audio_format = audio_devs[dev]->d->set_bits(dev, fmt);
+		audio_devs[dev]->local_format = fmt;
 	}
 	else
-		return local_format[dev];
+		return audio_devs[dev]->local_format;
 
-	return local_format[dev];
+	return audio_devs[dev]->local_format;
 }
 
-int audio_open(int dev, struct fileinfo *file)
+int audio_open(int dev, struct file *file)
 {
 	int ret;
 	int bits;
 	int dev_type = dev & 0x0f;
-	int mode = file->mode & O_ACCMODE;
+	int mode = translate_mode(file);
 
 	dev = dev >> 4;
 
@@ -96,15 +91,15 @@
 		}
 	}
 	
-	local_conversion[dev] = 0;
+	audio_devs[dev]->local_conversion = 0;
 
 	if (dev_type == SND_DEV_AUDIO)
-		  set_format(dev, AFMT_MU_LAW);
+		set_format(dev, AFMT_MU_LAW);
 	else 
 		set_format(dev, bits);
 
-	audio_mode[dev] = AM_NONE;
-	dev_nblock[dev] = 0;
+	audio_devs[dev]->audio_mode = AM_NONE;
+	audio_devs[dev]->dev_nblock = 0;
 
 
 	return ret;
@@ -154,12 +149,11 @@
 	dmap->flags |= DMA_DIRTY;
 }
 
-void audio_release(int dev, struct fileinfo *file)
+void audio_release(int dev, struct file *file)
 {
-	int             mode;
+	int             mode = translate_mode(file);
 
 	dev = dev >> 4;
-	mode = file->mode & O_ACCMODE;
 
 	audio_devs[dev]->dmap_out->closing = 1;
 	audio_devs[dev]->dmap_in->closing = 1;
@@ -202,7 +196,7 @@
 
 #endif
 
-int audio_write(int dev, struct fileinfo *file, const char *buf, int count)
+int audio_write(int dev, struct file *file, const char *buf, int count)
 {
 	int c, p, l, buf_size;
 	int err;
@@ -217,9 +211,9 @@
 		return -EPERM;
 
 	if (audio_devs[dev]->flags & DMA_DUPLEX)
-		audio_mode[dev] |= AM_WRITE;
+		audio_devs[dev]->audio_mode |= AM_WRITE;
 	else
-		audio_mode[dev] = AM_WRITE;
+		audio_devs[dev]->audio_mode = AM_WRITE;
 
 	if (!count)		/* Flush output */
 	{
@@ -229,10 +223,10 @@
 	
 	while (c)
 	{
-		if ((err = DMAbuf_getwrbuffer(dev, &dma_buf, &buf_size, dev_nblock[dev])) < 0)
+		if ((err = DMAbuf_getwrbuffer(dev, &dma_buf, &buf_size, audio_devs[dev]->dev_nblock)) < 0)
 		{
 			    /* Handle nonblocking mode */
-			if (dev_nblock[dev] && err == -EAGAIN)
+			if (audio_devs[dev]->dev_nblock && err == -EAGAIN)
 				return p;	/* No more space. Return # of accepted bytes */
 			return err;
 		}
@@ -259,7 +253,7 @@
 		} 
 		else audio_devs[dev]->d->copy_user(dev, dma_buf, 0, buf, p, l);
 
-		if (local_conversion[dev] & CNV_MU_LAW)
+		if (audio_devs[dev]->local_conversion & CNV_MU_LAW)
 		{
 			/*
 			 * This just allows interrupts while the conversion is running
@@ -276,7 +270,7 @@
 	return count;
 }
 
-int audio_read(int dev, struct fileinfo *file, char *buf, int count)
+int audio_read(int dev, struct file *file, char *buf, int count)
 {
 	int             c, p, l;
 	char           *dmabuf;
@@ -289,24 +283,24 @@
 	if (!(audio_devs[dev]->open_mode & OPEN_READ))
 		return -EPERM;
 
-	if ((audio_mode[dev] & AM_WRITE) && !(audio_devs[dev]->flags & DMA_DUPLEX))
+	if ((audio_devs[dev]->audio_mode & AM_WRITE) && !(audio_devs[dev]->flags & DMA_DUPLEX))
 		sync_output(dev);
 
 	if (audio_devs[dev]->flags & DMA_DUPLEX)
-		audio_mode[dev] |= AM_READ;
+		audio_devs[dev]->audio_mode |= AM_READ;
 	else
-		audio_mode[dev] = AM_READ;
+		audio_devs[dev]->audio_mode = AM_READ;
 
 	while(c)
 	{
 		if ((buf_no = DMAbuf_getrdbuffer(dev, &dmabuf, &l,
-			dev_nblock[dev])) < 0)
+			audio_devs[dev]->dev_nblock)) < 0)
 		{
 			/*
 			 *	Nonblocking mode handling. Return current # of bytes
 			 */
 
-			if (dev_nblock[dev] && buf_no == -EAGAIN)
+			if (audio_devs[dev]->dev_nblock && buf_no == -EAGAIN)
 				return p;
 
 			if (p > 0) 		/* Avoid throwing away data */
@@ -321,7 +315,7 @@
 		 * Insert any local processing here.
 		 */
 
-		if (local_conversion[dev] & CNV_MU_LAW)
+		if (audio_devs[dev]->local_conversion & CNV_MU_LAW)
 		{
 			/*
 			 * This just allows interrupts while the conversion is running
@@ -347,8 +341,7 @@
 	return count - c;
 }
 
-int audio_ioctl(int dev, struct fileinfo *file_must_not_be_used,
-		unsigned int cmd, caddr_t arg)
+int audio_ioctl(int dev, struct file *file, unsigned int cmd, caddr_t arg)
 {
 	int val, count;
 	unsigned long flags;
@@ -356,7 +349,7 @@
 
 	dev = dev >> 4;
 
-	if (((cmd >> 8) & 0xff) == 'C')	{
+	if (_IOC_TYPE(cmd) == 'C')	{
 		if (audio_devs[dev]->coproc)	/* Coprocessor ioctl */
 			return audio_devs[dev]->coproc->ioctl(audio_devs[dev]->coproc->devc, cmd, arg, 0);
 		/* else
@@ -386,7 +379,7 @@
 			return 0;
 
 		case SNDCTL_DSP_RESET:
-			audio_mode[dev] = AM_NONE;
+			audio_devs[dev]->audio_mode = AM_NONE;
 			DMAbuf_reset(dev);
 			return 0;
 
@@ -403,19 +396,19 @@
 		case SNDCTL_DSP_GETISPACE:
 			if (!(audio_devs[dev]->open_mode & OPEN_READ))
 				return 0;
-			if ((audio_mode[dev] & AM_WRITE) && !(audio_devs[dev]->flags & DMA_DUPLEX))
-				return -EBUSY;
+  			if ((audio_devs[dev]->audio_mode & AM_WRITE) && !(audio_devs[dev]->flags & DMA_DUPLEX))
+  				return -EBUSY;
 			return dma_ioctl(dev, cmd, arg);
 
 		case SNDCTL_DSP_GETOSPACE:
 			if (!(audio_devs[dev]->open_mode & OPEN_WRITE))
 				return -EPERM;
-			if ((audio_mode[dev] & AM_READ) && !(audio_devs[dev]->flags & DMA_DUPLEX))
-				return -EBUSY;
+  			if ((audio_devs[dev]->audio_mode & AM_READ) && !(audio_devs[dev]->flags & DMA_DUPLEX))
+  				return -EBUSY;
 			return dma_ioctl(dev, cmd, arg);
 		
 		case SNDCTL_DSP_NONBLOCK:
-			dev_nblock[dev] = 1;
+  			audio_devs[dev]->dev_nblock = 1;
 			return 0;
 
 		case SNDCTL_DSP_GETCAPS:
@@ -797,7 +790,9 @@
 				info.fragments = info.bytes / dmap->fragment_size;
 				info.bytes -= dmap->user_counter % dmap->fragment_size;
 			}
-			return copy_to_user(arg, &info, sizeof(info));
+			if (copy_to_user(arg, &info, sizeof(info)))
+				return -EFAULT;
+			return 0;
 
 		case SNDCTL_DSP_SETTRIGGER:
 			if (get_user(bits, (int *)arg))
@@ -863,7 +858,9 @@
 			if (dmap_in->mapping_flags & DMA_MAP_MAPPED)
 				dmap_in->qlen = 0;	/* Reset interrupt counter */
 			restore_flags(flags);
-			return copy_to_user(arg, &cinfo, sizeof(cinfo));
+			if (copy_to_user(arg, &cinfo, sizeof(cinfo)))
+				return -EFAULT;
+			return 0;
 
 		case SNDCTL_DSP_GETOPTR:
 			if (!(audio_devs[dev]->open_mode & OPEN_WRITE))
@@ -880,7 +877,9 @@
 			if (dmap_out->mapping_flags & DMA_MAP_MAPPED)
 				dmap_out->qlen = 0;	/* Reset interrupt counter */
 			restore_flags(flags);
-			return copy_to_user(arg, &cinfo, sizeof(cinfo));
+			if (copy_to_user(arg, &cinfo, sizeof(cinfo)))
+				return -EFAULT;
+			return 0;
 
 		case SNDCTL_DSP_GETODELAY:
 			if (!(audio_devs[dev]->open_mode & OPEN_WRITE))
diff -u --recursive --new-file v2.1.87/linux/drivers/sound/cs4232.c linux/drivers/sound/cs4232.c
--- v2.1.87/linux/drivers/sound/cs4232.c	Tue Jan 20 16:44:57 1998
+++ linux/drivers/sound/cs4232.c	Thu Feb 19 14:46:14 1998
@@ -67,17 +67,19 @@
 	0x09, 0x84, 0x42, 0xa1, 0xd0, 0x68, 0x34, 0x1a
 };
 
+static void sleep(unsigned howlong)
+{
+	current->state = TASK_INTERRUPTIBLE;
+	current->timeout = jiffies + howlong;
+	schedule();
+	current->timeout = 0;
+}
+
 int probe_cs4232(struct address_info *hw_config)
 {
 	int i, n;
 	int base = hw_config->io_base, irq = hw_config->irq;
 	int dma1 = hw_config->dma, dma2 = hw_config->dma2;
-	unsigned long tlimit;
-
-	static struct wait_queue *cs_sleeper = NULL;
-	static volatile struct snd_wait cs_sleep_flag = {
-		0
-	};
 
 	/*
 	 * Verify that the I/O port range is free.
@@ -104,9 +106,7 @@
 	 * first time.
 	 */
 
-	for (n = 0; n < 4; n++)
-	{
-		cs_sleep_flag.opts = WK_NONE;
+	for (n = 0; n < 4; n++) {
 		
 		/*
 		 *	Wake up the card by sending a 32 byte Crystal key to the key port.
@@ -115,15 +115,7 @@
 		for (i = 0; i < 32; i++)
 			CS_OUT(crystal_key[i]);
 
-		current->timeout = tlimit = jiffies + (HZ / 10);
-		cs_sleep_flag.opts = WK_SLEEP;
-		interruptible_sleep_on(&cs_sleeper);
-		if (!(cs_sleep_flag.opts & WK_WAKEUP))
-		{
-			if (jiffies >= tlimit)
-				cs_sleep_flag.opts |= WK_TIMEOUT;
-		}
-		cs_sleep_flag.opts &= ~WK_SLEEP;
+		sleep(HZ / 10);
 
 		/*
 		 *	Now set the CSN (Card Select Number).
@@ -154,15 +146,7 @@
 
 		CS_OUT2(0x33, 0x01);	/* Activate logical dev 0 */
 
-		current->timeout = tlimit = jiffies + (HZ / 10);
-		cs_sleep_flag.opts = WK_SLEEP;
-		interruptible_sleep_on(&cs_sleeper);
-		if (!(cs_sleep_flag.opts & WK_WAKEUP))
-		{
-			if (jiffies >= tlimit)
-				cs_sleep_flag.opts |= WK_TIMEOUT;
-		}
-		cs_sleep_flag.opts &= ~WK_SLEEP;
+		sleep(HZ / 10);
 
 		/*
 		 * Initialize logical device 3 (MPU)
@@ -184,15 +168,7 @@
 		
 		CS_OUT(0x79);
 
-		current->timeout = tlimit = jiffies + (HZ / 5);
-		cs_sleep_flag.opts = WK_SLEEP;
-		interruptible_sleep_on(&cs_sleeper);
-		if (!(cs_sleep_flag.opts & WK_WAKEUP))
-		{
-			if (jiffies >= tlimit)
-				cs_sleep_flag.opts |= WK_TIMEOUT;
-		}
-		cs_sleep_flag.opts &= ~WK_SLEEP;
+		sleep(HZ / 5);
 
 		/*
 		 * Then try to detect the codec part of the chip
@@ -200,16 +176,8 @@
 
 		if (ad1848_detect(hw_config->io_base, NULL, hw_config->osp))
 			return 1;
-
-		current->timeout = tlimit = jiffies + (HZ);
-		cs_sleep_flag.opts = WK_SLEEP;
-		interruptible_sleep_on(&cs_sleeper);
-		if (!(cs_sleep_flag.opts & WK_WAKEUP))
-		{
-			if (jiffies >= tlimit)
-				cs_sleep_flag.opts |= WK_TIMEOUT;
-		}
-		cs_sleep_flag.opts &= ~WK_SLEEP;
+		
+		sleep(HZ);
 	}
 	return 0;
 }
diff -u --recursive --new-file v2.1.87/linux/drivers/sound/dev_table.c linux/drivers/sound/dev_table.c
--- v2.1.87/linux/drivers/sound/dev_table.c	Fri Jan 23 18:10:32 1998
+++ linux/drivers/sound/dev_table.c	Thu Feb 19 14:46:14 1998
@@ -438,6 +438,8 @@
 		return -(ENOMEM);
 	}
 	memset((char *) op, 0, sizeof(struct audio_operations));
+	init_waitqueue(&op->in_sleeper);
+	init_waitqueue(&op->out_sleeper);	
 	if (driver_size < sizeof(struct audio_driver))
 		memset((char *) d, 0, sizeof(struct audio_driver));
 
diff -u --recursive --new-file v2.1.87/linux/drivers/sound/dev_table.h linux/drivers/sound/dev_table.h
--- v2.1.87/linux/drivers/sound/dev_table.h	Tue Feb 17 13:12:47 1998
+++ linux/drivers/sound/dev_table.h	Thu Feb 19 14:46:14 1998
@@ -221,6 +221,27 @@
 	int min_fragment;	/* 0 == unlimited */
 	int max_fragment;	/* 0 == unlimited */
 	int parent_dev;		/* 0 -> no parent, 1 to n -> parent=parent_dev+1 */
+
+	/* fields formerly in dmabuf.c */
+	struct wait_queue *in_sleeper;
+	struct wait_queue *out_sleeper;
+
+	/* fields formerly in audio.c */
+	int audio_mode;
+	/* why dont we use file->f_flags & O_NONBLOCK for the following? - ts */
+	int dev_nblock;	/* 1 if in nonblocking mode */
+
+#define		AM_NONE		0
+#define		AM_WRITE	OPEN_WRITE
+#define 	AM_READ		OPEN_READ
+
+	int local_format;
+	int audio_format;
+	int local_conversion;
+#define CNV_MU_LAW	0x00000001
+
+	/* large structures at the end to keep offsets small */
+	struct dma_buffparms dmaps[2];
 };
 
 int *load_mixer_volumes(char *name, int *levels, int present);
@@ -367,7 +388,7 @@
 #ifdef CONFIG_GUS16
 	{"GUS16", 0, SNDCARD_GUS16,	"Ultrasound 16-bit opt.",	attach_gus_db16, probe_gus_db16, unload_gus_db16},
 #endif
-#ifdef CONFIG_GUSHW
+#ifdef CONFIG_GUS
 	{"GUS", 0, SNDCARD_GUS,	"Gravis Ultrasound",	attach_gus_card, probe_gus, unload_gus},
 	{"GUSPNP", 1, SNDCARD_GUSPNP,	"GUS PnP",	attach_gus_card, probe_gus, unload_gus},
 #endif
@@ -418,7 +439,7 @@
 #endif
 #endif
 
-#ifdef CONFIG_SSCAPEHW
+#ifdef CONFIG_SSCAPE
 	{"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
@@ -633,15 +654,11 @@
 int sndtable_identify_card(char *name);
 void sound_setup (char *str, int *ints);
 
-int sound_alloc_dmap (int dev, struct dma_buffparms *dmap, int chan);
-void sound_free_dmap (int dev, struct dma_buffparms *dmap, int chn);
 extern int sound_map_buffer (int dev, struct dma_buffparms *dmap, buffmem_desc *info);
 int sndtable_probe (int unit, struct address_info *hw_config);
 int sndtable_init_card (int unit, struct address_info *hw_config);
 int sndtable_start_card (int unit, struct address_info *hw_config);
 void sound_timer_init (struct sound_lowlev_timer *t, char *name);
-int sound_start_dma(int dev, struct dma_buffparms *dmap, int chan,
-			unsigned long physaddr, int count, int dma_mode, int autoinit);
 void sound_dma_intr (int dev, struct dma_buffparms *dmap, int chan);
 
 #define AUDIO_DRIVER_VERSION	2
diff -u --recursive --new-file v2.1.87/linux/drivers/sound/dmabuf.c linux/drivers/sound/dmabuf.c
--- v2.1.87/linux/drivers/sound/dmabuf.c	Fri Jan 30 15:50:57 1998
+++ linux/drivers/sound/dmabuf.c	Thu Feb 19 14:46:14 1998
@@ -9,6 +9,15 @@
  * 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.
+ *
+ * Thomas Sailer   : moved several static variables into struct audio_operations
+ *                   (which is grossly misnamed btw.) because they have the same
+ *                   lifetime as the rest in there and dynamic allocation saves
+ *                   12k or so
+ * Thomas Sailer   : remove {in,out}_sleep_flag. It was used for the sleeper to
+ *                   determine if it was woken up by the expiring timeout or by
+ *                   an explicit wake_up. current->timeout can be used instead;
+ *                   if 0, the wakeup was due to the timeout.
  */
 #include <linux/config.h>
 
@@ -17,37 +26,132 @@
 
 #include "sound_config.h"
 
-#if defined(CONFIG_AUDIO) || defined(CONFIG_GUSHW)
+#if defined(CONFIG_AUDIO) || defined(CONFIG_GUS)
 
-static struct wait_queue *in_sleeper[MAX_AUDIO_DEV] = {
-	NULL
-};
 
-static volatile struct snd_wait in_sleep_flag[MAX_AUDIO_DEV] = {
-	{0}
-};
+static void dma_reset_output(int dev);
+static void dma_reset_input(int dev);
+static int local_start_dma(struct audio_operations *adev, unsigned long physaddr, int count, int dma_mode);
+
 
-static struct wait_queue *out_sleeper[MAX_AUDIO_DEV] = {
-	NULL
-};
 
-static volatile struct snd_wait out_sleep_flag[MAX_AUDIO_DEV] = {
-	{0}
-};
+static int debugmem = 0;	/* switched off by default */
+static int dma_buffsize = DSP_BUFFSIZE;
+
+static void dmabuf_set_timeout(struct dma_buffparms *dmap)
+{
+	unsigned long tmout;
 
-static int ndmaps = 0;
+	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;
+	current->timeout = jiffies + tmout;
+}
 
-#define MAX_DMAP (MAX_AUDIO_DEV*2)
+static int sound_alloc_dmap(struct dma_buffparms *dmap)
+{
+	char *start_addr, *end_addr;
+	int i, dma_pagesize;
+	int sz, size;
+
+	dmap->mapping_flags &= ~DMA_MAP_MAPPED;
+
+	if (dmap->raw_buf != NULL)
+		return 0;	/* Already done */
+	if (dma_buffsize < 4096)
+		dma_buffsize = 4096;
+	dma_pagesize = (dmap->dma < 4) ? (64 * 1024) : (128 * 1024);
+	dmap->raw_buf = NULL;
+	dmap->buffsize = dma_buffsize;
+	if (dmap->buffsize > dma_pagesize)
+		dmap->buffsize = dma_pagesize;
+	start_addr = NULL;
+	/*
+	 * Now loop until we get a free buffer. Try to get smaller buffer if
+	 * it fails. Don't accept smaller than 8k buffer for performance
+	 * reasons.
+	 */
+	while (start_addr == NULL && dmap->buffsize > PAGE_SIZE) {
+		for (sz = 0, size = PAGE_SIZE; size < dmap->buffsize; sz++, size <<= 1);
+		dmap->buffsize = PAGE_SIZE * (1 << sz);
+		start_addr = (char *) __get_free_pages(GFP_ATOMIC|GFP_DMA, sz);
+		if (start_addr == NULL)
+			dmap->buffsize /= 2;
+	}
+
+	if (start_addr == NULL) {
+		printk(KERN_WARNING "Sound error: Couldn't allocate DMA buffer\n");
+		return -ENOMEM;
+	} else {
+		/* make some checks */
+		end_addr = start_addr + dmap->buffsize - 1;
 
-static struct dma_buffparms dmaps[MAX_DMAP] = {
-	{0}
-};
+		if (debugmem)
+			printk(KERN_DEBUG "sound: start 0x%lx, end 0x%lx\n", (long) start_addr, (long) end_addr);
+		
+		/* now check if it fits into the same dma-pagesize */
 
-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);
+		if (((long) start_addr & ~(dma_pagesize - 1)) != ((long) end_addr & ~(dma_pagesize - 1))
+		    || end_addr >= (char *) (MAX_DMA_ADDRESS)) {
+			printk(KERN_ERR "sound: Got invalid address 0x%lx for %db DMA-buffer\n", (long) start_addr, dmap->buffsize);
+			return -EFAULT;
+		}
+	}
+	dmap->raw_buf = start_addr;
+	dmap->raw_buf_phys = virt_to_bus(start_addr);
+
+	for (i = MAP_NR(start_addr); i <= MAP_NR(end_addr); i++)
+		set_bit(PG_reserved, &mem_map[i].flags);;
+	return 0;
+}
+
+static void sound_free_dmap(struct dma_buffparms *dmap)
+{
+	int sz, size, i;
+	unsigned long start_addr, end_addr;
+
+	if (dmap->raw_buf == NULL)
+		return;
+	if (dmap->mapping_flags & DMA_MAP_MAPPED)
+		return;		/* Don't free mmapped buffer. Will use it next time */
+	for (sz = 0, size = PAGE_SIZE; size < dmap->buffsize; sz++, size <<= 1);
+
+	start_addr = (unsigned long) dmap->raw_buf;
+	end_addr = start_addr + dmap->buffsize;
+
+	for (i = MAP_NR(start_addr); i <= MAP_NR(end_addr); i++)
+		clear_bit(PG_reserved, &mem_map[i].flags);;
+
+	free_pages((unsigned long) dmap->raw_buf, sz);
+	dmap->raw_buf = NULL;
+}
+
+
+/* Intel version !!!!!!!!! */
 
-static void dma_init_buffers(int dev, struct dma_buffparms *dmap)
+static int sound_start_dma(struct dma_buffparms *dmap, unsigned long physaddr, int count, int dma_mode)
+{
+	unsigned long flags;
+	int chan = dmap->dma;
+
+	/* printk( "Start DMA%d %d, %d\n",  chan,  (int)(physaddr-dmap->raw_buf_phys),  count); */
+	save_flags(flags);
+	cli();
+	disable_dma(chan);
+	clear_dma_ff(chan);
+	set_dma_mode(chan, dma_mode);
+	set_dma_addr(chan, physaddr);
+	set_dma_count(chan, count);
+	enable_dma(chan);
+	restore_flags(flags);
+
+	return 0;
+}
+
+static void dma_init_buffers(struct dma_buffparms *dmap)
 {
 	dmap->qlen = dmap->qhead = dmap->qtail = dmap->user_counter = 0;
 	dmap->byte_counter = 0;
@@ -64,26 +168,24 @@
 	dmap->flags = DMA_BUSY;	/* Other flags off */
 }
 
-static int open_dmap(int dev, int mode, struct dma_buffparms *dmap, int chan)
+static int open_dmap(struct audio_operations *adev, int mode, struct dma_buffparms *dmap)
 {
 	int err;
 	
 	if (dmap->flags & DMA_BUSY)
 		return -EBUSY;
-	if ((err = sound_alloc_dmap(dev, dmap, chan)) < 0)
+	if ((err = sound_alloc_dmap(dmap)) < 0)
 		return err;
 
-	if (dmap->raw_buf == NULL)
-	{
+	if (dmap->raw_buf == NULL) {
 		printk(KERN_WARNING "Sound: DMA buffers not available\n");
 		return -ENOSPC;	/* Memory allocation failed during boot */
 	}
-	if (sound_open_dma(chan, audio_devs[dev]->name))
-	{
-		printk(KERN_WARNING "Unable to grab(2) DMA%d for the audio driver\n", chan);
+	if (sound_open_dma(dmap->dma, adev->name)) {
+		printk(KERN_WARNING "Unable to grab(2) DMA%d for the audio driver\n", dmap->dma);
 		return -EBUSY;
 	}
-	dma_init_buffers(dev, dmap);
+	dma_init_buffers(dmap);
 	dmap->open_mode = mode;
 	dmap->subdivision = dmap->underrun_count = 0;
 	dmap->fragment_size = 0;
@@ -94,19 +196,12 @@
 	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)
+static void close_dmap(struct audio_operations *adev, struct dma_buffparms *dmap)
 {
-	sound_close_dma(chan);
-
+	sound_close_dma(dmap->dma);
 	if (dmap->flags & DMA_BUSY)
 		dmap->dma_mode = DMODE_NONE;
 	dmap->flags &= ~DMA_BUSY;
@@ -157,74 +252,49 @@
 
 int DMAbuf_open(int dev, int mode)
 {
+	struct audio_operations *adev = audio_devs[dev];
 	int retval;
 	struct dma_buffparms *dmap_in = NULL;
 	struct dma_buffparms *dmap_out = NULL;
 
-	if (dev >= num_audiodevs || audio_devs[dev] == NULL)
-		return -ENXIO;
-
-	if (!audio_devs[dev])
+	if (!adev)
 		  return -ENXIO;
+	if (!(adev->flags & DMA_DUPLEX))
+		adev->dmap_in = adev->dmap_out;
+	check_driver(adev->d);
 
-	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)
+	if ((retval = adev->d->open(dev, mode)) < 0)
 		return retval;
-
-	dmap_out = audio_devs[dev]->dmap_out;
-	dmap_in = audio_devs[dev]->dmap_in;
-
+	dmap_out = adev->dmap_out;
+	dmap_in = adev->dmap_in;
 	if (dmap_in == dmap_out)
-		audio_devs[dev]->flags &= ~DMA_DUPLEX;
+		adev->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);
+	if (mode & OPEN_WRITE) {
+		if ((retval = open_dmap(adev, mode, dmap_out)) < 0) {
+			adev->d->close(dev);
 			return retval;
 		}
 	}
-	audio_devs[dev]->enable_bits = mode;
+	adev->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_READ || (mode != OPEN_WRITE && adev->flags & DMA_DUPLEX)) {
+		if ((retval = open_dmap(adev, mode, dmap_in)) < 0) {
+			adev->d->close(dev);
 			if (mode & OPEN_WRITE)
-			{
-				close_dmap(dev, dmap_out, audio_devs[dev]->dmap_out->dma);
-			}
+				close_dmap(adev, dmap_out);
 			return retval;
 		}
 	}
-	audio_devs[dev]->open_mode = mode;
-	audio_devs[dev]->go = 1;
-
-	if (mode & OPEN_READ)
-		in_sleep_flag[dev].opts = WK_NONE;
+	adev->open_mode = mode;
+	adev->go = 1;
 
-	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);
-	}
+	adev->d->set_bits(dev, 8);
+	adev->d->set_channels(dev, 1);
+	adev->d->set_speed(dev, DSP_DEFAULT_SPEED);
+	if (adev->dmap_out->dma_mode == DMODE_OUTPUT) 
+		memset(adev->dmap_out->raw_buf, adev->dmap_out->neutral_byte,
+		       adev->dmap_out->bytes_in_use);
 	return 0;
 }
 
@@ -239,9 +309,9 @@
 
 static void dma_reset_output(int dev)
 {
+	struct audio_operations *adev = audio_devs[dev];
 	unsigned long flags;
-	int tmout;
-	struct dma_buffparms *dmap = audio_devs[dev]->dmap_out;
+	struct dma_buffparms *dmap = adev->dmap_out;
 
 	if (!(dmap->flags & DMA_STARTED))	/* DMA is not active */
 		return;
@@ -251,162 +321,111 @@
 	 */
 	save_flags(flags);
 	cli();
+	adev->dmap_out->flags |= DMA_SYNCING;
 
-	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 (!signal_pending(current)
-	    && 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;
+	adev->dmap_out->underrun_count = 0;
+	if (!signal_pending(current) && adev->dmap_out->qlen && 
+	    adev->dmap_out->underrun_count == 0) {
+		dmabuf_set_timeout(dmap);
+		interruptible_sleep_on(&adev->out_sleeper);
+		current->timeout = 0;
 	}
-	audio_devs[dev]->dmap_out->flags &= ~(DMA_SYNCING | DMA_ACTIVE);
+	adev->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);
+	if (!(adev->flags & DMA_DUPLEX) || !adev->d->halt_output)
+		adev->d->halt_io(dev);
 	else
-		audio_devs[dev]->d->halt_output(dev);
-	audio_devs[dev]->dmap_out->flags &= ~DMA_STARTED;
-	restore_flags(flags);
-
+		adev->d->halt_output(dev);
+	adev->dmap_out->flags &= ~DMA_STARTED;
 	clear_dma_ff(dmap->dma);
 	disable_dma(dmap->dma);
+	restore_flags(flags);
 	dmap->byte_counter = 0;
-	reorganize_buffers(dev, audio_devs[dev]->dmap_out, 0);
+	reorganize_buffers(dev, adev->dmap_out, 0);
 	dmap->qlen = dmap->qhead = dmap->qtail = dmap->user_counter = 0;
 }
 
 static void dma_reset_input(int dev)
 {
+        struct audio_operations *adev = audio_devs[dev];
 	unsigned long flags;
-	struct dma_buffparms *dmap = audio_devs[dev]->dmap_in;
+	struct dma_buffparms *dmap = adev->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);
+	if (!(adev->flags & DMA_DUPLEX) || !adev->d->halt_input)
+		adev->d->halt_io(dev);
 	else
-		audio_devs[dev]->d->halt_input(dev);
-	audio_devs[dev]->dmap_in->flags &= ~DMA_STARTED;
+		adev->d->halt_input(dev);
+	adev->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);
+	reorganize_buffers(dev, adev->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))
+	struct audio_operations *adev = audio_devs[dev];
+
+	if (!((adev->enable_bits * adev->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))
-		{
+	if (!(dmap->flags & DMA_ACTIVE) || !(adev->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))
+			if (adev->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);
+				local_start_dma(adev, 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);
+		adev->d->output_block(dev, dmap->raw_buf_phys + dmap->qhead * dmap->fragment_size,
+				      dmap->counts[dmap->qhead], 1);
+		if (adev->d->trigger)
+			adev->d->trigger(dev,adev->enable_bits * adev->go);
 	}
 	dmap->flags |= DMA_ACTIVE;
 }
 
 int DMAbuf_sync(int dev)
 {
+	struct audio_operations *adev = audio_devs[dev];
 	unsigned long flags;
-	int tmout, n = 0;
+	int n = 0;
+	struct dma_buffparms *dmap;
 
-	if (!audio_devs[dev]->go && (!audio_devs[dev]->enable_bits & PCM_ENABLE_OUTPUT))
+	if (!adev->go && (!adev->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;
+	if (adev->dmap_out->dma_mode == DMODE_OUTPUT) {
+		dmap = adev->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 (!signal_pending(current) && 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;
+		if (dmap->qlen > 0 && !(dmap->flags & DMA_ACTIVE))
+			DMAbuf_launch_output(dev, dmap);
+		adev->dmap_out->flags |= DMA_SYNCING;
+		adev->dmap_out->underrun_count = 0;
+		while (!signal_pending(current) && n++ <= adev->dmap_out->nbufs && 
+		       adev->dmap_out->qlen && adev->dmap_out->underrun_count == 0) {
+			dmabuf_set_timeout(dmap);
+			interruptible_sleep_on(&adev->out_sleeper);
+			if (!current->timeout) {
+				adev->dmap_out->flags &= ~DMA_SYNCING;
 				restore_flags(flags);
-				return audio_devs[dev]->dmap_out->qlen;
+				return adev->dmap_out->qlen;
 			}
+			current->timeout = 0;
 		}
-		audio_devs[dev]->dmap_out->flags &= ~(DMA_SYNCING | DMA_ACTIVE);
+		adev->dmap_out->flags &= ~(DMA_SYNCING | DMA_ACTIVE);
 		restore_flags(flags);
 		
 		/*
@@ -416,195 +435,138 @@
 
 		save_flags(flags);
 		cli();
-		if (audio_devs[dev]->d->local_qlen)	/* Device has hidden buffers */
-		{
-			while (!signal_pending(current) && 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;
+		if (adev->d->local_qlen) {   /* Device has hidden buffers */
+			while (!signal_pending(current) && adev->d->local_qlen(dev)) {
+				dmabuf_set_timeout(dmap);
+				interruptible_sleep_on(&adev->out_sleeper);
+				current->timeout = 0;
 			}
 		}
 		restore_flags(flags);
 	}
-	audio_devs[dev]->dmap_out->dma_mode = DMODE_NONE;
-	return audio_devs[dev]->dmap_out->qlen;
+	adev->dmap_out->dma_mode = DMODE_NONE;
+	return adev->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;
+	struct audio_operations *adev = audio_devs[dev];
+	unsigned long flags;
 
-	if (audio_devs[dev]->open_mode & OPEN_WRITE)
-		if (!(audio_devs[dev]->dmap_in->mapping_flags & DMA_MAP_MAPPED))
-			if (!signal_pending(current)
-			    && (audio_devs[dev]->dmap_out->dma_mode == DMODE_OUTPUT))
-			{
+	if (adev->open_mode & OPEN_WRITE)
+		adev->dmap_out->closing = 1;
+	if (adev->open_mode & OPEN_READ)
+		adev->dmap_in->closing = 1;
+
+	if (adev->open_mode & OPEN_WRITE)
+		if (!(adev->dmap_in->mapping_flags & DMA_MAP_MAPPED))
+			if (!signal_pending(current) && (adev->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);
-	}
+	if (adev->dmap_out->dma_mode == DMODE_OUTPUT)
+		memset(adev->dmap_out->raw_buf, adev->dmap_out->neutral_byte, adev->dmap_out->bytes_in_use);
 	save_flags(flags);
 	cli();
 
 	DMAbuf_reset(dev);
-	audio_devs[dev]->d->close(dev);
+	adev->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 (adev->open_mode & OPEN_WRITE)
+		close_dmap(adev, adev->dmap_out);
 
-	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;
+	if (adev->open_mode == OPEN_READ ||
+	    (adev->open_mode != OPEN_WRITE &&
+	     adev->flags & DMA_DUPLEX))
+		close_dmap(adev, adev->dmap_in);
+	adev->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;
+	struct audio_operations *adev = audio_devs[dev];
+	int  err;
 
-	if (!(audio_devs[dev]->enable_bits & PCM_ENABLE_INPUT))
+	if (!(adev->open_mode & OPEN_READ))
 		return 0;
-
-	if (dmap->dma_mode == DMODE_OUTPUT)	/* Direction change */
-	{
+	if (!(adev->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;
-
+	if (!dmap->dma_mode) {
 		reorganize_buffers(dev, dmap, 1);
-		if ((err = audio_devs[dev]->d->prepare_for_input(dev,
+		if ((err = adev->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->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);
+		local_start_dma(adev, dmap->raw_buf_phys, dmap->bytes_in_use, DMA_MODE_READ);
+		adev->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);
+		if (adev->d->trigger)
+			adev->d->trigger(dev, adev->enable_bits * adev->go);
 	}
 	return 0;
 }
 
 int DMAbuf_getrdbuffer(int dev, char **buf, int *len, int dontblock)
 {
+	struct audio_operations *adev = audio_devs[dev];
 	unsigned long flags;
 	int err = 0, n = 0;
-	struct dma_buffparms *dmap = audio_devs[dev]->dmap_in;
+	struct dma_buffparms *dmap = adev->dmap_in;
+	int go;
 
-	if (!(audio_devs[dev]->open_mode & OPEN_READ))
+	if (!(adev->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)
-	{
+	if (adev->dmap_in->mapping_flags & DMA_MAP_MAPPED) {
 /*		  printk(KERN_WARNING "Sound: Can't read from mmapped device (1)\n");*/
 		  restore_flags(flags);
 		  return -EINVAL;
-	}
-	else while (dmap->qlen <= 0 && n++ < 10)
-	{
-		int tmout;
-		unsigned long tlimit;
-
-		if (!(audio_devs[dev]->enable_bits & PCM_ENABLE_INPUT) || !audio_devs[dev]->go)
-		{
+	} else while (dmap->qlen <= 0 && n++ < 10) {
+		if (!(adev->enable_bits & PCM_ENABLE_INPUT) || !adev->go) {
 			restore_flags(flags);
 			return -EAGAIN;
 		}
-		if ((err = DMAbuf_activate_recording(dev, dmap)) < 0)
-		{
+		if ((err = DMAbuf_activate_recording(dev, dmap)) < 0) {
 			restore_flags(flags);
 			return err;
 		}
 		/* Wait for the next block */
 
-		if (dontblock)
-		{
+		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;
-		}
-
-		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))
-		{
+		if (!(go = adev->go))
+			current->timeout = 0;
+		else 
+			dmabuf_set_timeout(dmap);
+		interruptible_sleep_on(&adev->in_sleeper);
+		if (go && !current->timeout) {
 			/* FIXME: include device name */
 			err = -EIO;
 			printk(KERN_WARNING "Sound: DMA (input) timed out - IRQ/DRQ config error?\n");
 			dma_reset_input(dev);
-		}
-		else
+		} else
 			err = -EINTR;
+		current->timeout = 0;
 	}
 	restore_flags(flags);
 
 	if (dmap->qlen <= 0)
-	{
-		if (err == 0)
-			err = -EINTR;
-		return err;
-	}
+		return err ? err : -EINTR;
 	*buf = &dmap->raw_buf[dmap->qhead * dmap->fragment_size + dmap->counts[dmap->qhead]];
 	*len = dmap->fragment_size - dmap->counts[dmap->qhead];
 
@@ -613,7 +575,8 @@
 
 int DMAbuf_rmchars(int dev, int buff_no, int c)
 {
-	struct dma_buffparms *dmap = audio_devs[dev]->dmap_in;
+	struct audio_operations *adev = audio_devs[dev];
+	struct dma_buffparms *dmap = adev->dmap_in;
 	int p = dmap->counts[dmap->qhead] + c;
 
 	if (dmap->mapping_flags & DMA_MAP_MAPPED)
@@ -623,8 +586,7 @@
 	}
 	else if (dmap->qlen <= 0)
 		return -EIO;
-	else if (p >= dmap->fragment_size)
-	{			/* This buffer is completely empty */
+	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;
@@ -648,24 +610,19 @@
 	cli();
 	if (!(dmap->flags & DMA_ACTIVE))
 		pos = 0;
-	else
-	{
+	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->mapping_flags & DMA_MAP_MAPPED)) {
+			if (direction == DMODE_OUTPUT) {
 				if (dmap->qhead == 0)
 					if (pos > dmap->fragment_size)
 						pos = 0;
-			}
-			else
-			{
+			} else {
 				if (dmap->qtail == 0)
 					if (pos > dmap->fragment_size)
 						pos = 0;
@@ -687,35 +644,33 @@
  *	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)
 {
+	struct audio_operations *adev;
 	int dev;
 
-	for (dev = 0; dev < num_audiodevs; dev++)
-		if ((devmask & (1 << dev)) && audio_devs[dev] != NULL)
-			DMAbuf_start_device(dev);
+	for (dev = 0; dev < num_audiodevs; dev++) {
+		if (!(devmask & (1 << dev)))
+			continue;
+		if (!(adev = audio_devs[dev]))
+			continue;
+		if (adev->open_mode == 0)
+			continue;
+		if (adev->go)
+			continue;
+		/* OK to start the device */
+		adev->go = 1;
+		if (adev->d->trigger)
+			adev->d->trigger(dev,adev->enable_bits * adev->go);
+	}
 }
 
 int DMAbuf_space_in_queue(int dev)
 {
+	struct audio_operations *adev = audio_devs[dev];
 	int len, max, tmp;
-	struct dma_buffparms *dmap = audio_devs[dev]->dmap_out;
+	struct dma_buffparms *dmap = adev->dmap_out;
 	int lim = dmap->nbufs;
 
 	if (lim < 2)
@@ -734,13 +689,10 @@
 		max = lim;
 	len = dmap->qlen;
 
-	if (audio_devs[dev]->d->local_qlen)
-	{
-		tmp = audio_devs[dev]->d->local_qlen(dev);
+	if (adev->d->local_qlen) {
+		tmp = adev->d->local_qlen(dev);
 		if (tmp && len)
-			tmp--;	/*
-				 * This buffer has been counted twice
-				 */
+			tmp--;	/* This buffer has been counted twice */
 		len += tmp;
 	}
 	if (dmap->byte_counter % dmap->fragment_size)	/* There is a partial fragment */
@@ -753,62 +705,42 @@
 
 static int output_sleep(int dev, int dontblock)
 {
-	int tmout;
+	struct audio_operations *adev = audio_devs[dev];
 	int err = 0;
-	struct dma_buffparms *dmap = audio_devs[dev]->dmap_out;
-	unsigned long   tlimit;
+	struct dma_buffparms *dmap = adev->dmap_out;
+	int timeout;
 
 	if (dontblock)
 		return -EAGAIN;
-	if (!(audio_devs[dev]->enable_bits & PCM_ENABLE_OUTPUT))
+	if (!(adev->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 (signal_pending(current))
 		return -EIO;
-
-	if (tmout)
-		current->timeout = tlimit = jiffies + (tmout);
+	timeout = (adev->go && !(dmap->flags & DMA_NOTIMEOUT));
+	if (timeout) 
+		dmabuf_set_timeout(dmap);
 	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))
-	{
+		current->timeout = 0;
+	interruptible_sleep_on(&adev->out_sleeper);
+	if (timeout && !current->timeout) {
 		printk(KERN_WARNING "Sound: DMA (output) timed out - IRQ/DRQ config error?\n");
 		dma_reset_output(dev);
+	} else {
+		current->timeout = 0;
+		if (signal_pending(current))
+			err = -EINTR;
 	}
-	else if (signal_pending(current))
-		err = -EINTR;
 	return err;
 }
 
 static int find_output_space(int dev, char **buf, int *size)
 {
-	struct dma_buffparms *dmap = audio_devs[dev]->dmap_out;
+	struct audio_operations *adev = audio_devs[dev];
+	struct dma_buffparms *dmap = adev->dmap_out;
 	unsigned long flags;
 	unsigned long active_offs;
 	long len, offs;
@@ -816,7 +748,6 @@
 	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);
@@ -833,8 +764,7 @@
 #endif
 
 	offs = (dmap->user_counter % dmap->bytes_in_use) & ~SAMPLE_ROUNDUP;
-	if (offs < 0 || offs >= dmap->bytes_in_use)
-	{
+	if (offs < 0 || offs >= dmap->bytes_in_use) {
 		printk(KERN_ERR "Sound: Got unexpected offs %ld. Giving up.\n", offs);
 		printk("Counter = %ld, bytes=%d\n", dmap->user_counter, dmap->bytes_in_use);
 		return 0;
@@ -845,53 +775,45 @@
 
 	if ((offs + len) > dmap->bytes_in_use)
 		len = dmap->bytes_in_use - offs;
-	if (len < 0)
-	{
+	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 & ~SAMPLE_ROUNDUP;
-
 	restore_flags(flags);
 	return (*size > 0);
 }
 
 int DMAbuf_getwrbuffer(int dev, char **buf, int *size, int dontblock)
 {
+	struct audio_operations *adev = audio_devs[dev];
 	unsigned long flags;
 	int err = -EIO;
-	struct dma_buffparms *dmap = audio_devs[dev]->dmap_out;
+	struct dma_buffparms *dmap = adev->dmap_out;
 
 	if (dmap->needs_reorg)
 		reorganize_buffers(dev, dmap, 0);
 
-	if (dmap->mapping_flags & DMA_MAP_MAPPED)
-	{
+	if (dmap->mapping_flags & DMA_MAP_MAPPED) {
 /*		printk(KERN_DEBUG "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;
+	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)
-		{
+	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;
@@ -899,7 +821,8 @@
 
 int DMAbuf_move_wrpointer(int dev, int l)
 {
-	struct dma_buffparms *dmap = audio_devs[dev]->dmap_out;
+	struct audio_operations *adev = audio_devs[dev];
+	struct dma_buffparms *dmap = adev->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);
@@ -909,10 +832,9 @@
 	dmap->user_counter += l;
 	dmap->flags |= DMA_DIRTY;
 
-	if (dmap->user_counter >= dmap->max_byte_counter)
-	{			/* Wrap the byte counters */
+	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;
@@ -923,8 +845,7 @@
 	dmap->neutral_byte = dmap->raw_buf[p];
 
 	/* Update the fragment based bookkeeping too */
-	while (ptr < end_ptr)
-	{
+	while (ptr < end_ptr) {
 		dmap->counts[dmap->qtail] = dmap->fragment_size;
 		dmap->qtail = (dmap->qtail + 1) % dmap->nbufs;
 		dmap->qlen++;
@@ -937,137 +858,85 @@
 	 *	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 (adev->d->postprocess_write)
+		adev->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);
-	}
+		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;
+	struct audio_operations *adev = audio_devs[dev];
+	struct dma_buffparms *dmap = (dma_mode == DMA_MODE_WRITE) ? adev->dmap_out : adev->dmap_in;
 
-	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)
-	{
+	if (dmap->raw_buf == NULL) {
 		printk(KERN_ERR "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 == adev->dmap_out) ? "out" : "in");
 		return 0;
 	}
-	if (chan < 0)
+	if (dmap->dma < 0)
 		return 0;
-
-	sound_start_dma(dev, dmap, chan, physaddr, count, dma_mode, 0);
-
+	sound_start_dma(dmap, physaddr, count, dma_mode);
 	return count;
 }
 
-static int local_start_dma(int dev, unsigned long physaddr, int count, int dma_mode)
+static int local_start_dma(struct audio_operations *adev, unsigned long physaddr, int count, int dma_mode)
 {
-	int chan;
-	struct dma_buffparms *dmap;
+	struct dma_buffparms *dmap = (dma_mode == DMA_MODE_WRITE) ? adev->dmap_out : adev->dmap_in;
 
-	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)
-	{
+	if (dmap->raw_buf == NULL) {
 		printk(KERN_ERR "sound: DMA buffer(2) == NULL\n");
-		printk(KERN_ERR "Device %d, chn=%s\n", dev, (dmap == audio_devs[dev]->dmap_out) ? "out" : "in");
+		printk(KERN_ERR "Device %s, chn=%s\n", adev->name, (dmap == adev->dmap_out) ? "out" : "in");
 		return 0;
 	}
 	if (dmap->flags & DMA_NODMA)
-	{
 		return 1;
-	}
-	if (chan < 0)
+	if (dmap->dma < 0)
 		return 0;
-
-	sound_start_dma(dev, dmap, chan, dmap->raw_buf_phys, dmap->bytes_in_use, dma_mode, 1);
+	sound_start_dma(dmap, dmap->raw_buf_phys, dmap->bytes_in_use, dma_mode | DMA_AUTOINIT);
 	dmap->flags |= DMA_STARTED;
-
 	return count;
 }
 
 static void finish_output_interrupt(int dev, struct dma_buffparms *dmap)
 {
-	unsigned long flags;
+	struct audio_operations *adev = audio_devs[dev];
 
 	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);
+	wake_up(&adev->out_sleeper);
 }
 
 static void do_outputintr(int dev, int dummy)
 {
+	struct audio_operations *adev = audio_devs[dev];
 	unsigned long flags;
-	struct dma_buffparms *dmap = audio_devs[dev]->dmap_out;
+	struct dma_buffparms *dmap = adev->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)
-	{
+	if (dmap->raw_buf == NULL) {
 		printk(KERN_ERR "Sound: Error. Audio interrupt (%d) after freeing buffers.\n", dev);
 		return;
 	}
-	if (dmap->mapping_flags & DMA_MAP_MAPPED)	/* Virtual memory mapped access */
-	{
+	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 */
-		{
+		if (dmap->qhead == 0) {	    /* Wrapped */
 			dmap->byte_counter += dmap->bytes_in_use;
-			if (dmap->byte_counter >= dmap->max_byte_counter)	/* Overflow */
-			{
+			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))
+		if (!(adev->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;
@@ -1079,53 +948,43 @@
 	this_fragment = dmap->qhead;
 	dmap->qhead = (dmap->qhead + 1) % dmap->nbufs;
 
-	if (dmap->qhead == 0)	/* Wrapped */
-	{
+	if (dmap->qhead == 0) {	/* Wrapped */
 		dmap->byte_counter += dmap->bytes_in_use;
-		if (dmap->byte_counter >= dmap->max_byte_counter)	/* Overflow */
-		{
+		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))
+	if (!(adev->flags & DMA_AUTOMODE))
 		dmap->flags &= ~DMA_ACTIVE;
-
-	while (dmap->qlen <= 0)
-	{
+	while (dmap->qlen <= 0) {
 		dmap->underrun_count++;
 		dmap->qlen++;
-		if (dmap->flags & DMA_DIRTY && dmap->applic_profile != APF_CPUINTENS)
-		{
+		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);
+			memset(adev->dmap_out->raw_buf, adev->dmap_out->neutral_byte,
+			       adev->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)
 {
+	struct audio_operations *adev = audio_devs[dev];
 	unsigned long flags;
-	struct dma_buffparms *dmap = audio_devs[dev]->dmap_out;
+	struct dma_buffparms *dmap = adev->dmap_out;
 
 	save_flags(flags);
 	cli();
-
-	if (!(dmap->flags & DMA_NODMA))
-	{
+	if (!(dmap->flags & DMA_NODMA)) {
 		int chan = dmap->dma, pos, n;
 		clear_dma_ff(chan);
 		disable_dma(dmap->dma);
@@ -1145,29 +1004,19 @@
 
 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
+	struct audio_operations *adev = audio_devs[dev];
+	struct dma_buffparms *dmap = adev->dmap_in;
 
-	if (dmap->raw_buf == NULL)
-	{
+	if (dmap->raw_buf == NULL) {
 		printk(KERN_ERR "Sound: Fatal error. Audio interrupt after freeing buffers.\n");
 		return;
 	}
-	if (dmap->mapping_flags & DMA_MAP_MAPPED)
-	{
+	if (dmap->mapping_flags & DMA_MAP_MAPPED) {
 		dmap->qtail = (dmap->qtail + 1) % dmap->nbufs;
-		if (dmap->qtail == 0)		/* Wrapped */
-		{
+		if (dmap->qtail == 0) {		/* Wrapped */
 			dmap->byte_counter += dmap->bytes_in_use;
-			if (dmap->byte_counter >= dmap->max_byte_counter)	/* Overflow */
-			{
+			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;
@@ -1175,79 +1024,58 @@
 		}
 		dmap->qlen++;
 
-		if (!(audio_devs[dev]->flags & DMA_AUTOMODE))
-		{
+		if (!(adev->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);
+			local_start_dma(adev, dmap->raw_buf_phys, dmap->bytes_in_use,DMA_MODE_READ);
+			adev->d->start_input(dev, dmap->raw_buf_phys + dmap->qtail * dmap->fragment_size,
+					     dmap->fragment_size, 1);
+			if (adev->d->trigger)
+				adev->d->trigger(dev, adev->enable_bits * adev->go);
 		}
 		dmap->flags |= DMA_ACTIVE;
-	}
-	else if (dmap->qlen >= (dmap->nbufs - 1))
-	{
+	} else if (dmap->qlen >= (dmap->nbufs - 1)) {
 		printk(KERN_WARNING "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)
-	{
+	} else if (dmap->qlen >= 0 && dmap->qlen < dmap->nbufs) {
 		dmap->qlen++;
 		dmap->qtail = (dmap->qtail + 1) % dmap->nbufs;
-		if (dmap->qtail == 0)		/* Wrapped */
-		{
+		if (dmap->qtail == 0) {		/* Wrapped */
 			dmap->byte_counter += dmap->bytes_in_use;
-			if (dmap->byte_counter >= dmap->max_byte_counter)	/* Overflow */
-			{
+			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);
+	if (!(adev->flags & DMA_AUTOMODE) || dmap->flags & DMA_NODMA) {
+		local_start_dma(adev, dmap->raw_buf_phys, dmap->bytes_in_use, DMA_MODE_READ);
+		adev->d->start_input(dev, dmap->raw_buf_phys + dmap->qtail * dmap->fragment_size, dmap->fragment_size, 1);
+		if (adev->d->trigger)
+			adev->d->trigger(dev,adev->enable_bits * adev->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);
+		wake_up(&adev->in_sleeper);
 }
 
 void DMAbuf_inputintr(int dev)
 {
-	struct dma_buffparms *dmap = audio_devs[dev]->dmap_in;
+	struct audio_operations *adev = audio_devs[dev];
+	struct dma_buffparms *dmap = adev->dmap_in;
 	unsigned long flags;
 
 	save_flags(flags);
 	cli();
 
-	if (!(dmap->flags & DMA_NODMA))
-	{
+	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);
@@ -1260,8 +1088,7 @@
 		n = 0;
 		while (dmap->qtail != pos && ++n < dmap->nbufs)
 			do_inputintr(dev);
-	}
-	else
+	} else
 		do_inputintr(dev);
 	restore_flags(flags);
 }
@@ -1271,25 +1098,22 @@
 	/*
 	 *    NOTE!  This routine opens only the primary DMA channel (output).
 	 */
-
-	int chan = audio_devs[dev]->dmap_out->dma;
+	struct audio_operations *adev = audio_devs[dev];
 	int err;
 
-	if ((err = open_dmap(dev, OPEN_READWRITE, audio_devs[dev]->dmap_out, chan)) < 0)
+	if ((err = open_dmap(adev, OPEN_READWRITE, adev->dmap_out)) < 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;
+	dma_init_buffers(adev->dmap_out);
+	adev->dmap_out->flags |= DMA_ALLOC_DONE;
+	adev->dmap_out->fragment_size = adev->dmap_out->buffsize;
 
-	if (chan >= 0)
-	{
+	if (adev->dmap_out->dma >= 0) {
 		unsigned long flags;
 
 		save_flags(flags);
 		cli();
-		disable_dma(audio_devs[dev]->dmap_out->dma);
-		clear_dma_ff(chan);
+		clear_dma_ff(adev->dmap_out->dma);
+		disable_dma(adev->dmap_out->dma);
 		restore_flags(flags);
 	}
 	return 0;
@@ -1297,147 +1121,107 @@
 
 void DMAbuf_close_dma(int dev)
 {
-	close_dmap(dev, audio_devs[dev]->dmap_out, audio_devs[dev]->dmap_out->dma);
+	close_dmap(audio_devs[dev], audio_devs[dev]->dmap_out);
 }
 
 void DMAbuf_init(int dev, int dma1, int dma2)
 {
+	struct audio_operations *adev = audio_devs[dev];
 	/*
 	 * NOTE! This routine could be called several times.
 	 */
 
-	if (audio_devs[dev] && audio_devs[dev]->dmap_out == NULL)
-	{
-		if (audio_devs[dev]->d == NULL)
+	if (adev && adev->dmap_out == NULL) {
+		if (adev->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;
+		if (adev->parent_dev) {	 /* Use DMA map of the parent dev */
+			int parent = adev->parent_dev - 1;
+			adev->dmap_out = audio_devs[parent]->dmap_out;
+			adev->dmap_in = audio_devs[parent]->dmap_in;
+		} else {
+			adev->dmap_out = adev->dmap_in = &adev->dmaps[0];
+			adev->dmap_out->dma = dma1;
+			if (adev->flags & DMA_DUPLEX) {
+				adev->dmap_in = &adev->dmaps[1];
+				adev->dmap_in->dma = dma2;
 			}
 		}
 	}
 }
 
-int DMAbuf_select(int dev, struct fileinfo *file, int sel_type, poll_table * wait)
+static unsigned int poll_input(int dev, poll_table *wait)
 {
-	struct dma_buffparms *dmap;
-	unsigned long   flags;
+	struct audio_operations *adev = audio_devs[dev];
+	struct dma_buffparms *dmap = adev->dmap_in;
 
-	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;
-
-		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;
+	if (!(adev->open_mode & OPEN_READ))
+		return 0;
+	if (dmap->mapping_flags & DMA_MAP_MAPPED) {
+		poll_wait(&adev->in_sleeper, wait);
+		if (dmap->qlen)
+			return POLLIN | POLLRDNORM;
+		return 0;
+	}
+	if (dmap->dma_mode != DMODE_INPUT) {
+		if (dmap->dma_mode == DMODE_NONE &&
+		    adev->enable_bits & PCM_ENABLE_INPUT &&
+		    !dmap->qlen && adev->go) {
+			unsigned long flags;
+			
+			poll_wait(&adev->in_sleeper, wait);
+			save_flags(flags);
+			cli();
+			DMAbuf_activate_recording(dev, dmap);
+			restore_flags(flags);
+		}
+		return 0;
+	}
+	poll_wait(&adev->in_sleeper, wait);
+	if (!dmap->qlen)
+		return 0;
+	return POLLIN | POLLRDNORM;
+}
 
-		case SEL_EX:
-			return 0;
+static unsigned int poll_output(int dev, poll_table *wait)
+{
+	struct audio_operations *adev = audio_devs[dev];
+	struct dma_buffparms *dmap = adev->dmap_out;
+	
+	if (!(adev->open_mode & OPEN_WRITE))
+		return 0;
+	if (dmap->mapping_flags & DMA_MAP_MAPPED) {
+		poll_wait(&adev->out_sleeper, wait);
+		if (dmap->qlen)
+			return POLLOUT | POLLWRNORM;
+		return 0;
 	}
+	if (dmap->dma_mode == DMODE_INPUT)
+		return 0;
+	poll_wait(&adev->out_sleeper, wait);
+	if (dmap->dma_mode == DMODE_NONE)
+		return POLLOUT | POLLWRNORM;
+	if (!DMAbuf_space_in_queue(dev))
+		return 0;
+	return POLLOUT | POLLWRNORM;
+}
 
-	return 0;
+unsigned int DMAbuf_poll(int dev, poll_table *wait)
+{
+	return poll_input(dev, wait) | poll_output(dev, wait);
 }
 
 void DMAbuf_deinit(int dev)
 {
-/* This routine is called when driver is being unloaded */
+	struct audio_operations *adev = audio_devs[dev];
+	/* This routine is called when driver is being unloaded */
+	if (!adev)
+		return;
 #ifdef RUNTIME_DMA_ALLOC
-	if (audio_devs[dev])
-		sound_free_dmap (dev, audio_devs[dev]->dmap_out, 
-				 audio_devs[dev]->dmap_out->dma);
-
-	if (audio_devs[dev] && audio_devs[dev]->flags & DMA_DUPLEX)
-		sound_free_dmap (dev, audio_devs[dev]->dmap_in, 
-				 audio_devs[dev]->dmap_in->dma);
+	sound_free_dmap(adev->dmap_out);
+
+	if (adev->flags & DMA_DUPLEX)
+		sound_free_dmap(adev->dmap_in);
 #endif
 }
 
diff -u --recursive --new-file v2.1.87/linux/drivers/sound/gus_card.c linux/drivers/sound/gus_card.c
--- v2.1.87/linux/drivers/sound/gus_card.c	Fri Jan 23 18:10:32 1998
+++ linux/drivers/sound/gus_card.c	Thu Feb 19 14:46:14 1998
@@ -16,7 +16,7 @@
 #include "sound_config.h"
 #include "soundmodule.h"
 
-#if defined(CONFIG_GUSHW) || defined(MODULE)
+#if defined(CONFIG_GUS) || defined(MODULE)
 
 #include "gus_hw.h"
 
@@ -167,7 +167,7 @@
 
 void attach_gus_db16(struct address_info *hw_config)
 {
-#if defined(CONFIG_GUSHW) || defined(MODULE)
+#if defined(CONFIG_GUS) || defined(MODULE)
 	gus_pcm_volume = 100;
 	gus_wave_volume = 90;
 #endif
diff -u --recursive --new-file v2.1.87/linux/drivers/sound/gus_midi.c linux/drivers/sound/gus_midi.c
--- v2.1.87/linux/drivers/sound/gus_midi.c	Fri Jan 23 18:10:32 1998
+++ linux/drivers/sound/gus_midi.c	Thu Feb 19 14:46:14 1998
@@ -17,7 +17,7 @@
 
 #include "gus_hw.h"
 
-#if ( defined(CONFIG_GUSHW) && defined(CONFIG_MIDI) ) || defined (MODULE)
+#if ( defined(CONFIG_GUS) && defined(CONFIG_MIDI) ) || defined (MODULE)
 
 static int      midi_busy = 0, input_opened = 0;
 static int      my_dev;
diff -u --recursive --new-file v2.1.87/linux/drivers/sound/gus_vol.c linux/drivers/sound/gus_vol.c
--- v2.1.87/linux/drivers/sound/gus_vol.c	Fri Jan 23 18:10:32 1998
+++ linux/drivers/sound/gus_vol.c	Thu Feb 19 14:46:14 1998
@@ -12,7 +12,7 @@
 #include <linux/config.h>
 
 #include "sound_config.h"
-#if defined(CONFIG_GUSHW) || defined(MODULE)
+#if defined(CONFIG_GUS) || defined(MODULE)
 #include "gus_linearvol.h"
 
 #define GUS_VOLUME	gus_wave_volume
diff -u --recursive --new-file v2.1.87/linux/drivers/sound/gus_wave.c linux/drivers/sound/gus_wave.c
--- v2.1.87/linux/drivers/sound/gus_wave.c	Fri Jan 23 18:10:32 1998
+++ linux/drivers/sound/gus_wave.c	Thu Feb 19 14:46:14 1998
@@ -22,7 +22,7 @@
 #include <linux/ultrasound.h>
 #include "gus_hw.h"
 
-#if defined(CONFIG_GUSHW) || defined(MODULE)
+#if defined(CONFIG_GUS) || defined(MODULE)
 
 #define GUS_BANK_SIZE (((iw_mode) ? 256*1024*1024 : 256*1024))
 
@@ -115,9 +115,6 @@
 static char     bounce_buf[8 * 1024];	/* Must match value set to max_fragment */
 
 static struct wait_queue *dram_sleeper = NULL;
-static volatile struct snd_wait dram_sleep_flag = {
-	0
-};
 
 /*
  * Variables and buffers for PCM output
@@ -1102,7 +1099,9 @@
 	{
 		case SNDCTL_SYNTH_INFO:
 			gus_info.nr_voices = nr_voices;
-			return copy_to_user(arg, &gus_info, sizeof(gus_info));
+			if (copy_to_user(arg, &gus_info, sizeof(gus_info)))
+				return -EFAULT;
+			return 0;
 
 		case SNDCTL_SEQ_RESETSAMPLES:
 			reset_sample_memory();
@@ -1658,7 +1657,7 @@
 	else
 		gus_no_dma = 0;
 
-	dram_sleep_flag.opts = WK_NONE;
+	init_waitqueue(&dram_sleeper);
 	gus_busy = 1;
 	active_device = GUS_DEV_WAVE;
 
@@ -1848,7 +1847,6 @@
 			unsigned long address, hold_address;
 			unsigned char dma_command;
 			unsigned long flags;
-			unsigned long tlimit;
 
 			if (audio_devs[gus_devnum]->dmap_out->raw_buf == NULL)
 			{
@@ -1921,17 +1919,11 @@
 			 */
 			active_device = GUS_DEV_WAVE;
 
-			current->timeout = tlimit = jiffies + HZ;
-			dram_sleep_flag.opts = WK_SLEEP;
+			current->timeout = jiffies + HZ;
 			interruptible_sleep_on(&dram_sleeper);
-			if (!(dram_sleep_flag.opts & WK_WAKEUP))
-			{
-				if (jiffies >= tlimit)
-					dram_sleep_flag.opts |= WK_TIMEOUT;
-			}
-			dram_sleep_flag.opts &= ~WK_SLEEP;
-			if ((dram_sleep_flag.opts & WK_TIMEOUT))
-				printk(KERN_WARNING "GUS: DMA Transfer timed out\n");
+			if (!current->timeout)
+				printk("GUS: DMA Transfer timed out\n");
+			current->timeout = 0;
 			restore_flags(flags);
 		}
 
@@ -3405,11 +3397,7 @@
 		switch (active_device)
 		{
 			case GUS_DEV_WAVE:
-				if ((dram_sleep_flag.opts & WK_SLEEP))
-				{
-					dram_sleep_flag.opts = WK_WAKEUP;
-					wake_up(&dram_sleeper);
-				}
+				wake_up(&dram_sleeper);
 				break;
 
 			case GUS_DEV_PCM_CONTINUE:	/* Left channel data transferred */
diff -u --recursive --new-file v2.1.87/linux/drivers/sound/ics2101.c linux/drivers/sound/ics2101.c
--- v2.1.87/linux/drivers/sound/ics2101.c	Mon Jan  5 09:39:52 1998
+++ linux/drivers/sound/ics2101.c	Thu Feb 19 14:46:14 1998
@@ -17,7 +17,7 @@
 
 
 #include "sound_config.h"
-#if defined(CONFIG_GUSHW) || defined(MODULE)
+#if defined(CONFIG_GUS) || defined(MODULE)
 
 #include <linux/ultrasound.h>
 #include "gus_hw.h"
diff -u --recursive --new-file v2.1.87/linux/drivers/sound/lowlevel/aci.c linux/drivers/sound/lowlevel/aci.c
--- v2.1.87/linux/drivers/sound/lowlevel/aci.c	Tue Oct 28 02:10:49 1997
+++ linux/drivers/sound/lowlevel/aci.c	Fri Feb 20 18:28:22 1998
@@ -58,6 +58,7 @@
  *
  */
 
+#include <linux/config.h> /* for CONFIG_ACI_MIXER */
 #include "lowlevel.h"
 #include "../sound_config.h"
 #include "lowlevel.h"
diff -u --recursive --new-file v2.1.87/linux/drivers/sound/maui.c linux/drivers/sound/maui.c
--- v2.1.87/linux/drivers/sound/maui.c	Tue Feb 17 13:12:47 1998
+++ linux/drivers/sound/maui.c	Thu Feb 19 14:46:14 1998
@@ -48,202 +48,178 @@
 static int maui_osLen = 0;
 #endif
 
-static struct wait_queue *maui_sleeper = NULL;
-static volatile struct snd_wait maui_sleep_flag =
-{0};
-
-static int
-maui_wait(int mask)
+static int maui_wait(int mask)
 {
-	int             i;
+	int i;
 
-/*
- * Perform a short initial wait without sleeping
- */
+	/*
+	 * Perform a short initial wait without sleeping
+	 */
 
 	for (i = 0; i < 100; i++)
-	  {
-		  if (inb(HOST_STAT_PORT) & mask)
-		    {
-			    return 1;
-		    }
-	  }
+	{
+		if (inb(HOST_STAT_PORT) & mask)
+		{
+			return 1;
+		}
+	}
 
-/*
- * Wait up to 15 seconds with sleeping
- */
+	/*
+	 * Wait up to 15 seconds with sleeping
+	 */
 
 	for (i = 0; i < 150; i++)
-	  {
-		  if (inb(HOST_STAT_PORT) & mask)
-		    {
-			    return 1;
-		    }
-		  {
-			  unsigned long   tlimit;
-
-			  if (HZ / 10)
-				  current->timeout = tlimit = jiffies + (HZ / 10);
-			  else
-				  tlimit = (unsigned long) -1;
-			  maui_sleep_flag.opts = WK_SLEEP;
-			  interruptible_sleep_on(&maui_sleeper);
-			  if (!(maui_sleep_flag.opts & WK_WAKEUP))
-			    {
-				    if (jiffies >= tlimit)
-					    maui_sleep_flag.opts |= WK_TIMEOUT;
-			    }
-			  maui_sleep_flag.opts &= ~WK_SLEEP;
-		  };
-		  if (signal_pending(current))
-		    {
-			    return 0;
-		    }
-	  }
-
+	{
+		if (inb(HOST_STAT_PORT) & mask)
+			return 1;
+		current->timeout = jiffies + HZ / 10;
+		current->state = TASK_INTERRUPTIBLE;
+		schedule();
+		current->timeout = 0;
+		if (signal_pending(current))
+			return 0;
+	}
 	return 0;
 }
 
-static int
-maui_read(void)
+static int maui_read(void)
 {
 	if (maui_wait(STAT_RX_AVAIL))
 		return inb(HOST_DATA_PORT);
-
 	return -1;
 }
 
-static int
-maui_write(unsigned char data)
+static int maui_write(unsigned char data)
 {
 	if (maui_wait(STAT_TX_AVAIL))
-	  {
-		  outb((data), HOST_DATA_PORT);
-		  return 1;
-	  }
-	printk("Maui: Write timeout\n");
-
+	{
+		outb((data), HOST_DATA_PORT);
+		return 1;
+	}
+	printk(KERN_WARNING "Maui: Write timeout\n");
 	return 0;
 }
 
-static void
-mauiintr(int irq, void *dev_id, struct pt_regs *dummy)
+static void mauiintr(int irq, void *dev_id, struct pt_regs *dummy)
 {
 	irq_ok = 1;
 }
 
-static int
-download_code(void)
+static int download_code(void)
 {
-	int             i, lines = 0;
-	int             eol_seen = 0, done = 0;
-	int             skip = 1;
+	int i, lines = 0;
+	int eol_seen = 0, done = 0;
+	int skip = 1;
 
-	printk("Code download (%d bytes): ", maui_osLen);
+	printk(KERN_INFO "Code download (%d bytes): ", maui_osLen);
 
 	for (i = 0; i < maui_osLen; i++)
-	  {
-		  if (maui_os[i] != '\r')
-			  if (!skip || (maui_os[i] == 'S' && (i == 0 || maui_os[i - 1] == '\n')))
-			    {
-				    skip = 0;
-
-				    if (maui_os[i] == '\n')
-					    eol_seen = skip = 1;
-				    else if (maui_os[i] == 'S')
-				      {
-					      if (maui_os[i + 1] == '8')
-						      done = 1;
-					      if (!maui_write(0xF1))
-						      goto failure;
-					      if (!maui_write('S'))
-						      goto failure;
-				    } else
-				      {
-					      if (!maui_write(maui_os[i]))
-						      goto failure;
-				      }
-
-				    if (eol_seen)
-				      {
-					      int             c = 0;
-
-					      int             n;
-
-					      eol_seen = 0;
-
-					      for (n = 0; n < 2; n++)
-						      if (maui_wait(STAT_RX_AVAIL))
-							{
-								c = inb(HOST_DATA_PORT);
-								break;
-							}
-					      if (c != 0x80)
-						{
-							printk("Download not acknowledged\n");
-							return 0;
-					      } else if (!(lines++ % 10))
-						      printk(".");
-
-					      if (done)
+	{
+		if (maui_os[i] != '\r')
+		{
+			if (!skip || (maui_os[i] == 'S' && (i == 0 || maui_os[i - 1] == '\n')))
+			{
+				skip = 0;
+
+				if (maui_os[i] == '\n')
+					eol_seen = skip = 1;
+				else if (maui_os[i] == 'S')
+				{
+					if (maui_os[i + 1] == '8')
+						done = 1;
+					if (!maui_write(0xF1))
+						goto failure;
+					if (!maui_write('S'))
+						goto failure;
+				}
+				else
+				{
+					if (!maui_write(maui_os[i]))
+						goto failure;
+				}
+
+				if (eol_seen)
+				{
+					int c = 0;
+					int n;
+
+					eol_seen = 0;
+
+					for (n = 0; n < 2; n++)
+					{
+						if (maui_wait(STAT_RX_AVAIL))
 						{
-							printk("\nDownload complete\n");
-							return 1;
+							c = inb(HOST_DATA_PORT);
+							break;
 						}
-				      }
-			    }
-	  }
-
-      failure:
-
-	printk("\nDownload failed!!!\n");
+					}
+					if (c != 0x80)
+					{
+						printk("Download not acknowledged\n");
+						return 0;
+					}
+					else if (!(lines++ % 10))
+						printk(".");
+
+					if (done)
+					{
+						printk("\n");
+						printk(KERN_INFO "Download complete\n");
+						return 1;
+					}
+				}
+			}
+		}
+	}
+
+failure:
+	printk("\n");
+	printk(KERN_ERR "Download failed!!!\n");
 	return 0;
 }
 
-static int
-maui_init(int irq)
+static int maui_init(int irq)
 {
-	int             i;
-	unsigned char   bits;
+#ifdef __SMP__
+	int i;
+#endif	
+	unsigned char bits;
 
 	switch (irq)
-	  {
-	  case 9:
-		  bits = 0x00;
-		  break;
-	  case 5:
-		  bits = 0x08;
-		  break;
-	  case 12:
-		  bits = 0x10;
-		  break;
-	  case 15:
-		  bits = 0x18;
-		  break;
-
-	  default:
-		  printk("Maui: Invalid IRQ %d\n", irq);
-		  return 0;
-	  }
+	{
+		case 9:
+			bits = 0x00;
+			break;
+		case 5:
+			bits = 0x08;
+			break;
+		case 12:
+			bits = 0x10;
+			break;
+		case 15:
+			bits = 0x18;
+			break;
 
+		default:
+			printk(KERN_ERR "Maui: Invalid IRQ %d\n", irq);
+			return 0;
+	}
 	outb((0x00), HOST_CTRL_PORT);	/* Reset */
-
 	outb((bits), HOST_DATA_PORT);	/* Set the IRQ bits */
 	outb((bits | 0x80), HOST_DATA_PORT);	/* Set the IRQ bits again? */
-
 	outb((0x80), HOST_CTRL_PORT);	/* Leave reset */
 	outb((0x80), HOST_CTRL_PORT);	/* Leave reset */
-
 	outb((0xD0), HOST_CTRL_PORT);	/* Cause interrupt */
 
+#ifndef __SMP__
 	for (i = 0; i < 1000000 && !irq_ok; i++);
 
 	if (!irq_ok)
 		return 0;
-
+#endif
 	outb((0x80), HOST_CTRL_PORT);	/* Leave reset */
 
-	printk("Turtle Beach Maui initialization\n");
+	printk(KERN_INFO "Turtle Beach Maui initialization\n");
 
 	if (!download_code())
 		return 0;
@@ -255,54 +231,51 @@
 	maui_write(0xf0);
 	maui_write(1);
 	if (maui_read() != 0x80)
-	  {
-		  maui_write(0xf0);
-		  maui_write(1);
-		  if (maui_read() != 0x80)
-			  printk("Maui didn't acknowledge set HW mode command\n");
-	  }
-	printk("Maui initialized OK\n");
+	{
+		maui_write(0xf0);
+		maui_write(1);
+		if (maui_read() != 0x80)
+			printk(KERN_ERR "Maui didn't acknowledge set HW mode command\n");
+	}
+	printk(KERN_INFO "Maui initialized OK\n");
 	return 1;
 }
 
-static int
-maui_short_wait(int mask)
+static int maui_short_wait(int mask)
 {
-	int             i;
+	int i;
 
 	for (i = 0; i < 1000; i++)
-	  {
-		  if (inb(HOST_STAT_PORT) & mask)
-		    {
-			    return 1;
-		    }
-	  }
-
+	{
+		if (inb(HOST_STAT_PORT) & mask)
+		{
+			return 1;
+		}
+	}
 	return 0;
 }
 
-static int
-maui_load_patch(int dev, int format, const char *addr,
+static int maui_load_patch(int dev, int format, const char *addr,
 		int offs, int count, int pmgr_flag)
 {
 
 	struct sysex_info header;
-	unsigned long   left, src_offs;
-	int             hdr_size = (unsigned long) &header.data[0] - (unsigned long) &header;
-	int             i;
+	unsigned long left, src_offs;
+	int hdr_size = (unsigned long) &header.data[0] - (unsigned long) &header;
+	int i;
 
 	if (format == SYSEX_PATCH)	/* Handled by midi_synth.c */
 		return orig_load_patch(dev, format, addr, offs, count, pmgr_flag);
 
 	if (format != MAUI_PATCH)
-	  {
-		  printk("Maui: Unknown patch format\n");
-	  }
+	{
+		  printk(KERN_WARNING "Maui: Unknown patch format\n");
+	}
 	if (count < hdr_size)
-	  {
-		  printk("Maui error: Patch header too short\n");
+	{
+/*		  printk("Maui error: Patch header too short\n");*/
 		  return -EINVAL;
-	  }
+	}
 	count -= hdr_size;
 
 	/*
@@ -310,43 +283,43 @@
 	 * been transferred already.
 	 */
 
-	copy_from_user(&((char *) &header)[offs], &(addr)[offs], hdr_size - offs);
+	if(copy_from_user(&((char *) &header)[offs], &(addr)[offs], hdr_size - offs))
+		return -EFAULT;
 
 	if (count < header.len)
-	  {
-		  printk("Maui warning: Host command record too short (%d<%d)\n", count, (int) header.len);
+	{
+		  printk(KERN_ERR "Maui warning: Host command record too short (%d<%d)\n", count, (int) header.len);
 		  header.len = count;
-	  }
+	}
 	left = header.len;
 	src_offs = 0;
 
 	for (i = 0; i < left; i++)
-	  {
-		  unsigned char   data;
+	{
+		unsigned char   data;
 
-		  get_user(*(unsigned char *) &data, (unsigned char *) &((addr)[hdr_size + i]));
-		  if (i == 0 && !(data & 0x80))
-			  return -EINVAL;
-
-		  if (maui_write(data) == -1)
-			  return -EIO;
-	  }
+		if(get_user(*(unsigned char *) &data, (unsigned char *) &((addr)[hdr_size + i])))
+			return -EFAULT;
+		if (i == 0 && !(data & 0x80))
+			return -EINVAL;
+
+		if (maui_write(data) == -1)
+			return -EIO;
+	}
 
 	if ((i = maui_read()) != 0x80)
-	  {
-		  if (i != -1)
-			  printk("Maui: Error status %02x\n", i);
-
-		  return -EIO;
-	  }
+	{
+		if (i != -1)
+			printk("Maui: Error status %02x\n", i);
+		return -EIO;
+	}
 	return 0;
 }
 
-int
-probe_maui(struct address_info *hw_config)
+int probe_maui(struct address_info *hw_config)
 {
-	int             i;
-	int             tmp1, tmp2, ret;
+	int i;
+	int tmp1, tmp2, ret;
 
 	if (check_region(hw_config->io_base, 8))
 		return 0;
@@ -357,42 +330,41 @@
 	if (snd_set_irq_handler(hw_config->irq, mauiintr, "Maui", maui_osp) < 0)
 		return 0;
 
-	maui_sleep_flag.opts = WK_NONE;
-/*
- * Initialize the processor if necessary
- */
+	/*
+	 * Initialize the processor if necessary
+	 */
 
 	if (maui_osLen > 0)
-	  {
-		  if (!(inb(HOST_STAT_PORT) & STAT_TX_AVAIL) ||
-		      !maui_write(0x9F) ||	/* Report firmware version */
-		      !maui_short_wait(STAT_RX_AVAIL) ||
-		      maui_read() == -1 || maui_read() == -1)
-			  if (!maui_init(hw_config->irq))
-			    {
-				    snd_release_irq(hw_config->irq);
-				    return 0;
-			    }
-	  }
+	{
+		if (!(inb(HOST_STAT_PORT) & STAT_TX_AVAIL) ||
+			!maui_write(0x9F) ||	/* Report firmware version */
+			!maui_short_wait(STAT_RX_AVAIL) ||
+			maui_read() == -1 || maui_read() == -1)
+			if (!maui_init(hw_config->irq))
+			{
+				snd_release_irq(hw_config->irq);
+				return 0;
+			}
+	}
 	if (!maui_write(0xCF))	/* Report hardware version */
-	  {
-		  printk("No WaveFront firmware detected (card uninitialized?)\n");
-		  snd_release_irq(hw_config->irq);
-		  return 0;
-	  }
+	{
+		printk(KERN_ERR "No WaveFront firmware detected (card uninitialized?)\n");
+		snd_release_irq(hw_config->irq);
+		return 0;
+	}
 	if ((tmp1 = maui_read()) == -1 || (tmp2 = maui_read()) == -1)
-	  {
-		  printk("No WaveFront firmware detected (card uninitialized?)\n");
-		  snd_release_irq(hw_config->irq);
-		  return 0;
-	  }
+	{
+		printk(KERN_ERR "No WaveFront firmware detected (card uninitialized?)\n");
+		snd_release_irq(hw_config->irq);
+		return 0;
+	}
 	if (tmp1 == 0xff || tmp2 == 0xff)
-	  {
+	{
 		  snd_release_irq(hw_config->irq);
 		  return 0;
-	  }
+	}
 	if (trace_init)
-		printk("WaveFront hardware version %d.%d\n", tmp1, tmp2);
+		printk(KERN_DEBUG "WaveFront hardware version %d.%d\n", tmp1, tmp2);
 
 	if (!maui_write(0x9F))	/* Report firmware version */
 		return 0;
@@ -400,17 +372,17 @@
 		return 0;
 
 	if (trace_init)
-		printk("WaveFront firmware version %d.%d\n", tmp1, tmp2);
+		printk(KERN_DEBUG "WaveFront firmware version %d.%d\n", tmp1, tmp2);
 
 	if (!maui_write(0x85))	/* Report free DRAM */
 		return 0;
 	tmp1 = 0;
 	for (i = 0; i < 4; i++)
-	  {
-		  tmp1 |= maui_read() << (7 * i);
-	  }
+	{
+		tmp1 |= maui_read() << (7 * i);
+	}
 	if (trace_init)
-		printk("Available DRAM %dk\n", tmp1 / 1024);
+		printk(KERN_DEBUG "Available DRAM %dk\n", tmp1 / 1024);
 
 	for (i = 0; i < 1000; i++)
 		if (probe_mpu401(hw_config))
@@ -424,10 +396,9 @@
 	return ret;
 }
 
-void
-attach_maui(struct address_info *hw_config)
+void attach_maui(struct address_info *hw_config)
 {
-	int             this_dev;
+	int this_dev;
 
 	conf_printf("Maui", hw_config);
 
@@ -436,75 +407,71 @@
 	attach_mpu401(hw_config);
 
 	if (hw_config->slots[1] != -1)	/* The MPU401 driver installed itself */
-	  {
-		  struct synth_operations *synth;
+	{
+		struct synth_operations *synth;
 
-		  this_dev = hw_config->slots[1];
+		this_dev = hw_config->slots[1];
 
-		  /*
-		   * Intercept patch loading calls so that they can be handled
-		   * by the Maui driver.
-		   */
+		/*
+		 * Intercept patch loading calls so that they can be handled
+		 * by the Maui driver.
+		 */
 
-		  synth = midi_devs[this_dev]->converter;
-		  synth->id = "MAUI";
+		synth = midi_devs[this_dev]->converter;
+		synth->id = "MAUI";
 
-		  if (synth != NULL)
-		    {
-			    orig_load_patch = synth->load_patch;
-			    synth->load_patch = &maui_load_patch;
-		  } else
-			  printk(KERN_ERR "Maui: Can't install patch loader\n");
-	  }
+		if (synth != NULL)
+		{
+			orig_load_patch = synth->load_patch;
+			synth->load_patch = &maui_load_patch;
+		}
+		else
+			printk(KERN_ERR "Maui: Can't install patch loader\n");
+	}
 }
 
-void
-unload_maui(struct address_info *hw_config)
+void unload_maui(struct address_info *hw_config)
 {
-	int             irq = hw_config->irq;
-
+	int irq = hw_config->irq;
 	release_region(hw_config->io_base + 2, 6);
-
 	unload_mpu401(hw_config);
 
 	if (irq < 0)
 		irq = -irq;
-
 	if (irq > 0)
 		snd_release_irq(irq);
 }
 
 #ifdef MODULE
 
-int             io = -1;
-int             irq = -1;
+int io = -1;
+int irq = -1;
 
-static int      fw_load = 0;
+static int fw_load = 0;
 
 struct address_info cfg;
 
 /*
- *    Install a CS4232 based card. Need to have ad1848 and mpu401
- *      loaded ready.
+ *	Install a CS4232 based card. Need to have ad1848 and mpu401
+ *	loaded ready.
  */
 
-int
-init_module(void)
+int init_module(void)
 {
-	printk("Turtle beach Maui and Tropez driver, Copyright (C) by Hannu Savolainen 1993-1996\n");
+	printk(KERN_INFO "Turtle beach Maui and Tropez driver, Copyright (C) by Hannu Savolainen 1993-1996\n");
 	if (io == -1 || irq == -1)
-	  {
-		  printk("maui: irq and io must be set.\n");
-		  return -EINVAL;
-	  }
+	{
+		printk(KERN_INFO "maui: irq and io must be set.\n");
+		return -EINVAL;
+	}
 	cfg.io_base = io;
 	cfg.irq = irq;
 
 	if (maui_os == NULL)
-	  {
-		  fw_load = 1;
-		  maui_osLen = mod_firmware_load("/etc/sound/oswf.mot", (char **) &maui_os);
-	  }
+	{
+		fw_load = 1;
+		maui_osLen = mod_firmware_load("/etc/sound/oswf.mot", (char **) &maui_os);
+	}
 	if (probe_maui(&cfg) == 0)
 		return -ENODEV;
 	attach_maui(&cfg);
@@ -512,8 +479,7 @@
 	return 0;
 }
 
-void
-cleanup_module(void)
+void cleanup_module(void)
 {
 	if (fw_load && maui_os)
 		kfree(maui_os);
diff -u --recursive --new-file v2.1.87/linux/drivers/sound/midi_synth.c linux/drivers/sound/midi_synth.c
--- v2.1.87/linux/drivers/sound/midi_synth.c	Tue Dec 30 10:59:17 1997
+++ linux/drivers/sound/midi_synth.c	Thu Feb 19 14:46:14 1998
@@ -24,10 +24,6 @@
 
 #define _MIDI_SYNTH_C_
 
-static struct wait_queue *sysex_sleeper = NULL;
-static volatile struct snd_wait sysex_sleep_flag =
-{0};
-
 #include "midi_synth.h"
 
 static int      midi2synth[MAX_MIDI_DEV];
@@ -270,7 +266,9 @@
 	switch (cmd) {
 
 	case SNDCTL_SYNTH_INFO:
-		return __copy_to_user(arg, synth_devs[dev]->info, sizeof(struct synth_info));
+		if (__copy_to_user(arg, synth_devs[dev]->info, sizeof(struct synth_info)))
+			return -EFAULT;
+		return 0;
 		
 	case SNDCTL_SYNTH_MEMAVL:
 		return 0x7fffffff;
@@ -448,8 +446,6 @@
 	inc->m_prev_status = 0x00;
 	restore_flags(flags);
 
-	sysex_sleep_flag.opts = WK_NONE;
-
 	return 1;
 }
 
@@ -521,8 +517,6 @@
   	left = sysex.len;
   	src_offs = 0;
 
-	sysex_sleep_flag.opts = WK_NONE;
-
 	for (i = 0; i < left && !signal_pending(current); i++)
 	{
 		unsigned char   data;
@@ -544,23 +538,7 @@
 		}
 		while (!midi_devs[orig_dev]->outputc(orig_dev, (unsigned char) (data & 0xff)) &&
 			!signal_pending(current))
-
-		{
-			unsigned long   tlimit;
-
-			if (1)
-				current->timeout = tlimit = jiffies + (1);
-			else
-				tlimit = (unsigned long) -1;
-			sysex_sleep_flag.opts = WK_SLEEP;
-			interruptible_sleep_on(&sysex_sleeper);
-			if (!(sysex_sleep_flag.opts & WK_WAKEUP))
-			{
-				if (jiffies >= tlimit)
-					sysex_sleep_flag.opts |= WK_TIMEOUT;
-			}
-			sysex_sleep_flag.opts &= ~WK_SLEEP;
-		};		/* Wait for timeout */
+			schedule();
 
 		if (!first_byte && data & 0x80)
 			return 0;
diff -u --recursive --new-file v2.1.87/linux/drivers/sound/midibuf.c linux/drivers/sound/midibuf.c
--- v2.1.87/linux/drivers/sound/midibuf.c	Tue Dec 30 11:02:39 1997
+++ linux/drivers/sound/midibuf.c	Thu Feb 19 14:46:14 1998
@@ -14,6 +14,8 @@
  * Thomas Sailer   : ioctl code reworked (vmalloc/vfree removed)
  */
 #include <linux/config.h>
+#include <linux/stddef.h>
+#include <linux/kerneld.h>
 
 #define MIDIBUF_C
 
@@ -27,41 +29,33 @@
 
 #define MAX_QUEUE_SIZE	4000
 
-static struct wait_queue *midi_sleeper[MAX_MIDI_DEV] =
-{NULL};
-static volatile struct snd_wait midi_sleep_flag[MAX_MIDI_DEV] =
-{
-	{0}};
-static struct wait_queue *input_sleeper[MAX_MIDI_DEV] =
-{NULL};
-static volatile struct snd_wait input_sleep_flag[MAX_MIDI_DEV] =
-{
-	{0}};
+static struct wait_queue *midi_sleeper[MAX_MIDI_DEV] = {NULL};
+static struct wait_queue *input_sleeper[MAX_MIDI_DEV] = {NULL};
 
 struct midi_buf
-  {
-	  int             len, head, tail;
-	  unsigned char   queue[MAX_QUEUE_SIZE];
-  };
+{
+	int len, head, tail;
+	unsigned char queue[MAX_QUEUE_SIZE];
+};
 
 struct midi_parms
-  {
-	  int             prech_timeout;	/*
-						 * Timeout before the first ch
-						 */
-  };
+{
+	int prech_timeout;	/*
+				 * Timeout before the first ch
+				 */
+};
 
-static struct midi_buf *midi_out_buf[MAX_MIDI_DEV] =
-{NULL};
-static struct midi_buf *midi_in_buf[MAX_MIDI_DEV] =
-{NULL};
+static struct midi_buf *midi_out_buf[MAX_MIDI_DEV] = {NULL};
+static struct midi_buf *midi_in_buf[MAX_MIDI_DEV] = {NULL};
 static struct midi_parms parms[MAX_MIDI_DEV];
 
-static void     midi_poll(unsigned long dummy);
+static void midi_poll(unsigned long dummy);
+
 
+static struct timer_list poll_timer = {
+	NULL, NULL, 0, 0, midi_poll
+};
 
-static struct timer_list poll_timer =
-{NULL, NULL, 0, 0, midi_poll};
 static volatile int open_devs = 0;
 
 #define DATA_AVAIL(q) (q->len)
@@ -87,8 +81,7 @@
 	  restore_flags(flags); \
 	}
 
-static void
-drain_midi_queue(int dev)
+static void drain_midi_queue(int dev)
 {
 
 	/*
@@ -96,29 +89,15 @@
 	 */
 
 	if (midi_devs[dev]->buffer_status != NULL)
-		while (!signal_pending(current) &&
-		       midi_devs[dev]->buffer_status(dev))
-
-		  {
-			  unsigned long   tlimit;
-
-			  if (HZ / 10)
-				  current->timeout = tlimit = jiffies + (HZ / 10);
-			  else
-				  tlimit = (unsigned long) -1;
-			  midi_sleep_flag[dev].opts = WK_SLEEP;
-			  interruptible_sleep_on(&midi_sleeper[dev]);
-			  if (!(midi_sleep_flag[dev].opts & WK_WAKEUP))
-			    {
-				    if (jiffies >= tlimit)
-					    midi_sleep_flag[dev].opts |= WK_TIMEOUT;
-			    }
-			  midi_sleep_flag[dev].opts &= ~WK_SLEEP;
-		  };
+		while (!signal_pending(current) && midi_devs[dev]->buffer_status(dev)) 
+		{
+			current->timeout = jiffies + HZ / 10;
+			interruptible_sleep_on(&midi_sleeper[dev]);
+			current->timeout = 0;
+		}
 }
 
-static void
-midi_input_intr(int dev, unsigned char data)
+static void midi_input_intr(int dev, unsigned char data)
 {
 	if (midi_in_buf[dev] == NULL)
 		return;
@@ -130,27 +109,20 @@
 				 * Ignore
 				 */
 
-	if (SPACE_AVAIL(midi_in_buf[dev]))
-	  {
-		  QUEUE_BYTE(midi_in_buf[dev], data);
-		  if ((input_sleep_flag[dev].opts & WK_SLEEP))
-		    {
-			    input_sleep_flag[dev].opts = WK_WAKEUP;
-			    wake_up(&input_sleeper[dev]);
-		    };
-	  }
+	if (SPACE_AVAIL(midi_in_buf[dev])) {
+		QUEUE_BYTE(midi_in_buf[dev], data);
+		wake_up(&input_sleeper[dev]);
+	}
 }
 
-static void
-midi_output_intr(int dev)
+static void midi_output_intr(int dev)
 {
 	/*
 	 * Currently NOP
 	 */
 }
 
-static void
-midi_poll(unsigned long dummy)
+static void midi_poll(unsigned long dummy)
 {
 	unsigned long   flags;
 	int             dev;
@@ -158,116 +130,99 @@
 	save_flags(flags);
 	cli();
 	if (open_devs)
-	  {
-		  for (dev = 0; dev < num_midis; dev++)
-			  if (midi_devs[dev] != NULL && midi_out_buf[dev] != NULL)
-			    {
-				    int             ok = 1;
-
-				    while (DATA_AVAIL(midi_out_buf[dev]) && ok)
-				      {
-					      int             c = midi_out_buf[dev]->queue[midi_out_buf[dev]->head];
-
-					      restore_flags(flags);	/* Give some time to others */
-					      ok = midi_devs[dev]->outputc(dev, c);
-					      save_flags(flags);
-					      cli();
-					      midi_out_buf[dev]->head = (midi_out_buf[dev]->head + 1) % MAX_QUEUE_SIZE;
-					      midi_out_buf[dev]->len--;
-				      }
-
-				    if (DATA_AVAIL(midi_out_buf[dev]) < 100 &&
-					(midi_sleep_flag[dev].opts & WK_SLEEP))
-				      {
-					      midi_sleep_flag[dev].opts = WK_WAKEUP;
-					      wake_up(&midi_sleeper[dev]);
-				      };
-			    }
-		  {
-			  poll_timer.expires = (1) + jiffies;
-			  add_timer(&poll_timer);
-		  };		/*
-				   * Come back later
-				 */
-	  }
+	{
+		for (dev = 0; dev < num_midis; dev++)
+			if (midi_devs[dev] != NULL && midi_out_buf[dev] != NULL)
+			{
+				int ok = 1;
+
+				while (DATA_AVAIL(midi_out_buf[dev]) && ok)
+				{
+					int c = midi_out_buf[dev]->queue[midi_out_buf[dev]->head];
+
+					restore_flags(flags);	/* Give some time to others */
+					ok = midi_devs[dev]->outputc(dev, c);
+					cli();
+					midi_out_buf[dev]->head = (midi_out_buf[dev]->head + 1) % MAX_QUEUE_SIZE;
+					midi_out_buf[dev]->len--;
+				}
+
+				if (DATA_AVAIL(midi_out_buf[dev]) < 100)
+					wake_up(&midi_sleeper[dev]);
+			}
+		poll_timer.expires = (1) + jiffies;
+		add_timer(&poll_timer);
+		/*
+		 * Come back later
+		 */
+	}
 	restore_flags(flags);
 }
 
-int
-MIDIbuf_open(int dev, struct fileinfo *file)
+int MIDIbuf_open(int dev, struct file *file)
 {
-	int             mode, err;
+	int mode, err;
 
 	dev = dev >> 4;
-	mode = file->mode & O_ACCMODE;
+	mode = translate_mode(file);
 
 	if (num_midis > MAX_MIDI_DEV)
-	  {
-		  printk("Sound: FATAL ERROR: Too many midi interfaces\n");
-		  num_midis = MAX_MIDI_DEV;
-	  }
+	{
+		printk(KERN_ERR "midi: Too many midi interfaces\n");
+		num_midis = MAX_MIDI_DEV;
+	}
 	if (dev < 0 || dev >= num_midis || midi_devs[dev] == NULL)
-	  {
-		  printk("Sound: Nonexistent MIDI interface %d\n", dev);
 		  return -ENXIO;
-	  }
 	/*
-	   *    Interrupts disabled. Be careful
+	 *    Interrupts disabled. Be careful
 	 */
 
 	if ((err = midi_devs[dev]->open(dev, mode,
 				 midi_input_intr, midi_output_intr)) < 0)
-	  {
-		  return err;
-	  }
-	parms[dev].prech_timeout = 0;
+		return err;
 
+	parms[dev].prech_timeout = 0;
 	midi_in_buf[dev] = (struct midi_buf *) vmalloc(sizeof(struct midi_buf));
 
 	if (midi_in_buf[dev] == NULL)
-	  {
-		  printk("midi: Can't allocate buffer\n");
-		  midi_devs[dev]->close(dev);
-		  return -EIO;
-	  }
+	{
+		printk(KERN_WARNING "midi: Can't allocate buffer\n");
+		midi_devs[dev]->close(dev);
+		return -EIO;
+	}
 	midi_in_buf[dev]->len = midi_in_buf[dev]->head = midi_in_buf[dev]->tail = 0;
 
 	midi_out_buf[dev] = (struct midi_buf *) vmalloc(sizeof(struct midi_buf));
 
 	if (midi_out_buf[dev] == NULL)
-	  {
-		  printk("midi: Can't allocate buffer\n");
-		  midi_devs[dev]->close(dev);
-		  vfree(midi_in_buf[dev]);
-		  midi_in_buf[dev] = NULL;
-		  return -EIO;
-	  }
+	{
+		printk(KERN_WARNING "midi: Can't allocate buffer\n");
+		midi_devs[dev]->close(dev);
+		vfree(midi_in_buf[dev]);
+		midi_in_buf[dev] = NULL;
+		return -EIO;
+	}
 	midi_out_buf[dev]->len = midi_out_buf[dev]->head = midi_out_buf[dev]->tail = 0;
 	open_devs++;
 
-	midi_sleep_flag[dev].opts = WK_NONE;
-	input_sleep_flag[dev].opts = WK_NONE;
+	init_waitqueue(&midi_sleeper[dev]);
+	init_waitqueue(&input_sleeper[dev]);
 
 	if (open_devs < 2)	/* This was first open */
-	  {
-		  ;
-
-		  {
-			  poll_timer.expires = (1) + jiffies;
-			  add_timer(&poll_timer);
-		  };		/* Start polling */
-	  }
+	{
+		poll_timer.expires = 1 + jiffies;
+		add_timer(&poll_timer);	/* Start polling */
+	}
 	return err;
 }
 
-void
-MIDIbuf_release(int dev, struct fileinfo *file)
+void MIDIbuf_release(int dev, struct file *file)
 {
-	int             mode;
-	unsigned long   flags;
+	int mode;
+	unsigned long flags;
 
 	dev = dev >> 4;
-	mode = file->mode & O_ACCMODE;
+	mode = translate_mode(file);
 
 	if (dev < 0 || dev >= num_midis || midi_devs[dev] == NULL)
 		return;
@@ -276,42 +231,26 @@
 	cli();
 
 	/*
-	   * Wait until the queue is empty
+	 * Wait until the queue is empty
 	 */
 
 	if (mode != OPEN_READ)
-	  {
-		  midi_devs[dev]->outputc(dev, 0xfe);	/*
+	{
+		midi_devs[dev]->outputc(dev, 0xfe);	/*
 							   * Active sensing to shut the
 							   * devices
 							 */
 
-		  while (!signal_pending(current) &&
-			 DATA_AVAIL(midi_out_buf[dev]))
-
-		    {
-			    unsigned long   tlimit;
-
-			    if (0)
-				    current->timeout = tlimit = jiffies + (0);
-			    else
-				    tlimit = (unsigned long) -1;
-			    midi_sleep_flag[dev].opts = WK_SLEEP;
-			    interruptible_sleep_on(&midi_sleeper[dev]);
-			    if (!(midi_sleep_flag[dev].opts & WK_WAKEUP))
-			      {
-				      if (jiffies >= tlimit)
-					      midi_sleep_flag[dev].opts |= WK_TIMEOUT;
-			      }
-			    midi_sleep_flag[dev].opts &= ~WK_SLEEP;
-		    };		/*
-				   * Sync
-				 */
+		while (!signal_pending(current) && DATA_AVAIL(midi_out_buf[dev]))
+			  interruptible_sleep_on(&midi_sleeper[dev]);
+		/*
+		 *	Sync
+		 */
 
-		  drain_midi_queue(dev);	/*
-						 * Ensure the output queues are empty
-						 */
-	  }
+		drain_midi_queue(dev);	/*
+					 * Ensure the output queues are empty
+					 */
+	}
 	restore_flags(flags);
 
 	midi_devs[dev]->close(dev);
@@ -325,12 +264,11 @@
 	open_devs--;
 }
 
-int
-MIDIbuf_write(int dev, struct fileinfo *file, const char *buf, int count)
+int MIDIbuf_write(int dev, struct file *file, const char *buf, int count)
 {
-	unsigned long   flags;
-	int             c, n, i;
-	unsigned char   tmp_data;
+	unsigned long flags;
+	int c, n, i;
+	unsigned char tmp_data;
 
 	dev = dev >> 4;
 
@@ -343,192 +281,143 @@
 	c = 0;
 
 	while (c < count)
-	  {
-		  n = SPACE_AVAIL(midi_out_buf[dev]);
+	{
+		n = SPACE_AVAIL(midi_out_buf[dev]);
 
-		  if (n == 0)	/*
+		if (n == 0) {	/*
 				 * No space just now. We have to sleep
 				 */
-		    {
-
-			    {
-				    unsigned long   tlimit;
-
-				    if (0)
-					    current->timeout = tlimit = jiffies + (0);
-				    else
-					    tlimit = (unsigned long) -1;
-				    midi_sleep_flag[dev].opts = WK_SLEEP;
-				    interruptible_sleep_on(&midi_sleeper[dev]);
-				    if (!(midi_sleep_flag[dev].opts & WK_WAKEUP))
-				      {
-					      if (jiffies >= tlimit)
-						      midi_sleep_flag[dev].opts |= WK_TIMEOUT;
-				      }
-				    midi_sleep_flag[dev].opts &= ~WK_SLEEP;
-			    };
-			    if (signal_pending(current))
-			      {
-				      restore_flags(flags);
-				      return -EINTR;
-			      }
-			    n = SPACE_AVAIL(midi_out_buf[dev]);
-		    }
-		  if (n > (count - c))
-			  n = count - c;
-
-		  for (i = 0; i < n; i++)
-		    {
-			    copy_from_user((char *) &tmp_data, &(buf)[c], 1);
-			    QUEUE_BYTE(midi_out_buf[dev], tmp_data);
-			    c++;
-		    }
-	  }
+			interruptible_sleep_on(&midi_sleeper[dev]);
+			if (signal_pending(current)) 
+			{
+				restore_flags(flags);
+				return -EINTR;
+			}
+			n = SPACE_AVAIL(midi_out_buf[dev]);
+		}
+		if (n > (count - c))
+			n = count - c;
 
+		for (i = 0; i < n; i++)
+		{
+			/* BROKE BROKE BROKE - CANT DO THIS WITH CLI !! */
+			copy_from_user((char *) &tmp_data, &(buf)[c], 1);
+			QUEUE_BYTE(midi_out_buf[dev], tmp_data);
+			c++;
+		}
+	}
 	restore_flags(flags);
-
 	return c;
 }
 
 
-int
-MIDIbuf_read(int dev, struct fileinfo *file, char *buf, int count)
+int MIDIbuf_read(int dev, struct file *file, char *buf, int count)
 {
-	int             n, c = 0;
-	unsigned long   flags;
-	unsigned char   tmp_data;
+	int n, c = 0;
+	unsigned long flags;
+	unsigned char tmp_data;
 
 	dev = dev >> 4;
 
 	save_flags(flags);
 	cli();
 
-	if (!DATA_AVAIL(midi_in_buf[dev]))	/*
+	if (!DATA_AVAIL(midi_in_buf[dev])) {	/*
 						 * No data yet, wait
 						 */
-	  {
-
-		  {
-			  unsigned long   tlimit;
-
-			  if (parms[dev].prech_timeout)
-				  current->timeout = tlimit = jiffies + (parms[dev].prech_timeout);
-			  else
-				  tlimit = (unsigned long) -1;
-			  input_sleep_flag[dev].opts = WK_SLEEP;
-			  interruptible_sleep_on(&input_sleeper[dev]);
-			  if (!(input_sleep_flag[dev].opts & WK_WAKEUP))
-			    {
-				    if (jiffies >= tlimit)
-					    input_sleep_flag[dev].opts |= WK_TIMEOUT;
-			    }
-			  input_sleep_flag[dev].opts &= ~WK_SLEEP;
-		  };
-		  if (signal_pending(current))
-			  c = -EINTR;	/*
-					   * The user is getting restless
-					 */
-	  }
+		current->timeout = parms[dev].prech_timeout ? jiffies + parms[dev].prech_timeout : 0;
+		interruptible_sleep_on(&input_sleeper[dev]);
+		current->timeout = 0;
+		if (signal_pending(current))
+			c = -EINTR;	/* The user is getting restless */
+	}
 	if (c == 0 && DATA_AVAIL(midi_in_buf[dev]))	/*
 							 * Got some bytes
 							 */
-	  {
-		  n = DATA_AVAIL(midi_in_buf[dev]);
-		  if (n > count)
-			  n = count;
-		  c = 0;
-
-		  while (c < n)
-		    {
-			    REMOVE_BYTE(midi_in_buf[dev], tmp_data);
-			    {
-				    char           *fixit = (char *) &tmp_data;
-
-				    copy_to_user(&(buf)[c], fixit, 1);
-			    };
-			    c++;
-		    }
-	  }
+	{
+		n = DATA_AVAIL(midi_in_buf[dev]);
+		if (n > count)
+			n = count;
+		c = 0;
+
+		while (c < n)
+		{
+			char *fixit;
+			REMOVE_BYTE(midi_in_buf[dev], tmp_data);
+			fixit = (char *) &tmp_data;
+			/* BROKE BROKE BROKE */
+			copy_to_user(&(buf)[c], fixit, 1);
+			c++;
+		}
+	}
 	restore_flags(flags);
-
 	return c;
 }
 
-int MIDIbuf_ioctl(int dev, struct fileinfo *file,
+int MIDIbuf_ioctl(int dev, struct file *file,
 		  unsigned int cmd, caddr_t arg)
 {
 	int val;
 
 	dev = dev >> 4;
-	if (((cmd >> 8) & 0xff) == 'C') {
-		  if (midi_devs[dev]->coproc)	/* Coprocessor ioctl */
-			  return midi_devs[dev]->coproc->ioctl(midi_devs[dev]->coproc->devc, cmd, arg, 0);
-		  printk("/dev/midi%d: No coprocessor for this device\n", dev);
-		  return -ENXIO;
-	} else
-		switch (cmd) {
-		case SNDCTL_MIDI_PRETIME:
-			if (__get_user(val, (int *)arg))
-				return -EFAULT;
-			if (val < 0)
-				val = 0;
-			val = (HZ * val) / 10;
-			parms[dev].prech_timeout = val;
-			return __put_user(val, (int *)arg);
+	
+	if (((cmd >> 8) & 0xff) == 'C') 
+	{
+		if (midi_devs[dev]->coproc)	/* Coprocessor ioctl */
+			return midi_devs[dev]->coproc->ioctl(midi_devs[dev]->coproc->devc, cmd, arg, 0);
+/*		printk("/dev/midi%d: No coprocessor for this device\n", dev);*/
+		return -ENXIO;
+	}
+	else
+	{
+		switch (cmd) 
+		{
+			case SNDCTL_MIDI_PRETIME:
+				if (get_user(val, (int *)arg))
+					return -EFAULT;
+				if (val < 0)
+					val = 0;
+				val = (HZ * val) / 10;
+				parms[dev].prech_timeout = val;
+				return put_user(val, (int *)arg);
 			
-		default:
-			if (!midi_devs[dev]->ioctl)
-				return -EINVAL;
-			return midi_devs[dev]->ioctl(dev, cmd, arg);
+			default:
+				if (!midi_devs[dev]->ioctl)
+					return -EINVAL;
+				return midi_devs[dev]->ioctl(dev, cmd, arg);
 		}
+	}
 }
 
-int
-MIDIbuf_select(int dev, struct fileinfo *file, int sel_type, poll_table * wait)
+unsigned int MIDIbuf_poll(int dev, struct file *file, poll_table * wait)
 {
+	unsigned int mask = 0;
+
 	dev = dev >> 4;
 
-	switch (sel_type)
-	  {
-	  case SEL_IN:
-		  if (!DATA_AVAIL(midi_in_buf[dev]))
-		    {
-
-			    input_sleep_flag[dev].opts = WK_SLEEP;
-			    poll_wait(&input_sleeper[dev], wait);
-			    return 0;
-		    }
-		  return 1;
-		  break;
-
-	  case SEL_OUT:
-		  if (SPACE_AVAIL(midi_out_buf[dev]))
-		    {
-
-			    midi_sleep_flag[dev].opts = WK_SLEEP;
-			    poll_wait(&midi_sleeper[dev], wait);
-			    return 0;
-		    }
-		  return 1;
-		  break;
-
-	  case SEL_EX:
-		  return 0;
-	  }
+	/* input */
+	poll_wait(&input_sleeper[dev], wait);
+	if (DATA_AVAIL(midi_in_buf[dev]))
+		mask |= POLLIN | POLLRDNORM;
 
-	return 0;
+	/* output */
+	poll_wait(&midi_sleeper[dev], wait);
+	if (!SPACE_AVAIL(midi_out_buf[dev]))
+		mask |= POLLOUT | POLLWRNORM;
+	
+	return mask;
 }
 
 
-void
-MIDIbuf_init(void)
+void MIDIbuf_init(void)
 {
 }
 
-int
-MIDIbuf_avail(int dev)
+int MIDIbuf_avail(int dev)
 {
-	return DATA_AVAIL (midi_in_buf[dev]);
+        if (midi_in_buf[dev])
+		return DATA_AVAIL (midi_in_buf[dev]);
+	return 0;
 }
 
 
diff -u --recursive --new-file v2.1.87/linux/drivers/sound/mpu401.c linux/drivers/sound/mpu401.c
--- v2.1.87/linux/drivers/sound/mpu401.c	Tue Dec 30 10:59:17 1997
+++ linux/drivers/sound/mpu401.c	Thu Feb 19 14:46:14 1998
@@ -772,7 +772,9 @@
 			return -EFAULT;
 		if ((ret = mpu401_command(dev, &rec)) < 0)
 			return ret;
-		return __copy_to_user(arg, &rec, sizeof(rec));
+		if (__copy_to_user(arg, &rec, sizeof(rec)))
+			return -EFAULT;
+		return 0;
 
 	default:
 		return -EINVAL;
diff -u --recursive --new-file v2.1.87/linux/drivers/sound/opl3.c linux/drivers/sound/opl3.c
--- v2.1.87/linux/drivers/sound/opl3.c	Tue Dec 30 10:59:17 1997
+++ linux/drivers/sound/opl3.c	Thu Feb 19 14:46:14 1998
@@ -15,6 +15,7 @@
  */
 #include <linux/config.h>
 #include <linux/module.h>
+#include <linux/delay.h>
 
 /*
  * Major improvements to the FM handling 30AUG92 by Rob Hooft,
@@ -120,7 +121,9 @@
 
 	case SNDCTL_SYNTH_INFO:
 		devc->fm_info.nr_voices = (devc->nr_voice == 12) ? 6 : devc->nr_voice;
-		return __copy_to_user(arg, &devc->fm_info, sizeof(devc->fm_info));
+		if (__copy_to_user(arg, &devc->fm_info, sizeof(devc->fm_info)))
+			return -EFAULT;
+		return 0;
 
 	case SNDCTL_SYNTH_MEMAVL:
 		return 0x7fffffff;
@@ -215,17 +218,17 @@
 			int tmp;
 
 			outb((0x02), ioaddr - 8);	/* Select OPL4 ID register */
-			tenmicrosec(devc->osp);
+			udelay(10);
 			tmp = inb(ioaddr - 7);		/* Read it */
-			tenmicrosec(devc->osp);
+			udelay(10);
 
 			if (tmp == 0x20)	/* OPL4 should return 0x20 here */
 			{
 				detected_model = 4;
 				outb((0xF8), ioaddr - 8);	/* Select OPL4 FM mixer control */
-				tenmicrosec(devc->osp);
+				udelay(10);
 				outb((0x1B), ioaddr - 7);	/* Write value */
-				tenmicrosec(devc->osp);
+				udelay(10);
 			}
 			else
 				detected_model = 3;
@@ -707,7 +710,7 @@
 	outb(((unsigned char) (addr & 0xff)), io_addr);
 
 	if (devc->model != 2)
-		tenmicrosec(devc->osp);
+		udelay(10);
 	else
 		for (i = 0; i < 2; i++)
 			inb(io_addr);
@@ -715,11 +718,7 @@
 	outb(((unsigned char) (val & 0xff)), io_addr + 1);
 
 	if (devc->model != 2)
-	{
-		tenmicrosec(devc->osp);
-		tenmicrosec(devc->osp);
-		tenmicrosec(devc->osp);
-	}
+		udelay(30);
 	else
 		for (i = 0; i < 2; i++)
 			inb(io_addr);
diff -u --recursive --new-file v2.1.87/linux/drivers/sound/os.h linux/drivers/sound/os.h
--- v2.1.87/linux/drivers/sound/os.h	Mon Jan  5 10:13:36 1998
+++ linux/drivers/sound/os.h	Thu Feb 19 14:46:14 1998
@@ -41,10 +41,6 @@
 #define FALSE	0
 #define TRUE	1
 
-struct snd_wait {
-	  volatile int opts;
-	};
-
 extern int sound_alloc_dma(int chn, char *deviceID);
 extern int sound_open_dma(int chn, char *deviceID);
 extern void sound_free_dma(int chn);
diff -u --recursive --new-file v2.1.87/linux/drivers/sound/pss.c linux/drivers/sound/pss.c
--- v2.1.87/linux/drivers/sound/pss.c	Tue Feb 17 13:12:47 1998
+++ linux/drivers/sound/pss.c	Thu Feb 19 14:46:14 1998
@@ -546,7 +546,7 @@
 				mbuf->len = i;	/* feed back number of WORDs sent */
 				err = __copy_to_user(arg, mbuf, sizeof(copr_msg));
 				vfree(mbuf);
-				return err ? err : -EIO;
+				return err ? -EFAULT : -EIO;
 			}
 		}
 		restore_flags(flags);
@@ -594,7 +594,9 @@
 		}
 		dbuf.parm1 = tmp;
 		restore_flags(flags);
-		return __copy_to_user(arg, &dbuf, sizeof(dbuf));
+		if (__copy_to_user(arg, &dbuf, sizeof(dbuf)))
+			return -EFAULT;
+		return 0;
 		
 	case SNDCTL_COPR_WDATA:
 		if (__copy_from_user(&dbuf, arg, sizeof(dbuf)))
@@ -667,7 +669,9 @@
 		}
 		dbuf.parm1 |= tmp & 0x00ff;
 		restore_flags(flags);
-		return __copy_to_user(arg, &dbuf, sizeof(dbuf));
+		if (__copy_to_user(arg, &dbuf, sizeof(dbuf)))
+			return -EFAULT;
+		return 0;
 
 	default:
 		return -EINVAL;
diff -u --recursive --new-file v2.1.87/linux/drivers/sound/sb_common.c linux/drivers/sound/sb_common.c
--- v2.1.87/linux/drivers/sound/sb_common.c	Sat Nov 29 10:33:21 1997
+++ linux/drivers/sound/sb_common.c	Thu Feb 19 14:46:14 1998
@@ -11,6 +11,7 @@
  * for more info.
  */
 #include <linux/config.h>
+#include <linux/delay.h>
 
 
 #include "sound_config.h"
@@ -27,12 +28,13 @@
 
 static sb_devc *detected_devc = NULL;	/* For communication from probe to init */
 static sb_devc *last_devc = NULL;	/* For MPU401 initialization */
-static sb_devc *irq2devc[16] =
-{NULL};
-static unsigned char jazz_irq_bits[] =
-{0, 0, 2, 3, 0, 1, 0, 4, 0, 2, 5, 0, 0, 0, 0, 6};
-static unsigned char jazz_dma_bits[] =
-{0, 1, 0, 2, 0, 3, 0, 4};
+static sb_devc *irq2devc[16] = {NULL};
+static unsigned char jazz_irq_bits[] = {
+	0, 0, 2, 3, 0, 1, 0, 4, 0, 2, 5, 0, 0, 0, 0, 6
+};
+static unsigned char jazz_dma_bits[] = {
+	0, 1, 0, 2, 0, 3, 0, 4
+};
 
 /*
  * Jazz16 chipset specific control variables
@@ -54,8 +56,7 @@
 #endif
 
 
-int
-sb_dsp_command(sb_devc * devc, unsigned char val)
+int sb_dsp_command(sb_devc * devc, unsigned char val)
 {
 	int             i;
 	unsigned long   limit;
@@ -70,33 +71,31 @@
 	 */
 
 	for (i = 0; i < 500000 && jiffies < limit; i++)
-	  {
-		  if ((inb(DSP_STATUS) & 0x80) == 0)
-		    {
-			    outb((val), DSP_COMMAND);
-			    return 1;
-		    }
-	  }
+	{
+		if ((inb(DSP_STATUS) & 0x80) == 0)
+		{
+			outb((val), DSP_COMMAND);
+			return 1;
+		}
+	}
 
-	printk("Sound Blaster: DSP Command(%x) Timeout.\n", val);
+	printk(KERN_WARNING "Sound Blaster: DSP Command(%x) Timeout.\n", val);
 	return 0;
 }
 
-static int
-sb_dsp_get_byte(sb_devc * devc)
+static int sb_dsp_get_byte(sb_devc * devc)
 {
 	int             i;
 
 	for (i = 1000; i; i--)
 		if (inb(DSP_DATA_AVAIL) & 0x80)
-		  {
-			  return inb(DSP_READ);
-		  }
+		{
+			return inb(DSP_READ);
+		}
 	return 0xffff;
 }
 
-int
-ess_write(sb_devc * devc, unsigned char reg, unsigned char data)
+int ess_write(sb_devc * devc, unsigned char reg, unsigned char data)
 {
 	/* Write a byte to an extended mode register of ES1688 */
 
@@ -106,8 +105,7 @@
 	return sb_dsp_command(devc, data);
 }
 
-int
-ess_read(sb_devc * devc, unsigned char reg)
+int ess_read(sb_devc * devc, unsigned char reg)
 {
 /* Read a byte from an extended mode register of ES1688 */
 
@@ -120,61 +118,61 @@
 	return sb_dsp_get_byte(devc);
 }
 
-static void
-sbintr(int irq, void *dev_id, struct pt_regs *dummy)
+static void sbintr(int irq, void *dev_id, struct pt_regs *dummy)
 {
-	int             status;
+	int status;
 	unsigned char   src = 0xff;
 
-	sb_devc        *devc = irq2devc[irq];
+	sb_devc *devc = irq2devc[irq];
 
 	if (devc == NULL || devc->irq != irq)
-	  {
-		  DEB(printk("sbintr: Bogus interrupt IRQ%d\n", irq));
-		  return;
-	  }
+	{
+		DEB(printk("sbintr: Bogus interrupt IRQ%d\n", irq));
+		return;
+	}
 	devc->irq_ok = 1;
 
 	if (devc->model == MDL_SB16)
-	  {
-
-		  src = sb_getmixer(devc, IRQ_STAT);	/* Interrupt source register */
+	{
+		src = sb_getmixer(devc, IRQ_STAT);	/* Interrupt source register */
 
 #if defined(CONFIG_MIDI)&& (defined(CONFIG_UART401)||defined(CONFIG_UART401_MODULE))
-		  if (src & 4)
-			  uart401intr(devc->irq, NULL, NULL);	/* MPU401 interrupt */
+		if (src & 4)
+			uart401intr(devc->irq, NULL, NULL);	/* MPU401 interrupt */
 #endif
 
-		  if (!(src & 3))
-			  return;	/* Not a DSP interrupt */
-	  }
+		if (!(src & 3))
+			return;	/* Not a DSP interrupt */
+	}
 	if (devc->intr_active)
+	{
 		switch (devc->irq_mode)
-		  {
-		  case IMODE_OUTPUT:
-			  DMAbuf_outputintr(devc->dev, 1);
-			  break;
-
-		  case IMODE_INPUT:
-			  DMAbuf_inputintr(devc->dev);
-			  break;
+		{
+			case IMODE_OUTPUT:
+				DMAbuf_outputintr(devc->dev, 1);
+				break;
+
+			case IMODE_INPUT:
+				DMAbuf_inputintr(devc->dev);
+				break;
 
-		  case IMODE_INIT:
-			  break;
+			case IMODE_INIT:
+				break;
 
-		  case IMODE_MIDI:
+			case IMODE_MIDI:
 #if  defined(CONFIG_MIDI)
-			  sb_midi_interrupt(devc);
+				sb_midi_interrupt(devc);
 #endif
-			  break;
+				break;
 
-		  default:
-			  /* printk( "Sound Blaster: Unexpected interrupt\n"); */
-			  ;
-		  }
-/*
- * Acknowledge interrupts 
- */
+			default:
+				/* printk( "Sound Blaster: Unexpected interrupt\n"); */
+				;
+		}
+	}
+	/*
+	 * Acknowledge interrupts 
+	 */
 
 	if (src & 0x01)
 		status = inb(DSP_DATA_AVAIL);
@@ -184,31 +182,28 @@
 }
 
 
-int
-sb_dsp_reset(sb_devc * devc)
+int sb_dsp_reset(sb_devc * devc)
 {
-	int             loopc;
+	int loopc;
 
 	DEB(printk("Entered sb_dsp_reset()\n"));
 
 	if (devc->model == MDL_ESS)
-		outb((3), DSP_RESET);	/* Reset FIFO too */
+		outb(3, DSP_RESET);	/* Reset FIFO too */
 	else
-		outb((1), DSP_RESET);
+		outb(1, DSP_RESET);
 
-	tenmicrosec(devc->osp);
-	outb((0), DSP_RESET);
-	tenmicrosec(devc->osp);
-	tenmicrosec(devc->osp);
-	tenmicrosec(devc->osp);
+	udelay(10);
+	outb(0, DSP_RESET);
+	udelay(30);
 
 	for (loopc = 0; loopc < 1000 && !(inb(DSP_DATA_AVAIL) & 0x80); loopc++);
 
 	if (inb(DSP_READ) != 0xAA)
-	  {
-		  DDB(printk("sb: No response to RESET\n"));
-		  return 0;	/* Sorry */
-	  }
+	{
+		DDB(printk("sb: No response to RESET\n"));
+		return 0;	/* Sorry */
+	}
 	if (devc->model == MDL_ESS)
 		sb_dsp_command(devc, 0xc6);	/* Enable extended mode */
 
@@ -216,10 +211,9 @@
 	return 1;
 }
 
-static void
-dsp_get_vers(sb_devc * devc)
+static void dsp_get_vers(sb_devc * devc)
 {
-	int             i;
+	int i;
 
 	unsigned long   flags;
 
@@ -230,32 +224,31 @@
 	sb_dsp_command(devc, 0xe1);	/* Get version */
 
 	for (i = 100000; i; i--)
-	  {
-		  if (inb(DSP_DATA_AVAIL) & 0x80)
-		    {
-			    if (devc->major == 0)
-				    devc->major = inb(DSP_READ);
-			    else
-			      {
-				      devc->minor = inb(DSP_READ);
-				      break;
-			      }
-		    }
-	  }
+	{
+		if (inb(DSP_DATA_AVAIL) & 0x80)
+		{
+			if (devc->major == 0)
+				devc->major = inb(DSP_READ);
+			else
+			{
+				devc->minor = inb(DSP_READ);
+				break;
+			}
+		}
+	}
 	DDB(printk("DSP version %d.%d\n", devc->major, devc->minor));
 	restore_flags(flags);
 }
 
-static int
-sb16_set_dma_hw(sb_devc * devc)
+static int sb16_set_dma_hw(sb_devc * devc)
 {
-	int             bits;
+	int bits;
 
 	if (devc->dma8 != 0 && devc->dma8 != 1 && devc->dma8 != 3)
-	  {
-		  printk("SB16: Invalid 8 bit DMA (%d)\n", devc->dma8);
-		  return 0;
-	  }
+	{
+		printk(KERN_ERR "SB16: Invalid 8 bit DMA (%d)\n", devc->dma8);
+		return 0;
+	}
 	bits = (1 << devc->dma8);
 
 	if (devc->dma16 >= 5 && devc->dma16 <= 7)
@@ -266,90 +259,84 @@
 }
 
 #if defined(CONFIG_MIDI) && defined(CONFIG_UART401)
-static void
-sb16_set_mpu_port(sb_devc * devc, struct address_info *hw_config)
+static void sb16_set_mpu_port(sb_devc * devc, struct address_info *hw_config)
 {
-/*
- * This routine initializes new MIDI port setup register of SB Vibra (CT2502).
- */
+	/*
+	 * This routine initializes new MIDI port setup register of SB Vibra (CT2502).
+	 */
 	unsigned char   bits = sb_getmixer(devc, 0x84) & ~0x06;
 
 	switch (hw_config->io_base)
-	  {
-	  case 0x300:
-		  sb_setmixer(devc, 0x84, bits | 0x04);
-		  break;
-
-	  case 0x330:
-		  sb_setmixer(devc, 0x84, bits | 0x00);
-		  break;
-
-	  default:
-		  sb_setmixer(devc, 0x84, bits | 0x02);		/* Disable MPU */
-		  printk("SB16: Invalid MIDI I/O port %x\n", hw_config->io_base);
-	  }
+	{
+		case 0x300:
+			sb_setmixer(devc, 0x84, bits | 0x04);
+			break;
+
+		case 0x330:
+			sb_setmixer(devc, 0x84, bits | 0x00);
+			break;
+
+		default:
+			sb_setmixer(devc, 0x84, bits | 0x02);		/* Disable MPU */
+			printk(KERN_ERR "SB16: Invalid MIDI I/O port %x\n", hw_config->io_base);
+	}
 }
 #endif
 
-static int
-sb16_set_irq_hw(sb_devc * devc, int level)
+static int sb16_set_irq_hw(sb_devc * devc, int level)
 {
-	int             ival;
+	int ival;
 
 	switch (level)
-	  {
-	  case 5:
-		  ival = 2;
-		  break;
-	  case 7:
-		  ival = 4;
-		  break;
-	  case 9:
-		  ival = 1;
-		  break;
-	  case 10:
-		  ival = 8;
-		  break;
-	  default:
-		  printk("SB16 IRQ%d is not possible\n", level);
-		  return 0;
-	  }
+	{
+		case 5:
+			ival = 2;
+			break;
+		case 7:
+			ival = 4;
+			break;
+		case 9:
+			ival = 1;
+			break;
+		case 10:
+			ival = 8;
+			break;
+		default:
+			printk(KERN_ERR "SB16 IRQ%d is not possible\n", level);
+			return 0;
+	}
 	sb_setmixer(devc, IRQ_NR, ival);
 	return 1;
 }
 
-static void
-relocate_Jazz16(sb_devc * devc, struct address_info *hw_config)
+static void relocate_Jazz16(sb_devc * devc, struct address_info *hw_config)
 {
-	unsigned char   bits = 0;
-	unsigned long   flags;
+	unsigned char bits = 0;
+	unsigned long flags;
 
 	if (jazz16_base != 0 && jazz16_base != hw_config->io_base)
 		return;
 
 	switch (hw_config->io_base)
-	  {
-	  case 0x220:
-		  bits = 1;
-		  break;
-	  case 0x240:
-		  bits = 2;
-		  break;
-	  case 0x260:
-		  bits = 3;
-		  break;
-
-	  default:
-		  return;
-	  }
-
+	{
+		case 0x220:
+			bits = 1;
+			break;
+		case 0x240:
+			bits = 2;
+			break;
+		case 0x260:
+			bits = 3;
+			break;
+		default:
+			return;
+	}
 	bits = jazz16_bits = bits << 5;
-
 	jazz16_base = hw_config->io_base;
 
-/*
- * Magic wake up sequence by writing to 0x201 (aka Joystick port)
- */
+	/*
+	 *	Magic wake up sequence by writing to 0x201 (aka Joystick port)
+	 */
 	save_flags(flags);
 	cli();
 	outb((0xAF), 0x201);
@@ -358,15 +345,13 @@
 	restore_flags(flags);
 }
 
-static int
-init_Jazz16(sb_devc * devc, struct address_info *hw_config)
+static int init_Jazz16(sb_devc * devc, struct address_info *hw_config)
 {
-	char            name[100];
-
-/*
- * First try to check that the card has Jazz16 chip. It identifies itself
- * by returning 0x12 as response to DSP command 0xfa.
- */
+	char name[100];
+	/*
+	 * First try to check that the card has Jazz16 chip. It identifies itself
+	 * by returning 0x12 as response to DSP command 0xfa.
+	 */
 
 	if (!sb_dsp_command(devc, 0xfa))
 		return 0;
@@ -374,32 +359,32 @@
 	if (sb_dsp_get_byte(devc) != 0x12)
 		return 0;
 
-/*
- * OK so far. Now configure the IRQ and DMA channel used by the card.
- */
+	/*
+	 * OK so far. Now configure the IRQ and DMA channel used by the card.
+	 */
 	if (hw_config->irq < 1 || hw_config->irq > 15 ||
 	    jazz_irq_bits[hw_config->irq] == 0)
-	  {
-		  printk("Jazz16: Invalid interrupt (IRQ%d)\n", hw_config->irq);
-		  return 0;
-	  }
+	{
+		printk(KERN_ERR "Jazz16: Invalid interrupt (IRQ%d)\n", hw_config->irq);
+		return 0;
+	}
 	if (hw_config->dma < 0 || hw_config->dma > 3 ||
 	    jazz_dma_bits[hw_config->dma] == 0)
-	  {
-		  printk("Jazz16: Invalid 8 bit DMA (DMA%d)\n", hw_config->dma);
+	{
+		  printk(KERN_ERR "Jazz16: Invalid 8 bit DMA (DMA%d)\n", hw_config->dma);
 		  return 0;
-	  }
+	}
 	if (hw_config->dma2 < 0)
-	  {
-		  printk("Jazz16: No 16 bit DMA channel defined\n");
-		  return 0;
-	  }
+	{
+		printk(KERN_ERR "Jazz16: No 16 bit DMA channel defined\n");
+		return 0;
+	}
 	if (hw_config->dma2 < 5 || hw_config->dma2 > 7 ||
 	    jazz_dma_bits[hw_config->dma2] == 0)
-	  {
-		  printk("Jazz16: Invalid 16 bit DMA (DMA%d)\n", hw_config->dma2);
-		  return 0;
-	  }
+	{
+		printk(KERN_ERR "Jazz16: Invalid 16 bit DMA (DMA%d)\n", hw_config->dma2);
+		return 0;
+	}
 	devc->dma16 = hw_config->dma2;
 
 	if (!sb_dsp_command(devc, 0xfb))
@@ -412,52 +397,45 @@
 	if (!sb_dsp_command(devc, jazz_irq_bits[hw_config->irq]))
 		return 0;
 
-/*
- * Now we have configured a standard Jazz16 device. 
- */
+	/*
+	 * Now we have configured a standard Jazz16 device. 
+	 */
 	devc->model = MDL_JAZZ;
 	strcpy(name, "Jazz16");
 
-
-	hw_config->name = (char *) (sound_mem_blocks[sound_nblocks] = vmalloc(strlen(name + 1)));
-	sound_mem_sizes[sound_nblocks] = strlen(name + 1);
-	if (sound_nblocks < 1024)
-		sound_nblocks++;;
-	if (hw_config->name != NULL)
-		strcpy(hw_config->name, name);
+	hw_config->name = "Jazz16";
 	devc->caps |= SB_NO_MIDI;
 	return 1;
 }
 
-static void
-relocate_ess1688(sb_devc * devc)
+static void relocate_ess1688(sb_devc * devc)
 {
-	unsigned char   bits;
+	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 */
-	  }
+	{
+		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.
- */
+	
+	/*
+	 * ES1688 supports two alternative ways for software address config.
+	 * First try the so called Read-Sequence-Key method.
+	 */
 
 	/* Reset the sequence logic */
 	inb(0x229);
@@ -479,9 +457,9 @@
 		return;
 
 #if 0				/* This causes system lockups (Nokia 386/25 at least) */
-/*
- * The last resort is the system control register method.
- */
+	/*
+	 * The last resort is the system control register method.
+	 */
 
 	outb((0x00), 0xfb);	/* 0xFB is the unlock register */
 	outb((0x00), 0xe0);	/* Select index 0 */
@@ -490,130 +468,123 @@
 #endif
 }
 
-static int
-ess_init(sb_devc * devc, struct address_info *hw_config)
+static int ess_init(sb_devc * devc, struct address_info *hw_config)
 {
-	unsigned char   cfg, irq_bits = 0, dma_bits = 0;
-	int             ess_major = 0, ess_minor = 0;
-	int             i;
-	char            name[100];
+	unsigned char cfg, irq_bits = 0, dma_bits = 0;
+	int ess_major = 0, ess_minor = 0;
+	int i;
+	static char name[100];
 
-/*
- * Try to detect ESS chips.
- */
+	/*
+	 * Try to detect ESS chips.
+	 */
 
 	sb_dsp_command(devc, 0xe7);	/* Return identification */
 
 	for (i = 1000; i; i--)
-	  {
-		  if (inb(DSP_DATA_AVAIL) & 0x80)
-		    {
-			    if (ess_major == 0)
-				    ess_major = inb(DSP_READ);
-			    else
-			      {
-				      ess_minor = inb(DSP_READ);
-				      break;
-			      }
-		    }
-	  }
+	{
+		if (inb(DSP_DATA_AVAIL) & 0x80)
+		{
+			if (ess_major == 0)
+				ess_major = inb(DSP_READ);
+			else
+			{
+				ess_minor = inb(DSP_READ);
+				break;
+			}
+		}
+	}
 
 	if (ess_major == 0)
 		return 0;
 
 	if (ess_major == 0x48 && (ess_minor & 0xf0) == 0x80)
-	  {
-		  sprintf(name, "ESS ES488 AudioDrive (rev %d)",
+	{
+		sprintf(name, "ESS ES488 AudioDrive (rev %d)",
 			  ess_minor & 0x0f);
-		  hw_config->name = name;
-		  devc->model = MDL_SBPRO;
-		  return 1;
-	} else if (ess_major == 0x68 && (ess_minor & 0xf0) == 0x80)
-	  {
-		  char           *chip = "ES688";
-
-		  if ((ess_minor & 0x0f) >= 8)
-			  chip = "ES1688";
-
-		  sprintf(name,
-			  "ESS %s AudioDrive (rev %d)",
-			  chip, ess_minor & 0x0f);
-	} else
+		hw_config->name = name;
+		devc->model = MDL_SBPRO;
+		return 1;
+	}
+	else if (ess_major == 0x68 && (ess_minor & 0xf0) == 0x80)
+	{
+		char *chip = "ES688";
+
+		if ((ess_minor & 0x0f) >= 8)
+			chip = "ES1688";
+
+		sprintf(name,"ESS %s AudioDrive (rev %d)",
+			chip, ess_minor & 0x0f);
+	}
+	else
 		strcpy(name, "Jazz16");
 
 	devc->model = MDL_ESS;
 	devc->submodel = ess_minor & 0x0f;
-
-	hw_config->name = (char *) (sound_mem_blocks[sound_nblocks] = vmalloc(strlen(name + 1)));
-	sound_mem_sizes[sound_nblocks] = strlen(name + 1);
-	if (sound_nblocks < 1024)
-		sound_nblocks++;;
-	if (hw_config->name != NULL)
-		strcpy(hw_config->name, name);
-
-
+	hw_config->name = name;
 	sb_dsp_reset(devc);	/* Turn on extended mode */
 
-/*
- *    Set IRQ configuration register
- */
+	/*
+	 *    Set IRQ configuration register
+	 */
 
 	cfg = 0x50;		/* Enable only DMA counter interrupt */
 
 	switch (devc->irq)
-	  {
-	  case 2:
-	  case 9:
-		  irq_bits = 0;
-		  break;
-
-	  case 5:
-		  irq_bits = 1;
-		  break;
-
-	  case 7:
-		  irq_bits = 2;
-		  break;
-
-	  case 10:
-		  irq_bits = 3;
-		  break;
-
-	  default:
-		  irq_bits = 0;
-		  cfg = 0x10;	/* Disable all interrupts */
-		  printk("\nESS1688: Invalid IRQ %d\n", devc->irq);
-		  return 0;
-	  }
+	{
+		case 2:
+		case 9:
+			irq_bits = 0;
+			break;
+
+		case 5:
+			irq_bits = 1;
+			break;
+
+		case 7:
+			irq_bits = 2;
+			break;
+
+		case 10:
+			irq_bits = 3;
+			break;
+
+		default:
+			irq_bits = 0;
+			cfg = 0x10;	/* Disable all interrupts */
+			printk(KERN_ERR "ESS1688: Invalid IRQ %d\n", devc->irq);
+			return 0;
+	}
 
 	if (!ess_write(devc, 0xb1, cfg | (irq_bits << 2)))
-		printk("\nESS1688: Failed to write to IRQ config register\n");
+		printk(KERN_ERR "ESS1688: Failed to write to IRQ config register\n");
 
-/*
- *    Set DMA configuration register
- */
+	/*
+	 *    Set DMA configuration register
+	 */
 
 	cfg = 0x50;		/* Extended mode DMA enable */
 
 	if (devc->dma8 > 3 || devc->dma8 < 0 || devc->dma8 == 2)
-	  {
-		  dma_bits = 0;
-		  cfg = 0x00;	/* Disable all DMA */
-		  printk("\nESS1688: Invalid DMA %d\n", devc->dma8);
-	} else
-	  {
-		  if (devc->dma8 == 3)
-			  dma_bits = 3;
-		  else
-			  dma_bits = devc->dma8 + 1;
-	  }
+	{
+		dma_bits = 0;
+		cfg = 0x00;	/* Disable all DMA */
+		printk(KERN_ERR "ESS1688: Invalid DMA %d\n", devc->dma8);
+	}
+	else
+	{
+		if (devc->dma8 == 3)
+			dma_bits = 3;
+		else
+			dma_bits = devc->dma8 + 1;
+	}
 
 	if (!ess_write(devc, 0xb2, cfg | (dma_bits << 2)))
-		printk("\nESS1688: Failed to write to DMA config register\n");
+		printk(KERN_ERR "ESS1688: Failed to write to DMA config register\n");
 
-/*
- * Enable joystick and OPL3
- */
+	/*
+	 *	Enable joystick and OPL3
+	 */
 
 	cfg = sb_getmixer(devc, 0x40);
 	sb_setmixer(devc, 0x40, cfg | 0x03);
@@ -623,26 +594,27 @@
 	return 1;
 }
 
-int
-sb_dsp_detect(struct address_info *hw_config)
+int sb_dsp_detect(struct address_info *hw_config)
 {
-	sb_devc         sb_info;
-	sb_devc        *devc = &sb_info;
+	sb_devc sb_info;
+	sb_devc *devc = &sb_info;
 
 	sb_info.my_mididev = -1;
 	sb_info.my_mixerdev = -1;
 	sb_info.my_dev = -1;
-/*
- * Initialize variables 
- */
+
+	/*
+	 * Initialize variables 
+	 */
+	
 	DDB(printk("sb_dsp_detect(%x) entered\n", hw_config->io_base));
 	if (check_region(hw_config->io_base, 16))
-	  {
+	{
 #ifdef MODULE
-		  printk("sb: I/O region in use.\n");
+		printk(KERN_INFO "sb: I/O region in use.\n");
 #endif
-		  return 0;
-	  }
+		return 0;
+	}
 	memset((char *) &sb_info, 0, sizeof(sb_info));	/* Zero everything */
 
 	devc->type = hw_config->card_subtype;
@@ -654,24 +626,24 @@
 	devc->dma16 = -1;
 
 	if (acer)
-	  {
-		  cli();
-		  inb(devc->base + 0x09);
-		  inb(devc->base + 0x09);
-		  inb(devc->base + 0x09);
-		  inb(devc->base + 0x0b);
-		  inb(devc->base + 0x09);
-		  inb(devc->base + 0x0b);
-		  inb(devc->base + 0x09);
-		  inb(devc->base + 0x09);
-		  inb(devc->base + 0x0b);
-		  inb(devc->base + 0x09);
-		  inb(devc->base + 0x00);
-		  sti();
-	  }
-/*
- * Detect the device
- */
+	{
+		cli();
+		inb(devc->base + 0x09);
+		inb(devc->base + 0x09);
+		inb(devc->base + 0x09);
+		inb(devc->base + 0x0b);
+		inb(devc->base + 0x09);
+		inb(devc->base + 0x0b);
+		inb(devc->base + 0x09);
+		inb(devc->base + 0x09);
+		inb(devc->base + 0x0b);
+		inb(devc->base + 0x09);
+		inb(devc->base + 0x00);
+		sti();
+	}
+	/*
+	 * Detect the device
+	 */
 
 	if (sb_dsp_reset(devc))
 		dsp_get_vers(devc);
@@ -686,36 +658,36 @@
 		relocate_ess1688(devc);
 
 	if (!sb_dsp_reset(devc))
-	  {
-		  DDB(printk("SB reset failed\n"));
+	{
+		DDB(printk("SB reset failed\n"));
 #ifdef MODULE
-		  printk("sb: dsp reset failed.\n");
+		printk(KERN_INFO "sb: dsp reset failed.\n");
 #endif
-		  return 0;
-	  }
+		return 0;
+	}
 	if (devc->major == 0)
 		dsp_get_vers(devc);
 
 	if (devc->major == 3 && devc->minor == 1)
-	  {
-		  if (devc->type == MDL_AZTECH)		/* SG Washington? */
-		    {
-			    if (sb_dsp_command(devc, 0x09))
-				    if (sb_dsp_command(devc, 0x00))	/* Enter WSS mode */
-				      {
-					      int             i;
-
-					      /* Have some delay */
-					      for (i = 0; i < 10000; i++)
-						      inb(DSP_DATA_AVAIL);
-					      devc->caps = SB_NO_AUDIO | SB_NO_MIDI;	/* Mixer only */
-					      devc->model = MDL_AZTECH;
-				      }
-		    }
-	  }
-/*
- * Save device information for sb_dsp_init()
- */
+	{
+		if (devc->type == MDL_AZTECH)		/* SG Washington? */
+		{
+			if (sb_dsp_command(devc, 0x09))
+				if (sb_dsp_command(devc, 0x00))	/* Enter WSS mode */
+				{
+					int i;
+
+					/* Have some delay */
+					for (i = 0; i < 10000; i++)
+						inb(DSP_DATA_AVAIL);
+					devc->caps = SB_NO_AUDIO | SB_NO_MIDI;	/* Mixer only */
+					devc->model = MDL_AZTECH;
+				}
+		}
+	}
+	/*
+	 * Save device information for sb_dsp_init()
+	 */
 
 
 	detected_devc = (sb_devc *) (sound_mem_blocks[sound_nblocks] = vmalloc(sizeof(sb_devc)));
@@ -724,22 +696,20 @@
 		sound_nblocks++;;
 
 	if (detected_devc == NULL)
-	  {
-		  printk(KERN_ERR "sb: Can't allocate memory for device information\n");
-		  return 0;
-	  }
+	{
+		printk(KERN_ERR "sb: Can't allocate memory for device information\n");
+		return 0;
+	}
 	memcpy((char *) detected_devc, (char *) devc, sizeof(sb_devc));
-
 	MDB(printk("SB %d.%d detected OK (%x)\n", devc->major, devc->minor, hw_config->io_base));
 	return 1;
 }
 
-void
-sb_dsp_init(struct address_info *hw_config)
+void sb_dsp_init(struct address_info *hw_config)
 {
-	sb_devc        *devc;
-	char            name[100];
-	extern int      sb_be_quiet;
+	sb_devc *devc;
+	char name[100];
+	extern int sb_be_quiet;
 
 /*
  * Check if we had detected a SB device earlier
@@ -748,130 +718,131 @@
 	name[0] = 0;
 
 	if (detected_devc == NULL)
-	  {
-		  MDB(printk("No detected device\n"));
-		  return;
-	  }
+	{
+		MDB(printk("No detected device\n"));
+		return;
+	}
 	devc = detected_devc;
 	detected_devc = NULL;
 
 	if (devc->base != hw_config->io_base)
-	  {
-		  DDB(printk("I/O port mismatch\n"));
-		  return;
-	  }
-/*
- * Now continue initialization of the device
- */
+	{
+		DDB(printk("I/O port mismatch\n"));
+		return;
+	}
+	/*
+	 * Now continue initialization of the device
+	 */
 	devc->dev = sound_alloc_audiodev();
 	if (devc->dev == -1)
-	  {
-		  printk(KERN_WARNING "sb: too many audio devices.\n");
-		  return;
-	  }
+	{
+		printk(KERN_WARNING "sb: too many audio devices.\n");
+		return;
+	}
 	devc->caps = hw_config->driver_use_1;
 
-	if (!(devc->caps & SB_NO_AUDIO && devc->caps & SB_NO_MIDI) &&
-	    hw_config->irq > 0)
-	  {			/* IRQ setup */
-		  if (snd_set_irq_handler(hw_config->irq,
-				  sbintr, "soundblaster", devc->osp) < 0)
-		    {
-			    printk(KERN_ERR "SB: Can't allocate IRQ%d\n", hw_config->irq);
-			    sound_unload_audiodev(devc->dev);
-			    return;
-		    }
-		  irq2devc[hw_config->irq] = devc;
-		  devc->irq_ok = 0;
-
-		  if (devc->major == 4)
-			  if (!sb16_set_irq_hw(devc, devc->irq))	/* Unsupported IRQ */
-			    {
-				    snd_release_irq(devc->irq);
-				    sound_unload_audiodev(devc->dev);
-				    irq2devc[hw_config->irq] = NULL;
-				    return;
-			    }
-		  if ((devc->type == 0 || devc->type == MDL_ESS) &&
-		      devc->major == 3 && devc->minor == 1)
-		    {		/* Handle various chipsets which claim they are SB Pro compatible */
-			    if ((devc->type != 0 && devc->type != MDL_ESS) ||
+	if (!(devc->caps & SB_NO_AUDIO && devc->caps & SB_NO_MIDI) && hw_config->irq > 0)
+	{			/* IRQ setup */
+		if (snd_set_irq_handler(hw_config->irq, sbintr, "soundblaster", devc->osp) < 0)
+		{
+			printk(KERN_ERR "SB: Can't allocate IRQ%d\n", hw_config->irq);
+			sound_unload_audiodev(devc->dev);
+			return;
+		}
+		irq2devc[hw_config->irq] = devc;
+		devc->irq_ok = 0;
+
+		if (devc->major == 4)
+			if (!sb16_set_irq_hw(devc, devc->irq))	/* Unsupported IRQ */
+			{
+				snd_release_irq(devc->irq);
+				sound_unload_audiodev(devc->dev);
+				irq2devc[hw_config->irq] = NULL;
+				return;
+			}
+		if ((devc->type == 0 || devc->type == MDL_ESS) &&
+			devc->major == 3 && devc->minor == 1)
+		{		/* Handle various chipsets which claim they are SB Pro compatible */
+			if ((devc->type != 0 && devc->type != MDL_ESS) ||
 				!ess_init(devc, hw_config))
-				    if ((devc->type != 0 && devc->type != MDL_JAZZ &&
+			{
+				if ((devc->type != 0 && devc->type != MDL_JAZZ &&
 					 devc->type != MDL_SMW) || !init_Jazz16(devc, hw_config))
-				      {
-					      DDB(printk("This is a genuine SB Pro\n"));
-				      }
-		    }
-#if defined(__SMP__) || defined(__FreeBSD__)
-		  /* Skip IRQ detection if SMP (doesn't work) */
-		  devc->irq_ok = 1;
+				{
+					DDB(printk("This is a genuine SB Pro\n"));
+				}
+			}
+		}
+#if defined(__SMP__)
+		/* Skip IRQ detection if SMP (doesn't work) */
+		devc->irq_ok = 1;
 #else
-		  if (devc->major == 4 && devc->minor <= 11)	/* Won't work */
-			  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 */
-				      {
-					      int             i;
-
-					      for (i = 0; !devc->irq_ok && i < 10000; i++);
-				      }
-			    if (!devc->irq_ok)
-			      {
-				      printk("sb: Interrupt test on IRQ%d failed - Probable IRQ conflict\n", devc->irq);
-			    } else
-			      {
-				      DDB(printk("IRQ test OK (IRQ%d)\n", devc->irq));
-			      }
-
-		    }
+		if (devc->major == 4 && devc->minor <= 11)	/* Won't work */
+			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 */
+				{
+					int i;
+
+					for (i = 0; !devc->irq_ok && i < 10000; i++);
+				}
+			}
+			if (!devc->irq_ok)
+				printk(KERN_WARNING "sb: Interrupt test on IRQ%d failed - Probable IRQ conflict\n", devc->irq);
+			else
+			{
+				DDB(printk("IRQ test OK (IRQ%d)\n", devc->irq));
+			}
+		}
 #endif				/* __SMP__ */
-	  }			/* IRQ setup */
+	}			/* IRQ setup */
 	request_region(hw_config->io_base, 16, "soundblaster");
 
 	switch (devc->major)
-	  {
-	  case 1:		/* SB 1.0 or 1.5 */
-		  devc->model = hw_config->card_subtype = MDL_SB1;
-		  break;
-
-	  case 2:		/* SB 2.x */
-		  if (devc->minor == 0)
-			  devc->model = hw_config->card_subtype = MDL_SB2;
-		  else
-			  devc->model = hw_config->card_subtype = MDL_SB201;
-		  break;
-
-	  case 3:		/* SB Pro and most clones */
-		  if (devc->model == 0)
-		    {
-			    devc->model = hw_config->card_subtype = MDL_SBPRO;
-			    if (hw_config->name == NULL)
-				    hw_config->name = "Sound Blaster Pro (8 BIT ONLY)";
-		    }
-		  break;
-
-	  case 4:
-		  devc->model = hw_config->card_subtype = MDL_SB16;
-		  if (hw_config->name == NULL)
-			  hw_config->name = "Sound Blaster 16";
-
-		  if (hw_config->dma2 == -1)
-			  devc->dma16 = devc->dma8;
-		  else if (hw_config->dma2 < 5 || hw_config->dma2 > 7)
-		    {
-			    printk("SB16: Bad or missing 16 bit DMA channel\n");
-			    devc->dma16 = devc->dma8;
-		  } else
-			  devc->dma16 = hw_config->dma2;
-
-		  sb16_set_dma_hw(devc);
-		  devc->caps |= SB_NO_MIDI;
-	  }
+	{
+		case 1:		/* SB 1.0 or 1.5 */
+			devc->model = hw_config->card_subtype = MDL_SB1;
+			break;
+
+		case 2:		/* SB 2.x */
+			if (devc->minor == 0)
+				devc->model = hw_config->card_subtype = MDL_SB2;
+			else
+				devc->model = hw_config->card_subtype = MDL_SB201;
+			break;
+
+		case 3:		/* SB Pro and most clones */
+			if (devc->model == 0)
+			{
+				devc->model = hw_config->card_subtype = MDL_SBPRO;
+				if (hw_config->name == NULL)
+					hw_config->name = "Sound Blaster Pro (8 BIT ONLY)";
+			}
+			break;
+
+		case 4:
+			devc->model = hw_config->card_subtype = MDL_SB16;
+			if (hw_config->name == NULL)
+				hw_config->name = "Sound Blaster 16";
+
+			if (hw_config->dma2 == -1)
+				devc->dma16 = devc->dma8;
+			else if (hw_config->dma2 < 5 || hw_config->dma2 > 7)
+			{
+				printk(KERN_WARNING  "SB16: Bad or missing 16 bit DMA channel\n");
+				devc->dma16 = devc->dma8;
+			}
+			else
+				devc->dma16 = hw_config->dma2;
+
+			sb16_set_dma_hw(devc);
+			devc->caps |= SB_NO_MIDI;
+	}
 
 	if (!(devc->caps & SB_NO_MIXER))
 		if (devc->major == 3 || devc->major == 4)
@@ -888,62 +859,63 @@
 	sprintf(name, "%s (%d.%d)", hw_config->name, devc->major, devc->minor);
 	conf_printf(name, hw_config);
 
-/*
- * Assuming that a soundcard is Sound Blaster (compatible) is the most common
- * configuration error and the mother of all problems. Usually soundcards
- * emulate SB Pro but in addition they have a 16 bit native mode which should be
- * used in Unix. See Readme.cards for more information about configuring OSS/Free
- * properly.
- */
+	/*
+	 * Assuming that a soundcard is Sound Blaster (compatible) is the most common
+	 * configuration error and the mother of all problems. Usually soundcards
+	 * emulate SB Pro but in addition they have a 16 bit native mode which should be
+	 * used in Unix. See Readme.cards for more information about configuring OSS/Free
+	 * properly.
+	 */
 	if (devc->model <= MDL_SBPRO)
+	{
 		if (devc->major == 3 && devc->minor != 1)	/* "True" SB Pro should have v3.1 (rare ones may have 3.2). */
-		  {
-			  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("several years old (8 bit only device)\n");
-			  printk("or alternatively the sound driver is incorrectly configured.\n");
-		  }
+		{
+			printk(KERN_INFO "This soundcard may not be fully Sound Blaster Pro compatible.\n");
+			printk(KERN_INFO "In many cases there is another way to configure OSS so that\n");
+			printk(KERN_INFO "it works properly with OSS (for example in 16 bit mode).\n");
+			printk(KERN_INFO "Please ignore this message if you _really_ have a SB Pro.\n");
+		}
+		else if (!sb_be_quiet && devc->model == MDL_SBPRO)
+		{
+			printk(KERN_INFO "SB DSP version is just %d.%d which means that your card is\n", devc->major, devc->minor);
+			printk(KERN_INFO "several years old (8 bit only device) or alternatively the sound driver\n");
+			printk(KERN_INFO "is incorrectly configured.\n");
+		}
+	}
 	hw_config->card_subtype = devc->model;
 	last_devc = devc;	/* For SB MPU detection */
 
 	if (!(devc->caps & SB_NO_AUDIO) && devc->dma8 >= 0)
-	  {
-		  if (sound_alloc_dma(devc->dma8, "SoundBlaster8"))
-		    {
-			    printk("SB: Can't allocate 8 bit DMA channel %d\n", devc->dma8);
-		    }
-		  if (devc->dma16 >= 0 && devc->dma16 != devc->dma8)
-			  if (sound_alloc_dma(devc->dma16, "SoundBlaster16"))
-			    {
-				    printk("SB: Can't allocate 16 bit DMA channel %d\n", devc->dma16);
-			    }
-		  sb_audio_init(devc, name);
-	} else
-	  {
-		  MDB(printk("sb: No audio devices found.\n"));
-	  }
+	{
+		if (sound_alloc_dma(devc->dma8, "SoundBlaster8"))
+		{
+			printk(KERN_WARNING "SB: Can't allocate 8 bit DMA channel %d\n", devc->dma8);
+		}
+		if (devc->dma16 >= 0 && devc->dma16 != devc->dma8)
+			if (sound_alloc_dma(devc->dma16, "SoundBlaster16"))
+			{
+				printk(KERN_WARNING "SB: Can't allocate 16 bit DMA channel %d\n", devc->dma16);
+			}
+		sb_audio_init(devc, name);
+	}
+	else
+	{
+		MDB(printk("sb: No audio devices found.\n"));
+	}
 }
 
-void
-sb_dsp_disable_midi(int io_base)
+void sb_dsp_disable_midi(int io_base)
 {
 }
 
-void
-sb_dsp_disable_recording(int io_base)
+void sb_dsp_disable_recording(int io_base)
 {
 }
 
-void
-sb_dsp_unload(struct address_info *hw_config)
+void sb_dsp_unload(struct address_info *hw_config)
 {
-	sb_devc        *devc;
-	int             irq = hw_config->irq;
+	sb_devc *devc;
+	int irq = hw_config->irq;
 
 	if (irq < 0)
 		irq *= -1;
@@ -954,36 +926,33 @@
 		devc = NULL;
 
 	if (devc && devc->base == hw_config->io_base)
-	  {
-		  release_region(devc->base, 16);
+	{
+		release_region(devc->base, 16);
 
-		  if (!(devc->caps & SB_NO_AUDIO))
-		    {
-			    sound_free_dma(devc->dma8);
-
-			    if (devc->dma16 >= 0)
-				    sound_free_dma(devc->dma16);
-		    }
-		  if (!(devc->caps & SB_NO_AUDIO && devc->caps & SB_NO_MIDI) &&
-		      devc->irq > 0)
-		    {
-			    snd_release_irq(devc->irq);
-			    irq2devc[devc->irq] = NULL;
-			    sound_unload_mixerdev(devc->my_mixerdev);
-			    sound_unload_mididev(devc->my_mididev);
-			    sound_unload_audiodev(devc->my_dev);
-		    }
-	} else
+		if (!(devc->caps & SB_NO_AUDIO))
+		{
+			sound_free_dma(devc->dma8);
+			if (devc->dma16 >= 0)
+				sound_free_dma(devc->dma16);
+		}
+		if (!(devc->caps & SB_NO_AUDIO && devc->caps & SB_NO_MIDI) && devc->irq > 0)
+		{
+			snd_release_irq(devc->irq);
+			irq2devc[devc->irq] = NULL;
+			sound_unload_mixerdev(devc->my_mixerdev);
+			sound_unload_mididev(devc->my_mididev);
+			sound_unload_audiodev(devc->my_dev);
+		}
+	}
+	else
 		release_region(hw_config->io_base, 16);
-
 }
 
 /*
- * Mixer access routines
+ *	Mixer access routines
  */
 
-void
-sb_setmixer(sb_devc * devc, unsigned int port, unsigned int value)
+void sb_setmixer(sb_devc * devc, unsigned int port, unsigned int value)
 {
 	unsigned long   flags;
 
@@ -991,43 +960,38 @@
 	cli();
 	outb(((unsigned char) (port & 0xff)), MIXER_ADDR);
 
-	tenmicrosec(devc->osp);
-	tenmicrosec(devc->osp);
+	udelay(20);
 	outb(((unsigned char) (value & 0xff)), MIXER_DATA);
-	tenmicrosec(devc->osp);
-	tenmicrosec(devc->osp);
+	udelay(20);
 	restore_flags(flags);
 }
 
-unsigned int
-sb_getmixer(sb_devc * devc, unsigned int port)
+unsigned int sb_getmixer(sb_devc * devc, unsigned int port)
 {
-	unsigned int    val;
-	unsigned long   flags;
+	unsigned int val;
+	unsigned long flags;
 
 	save_flags(flags);
 	cli();
 	outb(((unsigned char) (port & 0xff)), MIXER_ADDR);
 
-	tenmicrosec(devc->osp);
-	tenmicrosec(devc->osp);
+	udelay(20);
 	val = inb(MIXER_DATA);
-	tenmicrosec(devc->osp);
-	tenmicrosec(devc->osp);
+	udelay(20);
 	restore_flags(flags);
 
 	return val;
 }
 
 #if defined(CONFIG_MIDI)
+
 /*
- * MPU401 MIDI initialization.
+ *	MPU401 MIDI initialization.
  */
 
-static void
-smw_putmem(sb_devc * devc, int base, int addr, unsigned char val)
+static void smw_putmem(sb_devc * devc, int base, int addr, unsigned char val)
 {
-	unsigned long   flags;
+	unsigned long flags;
 
 	save_flags(flags);
 	cli();
@@ -1039,11 +1003,10 @@
 	restore_flags(flags);
 }
 
-static unsigned char
-smw_getmem(sb_devc * devc, int base, int addr)
+static unsigned char smw_getmem(sb_devc * devc, int base, int addr)
 {
-	unsigned long   flags;
-	unsigned char   val;
+	unsigned long flags;
+	unsigned char val;
 
 	save_flags(flags);
 	cli();
@@ -1056,42 +1019,40 @@
 	return val;
 }
 
-static int
-smw_midi_init(sb_devc * devc, struct address_info *hw_config)
+static int smw_midi_init(sb_devc * devc, struct address_info *hw_config)
 {
-	int             mpu_base = hw_config->io_base;
-	int             mp_base = mpu_base + 4;		/* Microcontroller base */
-	int             i;
-	unsigned char   control;
+	int mpu_base = hw_config->io_base;
+	int mp_base = mpu_base + 4;		/* Microcontroller base */
+	int i;
+	unsigned char control;
 
 
 	/*
-	   *  Reset the microcontroller so that the RAM can be accessed
+	 *  Reset the microcontroller so that the RAM can be accessed
 	 */
 
 	control = inb(mpu_base + 7);
 	outb((control | 3), mpu_base + 7);	/* Set last two bits to 1 (?) */
 	outb(((control & 0xfe) | 2), mpu_base + 7);	/* xxxxxxx0 resets the mc */
 
-	for (i = 0; i < 300; i++)	/* Wait at least 1ms */
-		tenmicrosec(devc->osp);
+	udelay(3000);	/* Wait at least 1ms */
 
 	outb((control & 0xfc), mpu_base + 7);	/* xxxxxx00 enables RAM */
 
 	/*
-	   *  Detect microcontroller by probing the 8k RAM area
+	 *  Detect microcontroller by probing the 8k RAM area
 	 */
 	smw_putmem(devc, mp_base, 0, 0x00);
 	smw_putmem(devc, mp_base, 1, 0xff);
-	tenmicrosec(devc->osp);
+	udelay(10);
 
 	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)));
-		  return 0;	/* No RAM */
-	  }
+	{
+		DDB(printk("SM Wave: No microcontroller RAM detected (%02x, %02x)\n", smw_getmem(devc, mp_base, 0), smw_getmem(devc, mp_base, 1)));
+		return 0;	/* No RAM */
+	}
 	/*
-	   *  There is RAM so assume it's really a SM Wave
+	 *  There is RAM so assume it's really a SM Wave
 	 */
 
 	devc->model = MDL_SMW;
@@ -1099,179 +1060,173 @@
 
 #ifdef MODULE
 	if (!smw_ucode)
-	  {
-		  extern void    *smw_free;
+	{
+		extern void *smw_free;
 
-		  smw_ucodeLen = mod_firmware_load("/etc/sound/midi0001.bin", (void *) &smw_ucode);
-		  smw_free = smw_ucode;
-	  }
+		smw_ucodeLen = mod_firmware_load("/etc/sound/midi0001.bin", (void *) &smw_ucode);
+		smw_free = smw_ucode;
+	}
 #endif
 	if (smw_ucodeLen > 0)
-	  {
-		  if (smw_ucodeLen != 8192)
-		    {
-			    printk("\nSM Wave: Invalid microcode (MIDI0001.BIN) length\n");
-			    return 1;
-		    }
-		  /*
-		   *  Download microcode
-		   */
-
-		  for (i = 0; i < 8192; i++)
-			  smw_putmem(devc, mp_base, i, smw_ucode[i]);
-
-		  /*
-		   *  Verify microcode
-		   */
-
-		  for (i = 0; i < 8192; i++)
-			  if (smw_getmem(devc, mp_base, i) != smw_ucode[i])
-			    {
-				    printk("SM Wave: Microcode verification failed\n");
-				    return 0;
-			    }
-	  }
+	{
+		if (smw_ucodeLen != 8192)
+		{
+			printk(KERN_ERR "SM Wave: Invalid microcode (MIDI0001.BIN) length\n");
+			return 1;
+		}
+		/*
+		 *  Download microcode
+		 */
+
+		for (i = 0; i < 8192; i++)
+			smw_putmem(devc, mp_base, i, smw_ucode[i]);
+
+		/*
+		 *  Verify microcode
+		 */
+
+		for (i = 0; i < 8192; i++)
+			if (smw_getmem(devc, mp_base, i) != smw_ucode[i])
+			{
+				printk(KERN_ERR "SM Wave: Microcode verification failed\n");
+				return 0;
+			}
+	}
 	control = 0;
 #ifdef SMW_SCSI_IRQ
 	/*
-	   * Set the SCSI interrupt (IRQ2/9, IRQ3 or IRQ10). The SCSI interrupt
-	   * is disabled by default.
-	   *
-	   * BTW the Zilog 5380 SCSI controller is located at MPU base + 0x10.
+	 * Set the SCSI interrupt (IRQ2/9, IRQ3 or IRQ10). The SCSI interrupt
+	 * is disabled by default.
+	 *
+	 * FIXME - make this a module option
+	 *
+	 * BTW the Zilog 5380 SCSI controller is located at MPU base + 0x10.
 	 */
 	{
-		static unsigned char scsi_irq_bits[] =
-		{0, 0, 3, 1, 0, 0, 0, 0, 0, 3, 2, 0, 0, 0, 0, 0};
-
+		static unsigned char scsi_irq_bits[] = {
+			0, 0, 3, 1, 0, 0, 0, 0, 0, 3, 2, 0, 0, 0, 0, 0
+		};
 		control |= scsi_irq_bits[SMW_SCSI_IRQ] << 6;
 	}
 #endif
 
 #ifdef SMW_OPL4_ENABLE
 	/*
-	   *  Make the OPL4 chip visible on the PC bus at 0x380.
-	   *
-	   *  There is no need to enable this feature since this driver
-	   *  doesn't support OPL4 yet. Also there is no RAM in SM Wave so
-	   *  enabling OPL4 is pretty useless.
+	 *  Make the OPL4 chip visible on the PC bus at 0x380.
+	 *
+	 *  There is no need to enable this feature since this driver
+	 *  doesn't support OPL4 yet. Also there is no RAM in SM Wave so
+	 *  enabling OPL4 is pretty useless.
 	 */
 	control |= 0x10;	/* Uses IRQ12 if bit 0x20 == 0 */
 	/* control |= 0x20;      Uncomment this if you want to use IRQ7 */
 #endif
-
 	outb((control | 0x03), mpu_base + 7);	/* xxxxxx11 restarts */
 	hw_config->name = "SoundMan Wave";
 	return 1;
 }
 
-static int
-ess_midi_init(sb_devc * devc, struct address_info *hw_config)
+static int ess_midi_init(sb_devc * devc, struct address_info *hw_config)
 {
 	unsigned char   cfg, tmp;
 
 	cfg = sb_getmixer(devc, 0x40) & 0x03;
 
 	if (devc->submodel < 8)
-	  {
-		  sb_setmixer(devc, 0x40, cfg | 0x03);	/* Enable OPL3 & joystick */
-		  return 0;	/* ES688 doesn't support MPU401 mode */
-	  }
+	{
+		sb_setmixer(devc, 0x40, cfg | 0x03);	/* Enable OPL3 & joystick */
+		return 0;	/* ES688 doesn't support MPU401 mode */
+	}
 	tmp = (hw_config->io_base & 0x0f0) >> 4;
 
 	if (tmp > 3)
-	  {
-		  sb_setmixer(devc, 0x40, cfg);
-		  return 0;
-	  }
+	{
+		sb_setmixer(devc, 0x40, cfg);
+		return 0;
+	}
 	cfg |= tmp << 3;
 
 	tmp = 1;		/* MPU enabled without interrupts */
 
 	switch (hw_config->irq)
-	  {
-	  case 9:
-		  tmp = 0x4;
-		  break;
-	  case 5:
-		  tmp = 0x5;
-		  break;
-	  case 7:
-		  tmp = 0x6;
-		  break;
-	  case 10:
-		  tmp = 0x7;
-		  break;
-	  default:
-		  return 0;
-	  }
+	{
+		case 9:
+			tmp = 0x4;
+			break;
+		case 5:
+			tmp = 0x5;
+			break;
+		case 7:
+			tmp = 0x6;
+			break;
+		case 10:
+			tmp = 0x7;
+			break;
+		default:
+			return 0;
+	}
 
 	cfg |= tmp << 5;
-
 	sb_setmixer(devc, 0x40, cfg | 0x03);
 	return 1;
 }
 
-static int
-init_Jazz16_midi(sb_devc * devc, struct address_info *hw_config)
+static int init_Jazz16_midi(sb_devc * devc, struct address_info *hw_config)
 {
-	int             mpu_base = hw_config->io_base;
-	int             sb_base = devc->base;
-	int             irq = hw_config->irq;
+	int mpu_base = hw_config->io_base;
+	int sb_base = devc->base;
+	int irq = hw_config->irq;
 
-	unsigned char   bits = 0;
-	unsigned long   flags;
+	unsigned char bits = 0;
+	unsigned long flags;
 
 	if (irq < 0)
 		irq *= -1;
 
 	if (irq < 1 || irq > 15 ||
 	    jazz_irq_bits[irq] == 0)
-	  {
-		  printk("Jazz16: Invalid MIDI interrupt (IRQ%d)\n", irq);
-		  return 0;
-	  }
+	{
+		printk(KERN_ERR "Jazz16: Invalid MIDI interrupt (IRQ%d)\n", irq);
+		return 0;
+	}
 	switch (sb_base)
-	  {
-	  case 0x220:
-		  bits = 1;
-		  break;
-	  case 0x240:
-		  bits = 2;
-		  break;
-	  case 0x260:
-		  bits = 3;
-		  break;
-
-	  default:
-		  return 0;
-	  }
-
+	{
+		case 0x220:
+			bits = 1;
+			break;
+		case 0x240:
+			bits = 2;
+			break;
+		case 0x260:
+			bits = 3;
+			break;
+		default:
+			return 0;
+	}
 	bits = jazz16_bits = bits << 5;
-
 	switch (mpu_base)
-	  {
-	  case 0x310:
-		  bits |= 1;
-		  break;
-	  case 0x320:
-		  bits |= 2;
-		  break;
-	  case 0x330:
-		  bits |= 3;
-		  break;
-
-	  default:
-		  printk("Jazz16: Invalid MIDI I/O port %x\n", mpu_base);
-		  return 0;
-	  }
-/*
- * Magic wake up sequence by writing to 0x201 (aka Joystick port)
- */
+	{
+		case 0x310:
+			bits |= 1;
+			break;
+		case 0x320:
+			bits |= 2;
+			break;
+		case 0x330:
+			bits |= 3;
+			break;
+		default:
+			printk(KERN_ERR "Jazz16: Invalid MIDI I/O port %x\n", mpu_base);
+			return 0;
+	}
+	/*
+	 *	Magic wake up sequence by writing to 0x201 (aka Joystick port)
+	 */
 	save_flags(flags);
 	cli();
-	outb((0xAF), 0x201);
-	outb((0x50), 0x201);
-	outb((bits), 0x201);
+	outb(0xAF, 0x201);
+	outb(0x50, 0x201);
+	outb(bits, 0x201);
 	restore_flags(flags);
 
 	hw_config->name = "Jazz16";
@@ -1291,19 +1246,17 @@
 	return 1;
 }
 
-void
-attach_sbmpu(struct address_info *hw_config)
+void attach_sbmpu(struct address_info *hw_config)
 {
 #if defined(CONFIG_MIDI) && (defined(CONFIG_UART401)||defined(CONFIG_UART401_MODULE))
 	attach_uart401(hw_config);
 #endif
 }
 
-int
-probe_sbmpu(struct address_info *hw_config)
+int probe_sbmpu(struct address_info *hw_config)
 {
 #if defined(CONFIG_MIDI) && (defined(CONFIG_UART401)||defined(CONFIG_UART401_MODULE))
-	sb_devc        *devc = last_devc;
+	sb_devc *devc = last_devc;
 
 	if (last_devc == NULL)
 		return 0;
@@ -1314,53 +1267,51 @@
 		return 0;
 
 	if (check_region(hw_config->io_base, 4))
-	  {
-		  printk("sbmpu: I/O port conflict (%x)\n", hw_config->io_base);
-		  return 0;
-	  }
+	{
+		printk(KERN_ERR "sbmpu: I/O port conflict (%x)\n", hw_config->io_base);
+		return 0;
+	}
 	switch (devc->model)
-	  {
-	  case MDL_SB16:
-		  if (hw_config->io_base != 0x300 && hw_config->io_base != 0x330)
-		    {
-			    printk("SB16: Invalid MIDI port %x\n", hw_config->irq);
-			    return 0;
-		    }
-		  hw_config->name = "Sound Blaster 16";
-		  hw_config->irq = -devc->irq;
+	{
+		case MDL_SB16:
+			if (hw_config->io_base != 0x300 && hw_config->io_base != 0x330)
+			{
+				printk(KERN_ERR "SB16: Invalid MIDI port %x\n", hw_config->irq);
+				return 0;
+			}
+			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);
+			if (devc->minor > 12)		/* What is Vibra's version??? */
+				sb16_set_mpu_port(devc, hw_config);
 #endif
-		  break;
+			break;
 
-	  case MDL_ESS:
-		  if (hw_config->irq < 3 || hw_config->irq == devc->irq)
-			  hw_config->irq = -devc->irq;
-		  if (!ess_midi_init(devc, hw_config))
-			  return 0;
-		  hw_config->name = "ESS ES1688";
-		  break;
-
-	  case MDL_JAZZ:
-		  if (hw_config->irq < 3 || hw_config->irq == devc->irq)
-			  hw_config->irq = -devc->irq;
-		  if (!init_Jazz16_midi(devc, hw_config))
-			  return 0;
-		  break;
-
-	  default:
-		  return 0;
-	  }
+		case MDL_ESS:
+			if (hw_config->irq < 3 || hw_config->irq == devc->irq)
+				hw_config->irq = -devc->irq;
+			if (!ess_midi_init(devc, hw_config))
+				return 0;
+			hw_config->name = "ESS ES1688";
+			break;
+
+		case MDL_JAZZ:
+			if (hw_config->irq < 3 || hw_config->irq == devc->irq)
+				hw_config->irq = -devc->irq;
+			if (!init_Jazz16_midi(devc, hw_config))
+				return 0;
+			break;
 
+		default:
+			return 0;
+	}
 	return probe_uart401(hw_config);
 #else
 	return 0;
 #endif
 }
 
-void
-unload_sbmpu(struct address_info *hw_config)
+void unload_sbmpu(struct address_info *hw_config)
 {
 #if defined(CONFIG_MIDI) && (defined(CONFIG_UART401)||defined(CONFIG_UART401_MODULE))
 	unload_uart401(hw_config);
@@ -1368,22 +1319,18 @@
 }
 #else				/* !CONFIG_MIDI */
 
-void
-unload_sbmpu(struct address_info *hw_config)
+void unload_sbmpu(struct address_info *hw_config)
 {
 }
 
-int
-probe_sbmpu(struct address_info *hw_config)
+int probe_sbmpu(struct address_info *hw_config)
 {
 	return 0;
 }
 
-void
-attach_sbmpu(struct address_info *hw_config)
+void attach_sbmpu(struct address_info *hw_config)
 {
 }
 #endif
-
 
 #endif
diff -u --recursive --new-file v2.1.87/linux/drivers/sound/sequencer.c linux/drivers/sound/sequencer.c
--- v2.1.87/linux/drivers/sound/sequencer.c	Thu Feb 12 20:56:10 1998
+++ linux/drivers/sound/sequencer.c	Thu Feb 19 14:46:15 1998
@@ -52,15 +52,7 @@
 static int      seq_mode = SEQ_1;
 
 static struct wait_queue *seq_sleeper = NULL;
-
-static volatile struct snd_wait seq_sleep_flag = {
-	0
-};
-
 static struct wait_queue *midi_sleeper = NULL;
-static volatile struct snd_wait midi_sleep_flag = {
-	0
-};
 
 static int      midi_opened[MAX_MIDI_DEV] = {
 	0
@@ -98,7 +90,7 @@
 #error Too many synthesizer devices enabled.
 #endif
 
-int sequencer_read(int dev, struct fileinfo *file, char *buf, int count)
+int sequencer_read(int dev, struct file *file, char *buf, int count)
 {
 	int c = count, p = 0;
 	int ev_len;
@@ -113,25 +105,13 @@
 
 	if (!iqlen)
 	{
-		unsigned long   tlimit;
-		if ((file->flags & (O_NONBLOCK) ? 1 : 0))
-		{
-			restore_flags(flags);
-			return -EAGAIN;
-		}
-		if (pre_event_timeout)
-			current->timeout = tlimit = jiffies + (pre_event_timeout);
-		else
-			tlimit = (unsigned long) -1;
-		midi_sleep_flag.opts = WK_SLEEP;
-		interruptible_sleep_on(&midi_sleeper);
-		if (!(midi_sleep_flag.opts & WK_WAKEUP))
-		{
-			if (jiffies >= tlimit)
-				midi_sleep_flag.opts |= WK_TIMEOUT;
-		}
-		midi_sleep_flag.opts &= ~WK_SLEEP;
-
+ 		if (file->f_flags & O_NONBLOCK) {
+  			restore_flags(flags);
+  			return -EAGAIN;
+  		}
+ 		current->timeout = pre_event_timeout ? jiffies + pre_event_timeout : 0;
+ 		interruptible_sleep_on(&midi_sleeper);
+ 		current->timeout = 0;
 		if (!iqlen)
 		{
 			restore_flags(flags);
@@ -180,12 +160,7 @@
 	memcpy(&iqueue[iqtail * IEV_SZ], event_rec, len);
 	iqlen++;
 	iqtail = (iqtail + 1) % SEQ_MAX_QUEUE;
-
-	if ((midi_sleep_flag.opts & WK_SLEEP))
-	{
-		midi_sleep_flag.opts = WK_WAKEUP;
-		wake_up(&midi_sleeper);
-	}
+	wake_up(&midi_sleeper);
 	restore_flags(flags);
 }
 
@@ -243,12 +218,12 @@
 	seq_copy_to_input(event_rec, len);
 }
 
-int sequencer_write(int dev, struct fileinfo *file, const char *buf, int count)
+int sequencer_write(int dev, struct file *file, const char *buf, int count)
 {
 	unsigned char event_rec[EV_SZ], ev_code;
 	int p = 0, c, ev_size;
 	int err;
-	int mode = file->mode & O_ACCMODE;
+	int mode = translate_mode(file);
 
 	dev = dev >> 4;
 
@@ -325,7 +300,7 @@
 					/*printk("Sequencer Error: Nonexistent MIDI device %d\n", dev);*/
 					return -ENXIO;
 				}
-				mode = file->mode & O_ACCMODE;
+				mode = translate_mode(file);
 
 				if ((err = midi_devs[dev]->open(dev, mode,
 								sequencer_midi_input, sequencer_midi_output)) < 0)
@@ -337,14 +312,14 @@
 				midi_opened[dev] = 1;
 			}
 		}
-		if (!seq_queue(event_rec, (file->flags & (O_NONBLOCK) ? 1 : 0)))
+		if (!seq_queue(event_rec, (file->f_flags & (O_NONBLOCK) ? 1 : 0)))
 		{
 			int processed = count - c;
 
 			if (!seq_playing)
 				seq_startplay();
 
-			if (!processed && (file->flags & (O_NONBLOCK) ? 1 : 0))
+			if (!processed && (file->f_flags & O_NONBLOCK))
 				return -EAGAIN;
 			else
 				return processed;
@@ -372,15 +347,12 @@
 						 * Give chance to drain the queue
 						 */
 
-	if (!nonblock && qlen >= SEQ_MAX_QUEUE && !(seq_sleep_flag.opts & WK_SLEEP))
-	{
+	if (!nonblock && qlen >= SEQ_MAX_QUEUE && !waitqueue_active(&seq_sleeper)) {
 		/*
 		 * Sleep until there is enough space on the queue
 		 */
-
-		seq_sleep_flag.opts = WK_SLEEP;
+		current->timeout = 0;
 		interruptible_sleep_on(&seq_sleeper);
-		seq_sleep_flag.opts &= ~WK_SLEEP;;
 	}
 	if (qlen >= SEQ_MAX_QUEUE)
 	{
@@ -667,20 +639,8 @@
 		int ret;
 
 		if ((ret = tmr->event(tmr_no, event_rec)) == TIMER_ARMED)
-		{
 			if ((SEQ_MAX_QUEUE - qlen) >= output_threshold)
-			{
-				unsigned long   flags;
-				save_flags(flags);
-				cli();
-				if ((seq_sleep_flag.opts & WK_SLEEP))
-				{
-					seq_sleep_flag.opts = WK_WAKEUP;
-					wake_up(&seq_sleeper);
-				}
-				restore_flags(flags);
-			}
-		}
+				wake_up(&seq_sleeper);
 		return ret;
 	}
 	switch (cmd)
@@ -708,18 +668,7 @@
 					request_sound_timer(time);
 
 				if ((SEQ_MAX_QUEUE - qlen) >= output_threshold)
-				{
-					unsigned long flags;
-
-					save_flags(flags);
-					cli();
-					if ((seq_sleep_flag.opts & WK_SLEEP))
-					{
-						seq_sleep_flag.opts = WK_WAKEUP;
-						wake_up(&seq_sleeper);
-					}
-					restore_flags(flags);
-				}
+					wake_up(&seq_sleeper);
 				return TIMER_ARMED;
 			}
 			break;
@@ -847,20 +796,7 @@
 					request_sound_timer(time);
 
 				if ((SEQ_MAX_QUEUE - qlen) >= output_threshold)
-				{
-					unsigned long   flags;
-
-					save_flags(flags);
-					cli();
-					if ((seq_sleep_flag.opts & WK_SLEEP))
-					{
-						{
-							seq_sleep_flag.opts = WK_WAKEUP;
-							wake_up(&seq_sleeper);
-						};
-					}
-					restore_flags(flags);
-				}
+					wake_up(&seq_sleeper);
 				/*
 				 * The timer is now active and will reinvoke this function
 				 * after the timer expires. Return to the caller now.
@@ -991,20 +927,7 @@
 	seq_playing = 0;
 
 	if ((SEQ_MAX_QUEUE - qlen) >= output_threshold)
-	{
-		unsigned long   flags;
-
-		save_flags(flags);
-		cli();
-		if ((seq_sleep_flag.opts & WK_SLEEP))
-		{
-			{
-				seq_sleep_flag.opts = WK_WAKEUP;
-				wake_up(&seq_sleeper);
-			};
-		}
-		restore_flags(flags);
-	}
+		wake_up(&seq_sleeper);
 }
 
 static void reset_controllers(int dev, unsigned char *controller, int update_dev)
@@ -1048,7 +971,7 @@
 	seq_mode = SEQ_2;
 }
 
-int sequencer_open(int dev, struct fileinfo *file)
+int sequencer_open(int dev, struct file *file)
 {
 	int retval, mode, i;
 	int level, tmp;
@@ -1060,7 +983,7 @@
 	level = ((dev & 0x0f) == SND_DEV_SEQ2) ? 2 : 1;
 
 	dev = dev >> 4;
-	mode = file->mode & O_ACCMODE;
+	mode = translate_mode(file);
 
 	DEB(printk("sequencer_open(dev=%d)\n", dev));
 
@@ -1187,8 +1110,8 @@
 	if (seq_mode == SEQ_2)
 		tmr->open(tmr_no, seq_mode);
 
-	seq_sleep_flag.opts = WK_NONE;
-	midi_sleep_flag.opts = WK_NONE;
+ 	init_waitqueue(&seq_sleeper);
+ 	init_waitqueue(&midi_sleeper);
 	output_threshold = SEQ_MAX_QUEUE / 2;
 
 	return 0;
@@ -1218,27 +1141,18 @@
 		 * Let's have a delay
 		 */
 		
-		if (n)
-		{
-			unsigned long   tlimit;
-
-			current->timeout = tlimit = jiffies + (HZ / 10);
-			seq_sleep_flag.opts = WK_SLEEP;
-			interruptible_sleep_on(&seq_sleeper);
-			if (!(seq_sleep_flag.opts & WK_WAKEUP))
-			{
-				if (jiffies >= tlimit)
-					seq_sleep_flag.opts |= WK_TIMEOUT;
-			}
-			seq_sleep_flag.opts &= ~WK_SLEEP;
-		}
+ 		if (n) {
+ 			current->timeout = jiffies + HZ / 10;
+ 			interruptible_sleep_on(&seq_sleeper);
+ 			current->timeout = 0;
+  		}
 	}
 }
 
-void sequencer_release(int dev, struct fileinfo *file)
+void sequencer_release(int dev, struct file *file)
 {
 	int i;
-	int mode = file->mode & O_ACCMODE;
+	int mode = translate_mode(file);
 
 	dev = dev >> 4;
 
@@ -1248,21 +1162,15 @@
 	 * Wait until the queue is empty (if we don't have nonblock)
 	 */
 
-	if (mode != OPEN_READ && !(file->flags & (O_NONBLOCK) ? 1 : 0))
+	if (mode != OPEN_READ && !(file->f_flags & O_NONBLOCK))
 	{
 		while (!signal_pending(current) && qlen > 0)
 		{
-			unsigned long   tlimit;
-			seq_sync();
-			current->timeout = tlimit = jiffies + (3 * HZ);
-			seq_sleep_flag.opts = WK_SLEEP;
-			interruptible_sleep_on(&seq_sleeper);
-			if (!(seq_sleep_flag.opts & WK_WAKEUP))
-			{
-				if (jiffies >= tlimit)
-					seq_sleep_flag.opts |= WK_TIMEOUT;
-			}
-			seq_sleep_flag.opts &= ~WK_SLEEP;
+  			seq_sync();
+ 			current->timeout = jiffies + 3 * HZ;
+ 			interruptible_sleep_on(&seq_sleeper);
+ 			current->timeout = 0;
+ 			/* Extra delay */
 		}
 	}
 		  
@@ -1313,21 +1221,11 @@
 
 	save_flags(flags);
 	cli();
-	if (qlen > 0)
-	{
-		unsigned long   tlimit;
-
-		if (HZ)
-			current->timeout = tlimit = jiffies + (HZ);
-		seq_sleep_flag.opts = WK_SLEEP;
-		interruptible_sleep_on(&seq_sleeper);
-		if (!(seq_sleep_flag.opts & WK_WAKEUP))
-		{
-			if (jiffies >= tlimit)
-				seq_sleep_flag.opts |= WK_TIMEOUT;
-		}
-		seq_sleep_flag.opts &= ~WK_SLEEP;
-	}
+ 	if (qlen > 0) {
+ 		current->timeout = jiffies + HZ;
+ 		interruptible_sleep_on(&seq_sleeper);
+ 		current->timeout = 0;
+  	}
 	restore_flags(flags);
 	return qlen;
 }
@@ -1351,21 +1249,12 @@
 
 	save_flags(flags);
 	cli();
-	while (n && !midi_devs[dev]->outputc(dev, data))
-	{
-		unsigned long   tlimit;
-
-		current->timeout = tlimit = jiffies + (4);
-		seq_sleep_flag.opts = WK_SLEEP;
-		interruptible_sleep_on(&seq_sleeper);
-		if (!(seq_sleep_flag.opts & WK_WAKEUP))
-		{
-			if (jiffies >= tlimit)
-				seq_sleep_flag.opts |= WK_TIMEOUT;
-		}
-		seq_sleep_flag.opts &= ~WK_SLEEP;
-		n--;
-	}
+ 	while (n && !midi_devs[dev]->outputc(dev, data)) {
+ 		current->timeout = jiffies + 4;
+ 		interruptible_sleep_on(&seq_sleeper);
+ 		current->timeout = 0;
+  		n--;
+  	}
 	restore_flags(flags);
 }
 
@@ -1441,13 +1330,9 @@
 	save_flags(flags);
 	cli();
 	
-	if ((seq_sleep_flag.opts & WK_SLEEP))
-	{
+	if (waitqueue_active(&seq_sleeper)) {
 		/*      printk( "Sequencer Warning: Unexpected sleeping process - Waking up\n"); */
-		{
-			seq_sleep_flag.opts = WK_WAKEUP;
-			wake_up(&seq_sleeper);
-		};
+		wake_up(&seq_sleeper);
 	}
 	restore_flags(flags);
 }
@@ -1473,11 +1358,10 @@
 	 */
 }
 
-int sequencer_ioctl(int dev, struct fileinfo *file,
-		    unsigned int cmd, caddr_t arg)
+int sequencer_ioctl(int dev, struct file *file, unsigned int cmd, caddr_t arg)
 {
 	int midi_dev, orig_dev, val, err;
-	int mode = file->mode & O_ACCMODE;
+	int mode = translate_mode(file);
 	struct synth_info inf;
 	struct seq_event_rec event_rec;
 	unsigned long flags;
@@ -1684,44 +1568,26 @@
 	return put_user(val, (int *)arg);
 }
 
-int sequencer_select(int dev, struct fileinfo *file, int sel_type, poll_table * wait)
+unsigned int sequencer_poll(int dev, struct file *file, poll_table * wait)
 {
-	unsigned long   flags;
+	unsigned long flags;
+	unsigned int mask = 0;
 
 	dev = dev >> 4;
 
-	switch (sel_type)
-	{
-		case SEL_IN:
-			save_flags(flags);
-			cli();
-			if (!iqlen)
-			{
-				midi_sleep_flag.opts = WK_SLEEP;
-				poll_wait(&midi_sleeper, wait);
-				restore_flags(flags);
-				return 0;
-			}
-			restore_flags(flags);
-			return 1;
-
-		case SEL_OUT:
-			save_flags(flags);
-			cli();
-			if ((SEQ_MAX_QUEUE - qlen) < output_threshold)
-			{
-				seq_sleep_flag.opts = WK_SLEEP;
-				poll_wait(&seq_sleeper, wait);
-				restore_flags(flags);
-				return 0;
-			}
-			restore_flags(flags);
-			return 1;
-
-		case SEL_EX:
-			return 0;
-	}
-	return 0;
+	save_flags(flags);
+	cli();
+	/* input */
+	poll_wait(&midi_sleeper, wait);
+	if (iqlen)
+		mask |= POLLIN | POLLRDNORM;
+
+	/* output */
+	poll_wait(&seq_sleeper, wait);
+	if ((SEQ_MAX_QUEUE - qlen) >= output_threshold) 
+		mask |= POLLOUT | POLLWRNORM;
+	restore_flags(flags);
+	return mask;
 }
 
 
diff -u --recursive --new-file v2.1.87/linux/drivers/sound/softoss.c linux/drivers/sound/softoss.c
--- v2.1.87/linux/drivers/sound/softoss.c	Tue Feb 17 13:12:47 1998
+++ linux/drivers/sound/softoss.c	Thu Feb 19 14:46:15 1998
@@ -762,7 +762,9 @@
 
 	case SNDCTL_SYNTH_INFO:
 		softsyn_info.nr_voices = devc->maxvoice;
-		return __copy_to_user(arg, &softsyn_info, sizeof(softsyn_info));
+		if (__copy_to_user(arg, &softsyn_info, sizeof(softsyn_info)))
+			return -EFAULT;
+		return 0;
 
 	case SNDCTL_SEQ_RESETSAMPLES:
 		stop_engine(devc);
@@ -992,8 +994,8 @@
 		return 0;
 
 	softsynth_disabled = 0;
-	devc->finfo.mode = OPEN_WRITE;
-	devc->finfo.flags = 0;
+	devc->finfo.f_mode = FMODE_WRITE;
+	devc->finfo.f_flags = 0;
 
 	if (softoss_dev >= num_audiodevs)
 		softoss_dev = num_audiodevs - 1;
diff -u --recursive --new-file v2.1.87/linux/drivers/sound/softoss.h linux/drivers/sound/softoss.h
--- v2.1.87/linux/drivers/sound/softoss.h	Sun Dec 21 17:41:24 1997
+++ linux/drivers/sound/softoss.h	Thu Feb 19 14:46:15 1998
@@ -105,7 +105,7 @@
 	int bits;
 	int default_max_voices;
 	int max_playahead;
-	struct fileinfo finfo;
+	struct file finfo;
 	int fragsize;
 	int samples_per_fragment;
 	
diff -u --recursive --new-file v2.1.87/linux/drivers/sound/sound_calls.h linux/drivers/sound/sound_calls.h
--- v2.1.87/linux/drivers/sound/sound_calls.h	Thu Feb 12 20:56:10 1998
+++ linux/drivers/sound/sound_calls.h	Thu Feb 19 14:46:15 1998
@@ -22,24 +22,23 @@
 int DMAbuf_activate_recording (int dev, struct dma_buffparms *dmap);
 int DMAbuf_get_buffer_pointer (int dev, struct dma_buffparms *dmap, int direction);
 void DMAbuf_launch_output(int dev, struct dma_buffparms *dmap);
-int DMAbuf_select(int dev, struct fileinfo *file, int sel_type, poll_table * wait);
+unsigned int DMAbuf_poll(int dev, poll_table *wait);
 void DMAbuf_start_devices(unsigned int devmask);
 void DMAbuf_reset (int dev);
 int DMAbuf_sync (int dev);
+unsigned int DMAbuf_poll(int dev, poll_table *wait);
 
 /*
  *	System calls for /dev/dsp and /dev/audio (audio.c)
  */
 
-int audio_read (int dev, struct fileinfo *file, char *buf, int count);
-int audio_write (int dev, struct fileinfo *file, const char *buf, int count);
-int audio_open (int dev, struct fileinfo *file);
-void audio_release (int dev, struct fileinfo *file);
-int audio_ioctl (int dev, struct fileinfo *file,
+int audio_read (int dev, struct file *file, char *buf, int count);
+int audio_write (int dev, struct file *file, const char *buf, int count);
+int audio_open (int dev, struct file *file);
+void audio_release (int dev, struct file *file);
+int audio_ioctl (int dev, struct file *file,
 	   unsigned int cmd, caddr_t arg);
 void audio_init_devices (void);
-
-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);
 
@@ -47,13 +46,12 @@
  *	System calls for the /dev/sequencer
  */
 
-int sequencer_read (int dev, struct fileinfo *file, char *buf, int count);
-int sequencer_write (int dev, struct fileinfo *file, const char *buf, int count);
-int sequencer_open (int dev, struct fileinfo *file);
-void sequencer_release (int dev, struct fileinfo *file);
-int sequencer_ioctl (int dev, struct fileinfo *file,
-	   unsigned int cmd, caddr_t arg);
-int sequencer_select(int dev, struct fileinfo *file, int sel_type, poll_table * wait);
+int sequencer_read (int dev, struct file *file, char *buf, int count);
+int sequencer_write (int dev, struct file *file, const char *buf, int count);
+int sequencer_open (int dev, struct file *file);
+void sequencer_release (int dev, struct file *file);
+int sequencer_ioctl (int dev, struct file *file, unsigned int cmd, caddr_t arg);
+unsigned int sequencer_poll(int dev, struct file *file, poll_table * wait);
 
 void sequencer_init (void);
 void sequencer_unload (void);
@@ -68,13 +66,12 @@
  *	System calls for the /dev/midi
  */
 
-int MIDIbuf_read (int dev, struct fileinfo *file, char *buf, int count);
-int MIDIbuf_write (int dev, struct fileinfo *file, const char *buf, int count);
-int MIDIbuf_open (int dev, struct fileinfo *file);
-void MIDIbuf_release (int dev, struct fileinfo *file);
-int MIDIbuf_ioctl (int dev, struct fileinfo *file,
-	   unsigned int cmd, caddr_t arg);
-int MIDIbuf_select(int dev, struct fileinfo *file, int sel_type, poll_table * wait);
+int MIDIbuf_read (int dev, struct file *file, char *buf, int count);
+int MIDIbuf_write (int dev, struct file *file, const char *buf, int count);
+int MIDIbuf_open (int dev, struct file *file);
+void MIDIbuf_release (int dev, struct file *file);
+int MIDIbuf_ioctl (int dev, struct file *file, unsigned int cmd, caddr_t arg);
+unsigned int MIDIbuf_poll(int dev, struct file *file, poll_table * wait);
 int MIDIbuf_avail(int dev);
 
 void MIDIbuf_bytes_received(int dev, unsigned char *buf, int count);
@@ -86,9 +83,6 @@
  */
 
 /*	From soundcard.c	*/
-#if !defined(__bsdi__) && !defined(__NjetBSD__)
-void tenmicrosec(int *osp);
-#endif
 void request_sound_timer (int count);
 void sound_stop_timer(void);
 int snd_set_irq_handler (int interrupt_level, void(*iproc)(int, void*, struct pt_regs *), char *name, int *osp);
diff -u --recursive --new-file v2.1.87/linux/drivers/sound/sound_config.h linux/drivers/sound/sound_config.h
--- v2.1.87/linux/drivers/sound/sound_config.h	Tue Feb 17 13:12:47 1998
+++ linux/drivers/sound/sound_config.h	Thu Feb 19 14:46:15 1998
@@ -10,7 +10,7 @@
  * for more info.
  */
 
-
+#include <linux/fs.h>
 #include "local.h.master"
 #include <linux/config.h>
 #include "os.h"
@@ -18,9 +18,6 @@
 
 
 
-
-
-
 #ifndef SND_DEFAULT_ENABLE
 #define SND_DEFAULT_ENABLE	1
 #endif
@@ -91,21 +88,12 @@
 
 #define DSP_DEFAULT_SPEED	8000
 
-#define ON		1
-#define OFF		0
-
 #define MAX_AUDIO_DEV	5
 #define MAX_MIXER_DEV	5
 #define MAX_SYNTH_DEV	5
 #define MAX_MIDI_DEV	6
 #define MAX_TIMER_DEV	4
 
-struct fileinfo {
-       	  int mode;	      /* Open mode */
-	  int flags;
-	  int dummy;     /* Reference to file-flags. OS-dependent. */
-       };
-
 struct address_info {
 	int io_base;
 	int irq;
@@ -154,6 +142,24 @@
 #define OPEN_WRITE	PCM_ENABLE_OUTPUT
 #define OPEN_READWRITE	(OPEN_READ|OPEN_WRITE)
 
+#if OPEN_READ == FMODE_READ && OPEN_WRITE == FMODE_WRITE
+
+extern __inline__ int translate_mode(struct file *file)
+{
+	return file->f_mode;
+}
+
+#else
+
+extern __inline__ int translate_mode(struct file *file)
+{
+	return ((file->f_mode & FMODE_READ) ? OPEN_READ : 0) |
+		((file->f_mode & FMODE_WRITE) ? OPEN_WRITE : 0);
+}
+
+#endif
+
+
 #include "sound_calls.h"
 #include "dev_table.h"
 
@@ -175,7 +181,3 @@
 
 #define TIMER_ARMED	121234
 #define TIMER_NOT_ARMED	1
-
-
-
-
diff -u --recursive --new-file v2.1.87/linux/drivers/sound/sound_timer.c linux/drivers/sound/sound_timer.c
--- v2.1.87/linux/drivers/sound/sound_timer.c	Thu Feb 12 20:56:10 1998
+++ linux/drivers/sound/sound_timer.c	Thu Feb 19 14:46:15 1998
@@ -239,9 +239,6 @@
 			break;
 		
 		case SNDCTL_TMR_METRONOME:
-			/* NOP */
-			break;
-		
 		default:
 			return -EINVAL;
 	}
diff -u --recursive --new-file v2.1.87/linux/drivers/sound/soundcard.c linux/drivers/sound/soundcard.c
--- v2.1.87/linux/drivers/sound/soundcard.c	Thu Feb 12 20:56:10 1998
+++ linux/drivers/sound/soundcard.c	Thu Feb 19 14:46:15 1998
@@ -25,6 +25,8 @@
 #include <linux/signal.h>
 #include <linux/fcntl.h>
 #include <linux/ctype.h>
+#include <linux/stddef.h>
+#include <linux/kerneld.h>
 #ifdef __KERNEL__
 #include <asm/io.h>
 #include <asm/segment.h>
@@ -60,8 +62,6 @@
 
 static int      soundcard_configured = 0;
 
-static struct fileinfo files[SND_NDEVS];
-
 static char     dma_alloc_map[8] =
 {0};
 
@@ -118,7 +118,9 @@
 	if (__copy_from_user(&buf, arg, sizeof(buf)))
 		return -EFAULT;
 	load_mixer_volumes(buf.name, buf.levels, 0);
-	return __copy_to_user(arg, &buf, sizeof(buf));
+	if (__copy_to_user(arg, &buf, sizeof(buf)))
+		return -EFAULT;
+	return 0;
 }
 
 static int get_mixer_levels(caddr_t arg)
@@ -129,7 +131,9 @@
 		return -EFAULT;
 	if (n < 0 || n >= num_mixer_volumes)
 		return -EINVAL;
-	return __copy_to_user(arg, &mixer_vols[n], sizeof(mixer_vol_table));
+	if (__copy_to_user(arg, &mixer_vols[n], sizeof(mixer_vol_table)))
+		return -EFAULT;
+	return 0;
 }
 
 static int sound_proc_get_info(char *buffer, char **start, off_t offset, int length, int inout)
@@ -351,7 +355,6 @@
 {
 	int dev = MINOR(file->f_dentry->d_inode->i_rdev);
 
-	files[dev].flags = file->f_flags;
 	DEB(printk("sound_read(dev=%d, count=%d)\n", dev, count));
 	switch (dev & 0x0f) {
 	case SND_DEV_STATUS:
@@ -361,18 +364,18 @@
 	case SND_DEV_DSP:
 	case SND_DEV_DSP16:
 	case SND_DEV_AUDIO:
-		return audio_read(dev, &files[dev], buf, count);
+		return audio_read(dev, file, buf, count);
 #endif
 
 #ifdef CONFIG_SEQUENCER
 	case SND_DEV_SEQ:
 	case SND_DEV_SEQ2:
-		return sequencer_read(dev, &files[dev], buf, count);
+		return sequencer_read(dev, file, buf, count);
 #endif
 
 #ifdef CONFIG_MIDI
 	case SND_DEV_MIDIN:
-		return MIDIbuf_read(dev, &files[dev], buf, count);
+		return MIDIbuf_read(dev, file, buf, count);
 #endif
 
 	default:;
@@ -384,25 +387,24 @@
 {
 	int dev = MINOR(file->f_dentry->d_inode->i_rdev);
 
-	files[dev].flags = file->f_flags;
 	DEB(printk("sound_write(dev=%d, count=%d)\n", dev, count));
 	switch (dev & 0x0f) {
 #ifdef CONFIG_SEQUENCER
 	case SND_DEV_SEQ:
 	case SND_DEV_SEQ2:
-		return sequencer_write(dev, &files[dev], buf, count);
+		return sequencer_write(dev, file, buf, count);
 #endif
 
 #ifdef CONFIG_AUDIO
 	case SND_DEV_DSP:
 	case SND_DEV_DSP16:
 	case SND_DEV_AUDIO:
-		return audio_write(dev, &files[dev], buf, count);
+		return audio_write(dev, file, buf, count);
 #endif
 
 #ifdef CONFIG_MIDI
 	case SND_DEV_MIDIN:
-		return MIDIbuf_write(dev, &files[dev], buf, count);
+		return MIDIbuf_write(dev, file, buf, count);
 #endif
 	}
 	return -EINVAL;
@@ -416,7 +418,6 @@
 static int sound_open(struct inode *inode, struct file *file)
 {
 	int dev, retval;
-	struct fileinfo tmp_file;
 
 	if (is_unloading) {
 		/* printk(KERN_ERR "Sound: Driver partially removed. Can't open device\n");*/
@@ -427,15 +428,6 @@
 		/* printk("SoundCard Error: The soundcard system has not been configured\n");*/
 		return -ENXIO;
 	}
-	tmp_file.mode = 0;
-	tmp_file.flags = file->f_flags;
-
-	if ((tmp_file.flags & O_ACCMODE) == O_RDWR)
-		tmp_file.mode = OPEN_READWRITE;
-	if ((tmp_file.flags & O_ACCMODE) == O_RDONLY)
-		tmp_file.mode = OPEN_READ;
-	if ((tmp_file.flags & O_ACCMODE) == O_WRONLY)
-		tmp_file.mode = OPEN_WRITE;
 	DEB(printk("sound_open(dev=%d)\n", dev));
 	if ((dev >= SND_NDEVS) || (dev < 0)) {
 		printk(KERN_ERR "Invalid minor device %d\n", dev);
@@ -446,21 +438,29 @@
 		break;
 
 	case SND_DEV_CTL:
-		if ((dev & 0xf0) && ((dev & 0xf0) >> 4) >= num_mixers)
+		dev >>= 4;
+#ifdef CONFIG_KERNELD
+	if (dev >= 0 && dev < MAX_MIXER_DEV && mixer_devs[dev] == NULL) {
+		char modname[20];
+		sprintf(modname, "mixer%d", dev);
+		request_module(modname);
+	}
+#endif
+		if (dev && (dev >= num_mixers || mixer_devs[dev] == NULL))
 			return -ENXIO;
 		break;
 
 #ifdef CONFIG_SEQUENCER
 	case SND_DEV_SEQ:
 	case SND_DEV_SEQ2:
-		if ((retval = sequencer_open(dev, &tmp_file)) < 0)
+		if ((retval = sequencer_open(dev, file)) < 0)
 			return retval;
 		break;
 #endif
 
 #ifdef CONFIG_MIDI
 	case SND_DEV_MIDIN:
-		if ((retval = MIDIbuf_open(dev, &tmp_file)) < 0)
+		if ((retval = MIDIbuf_open(dev, file)) < 0)
 			return retval;
 		break;
 #endif
@@ -469,7 +469,7 @@
 	case SND_DEV_DSP:
 	case SND_DEV_DSP16:
 	case SND_DEV_AUDIO:
-		if ((retval = audio_open(dev, &tmp_file)) < 0)
+		if ((retval = audio_open(dev, file)) < 0)
 			return retval;
 		break;
 #endif
@@ -482,7 +482,6 @@
 #ifdef MODULE
 	SOUND_INC_USE_COUNT;
 #endif
-	memcpy(&files[dev], &tmp_file, sizeof(tmp_file));
 	return 0;
 }
 
@@ -490,7 +489,6 @@
 {
 	int dev = MINOR(inode->i_rdev);
 
-	files[dev].flags = file->f_flags;
 	DEB(printk("sound_release(dev=%d)\n", dev));
 	switch (dev & 0x0f) {
 	case SND_DEV_STATUS:
@@ -500,13 +498,13 @@
 #ifdef CONFIG_SEQUENCER
 	case SND_DEV_SEQ:
 	case SND_DEV_SEQ2:
-		sequencer_release(dev, &files[dev]);
+		sequencer_release(dev, file);
 		break;
 #endif
 
 #ifdef CONFIG_MIDI
 	case SND_DEV_MIDIN:
-		MIDIbuf_release(dev, &files[dev]);
+		MIDIbuf_release(dev, file);
 		break;
 #endif
 
@@ -514,7 +512,7 @@
 	case SND_DEV_DSP:
 	case SND_DEV_DSP16:
 	case SND_DEV_AUDIO:
-		audio_release(dev, &files[dev]);
+		audio_release(dev, file);
 		break;
 #endif
 
@@ -531,14 +529,10 @@
 static int 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);
-	for (i = 0; i < 32 && mixer_devs[dev]->name; i++)
-		info.name[i] = mixer_devs[dev]->name[i];
-	info.name[i] = 0;
+	strncpy(info.id, mixer_devs[dev]->id, sizeof(info.id));
+	strncpy(info.name, mixer_devs[dev]->name, sizeof(info.name));
+	info.name[sizeof(info.name)-1] = 0;
 	info.modify_counter = mixer_devs[dev]->modify_counter;
 	if (__copy_to_user(arg, &info,  sizeof(info)))
 		return -EFAULT;
@@ -548,21 +542,29 @@
 static int 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);
-	for (i = 0; i < 32 && mixer_devs[dev]->name; i++)
-		info.name[i] = mixer_devs[dev]->name[i];
-	info.name[i] = 0;
-	if (__copy_to_user(arg, &info,  sizeof(info)))
+ 	strncpy(info.id, mixer_devs[dev]->id, sizeof(info.id));
+ 	strncpy(info.name, mixer_devs[dev]->name, sizeof(info.name));
+ 	info.name[sizeof(info.name)-1] = 0;	
+ 	if (copy_to_user(arg, &info,  sizeof(info)))
 		return -EFAULT;
 	return 0;
 }
 
 static int sound_mixer_ioctl(int mixdev, unsigned int cmd, caddr_t arg)
 {
+ 	if (mixdev < 0 || mixdev >= MAX_MIXER_DEV)
+ 		return -ENXIO;
+#ifdef CONFIG_KERNELD
+ 	/* Try to load the mixer... */
+ 	if (mixer_devs[mixdev] == NULL) {
+ 		char modname[20];
+ 		sprintf(modname, "mixer%d", mixdev);
+ 		request_module(modname);
+ 	}
+#endif	/* CONFIG_KERNELD */
+ 	if (mixdev >= num_mixers || !mixer_devs[mixdev])
+ 		return -ENXIO;
 	if (cmd == SOUND_MIXER_INFO)
 		return get_mixer_info(mixdev, arg);
 	if (cmd == SOUND_OLD_MIXER_INFO)
@@ -577,10 +579,9 @@
 static int sound_ioctl(struct inode *inode, struct file *file,
 		       unsigned int cmd, unsigned long arg)
 {
-	int err, len = 0, dtype, mixdev;
+	int err, len = 0, dtype;
 	int dev = MINOR(inode->i_rdev);
 
-	files[dev].flags = file->f_flags;
 	if (_SIOC_DIR(cmd) != _SIOC_NONE && _SIOC_DIR(cmd) != 0) {
 		/*
 		 * Have to validate the address given by the process.
@@ -599,55 +600,47 @@
 	if (cmd == OSS_GETVERSION)
 		return __put_user(SOUND_VERSION, (int *)arg);
 	
-	if (((cmd >> 8) & 0xff) == 'M' && num_mixers > 0)	/* Mixer ioctl */
-		if ((dev & 0x0f) != SND_DEV_CTL) {
-			dtype = dev & 0x0f;
-			switch (dtype) {
+	if (_IOC_TYPE(cmd) == 'M' && num_mixers > 0 &&   /* Mixer ioctl */
+	    (dev & 0x0f) != SND_DEV_CTL) {              
+		dtype = dev & 0x0f;
+		switch (dtype) {
 #ifdef CONFIG_AUDIO
-			case SND_DEV_DSP:
-			case SND_DEV_DSP16:
-			case SND_DEV_AUDIO:
-				mixdev = audio_devs[dev >> 4]->mixer_dev;
-				if (mixdev < 0 || mixdev >= num_mixers)
-					return -ENXIO;
-				return sound_mixer_ioctl(mixdev, cmd, (caddr_t)arg);
-#endif
-				
-			default:
-				return sound_mixer_ioctl(dev, cmd, (caddr_t)arg);
-			}
+		case SND_DEV_DSP:
+		case SND_DEV_DSP16:
+		case SND_DEV_AUDIO:
+			return sound_mixer_ioctl(audio_devs[dev >> 4]->mixer_dev,
+						 cmd, (caddr_t)arg);
+#endif
+			
+		default:
+			return sound_mixer_ioctl(dev >> 4, cmd, (caddr_t)arg);
 		}
+	}
 	switch (dev & 0x0f) {
 	case SND_DEV_CTL:
 		if (cmd == SOUND_MIXER_GETLEVELS)
 			return get_mixer_levels((caddr_t)arg);
 		if (cmd == SOUND_MIXER_SETLEVELS)
 			return set_mixer_levels((caddr_t)arg);
-		if (!num_mixers)
-			return -ENXIO;
-		dev = dev >> 4;
-		if (dev >= num_mixers)
-			return -ENXIO;
-		return sound_mixer_ioctl(dev, cmd, (caddr_t)arg);
-		break;
+		return sound_mixer_ioctl(dev >> 4, cmd, (caddr_t)arg);
 
 #ifdef CONFIG_SEQUENCER
 	case SND_DEV_SEQ:
 	case SND_DEV_SEQ2:
-		return sequencer_ioctl(dev, &files[dev], cmd, (caddr_t)arg);
+		return sequencer_ioctl(dev, file, cmd, (caddr_t)arg);
 #endif
 
 #ifdef CONFIG_AUDIO
 	case SND_DEV_DSP:
 	case SND_DEV_DSP16:
 	case SND_DEV_AUDIO:
-		return audio_ioctl(dev, &files[dev], cmd, (caddr_t)arg);
+		return audio_ioctl(dev, file, cmd, (caddr_t)arg);
 		break;
 #endif
 
 #ifdef CONFIG_MIDI
 	case SND_DEV_MIDIN:
-		return MIDIbuf_ioctl(dev, &files[dev], cmd, (caddr_t)arg);
+		return MIDIbuf_ioctl(dev, file, cmd, (caddr_t)arg);
 		break;
 #endif
 
@@ -655,47 +648,34 @@
 	return -EINVAL;
 }
 
-static int sound_select(struct inode *inode, struct file *file, int sel_type, poll_table * wait)
+static unsigned int sound_poll(struct file *file, poll_table * wait)
 {
+	struct inode *inode = file->f_dentry->d_inode;
 	int dev = MINOR(inode->i_rdev);
 
-	files[dev].flags = file->f_flags;
-	DEB(printk("sound_select(dev=%d, type=0x%x)\n", dev, sel_type));
+	DEB(printk("sound_poll(dev=%d)\n", dev));
 	switch (dev & 0x0f) {
 #if defined(CONFIG_SEQUENCER) || defined(MODULE)
 	case SND_DEV_SEQ:
 	case SND_DEV_SEQ2:
-		return sequencer_select(dev, &files[dev], sel_type, wait);
+		return sequencer_poll(dev, file, wait);
 #endif
 
 #if defined(CONFIG_MIDI)
 	case SND_DEV_MIDIN:
-		return MIDIbuf_select(dev, &files[dev], sel_type, wait);
+		return MIDIbuf_poll(dev, file, wait);
 #endif
 
 #if defined(CONFIG_AUDIO) || defined(MODULE)
 	case SND_DEV_DSP:
 	case SND_DEV_DSP16:
 	case SND_DEV_AUDIO:
-		return DMAbuf_select(dev >> 4, &files[dev], sel_type, wait);
+		return DMAbuf_poll(dev >> 4, wait);
 #endif
 	}
 	return 0;
 }
 
-static unsigned int sound_poll(struct file *file, poll_table * wait)
-{
-	struct inode *inode;
-	int ret = 0;
-
-	inode = file->f_dentry->d_inode;
-	if (sound_select(inode, file, SEL_IN, wait))
-		ret |= POLLIN;
-	if (sound_select(inode, file, SEL_OUT, wait))
-		ret |= POLLOUT;
-	return ret;
-}
-
 static int sound_mmap(struct file *file, struct vm_area_struct *vma)
 {
 	int dev_class;
@@ -703,8 +683,6 @@
 	struct dma_buffparms *dmap = NULL;
 	int dev = MINOR(file->f_dentry->d_inode->i_rdev);
 
-	files[dev].flags = file->f_flags;
-
 	dev_class = dev & 0x0f;
 	dev >>= 4;
 
@@ -832,8 +810,6 @@
 
 #endif
 
-static int      debugmem = 0;	/* switched off by default */
-
 static int      sound[20] =
 {0};
 
@@ -918,11 +894,6 @@
 }
 #endif
 
-void tenmicrosec(int *osp)
-{
-	udelay(10);
-}
-
 int snd_set_irq_handler(int interrupt_level, void (*iproc) (int, void *, struct pt_regs *), char *name, int *osp)
 {
 	int             retcode;
@@ -1053,143 +1024,6 @@
 }
 #endif
 
-#ifdef CONFIG_AUDIO
-
-static int dma_buffsize = DSP_BUFFSIZE;
-
-int sound_alloc_dmap(int dev, struct dma_buffparms *dmap, int chan)
-{
-	char *start_addr, *end_addr;
-	int i, dma_pagesize;
-
-	dmap->mapping_flags &= ~DMA_MAP_MAPPED;
-
-	if (dmap->raw_buf != NULL)
-		return 0;	/* Already done */
-
-	if (dma_buffsize < 4096)
-		dma_buffsize = 4096;
-
-	if (chan < 4)
-		dma_pagesize = 64 * 1024;
-	else
-		dma_pagesize = 128 * 1024;
-
-	dmap->raw_buf = NULL;
-
-	dmap->buffsize = dma_buffsize;
-
-	if (dmap->buffsize > dma_pagesize)
-		dmap->buffsize = dma_pagesize;
-
-	start_addr = NULL;
-
-/*
- * Now loop until we get a free buffer. Try to get smaller buffer if
- * it fails. Don't accept smaller than 8k buffer for performance
- * reasons.
- */
-
-	while (start_addr == NULL && dmap->buffsize > PAGE_SIZE)
-	{
-		int sz, size;
-
-		for (sz = 0, size = PAGE_SIZE;
-			size < dmap->buffsize;
-			sz++, size <<= 1);
-
-		dmap->buffsize = PAGE_SIZE * (1 << sz);
-
-		if ((start_addr = (char *) __get_free_pages(GFP_ATOMIC|GFP_DMA, sz)) == NULL)
-			dmap->buffsize /= 2;
-	}
-
-	if (start_addr == NULL)
-	{
-		printk(KERN_WARNING "Sound error: Couldn't allocate DMA buffer\n");
-		return -ENOMEM;
-	}
-	else
-	{
-		/* make some checks */
-		end_addr = start_addr + dmap->buffsize - 1;
-
-		if (debugmem)
-			printk(KERN_DEBUG "sound: start 0x%lx, end 0x%lx\n", (long) start_addr, (long) end_addr);
-
-		/* now check if it fits into the same dma-pagesize */
-
-		if (((long) start_addr & ~(dma_pagesize - 1))
-		      != ((long) end_addr & ~(dma_pagesize - 1))
-		      || end_addr >= (char *) (MAX_DMA_ADDRESS))
-		{
-			printk(KERN_ERR "sound: Got invalid address 0x%lx for %db DMA-buffer\n", (long) start_addr, dmap->buffsize);
-			return -EFAULT;
-		}
-	}
-	dmap->raw_buf = start_addr;
-	dmap->raw_buf_phys = virt_to_bus(start_addr);
-
-	for (i = MAP_NR(start_addr); i <= MAP_NR(end_addr); i++)
-	{
-		set_bit(PG_reserved, &mem_map[i].flags);;
-	}
-
-	return 0;
-}
-
-void sound_free_dmap(int dev, struct dma_buffparms *dmap, int chan)
-{
-	int sz, size, i;
-	unsigned long start_addr, end_addr;
-
-	if (dmap->raw_buf == NULL)
-		return;
-
-	if (dmap->mapping_flags & DMA_MAP_MAPPED)
-		return;		/* Don't free mmapped buffer. Will use it next time */
-
-	for (sz = 0, size = PAGE_SIZE; size < dmap->buffsize; sz++, size <<= 1);
-
-	start_addr = (unsigned long) dmap->raw_buf;
-	end_addr = start_addr + dmap->buffsize;
-
-	for (i = MAP_NR(start_addr); i <= MAP_NR(end_addr); i++)
-	{
-		clear_bit(PG_reserved, &mem_map[i].flags);;
-	}
-
-	free_pages((unsigned long) dmap->raw_buf, sz);
-	dmap->raw_buf = NULL;
-}
-
-
-/* Intel version !!!!!!!!! */
-
-int sound_start_dma(int dev, struct dma_buffparms *dmap, int chan,
-		unsigned long physaddr,
-		int count, int dma_mode, int autoinit)
-{
-	unsigned long   flags;
-
-	/* printk( "Start DMA%d %d, %d\n",  chan,  (int)(physaddr-dmap->raw_buf_phys),  count); */
-	if (autoinit)
-		dma_mode |= DMA_AUTOINIT;
-	save_flags(flags);
-	cli();
-	disable_dma(chan);
-	clear_dma_ff(chan);
-	set_dma_mode(chan, dma_mode);
-	set_dma_addr(chan, physaddr);
-	set_dma_count(chan, count);
-	enable_dma(chan);
-	restore_flags(flags);
-
-	return 0;
-}
-
-#endif
-
 void conf_printf(char *name, struct address_info *hw_config)
 {
 	if (!trace_init)
@@ -1258,4 +1092,3 @@
 		ct++;
 	}
 }
-
diff -u --recursive --new-file v2.1.87/linux/drivers/sound/sscape.c linux/drivers/sound/sscape.c
--- v2.1.87/linux/drivers/sound/sscape.c	Tue Feb 17 13:12:47 1998
+++ linux/drivers/sound/sscape.c	Thu Feb 19 14:46:15 1998
@@ -22,7 +22,7 @@
 #include "sound_config.h"
 #include "soundmodule.h"
 
-#if defined(CONFIG_SSCAPEHW) || defined(MODULE)
+#if defined(CONFIG_SSCAPE) || defined(MODULE)
 
 #include "coproc.h"
 
@@ -101,11 +101,6 @@
 static struct sscape_info *devc = &adev_info;
 static int sscape_mididev = -1;
 
-static struct wait_queue *sscape_sleeper = NULL;
-static volatile struct snd_wait sscape_sleep_flag = {
-	0
-};
-
 /* Some older cards have assigned interrupt bits differently than new ones */
 static char valid_interrupts_old[] = {
 	9, 7, 5, 15
@@ -127,6 +122,14 @@
 static char old_hardware = 0;
 #endif
 
+static void sleep(unsigned howlong)
+{
+	current->timeout = jiffies + 1;
+	current->state = TASK_INTERRUPTIBLE;
+	schedule();
+	current->timeout = 0;
+}
+
 static unsigned char sscape_read(struct sscape_info *devc, int reg)
 {
 	unsigned long flags;
@@ -317,7 +320,6 @@
 		if (!verify_mpu(devc))
 			return -EIO;
 	}
-	sscape_sleep_flag.opts = WK_NONE;
 	return 0;
 }
 
@@ -333,7 +335,6 @@
 		sscape_write(devc, GA_DMAA_REG, 0x20);	/* DMA channel disabled */
 		devc->dma_allocated = 0;
 	}
-	sscape_sleep_flag.opts = WK_NONE;
 	restore_flags(flags);
 	return;
 }
@@ -398,27 +399,17 @@
 	 * Wait until transfer completes.
 	 */
 	
-	sscape_sleep_flag.opts = WK_NONE;
 	done = 0;
 	timeout_val = 30;
 	while (!done && timeout_val-- > 0)
 	{
 		int resid;
-		unsigned long   tlimit;
-		current->timeout = tlimit = jiffies + (HZ / 50);
-		sscape_sleep_flag.opts = WK_SLEEP;
-		interruptible_sleep_on(&sscape_sleeper);
-		if (!(sscape_sleep_flag.opts & WK_WAKEUP))
-		{
-			if (jiffies >= tlimit)
-				sscape_sleep_flag.opts |= WK_TIMEOUT;
-		}
-		sscape_sleep_flag.opts &= ~WK_SLEEP;
+
+		if (HZ / 50)
+			sleep(HZ / 50);
 		clear_dma_ff(devc->dma);
 		if ((resid = get_dma_residue(devc->dma)) == 0)
-		{
 			done = 1;
-		}
 	}
 
 	restore_flags(flags);
@@ -448,18 +439,8 @@
 		while (!done && timeout_val-- > 0)
 		{
 			unsigned char x;
-			unsigned long tlimit;
-
-			current->timeout = tlimit = jiffies + 1;
-			sscape_sleep_flag.opts = WK_SLEEP;
-			interruptible_sleep_on(&sscape_sleeper);
-			if (!(sscape_sleep_flag.opts & WK_WAKEUP))
-			{
-				if (jiffies >= tlimit)
-					sscape_sleep_flag.opts |= WK_TIMEOUT;
-			}
-			sscape_sleep_flag.opts &= ~WK_SLEEP;
 			
+			sleep(1);
 			x = inb(PORT(HOST_DATA));
 			if (x == 0xff || x == 0xfe)		/* OBP startup acknowledge */
 			{
@@ -481,16 +462,7 @@
 		timeout_val = 5 * HZ;
 		while (!done && timeout_val-- > 0)
 		{
-			unsigned long   tlimit;
-			current->timeout = tlimit = jiffies + (1);
-			sscape_sleep_flag.opts = WK_SLEEP;
-			interruptible_sleep_on(&sscape_sleeper);
-			if (!(sscape_sleep_flag.opts & WK_WAKEUP))
-			{
-				if (jiffies >= tlimit)
-					sscape_sleep_flag.opts |= WK_TIMEOUT;
-			}
-			sscape_sleep_flag.opts &= ~WK_SLEEP;
+			sleep(1);
 			if (inb(PORT(HOST_DATA)) == 0xfe)	/* Host startup acknowledge */
 				done = 1;
 		}
diff -u --recursive --new-file v2.1.87/linux/drivers/video/dn_fb.c linux/drivers/video/dn_fb.c
--- v2.1.87/linux/drivers/video/dn_fb.c	Mon Nov 24 01:18:53 1997
+++ linux/drivers/video/dn_fb.c	Fri Feb 20 18:28:22 1998
@@ -5,7 +5,6 @@
 #include <linux/tty.h>
 #include <linux/malloc.h>
 #include <linux/delay.h>
-#include <linux/config.h>
 #include <linux/interrupt.h>
 #include <asm/setup.h>
 #include <asm/segment.h>
diff -u --recursive --new-file v2.1.87/linux/drivers/video/fbcon-afb.c linux/drivers/video/fbcon-afb.c
--- v2.1.87/linux/drivers/video/fbcon-afb.c	Tue Sep 16 02:29:29 1997
+++ linux/drivers/video/fbcon-afb.c	Fri Feb 20 18:28:22 1998
@@ -13,7 +13,6 @@
 #include <linux/tty.h>
 #include <linux/console.h>
 #include <linux/string.h>
-#include <linux/config.h>
 #include <linux/fb.h>
 
 #include "fbcon.h"
diff -u --recursive --new-file v2.1.87/linux/drivers/video/fbcon-cfb16.c linux/drivers/video/fbcon-cfb16.c
--- v2.1.87/linux/drivers/video/fbcon-cfb16.c	Tue Sep 16 02:29:31 1997
+++ linux/drivers/video/fbcon-cfb16.c	Fri Feb 20 18:28:22 1998
@@ -13,7 +13,6 @@
 #include <linux/tty.h>
 #include <linux/console.h>
 #include <linux/string.h>
-#include <linux/config.h>
 #include <linux/fb.h>
 
 #include "fbcon.h"
diff -u --recursive --new-file v2.1.87/linux/drivers/video/fbcon-cfb8.c linux/drivers/video/fbcon-cfb8.c
--- v2.1.87/linux/drivers/video/fbcon-cfb8.c	Tue Sep 16 02:29:34 1997
+++ linux/drivers/video/fbcon-cfb8.c	Fri Feb 20 18:28:22 1998
@@ -13,7 +13,6 @@
 #include <linux/tty.h>
 #include <linux/console.h>
 #include <linux/string.h>
-#include <linux/config.h>
 #include <linux/fb.h>
 
 #include "fbcon.h"
diff -u --recursive --new-file v2.1.87/linux/drivers/video/fbcon-cyber.c linux/drivers/video/fbcon-cyber.c
--- v2.1.87/linux/drivers/video/fbcon-cyber.c	Tue Sep 16 02:29:36 1997
+++ linux/drivers/video/fbcon-cyber.c	Fri Feb 20 18:28:22 1998
@@ -13,7 +13,6 @@
 #include <linux/tty.h>
 #include <linux/console.h>
 #include <linux/string.h>
-#include <linux/config.h>
 #include <linux/fb.h>
 
 #include "fbcon.h"
diff -u --recursive --new-file v2.1.87/linux/drivers/video/fbcon-ilbm.c linux/drivers/video/fbcon-ilbm.c
--- v2.1.87/linux/drivers/video/fbcon-ilbm.c	Tue Sep 16 02:29:39 1997
+++ linux/drivers/video/fbcon-ilbm.c	Fri Feb 20 18:28:22 1998
@@ -13,7 +13,6 @@
 #include <linux/tty.h>
 #include <linux/console.h>
 #include <linux/string.h>
-#include <linux/config.h>
 #include <linux/fb.h>
 
 #include "fbcon.h"
diff -u --recursive --new-file v2.1.87/linux/drivers/video/fbcon-iplan2p2.c linux/drivers/video/fbcon-iplan2p2.c
--- v2.1.87/linux/drivers/video/fbcon-iplan2p2.c	Mon Nov 24 01:18:54 1997
+++ linux/drivers/video/fbcon-iplan2p2.c	Fri Feb 20 18:28:22 1998
@@ -14,7 +14,6 @@
 #include <linux/tty.h>
 #include <linux/console.h>
 #include <linux/string.h>
-#include <linux/config.h>
 #include <linux/fb.h>
 
 #include "fbcon.h"
diff -u --recursive --new-file v2.1.87/linux/drivers/video/fbcon-iplan2p4.c linux/drivers/video/fbcon-iplan2p4.c
--- v2.1.87/linux/drivers/video/fbcon-iplan2p4.c	Mon Nov 24 01:18:55 1997
+++ linux/drivers/video/fbcon-iplan2p4.c	Fri Feb 20 18:28:22 1998
@@ -14,7 +14,6 @@
 #include <linux/tty.h>
 #include <linux/console.h>
 #include <linux/string.h>
-#include <linux/config.h>
 #include <linux/fb.h>
 
 #include "fbcon.h"
diff -u --recursive --new-file v2.1.87/linux/drivers/video/fbcon-iplan2p8.c linux/drivers/video/fbcon-iplan2p8.c
--- v2.1.87/linux/drivers/video/fbcon-iplan2p8.c	Mon Nov 24 01:18:56 1997
+++ linux/drivers/video/fbcon-iplan2p8.c	Fri Feb 20 18:28:23 1998
@@ -14,7 +14,6 @@
 #include <linux/tty.h>
 #include <linux/console.h>
 #include <linux/string.h>
-#include <linux/config.h>
 #include <linux/fb.h>
 
 #include "fbcon.h"
diff -u --recursive --new-file v2.1.87/linux/drivers/video/fbcon-mfb.c linux/drivers/video/fbcon-mfb.c
--- v2.1.87/linux/drivers/video/fbcon-mfb.c	Mon Nov 10 05:20:37 1997
+++ linux/drivers/video/fbcon-mfb.c	Fri Feb 20 18:28:23 1998
@@ -13,7 +13,6 @@
 #include <linux/tty.h>
 #include <linux/console.h>
 #include <linux/string.h>
-#include <linux/config.h>
 #include <linux/fb.h>
 
 #include "fbcon.h"
diff -u --recursive --new-file v2.1.87/linux/drivers/video/fbcon-retz3.c linux/drivers/video/fbcon-retz3.c
--- v2.1.87/linux/drivers/video/fbcon-retz3.c	Tue Sep 16 02:29:50 1997
+++ linux/drivers/video/fbcon-retz3.c	Fri Feb 20 18:28:23 1998
@@ -13,7 +13,6 @@
 #include <linux/tty.h>
 #include <linux/console.h>
 #include <linux/string.h>
-#include <linux/config.h>
 #include <linux/fb.h>
 
 #include "fbcon.h"
diff -u --recursive --new-file v2.1.87/linux/drivers/video/fonts.c linux/drivers/video/fonts.c
--- v2.1.87/linux/drivers/video/fonts.c	Mon Nov 24 01:18:58 1997
+++ linux/drivers/video/fonts.c	Fri Feb 20 18:28:23 1998
@@ -9,6 +9,7 @@
  */
 
 
+#include <linux/config.h> /* for CONFIG_AMIGA */
 #include <linux/types.h>
 #include <linux/string.h>
 #ifdef __mc68000__
diff -u --recursive --new-file v2.1.87/linux/fs/adfs/super.c linux/fs/adfs/super.c
--- v2.1.87/linux/fs/adfs/super.c	Tue Feb 17 13:12:47 1998
+++ linux/fs/adfs/super.c	Thu Feb 19 14:46:15 1998
@@ -331,7 +331,7 @@
 #ifdef MODULE
 int init_module (void)
 {
-	return (init_adfs_fs());
+	return init_adfs_fs();
 }
 
 void cleanup_module (void)
diff -u --recursive --new-file v2.1.87/linux/fs/buffer.c linux/fs/buffer.c
--- v2.1.87/linux/fs/buffer.c	Thu Jan 15 21:01:04 1998
+++ linux/fs/buffer.c	Thu Feb 19 01:09:47 1998
@@ -52,10 +52,14 @@
 #define NR_RESERVED (2*MAX_BUF_PER_PAGE)
 #define MAX_UNUSED_BUFFERS NR_RESERVED+20 /* don't ever have more than this 
 					     number of unused buffer heads */
-#define HASH_PAGES         4  /* number of pages to use for the hash table */
-#define HASH_PAGES_ORDER   2
-#define NR_HASH (HASH_PAGES*PAGE_SIZE/sizeof(struct buffer_head *))
-#define HASH_MASK (NR_HASH-1)
+
+/*
+ * How large a hash table do we need?
+ */
+#define HASH_PAGES_ORDER	4
+#define HASH_PAGES		(1UL << HASH_PAGES_ORDER)
+#define NR_HASH			(HASH_PAGES*PAGE_SIZE/sizeof(struct buffer_head *))
+#define HASH_MASK		(NR_HASH-1)
 
 static int grow_buffers(int pri, int size);
 
diff -u --recursive --new-file v2.1.87/linux/fs/coda/psdev.c linux/fs/coda/psdev.c
--- v2.1.87/linux/fs/coda/psdev.c	Tue Jan  6 10:00:21 1998
+++ linux/fs/coda/psdev.c	Fri Feb 20 18:28:23 1998
@@ -17,6 +17,7 @@
  *              Copyright (c) 1997 Carnegie-Mellon University
  */
 
+#include <linux/config.h> /* for CONFIG_PROC_FS */
 #include <linux/module.h>
 #include <linux/errno.h>
 #include <linux/kernel.h>
diff -u --recursive --new-file v2.1.87/linux/fs/coda/super.c linux/fs/coda/super.c
--- v2.1.87/linux/fs/coda/super.c	Thu Feb 12 20:56:11 1998
+++ linux/fs/coda/super.c	Fri Feb 20 18:28:23 1998
@@ -9,7 +9,6 @@
  */
 
 #define __NO_VERSION__
-#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/mm.h>
diff -u --recursive --new-file v2.1.87/linux/fs/coda/sysctl.c linux/fs/coda/sysctl.c
--- v2.1.87/linux/fs/coda/sysctl.c	Sun Dec 21 14:45:14 1997
+++ linux/fs/coda/sysctl.c	Fri Feb 20 18:28:23 1998
@@ -8,7 +8,6 @@
  */
 /* sysctl entries for Coda! */
 
-#include <linux/config.h>
 #include <linux/sched.h>
 #include <linux/mm.h>
 #include <linux/sysctl.h>
diff -u --recursive --new-file v2.1.87/linux/fs/ext2/namei.c linux/fs/ext2/namei.c
--- v2.1.87/linux/fs/ext2/namei.c	Thu Feb 12 20:56:12 1998
+++ linux/fs/ext2/namei.c	Fri Feb 20 15:08:37 1998
@@ -853,9 +853,7 @@
 	struct ext2_dir_entry * old_de, * new_de;
 	int retval;
 
-	old_inode = new_inode = NULL;
 	old_bh = new_bh = dir_bh = NULL;
-	new_de = NULL;
 	retval = -ENAMETOOLONG;
 	if (old_dentry->d_name.len > EXT2_NAME_LEN)
 		goto end_rename;
@@ -875,7 +873,8 @@
 		goto end_rename;
 
 	new_inode = new_dentry->d_inode;
-	new_bh = ext2_find_entry (new_dir, new_dentry->d_name.name, new_dentry->d_name.len, &new_de);
+	new_bh = ext2_find_entry (new_dir, new_dentry->d_name.name,
+				new_dentry->d_name.len, &new_de);
 	if (new_bh) {
 		if (!new_inode) {
 			brelse (new_bh);
@@ -895,6 +894,9 @@
 		retval = -EINVAL;
 		if (is_subdir(new_dentry, old_dentry))
 			goto end_rename;
+		/* Prune any children before testing for busy */
+		if (new_dentry->d_count > 1)
+			shrink_dcache_parent(new_dentry);
 		retval = -ENOTEMPTY;
 		if (!empty_dir (new_inode))
 			goto end_rename;
@@ -927,11 +929,13 @@
 		if (!new_inode && new_dir->i_nlink >= EXT2_LINK_MAX)
 			goto end_rename;
 	}
-	if (!new_bh)
-		new_bh = ext2_add_entry (new_dir, new_dentry->d_name.name, new_dentry->d_name.len, &new_de,
-					 &retval);
-	if (!new_bh)
-		goto end_rename;
+	if (!new_bh) {
+		new_bh = ext2_add_entry (new_dir, new_dentry->d_name.name,
+					new_dentry->d_name.len, &new_de,
+					&retval);
+		if (!new_bh)
+			goto end_rename;
+	}
 	new_dir->i_version = ++event;
 
 	/*
@@ -975,6 +979,7 @@
 	/* Update the dcache */
 	d_move(old_dentry, new_dentry);
 	retval = 0;
+
 end_rename:
 	brelse (dir_bh);
 	brelse (old_bh);
diff -u --recursive --new-file v2.1.87/linux/fs/fat/fatfs_syms.c linux/fs/fat/fatfs_syms.c
--- v2.1.87/linux/fs/fat/fatfs_syms.c	Thu Jan  8 14:02:41 1998
+++ linux/fs/fat/fatfs_syms.c	Fri Feb 20 18:28:23 1998
@@ -6,7 +6,6 @@
  */
 #define ASC_LINUX_VERSION(V, P, S)	(((V) * 65536) + ((P) * 256) + (S))
 #include <linux/version.h>
-#include <linux/config.h>
 #include <linux/module.h>
 
 #include <linux/mm.h>
diff -u --recursive --new-file v2.1.87/linux/fs/hfs/super.c linux/fs/hfs/super.c
--- v2.1.87/linux/fs/hfs/super.c	Mon Jan 12 14:46:24 1998
+++ linux/fs/hfs/super.c	Fri Feb 20 18:28:23 1998
@@ -27,6 +27,7 @@
 #include <linux/hfs_fs_i.h>
 #include <linux/hfs_fs.h>
 
+#include <linux/config.h> /* for CONFIG_MAC_PARTITION */
 #include <linux/blkdev.h>
 #include <linux/module.h>
 #include <linux/init.h>
diff -u --recursive --new-file v2.1.87/linux/fs/locks.c linux/fs/locks.c
--- v2.1.87/linux/fs/locks.c	Tue Dec  2 22:20:18 1997
+++ linux/fs/locks.c	Fri Feb 20 15:04:45 1998
@@ -441,30 +441,32 @@
 	return (posix_lock_file(filp, &file_lock, cmd == F_SETLKW));
 }
 
-/* This function is called when the file is closed.
+/*
+ * This function is called when the file is being removed
+ * from the task's fd array.
  */
-void locks_remove_locks(struct task_struct *task, struct file *filp)
+void locks_remove_posix(struct task_struct *task, struct file *filp)
 {
+	struct inode * inode = filp->f_dentry->d_inode;
 	struct file_lock file_lock, *fl;
 	struct file_lock **before;
-	struct inode * inode;
 
-	/* For POSIX locks we free all locks on this file for the given task.
-	 * For FLOCK we only free locks on this *open* file if it is the last
-	 * close on that file.
+	/*
+	 * For POSIX locks we free all locks on this file for the given task.
 	 */
-	inode = filp->f_dentry->d_inode;
 repeat:
 	before = &inode->i_flock;
 	while ((fl = *before) != NULL) {
-		if (((fl->fl_flags & FL_POSIX) && (fl->fl_owner == task)) ||
-		    ((fl->fl_flags & FL_FLOCK) && (fl->fl_file == filp) &&
-		     (filp->f_count == 1))) {
-			file_lock = *fl;
-			locks_delete_lock(before, 0);
-			if (filp->f_op->lock) {
+		if ((fl->fl_flags & FL_POSIX) && fl->fl_owner == task) {
+			int (*lock)(struct file *, int, struct file_lock *);
+			lock = filp->f_op->lock;
+			if (lock) {
+				file_lock = *fl;
 				file_lock.fl_type = F_UNLCK;
-				filp->f_op->lock(filp, F_SETLK, &file_lock);
+			}
+			locks_delete_lock(before, 0);
+			if (lock) {
+				lock(filp, F_SETLK, &file_lock);
 				/* List may have changed: */
 				goto repeat;
 			}
@@ -472,8 +474,37 @@
 		}
 		before = &fl->fl_next;
 	}
+}
 
-	return;
+/*
+ * This function is called on the last close of an open file.
+ */
+void locks_remove_flock(struct file *filp)
+{
+	struct inode * inode = filp->f_dentry->d_inode; 
+	struct file_lock file_lock, *fl;
+	struct file_lock **before;
+
+repeat:
+	before = &inode->i_flock;
+	while ((fl = *before) != NULL) {
+		if ((fl->fl_flags & FL_FLOCK) && fl->fl_file == filp) {
+			int (*lock)(struct file *, int, struct file_lock *);
+			lock = filp->f_op->lock;
+			if (lock) {
+				file_lock = *fl;
+				file_lock.fl_type = F_UNLCK;
+			}
+			locks_delete_lock(before, 0);
+			if (lock) {
+				lock(filp, F_SETLK, &file_lock);
+				/* List may have changed: */
+				goto repeat;
+			}
+			continue;
+		}
+		before = &fl->fl_next;
+	}
 }
 
 struct file_lock *
diff -u --recursive --new-file v2.1.87/linux/fs/msdos/msdosfs_syms.c linux/fs/msdos/msdosfs_syms.c
--- v2.1.87/linux/fs/msdos/msdosfs_syms.c	Mon Jun 16 16:35:57 1997
+++ linux/fs/msdos/msdosfs_syms.c	Fri Feb 20 18:28:23 1998
@@ -5,7 +5,6 @@
  * These symbols are used by umsdos.
  */
 
-#include <linux/config.h>
 #include <linux/module.h>
 
 #include <linux/mm.h>
diff -u --recursive --new-file v2.1.87/linux/fs/ntfs/support.c linux/fs/ntfs/support.c
--- v2.1.87/linux/fs/ntfs/support.c	Fri Jan  2 01:42:59 1998
+++ linux/fs/ntfs/support.c	Fri Feb 20 18:28:23 1998
@@ -7,9 +7,6 @@
  *
  */
 
-#ifdef NTFS_IN_LINUX_KERNEL
-#include <linux/config.h>
-#endif
 #include "types.h"
 #include "struct.h"
 #include "support.h"
diff -u --recursive --new-file v2.1.87/linux/fs/open.c linux/fs/open.c
--- v2.1.87/linux/fs/open.c	Fri Jan 23 18:10:32 1998
+++ linux/fs/open.c	Fri Feb 20 15:04:45 1998
@@ -745,17 +745,14 @@
 
 int close_fp(struct file *filp)
 {
-	struct dentry *dentry;
-	struct inode *inode;
+	struct dentry *dentry = filp->f_dentry;
 
 	if (filp->f_count == 0) {
 		printk("VFS: Close: file count is 0\n");
 		return 0;
 	}
-	dentry = filp->f_dentry;
-	inode = dentry->d_inode;
-	if (inode)
-		locks_remove_locks(current, filp);
+	if (dentry->d_inode)
+		locks_remove_posix(current, filp);
 	return fput(filp);
 }
 
diff -u --recursive --new-file v2.1.87/linux/fs/romfs/inode.c linux/fs/romfs/inode.c
--- v2.1.87/linux/fs/romfs/inode.c	Sun Jan  4 00:53:43 1998
+++ linux/fs/romfs/inode.c	Fri Feb 20 18:28:23 1998
@@ -48,7 +48,6 @@
  * to squeeze some more bytes out of this code.. :)
  */
 
-#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/types.h>
 #include <linux/errno.h>
diff -u --recursive --new-file v2.1.87/linux/fs/sysv/inode.c linux/fs/sysv/inode.c
--- v2.1.87/linux/fs/sysv/inode.c	Wed Feb  4 11:36:01 1998
+++ linux/fs/sysv/inode.c	Fri Feb 20 18:28:23 1998
@@ -20,7 +20,6 @@
  *  the superblock.
  */
 
-#include <linux/config.h>
 #include <linux/module.h>
 
 #include <linux/sched.h>
diff -u --recursive --new-file v2.1.87/linux/fs/ufs/ufs_super.c linux/fs/ufs/ufs_super.c
--- v2.1.87/linux/fs/ufs/ufs_super.c	Tue Feb 17 13:12:48 1998
+++ linux/fs/ufs/ufs_super.c	Fri Feb 20 18:28:23 1998
@@ -32,7 +32,6 @@
 /*#define DEBUG_UFS_SUPER 1*/
 /* Uncomment the line above when hacking ufs superblock code */
 
-#include <linux/config.h>
 #include <linux/module.h>
 
 #include <linux/kernel.h>
diff -u --recursive --new-file v2.1.87/linux/fs/umsdos/README-WIP.txt linux/fs/umsdos/README-WIP.txt
--- v2.1.87/linux/fs/umsdos/README-WIP.txt	Tue Feb 17 13:12:48 1998
+++ linux/fs/umsdos/README-WIP.txt	Fri Feb 20 09:44:57 1998
@@ -1,7 +1,14 @@
 Changes by Matija Nalis (mnalis@jagor.srce.hr) on umsdos dentry fixing
 (started by Peter T. Waltenberg <peterw@karaka.chch.cri.nz>)
 
-Current status (980211) - UMSDOS dentry-WIP-Beta 0.82:
+--------- WARNING --------- WARNING --------- WARNING -----------
+THIS IS TRULY EXPERIMENTAL. IT IS NOT BETA YET. PLEASE EXCUSE MY
+YELLING, BUT ANY USE OF THIS MODULES MAY VERY WELL DESTROY YOUR
+UMSDOS FILESYSTEM, AND MAYBE EVEN OTHER FILESYSTEMS IN USE.
+YOU'VE BEEN WARNED.
+--------- WARNING --------- WARNING --------- WARNING -----------
+
+Current status (980220) - UMSDOS dentry-WIP-Beta 0.82-1:
 
 (1) pure MSDOS (no --linux-.--- EMD file):
 
@@ -19,40 +26,49 @@
 
 (2) umsdos (with --linux-.--- EMD file):
 
-- readdir - works. problems with symlink/hardlink resolving. see below.
-- lookup -  works. problems with symlink/hardlink resolving. see below.
+- readdir - works.
+- lookup -  works.
 - permissions/owners stuff - works
 - long file names - works
 - read file - works
-- switching MSDOS/UMSDOS - untested
+- switching MSDOS/UMSDOS - works?
 - switching UMSDOS/MSDOS - untested
 - pseudo root things - commented out mostly currently. To be fixed when
   dentries stuff is straightened out.
-- resolve symlink - seems to work (ls -l shows both symlink and filename it
-  points to, but dereferencing does not work. see below)
-- dereference symlink - for some strange reason does not work. Is
-  follow_symlink now obligatory (in contrary what is said in
-  linux/Documentation/fs/vfs.txt ?) - working on it
-- hard links - totally defunct currently.
+- resolve symlink - seems to work fully now!
+- dereference symlink - seems to work fully now!
+- hard links - seems to work now
 - special files (block/char device, fifos, sockets...) - seems to work ok.
 - other ioctls - mostly untested
+- dangling symlink - UNTESTED !
 
-- create symlink	- WARNING: NONE OF WRITE OPERATIONS FIXED YET!
-- create hardlink	- WARNING: NONE OF WRITE OPERATIONS FIXED YET!
-- creat file		- WARNING: NONE OF WRITE OPERATIONS FIXED YET!
-- write file		- WARNING: NONE OF WRITE OPERATIONS FIXED YET!
-- mkdir			- WARNING: NONE OF WRITE OPERATIONS FIXED YET!
-- rmdir 		- WARNING: NONE OF WRITE OPERATIONS FIXED YET!
-- umssyncing		- WARNING: NONE OF WRITE OPERATIONS FIXED YET!
+- create symlink		- WARNING: NONE OF WRITE OPERATIONS FIXED YET!
+- create hardlink		- WARNING: NONE OF WRITE OPERATIONS FIXED YET!
+- create file			- creates, but corrupts. after reboot seem ok ?
+- create special file		- WARNING: NONE OF WRITE OPERATIONS FIXED YET!
+- write to file			- WARNING: NONE OF WRITE OPERATIONS FIXED YET!
+- rename file (same dir)	- WARNING: NONE OF WRITE OPERATIONS FIXED YET!
+- rename file (dif. dir)	- WARNING: NONE OF WRITE OPERATIONS FIXED YET!
+- rename dir (same dir)		- WARNING: NONE OF WRITE OPERATIONS FIXED YET!
+- rename dir (dif. dir)		- WARNING: NONE OF WRITE OPERATIONS FIXED YET!
+- delete file			- WARNING: NONE OF WRITE OPERATIONS FIXED YET!
+- delete hardlink		- WARNING: NONE OF WRITE OPERATIONS FIXED YET!
+- mkdir				- WARNING: NONE OF WRITE OPERATIONS FIXED YET!
+- rmdir 			- WARNING: NONE OF WRITE OPERATIONS FIXED YET!
+- umssyncing			- WARNING: NONE OF WRITE OPERATIONS FIXED YET!
 
-Notes: heavy dentry kernel structures trashing. Probably some other kernel
-structures compromised. Have SysRq support compiled in, and use
+Notes: moderate dentry/inode kernel structures trashing. Probably some other
+kernel structures compromised. Have SysRq support compiled in, and use
 Sync/Emergency-remount-RO. And don't try mounting read/write yet - and then
-you should have no big problems... 
+you should have no big problems...
 
 Notes2: kernel structures trashing seems to be _MUCH_ lower if no
 symlinks/hardlink present. (hardlinks seem to be biggest problem)
 
+Notes3: Notes2 is probably somewhat outdated now that hardlink/symlink stuff
+is supposed to be fixed enough to work, but I haven't got the time to test
+it.
+
 ------------------------------------------------------------------------------
 
 Some general notes:
@@ -61,16 +77,20 @@
 most of them off, or 4 to turn all but really fatal ones off. Probably good
 idea to kill klogd/syslogd so it will only go to console.
 
-It should work enough to test it, but probably not enough to give you second
-chance to umount/rmmod module, recompile it, and reinsert it again. This is
-first on my list of things to get fixed, as it would greatly improve speed
-of compile/test/reboot/set_environment/recompile cycle by removing
-'reboot/set_environment' component.
+It should work enough to test it, even enough to give you few chances to
+umount/rmmod module, recompile it, and reinsert it again (but probably
+screaming about still used inodes on device and stuff). This is first on
+my list of things to get fixed, as it would greatly improve speed of
+compile/test/reboot/set_environment/recompile cycle by removing
+'reboot/set_environment' component that now occures every few cycles.
 
 But I need some help from someone knowing about dentries/inodes use more
-than I. If you can help, please contact me...
+than I. If you can help, please contact me... I'm mostly worries about
+iget/iput and dget/dput, and deallocating temporary dentries we create.
+should we destroy temp dentries ? using d_invalidate ? using d_drop ? just
+dput them ?
 
-I'm unfortunatelly totally out of time to read linux-kernel, but I do check
+I'm unfortunatelly somewhat out of time to read linux-kernel, but I do check
 for any messages having UMSDOS in subject, and read them. I should reply to
 any direct Email in few days. If I don't - probably I never got your
 message. You can try mnalis@open.hr or mnalis@voyager.hr; however
@@ -80,10 +100,11 @@
 
 some of my notes for myself:
 
-- hardlinks/symlinks. test with files in not_the_same_dir
++ hardlinks/symlinks. test with files in not_the_same_dir
 - also test not_the_same_dir for other file operations like rename etc.
 - iput: device 00:00 inode 318 still has aliases! problem. Check in iput()
   for device 0,0. Probably null pointer passed arount when it shouldn't be ?
 - dput/iput problem...
 - what about .dotfiles ? working ? multiple dots ? etc....
+- fix stuff like dir->i_count++ to atomic_inc(&dir->i_count) and simular?
 
diff -u --recursive --new-file v2.1.87/linux/fs/umsdos/dir.c linux/fs/umsdos/dir.c
--- v2.1.87/linux/fs/umsdos/dir.c	Tue Feb 17 13:12:48 1998
+++ linux/fs/umsdos/dir.c	Fri Feb 20 09:44:57 1998
@@ -30,18 +30,6 @@
    I've retained this to facilitate the lookup of some of the hard-wired files/directories UMSDOS
    uses. It's easier to do once than hack all the other instances. Probably safer as well
 */   
-int compat_UMSDOS_lookup(struct inode *dir,const char *name,int len, struct inode **inode)
-{
-  int rv;
-  struct dentry *dentry;
-
-  dentry = creat_dentry (name, len, NULL);
-  rv =  UMSDOS_lookup(dir,dentry);
-  
-  if (inode) *inode = dentry->d_inode;
-  return rv;
-}    
-
 
 int compat_umsdos_real_lookup(struct inode *dir,const char *name,int len, struct inode **inode)
 {
@@ -102,12 +90,7 @@
   int ret = -EINVAL;
   struct UMSDOS_DIR_ONCE *d = (struct UMSDOS_DIR_ONCE *)buf;
   if (d->count == 0){
-#if 0
-    char zname[100];
-    memcpy (zname,dentry->d_name,dentry->d_len);
-    zname[name_len] = '\0';
-    Printk ((KERN_DEBUG "dir_once :%s: offset %Ld\n",zname,offset));
-#endif
+    PRINTK ((KERN_DEBUG "dir_once :%.*s: offset %Ld\n", dentry->d_len, dentry->d_name, offset));
     ret = d->filldir (d->dirbuf,name,len,offset,ino);
     d->stop = ret < 0;
     d->count = 1;
@@ -218,9 +201,9 @@
     emd_dir = umsdos_emd_dir_lookup(dir,0);
     if (emd_dir != NULL){
       off_t start_fpos = filp->f_pos;
-      Printk (("umsdos_readdir_x: emd_dir->i_ino=%d\n",emd_dir->i_ino));
+      Printk (("umsdos_readdir_x: emd_dir->i_ino=%ld\n",emd_dir->i_ino));
       if (filp->f_pos <= UMSDOS_SPECIAL_DIRFPOS+1) filp->f_pos = 0;
-      Printk (("f_pos %lu i_size %ld\n",filp->f_pos,emd_dir->i_size));
+      Printk (("f_pos %Ld i_size %ld\n",filp->f_pos,emd_dir->i_size));
       ret = 0;
       while (filp->f_pos < emd_dir->i_size){
 	struct umsdos_dirent entry;
@@ -342,7 +325,7 @@
   while (ret == 0 && bufk.stop == 0){
     struct umsdos_dirent entry;
     bufk.count = 0;
-    Printk (("UMSDOS_readdir: calling _x (%p,%p,%p,%d,%p,%d,%d)\n",dir,filp,&bufk,0,&entry,1,umsdos_dir_once));
+    Printk (("UMSDOS_readdir: calling _x (%p,%p,%p,%d,%p,%d,%p)\n",dir,filp,&bufk,0,&entry,1,umsdos_dir_once));
     ret = umsdos_readdir_x (dir,filp,&bufk,0,&entry,1,umsdos_dir_once);
     if (bufk.count == 0) break;
     count += bufk.count;
@@ -639,7 +622,7 @@
 	if (ret == 0){
 	  ptbpath -= entry.name_len;
 	  memcpy (ptbpath,entry.name,entry.name_len);
-	  Printk (("ptbpath :%s: ",ptbpath));
+	  Printk (("ptbpath :%.*s: ",entry.name_len,ptbpath));
 	}
       }
     }else{
@@ -657,7 +640,7 @@
 	  ptbpath -= entry.name_len;
 	  memcpy (ptbpath,entry.name,entry.name_len);
 	  dir = adir;
-	  Printk (("ptbpath :%s: ",ptbpath));
+	  Printk (("ptbpath :%.*s: ",entry.name_len,ptbpath));
 	}else{
 	  break;
 	}
@@ -697,7 +680,7 @@
   Check if a file exist in the current directory.
   Return 0 if ok, negative error code if not (ex: -ENOENT).
 */
-static int umsdos_lookup_x (
+int umsdos_lookup_x (
 			    struct inode *dir,
 			    struct dentry *dentry,
 			    int nopseudo)/* Don't care about pseudo root mode */
@@ -760,7 +743,7 @@
     struct umsdos_info info;
     ret = umsdos_parse (dentry->d_name.name,dentry->d_name.len,&info);
     if (ret == 0) ret = umsdos_findentry (dir,&info,0);
-    Printk (("lookup %s pos %lu ret %d len %d ",info.fake.fname,info.f_pos,ret
+    Printk (("lookup %.*s pos %lu ret %d len %d ",info.fake.len,info.fake.fname,info.f_pos,ret
 	     ,info.fake.len));
     if (ret == 0){
       /* #Specification: umsdos / lookup
@@ -776,14 +759,14 @@
       
       ret = compat_umsdos_real_lookup (dir,info.fake.fname,info.fake.len,&inode);
 
-      Printk (("umsdos_lookup_x: compat_umsdos_real_lookup for %25s returned %d with inode=%p\n", info.fake.fname, ret, inode));
+      Printk ((KERN_DEBUG "umsdos_lookup_x: compat_umsdos_real_lookup for %.*s returned %d with inode=%p\n", info.fake.len, info.fake.fname, ret, inode));
 
       if (inode == NULL){
-	printk (KERN_WARNING "UMSDOS: Erase entry %s, out of sync with MsDOS\n"
-		,info.fake.fname);
+	printk (KERN_WARNING "UMSDOS: Erase entry %.*s, out of sync with MsDOS\n"
+		,info.fake.len, info.fake.fname);
 	umsdos_delentry (dir,&info,S_ISDIR(info.entry.mode));
       }else{
-        Printk ((KERN_DEBUG "umsdos_lookup_x /mn/ debug: ino=%d\n",inode->i_ino));
+        Printk ((KERN_DEBUG "umsdos_lookup_x /mn/ debug: ino=%li\n", inode->i_ino));
 
         /* we've found it. now get inode and put it in dentry. Is this ok /mn/ ? */
         dentry->d_inode = iget(dir->i_sb, inode->i_ino);
@@ -792,7 +775,7 @@
 	Printk (("lookup ino %ld flags %d\n",inode->i_ino
 		 ,info.entry.flags));
 	if (info.entry.flags & UMSDOS_HLINK){
-	  Printk ((KERN_ERR "umsdos_lookup_x: warning: untested /mn/ HLINK\n"));
+	  Printk ((KERN_DEBUG "umsdos_lookup_x: here goes HLINK\n"));
 	  ret = umsdos_hlink2inode (inode,&dentry->d_inode);
 	}
 	if (pseudo_root && dentry->d_inode == pseudo_root && !nopseudo){
@@ -830,7 +813,18 @@
 		   struct inode *dir,
 		   struct dentry *dentry)
 {
-  return umsdos_lookup_x(dir,dentry,0);
+  int ret;
+  ret = umsdos_lookup_x(dir,dentry,0);
+  
+#if 1
+  if (ret == -ENOENT) {
+    Printk ((KERN_INFO "UMSDOS_lookup: converting -ENOENT to negative dentry !\n"));
+    d_add (dentry, NULL);	/* create negative dentry if not found */
+    ret = 0;
+  }
+#endif
+  
+  return ret;
 }
 
 
@@ -895,17 +889,18 @@
 	    if (dir->u.umsdos_i.i_emd_dir == 0){
 	      /* This is a DOS directory */
 		
-	      Printk (("hlink2inode /mn/: doing umsdos_rlookup_x on %20s\n", dentry_dst->d_name.name));
+	      Printk (("hlink2inode /mn/: doing umsdos_rlookup_x on %.*s\n", (int) dentry_dst->d_name.len, dentry_dst->d_name.name));
 	      ret = umsdos_rlookup_x(dir,dentry_dst,1);
 	    }else{
-	      Printk (("hlink2inode /mn/: doing umsdos_lookup_x on %20s\n", dentry_dst->d_name.name));
+	      Printk (("hlink2inode /mn/: doing umsdos_lookup_x on %.*s\n", (int) dentry_dst->d_name.len, dentry_dst->d_name.name));
 	      ret = umsdos_lookup_x(dir,dentry_dst,1);
 	    }
 	    Printk (("  returned %d\n", ret));
+	    *result = dentry_dst->d_inode;	/* /mn/ ok ? */
 
 	    Printk (("h2n lookup :%s: -> %d ",start,ret));
 	    if (ret == 0 && *pt != '\0'){
-	      dir = dentry_dst->d_inode;
+	      dir = *result;
 	    }else{
 	      break;
 	    }
@@ -914,7 +909,7 @@
 	  Printk (("umsdos_hlink2inode: all those iput's() frighten me /mn/. Whatabout dput() ? FIXME!\n"));
 	  iput (hlink); /* FIXME */
       }
-    Printk (("hlink2inode ret = %d %p -> %p\n",ret,hlink,*result));
+    Printk (("hlink2inode ret = %d %p -> %p\n", ret, hlink, *result));
     kfree (path);
   }
   return ret;
diff -u --recursive --new-file v2.1.87/linux/fs/umsdos/emd.c linux/fs/umsdos/emd.c
--- v2.1.87/linux/fs/umsdos/emd.c	Tue Feb 17 13:12:48 1998
+++ linux/fs/umsdos/emd.c	Fri Feb 20 09:44:57 1998
@@ -28,15 +28,15 @@
  *
  */
  
-struct dentry *creat_dentry (const char *name, const int len, const struct inode *inode)
+struct dentry *creat_dentry (const char *name, const int len, struct inode *inode)
 {
     struct dentry *ret, *parent=NULL;	/* FIXME /mn/: whatis parent ?? */
     struct qstr qname;
     
     if (inode)
-      Printk (("/mn/ creat_dentry: creating dentry with inode=%d for %20s\n", inode->i_ino, name));
+      Printk (("/mn/ creat_dentry: creating dentry with inode=%lu for %.*s\n", inode->i_ino, len, name));
     else
-      Printk (("/mn/ creat_dentry: creating empty dentry for %20s\n", name));
+      Printk (("/mn/ creat_dentry: creating empty dentry for %.*s\n", len, name));
 
     qname.name = name;
     qname.len = len;
@@ -44,6 +44,7 @@
 
     ret = d_alloc (parent,&qname);	/* create new dentry */
     ret->d_inode = inode;
+    return ret;
 }
 
 
@@ -71,20 +72,20 @@
     filp->f_dentry = creat_dentry (UMSDOS_EMD_FILE, UMSDOS_EMD_NAMELEN, emd_dir);
     *offs = filp->f_pos;
     
-    Printk ((KERN_DEBUG "umsdos_file_read_kmem /mn/: Checkin: filp=%p, buf=%p, size=%ld, offs=%p\n", filp, buf, count, offs));
-    Printk ((KERN_DEBUG "  using emd=%d\n", emd_dir->i_ino));
-    Printk ((KERN_DEBUG "  inode=%d, i_size=%d\n", filp->f_dentry->d_inode->i_ino,filp->f_dentry->d_inode->i_size));
-    Printk ((KERN_DEBUG "  ofs=%ld\n", *offs));
-    Printk ((KERN_DEBUG "  f_pos=%ld\n", filp->f_pos));
-    Printk ((KERN_DEBUG "  name=%12s\n", filp->f_dentry->d_name.name));
-    Printk ((KERN_DEBUG "  i_binary(sb)=%d\n", MSDOS_I(filp->f_dentry->d_inode)->i_binary ));
-    Printk ((KERN_DEBUG "  f_count=%d, f_flags=%d\n", filp->f_count, filp->f_flags));
-    Printk ((KERN_DEBUG "  f_owner=%d\n", filp->f_owner));
-    Printk ((KERN_DEBUG "  f_version=%ld\n", filp->f_version));
-    Printk ((KERN_DEBUG "  f_reada=%ld, f_ramax=%ld, f_raend=%ld, f_ralen=%ld, f_rawin=%ld\n", filp->f_reada, filp->f_ramax, filp->f_raend, filp->f_ralen, filp->f_rawin));
+    PRINTK ((KERN_DEBUG "umsdos_file_read_kmem /mn/: Checkin: filp=%p, buf=%p, size=%d, offs=%p\n", filp, buf, count, offs));
+    PRINTK ((KERN_DEBUG "  using emd=%ld\n", emd_dir->i_ino));
+    PRINTK ((KERN_DEBUG "  inode=%lu, i_size=%lu\n", filp->f_dentry->d_inode->i_ino, filp->f_dentry->d_inode->i_size));
+    PRINTK ((KERN_DEBUG "  ofs=%ld\n",(unsigned long) *offs));
+    PRINTK ((KERN_DEBUG "  f_pos=%Lu\n", filp->f_pos));
+    PRINTK ((KERN_DEBUG "  name=%.*s\n", (int) filp->f_dentry->d_name.len, filp->f_dentry->d_name.name));
+    PRINTK ((KERN_DEBUG "  i_binary(sb)=%d\n", MSDOS_I(filp->f_dentry->d_inode)->i_binary ));
+    PRINTK ((KERN_DEBUG "  f_count=%d, f_flags=%d\n", filp->f_count, filp->f_flags));
+    PRINTK ((KERN_DEBUG "  f_owner=%d\n", filp->f_owner.uid));
+    PRINTK ((KERN_DEBUG "  f_version=%ld\n", filp->f_version));
+    PRINTK ((KERN_DEBUG "  f_reada=%ld, f_ramax=%ld, f_raend=%ld, f_ralen=%ld, f_rawin=%ld\n", filp->f_reada, filp->f_ramax, filp->f_raend, filp->f_ralen, filp->f_rawin));
 
     ret = fat_file_read(filp,buf,count,offs);
-    Printk ((KERN_DEBUG "fat_file_read returned with %ld!\n", ret));
+    PRINTK ((KERN_DEBUG "fat_file_read returned with %d!\n", ret));
 
     filp->f_pos = *offs;	/* we needed *filp only for this? grrrr... /mn/ */
     				/* FIXME: I have no idea what f_pos is used for. It seems to be used this way before offs was introduced.
@@ -98,24 +99,24 @@
     						 /* FIXME: we probably need to destroy originl filp->f_dentry first ? Do we ? And how ? this way we leave all sorts of dentries, inodes etc. lying around */
     						 /* Also FIXME: all the same problems in umsdos_file_write_kmem */
 
-    Printk ((KERN_DEBUG "  (ret) using emd=%d\n", emd_dir->i_ino));
-    Printk ((KERN_DEBUG "  (ret) inode=%d, i_size=%d\n", filp->f_dentry->d_inode->i_ino,filp->f_dentry->d_inode->i_size));
-    Printk ((KERN_DEBUG "  (ret) ofs=%ld\n", *offs));
-    Printk ((KERN_DEBUG "  (ret) f_pos=%ld\n", filp->f_pos));
-    Printk ((KERN_DEBUG "  (ret) name=%12s\n", filp->f_dentry->d_name.name));
-    Printk ((KERN_DEBUG "  (ret) i_binary(sb)=%d\n", MSDOS_I(filp->f_dentry->d_inode)->i_binary ));
-    Printk ((KERN_DEBUG "  (ret) f_count=%d, f_flags=%d\n", filp->f_count, filp->f_flags));
-    Printk ((KERN_DEBUG "  (ret) f_owner=%d\n", filp->f_owner));
-    Printk ((KERN_DEBUG "  (ret) f_version=%ld\n", filp->f_version));
-    Printk ((KERN_DEBUG "  (ret) f_reada=%ld, f_ramax=%ld, f_raend=%ld, f_ralen=%ld, f_rawin=%ld\n", filp->f_reada, filp->f_ramax, filp->f_raend, filp->f_ralen, filp->f_rawin));
+    PRINTK ((KERN_DEBUG "  (ret) using emd=%lu\n", emd_dir->i_ino));
+    PRINTK ((KERN_DEBUG "  (ret) inode=%lu, i_size=%lu\n", filp->f_dentry->d_inode->i_ino, filp->f_dentry->d_inode->i_size));
+    PRINTK ((KERN_DEBUG "  (ret) ofs=%Lu\n", *offs));
+    PRINTK ((KERN_DEBUG "  (ret) f_pos=%Lu\n", filp->f_pos));
+    PRINTK ((KERN_DEBUG "  (ret) name=%.*s\n", (int) filp->f_dentry->d_name.len, filp->f_dentry->d_name.name));
+    PRINTK ((KERN_DEBUG "  (ret) i_binary(sb)=%d\n", MSDOS_I(filp->f_dentry->d_inode)->i_binary ));
+    PRINTK ((KERN_DEBUG "  (ret) f_count=%d, f_flags=%d\n", filp->f_count, filp->f_flags));
+    PRINTK ((KERN_DEBUG "  (ret) f_owner=%d\n", filp->f_owner.uid));
+    PRINTK ((KERN_DEBUG "  (ret) f_version=%ld\n", filp->f_version));
+    PRINTK ((KERN_DEBUG "  (ret) f_reada=%ld, f_ramax=%ld, f_raend=%ld, f_ralen=%ld, f_rawin=%ld\n", filp->f_reada, filp->f_ramax, filp->f_raend, filp->f_ralen, filp->f_rawin));
 
 #if 0
     {
       struct umsdos_dirent *mydirent=buf;
 
-      Printk ((KERN_DEBUG "  (DDD) uid=%d\n",mydirent->uid));
-      Printk ((KERN_DEBUG "  (DDD) gid=%d\n",mydirent->gid));
-      Printk ((KERN_DEBUG "  (DDD) name=>%20s<\n",mydirent->name));
+      PRINTK ((KERN_DEBUG "  (DDD) uid=%d\n",mydirent->uid));
+      PRINTK ((KERN_DEBUG "  (DDD) gid=%d\n",mydirent->gid));
+      PRINTK ((KERN_DEBUG "  (DDD) name=>%.20s<\n",mydirent->name));
     }
 #endif  
     
@@ -136,18 +137,29 @@
 {
 	int ret;
 	mm_segment_t old_fs = get_fs();
-	struct dentry *old_dentry;	/* FIXME /mn/: whatis parent ?? */
+	struct dentry *old_dentry;
 	
-	set_fs (KERNEL_DS);
-	ret = fat_file_write(filp,buf,count,offs);
+	Printk ((KERN_ERR " STARTED WRITE_KMEM /mn/\n"));
 
+        Printk ((KERN_ERR "umsdos_file_write_kmem /mn/: Checkin: filp=%p, buf=%p, size=%d, offs=%p\n", filp, buf, count, offs));
+        Printk ((KERN_ERR "  using emd=%ld\n", emd_dir->i_ino));
+        Printk ((KERN_ERR "  inode=%lu, i_size=%lu\n", filp->f_dentry->d_inode->i_ino, filp->f_dentry->d_inode->i_size));
+        Printk ((KERN_ERR "  ofs=%ld\n",(unsigned long) *offs));
+        Printk ((KERN_ERR "  f_pos=%Lu\n", filp->f_pos));
+        Printk ((KERN_ERR "  name=%.*s\n", (int) filp->f_dentry->d_name.len, filp->f_dentry->d_name.name));
+        Printk ((KERN_ERR "  i_binary(sb)=%d\n", MSDOS_I(filp->f_dentry->d_inode)->i_binary ));
+        Printk ((KERN_ERR "  f_count=%d, f_flags=%d\n", filp->f_count, filp->f_flags));
+        Printk ((KERN_ERR "  f_owner=%d\n", filp->f_owner.uid));
+        Printk ((KERN_ERR "  f_version=%ld\n", filp->f_version));
+        Printk ((KERN_ERR "  f_reada=%ld, f_ramax=%ld, f_raend=%ld, f_ralen=%ld, f_rawin=%ld\n", filp->f_reada, filp->f_ramax, filp->f_raend, filp->f_ralen, filp->f_rawin));
+	
+	set_fs (KERNEL_DS);
 	old_dentry=filp->f_dentry;	/* save it */
 	filp->f_dentry = creat_dentry (UMSDOS_EMD_FILE, UMSDOS_EMD_NAMELEN, emd_dir);
-
 	*offs = filp->f_pos;
 
-	ret = fat_file_write (filp,buf,count,offs);
-	PRINTK ((KERN_DEBUG "fat_file_write returned with %ld!\n", ret));
+	ret = fat_file_write (filp, buf, count, offs);
+	PRINTK ((KERN_ERR "fat_file_write returned with %ld!\n", ret));
 
 	filp->f_pos = *offs;
 	filp->f_dentry=old_dentry;
@@ -189,7 +201,9 @@
 #endif	
 	
 	if (offs) myofs=*offs;	/* if offs is not NULL, read it */
+	Printk (("umsdos_emd_dir_write /mn/: calling write_kmem with %p, %p, %p, %ld, %Ld\n", emd_dir, filp, buf, count, myofs));
 	written = umsdos_file_write_kmem (emd_dir, filp, buf, count, &myofs);
+	Printk (("umsdos_emd_dir_write /mn/: write_kmem returned\n"));
 	if (offs) *offs=myofs;	/* if offs is not NULL, store myofs there */
 	
 #ifdef __BIG_ENDIAN	
@@ -264,32 +278,32 @@
 {
     struct inode *ret = NULL;
     int res;
-    Printk (("Entering umsdos_emd_dir_lookup\n"));
+    PRINTK ((KERN_DEBUG "Entering umsdos_emd_dir_lookup\n"));
     if (dir->u.umsdos_i.i_emd_dir != 0){
 	ret = iget (dir->i_sb,dir->u.umsdos_i.i_emd_dir);
 	Printk (("umsdos_emd_dir_lookup: deja trouve %ld %p\n"
 		 ,dir->u.umsdos_i.i_emd_dir,ret));
     } else {
-      Printk ((KERN_DEBUG "umsdos /mn/: Looking for %20s -", UMSDOS_EMD_FILE));
+      PRINTK ((KERN_DEBUG "umsdos /mn/: Looking for %.*s -", UMSDOS_EMD_NAMELEN, UMSDOS_EMD_FILE));
       res = compat_umsdos_real_lookup (dir, UMSDOS_EMD_FILE, UMSDOS_EMD_NAMELEN, &ret);
-      Printk ((KERN_DEBUG "-returned %d\n", res));
-	Printk ((KERN_DEBUG "emd_dir_lookup "));
+      PRINTK ((KERN_DEBUG "-returned %d\n", res));
+	Printk ((KERN_INFO "emd_dir_lookup "));
 	if (ret != NULL){
-	    Printk ((KERN_DEBUG "Found --linux "));
+	    Printk (("Found --linux "));
 	    dir->u.umsdos_i.i_emd_dir = ret->i_ino;
 	} else if (creat) {
                         int code;
-                        Printk ((KERN_ERR " * ERROR * /mn/: creat not yet implemented!!!!" ));
+                        Printk ((" * ERROR * /mn/: creat not yet implemented!!!!" ));
                         Printk ((KERN_DEBUG "avant create "));
-                        atomic_inc(&dir->i_count);
+                        dir->i_count++;
 
                         code = compat_msdos_create (dir,UMSDOS_EMD_FILE,UMSDOS_EMD_NAMELEN
                                 ,S_IFREG|0777,&ret);
-                        Printk ((KERN_DEBUG "Creat EMD code %d ret %x ",code,ret));
+                        Printk ((KERN_WARNING "Creat EMD code %d ret %p ", code, ret));
                         if (ret != NULL){
                                 dir->u.umsdos_i.i_emd_dir = ret->i_ino;
                         }else{
-                                printk ("UMSDOS: Can't create EMD file\n");
+                                printk (KERN_WARNING "UMSDOS: Can't create EMD file\n");
                         }
 	}
 	
@@ -300,8 +314,10 @@
 
     }
     
-    Printk ((KERN_DEBUG "umsdos_emd_dir_lookup returning %p /mn/\n", ret));
-    if (ret != NULL) Printk ((KERN_DEBUG " debug : returning ino=%d\n",ret->i_ino));
+#if 0
+    PRINTK ((KERN_DEBUG "umsdos_emd_dir_lookup returning %p /mn/\n", ret));
+    if (ret != NULL) PRINTK ((KERN_DEBUG " debug : returning ino=%lu\n", ret->i_ino));
+#endif    
     return ret;
 }
 
@@ -316,7 +332,7 @@
 	struct inode *ret = NULL;
 	if (dir->u.umsdos_i.i_emd_dir != 0){
 		ret = iget (dir->i_sb,dir->u.umsdos_i.i_emd_dir);
-		Printk (("deja trouve %d %x",dir->u.umsdos_i.i_emd_dir,ret));
+		Printk (("deja trouve %lu %p", dir->u.umsdos_i.i_emd_dir, ret));
 	}else{
 	    
 	    int code;
@@ -329,7 +345,7 @@
 	       FIXME, I think I need a new dentry here
 	    */
 	    code = compat_msdos_create (dir,UMSDOS_EMD_FILE,UMSDOS_EMD_NAMELEN, S_IFREG|0777, &ret);
-	    Printk (("Creat EMD code %d ret %x ",code,ret));
+	    Printk (("Creat EMD code %d ret %p ", code, ret));
 	    if (ret != NULL){
 		dir->u.umsdos_i.i_emd_dir = ret->i_ino;
 	    }else{
@@ -358,7 +374,7 @@
 {
 	int ret;
 	Printk ((KERN_DEBUG "umsdos_emd_dir_readentry /mn/: entering.\n"));
-	Printk (("umsdos_emd_dir_readentry /mn/: trying to lookup %12s (ino=%d) using EMD %d\n", filp->f_dentry->d_name.name, filp->f_dentry->d_inode->i_ino, emd_dir->i_ino));
+	Printk (("umsdos_emd_dir_readentry /mn/: trying to lookup %.*s (ino=%lu) using EMD %lu\n", (int) filp->f_dentry->d_name.len, filp->f_dentry->d_name.name, filp->f_dentry->d_inode->i_ino, emd_dir->i_ino));
     
 	ret = umsdos_emd_dir_read(emd_dir, filp, (char*)entry, UMSDOS_REC_SIZE, NULL);
 	if (ret == 0){ /* note /mn/: is this wrong? ret is allways 0 or -EIO. but who knows. It used to work this way... */
@@ -389,14 +405,13 @@
 	int free_entry)		/* This entry is deleted, so Write all 0's */
 {
 	int ret = 0;
+	struct dentry *emd_dentry;
 	struct file filp;
 	struct umsdos_dirent *entry = &info->entry;
 	struct umsdos_dirent entry0;
 	
 	Printk (("umsdos_writeentry /mn/: entering...\n"));
-	
-	Printk ((KERN_ERR "umsdos_writeentry /mn/: FIXME! this is READ ONLY FOR NOW. RETURNING...\n"));
-	return -EIO;
+	emd_dentry=creat_dentry ("wremd_mn", 8, emd_dir);
 	
 	if (free_entry){
 		/* #Specification: EMD file / empty entries
@@ -421,9 +436,15 @@
 
 	Printk (("umsdos_writeentry /mn/: if passed...\n"));
 
+	if (!info) printk (KERN_ERR "umsdosfs: /mn/ info is empty ! ooops...\n");
 	filp.f_pos = info->f_pos;
 	filp.f_reada = 0;
-	ret = umsdos_emd_dir_write(emd_dir, &filp,(char*)entry,info->recsize,NULL);
+	filp.f_flags = O_RDWR;
+	filp.f_dentry = emd_dentry;
+       	filp.f_op = &umsdos_file_operations;	/* /mn/ - we have to fill it with dummy values so we won't segfault */
+       	
+	ret = umsdos_emd_dir_write (emd_dir, &filp, (char*)entry, info->recsize, NULL);
+	Printk (("emd_dir_write returned !\n"));
 	if (ret != 0){
 		printk ("UMSDOS: problem with EMD file. Can't write\n");
 	}else{
@@ -435,6 +456,8 @@
 	return ret;
 }
 
+
+
 #define CHUNK_SIZE (8*UMSDOS_REC_SIZE)
 struct find_buffer{
 	char buffer[CHUNK_SIZE];
@@ -462,7 +485,7 @@
 	int mustread;
 	int remain;
 	
-	Printk ((KERN_DEBUG "Entering umsdos_fillbuf, for inode %d, buf=%p\n",inode->i_ino, buf));
+	PRINTK ((KERN_DEBUG "Entering umsdos_fillbuf, for inode %lu, buf=%p\n", inode->i_ino, buf));
 	
 	if (mustmove > 0){
 		memcpy (buf->buffer,buf->buffer+buf->pos,mustmove);
@@ -518,8 +541,8 @@
 		record, multiple contiguous record are allocated.
 	*/
 	int ret = -ENOENT;
-	/* FIXME */
-	struct inode *emd_dir = umsdos_emd_dir_lookup(dir,1);
+	/* FIXME  -- /mn/ fixed ? */
+	struct inode *emd_dir = umsdos_emd_dir_lookup (dir, 1);
 	if (emd_dir != NULL){
 		struct umsdos_dirent *entry = &info->entry;
 		int recsize = info->recsize;
diff -u --recursive --new-file v2.1.87/linux/fs/umsdos/inode.c linux/fs/umsdos/inode.c
--- v2.1.87/linux/fs/umsdos/inode.c	Tue Feb 17 13:12:48 1998
+++ linux/fs/umsdos/inode.c	Fri Feb 20 09:44:57 1998
@@ -38,8 +38,8 @@
 
 void UMSDOS_put_inode(struct inode *inode)
 {
-  Printk ((KERN_DEBUG "put inode %x (%d) owner %x pos %d dir %x\n",inode, inode->i_ino
-	   ,inode->u.umsdos_i.i_emd_owner,inode->u.umsdos_i.pos
+  PRINTK ((KERN_DEBUG "put inode %p (%lu) owner %lu pos %lu dir %lu\n", inode, inode->i_ino
+	   ,inode->u.umsdos_i.i_emd_owner, inode->u.umsdos_i.pos
 	   ,inode->u.umsdos_i.i_emd_dir));
   if (inode && pseudo_root && inode == pseudo_root){
     printk (KERN_ERR "Umsdos: Oops releasing pseudo_root. Notify jacques@solucorp.qc.ca\n");
@@ -55,7 +55,7 @@
 
 void UMSDOS_put_super(struct super_block *sb)
 {
-  Printk (("UMSDOS_put_super: /mn/ entering\n"));
+  Printk ((KERN_DEBUG "UMSDOS_put_super: entering\n"));
   msdos_put_super(sb);
   MOD_DEC_USE_COUNT;
 }
@@ -73,10 +73,10 @@
 {
   int ret;
   
-  Printk (("umsdos_real_lookup /mn/: looking for %s /",dentry->d_name.name));
+  PRINTK ((KERN_DEBUG "umsdos_real_lookup /mn/: looking for %s /",dentry->d_name.name));
   dir->i_count++;  /* /mn/ what is this and why ? locking? */
   ret = msdos_lookup (dir,dentry);
-  Printk (("/ returned %d\n", ret));
+  PRINTK (("/ returned %d\n", ret));
   
   return ret;
 }
@@ -95,13 +95,13 @@
     extern struct inode_operations umsdos_rdir_inode_operations;
 
     emd_dir = umsdos_emd_dir_lookup (inode,0);
-    Printk (("umsdos_setup_dir_inode: umsdos_emd_dir_lookup for inode=%p returned %p\n",inode,emd_dir));
+    Printk ((KERN_DEBUG "umsdos_setup_dir_inode: umsdos_emd_dir_lookup for inode=%p returned %p\n",inode,emd_dir));
 
     if (emd_dir == NULL) {
-      Printk (("umsdos_setup_dir_inode /mn/: Setting up dir_inode_ops --> eg. NOT using EMD.\n"));
+      Printk ((KERN_DEBUG "umsdos_setup_dir_inode /mn/: Setting up dir_inode_ops --> eg. NOT using EMD.\n"));
       inode->i_op = &umsdos_rdir_inode_operations;
     } else {
-      Printk (("umsdos_setup_dir_inode /mn/: Setting up rdir_inode_ops --> eg. using EMD.\n"));
+      Printk ((KERN_DEBUG "umsdos_setup_dir_inode /mn/: Setting up rdir_inode_ops --> eg. using EMD.\n"));
       inode->i_op = &umsdos_dir_inode_operations;
     }
     
@@ -120,7 +120,7 @@
 {
     struct inode *emd_owner;
     /* FIXME, I don't have a clue on this one */
-    Printk (("umsdos_set_dirinfo: /mn/ FIXME: no clue\n"));
+    Printk ((KERN_WARNING "umsdos_set_dirinfo: /mn/ FIXME: no clue\n"));
     emd_owner = umsdos_emd_dir_lookup(dir,1);
     inode->u.umsdos_i.i_dir_owner = dir->i_ino;
     inode->u.umsdos_i.i_emd_owner = emd_owner->i_ino;
@@ -169,14 +169,17 @@
     them at the end. Doing that now introduce a problem. unmount
     always fail because some inodes are in use.
   */
+  
+  Printk ((KERN_DEBUG "Entering umsdos_patch_inode for inode=%lu\n", inode->i_ino));
+  
   if (!umsdos_isinit(inode)){
     inode->u.umsdos_i.i_emd_dir = 0;
     if (S_ISREG(inode->i_mode)){
       if (inode->i_op->bmap != NULL){
-        Printk (("umsdos_patch_inode /mn/: seting i_op = umsdos_file_inode_operations\n"));
+        Printk ((KERN_DEBUG "umsdos_patch_inode /mn/: seting i_op = umsdos_file_inode_operations\n"));
 	inode->i_op = &umsdos_file_inode_operations;
       }else{
-        Printk (("umsdos_patch_inode /mn/: seting i_op = umsdos_file_inode_operations_no_bmap\n"));
+        Printk ((KERN_DEBUG "umsdos_patch_inode /mn/: seting i_op = umsdos_file_inode_operations_no_bmap\n"));
 	inode->i_op = &umsdos_file_inode_operations_no_bmap;
       }
     }else if (S_ISDIR(inode->i_mode)){
@@ -184,16 +187,16 @@
 	umsdos_setup_dir_inode(inode);
       }
     }else if (S_ISLNK(inode->i_mode)){
-      Printk (("umsdos_patch_inode /mn/: seting i_op = umsdos_symlink_inode_operations\n"));
+      Printk ((KERN_DEBUG "umsdos_patch_inode /mn/: seting i_op = umsdos_symlink_inode_operations\n"));
       inode->i_op = &umsdos_symlink_inode_operations;
     }else if (S_ISCHR(inode->i_mode)){
-      Printk (("umsdos_patch_inode /mn/: seting i_op = chrdev_inode_operations\n"));
+      Printk ((KERN_DEBUG "umsdos_patch_inode /mn/: seting i_op = chrdev_inode_operations\n"));
       inode->i_op = &chrdev_inode_operations;
     }else if (S_ISBLK(inode->i_mode)){
-      Printk (("umsdos_patch_inode /mn/: seting i_op = blkdev_inode_operations\n"));
+      Printk ((KERN_DEBUG "umsdos_patch_inode /mn/: seting i_op = blkdev_inode_operations\n"));
       inode->i_op = &blkdev_inode_operations;
     }else if (S_ISFIFO(inode->i_mode)){
-      Printk (("umsdos_patch_inode /mn/: uhm, init_fifo\n"));
+      Printk ((KERN_DEBUG "umsdos_patch_inode /mn/: uhm, init_fifo\n"));
       init_fifo(inode);
     }
     if (dir != NULL){
@@ -207,7 +210,7 @@
 	This is done last because it also control the
 	status of umsdos_isinit()
       */
-      Printk (("umsdos_patch_inode /mn/: here we go: calling umsdos_set_dirinfo (%p,%p,%d)\n", inode, dir, f_pos));
+      PRINTK ((KERN_DEBUG "umsdos_patch_inode /mn/: here we go: calling umsdos_set_dirinfo (%p,%p,%lu)\n", inode, dir, f_pos));
       umsdos_set_dirinfo (inode,dir,f_pos);
     }
   }else if (dir != NULL){
@@ -217,7 +220,7 @@
     */
       /* FIXME, again, not a clue */
       struct inode *emd_owner;
-      Printk (("umsdos_patch_inode: /mn/ Warning: untested emd_owner thingy...\n"));
+      Printk ((KERN_WARNING "umsdos_patch_inode: /mn/ Warning: untested emd_owner thingy...\n"));
       emd_owner = umsdos_emd_dir_lookup(dir,1);
       iput (emd_owner);
       if (emd_owner->i_ino != inode->u.umsdos_i.i_emd_owner){
@@ -259,14 +262,14 @@
 */
 void UMSDOS_read_inode(struct inode *inode)
 {
-	Printk (("UMSDOS_read_inode %x ino = %d ",inode,inode->i_ino));
+	PRINTK ((KERN_DEBUG "UMSDOS_read_inode %p ino = %lu ",inode,inode->i_ino));
 	msdos_read_inode(inode);
-	PRINTK (("ino after msdos_read_inode= %d\n",inode->i_ino));
+	PRINTK (("ino after msdos_read_inode= %lu\n",inode->i_ino));
 	if (S_ISDIR(inode->i_mode)
 		&& (inode->u.umsdos_i.u.dir_info.creating != 0
 			|| inode->u.umsdos_i.u.dir_info.looking != 0
 			|| waitqueue_active(&inode->u.umsdos_i.u.dir_info.p))){
-		Printk (("read inode %d %d %p\n"
+		PRINTK (("read inode %d %d %p\n"
 			,inode->u.umsdos_i.u.dir_info.creating
 			,inode->u.umsdos_i.u.dir_info.looking
 			,inode->u.umsdos_i.u.dir_info.p));
@@ -319,7 +322,7 @@
   int ret = 0;
   struct inode *inode = dentry->d_inode;
   
-  Printk (("UMSDOS_notify_change: /mn/ completly untested\n"));
+  Printk ((KERN_ERR "UMSDOS_notify_change: /mn/ completly untested\n"));
   
   if ((ret = inode_change_ok(inode, attr)) != 0)
     return ret;
@@ -363,7 +366,7 @@
 	offs = 0;
 	filp.f_pos = inode->u.umsdos_i.pos;
 	filp.f_reada = 0;
-	Printk (("pos = %d ",filp.f_pos));
+	Printk (("pos = %Lu ", filp.f_pos));
 	/* Read only the start of the entry since we don't touch */
 	/* the name */
 	ret = umsdos_emd_dir_read (emd_owner, &filp, (char*)&entry, UMSDOS_REC_SIZE, &offs);
@@ -386,9 +389,9 @@
 	  offs = 0; /* FIXME */
 	  ret = umsdos_emd_dir_write (emd_owner, &filp, (char*)&entry, UMSDOS_REC_SIZE, &offs);
 	  
-	  Printk (("notify pos %d ret %d nlink %d "
+	  Printk (("notify pos %lu ret %d nlink %d "
 		   ,inode->u.umsdos_i.pos
-		   ,ret,entry.nlink));
+		   ,ret, entry.nlink));
 	  /* #Specification: notify_change / msdos fs
 	     notify_change operation are done only on the
 	     EMD file. The msdos fs is not even called.
@@ -449,21 +452,21 @@
   */
     struct super_block *res;
     struct inode *pseudo=NULL;
-    Printk (("UMSDOS /mn/: starting UMSDOS_read_super\n"));
+    Printk ((KERN_DEBUG "UMSDOS /mn/: starting UMSDOS_read_super\n"));
     MOD_INC_USE_COUNT;
-    Printk (("UMSDOS /mn/: sb = %p\n",sb));
+    PRINTK ((KERN_DEBUG "UMSDOS /mn/: sb = %p\n",sb));
     res = msdos_read_super(sb,data,silent);
-    Printk (("UMSDOS /mn/: res = %p\n",res));
-    printk (KERN_INFO "UMSDOS dentry-WIP-Beta 0.82 (compatibility level %d.%d, fast msdos)\n", UMSDOS_VERSION, UMSDOS_RELEASE);
+    PRINTK ((KERN_DEBUG "UMSDOS /mn/: res = %p\n",res));
+    printk (KERN_INFO "UMSDOS dentry-WIP-Beta 0.82-1 (compatibility level %d.%d, fast msdos)\n", UMSDOS_VERSION, UMSDOS_RELEASE);
 	  
     if (res == NULL) { MOD_DEC_USE_COUNT; return NULL; }
 
     MSDOS_SB(res)->options.dotsOK = 0;  /* disable hidden==dotfile */
     res->s_op = &umsdos_sops;
-    Printk (("umsdos /mn/: here goes the iget ROOT_INO\n"));
+    Printk ((KERN_DEBUG "umsdos /mn/: here goes the iget ROOT_INO\n"));
 
     pseudo = iget(res,UMSDOS_ROOT_INO); 		
-    Printk (("umsdos_read_super %p\n",pseudo));
+    Printk ((KERN_DEBUG "umsdos_read_super %p\n",pseudo));
 
     umsdos_setup_dir_inode (pseudo);
     
@@ -506,13 +509,13 @@
       root = creat_dentry (UMSDOS_PSDROOT_NAME, strlen(UMSDOS_PSDROOT_NAME), NULL);
       sbin = creat_dentry ("sbin", 4, NULL);
       
-      Printk (("Mounting root\n"));
+      Printk ((KERN_DEBUG "Mounting root\n"));
       if (umsdos_real_lookup (pseudo,root)==0
           && (root->d_inode != NULL)
 	  && S_ISDIR(root->d_inode->i_mode)){
 	
 	int pseudo_ok = 0;
-	Printk (("/%s is there\n",UMSDOS_PSDROOT_NAME));
+	Printk ((KERN_DEBUG "/%s is there\n",UMSDOS_PSDROOT_NAME));
 	etc = creat_dentry ("etc", 3, NULL);
 	
 	
@@ -520,7 +523,7 @@
 	if(umsdos_real_lookup(pseudo, etc) == 0
 	   && S_ISDIR(etc->d_inode->i_mode)){
 
-	    Printk (("/%s/etc is there\n",UMSDOS_PSDROOT_NAME));
+	    Printk ((KERN_DEBUG "/%s/etc is there\n",UMSDOS_PSDROOT_NAME));
 	    
 	    init = creat_dentry ("init", 4, NULL);
 	    etc_rc = creat_dentry ("rc", 2, NULL);
@@ -542,7 +545,7 @@
 	    && umsdos_real_lookup(pseudo, sbin) == 0
 	    && S_ISDIR(sbin->d_inode->i_mode)){
 	  
-	  Printk (("/%s/sbin is there\n",UMSDOS_PSDROOT_NAME));
+	  Printk ((KERN_DEBUG "/%s/sbin is there\n",UMSDOS_PSDROOT_NAME));
 	  /* if (umsdos_real_lookup (sbin,"init",4,init)==0 */
 	  if(umsdos_real_lookup(pseudo, init) == 0	
 	     && S_ISREG(init->d_inode->i_mode)){
@@ -553,7 +556,7 @@
 	}
 	if (pseudo_ok){
 	  umsdos_setup_dir_inode (pseudo);
-	  Printk (("Activating pseudo root /%s\n",UMSDOS_PSDROOT_NAME));
+	  Printk ((KERN_INFO "Activating pseudo root /%s\n",UMSDOS_PSDROOT_NAME));
 	  pseudo_root = pseudo;
 	  pseudo->i_count++;
 	  pseudo = NULL;
@@ -565,14 +568,14 @@
 	*/
       }
       
-      Printk (("umsdos_read_super /mn/: Pseudo should be iput-ed here...\n"));
+      Printk ((KERN_WARNING "umsdos_read_super /mn/: Pseudo should be iput-ed here...\n"));
 
       iput (pseudo); /* FIXME */
     }
 
 #endif /* disabled */
 
-    Printk (("umsdos_read_super /mn/: returning %p\n",res));
+    PRINTK ((KERN_DEBUG "umsdos_read_super /mn/: returning %p\n",res));
     return res;
 }
 
diff -u --recursive --new-file v2.1.87/linux/fs/umsdos/namei.c linux/fs/umsdos/namei.c
--- v2.1.87/linux/fs/umsdos/namei.c	Tue Feb 17 13:12:48 1998
+++ linux/fs/umsdos/namei.c	Fri Feb 20 09:44:57 1998
@@ -238,8 +238,8 @@
 	  struct inode *inode = dentry->d_inode;
 	  umsdos_lookup_patch (dir,inode,&info.entry,info.f_pos);
 	  Printk (("inode %p[%d] ",inode,inode->i_count));
-	  Printk (("Creation OK: [%d] %s %d pos %d\n",dir->i_ino
-		   ,info.fake.fname,current->pid,info.f_pos));
+	  Printk (("Creation OK: [%lu] %.*s %d pos %ld\n", dir->i_ino,
+		   info.fake.len, info.fake.fname, current->pid, info.f_pos));
 	}else{
 	  /* #Specification: create / file exist in DOS
 	     Here is a situation. Trying to create a file with
@@ -265,13 +265,13 @@
 	  */
 	  if (ret == -EEXIST){
 	    printk ("UMSDOS: out of sync, Creation error [%ld], "
-		    "deleting %s %d %d pos %ld\n",dir->i_ino
-		    ,info.fake.fname,-ret,current->pid,info.f_pos);
+		    "deleting %.*s %d %d pos %ld\n",dir->i_ino
+		    ,info.fake.len,info.fake.fname,-ret,current->pid,info.f_pos);
 	  }
 	  umsdos_delentry (dir,&info,0);
 	}
-	Printk (("umsdos_create %s ret = %d pos %d\n"
-		 ,info.fake.fname,ret,info.f_pos));
+	Printk (("umsdos_create %.*s ret = %d pos %ld\n",
+		 info.fake.len, info.fake.fname, ret, info.f_pos));
       }
       umsdos_unlockcreate(dir);
     }
@@ -379,7 +379,7 @@
 	      chkstk();
 	      if (ret == 0){
 		/*
-		  This UMSDOS_lookup does not look very useful.
+		  This umsdos_lookup_x does not look very useful.
 		  It makes sure that the inode of the file will
 		  be correctly setup (umsdos_patch_inode()) in
 		  case it is already in use.
@@ -388,13 +388,13 @@
 		*/
 		struct inode *inode;
 		new_dir->i_count++;
-		PRINTK (("rename lookup len %d %d -- ",new_len,new_info.entry.flags));
-		ret = UMSDOS_lookup (new_dir,new_dentry);
+		PRINTK ((KERN_DEBUG "rename lookup len %d %d -- ",new_len,new_info.entry.flags));
+		ret = umsdos_lookup_x (new_dir, new_dentry, 0);
 		inode = new_dentry->d_inode;
 		chkstk();
 		if (ret != 0){
-		  printk ("UMSDOS: partial rename for file %s\n"
-			  ,new_info.entry.name);
+		  printk ("UMSDOS: partial rename for file %.*s\n"
+			  ,new_info.entry.name_len,new_info.entry.name);
 		}else{
 		  /*
 		    Update f_pos so notify_change will succeed
@@ -459,7 +459,8 @@
     filp.f_pos = 0;
     /* Make the inode acceptable to MSDOS FIXME */
     Printk ((KERN_ERR "umsdos_symlink_x: FIXME /mn/ Here goes the crash.. known wrong code...\n"));
-    ret = umsdos_file_write_kmem (dentry->d_inode->i_ino, &filp,symname,ret,NULL);	/* FIXME /mn/: dentry->d_inode->i_ino is totaly wrong, just put in to compile the beast... */
+    ret = umsdos_file_write_kmem (dentry->d_inode, &filp,symname,ret,NULL);	/* FIXME /mn/: dentry->d_inode->i_ino is totaly wrong, just put in to compile the beast...
+ PTW dentry->d_inode is "less incorrect" 										 */
     /* dput(dentry); ?? where did this come from FIXME */
     if (ret >= 0){
       if (ret != len){
@@ -580,16 +581,16 @@
   }else if ((ret = umsdos_nevercreat(dir,dentry,-EPERM))==0){
     struct inode *olddir;
     ret = umsdos_get_dirowner(oldinode,&olddir);
-    Printk (("umsdos_link dir_owner = %d -> %p [%d] "
-	     ,oldinode->u.umsdos_i.i_dir_owner,olddir,olddir->i_count));
+    Printk (("umsdos_link dir_owner = %lu -> %p [%d] ",
+	     oldinode->u.umsdos_i.i_dir_owner, olddir, olddir->i_count));
     if (ret == 0){
       struct umsdos_dirent entry;
       umsdos_lockcreate2(dir,olddir);
       ret = umsdos_inode2entry (olddir,oldinode,&entry);
       if (ret == 0){
-	Printk (("umsdos_link :%s: ino %d flags %d "
-		 ,entry.name
-		 ,oldinode->i_ino,entry.flags));
+	Printk (("umsdos_link :%.*s: ino %lu flags %d "
+		 ,entry.name_len, entry.name
+		 ,oldinode->i_ino, entry.flags));
 	if (!(entry.flags & UMSDOS_HIDDEN)){
 	  /* #Specification: hard link / first hard link
 	     The first time a hard link is done on a file, this
@@ -882,7 +883,7 @@
   if (ret == 0){
     volatile struct inode *sdir;
     dir->i_count++;
-    ret = UMSDOS_lookup (dir, dentry);
+    ret = umsdos_lookup_x (dir, dentry, 0);
     sdir = dentry->d_inode;
     Printk (("rmdir lookup %d ",ret));
     if (ret == 0){
@@ -892,7 +893,7 @@
 	ret = -EBUSY;
       }else if ((empty = umsdos_isempty (sdir)) != 0){
 	struct dentry *tdentry;
-	tdentry = creat_dentry (UMSDOS_EMD_FILE, UMSDOS_EMD_FILE, NULL);
+	tdentry = creat_dentry (UMSDOS_EMD_FILE, UMSDOS_EMD_NAMELEN, NULL);
 	Printk (("isempty %d i_count %d ",empty,sdir->i_count));
 				/* check sticky bit */
 	if ( !(dir->i_mode & S_ISVTX) || fsuser() ||
@@ -959,7 +960,7 @@
       umsdos_lockcreate(dir);
       ret = umsdos_findentry(dir,&info,1);
       if (ret == 0){
-	Printk (("UMSDOS_unlink %s ",info.fake.fname));
+	Printk (("UMSDOS_unlink %.*s ",info.fake.len,info.fake.fname));
 				/* check sticky bit */
 	if ( !(dir->i_mode & S_ISVTX) || fsuser() ||
 	     current->fsuid == info.entry.uid ||
@@ -979,7 +980,7 @@
 	    */
 	    struct inode *inode;
 	    dir->i_count++;
-	    ret = UMSDOS_lookup (dir,dentry);
+	    ret = umsdos_lookup_x (dir, dentry, 0);
 	    inode = dentry->d_inode;
 	    if (ret == 0){
 	      Printk (("unlink nlink = %d ",inode->i_nlink));
@@ -1006,11 +1007,11 @@
 	    ret = umsdos_delentry (dir,&info,0);
 	    if (ret == 0){
 	      struct dentry *temp;
-	      Printk (("Avant msdos_unlink %s ",info.fake.fname));
+	      Printk (("Avant msdos_unlink %.*s ",info.fake.len,info.fake.fname));
 	      dir->i_count++;
 	      temp = creat_dentry (info.fake.fname, info.fake.len, NULL);
 	      ret = msdos_unlink_umsdos (dir, temp);
-	      Printk (("msdos_unlink %s %o ret %d ",info.fake.fname
+	      Printk (("msdos_unlink %.*s %o ret %d ",info.fake.len,info.fake.fname
 		       ,info.entry.mode,ret));
 	    }
 	  }
diff -u --recursive --new-file v2.1.87/linux/fs/umsdos/rdir.c linux/fs/umsdos/rdir.c
--- v2.1.87/linux/fs/umsdos/rdir.c	Tue Feb 17 13:12:48 1998
+++ linux/fs/umsdos/rdir.c	Fri Feb 20 09:44:57 1998
@@ -39,9 +39,9 @@
 {
   int ret = 0;
   struct RDIR_FILLDIR *d = (struct RDIR_FILLDIR*) buf;
-  Printk ((KERN_DEBUG "rdir_filldir /mn/: entering\n"));
+  PRINTK ((KERN_DEBUG "rdir_filldir /mn/: entering\n"));
   if (d->real_root){
-    Printk ((KERN_DEBUG "rdir_filldir /mn/: real root!\n"));
+    PRINTK ((KERN_DEBUG "rdir_filldir /mn/: real root!\n"));
     /* real root of a pseudo_rooted partition */
     if (name_len != UMSDOS_PSDROOT_LEN
 	|| memcmp(name,UMSDOS_PSDROOT_NAME,UMSDOS_PSDROOT_LEN)!=0){
@@ -56,8 +56,8 @@
     }
   }else{
     /* Any DOS directory */
-    Printk ((KERN_DEBUG "rdir_filldir /mn/: calling d->filldir (%p) for %12s (%d)\n",d->filldir,name,ino));
-    ret = d->filldir (d->dirbuf,name,name_len,offset,ino);
+    PRINTK ((KERN_DEBUG "rdir_filldir /mn/: calling d->filldir (%p) for %.*s (%lu)\n", d->filldir, name_len, name, ino));
+    ret = d->filldir (d->dirbuf, name, name_len, offset, ino);
   }
   return ret;
 }
@@ -71,7 +71,7 @@
   struct RDIR_FILLDIR bufk;
   struct inode *dir = filp->f_dentry->d_inode;
   
-  Printk ((KERN_DEBUG "UMSDOS_rreaddir /mn/: entering %p %p\n", filldir, dirbuf));
+  PRINTK ((KERN_DEBUG "UMSDOS_rreaddir /mn/: entering %p %p\n", filldir, dirbuf));
 
   
   bufk.filldir = filldir;
@@ -79,7 +79,7 @@
   bufk.real_root = pseudo_root
     && dir == iget(dir->i_sb,UMSDOS_ROOT_INO)
     && dir == iget(pseudo_root->i_sb,UMSDOS_ROOT_INO);
-  Printk ((KERN_DEBUG "UMSDOS_rreaddir /mn/: calling fat_readdir with filldir=%p and exiting\n",filldir));
+  PRINTK ((KERN_DEBUG "UMSDOS_rreaddir /mn/: calling fat_readdir with filldir=%p and exiting\n",filldir));
   return fat_readdir(filp, &bufk, rdir_filldir);
 }
 
@@ -106,7 +106,7 @@
       && dir == iget(dir->i_sb,UMSDOS_ROOT_INO)
       && dir == iget(pseudo_root->i_sb,UMSDOS_ROOT_INO) ){
     /*    *result = pseudo_root;*/
-    Printk (("umsdos_rlookup_x: we are at pseudo-root thingy?\n"));
+    Printk ((KERN_WARNING "umsdos_rlookup_x: we are at pseudo-root thingy?\n"));
     pseudo_root->i_count++;
     ret = 0;
     /* #Specification: pseudo root / DOS/..
@@ -115,13 +115,17 @@
     */
   }else{
     ret = umsdos_real_lookup (dir, dentry); inode=dentry->d_inode;
-    Printk ((KERN_DEBUG "umsdos_rlookup_x: umsdos_real_lookup for %s in %d returned %d\n", name, dir->i_ino, ret));
+
+#if 0
+    Printk ((KERN_DEBUG "umsdos_rlookup_x: umsdos_real_lookup for %.*s in %lu returned %d\n", len, name, dir->i_ino, ret));
     Printk ((KERN_DEBUG "umsdos_rlookup_x: umsdos_real_lookup: inode is %p resolving to ", inode));
     if (inode) {	/* /mn/ FIXME: DEL_ME */
-        Printk ((KERN_DEBUG "i_ino=%d\n",inode->i_ino));
+        Printk ((KERN_DEBUG "i_ino=%lu\n", inode->i_ino));
     } else {
         Printk ((KERN_DEBUG "NONE!\n"));
     }
+#endif
+    
     if ((ret == 0) && inode){
       
       if (pseudo_root && inode == pseudo_root && !nopseudo){
@@ -129,20 +133,20 @@
 	 Even in the real root directory (c:\), the directory
 	 /linux won't show
       */
-        Printk (("umsdos_rlookup_x: do the pseudo-thingy...\n"));
+        Printk ((KERN_WARNING "umsdos_rlookup_x: do the pseudo-thingy...\n"));
 	ret = -ENOENT;
 	iput (pseudo_root);
        
       }else if (S_ISDIR(inode->i_mode)){
 	/* We must place the proper function table */
 	/* depending if this is a MsDOS directory or an UMSDOS directory */
-        Printk (("umsdos_rlookup_x: setting up setup_dir_inode %d...\n", inode->i_ino));
+        Printk ((KERN_DEBUG "umsdos_rlookup_x: setting up setup_dir_inode %lu...\n", inode->i_ino));
 	umsdos_setup_dir_inode (inode);
       }
     }
   }
   iput (dir);
-  Printk ((KERN_DEBUG "umsdos_rlookup_x: returning %d\n", ret));
+  PRINTK ((KERN_DEBUG "umsdos_rlookup_x: returning %d\n", ret));
   return ret;
 }
 
@@ -152,7 +156,7 @@
 	struct dentry *dentry
 	)
 {
-  Printk ((KERN_DEBUG "UMSDOS_rlookup /mn/: executing umsdos_rlookup_x for ino=%d in %20s\n",dir->i_ino,dentry->d_name.name));
+  PRINTK ((KERN_DEBUG "UMSDOS_rlookup /mn/: executing umsdos_rlookup_x for ino=%lu in %.*s\n", dir->i_ino, (int) dentry->d_name.len, dentry->d_name.name));
   return umsdos_rlookup_x(dir,dentry,0);
 }
 
diff -u --recursive --new-file v2.1.87/linux/fs/umsdos/symlink.c linux/fs/umsdos/symlink.c
--- v2.1.87/linux/fs/umsdos/symlink.c	Tue Feb 17 13:12:48 1998
+++ linux/fs/umsdos/symlink.c	Fri Feb 20 09:44:57 1998
@@ -28,19 +28,21 @@
 /*
 	Read the data associate with the symlink.
 	Return length read in buffer or  a negative error code.
-	FIXME, this is messed up.
 	
-	/mn/ WIP fixing...
 */
+
 static int umsdos_readlink_x (
 	struct dentry *dentry,
 	char *buffer,
 	int bufsiz)
 {
-	int ret = dentry->d_inode->i_size;
+	int ret;
 	loff_t loffs = 0;
 	struct file filp;
 	
+	
+	ret = dentry->d_inode->i_size;
+	
 	memset (&filp, 0, sizeof (filp));
 	
 	filp.f_pos = 0;
@@ -51,34 +53,33 @@
 
 	if (ret > bufsiz) ret = bufsiz;
 
-        Printk ((KERN_WARNING "umsdos_readlink_x /mn/: Checkin: filp=%p, buffer=%p, size=%ld, offs=%d\n", &filp, buffer, ret, loffs));
-        Printk ((KERN_WARNING "  f_op=%p\n", filp.f_op));
-        Printk ((KERN_WARNING "  inode=%d, i_size=%d\n", filp.f_dentry->d_inode->i_ino,filp.f_dentry->d_inode->i_size));
-        Printk ((KERN_WARNING "  f_pos=%ld\n", filp.f_pos));
-        Printk ((KERN_WARNING "  name=%12s\n", filp.f_dentry->d_name.name));
-        Printk ((KERN_WARNING "  i_binary(sb)=%d\n", MSDOS_I(filp.f_dentry->d_inode)->i_binary ));
-        Printk ((KERN_WARNING "  f_count=%d, f_flags=%d\n", filp.f_count, filp.f_flags));
-        Printk ((KERN_WARNING "  f_owner=%d\n", filp.f_owner));
-        Printk ((KERN_WARNING "  f_version=%ld\n", filp.f_version));
-        Printk ((KERN_WARNING "  f_reada=%ld, f_ramax=%ld, f_raend=%ld, f_ralen=%ld, f_rawin=%ld\n", filp.f_reada, filp.f_ramax, filp.f_raend, filp.f_ralen, filp.f_rawin));
+        PRINTK ((KERN_DEBUG "umsdos_readlink_x /mn/: Checkin: filp=%p, buffer=%p, size=%d, offs=%Lu\n", &filp, buffer, ret, loffs));
+        PRINTK ((KERN_DEBUG "  f_op=%p\n", filp.f_op));
+        PRINTK ((KERN_DEBUG "  inode=%lu, i_size=%lu\n", filp.f_dentry->d_inode->i_ino, filp.f_dentry->d_inode->i_size));
+        PRINTK ((KERN_DEBUG "  f_pos=%Lu\n", filp.f_pos));
+        PRINTK ((KERN_DEBUG "  name=%.*s\n", (int) filp.f_dentry->d_name.len, filp.f_dentry->d_name.name));
+        PRINTK ((KERN_DEBUG "  i_binary(sb)=%d\n", MSDOS_I(filp.f_dentry->d_inode)->i_binary ));
+        PRINTK ((KERN_DEBUG "  f_count=%d, f_flags=%d\n", filp.f_count, filp.f_flags));
+        PRINTK ((KERN_DEBUG "  f_owner=%d\n", filp.f_owner.uid));
+        PRINTK ((KERN_DEBUG "  f_version=%ld\n", filp.f_version));
+        PRINTK ((KERN_DEBUG "  f_reada=%ld, f_ramax=%ld, f_raend=%ld, f_ralen=%ld, f_rawin=%ld\n", filp.f_reada, filp.f_ramax, filp.f_raend, filp.f_ralen, filp.f_rawin));
 
 
-	/* FIXME */
-	Printk (("umsdos_readlink_x: FIXME /mn/: running fat_file_read (%p, %p, %d, %ld)\n", &filp, buffer, ret, loffs));
+	PRINTK ((KERN_DEBUG"umsdos_readlink_x: FIXME /mn/: running fat_file_read (%p, %p, %d, %Lu)\n", &filp, buffer, ret, loffs));
 	if (fat_file_read (&filp, buffer, (size_t) ret, &loffs) != ret){
 		ret = -EIO;
 	}
-#if 0
+#if 0	/* DEBUG */
     {
       struct umsdos_dirent *mydirent=buffer;
 
-      Printk ((KERN_DEBUG "  (DDD) uid=%d\n",mydirent->uid));
-      Printk ((KERN_DEBUG "  (DDD) gid=%d\n",mydirent->gid));
-      Printk ((KERN_DEBUG "  (DDD) name=>%20s<\n",mydirent->name));
+      PRINTK ((KERN_DEBUG "  (DDD) uid=%d\n",mydirent->uid));
+      PRINTK ((KERN_DEBUG "  (DDD) gid=%d\n",mydirent->gid));
+      PRINTK ((KERN_DEBUG "  (DDD) name=>%.20s<\n",mydirent->name));
     }
 #endif  
 
-	Printk (("umsdos_readlink_x: FIXME /mn/: fat_file_read returned offs=%ld ret=%d\n", loffs, ret));
+	PRINTK ((KERN_DEBUG "umsdos_readlink_x: FIXME /mn/: fat_file_read returned offs=%Lu ret=%d\n", loffs, ret));
 	return ret;
 }
 
@@ -88,33 +89,57 @@
 {
 	int ret;
 
-	Printk (("UMSDOS_readlink: calling umsdos_readlink_x for %20s\n", dentry->d_name.name));
+	PRINTK ((KERN_DEBUG "UMSDOS_readlink: calling umsdos_readlink_x for %.*s\n", (int) dentry->d_name.len, dentry->d_name.name));
 	ret = umsdos_readlink_x (dentry, buffer, buflen);
-	Printk (("readlink %d bufsiz %d\n", ret, buflen));
+	PRINTK ((KERN_DEBUG "readlink %d bufsiz %d\n", ret, buflen));
 	/* dput(dentry); / * FIXME /mn/ */
-	Printk (("UMSDOS_readlink /mn/: FIXME! skipped dput(dentry). returning %d\n", ret));
+	Printk ((KERN_WARNING "UMSDOS_readlink /mn/: FIXME! skipped dput(dentry). returning %d\n", ret));
 	return ret;
 	
 }
 
-static struct dentry *UMSDOS_followlink(struct dentry * dentry,	struct dentry * base)
+/* this one mostly stolen from romfs :) */
+static struct dentry *UMSDOS_followlink(struct dentry *dentry, struct dentry *base)
 {
-	int ret;
-	char symname[256];
 	struct inode *inode = dentry->d_inode;
+	char *symname=NULL;
+	int len, cnt;
 	mm_segment_t old_fs = get_fs();
-	Printk ((KERN_ERR "UMSDOS_followlink /mn/: (%s/%s)\n", dentry->d_parent->d_name.name, dentry->d_name.name));
+	Printk ((KERN_DEBUG "UMSDOS_followlink /mn/: (%.*s/%.*s)\n", (int) dentry->d_parent->d_name.len, dentry->d_parent->d_name.name, (int) dentry->d_name.len, dentry->d_name.name));
+
+	len = inode->i_size;
 
-	set_fs (KERNEL_DS);	/* we read into kernel space */
-	ret = umsdos_readlink_x (dentry, &symname, 256);
+	if (!(symname = kmalloc(len+1, GFP_KERNEL))) {
+		dentry = ERR_PTR(-EAGAIN);			/* correct? */
+		goto outnobuf;
+	}
+
+	set_fs (KERNEL_DS);	/* we read into kernel space this time */
+	PRINTK ((KERN_DEBUG "UMSDOS_followlink /mn/: Here goes umsdos_readlink_x %p, %p, %d\n", dentry, symname, len));
+	cnt = umsdos_readlink_x (dentry, symname, len);
+	PRINTK ((KERN_DEBUG "UMSDOS_followlink /mn/: back from umsdos_readlink_x %p, %p, %d!\n", dentry, symname, len));
 	set_fs (old_fs);
+	Printk ((KERN_DEBUG "UMSDOS_followlink /mn/: link name is %.*s with len %d\n", cnt, symname, cnt));
 	
-	base = creat_dentry (symname, ret, NULL);
-/*	UMSDOS_lookup (dentry->d_parent->d_inode, base);*/
-	
-	return base;
+	if (len != cnt) {
+		dentry = ERR_PTR(-EIO);
+		goto out;
+	} else
+		symname[len] = 0;
+
+	dentry = lookup_dentry(symname, base, 1);
+	kfree(symname);
+
+	if (0) {
+out:
+		kfree(symname);
+outnobuf:
+		dput(base);
+	}
+	return dentry;
 }
 
+
 static struct file_operations umsdos_symlink_operations = {
 	NULL,				/* lseek - default */
 	NULL,				/* read */
@@ -141,7 +166,7 @@
 	NULL,			/* mknod */
 	NULL,			/* rename */
 	UMSDOS_readlink,	/* readlink */
-	NULL/*UMSDOS_followlink*/,	/* followlink */	/* /mn/ is this REALLY needed ? I recall seeing it working w/o it... */
+	UMSDOS_followlink,	/* followlink */	/* /mn/ is this REALLY needed ? I recall seeing it working w/o it... */
 	generic_readpage,	/* readpage */	/* in original NULL. changed to generic_readpage. FIXME? /mn/ */
 	NULL,			/* writepage */
 	fat_bmap,		/* bmap */	/* in original NULL. changed to fat_bmap. FIXME? /mn/ */
diff -u --recursive --new-file v2.1.87/linux/fs/vfat/namei.c linux/fs/vfat/namei.c
--- v2.1.87/linux/fs/vfat/namei.c	Tue Feb 17 13:12:48 1998
+++ linux/fs/vfat/namei.c	Fri Feb 20 18:28:23 1998
@@ -10,7 +10,6 @@
  *    the problem, send a script that demonstrates it.
  */
 
-#include <linux/config.h>
 #define __NO_VERSION__
 #include <linux/module.h>
 
diff -u --recursive --new-file v2.1.87/linux/include/asm-alpha/dma.h linux/include/asm-alpha/dma.h
--- v2.1.87/linux/include/asm-alpha/dma.h	Mon Jun  3 20:06:38 1996
+++ linux/include/asm-alpha/dma.h	Thu Feb 19 14:46:15 1998
@@ -313,6 +313,8 @@
 /* These are in kernel/dma.c: */
 extern int request_dma(unsigned int dmanr, const char * device_id);	/* reserve a DMA channel */
 extern void free_dma(unsigned int dmanr);	/* release it again */
+#define KERNEL_HAVE_CHECK_DMA
+extern int check_dma(unsigned int dmanr);
 
 
 #endif /* _ASM_DMA_H */
diff -u --recursive --new-file v2.1.87/linux/include/asm-i386/floppy.h linux/include/asm-i386/floppy.h
--- v2.1.87/linux/include/asm-i386/floppy.h	Fri Feb  6 15:33:06 1998
+++ linux/include/asm-i386/floppy.h	Fri Feb 20 19:12:50 1998
@@ -60,7 +60,7 @@
 	register unsigned char st;
 
 #undef TRACE_FLPY_INT
-#undef NO_FLOPPY_ASSEMBLER
+#define NO_FLOPPY_ASSEMBLER
 
 #ifdef TRACE_FLPY_INT
 	static int calls=0;
@@ -123,10 +123,10 @@
 				outb_p(*lptr, virtual_dma_port+5);
 			else
 				*lptr = inb_p(virtual_dma_port+5);
-			st = inb(virtual_dma_port+4);
 		}
 		virtual_dma_count = lcount;
 		virtual_dma_addr = lptr;
+		st = inb(virtual_dma_port+4);
 	}
 #endif
 
@@ -223,7 +223,7 @@
 		   _CROSS_64KB(addr, size, 0))
 			use_virtual_dma = 1;
 		else
-			use_virtual_dma = 1;
+			use_virtual_dma = 0;
 	} else {
 		use_virtual_dma = can_use_virtual_dma & 1;
 	}
diff -u --recursive --new-file v2.1.87/linux/include/asm-m68k/io.h linux/include/asm-m68k/io.h
--- v2.1.87/linux/include/asm-m68k/io.h	Tue Feb 17 13:12:49 1998
+++ linux/include/asm-m68k/io.h	Fri Feb 20 18:28:23 1998
@@ -3,6 +3,7 @@
 
 #ifdef __KERNEL__
 
+#include <linux/config.h> /* CONFIG_ATARI, CONFIG_HADES */
 #include <asm/byteorder.h>
 
 #ifdef CONFIG_ATARI
diff -u --recursive --new-file v2.1.87/linux/include/asm-m68k/keyboard.h linux/include/asm-m68k/keyboard.h
--- v2.1.87/linux/include/asm-m68k/keyboard.h	Tue Feb 17 13:12:49 1998
+++ linux/include/asm-m68k/keyboard.h	Fri Feb 20 18:28:23 1998
@@ -8,6 +8,7 @@
  *  This file contains the m68k architecture specific keyboard definitions
  */
 
+#include <linux/config.h> /* CONFIG_MAGIC_SYSRQ */
 #ifndef __M68K_KEYBOARD_H
 #define __M68K_KEYBOARD_H
 
diff -u --recursive --new-file v2.1.87/linux/include/asm-mips/inventory.h linux/include/asm-mips/inventory.h
--- v2.1.87/linux/include/asm-mips/inventory.h	Tue Dec 16 12:46:06 1997
+++ linux/include/asm-mips/inventory.h	Fri Feb 20 18:25:09 1998
@@ -26,31 +26,3 @@
 #define init_inventory()
 #endif
 #endif /* defined(CONFIG_BINFMT_IRIX) */
-/*
- * $Id: inventory.h,v 1.2 1997/12/06 12:39:03 ralf Exp $
- */
-#ifndef __ASM_MIPS_INVENTORY_H
-#define __ASM_MIPS_INVENTORY_H
-
-#include <linux/config.h>
-
-#ifdef CONFIG_BINFMT_IRIX
-typedef struct inventory_s {
-	struct inventory_s *inv_next;
-	int    inv_class;
-	int    inv_type;
-	int    inv_controller;
-	int    inv_unit;
-	int    inv_state;
-} inventory_t;
-
-extern int inventory_items;
-void add_to_inventory (int class, int type, int controller, int unit, int state);
-int dump_inventory_to_user (void *userbuf, int size);
-void init_inventory (void);
-
-#else
-#define add_to_inventory(c,t,o,u,s)
-#define init_inventory()
-#endif
-#endif /* defined(CONFIG_BINFMT_IRIX) */
diff -u --recursive --new-file v2.1.87/linux/include/asm-ppc/prom.h linux/include/asm-ppc/prom.h
--- v2.1.87/linux/include/asm-ppc/prom.h	Mon Jan 12 15:18:13 1998
+++ linux/include/asm-ppc/prom.h	Fri Feb 20 18:28:23 1998
@@ -5,8 +5,6 @@
  * Copyright (C) 1996 Paul Mackerras.
  */
 
-#include <linux/config.h>
-
 typedef void *phandle;
 typedef void *ihandle;
 
diff -u --recursive --new-file v2.1.87/linux/include/linux/apm_bios.h linux/include/linux/apm_bios.h
--- v2.1.87/linux/include/linux/apm_bios.h	Thu Feb 12 20:56:13 1998
+++ linux/include/linux/apm_bios.h	Thu Feb 19 14:46:15 1998
@@ -19,6 +19,7 @@
  */
 
 typedef unsigned short	apm_event_t;
+typedef unsigned short	apm_eventinfo_t;
 
 #ifdef __KERNEL__
 
@@ -35,6 +36,7 @@
 	unsigned short	dseg;
 	unsigned short	flags;
 	unsigned short	cseg_len;
+	unsigned short	cseg_16_len;
 	unsigned short	dseg_len;
 };
 
@@ -111,6 +113,7 @@
 #define APM_USER_STANDBY	0x0009
 #define APM_USER_SUSPEND	0x000a
 #define APM_STANDBY_RESUME	0x000b
+#define APM_CAPABILITY_CHANGE   0x000c
 
 /*
  * Error codes
@@ -126,6 +129,8 @@
 #define APM_BAD_DEVICE		0x09
 #define APM_BAD_PARAM		0x0a
 #define APM_NOT_ENGAGED		0x0b
+#define APM_BAD_FUNCTION        0x0c
+#define APM_RESUME_DISABLED	0x0d
 #define APM_BAD_STATE		0x60
 #define APM_NO_EVENTS		0x80
 #define APM_NOT_PRESENT		0x86
diff -u --recursive --new-file v2.1.87/linux/include/linux/blk.h linux/include/linux/blk.h
--- v2.1.87/linux/include/linux/blk.h	Tue Feb 17 13:12:49 1998
+++ linux/include/linux/blk.h	Fri Feb 20 19:12:39 1998
@@ -12,7 +12,7 @@
  * is mucked around with in interrupts on potentially
  * multiple CPU's..
  */
-extern spinlock_t current_lock;
+extern spinlock_t io_request_lock;
 
 /*
  * NR_REQUEST is the number of entries in the request-queue.
@@ -445,6 +445,11 @@
 
 #if ! SCSI_BLK_MAJOR(MAJOR_NR)
 
+/*
+ * The [*_]end_request() handler has to be called with the request queue
+ * spinlock aquired. All functions called within end_request() _must be_
+ * atomic.
+ */
 #if defined(IDE_DRIVER) && !defined(_IDE_C) /* shared copy for IDE modules */
 void ide_end_request(byte uptodate, ide_hwgroup_t *hwgroup);
 #else
diff -u --recursive --new-file v2.1.87/linux/include/linux/blkdev.h linux/include/linux/blkdev.h
--- v2.1.87/linux/include/linux/blkdev.h	Fri Feb  6 15:32:54 1998
+++ linux/include/linux/blkdev.h	Fri Feb 20 19:12:38 1998
@@ -38,6 +38,9 @@
 
 struct blk_dev_struct {
 	request_fn_proc		*request_fn;
+	/*
+	 * queue_proc has to be atomic
+	 */
 	queue_proc		*queue;
 	void			*data;
 	struct request		*current_request;
diff -u --recursive --new-file v2.1.87/linux/include/linux/coda_psdev.h linux/include/linux/coda_psdev.h
--- v2.1.87/linux/include/linux/coda_psdev.h	Tue Jan  6 10:00:22 1998
+++ linux/include/linux/coda_psdev.h	Fri Feb 20 18:28:23 1998
@@ -4,8 +4,6 @@
 #define CODA_PSDEV_MAJOR 67
 #define MAX_CODADEVS  5	   /* how many do we allow */
 
-#include <linux/config.h>
-
 extern struct vcomm psdev_vcomm[];
 
 /* queue stuff; the rest is static to psdev.c */
diff -u --recursive --new-file v2.1.87/linux/include/linux/file.h linux/include/linux/file.h
--- v2.1.87/linux/include/linux/file.h	Sun Jul 13 21:20:10 1997
+++ linux/include/linux/file.h	Fri Feb 20 15:04:45 1998
@@ -29,6 +29,7 @@
 	int error = 0;
 
 	if (!count) {
+		locks_remove_flock(file);
 		error = __fput(file);
 		file->f_count = 0;
 		remove_filp(file);
diff -u --recursive --new-file v2.1.87/linux/include/linux/fs.h linux/include/linux/fs.h
--- v2.1.87/linux/include/linux/fs.h	Thu Feb 12 20:56:13 1998
+++ linux/include/linux/fs.h	Fri Feb 20 19:12:38 1998
@@ -446,7 +446,10 @@
 
 extern int fcntl_getlk(unsigned int fd, struct flock *l);
 extern int fcntl_setlk(unsigned int fd, unsigned int cmd, struct flock *l);
-extern void locks_remove_locks(struct task_struct *task, struct file *filp);
+
+/* fs/locks.c */
+extern void locks_remove_posix(struct task_struct *, struct file *);
+extern void locks_remove_flock(struct file *);
 extern struct file_lock *posix_test_lock(struct file *, struct file_lock *);
 extern int posix_lock_file(struct file *, struct file_lock *, unsigned int);
 extern void posix_block_lock(struct file_lock *, struct file_lock *);
diff -u --recursive --new-file v2.1.87/linux/include/linux/in_route.h linux/include/linux/in_route.h
--- v2.1.87/linux/include/linux/in_route.h	Mon Jan 12 15:28:18 1998
+++ linux/include/linux/in_route.h	Thu Feb 19 14:46:15 1998
@@ -11,6 +11,7 @@
 #define RTCF_NOTIFY	0x00010000
 #define RTCF_DIRECTDST	0x00020000
 #define RTCF_REDIRECTED	0x00040000
+#define RTCF_TPROXY	0x00080000
 
 #define RTCF_FAST	0x00200000
 #define RTCF_MASQ	0x00400000
diff -u --recursive --new-file v2.1.87/linux/include/linux/ipx.h linux/include/linux/ipx.h
--- v2.1.87/linux/include/linux/ipx.h	Fri Feb  6 15:33:34 1998
+++ linux/include/linux/ipx.h	Fri Feb 20 19:13:19 1998
@@ -76,5 +76,12 @@
 #define SIOCAIPXITFCRT		(SIOCPROTOPRIVATE)
 #define SIOCAIPXPRISLT		(SIOCPROTOPRIVATE+1)
 #define SIOCIPXCFGDATA		(SIOCPROTOPRIVATE+2)
-#endif
 
+#ifdef __KERNEL__
+#include <linux/skbuff.h>
+
+extern int ipxrtr_route_skb(struct sk_buff *);
+extern int ipx_if_offset(unsigned long ipx_net_number);
+#endif /* def __KERNEL__ */
+
+#endif /* def _IPX_H_ */
diff -u --recursive --new-file v2.1.87/linux/include/linux/swap.h linux/include/linux/swap.h
--- v2.1.87/linux/include/linux/swap.h	Thu Jan 15 20:13:21 1998
+++ linux/include/linux/swap.h	Tue Feb 17 10:36:57 1998
@@ -81,7 +81,7 @@
  *
  * Will go away eventually..
  */
-#define SHM_SWP_TYPE 0x40
+#define SHM_SWP_TYPE 0x20
 
 /*
  * swap cache stuff (in linux/mm/swap_state.c)
diff -u --recursive --new-file v2.1.87/linux/include/linux/timer.h linux/include/linux/timer.h
--- v2.1.87/linux/include/linux/timer.h	Wed Feb  4 11:36:01 1998
+++ linux/include/linux/timer.h	Fri Feb 20 18:29:38 1998
@@ -20,6 +20,7 @@
 #define GSCD_TIMER	9	/* Goldstar CDROM */
 #define COMTROL_TIMER	10	/* Comtrol serial */
 #define DIGI_TIMER	11	/* Digi serial */
+#define GDTH_TIMER	12	/* Ugh - gdth scsi driver */
 
 #define COPRO_TIMER	31	/* 387 timeout for buggy hardware (boot only) */
 
diff -u --recursive --new-file v2.1.87/linux/include/linux/umsdos_fs.p linux/include/linux/umsdos_fs.p
--- v2.1.87/linux/include/linux/umsdos_fs.p	Tue Feb 17 13:12:49 1998
+++ linux/include/linux/umsdos_fs.p	Fri Feb 20 09:44:57 1998
@@ -1,6 +1,14 @@
 /* check.c 23/01/95 03.38.30 */
 void check_page_tables (void);
 /* dir.c 22/06/95 00.22.12 */
+struct dentry *creat_dentry (const char *name,
+			     const int len,
+			     struct inode *inode);
+int compat_msdos_create(struct inode *dir,
+			const char *name,
+			int len,
+			int mode,
+			struct inode **inode);
 int  UMSDOS_dir_read ( struct file *filp,
 	 char *buf,
 	 size_t size,
@@ -14,10 +22,10 @@
 	 struct umsdos_dirent *entry);
 int umsdos_locate_path (struct inode *inode, char *path);
 int umsdos_is_pseudodos (struct inode *dir, struct dentry *dentry);
-int compat_UMSDOS_lookup (struct inode *dir,
-	 const char *name,
-	 int len,
-	 struct inode **result);
+int umsdos_lookup_x (
+			    struct inode *dir,
+			    struct dentry *dentry,
+			    int nopseudo);
 int UMSDOS_lookup(struct inode *dir,struct dentry *dentry);
 	 
 int umsdos_hlink2inode (struct inode *hlink, struct inode **result);
diff -u --recursive --new-file v2.1.87/linux/include/net/ip_masq.h linux/include/net/ip_masq.h
--- v2.1.87/linux/include/net/ip_masq.h	Fri Feb  6 15:35:11 1998
+++ linux/include/net/ip_masq.h	Fri Feb 20 19:14:58 1998
@@ -2,6 +2,7 @@
  * 	IP masquerading functionality definitions
  */
 
+#include <linux/config.h> /* for CONFIG_IP_MASQ_NDEBUG */
 #ifndef _IP_MASQ_H
 #define _IP_MASQ_H
 
diff -u --recursive --new-file v2.1.87/linux/include/net/ipx.h linux/include/net/ipx.h
--- v2.1.87/linux/include/net/ipx.h	Fri Feb  6 15:33:34 1998
+++ linux/include/net/ipx.h	Fri Feb 20 19:13:19 1998
@@ -1,4 +1,3 @@
-
 /*
  *	The following information is in its entirety obtained from:
  *
@@ -12,7 +11,6 @@
 #define _NET_INET_IPX_H_
 
 #include <linux/netdevice.h>
-#include <linux/skbuff.h>
 #include <net/datalink.h>
 #include <linux/ipx.h>
 
@@ -44,8 +42,6 @@
 };
 
 #include <net/ipxcall.h>
-extern int ipx_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt);
-extern void ipxrtr_device_down(struct device *dev);
 
 typedef struct ipx_interface {
 	/* IPX address */
@@ -80,4 +76,4 @@
 #define IPX_MIN_EPHEMERAL_SOCKET	0x4000
 #define IPX_MAX_EPHEMERAL_SOCKET	0x7fff
 
-#endif
+#endif /* def _NET_INET_IPX_H_ */
diff -u --recursive --new-file v2.1.87/linux/include/net/profile.h linux/include/net/profile.h
--- v2.1.87/linux/include/net/profile.h	Fri Feb  6 15:33:34 1998
+++ linux/include/net/profile.h	Fri Feb 20 18:28:23 1998
@@ -1,3 +1,4 @@
+#include <linux/config.h> /* for CONFIG_NET_PROFILE */
 #ifndef _NET_PROFILE_H_
 #define _NET_PROFILE_H_ 1
 
diff -u --recursive --new-file v2.1.87/linux/include/net/route.h linux/include/net/route.h
--- v2.1.87/linux/include/net/route.h	Fri Feb  6 15:34:56 1998
+++ linux/include/net/route.h	Fri Feb 20 19:14:44 1998
@@ -61,6 +61,9 @@
 
 #include <linux/route.h>
 
+#define RTO_ONLINK	0x01
+#define RTO_TPROXY	0x80000000
+
 struct rt_key
 {
 	__u32			dst;
@@ -111,7 +114,7 @@
 				       u32 src, u8 tos, struct device *dev);
 extern void		ip_rt_advice(struct rtable **rp, int advice);
 extern void		rt_cache_flush(int how);
-extern int		ip_route_output(struct rtable **, u32 dst, u32 src, u8 tos, int oif);
+extern int		ip_route_output(struct rtable **, u32 dst, u32 src, u32 tos, int oif);
 extern int		ip_route_input(struct sk_buff*, u32 dst, u32 src, u8 tos, struct device *devin);
 extern unsigned short	ip_rt_frag_needed(struct iphdr *iph, unsigned short new_mtu);
 extern void		ip_rt_send_redirect(struct sk_buff *skb);
diff -u --recursive --new-file v2.1.87/linux/include/net/tcp.h linux/include/net/tcp.h
--- v2.1.87/linux/include/net/tcp.h	Fri Feb  6 15:34:58 1998
+++ linux/include/net/tcp.h	Fri Feb 20 19:14:46 1998
@@ -275,6 +275,9 @@
 		struct tcp_v6_open_req v6_req;
 #endif
 	} af;
+#ifdef CONFIG_IP_TRANSPARENT_PROXY
+	__u16			lcl_port; /* LVE */
+#endif
 };
 
 /* SLAB cache for open requests. */
diff -u --recursive --new-file v2.1.87/linux/net/802/llc_macinit.c linux/net/802/llc_macinit.c
--- v2.1.87/linux/net/802/llc_macinit.c	Thu Feb 12 20:56:13 1998
+++ linux/net/802/llc_macinit.c	Fri Feb 20 18:28:23 1998
@@ -19,7 +19,6 @@
  *					Started restructuring handlers
  */
 
-#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/version.h>
 #include <linux/kernel.h>
diff -u --recursive --new-file v2.1.87/linux/net/802/p8022.c linux/net/802/p8022.c
--- v2.1.87/linux/net/802/p8022.c	Thu Feb 12 20:56:14 1998
+++ linux/net/802/p8022.c	Fri Feb 20 18:28:23 1998
@@ -16,7 +16,6 @@
  *		4 entries at most). The current demux assumes this.
  */
 
-#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/netdevice.h>
 #include <linux/skbuff.h>
diff -u --recursive --new-file v2.1.87/linux/net/802/p8022tr.c linux/net/802/p8022tr.c
--- v2.1.87/linux/net/802/p8022tr.c	Thu Feb 12 20:56:14 1998
+++ linux/net/802/p8022tr.c	Fri Feb 20 18:28:23 1998
@@ -8,7 +8,6 @@
  *		2 of the License, or (at your option) any later version.
  */
 
-#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/netdevice.h>
 #include <linux/skbuff.h>
diff -u --recursive --new-file v2.1.87/linux/net/802/psnap.c linux/net/802/psnap.c
--- v2.1.87/linux/net/802/psnap.c	Thu Feb 12 20:56:14 1998
+++ linux/net/802/psnap.c	Fri Feb 20 18:28:23 1998
@@ -10,7 +10,6 @@
  *		2 of the License, or (at your option) any later version.
  */
 
-#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/netdevice.h>
 #include <linux/skbuff.h>
diff -u --recursive --new-file v2.1.87/linux/net/802/tr.c linux/net/802/tr.c
--- v2.1.87/linux/net/802/tr.c	Thu Sep  4 13:25:28 1997
+++ linux/net/802/tr.c	Thu Feb 19 14:59:53 1998
@@ -50,7 +50,7 @@
 	unsigned char addr[TR_ALEN];
 	unsigned char iface[5];
 	__u16 rcf;
-	__u8 rseg[8];
+	__u16 rseg[8];
 	rif_cache next;
 	unsigned long last_used;
 	unsigned char local_ring;
@@ -441,7 +441,7 @@
 	int len=0;
 	off_t begin=0;
 	off_t pos=0;
-	int size,i,j,rcf_len;
+	int size,i,j,rcf_len,segment,brdgnmb;
 	unsigned long now=jiffies;
 
 	rif_cache entry;
@@ -466,10 +466,18 @@
 				rcf_len = ((ntohs(entry->rcf) & TR_RCF_LEN_MASK)>>8)-2; 
 				if (rcf_len)
 				        rcf_len >>= 1;
-				for(j = 0; j < rcf_len; j++) {
-				        len+=size;
-				        pos=begin+len;
-			                size=sprintf(buffer+len," %04X",ntohs(entry->rseg[j]));
+				for(j = 1; j < rcf_len; j++) {
+					if(j==1) {
+						segment=ntohs(entry->rseg[j-1])>>4;
+						len+=size;
+						pos=begin+len;
+						size=sprintf(buffer+len,"  %03X",segment);
+					};
+					segment=ntohs(entry->rseg[j])>>4;
+					brdgnmb=ntohs(entry->rseg[j-1])&0x00f;
+					len+=size;
+					pos=begin+len;
+					size=sprintf(buffer+len,"-%01X-%03X",brdgnmb,segment);
 				}
 				len+=size;
 				pos=begin+len;
diff -u --recursive --new-file v2.1.87/linux/net/Config.in linux/net/Config.in
--- v2.1.87/linux/net/Config.in	Tue Jan 13 20:15:35 1998
+++ linux/net/Config.in	Thu Feb 19 14:46:15 1998
@@ -27,7 +27,7 @@
 comment ' '
 tristate 'The IPX protocol' CONFIG_IPX
 if [ "$CONFIG_IPX" != "n" ]; then
-  bool 'Full internal IPX network' CONFIG_IPX_INTERN
+  source net/ipx/Config.in
 fi
 tristate 'Appletalk DDP' CONFIG_ATALK
 if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
diff -u --recursive --new-file v2.1.87/linux/net/ax25/sysctl_net_ax25.c linux/net/ax25/sysctl_net_ax25.c
--- v2.1.87/linux/net/ax25/sysctl_net_ax25.c	Mon Jul  7 08:19:59 1997
+++ linux/net/ax25/sysctl_net_ax25.c	Fri Feb 20 18:28:23 1998
@@ -8,7 +8,6 @@
 #include <linux/config.h>
 #include <linux/mm.h>
 #include <linux/sysctl.h>
-#include <linux/config.h>
 #include <net/ax25.h>
 
 static int min_ipdefmode[] = {0},	max_ipdefmode[] = {1};
diff -u --recursive --new-file v2.1.87/linux/net/core/firewall.c linux/net/core/firewall.c
--- v2.1.87/linux/net/core/firewall.c	Tue May 13 22:41:21 1997
+++ linux/net/core/firewall.c	Fri Feb 20 18:28:23 1998
@@ -6,7 +6,6 @@
  *	much hacked by:	Alan Cox
  */
 
-#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/skbuff.h>
 #include <linux/firewall.h>
diff -u --recursive --new-file v2.1.87/linux/net/ipv4/Config.in linux/net/ipv4/Config.in
--- v2.1.87/linux/net/ipv4/Config.in	Thu Jan 15 14:33:07 1998
+++ linux/net/ipv4/Config.in	Thu Feb 19 14:46:15 1998
@@ -37,14 +37,16 @@
   fi
 fi
 bool 'IP: accounting' CONFIG_IP_ACCT
-bool 'IP: masquerading' CONFIG_IP_MASQUERADE
-if [ "$CONFIG_IP_MASQUERADE" != "n" ]; then
-  comment 'Protocol-specific masquerading support will be built as modules.'
-  bool 'IP: ICMP masquerading' CONFIG_IP_MASQUERADE_ICMP
-  comment 'Protocol-specific masquerading support will be built as modules.'
-  if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
-    tristate 'IP: ipautofw masq support (EXPERIMENTAL)' CONFIG_IP_MASQUERADE_IPAUTOFW
-    tristate 'IP: ipportfw masq support (EXPERIMENTAL)' CONFIG_IP_MASQUERADE_IPPORTFW
+if [ "$CONFIG_IP_FIREWALL" = "y" ]; then
+  bool 'IP: masquerading' CONFIG_IP_MASQUERADE
+  if [ "$CONFIG_IP_MASQUERADE" != "n" ]; then
+    comment 'Protocol-specific masquerading support will be built as modules.'
+    bool 'IP: ICMP masquerading' CONFIG_IP_MASQUERADE_ICMP
+    comment 'Protocol-specific masquerading support will be built as modules.'
+    if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+      tristate 'IP: ipautofw masq support (EXPERIMENTAL)' CONFIG_IP_MASQUERADE_IPAUTOFW
+      tristate 'IP: ipportfw masq support (EXPERIMENTAL)' CONFIG_IP_MASQUERADE_IPPORTFW
+    fi
   fi
 fi
 bool 'IP: optimize as router not host' CONFIG_IP_ROUTER
diff -u --recursive --new-file v2.1.87/linux/net/ipv4/arp.c linux/net/ipv4/arp.c
--- v2.1.87/linux/net/ipv4/arp.c	Thu Feb 12 20:56:14 1998
+++ linux/net/ipv4/arp.c	Thu Feb 19 14:46:15 1998
@@ -899,7 +899,6 @@
 	int len=0;
 	off_t pos=0;
 	int size;
-	struct neighbour *n;
 	char hbuffer[HBUFFERLEN];
 	int i,j,k;
 	const char hexbuf[] =  "0123456789ABCDEF";
@@ -912,6 +911,7 @@
 	neigh_table_lock(&arp_tbl);
 
 	for(i=0; i<=NEIGH_HASHMASK; i++)	{
+		struct neighbour *n;
 		for (n=arp_tbl.hash_buckets[i]; n; n=n->next) {
 			struct device *dev = n->dev;
 			int hatype = dev->type;
@@ -957,6 +957,32 @@
 			size += sprintf(buffer+len+size,
 				 "     %-17s %s\n",
 				 "*", dev->name);
+
+			len += size;
+			pos += size;
+		  
+			if (pos <= offset)
+				len=0;
+			if (pos >= offset+length)
+				goto done;
+		}
+	}
+
+	for (i=0; i<=PNEIGH_HASHMASK; i++) {
+		struct pneigh_entry *n;
+		for (n=arp_tbl.phash_buckets[i]; n; n=n->next) {
+			struct device *dev = n->dev;
+			int hatype = dev ? dev->type : 0;
+
+			size = sprintf(buffer+len,
+				"%-17s0x%-10x0x%-10x%s",
+				in_ntoa(*(u32*)n->key),
+				hatype,
+				ATF_PUBL|ATF_PERM,       
+				"00:00:00:00:00:00");
+			size += sprintf(buffer+len+size,
+				 "     %-17s %s\n",
+				 "*", dev ? dev->name : "*");
 
 			len += size;
 			pos += size;
diff -u --recursive --new-file v2.1.87/linux/net/ipv4/devinet.c linux/net/ipv4/devinet.c
--- v2.1.87/linux/net/ipv4/devinet.c	Tue Feb 17 13:12:50 1998
+++ linux/net/ipv4/devinet.c	Thu Feb 19 14:46:16 1998
@@ -418,6 +418,7 @@
 		break;
 
 	case SIOCSIFFLAGS:
+	case SIOCSIFPFLAGS:	/* Set per device sysctl controls */	
 		if (!suser())
 			return -EACCES;
 		rtnl_lock();
@@ -427,7 +428,6 @@
 	case SIOCSIFBRDADDR:	/* Set the broadcast address */
 	case SIOCSIFDSTADDR:	/* Set the destination address */
 	case SIOCSIFNETMASK: 	/* Set the netmask for the interface */
-	case SIOCSIFPFLAGS:	/* Set per device sysctl controls */	
 		if (!suser())
 			return -EACCES;
 		if (sin->sin_family != AF_INET)
diff -u --recursive --new-file v2.1.87/linux/net/ipv4/fib_frontend.c linux/net/ipv4/fib_frontend.c
--- v2.1.87/linux/net/ipv4/fib_frontend.c	Mon Jan 12 15:28:27 1998
+++ linux/net/ipv4/fib_frontend.c	Thu Feb 19 14:46:16 1998
@@ -536,6 +536,10 @@
 		rt_cache_flush(0);
 		arp_ifdown(dev);
 		break;
+	case NETDEV_CHANGEMTU:
+	case NETDEV_CHANGE:
+		rt_cache_flush(1*HZ);
+		break;
 	}
 	return NOTIFY_DONE;
 }
diff -u --recursive --new-file v2.1.87/linux/net/ipv4/ip_fw.c linux/net/ipv4/ip_fw.c
--- v2.1.87/linux/net/ipv4/ip_fw.c	Thu Feb 12 20:56:14 1998
+++ linux/net/ipv4/ip_fw.c	Fri Feb 20 18:28:23 1998
@@ -90,7 +90,6 @@
 #include <linux/sched.h>
 #include <linux/string.h>
 #include <linux/errno.h>
-#include <linux/config.h>
 
 #include <linux/socket.h>
 #include <linux/sockios.h>
diff -u --recursive --new-file v2.1.87/linux/net/ipv4/ip_gre.c linux/net/ipv4/ip_gre.c
--- v2.1.87/linux/net/ipv4/ip_gre.c	Thu Feb 12 20:56:14 1998
+++ linux/net/ipv4/ip_gre.c	Thu Feb 19 14:46:16 1998
@@ -628,7 +628,7 @@
 				goto tx_error;
 
 			addr6 = (struct in6_addr*)&neigh->primary_key;
-			addr_type = neigh->type;
+			addr_type = ipv6_addr_type(addr6);
 
 			if (addr_type == IPV6_ADDR_ANY) {
 				addr6 = &skb->nh.ipv6h->daddr;
diff -u --recursive --new-file v2.1.87/linux/net/ipv4/ip_masq_ftp.c linux/net/ipv4/ip_masq_ftp.c
--- v2.1.87/linux/net/ipv4/ip_masq_ftp.c	Wed Dec 10 09:45:16 1997
+++ linux/net/ipv4/ip_masq_ftp.c	Fri Feb 20 18:28:23 1998
@@ -37,7 +37,6 @@
  *	
  */
 
-#include <linux/config.h>
 #include <linux/module.h>
 #include <asm/system.h>
 #include <linux/types.h>
diff -u --recursive --new-file v2.1.87/linux/net/ipv4/ip_masq_irc.c linux/net/ipv4/ip_masq_irc.c
--- v2.1.87/linux/net/ipv4/ip_masq_irc.c	Wed Dec 10 09:45:16 1997
+++ linux/net/ipv4/ip_masq_irc.c	Fri Feb 20 18:28:23 1998
@@ -40,7 +40,6 @@
  *	
  */
 
-#include <linux/config.h>
 #include <linux/module.h>
 
 #include <linux/types.h>
diff -u --recursive --new-file v2.1.87/linux/net/ipv4/ip_masq_quake.c linux/net/ipv4/ip_masq_quake.c
--- v2.1.87/linux/net/ipv4/ip_masq_quake.c	Wed Dec 10 09:45:16 1997
+++ linux/net/ipv4/ip_masq_quake.c	Fri Feb 20 18:28:23 1998
@@ -21,7 +21,6 @@
  *  
  */
 
-#include <linux/config.h>
 #include <linux/module.h>
 #include <asm/system.h>
 #include <linux/types.h>
diff -u --recursive --new-file v2.1.87/linux/net/ipv4/ip_masq_raudio.c linux/net/ipv4/ip_masq_raudio.c
--- v2.1.87/linux/net/ipv4/ip_masq_raudio.c	Wed Dec 10 09:45:16 1997
+++ linux/net/ipv4/ip_masq_raudio.c	Fri Feb 20 18:28:23 1998
@@ -62,7 +62,6 @@
  *	
  */
 
-#include <linux/config.h>
 #include <linux/module.h>
 #include <asm/system.h>
 #include <linux/types.h>
diff -u --recursive --new-file v2.1.87/linux/net/ipv4/ip_output.c linux/net/ipv4/ip_output.c
--- v2.1.87/linux/net/ipv4/ip_output.c	Thu Feb 12 20:56:14 1998
+++ linux/net/ipv4/ip_output.c	Thu Feb 19 14:46:16 1998
@@ -29,6 +29,7 @@
  *		Alexey Kuznetsov:	use new route cache
  *		Andi Kleen:		Fix broken PMTU recovery and remove
  *					some redundant tests.
+ *	Vitaly E. Lavrov	:	Transparent proxy revived after year coma.
  */
 
 #include <asm/uaccess.h>
@@ -91,6 +92,13 @@
 		daddr = opt->faddr;
 
 	err = ip_route_output(&rt, daddr, saddr, RT_TOS(sk->ip_tos) |
+#ifdef CONFIG_IP_TRANSPARENT_PROXY
+			      /* Rationale: this routine is used only
+				 by TCP, so that validity of saddr is already
+				 checked and we can safely use RTO_TPROXY.
+			       */
+			      RTO_TPROXY |
+#endif
 			      (sk->localroute||0), sk->bound_dev_if);
 	if (err)
 	{
diff -u --recursive --new-file v2.1.87/linux/net/ipv4/ipip.c linux/net/ipv4/ipip.c
--- v2.1.87/linux/net/ipv4/ipip.c	Thu Feb 12 20:56:14 1998
+++ linux/net/ipv4/ipip.c	Fri Feb 20 18:28:23 1998
@@ -93,7 +93,6 @@
  */
 
  
-#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/types.h>
 #include <linux/sched.h>
diff -u --recursive --new-file v2.1.87/linux/net/ipv4/route.c linux/net/ipv4/route.c
--- v2.1.87/linux/net/ipv4/route.c	Thu Feb 12 20:56:14 1998
+++ linux/net/ipv4/route.c	Thu Feb 19 14:46:16 1998
@@ -46,6 +46,7 @@
  *	Alexey Kuznetsov	:	End of old history. Splitted to fib.c and
  *					route.c and rewritten from scratch.
  *		Andi Kleen	:	Load-limit warning messages.
+ *	Vitaly E. Lavrov	:	Transparent proxy revived after year coma.
  *
  *		This program is free software; you can redistribute it and/or
  *		modify it under the terms of the GNU General Public License
@@ -161,7 +162,7 @@
 	pos = 128;
 
 	if (offset<128)	{
-		sprintf(buffer,"%-127s\n", "Iface\tDestination\tGateway \tFlags\t\tRefCnt\tUse\tMetric\tSource\t\tMTU\tWindow\tIRTT\tTOS\tHHRef\tHHUptod\tSpecDst\tHash");
+		sprintf(buffer,"%-127s\n", "Iface\tDestination\tGateway \tFlags\t\tRefCnt\tUse\tMetric\tSource\t\tMTU\tWindow\tIRTT\tTOS\tHHRef\tHHUptod\tSpecDst");
 		len = 128;
   	}
 	
@@ -179,8 +180,7 @@
 				len = 0;
 				continue;
 			}
-					
-			sprintf(temp, "%s\t%08lX\t%08lX\t%8X\t%d\t%u\t%d\t%08lX\t%d\t%u\t%u\t%02X\t%d\t%1d\t%08X\t%02X",
+			sprintf(temp, "%s\t%08lX\t%08lX\t%8X\t%d\t%u\t%d\t%08lX\t%d\t%u\t%u\t%02X\t%d\t%1d\t%08X",
 				r->u.dst.dev ? r->u.dst.dev->name : "*",
 				(unsigned long)r->rt_dst,
 				(unsigned long)r->rt_gateway,
@@ -193,8 +193,7 @@
 				(int)r->u.dst.rtt, r->key.tos,
 				r->u.dst.hh ? atomic_read(&r->u.dst.hh->hh_refcnt) : -1,
 				r->u.dst.hh ? (r->u.dst.hh->hh_output == ip_acct_output) : 0,
-				r->rt_spec_dst,
-				i);
+				r->rt_spec_dst);
 			sprintf(buffer+len,"%-127s\n",temp);
 			len += 128;
 			if (pos >= offset+length)
@@ -278,14 +277,8 @@
 	for (i=0; i<RT_HASH_DIVISOR; i++) {
 		int nr=0;
 
-		cli();
-		if (!(rth = rt_hash_table[i])) {
-			sti();
+		if ((rth = xchg(&rt_hash_table[i], NULL)) == NULL)
 			continue;
-		}
-
-		rt_hash_table[i] = NULL;
-		sti();
 
 		for (; rth; rth=next) {
 			next = rth->u.rt_next;
@@ -1110,7 +1103,7 @@
  * Major route resolver routine.
  */
 
-int ip_route_output_slow(struct rtable **rp, u32 daddr, u32 saddr, u8 tos, int oif)
+int ip_route_output_slow(struct rtable **rp, u32 daddr, u32 saddr, u32 tos, int oif)
 {
 	struct rt_key key;
 	struct fib_result res;
@@ -1118,14 +1111,17 @@
 	struct rtable *rth;
 	struct device *dev_out = NULL;
 	unsigned hash;
+#ifdef CONFIG_IP_TRANSPARENT_PROXY
+	u32 nochecksrc = (tos & RTO_TPROXY);
+#endif
 
-	tos &= IPTOS_TOS_MASK|1;
+	tos &= IPTOS_TOS_MASK|RTO_ONLINK;
 	key.dst = daddr;
 	key.src = saddr;
 	key.tos = tos&IPTOS_TOS_MASK;
 	key.iif = loopback_dev.ifindex;
 	key.oif = oif;
-	key.scope = (tos&1) ? RT_SCOPE_LINK : RT_SCOPE_UNIVERSE;
+	key.scope = (tos&RTO_ONLINK) ? RT_SCOPE_LINK : RT_SCOPE_UNIVERSE;
 	res.fi = NULL;
 
 	if (saddr) {
@@ -1134,8 +1130,19 @@
 
 		/* It is equivalent to inet_addr_type(saddr) == RTN_LOCAL */
 		dev_out = ip_dev_find(saddr);
+#ifdef CONFIG_IP_TRANSPARENT_PROXY
+		/* If address is not local, test for transparent proxy flag;
+		   if address is local --- clear the flag.
+		 */
+		if (dev_out == NULL) {
+			if (nochecksrc == 0)
+				return -EINVAL;
+			flags |= RTCF_TPROXY;
+		}
+#else
 		if (dev_out == NULL)
 			return -EINVAL;
+#endif
 
 		/* I removed check for oif == dev_out->oif here.
 		   It was wrong by three reasons:
@@ -1145,7 +1152,11 @@
 		      of another iface. --ANK
 		 */
 
-		if (oif == 0 && (MULTICAST(daddr) || daddr == 0xFFFFFFFF)) {
+		if (oif == 0 &&
+#ifdef CONFIG_IP_TRANSPARENT_PROXY
+			dev_out &&
+#endif
+			(MULTICAST(daddr) || daddr == 0xFFFFFFFF)) {
 			/* Special hack: user can direct multicasts
 			   and limited broadcast via necessary interface
 			   without fiddling with IP_MULTICAST_IF or IP_TXINFO.
@@ -1343,7 +1354,7 @@
 	return 0;
 }
 
-int ip_route_output(struct rtable **rp, u32 daddr, u32 saddr, u8 tos, int oif)
+int ip_route_output(struct rtable **rp, u32 daddr, u32 saddr, u32 tos, int oif)
 {
 	unsigned hash;
 	struct rtable *rth;
@@ -1356,7 +1367,13 @@
 		    rth->key.src == saddr &&
 		    rth->key.iif == 0 &&
 		    rth->key.oif == oif &&
-		    rth->key.tos == tos) {
+#ifndef CONFIG_IP_TRANSPARENT_PROXY
+		    rth->key.tos == tos
+#else
+		    !((rth->key.tos^tos)&(IPTOS_TOS_MASK|RTO_ONLINK)) &&
+		    ((tos&RTO_TPROXY) || !(rth->rt_flags&RTCF_TPROXY))
+#endif
+		) {
 			rth->u.dst.lastuse = jiffies;
 			atomic_inc(&rth->u.dst.use);
 			atomic_inc(&rth->u.dst.refcnt);
diff -u --recursive --new-file v2.1.87/linux/net/ipv4/tcp_ipv4.c linux/net/ipv4/tcp_ipv4.c
--- v2.1.87/linux/net/ipv4/tcp_ipv4.c	Thu Feb 12 20:56:14 1998
+++ linux/net/ipv4/tcp_ipv4.c	Thu Feb 19 14:46:16 1998
@@ -42,6 +42,7 @@
  *					NEW_LISTEN for now)
  *	Juan Jose Ciarlante:		ip_dynaddr bits
  *		Andi Kleen:		various fixes.
+ *	Vitaly E. Lavrov	:	Transparent proxy revived after year coma.
  */
 
 #include <linux/config.h>
@@ -1082,7 +1083,12 @@
 	memset(th, 0, sizeof(struct tcphdr));
 	th->syn = 1;
 	th->ack = 1;
+	th->source =
+#ifdef CONFIG_IP_TRANSPARENT_PROXY
+	req->lcl_port; /* LVE */
+#else
 	th->source = sk->dummy_th.source;
+#endif
 	th->dest = req->rmt_port;
 	skb->seq = req->snt_isn;
 	skb->end_seq = skb->seq + 1;
@@ -1239,6 +1245,9 @@
 	req->snd_wscale = tp.snd_wscale;
 	req->wscale_ok = tp.wscale_ok;
 	req->rmt_port = th->source;
+#ifdef CONFIG_IP_TRANSPARENT_PROXY
+	req->lcl_port = th->dest ; /* LVE */
+#endif
 	req->af.v4_req.loc_addr = daddr;
 	req->af.v4_req.rmt_addr = saddr;
 
@@ -1361,7 +1370,12 @@
 
 	tcp_init_xmit_timers(newsk);
 
-	newsk->dummy_th.source = sk->dummy_th.source;
+	newsk->dummy_th.source = 
+#ifdef CONFIG_IP_TRANSPARENT_PROXY
+	req->lcl_port; /* LVE */
+#else
+	sk->dummy_th.source;
+#endif
 	newsk->dummy_th.dest = req->rmt_port;
 	newsk->sock_readers=0;
 
@@ -1371,6 +1385,13 @@
 
 	newsk->socket = NULL;
 
+#ifdef CONFIG_IP_TRANSPARENT_PROXY
+	/*
+	*      Deal with possibly redirected traffic by setting num to
+	*      the intended destination port of the received packet.
+        */
+	newsk->num = ntohs(skb->h.th->dest);
+#endif
 	newsk->daddr = req->af.v4_req.rmt_addr;
 	newsk->saddr = req->af.v4_req.loc_addr;
 	newsk->rcv_saddr = req->af.v4_req.loc_addr;
diff -u --recursive --new-file v2.1.87/linux/net/ipv4/udp.c linux/net/ipv4/udp.c
--- v2.1.87/linux/net/ipv4/udp.c	Thu Feb 12 20:56:14 1998
+++ linux/net/ipv4/udp.c	Thu Feb 19 14:46:16 1998
@@ -55,6 +55,7 @@
  *		Olaf Kirch	:	Don't linearise iovec on sendmsg.
  *		Andi Kleen	:	Some cleanups, cache destination entry
  *					for connect. 
+ *	Vitaly E. Lavrov	:	Transparent proxy revived after year coma.
  *
  *
  *		This program is free software; you can redistribute it and/or
@@ -360,14 +361,14 @@
 #ifdef CONFIG_IP_TRANSPARENT_PROXY
 #define secondlist(hpnum, sk, fpass) \
 ({ struct sock *s1; if(!(sk) && (fpass)--) \
-	s1 = udp_hash[(hpnum) & (TCP_HTABLE_SIZE - 1)]; \
+	s1 = udp_hash[(hpnum) & (UDP_HTABLE_SIZE - 1)]; \
    else \
 	s1 = (sk); \
    s1; \
 })
 
 #define udp_v4_proxy_loop_init(hnum, hpnum, sk, fpass) \
-	secondlist((hpnum), udp_hash[(hnum)&(TCP_HTABLE_SIZE-1)],(fpass))
+	secondlist((hpnum), udp_hash[(hnum)&(UDP_HTABLE_SIZE-1)],(fpass))
 
 #define udp_v4_proxy_loop_next(hnum, hpnum, sk, fpass) \
 	secondlist((hpnum),(sk)->next,(fpass))
@@ -641,8 +642,15 @@
 	if (msg->msg_flags&MSG_OOB)	/* Mirror BSD error message compatibility */
 		return -EOPNOTSUPP;
 
+#ifdef CONFIG_IP_TRANSPARENT_PROXY
+	if (msg->msg_flags&~(MSG_DONTROUTE|MSG_DONTWAIT|MSG_PROXY))
+	  	return -EINVAL;
+	if ((msg->msg_flags&MSG_PROXY) && !suser() )
+	  	return -EPERM;
+#else
 	if (msg->msg_flags&~(MSG_DONTROUTE|MSG_DONTWAIT))
 	  	return -EINVAL;
+#endif
 
 	/*
 	 *	Get and verify the address. 
@@ -685,8 +693,27 @@
 		 */
 		rt = (struct rtable *)dst_check(&sk->dst_cache, 0);
   	}
+#ifdef CONFIG_IP_TRANSPARENT_PROXY
+	if (msg->msg_flags&MSG_PROXY) {
+		/*
+		 * We map the first 8 bytes of a second sockaddr_in
+		 * into the last 8 (unused) bytes of a sockaddr_in.
+		 */
+		struct sockaddr_in *from = (struct sockaddr_in *)msg->msg_name;
+		from = (struct sockaddr_in *)&from->sin_zero;
+		if (from->sin_family != AF_INET)
+			return -EINVAL;
+		ipc.addr = from->sin_addr.s_addr;
+		ufh.uh.source = from->sin_port;
+		if (ipc.addr == 0)
+			ipc.addr = sk->saddr;
+	} else
+#endif
+	{
+		ipc.addr = sk->saddr;
+		ufh.uh.source = sk->dummy_th.source;
+	}
 
-	ipc.addr = sk->saddr;
 	ipc.opt = NULL;
 	ipc.oif = sk->bound_dev_if;
 	if (msg->msg_controllen) {
@@ -710,7 +737,7 @@
 	tos = RT_TOS(sk->ip_tos);
 	if (sk->localroute || (msg->msg_flags&MSG_DONTROUTE) || 
 	    (ipc.opt && ipc.opt->is_strictroute)) {
-		tos |= 1;
+		tos |= RTO_ONLINK;
 		rt = NULL; /* sorry */
 	}
 
@@ -722,7 +749,11 @@
 	}
 
 	if (rt == NULL) {
-		err = ip_route_output(&rt, daddr, ufh.saddr, tos, ipc.oif);
+		err = ip_route_output(&rt, daddr, ufh.saddr,
+#ifdef CONFIG_IP_TRANSPARENT_PROXY
+			(msg->msg_flags&MSG_PROXY ? RTO_TPROXY : 0) |
+#endif
+			 tos, ipc.oif);
 		if (err) 
 			goto out;
 		localroute = 1;
@@ -735,7 +766,6 @@
 	ufh.saddr = rt->rt_src;
 	if (!ipc.addr)
 		ufh.daddr = ipc.addr = rt->rt_dst;
-	ufh.uh.source = sk->dummy_th.source;
 	ufh.uh.len = htons(ulen);
 	ufh.uh.check = 0;
 	ufh.other = (htons(ulen) << 16) + IPPROTO_UDP*256;
diff -u --recursive --new-file v2.1.87/linux/net/ipv6/Config.in linux/net/ipv6/Config.in
--- v2.1.87/linux/net/ipv6/Config.in	Mon Jan 12 15:28:28 1998
+++ linux/net/ipv6/Config.in	Thu Feb 19 14:46:16 1998
@@ -3,7 +3,7 @@
 # 
 bool 'IPv6: enable EUI-64 token format' CONFIG_IPV6_EUI64
 if [ "$CONFIG_IPV6_EUI64" = "y" ]; then
-  bool 'IPv6: disable provided based addresses' CONFIG_IPV6_NO_PB
+  bool 'IPv6: disable provider based addresses' CONFIG_IPV6_NO_PB
 fi
 if [ "$CONFIG_NETLINK" = "y" ]; then
   if [ "$CONFIG_RTNETLINK" = "n" ]; then
diff -u --recursive --new-file v2.1.87/linux/net/ipv6/addrconf.c linux/net/ipv6/addrconf.c
--- v2.1.87/linux/net/ipv6/addrconf.c	Tue Feb 17 13:12:50 1998
+++ linux/net/ipv6/addrconf.c	Thu Feb 19 14:59:53 1998
@@ -1091,8 +1091,12 @@
 		}
 
 		break;
+	case NETDEV_CHANGEMTU:
+	case NETDEV_CHANGE:
+		/* BUGGG... Should scan FIB to change pmtu on routes. --ANK */
+		break;
 	};
-	
+
 	return NOTIFY_OK;
 }
 
diff -u --recursive --new-file v2.1.87/linux/net/ipv6/route.c linux/net/ipv6/route.c
--- v2.1.87/linux/net/ipv6/route.c	Tue Feb 17 13:12:50 1998
+++ linux/net/ipv6/route.c	Thu Feb 19 14:59:53 1998
@@ -836,7 +836,7 @@
 				if (fn->fn_flags & RTN_ROOT)
 					break;
 				if (fn->fn_flags & RTN_RTINFO) {
-					rt = rt6_device_match(rt, dev, RTF_LINKRT);
+					rt = rt6_device_match(fn->leaf, dev, RTF_LINKRT);
 					goto restart;
 				}
 			}
@@ -1743,7 +1743,7 @@
 	struct sk_buff *skb;
 	int size = NLMSG_SPACE(sizeof(struct rtmsg)+256);
 
-	skb = alloc_skb(size, GFP_KERNEL);
+	skb = alloc_skb(size, GFP_ATOMIC);
 	if (!skb) {
 		netlink_set_err(rtnl, 0, RTMGRP_IPV6_ROUTE, ENOBUFS);
 		return;
diff -u --recursive --new-file v2.1.87/linux/net/ipv6/sit.c linux/net/ipv6/sit.c
--- v2.1.87/linux/net/ipv6/sit.c	Thu Feb 12 20:56:15 1998
+++ linux/net/ipv6/sit.c	Fri Feb 20 18:28:23 1998
@@ -14,7 +14,6 @@
  *      2 of the License, or (at your option) any later version.
  */
 
-#include <linux/config.h>
 #define __NO_VERSION__
 #include <linux/module.h>
 #include <linux/errno.h>
@@ -404,7 +403,7 @@
 		}
 
 		addr6 = (struct in6_addr*)&neigh->primary_key;
-		addr_type = neigh->type;
+		addr_type = ipv6_addr_type(addr6);
 
 		if (addr_type == IPV6_ADDR_ANY) {
 			addr6 = &skb->nh.ipv6h->daddr;
diff -u --recursive --new-file v2.1.87/linux/net/ipx/Config.in linux/net/ipx/Config.in
--- v2.1.87/linux/net/ipx/Config.in	Wed Dec 31 16:00:00 1969
+++ linux/net/ipx/Config.in	Thu Feb 19 14:46:15 1998
@@ -0,0 +1,6 @@
+#
+# IPX configuration
+#
+
+comment 'IPX options'
+bool 'Full internal IPX network' CONFIG_IPX_INTERN
diff -u --recursive --new-file v2.1.87/linux/net/ipx/Makefile linux/net/ipx/Makefile
--- v2.1.87/linux/net/ipx/Makefile	Mon Apr  7 11:35:33 1997
+++ linux/net/ipx/Makefile	Thu Feb 19 14:46:15 1998
@@ -1,5 +1,5 @@
 #
-# Makefile for the Linux TCP/IP (INET) layer.
+# Makefile for the Linux IPX layer.
 #
 # Note! Dependencies are done automagically by 'make dep', which also
 # removes any old dependencies. DON'T put your own dependencies here
@@ -7,12 +7,14 @@
 #
 # Note 2! The CFLAGS definition is now in the main makefile...
 
+# We only get in/to here if CONFIG_IPX = 'y' or 'm'
+
 O_TARGET := ipx.o
-O_OBJS   := af_ipx.o
 M_OBJS   := $(O_TARGET)
+OX_OBJS  += af_ipx.o
 
 ifeq ($(CONFIG_SYSCTL),y)
-O_OBJS += sysctl_net_ipx.o
+  O_OBJS += sysctl_net_ipx.o
 endif
 
 include $(TOPDIR)/Rules.make
diff -u --recursive --new-file v2.1.87/linux/net/ipx/af_ipx.c linux/net/ipx/af_ipx.c
--- v2.1.87/linux/net/ipx/af_ipx.c	Thu Feb 12 20:56:15 1998
+++ linux/net/ipx/af_ipx.c	Thu Feb 19 14:46:15 1998
@@ -214,7 +214,6 @@
 	}
 
 	sk_free(sk);
-	MOD_DEC_USE_COUNT;
 }
 
 /* The following code is used to support IPX Interfaces (IPXITF).  An
@@ -682,7 +681,6 @@
 
 static const char * ipx_frame_name(unsigned short);
 static const char * ipx_device_name(ipx_interface *);
-static int ipxrtr_route_skb(struct sk_buff *);
 
 static int ipxitf_rcv(ipx_interface *intrfc, struct sk_buff *skb)
 {
@@ -1391,7 +1389,7 @@
 				rt->ir_router_node : ipx->ipx_dest.node);
 }
 	
-static int ipxrtr_route_skb(struct sk_buff *skb)
+int ipxrtr_route_skb(struct sk_buff *skb)
 {
 	struct ipxhdr	*ipx = skb->nh.ipxh;
 	ipx_route	*r;
@@ -1720,8 +1718,11 @@
 	switch(sock->type)
 	{
 		case SOCK_DGRAM:
-			sock->ops = &ipx_dgram_ops;
-			break;
+                        sock->ops = &ipx_dgram_ops;
+                        break;
+		case SOCK_STREAM:	/* Allow higher levels to piggyback */
+		case SOCK_SEQPACKET:
+			printk(KERN_CRIT "IPX: _create-ing non_DGRAM socket\n");
 		default:
 			sk_free(sk);
 			return(-ESOCKTNOSUPPORT);
@@ -1744,6 +1745,9 @@
 	sk->dead=1;
 	sock->sk=NULL;
 	ipx_destroy_socket(sk);
+	if ( sock->type == SOCK_DGRAM ) {
+		MOD_DEC_USE_COUNT;
+	}
 	return(0);
 }
 
@@ -1819,7 +1823,9 @@
 			sk->protinfo.af_ipx.node,
 			sk->protinfo.af_ipx.port) != NULL)
 		{
-			SOCK_DEBUG(sk, "IPX: bind failed because port %X in use.\n", (int)addr->sipx_port);
+			SOCK_DEBUG(sk,
+				"IPX: bind failed because port %X in use.\n",
+				ntohs((int)addr->sipx_port));
 			return -EADDRINUSE;
 		}
 	}
@@ -1834,7 +1840,9 @@
 			IPX_NODE_LEN);
 
 		if(ipxitf_find_socket(intrfc, addr->sipx_port)!=NULL) {
-			SOCK_DEBUG(sk, "IPX: bind failed because port %X in use.\n", (int)addr->sipx_port);
+			SOCK_DEBUG(sk,
+				"IPX: bind failed because port %X in use.\n",
+				ntohs((int)addr->sipx_port));
 			return -EADDRINUSE;
 		}
 	}
@@ -1845,7 +1853,8 @@
 	   an interface routed to IPX with the ipx routing ioctl() */
 
 	if(ipxitf_find_socket(intrfc, addr->sipx_port)!=NULL) {
-		SOCK_DEBUG(sk, "IPX: bind failed because port %X in use.\n", (int)addr->sipx_port);
+		SOCK_DEBUG(sk, "IPX: bind failed because port %X in use.\n",
+				ntohs((int)addr->sipx_port));
 		return -EADDRINUSE;
 	}
 
@@ -1853,7 +1862,8 @@
 
 	ipxitf_insert_socket(intrfc, sk);
 	sk->zapped=0;
-	SOCK_DEBUG(sk, "IPX: socket is bound.\n");
+	SOCK_DEBUG(sk, "IPX: bound socket 0x%04X.\n", ntohs(addr->sipx_port) );
+
 	return 0;
 }
 
@@ -1894,8 +1904,10 @@
 	memcpy(sk->protinfo.af_ipx.dest_addr.node,
 		addr->sipx_node,IPX_NODE_LEN);
 	sk->protinfo.af_ipx.type=addr->sipx_type;
-	sock->state = SS_CONNECTED;
-	sk->state=TCP_ESTABLISHED;
+	if(sock->type == SOCK_DGRAM ) {
+		sock->state = SS_CONNECTED;
+		sk->state=TCP_ESTABLISHED;
+	}
 	return 0;
 }
 
@@ -2346,6 +2358,19 @@
 	printk(KERN_INFO "IPX Portions Copyright (c) 1995 Caldera, Inc.\n");
 }
 
+/* Higher layers need this info to prep tx pkts */
+int ipx_if_offset(unsigned long ipx_net_number)
+{
+	ipx_route *rt = NULL;
+
+	rt = ipxrtr_lookup(ipx_net_number);
+	return ( rt ? rt->ir_intrfc->if_ipx_offset : -ENETUNREACH );
+}
+
+/* Export symbols for higher layers */
+EXPORT_SYMBOL(ipxrtr_route_skb);
+EXPORT_SYMBOL(ipx_if_offset);
+
 #ifdef MODULE
 /* Note on MOD_{INC,DEC}_USE_COUNT:
  *
@@ -2399,8 +2424,6 @@
 
 	return;
 }
-
-EXPORT_NO_SYMBOLS;
 
 int init_module(void)
 {
diff -u --recursive --new-file v2.1.87/linux/net/sched/sch_sfq.c linux/net/sched/sch_sfq.c
--- v2.1.87/linux/net/sched/sch_sfq.c	Thu Feb 12 20:56:15 1998
+++ linux/net/sched/sch_sfq.c	Fri Feb 20 18:28:23 1998
@@ -12,7 +12,6 @@
 #include <asm/uaccess.h>
 #include <asm/system.h>
 #include <asm/bitops.h>
-#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/types.h>
 #include <linux/kernel.h>
diff -u --recursive --new-file v2.1.87/linux/net/sunrpc/clnt.c linux/net/sunrpc/clnt.c
--- v2.1.87/linux/net/sunrpc/clnt.c	Mon Jan 12 14:39:07 1998
+++ linux/net/sunrpc/clnt.c	Fri Feb 20 18:28:23 1998
@@ -21,8 +21,6 @@
  *  Copyright (C) 1995,1996 Olaf Kirch <okir@monad.swb.de>
  */
 
-#include <linux/config.h>
-
 #include <asm/system.h>
 #include <asm/segment.h>
 
diff -u --recursive --new-file v2.1.87/linux/net/wanrouter/wanmain.c linux/net/wanrouter/wanmain.c
--- v2.1.87/linux/net/wanrouter/wanmain.c	Fri Jan 30 11:28:10 1998
+++ linux/net/wanrouter/wanmain.c	Fri Feb 20 18:28:23 1998
@@ -27,7 +27,6 @@
 
 #include <linux/stddef.h>	/* offsetof(), etc. */
 #include <linux/errno.h>	/* return codes */
-#include <linux/config.h>	/* OS configuration options */
 #include <linux/kernel.h>
 #include <linux/module.h>	/* support for loadable modules */
 #include <linux/malloc.h>	/* kmalloc(), kfree() */
