diff -u --recursive --new-file v1.3.45/linux/CREDITS linux/CREDITS
--- v1.3.45/linux/CREDITS	Mon Nov 27 12:48:25 1995
+++ linux/CREDITS	Mon Dec 11 09:55:42 1995
@@ -2,7 +2,8 @@
 	contributed to the linux project.  It is sorted by name, and
 	formatted in a format that allows for easy grepping and
 	beautification by scripts.  The fields are: name (N), email (E),
-	description (D) and snail-mail address (S).  Thanks,
+	web-address (W), description (D) and snail-mail address (S).
+	Thanks,
 
 			Linus
 ----------
@@ -286,7 +287,10 @@
 
 N: Rik Faith
 E: faith@cs.unc.edu
-D: Future Domain TMC-16x0 SCSI driver
+E: r.faith@ieee.org
+D: Author: Future Domain TMC-16x0 SCSI driver
+D: Debugging: SCSI code; Cyclades serial driver; APM driver
+D: Debugging: XFree86 Mach 32 server, accelerated server code
 
 N: Juergen Fischer
 E: fischer@server.et-inf.fho-emden.de
@@ -420,6 +424,13 @@
 S: D-69126 Heidelberg
 S: Germany
 
+N: Miguel de Icaza Amozurrutia
+E: miguel@nuclecu.unam.mx
+D: Linux/SPARC team, Midnight Commander maintainer
+S: Avenida Copilco 162, 22-1003
+S: Mexico, DF
+S: Mexico
+
 N: Ian Jackson
 E: iwj10@cus.cam.ac.uk
 E: ijackson@nyx.cs.du.edu
@@ -794,6 +805,18 @@
 S: San Jose, California 95129
 S: USA
 
+N: Eric S. Raymond
+E: esr@thyrsus.com
+W: http://www.ccil.org/~esr/home.html
+S: 22 South Warren Avenue
+S: Malvern, PA 19355 USA
+D: ncurses library co-maintainer
+D: terminfo master file maintainer
+D: Distributions HOWTO editor
+D: Instigator, FHS standard
+D: Keeper of the Jargon File and curator of the Retrocomputing Museum
+D: Author, Emacs VC and GUD modes
+
 N: Florian La Roche
 E: rzsfl@rz.uni-sb.de
 E: flla@stud.uni-sb.de
@@ -803,8 +826,9 @@
 S: Germany
 
 N: Stephen Rothwell
-E: sfr@pdact.pd.necisa.oz.au
+E: Stephen.Rothwell@nec.com.au
 D: Boot/setup/build work for setup > 2K
+D: Author, APM driver
 S: 59 Bugden Avenue
 S: Gowrie ACT 2904
 S: Australia
@@ -1098,4 +1122,22 @@
 D: Miscellaneous kernel fixes
 S: 3078 Sulphur Spring Court
 S: San Jose, California 95148
+S: USA
+
+N: Chih-Jen Chang     
+E: chihjenc@scf.usc.edu
+E: chihjen@iis.sinica.edu.tw
+D: IGMP(Internet Group Management Protocol) version 2
+S: 3F, 65 Tajen street
+S: Tamsui town, Taipei county,
+S: Taiwan 251, Republic of China
+
+N: Tsu-Sheng Tsao
+E: tsusheng@scf.usc.edu
+D: IGMP(Internet Group Management Protocol) version 2
+S: 2F 14 ALY 31 LN 166 SEC 1 SHIH-PEI RD
+S: Taipei 
+S: Taiwan 112, Republic of China
+S: 24335 Delta Drive
+S: Diamond Bar, CA 91765
 S: USA
diff -u --recursive --new-file v1.3.45/linux/Documentation/cdrom/aztcd linux/Documentation/cdrom/aztcd
--- v1.3.45/linux/Documentation/cdrom/aztcd	Sat Nov 25 19:04:35 1995
+++ linux/Documentation/cdrom/aztcd	Mon Dec 11 11:18:47 1995
@@ -1,4 +1,4 @@
-$Id: README.aztcd,v 2.0 1995/11/10 19:37:18 root Exp root $
+$Id: README.aztcd,v 2.10 1995/12/03 11:55:14 root Exp root $
           Readme-File /usr/src/Documentation/cdrom/aztcd
            for Aztech CD-ROM CDA268-01A, ORCHID CD-3110,
                   OKANO/WEARNES CDD110, Conrad TXC
@@ -460,7 +460,7 @@
 Reinhard Max delivered the information for the CDROM-interface of the
 SoundWave32 soundcards.
 
-Jochen Koch and Olaf Koluza delivered the information for supporting Conrad's TXC
+Jochen Koch and Olaf Kaluza delivered the information for supporting Conrad's TXC
 drive.
 
 Anybody, who is interested in these items should have a look at 'ftp.gwdg.de',
diff -u --recursive --new-file v1.3.45/linux/Documentation/devices.tex linux/Documentation/devices.tex
--- v1.3.45/linux/Documentation/devices.tex	Tue Nov 21 13:22:04 1995
+++ linux/Documentation/devices.tex	Mon Dec 11 10:46:30 1995
@@ -42,7 +42,7 @@
 %
 \title{{\bf Linux Allocated Devices}}
 \author{Maintained by H. Peter Anvin $<$hpa@storm.net$>$}
-\date{Last revised: November 17, 1995}
+\date{Last revised: December 4, 1995}
 \maketitle
 %
 \noindent
@@ -77,12 +77,12 @@
 \section{Major numbers}
 
 \begin{devicelist}
-\major{ 0}{}{     }{Unnamed devices (NFS mounts, loopback devices)}
+\major{ 0}{}{     }{Unnamed devices (e.g. non-device mounts)}
 \major{ 1}{}{char }{Memory devices}
 \major{  }{}{block}{RAM disk}
-\major{ 2}{}{char }{Reserved for PTY's $<$tytso@athena.mit.edu$>$}
+\major{ 2}{}{char }{Pseudo-TTY masters}
 \major{  }{}{block}{Floppy disks}
-\major{ 3}{}{char }{Reserved for PTY's $<$tytso@athena.mit.edu$>$}
+\major{ 3}{}{char }{Pseudo-TTY slaves}
 \major{  }{}{block}{First MFM, RLL and IDE hard disk/CD-ROM interface}
 \major{ 4}{}{char }{TTY devices}
 \major{ 5}{}{char }{Alternate TTY devices}
@@ -142,7 +142,8 @@
 \major{  }{}{block}{Modular RAM disk}
 \major{36}{}{char }{Netlink support}
 \major{  }{}{block}{MCA ESDI hard disk}
-\major{37}{--41}{}{Unallocated}
+\major{37}{}{char }{IDE tape}
+\major{38}{--41}{}{Unallocated}
 \major{42}{}{}{Demo/sample use}
 \major{43}{--223}{}{Unallocated}
 \major{224}{--239}{}{SEE NOTE}
@@ -154,7 +155,7 @@
 
 
 \begin{devicelist}
-\major{0}{}{}{Unnamed devices (NFS mounts, loopback devices)}
+\major{0}{}{}{Unnamed devices (e.g. non-device mounts)}
 	\minor{0}{}{reserved as null device number}
 \end{devicelist}
 
@@ -175,7 +176,24 @@
 \end{devicelist}
 
 \begin{devicelist}
-\major{2}{}{char}{Reserved for PTY's $<$tytso@athena.mit.edu$>$}
+\major{2}{}{char}{Pseudo-TTY masters}
+	\minor{0}{/dev/ptyp0}{First PTY master}
+	\minor{1}{/dev/ptyp1}{Second PTY master}
+	\minordots
+	\minor{255}{/dev/ptyef}{256th PTY master}
+\end{devicelist}
+
+\noindent
+Pseudo-TTY's are named as follows:
+\begin{itemize}
+\item Masters are {\file pty}, slaves are {\file tty};
+\item the fourth letter is one of {\file pqrstuvwxyzabcde} indicating
+the 1st through 16th series of 16 pseudo-ttys each, and
+\item the fifth letter is one of {\file 0123456789abcdef} indicating
+the position within the series.
+\end{itemize}
+
+\begin{devicelist}
 \major{}{}{block}{Floppy disks}
 	\minor{0}{/dev/fd0}{Controller 1, drive 1 autodetect}
 	\minor{1}{/dev/fd1}{Controller 1, drive 2 autodetect}
@@ -235,7 +253,12 @@
 been deprecated, since the drive type is insignificant for these devices.
 
 \begin{devicelist}
-\major{3}{}{char}{Reserved for PTY's $<$tytso@athena.mit.edu$>$}
+\major{3}{}{char}{Pseudo-TTY slaves}
+	\minor{0}{/dev/ttyp0}{First PTY slave}
+	\minor{1}{/dev/ttyp1}{Second PTY slave}
+	\minordots
+	\minor{255}{/dev/ttyef}{256th PTY slave}
+\\
 \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)}
@@ -271,14 +294,10 @@
 \end{devicelist}
 
 \noindent
-Pseudo-tty's are named as follows:
-\begin{itemize}
-\item Masters are {\file pty}, slaves are {\file tty};
-\item the fourth letter is one of {\file pqrs} indicating
-the 1st, 2nd, 3rd, 4th series of 16 pseudo-ttys each, and
-\item the fifth letter is one of {\file 0123456789abcdef} indicating
-the position within the series.
-\end{itemize}
+For compatibility with previous versions of Linux, the first 64 PTYs
+are replicated under this device number.  This use will be obsolescent
+with the release of Linux 1.4 and may be removed in a future version
+of Linux.
 
 \begin{devicelist}
 \major{ 5}{}{char }{Alternate TTY devices}
@@ -358,6 +377,10 @@
 	\minor{5}{/dev/atarimouse}{Atari Mouse (68k)}
 	\minor{128}{/dev/beep}{Fancy beep device}
 	\minor{129}{/dev/modreq}{Kernel module load request}
+	\minor{130}{/dev/watchdog}{Watchdog timer port}
+	\minor{131}{/dev/temperature}{Machine internal temperature}
+	\minor{132}{/dev/hwtrap}{Hardware fault trap}
+	\minor{133}{/dev/exttrp}{External device trap}
 \end{devicelist}
 
 \begin{devicelist}
@@ -730,16 +753,16 @@
 
 \begin{devicelist}
 \major{34}{}{char }{Z8530 HDLC driver}
-	\minor{0}{/dev/sc1}{First Z8530, first port}
-	\minor{1}{/dev/sc2}{First Z8530, second port}
-	\minor{2}{/dev/sc3}{Second Z8530, first port}
-	\minor{3}{/dev/sc4}{Second Z8530, second port}
+	\minor{0}{/dev/scc0}{First Z8530, first port}
+	\minor{1}{/dev/scc1}{First Z8530, second port}
+	\minor{2}{/dev/scc2}{Second Z8530, first port}
+	\minor{3}{/dev/scc3}{Second Z8530, second port}
 	\minordots
 \end{devicelist}
 
 \noindent
-The author has indicated that the device names will change in a
-subsequent version; {\file /dev/sc1} will become {\file /dev/scc0} etc.
+In a previous version these files were named {\file /dev/sc1} for
+{\file /dev/scc0}, {\file /dev/sc2} for {\file /dev/scc1}, and so on.
 
 \begin{devicelist}
 \major{  }{}{block}{Fourth IDE hard disk/CD-ROM interface}
@@ -789,7 +812,16 @@
 3).
 
 \begin{devicelist}
-\major{37}{--41}{}{Unallocated}
+\major{37}{}{char }{IDE tape}
+	\minor{0}{/dev/ht0}{First IDE tape}
+	\minor{128}{/dev/nht0}{First IDE tape, no rewind-on-close}
+\end{devicelist}
+
+\noindent
+Currently, only one IDE tape drive is supported.
+
+\begin{devicelist}
+\major{38}{--41}{}{Unallocated}
 \end{devicelist}
 
 \begin{devicelist}
diff -u --recursive --new-file v1.3.45/linux/Documentation/devices.txt linux/Documentation/devices.txt
--- v1.3.45/linux/Documentation/devices.txt	Tue Nov 21 13:22:04 1995
+++ linux/Documentation/devices.txt	Mon Dec 11 10:46:30 1995
@@ -2,7 +2,7 @@
 
 	 Maintained by H. Peter Anvin <Peter.Anvin@linux.org>
 
-		   Last revised: September 20, 1995
+		    Last revised: December 4, 1995
 
 This list is the successor to Rick Miller's Linux Device List, which
 he stopped maintaining when he lost network access in 1993.  It is a
@@ -31,7 +31,7 @@
 permission of the author, assuming the author can be contacted without
 an unreasonable effort.
 
-  0 		Unnamed devices (NFS mounts, loopback devices)
+  0 		Unnamed devices (e.g. non-device mounts)
 		  0 = reserved as null device number
 
   1 char	Memory devices
@@ -46,8 +46,20 @@
 		  9 = /dev/urandom	Faster, less secure random number gen.
     block	RAM disk
 		  1 = /dev/ramdisk	RAM disk
+		    
+  2 char	Pseudo-TTY masters
+		  0 = /dev/ptyp0	First PTY master
+		  1 = /dev/ptyp1	Second PTY master
+		    ...
+		256 = /dev/ptyef	256th PTY master
 
-  2 char	Reserved for PTY's <tytso@athena.mit.edu>
+		Pseudo-tty's are named as follows:
+		* Masters are "pty", slaves are "tty";
+		* the fourth letter is one of pqrstuvwxyzabcde indicating
+		  the 1st through 16th series of 16 pseudo-ttys each, and
+		* the fifth letter is one of 0123456789abcdef indicating
+		  the position within the series.
+  
     block	Floppy disks
 		  0 = /dev/fd0		First floppy disk autodetect
 		  1 = /dev/fd1		Second floppy disk autodetect
@@ -100,7 +112,12 @@
 		and E for the 3.5" models have been deprecated, since
 		the drive type is insignificant for these devices.
 
-  3 char	Reserved for pty's <tytso@athena.mit.edu>
+  3 char	Pseudo-TTY slaves
+		  0 = /dev/ttyp0	First PTY slave
+		  1 = /dev/ttyp1	Second PTY slave
+		    ...
+		256 = /dev/ttyef	256th PTY slave
+
     block	First MFM, RLL and IDE hard disk/CD-ROM interface
     		  0 = /dev/hda		Master: whole disk (or CD-ROM)
 		 64 = /dev/hdb		Slave: whole disk (or CD-ROM)
@@ -133,12 +150,11 @@
 		      ...
 		255 = /dev/ttysf	64th pseudo-tty slave
 
-		Pseudo-tty's are named as follows:
-		* Masters are "pty", slaves are "tty";
-		* the fourth letter is one of p, q, r, s indicating
-		  the 1st, 2nd, 3rd, 4th series of 16 pseudo-ttys each, and
-		* the fifth letter is one of 0123456789abcdef indicating
-		  the position within the series.
+		For compatibility with previous versions of Linux, the
+		first 64 PTYs are replicated under this device
+		number.  This use will be obsolescent with the release
+		of Linux 1.4 and may be removed in a future version of
+		Linux.
 
   5 char	Alternate TTY devices
     		  0 = /dev/tty		Current TTY device
@@ -203,9 +219,10 @@
 		  5 = /dev/atarimouse	Atari Mouse (68k)
 		128 = /dev/beep		Fancy beep device
 		129 = /dev/modreq	Kernel module load request
-
-		The use of the suffix -mouse instead of -bm or -aux
-		has also been used.
+		130 = /dev/watchdog	Watchdog timer port
+		131 = /dev/temperature	Machine internal temperature
+		132 = /dev/hwtrap	Hardware fault trap
+		133 = /dev/exttrp	External device trap
 
  11 block	SCSI CD-ROM devices
     		  0 = /dev/sr0		First SCSI CD-ROM
@@ -533,7 +550,13 @@
 		Partitions are handled in the same way as IDE disks
 		(see major number 3).
 
- 37-41		UNALLOCATED
+ 37 char	IDE tape
+		  0 = /dev/ht0		First IDE tape
+		128 = /dev/nht0		First IDE tape, no rewind-on-close
+
+		Currently, only one IDE tape drive is supported.
+
+ 38-41		UNALLOCATED
 
  42		Demo/sample use
 
diff -u --recursive --new-file v1.3.45/linux/Documentation/networking/arcnet-hardware.txt linux/Documentation/networking/arcnet-hardware.txt
--- v1.3.45/linux/Documentation/networking/arcnet-hardware.txt	Sat Sep  9 15:26:50 1995
+++ linux/Documentation/networking/arcnet-hardware.txt	Thu Nov 30 14:05:22 1995
@@ -39,7 +39,8 @@
 but which makes transfers much more reliable than with ethernet.  In fact,
 ARCnet will guarantee that a packet arrives safely at the destination, and
 even if it can't possibly be delivered properly (ie. because of a cable
-break) it will at least tell the sender about it.
+break, or because the destination computer does not exist) it will at least
+tell the sender about it.
 
 In addition, all known ARCnet cards have an (almost) identical programming
 interface.  This means that with one "arcnet" driver you can support any
@@ -54,9 +55,9 @@
 limit on their packet sizes; standard ARCnet can only send packets that are
 up to 508 bytes in length.  This is smaller than the internet "bare minimum"
 of 576 bytes, let alone the ethernet MTU of 1500.  To compensate, an extra
-level of encapsulation is defined by RFC1201 called "packet splitting" which
-allows "virtual packets" to grow as large as 64K each, although they are
-generally kept down to the ethernet-style 1500 bytes.
+level of encapsulation is defined by RFC1201, which I call "packet
+splitting," which allows "virtual packets" to grow as large as 64K each,
+although they are generally kept down to the ethernet-style 1500 bytes.
 
 
 CABLING ARCNET NETWORKS
@@ -69,7 +70,7 @@
 
  - I, Avery Pennarun, tried to arrange it into something that makes sense
    when all put together.  All mistakes, then, are most likely my fault.
-   Bug me about them.
+   Bug me about them, and they will probably get fixed.
 
 Ideally, according to documentation, ARCnet networks should be connected
 with 93 Ohm cables with 93 Ohm resistors as terminators.  I use TV cable and
@@ -86,7 +87,9 @@
 		IBM Typ 3 100 Ohm up to 100 m
 		
 So you can see that while 93 Ohms is ideal, you can still go half a
-kilometer with 75 Ohm TV cable.
+kilometer with 75 Ohm TV cable.  A rule of thumb might be handy here, to
+help you remember:
+	Basically any coax cable, up to a ridiculously large distance.
 
 The above applies to all known ARCnet cards.  Specific to STAR cards,
 though, Stephen A. Wood has some information:
@@ -131,7 +134,7 @@
 	
 According to Vojtech Pavlik, there shouldn't be more than one passive hub
 between two "active ends", an active end being an active hub or an ARCnet
-card.
+card.  That makes sense to me.
 	
 As for BUS cards, they're even easier (for more than two cards, anyway; you
 can't get much simpler than direct-connecting two STAR cards with a TV
@@ -195,18 +198,20 @@
 All ARCnet cards should have a total of four or five different settings:
 
   - the I/O address:  this is the "port" your ARCnet card is on.  Probed
-    values, as of v0.14, are only from 0x200 through 0x3F0. (If your card
-    has additional ones, which is possible, please tell me.) This should not
-    be the same as any other device on your system.  According to a doc I
-    got from Novell, MS Windows prefers values of 0x300 or more, eating
-    netconnections on my system otherwise.
+    values in the Linux ARCnet driver are only from 0x200 through 0x3F0. (If
+    your card has additional ones, which is possible, please tell me.) This
+    should not be the same as any other device on your system.  According to
+    a doc I got from Novell, MS Windows prefers values of 0x300 or more,
+    eating netconnections on my system (at least) otherwise.  My guess is
+    this may be because, if your card is at 0x2E0, probing for a serial port
+    at 0x2E8 will reset the card and probably mess things up royally.
 	- Avery's favourite: 0x300.
 
-  - the IRQ:  on 8-bit cards, it might be 2 (9), 3, 4, 5, or 7.
+  - the IRQ: on  8-bit cards, it might be 2 (9), 3, 4, 5, or 7.
              on 16-bit cards, it might be 2 (9), 3, 4, 5, 7, or 10-15.  Make
     sure this is different from any other card on your system.  Note that
     IRQ2 is the same as IRQ9, as far as Linux is concerned.
-	- Avery's favourite: IRQ2.
+	- Avery's favourite: IRQ2 (actually IRQ9).
 
   - the memory address:  Unlike most cards, ARCnets use "shared memory" for
     copying buffers around.  Make SURE it doesn't conflict with any other
@@ -223,31 +228,33 @@
 
   - the station address:  Every ARCnet card has its own "unique" network
     address from 0 to 255.  Unlike ethernet, you can set this address
-    yourself with a jumper.  Since it's only 8 bits, you can only have 254
-    ARCnet cards on a network.  DON'T use 0 or 255, since these are
-    reserved. (although neat stuff will probably happen if you DO use them). 
-    By the way, if you haven't already guessed, don't set this the same as
-    any other ARCnet on your network!
+    yourself with a jumper or switch (or on some cards, with special
+    software).  Since it's only 8 bits, you can only have 254 ARCnet cards
+    on a network.  DON'T use 0 or 255, since these are reserved. (although
+    neat stuff will probably happen if you DO use them).  By the way, if you
+    haven't already guessed, don't set this the same as any other ARCnet on
+    your network!
 	- Avery's favourite:  3 and 4.  Not that it matters.
 	
   - There may be ETS1 and ETS2 settings.  These may or may not make a
-    difference, but are used to change the delays used when powering up
-    a computer on the network.  This is only necessary when wiring VERY
-    long range ARCnet networks, on the order of 4km or so; in any case,
-    the only real requirement here is that all cards on the network with
-    ETS1 and ETS2 jumpers have them in the same position.
+    difference on your card, but are used to change the delays used when
+    powering up a computer on the network.  This is only necessary when
+    wiring VERY long range ARCnet networks, on the order of 4km or so; in
+    any case, the only real requirement here is that all cards on the
+    network with ETS1 and ETS2 jumpers have them in the same position.
 
 
 Here's the all the jumper information I could obtain for individual card
-types.  Unfortunately, there is a lot of duplicated information here. 
-Someday, I may get around to actually organizing it.  Until then... too much
-is better than too little, I say :)
+types.  The format of this list has changed somewhat; I finally got around
+to unduplicating some of the information and making a few other changes, but
+didn't get very far yet.  If you notice any problems with the info, it's now
+officially my fault :(
 
 The model # is listed right above specifics for that card, so you should be
 able to use your text viewer's "search" function to find the entry you want. 
 
-If your model isn't listed, and has different settings, PLEASE PLEASE tell
-me.  I had to figure mine out without the manual, and it WASN'T FUN!
+If your model isn't listed and/or has different settings, PLEASE PLEASE
+tell me.  I had to figure mine out without the manual, and it WASN'T FUN!
 
 Even if your ARCnet model isn't listed, but has the same jumpers as another
 model that is, please e-mail me to say so.
@@ -266,7 +273,7 @@
 	SMC		PC550Longboard	16
 	SMC		PC600		16
 	SMC?		LCS-8830-T	16?
-	Puredata	PDI507		16
+	Puredata	PDI507		8
 	CNet Tech	CN120-Series	8
 	CNet Tech	CN160-Series	16
 	No Name		--		8/16
@@ -311,9 +318,6 @@
     130, 500, and 600 all have the same switches as Avery's PC100. 
     PC500/600 have several extra, undocumented pins though. (?)
   - PC110 settings were verified by Stephen A. Wood <saw@cebaf.gov>
-  - On the other hand, John Edward Bauer <jbauer@badlands.NoDak.edu> said
-    the PC110 settings are all wrong.  In his case, you need to switch all
-    the 1's with 0's.  If you're having problems, try that.
   - Also, the JP- and S-numbers probably don't match your card exactly.  Try
     to find jumpers/switches with the same number of settings - it's
     probably more reliable.
@@ -323,12 +327,20 @@
 (IRQ Setting)		      IRQ2  IRQ3 IRQ4 IRQ5 IRQ7
 		Put exactly one jumper on exactly one set of pins.
 
+
                           1  2   3  4  5  6   7  8  9 10
      S1                /----------------------------------\
 (I/O and Memory        |  1  1 * 0  0  0  0 * 1  1  0  1  |
  addresses)            \----------------------------------/
                           |--|   |--------|   |--------|
                           (a)       (b)           (m)
+                          
+                WARNING.  It's very important when setting these which way
+                you're holding the card, and which way you think is '1'!
+                
+                If you suspect that your settings are not being made
+		correctly, try reversing the direction or inverting the
+		switch positions.
 
 		a: The first digit of the I/O address.
 			Setting		Value
@@ -473,40 +485,8 @@
 -------------------
 
 The eight switches in group S2 are used to set the node ID.
-Each node attached to the network must have an unique node ID which
-must be diffrent from 0.
-Switch 1 serves as the least significant bit (LSB).
-
-The node ID is the sum of the values of all switches set to "1"  
-These values are:
-    Switch | Value
-    -------|-------
-      1    |   1
-      2    |   2
-      3    |   4
-      4    |   8
-      5    |  16
-      6    |  32
-      7    |  64
-      8    | 128
-
-Some Examples:
-
-    Switch         | Hex     | Decimal 
-   8 7 6 5 4 3 2 1 | Node ID | Node ID
-   ----------------|---------|---------
-   0 0 0 0 0 0 0 0 |    not allowed
-   0 0 0 0 0 0 0 1 |    1    |    1 
-   0 0 0 0 0 0 1 0 |    2    |    2
-   0 0 0 0 0 0 1 1 |    3    |    3
-       . . .       |         |
-   0 1 0 1 0 1 0 1 |   55    |   85
-       . . .       |         |
-   1 0 1 0 1 0 1 0 |   AA    |  170
-       . . .       |         |  
-   1 1 1 1 1 1 0 1 |   FD    |  253
-   1 1 1 1 1 1 1 0 |   FE    |  254
-   1 1 1 1 1 1 1 1 |   FF    |  255
+These switches work in a way similar to the PC100-series cards; see that
+entry for more information.
 
 
 Setting the I/O Base Address
@@ -556,7 +536,7 @@
    0 1 0  1 0 |  CD000  |  CE000
    0 1 0  1 1 |  CD800  |  CE000
               |         |
-   0 1 1  0 0 |  D0000  |  D2000  (Manufactor's default)
+   0 1 1  0 0 |  D0000  |  D2000  (Manufacturer's default)
    0 1 1  0 1 |  D0800  |  D2000
    0 1 1  1 0 |  D1000  |  D2000
    0 1 1  1 1 |  D1800  |  D2000
@@ -590,10 +570,9 @@
 
 The jumpers labeled EXT1 and EXT2 are used to determine the timeout 
 parameters. These two jumpers are normally left open.
-Refer to the COM9026 Data Sheet for alternate configurations.
 
 To select a hardware interrupt level set one (only one!) of the jumpers
-IRQ2, IRQ3, IRQ4, IRQ5, IRQ7. The Manufactor's default is IRQ2.
+IRQ2, IRQ3, IRQ4, IRQ5, IRQ7. The Manufacturer's default is IRQ2.
  
 
 Configuring the PC130E for Star or Bus Topology
@@ -658,7 +637,7 @@
 
 The PC550 is equipped with two modular RJ11-type jacks for connection
 to twisted pair wiring.
-It can be used in a star or a daisy-chained network.
+It can be used in a star or a daisy-chained (BUS) network.
 
        1 
        0 9 8 7 6 5 4 3 2 1     6 5 4 3 2 1
@@ -847,7 +826,7 @@
 ** Possibly SMC **
 LCS-8830-T (16-bit card)
 ------------------------
-	- from Mathias Katzer <mkatzer@HRZ.Uni-Bielefeld.DE>
+  - from Mathias Katzer <mkatzer@HRZ.Uni-Bielefeld.DE>
 	
 This is a LCS-8830-T made by SMC, I think ('SMC' only appears on one PLCC,
 nowhere else, not even on the few xeroxed sheets from the manual).
@@ -954,11 +933,9 @@
 *****************************************************************************
 
 ** PureData Corp **
-PDI507 (16-bit card)
+PDI507 (8-bit card)
 --------------------
-	- from Mark Rejhon <mdrejhon@magi.com> (slight modifications by
-	  Avery)
-	- Send questions/suggestions/etc about this text to Mark.
+  - from Mark Rejhon <mdrejhon@magi.com> (slight modifications by Avery)
 
 Jumpers:
 
@@ -1868,7 +1845,7 @@
 ** No Name **
 8-bit cards ("Made in Taiwan R.O.C.")
 -----------
- - from Vojtech Pavlik <vpav4328@diana.troja.mff.cuni.cz>
+  - from Vojtech Pavlik <vpav4328@diana.troja.mff.cuni.cz>
 
 I have named this ARCnet card "NONAME", since I got only the card with
 no manual at all and the only text identifying the manufacturer is 
diff -u --recursive --new-file v1.3.45/linux/Documentation/networking/arcnet.txt linux/Documentation/networking/arcnet.txt
--- v1.3.45/linux/Documentation/networking/arcnet.txt	Sun Oct 29 11:38:47 1995
+++ linux/Documentation/networking/arcnet.txt	Thu Nov 30 14:05:11 1995
@@ -58,8 +58,15 @@
 BOINGY - the linux-arcnet@807-city.on.ca mailing list is now so unstable
 that I can't recommend people even bother with it.  I no longer have an
 account on 807-CITY (though they still graciously forward my mail to me) so
-there's not much I can do.  If there's sufficient interest (e-mail me!) I
-will set one up at my new address, Foxnet.
+there's not much I can do.
+
+However, Tomasz Motylewski has been so kind as to set up a new and improved
+mailing list; subscribe by sending a message with the BODY "subscribe
+linux-arcnet YOUR REAL NAME" to listserv@tichy.ch.uj.edu.pl.  Then, to
+submit messages to the list, mail to linux-arcnet@tichy.ch.uj.edu.pl.
+
+There are mailing list archives at:
+	http://tichy.ch.uj.edu.pl/lists/linux-arcnet
 
 Send all bug (or success) reports to me, then, not the list, since (as I
 mentioned) the list doesn't work.
diff -u --recursive --new-file v1.3.45/linux/MAGIC linux/MAGIC
--- v1.3.45/linux/MAGIC	Sat Nov 25 19:04:35 1995
+++ linux/MAGIC	Mon Dec 11 09:55:42 1995
@@ -50,6 +50,7 @@
 
 Magic Name          Number  Structure            File
 ===========================================================================
+APM_BIOS_MAGIC      0x4101  struct apm_struct    include/linux/apm_bios.h
 CYCLADES_MAGIC      0x4359  struct cyclades_port include/linux/cyclades.h
 FASYNC_MAGIC        0x4601  struct fasync_struct include/linux/fs.h
 PTY_MAGIC           0x5001  struct pty_struct    drivers/char/pty.c
@@ -73,6 +74,7 @@
 0x06	linux/lp.h
 0x12	linux/fs.h
 0x20	linux/cm206.h
+'A'	linux/apm_bios.h
 'C'	linux/soundcard.h
 'K'	linux/kd.h
 'M'	linux/soundcard.h
diff -u --recursive --new-file v1.3.45/linux/Makefile linux/Makefile
--- v1.3.45/linux/Makefile	Mon Nov 27 12:48:26 1995
+++ linux/Makefile	Mon Dec 11 08:52:55 1995
@@ -1,6 +1,6 @@
 VERSION = 1
 PATCHLEVEL = 3
-SUBLEVEL = 45
+SUBLEVEL = 46
 
 ARCH = i386
 
@@ -171,9 +171,11 @@
 oldconfig: symlinks
 	$(CONFIG_SHELL) scripts/Configure -d arch/$(ARCH)/config.in
 
-xconfig: symlinks
-	( cd scripts ; make kconfig.tk)
-	./scripts/kconfig.tk
+xconfig: symlinks scripts/kconfig.tk
+	wish -f scripts/kconfig.tk
+
+scripts/kconfig.tk:
+	$(MAKE) -C scripts kconfig.tk
 
 config: symlinks
 	$(CONFIG_SHELL) scripts/Configure arch/$(ARCH)/config.in
@@ -294,7 +296,8 @@
 	rm -f $(TOPDIR)/include/linux/modules/*
 
 distclean: mrproper
-	rm -f core `find . -name '*.orig' -print`
+	rm -f core `find . \( -name '*.orig' -o -name '*~' -o -name '*.bak' \
+		-o -name '#*#' -o -name '.*.orig' \) -print`
 
 
 backup: mrproper
diff -u --recursive --new-file v1.3.45/linux/arch/alpha/kernel/entry.S linux/arch/alpha/kernel/entry.S
--- v1.3.45/linux/arch/alpha/kernel/entry.S	Wed Nov  8 07:11:29 1995
+++ linux/arch/alpha/kernel/entry.S	Mon Dec 11 06:56:35 1995
@@ -624,7 +624,7 @@
 	.quad sys_ftruncate, do_entSys, sys_setgid, sys_sendto, sys_shutdown
 	.quad sys_socketpair, sys_mkdir, sys_rmdir, sys_utimes, do_entSys
 	.quad do_entSys, sys_getpeername, do_entSys, do_entSys, sys_getrlimit
-	.quad sys_setrlimit, do_entSys, sys_setsid, do_entSys, do_entSys
+	.quad sys_setrlimit, do_entSys, sys_setsid, sys_quotactl, do_entSys
 /*150*/	.quad sys_getsockname, do_entSys, do_entSys, do_entSys, do_entSys
 	.quad do_entSys, sys_sigaction, do_entSys, do_entSys, osf_getdirentries
 	.quad osf_statfs, osf_fstatfs, do_entSys, do_entSys, do_entSys
diff -u --recursive --new-file v1.3.45/linux/arch/alpha/kernel/osf_sys.c linux/arch/alpha/kernel/osf_sys.c
--- v1.3.45/linux/arch/alpha/kernel/osf_sys.c	Mon Sep 18 14:53:48 1995
+++ linux/arch/alpha/kernel/osf_sys.c	Mon Dec 11 06:56:35 1995
@@ -32,7 +32,7 @@
 #include <asm/system.h>
 #include <asm/io.h>
 
-extern int do_mount(kdev_t, const char *, char *, int, void *);
+extern int do_mount(kdev_t, const char *, const char *, char *, int, void *);
 extern int do_pipe(int *);
 
 extern struct file_operations * get_blkfops(unsigned int);
@@ -308,7 +308,7 @@
 	retval = getdev(tmp.devname, 0, &inode);
 	if (retval)
 		return retval;
-	retval = do_mount(inode->i_rdev, dirname, "ext2", flags, NULL);
+	retval = do_mount(inode->i_rdev, tmp.devname, dirname, "ext2", flags, NULL);
 	if (retval)
 		putdev(inode);
 	iput(inode);
@@ -328,7 +328,7 @@
 	retval = getdev(tmp.devname, 1, &inode);
 	if (retval)
 		return retval;
-	retval = do_mount(inode->i_rdev, dirname, "iso9660", flags, NULL);
+	retval = do_mount(inode->i_rdev, tmp.devname, dirname, "iso9660", flags, NULL);
 	if (retval)
 		putdev(inode);
 	iput(inode);
@@ -348,7 +348,7 @@
 	dev = get_unnamed_dev();
 	if (!dev)
 		return -ENODEV;
-	retval = do_mount(dev, dirname, "proc", flags, NULL);
+	retval = do_mount(dev, "", dirname, "proc", flags, NULL);
 	if (retval)
 		put_unnamed_dev(dev);
 	return retval;
diff -u --recursive --new-file v1.3.45/linux/arch/i386/boot/setup.S linux/arch/i386/boot/setup.S
--- v1.3.45/linux/arch/i386/boot/setup.S	Fri Nov 17 08:42:24 1995
+++ linux/arch/i386/boot/setup.S	Mon Dec 11 09:55:42 1995
@@ -1,32 +1,34 @@
 !
 !	setup.S		Copyright (C) 1991, 1992 Linus Torvalds
 !
-! This code performs all initialization procedures that should be done
-! before entering the protected mode. It's responsible for getting of all
-! system data offered by BIOS and for detection / selection of video
-! mode. All information is put in a "safe" place: 0x90000-0x901FF, i. e.
-! where the boot-block used to be.  It is then up to the protected mode
+! setup.s is responsible for getting the system data from the BIOS,
+! and putting them into the appropriate places in system memory.
+! both setup.s and system has been loaded by the bootblock.
+!
+! This code asks the bios for memory/disk/other parameters, and
+! puts them in a "safe" place: 0x90000-0x901FF, ie where the
+! boot-block used to be. It is then up to the protected mode
 ! system to read them from there before the area is overwritten
 ! for buffer-blocks.
 !
 ! Move PS/2 aux init code to psaux.c
 ! (troyer@saifr00.cfsat.Honeywell.COM) 03Oct92
 !
-! Some changes and additional features by Christoph Niemann,
+! some changes and additional features by Christoph Niemann,
 ! March 1993/June 1994 (Christoph.Niemann@linux.org)
 !
-! Completely new video-mode handling code, VESA mode detection, support
-! for new Cirrus Logic cards and some additional changes
-! by Martin Mares <mj@k332.feld.cvut.cz>, October 1995.
+! add APM BIOS checking by Stephen Rothwell, May 1994
+! (Stephen.Rothwell@pd.necisa.oz.au)
 !
 
-! NOTE! These had better be the same as in bootsect.S!
+! NOTE! These had better be the same as in bootsect.s!
 #define __ASSEMBLY__
 #include <linux/config.h>
 #include <asm/segment.h>
 
-! Uncomment this if you want the BIOS mode numbers to be listed
-!#define SHOW_BIOS_MODES
+#ifndef SVGA_MODE
+#define SVGA_MODE ASK_VGA
+#endif
 
 ! Signature words to ensure LILO loaded us right
 #define SIG1	0xAA55
@@ -47,7 +49,6 @@
 
 entry start
 start:
-
 ! Bootlin depends on this being done early
 	mov	ax,#0x01500
 	mov	dl,#0x81
@@ -74,14 +75,12 @@
 ! Part of above routine, this one just prints ascii al
 
 prnt1:	push	ax
-	push	bx
 	push	cx
 	xor	bh,bh
 	mov	cx,#0x01
 	mov	ah,#0x0e
 	int	0x10
 	pop	cx
-	pop	bx
 	pop	ax
 	ret
 
@@ -149,10 +148,43 @@
 	xor	bx,bx		! clear bx
 	int	0x16
 
-! Check for video adapter and its parameters
-! Video mode selection is also handled here
+! check for EGA/VGA and some config parameters
 
-	call	video
+	mov	ah,#0x12
+	mov	bl,#0x10
+	int	0x10
+	mov	[8],ax
+	mov	[10],bx
+	mov	[12],cx
+	mov	ax,#0x5019
+	cmp	bl,#0x10
+	je	novga
+	mov	ax,#0x1a00	! Added check for EGA/VGA discrimination
+	int	0x10
+	mov	bx,ax
+	mov	ax,#0x5019
+	movb	[15],#0		! by default, no VGA
+	cmp	bl,#0x1a	! 1a means VGA, anything else EGA or lower
+	jne	novga
+	movb	[15],#1		! we've detected a VGA
+	call	chsvga
+novga:	mov	[14],al
+	mov	ah,#0x03	! read cursor pos
+	xor	bh,bh		! clear bh
+	int	0x10		! save it in known place, con_init fetches
+	mov	[0],dx		! it from 0x90000.
+	
+! Get video-card data:
+	
+	mov	ah,#0x0f
+	int	0x10
+	mov	[4],bx		! bh = display page
+	mov	[6],ax		! al = video mode, ah = window width
+	xor	ax,ax
+	mov	es,ax		! Access low memory
+	seg es
+	mov	ax,[0x485]	! POINTS - Height of character matrix
+	mov	[16],ax
 
 ! Get hd0 data
 
@@ -209,6 +241,48 @@
 	jz	no_psmouse
 	mov	[0x1ff],#0xaa	! device present
 no_psmouse:
+
+#ifdef CONFIG_APM
+! check for APM BIOS
+
+	mov	[64],#0		! version == 0 means no APM BIOS
+
+	mov	ax,#0x05300	! APM BIOS installation check
+	xor	bx,bx
+	int	0x15
+	jc	done_apm_bios	! error -> no APM BIOS
+
+	cmp	bx,#0x0504d	! check for "PM" signature
+	jne	done_apm_bios	! no signature -> no APM BIOS
+
+	mov	[64],ax		! record the APM BIOS version
+	mov	[76],cx		!	and flags
+	and	cx,#0x02	! Is 32 bit supported?
+	je	done_apm_bios	!	no ...
+
+	mov	ax,#0x05304	! Disconnect first just in case
+	xor	bx,bx
+	int	0x15		! ignore return code
+
+	mov	ax,#0x05303	! 32 bit connect
+	xor	bx,bx
+	int	0x15
+	jc	no_32_apm_bios	! error
+
+	mov	[66],ax		! BIOS code segment
+	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
+	jmp	done_apm_bios
+
+no_32_apm_bios:
+	and	[76], #0xfffd	! remove 32 bit support bit
+
+done_apm_bios:
+#endif
+
 ! now we want to move to protected mode ...
 
 	cli			! no interrupts allowed !
@@ -305,16 +379,17 @@
 ! Well, now's the time to actually move into protected mode. To make
 ! things as simple as possible, we do no register set-up or anything,
 ! we let the gnu-compiled 32-bit programs do that. We just jump to
-! absolute address 0x10000, in 32-bit protected mode.
+! absolute address 0x00000, in 32-bit protected mode.
 !
 ! Note that the short jump isn't strictly needed, although there are
 ! reasons why it might be a good idea. It won't hurt in any case.
 !
-	mov	ax,#1		! protected mode (PE) bit
+	xor	ax,ax
+	inc	ax		! protected mode (PE) bit
 	lmsw	ax		! This is it!
 	jmp	flush_instr
 flush_instr:
-	xor	bx,bx		! Flag to indicate a boot
+	mov	bx,#0		! Flag to indicate a boot
 	jmpi	0x1000,KERNEL_CS	! jmp offset 1000 of segment 0x10 (cs)
 
 ! This routine checks that the keyboard command queue is empty
@@ -334,6 +409,61 @@
 	test	al,#2		! is input buffer full?
 	jnz	empty_8042	! yes - loop
 	ret
+!
+! Read a key and return the (US-)ascii code in al, scan code in ah
+!
+getkey:
+	xor	ah,ah
+	int	0x16
+	ret
+
+!
+! Read a key with a timeout of 30 seconds. The cmos clock is used to get
+! the time.
+!
+getkt:
+	call	gettime
+	add	al,#30		! wait 30 seconds
+	cmp	al,#60
+	jl	lminute
+	sub	al,#60
+lminute:
+	mov	cl,al
+again:	mov	ah,#0x01
+	int	0x16
+	jnz	getkey		! key pressed, so get it
+	call	gettime
+	cmp	al,cl
+	jne	again
+	mov	al,#0x20	! timeout, return default char `space'
+	ret
+
+!
+! Flush the keyboard buffer
+!
+flush:	mov	ah,#0x01
+	int	0x16
+	jz	empty
+	xor	ah,ah
+	int	0x16
+	jmp	flush
+empty:	ret
+
+!
+! Read the cmos clock. Return the seconds in al
+!
+gettime:
+	push	cx
+	mov	ah,#0x02
+	int	0x1a
+	mov	al,dh			! dh contains the seconds
+	and	al,#0x0f
+	mov	ah,dh
+	mov	cl,#0x04
+	shr	ah,cl
+	aad
+	pop	cx
+	ret
 
 !
 ! Delay is needed after doing i/o
@@ -342,199 +472,43 @@
 	.word	0x00eb			! jmp $+2
 	ret
 
-!
-! Video card / mode detection. We do some hardware testing and build
-! a video mode list (placed directly after our code and data). Then we
-! choose the right mode given in the configuration or ask the user if
-! we are requested to do so. After all, all video parameters are stored
-! for later perusal by the kernel.
-!
+! Routine trying to recognize type of SVGA-board present (if any)
+! and if it recognize one gives the choices of resolution it offers.
+! If one is found the resolution chosen is given by al,ah (rows,cols).
 
-video:	movb	[15],#0		! Default is no VGA
-	mov	ax,[0x01fa]
+chsvga:	cld
 	push	ds
-	push	ds
-	pop	fs		! In this routine: FS=orig. DS
-	push	cs
-	pop	ds		! ES=DS=CS
 	push	cs
-	pop	es
+	mov	ax,[0x01fa]
+	pop	ds
 	mov	modesave,ax
-	lea	di,modelist	! ES:DI points to current item in our mode list
-	mov	eax,#0x50190000	! Store current mode: 80x25
-	cld
-	stosd
-
-	mov	ah,#0x12	! Check EGA/VGA
-	mov	bl,#0x10
-	int	0x10
-	seg	fs
-	mov	[10],bx		! Used for identification of VGA in the kernel
-	cmp	bl,#0x10	! Not EGA nor VGA -> 80x25 only
-	je	selmd1
-
-	mov	eax,#0x5032FFFF	! EGA or VGA: 80x50 supported
-	stosd
-
-	mov	ax,#0x1a00	! Added check for EGA/VGA discrimination
-	int	0x10
-	cmp	al,#0x1a	! 1a means VGA, anything else EGA
-	jne	selmd1
-	seg	fs
-	movb	[15],#1		! We've detected a VGA
-
-	mov	eax,#0x501cFFFE	! VGA: 80x28 supported
-	stosd
-
-	lea	si,vgatable	! Test all known SVGA adapters
-dosvga:	lodsw
-	mov	bp,ax		! Default mode table
-	or	ax,ax
-	jz	didsv
-	lodsw			! Pointer to test routine
-	push	si
-	push	di
-	push	es
-	mov	bx,#0xc000
-	mov	es,bx
-	call	ax		! Call test routine
-	pop	es
-	pop	di
-	pop	si
-	or	bp,bp
-	jz	dosvga
-	mov	si,bp		! Found, copy the modes
-	mov	ah,#0
-cpsvga:	lodsb
-	or	al,al
-	jz	didsv
-	stosw
-	movsw
-	jmp	cpsvga
-
-selmd1:	jmp	selmd
-
-didsv:	mov	ax,#0x4f00	! Fetch VESA information to ES:DI+0x400
-	add	di,#0x400
-	int	0x10
-	sub	di,#0x400
-	cmp	al,#0x4f
-	jne	selmd
-	lgs	bx,(di+0x40e)
-	cmp	(di+0x400),#0x4556
-	jne	selmd
-	cmp	(di+0x402),#0x4153
-	jne	selmd
-
-vesa1:	seg	gs
-	mov	cx,(bx)
-	add	bx,#2
-	cmp	cx,#0xffff
-	je	selmd
-	mov	ax,#0x4f01
-	add	di,#0xc00
-	int	0x10
-	sub	di,#0xc00
-	cmp	al,#0x4f
-	jne	selmd
-	testb	(di+0xc00),#0x10	! Is it a text mode?
-	jne	vesa1
-	testb	(di+0xc00),#0x08	! Has it colors?
-	je	vesa1
-	mov	dh,(di+0xc12)	! DX=dimensions, CX=mode
-	mov	dl,(di+0xc14)
-
-	lea	si,modelist	! Check if it's already on the list
-vesa2:	lodsw
-	lodsw
-	cmp	ax,dx
-	je	vesa1
-	cmp	si,di
-	jc	vesa2
-
-	mov	ax,cx		! New mode, store it
-	stosw
-	mov	ax,dx
-	stosw
-	jmp	vesa1
-
-!
-! Video mode table built. Determine the mode we should use and set it.
-!
-selmd:	mov	ax,modesave
-	cmp	ax,#NORMAL_VGA	! Current mode (80x25)
-	je	defmd1
-	cmp	ax,#EXTENDED_VGA ! 80x50 mode
-	je	try50
+	mov 	ax,#0xc000
+	mov	es,ax
+	mov	ax,modesave
+	cmp	ax,#NORMAL_VGA
+	je	defvga
+	cmp	ax,#EXTENDED_VGA
+	je	vga50
 	cmp	ax,#ASK_VGA
-	jne	usemd
-banner:	lea	si,keymsg
+	jne	svga
+	lea	si,msg1
 	call	prtstr
 	call	flush
 nokey:	call	getkt
-	cmp	al,#0x0d	! ENTER ?
-	je	listm		! yes - manual mode selection
-	cmp	al,#0x20	! SPACE ?
-	je	defmd1		! no - repeat
+	cmp	al,#0x0d		! enter ?
+	je	svga			! yes - svga selection
+	cmp	al,#0x20		! space ?
+	je	defvga			! no - repeat
 	call 	beep
 	jmp	nokey
-
-defmd1:	br	defmd
-
-listm:	call	listmodes	! List all available modes
-keymd:	call	getkey		! Get key representing mode ID
-	xor	ah,ah
-	sub	al,#0x30
-	jc	keymd
-	cmp	al,#10
-	jc	usemd
-	sub	al,#39
-	cmp	al,#10
-	jc	keymd
-	cmp	al,#26
-	jnc	keymd
-	jmp	usemd
-
-try50:	mov	ax,#1		! 80x50 is mode #1
-usemd:	shl	ax,#2		! We're requested to set mode in AX
-	lea	si,modelist
-	add	si,ax
-	cmp	si,di
-	jc	mdok
-	cmp	modesave,#ASK_VGA
-	je	keymd
-	lea	si,undefd
-	call	prtstr
-	jmp	banner
-
-mdok:	lodsw			! AX=mode number
-	cmp	ah,#0xff
-	jz	mdspec
-	or	ax,ax
-	jz	mdsetd
-	or	ah,ah
-	jz	mdset
-	mov	bx,ax
-	mov	ax,#0x4f02
-mdset:	int	0x10
-mdsetd:	lodsb			! AL=number of lines
-	jmp	getpar
-
-mdspec:	inc	ax		! Special modes
-	jz	m80x50
-
-m80x28: mov	ax,#0x1111	! Setting 80x28 (VGA with EGA font)
-	xor	bl,bl
-	int	0x10		! use 9x14 fontset (28 lines on VGA)
-	mov	ah,#0x01
-	mov	cx,#0x0b0c
-	int	0x10		! turn on cursor (scan lines 11 to 12)
-	mov	al,#28
-	jmp	getpar
-
-m80x50: mov	ax,#0x1112	! Setting 80x50 (EGA/VGA)
+defvga:	mov	ax,#0x5019
+	pop	ds
+	ret
+/* extended vga mode: 80x50 */
+vga50:
+	mov	ax,#0x1112
 	xor	bl,bl
-	int	0x10		! use 8x8 font set
+	int	0x10		! use 8x8 font set (50 lines on VGA)
 	mov	ax,#0x1200
 	mov	bl,#0x20
 	int	0x10		! use alternate print screen
@@ -544,65 +518,28 @@
 	mov	ah,#0x01
 	mov	cx,#0x0607
 	int	0x10		! turn on cursor (scan lines 6 to 7)
-	mov	al,#50
-	jmp	getpar
-
-defmd:	mov	al,#25		! Default is 25 lines
-
-!
-! Correct video mode set. Determine the remaining parameters.
-!
-
-getpar:	pop	ds		! Restore original DS
-	mov	[14],al		! Number of lines
-
-	mov	ah,#0x03	! read cursor pos
-	xor	bh,bh		! clear bh
-	int	0x10		! save it in known place, con_init fetches
-	mov	[0],dx		! it from 0x90000.
-	
-	mov	ah,#0x0f
-	int	0x10
-	mov	[4],bx		! bh = display page
-	mov	[6],ax		! al = video mode, ah = window width
-	xor	ax,ax
-	mov	es,ax		! Access low memory
-	seg es
-	mov	ax,[0x485]	! POINTS - Height of character matrix
-	mov	[16],ax
-
-	ret			! Well done...
-
-!
-! Table of all known SVGA cards. For each card, we store a pointer to
-! a table of video modes supported by the card and a pointer to a routine
-! used for testing of presence of the card.
-!
-
-vgatable:
-	.word	s3_md, s3_test
-	.word	ati_md, ati_test
-	.word	ahead_md, ahead_test
-	.word	chips_md, chips_test
-	.word	cirrus2_md, cirrus2_test
-	.word	cirrus1_md, cirrus1_test
-	.word	everex_md, everex_test
-	.word	genoa_md, genoa_test
-	.word	oak_md, oak_test
-	.word	paradise_md, paradise_test
-	.word	trident_md, trident_test
-	.word	tseng_md, tseng_test
-	.word	video7_md, video7_test
-	.word	0
-
+	pop	ds
+	mov	ax,#0x5032	! return 80x50
+	ret
+/* extended vga mode: 80x28 */
+vga28:
+	pop	ax		! clean the stack
+	mov	ax,#0x1111
+	xor	bl,bl
+	int	0x10		! use 9x14 fontset (28 lines on VGA)
+	mov	ah, #0x01
+	mov	cx,#0x0b0c
+	int	0x10		! turn on cursor (scan lines 11 to 12)
+	pop	ds
+	mov	ax,#0x501c	! return 80x28
+	ret
+/* svga modes */
 !
-! Test routines and mode tables:
+!	test for presence of an S3 VGA chip. The algorithm was taken
+!	from the SuperProbe package of XFree86 1.2.1
+!	report bugs to Christoph.Niemann@linux.org
 !
-
-! S3 - The test algorithm was taken from the SuperProbe package
-! for XFree86 1.2.1. Report bugs to Christoph.Niemann@linux.org
-
-s3_test:
+svga:   cld
 	mov	cx,#0x0f35	! we store some constants in cl/ch
 	mov	dx,#0x03d4
 	movb	al,#0x38
@@ -667,64 +604,29 @@
 	repne
 	scasb
 	je	no_s31
+	lea 	si,dsc_S3	! table of descriptions of video modes for BIOS
+	lea	di,mo_S3	! table of sizes of video modes for my BIOS
 	movb	ah,bh
 	movb	al,#0x38
-	jmp	s3rest
+	call	outidx		! restore old value of CRT register 0x38
+	br	selmod		! go ask for video mode
 no_s3:	movb	al,#0x35	! restore CRT register 0x35
 	movb	ah,bl
 	call	outidx
-no_s31:	xor	bp,bp		! Detection failed
-s3rest:	movb	ah,bh
-	movb	al,#0x38	! restore old value of CRT register 0x38
-outidx:	out	dx,al		! Write to indexed VGA register
-	push	ax		! AL=index, AH=data, DX=index reg port
-	mov	al,ah
-	inc	dx
-	out	dx,al
-	dec	dx
-	pop	ax
-	ret
-
-tstidx:	out	dx,ax		! OUT DX,AX and inidx
-inidx:	out	dx,al		! Read from indexed VGA register
-	inc	dx		! AL=index, DX=index reg port -> AL=data
-	in	al,dx
-	dec	dx
-	ret
-
-idS3:	.byte	0x81, 0x82, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95
-	.byte	0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa8, 0xb0
-
-s3_md:	.byte	0x54, 0x2b, 0x84
-	.byte	0x55, 0x19, 0x84
-	.byte	0
-
-! ATI cards.
+no_s31:	movb	ah,bh
+	movb	al,#0x38
+	call	outidx		! restore old value of CRT register 0x38
 
-ati_test:
-	lea 	si,idati
+	lea 	si,idati		! Check ATI 'clues'
 	mov	di,#0x31
 	mov 	cx,#0x09
 	repe
 	cmpsb
-	je	atiok
-	xor	bp,bp
-atiok:	ret
-
-idati:	.ascii	"761295520"
-
-ati_md:	.byte	0x23, 0x19, 0x84
-	.byte	0x33, 0x2c, 0x84
-	.byte	0x22, 0x1e, 0x64
-	.byte	0x21, 0x19, 0x64
-	.byte	0x58, 0x21, 0x50
-	.byte	0x5b, 0x1e, 0x50
-	.byte	0
-
-! AHEAD
-
-ahead_test:
-	mov	ax,#0x200f
+	jne	noati
+	lea	si,dscati
+	lea	di,moati
+	br	selmod
+noati:	mov	ax,#0x200f		! Check Ahead 'clues'
 	mov	dx,#0x3ce
 	out	dx,ax
 	inc	dx
@@ -732,23 +634,11 @@
 	cmp	al,#0x20
 	je	isahed
 	cmp	al,#0x21
-	je	isahed
-	xor	bp,bp
-isahed:	ret
-
-ahead_md:
-	.byte	0x22, 0x2c, 0x84
-	.byte	0x23, 0x19, 0x84
-	.byte	0x24, 0x1c, 0x84
-	.byte	0x2f, 0x32, 0xa0
-	.byte	0x32, 0x22, 0x50
-	.byte	0x34, 0x42, 0x50
-	.byte	0
-
-! Chips & Tech.
-
-chips_test:
-	mov	dx,#0x3c3
+	jne	noahed
+isahed:	lea	si,dscahead
+	lea	di,moahead
+	br	selmod
+noahed:	mov	dx,#0x3c3		! Check Chips & Tech. 'clues'
 	in	al,dx
 	or	al,#0x10
 	out	dx,al
@@ -759,20 +649,12 @@
 	in	al,dx
 	and	al,#0xef
 	out	dx,al
-	cmp	bl,#0xa5
-	je	cantok
-	xor	bp,bp
-cantok:	ret
-
-chips_md:
-	.byte	0x60, 0x19, 0x84
-	.byte	0x61, 0x32, 0x84
-	.byte	0
-
-! Cirrus Logic 5X0
-
-cirrus1_test:
-	mov	dx,#0x3d4
+	cmp	bl,[idcandt]
+	jne	nocant
+	lea	si,dsccandt
+	lea	di,mocandt
+	br	selmod
+nocant:	mov	dx,#0x3d4		! Check Cirrus 'clues'
 	mov	al,#0x0c
 	out	dx,al
 	inc	dx
@@ -805,76 +687,19 @@
 	out	dx,al
 	in	al,dx
 	cmp	al,#0x01
-	je	iscirr
-nocirr:	xor	bp,bp
-iscirr: mov	dx,#0x3d4
+	jne	nocirr
+	call	rst3d4	
+	lea	si,dsccirrus
+	lea	di,mocirrus
+	br	selmod
+rst3d4:	mov	dx,#0x3d4
 	mov	al,bl
 	xor	ah,ah
 	shl	ax,#8
 	add	ax,#0x0c
 	out	dx,ax
-	ret
-
-cirrus1_md:
-	.byte	0x1f, 0x19, 0x84
-	.byte	0x20, 0x2c, 0x84
-	.byte	0x22, 0x1e, 0x84
-	.byte	0x31, 0x25, 0x64
-	.byte	0
-
-! Cirrus Logic 54XX
-
-cirrus2_test:
-	mov	dx,#0x3c4
-	mov	al,#6
-	call	inidx
-	mov	bl,al			! BL=backup
-	mov	al,#6
-	xor	ah,ah
-	call	tstidx
-	cmp	al,#0x0f
-	jne	c2fail
-	mov	ax,#0x1206
-	call	tstidx
-	cmp	al,#0x12
-	jne	c2fail
-	mov	al,#0x1e
-	call	inidx
-	mov	bh,al
-	and	bh,#0xc0
-	mov	ah,bh
-	mov	al,#0x1e
-	call	tstidx
-	xor	al,bh
-	and	al,#0x3f
-	jne	c2xx
-	mov	al,#0x1e
-	mov	ah,bh
-	or	ah,#0x3f
-	call	tstidx
-	xor	al,bh
-	xor	al,#0x3f
-	and	al,#0x3f
-c2xx:	pushf
-	mov	al,#0x1e
-	mov	ah,bh
-	out	dx,ax
-	popf
-	je	c2done
-c2fail:	xor	bp,bp
-c2done:	mov	al,#6
-	mov	ah,bl
-	out	dx,ax
-	ret
-
-cirrus2_md:
-	.byte	0x14, 0x19, 0x84
-	.byte	0x54, 0x2b, 0x84
-	.byte	0
-
-! Everex / Trident
-
-everex_test:
+	ret	
+nocirr:	call	rst3d4			! Check Everex 'clues'
 	mov	ax,#0x7000
 	xor	bx,bx
 	int	0x10
@@ -882,32 +707,15 @@
 	jne	noevrx
 	shr	dx,#4
 	cmp	dx,#0x678
-	je	evtrid
+	je	istrid
 	cmp	dx,#0x236
-	jne	evrxok
-evtrid:	lea	bp,trident_md
-evrxok:	ret
-
-noevrx:	xor	bp,bp
-	ret
-
-everex_md:
-	.byte	0x03, 0x22, 0x50
-	.byte	0x04, 0x3c, 0x50
-	.byte	0x07, 0x2b, 0x64
-	.byte	0x08, 0x4b, 0x64
-	.byte	0x0a, 0x19, 0x84
-	.byte	0x0b, 0x2c, 0x84
-	.byte	0x16, 0x1e, 0x50
-	.byte	0x18, 0x1b, 0x64
-	.byte	0x21, 0x40, 0xa0
-	.byte	0x40, 0x1e, 0x84
-	.byte	0
-
-! Genoa.
-
-genoa_test:
-	lea	si,idgenoa		! Check Genoa 'clues'
+	je	istrid
+	lea	si,dsceverex
+	lea	di,moeverex
+	br	selmod
+istrid:	lea	cx,ev2tri
+	jmp	cx
+noevrx:	lea	si,idgenoa		! Check Genoa 'clues'
 	xor 	ax,ax
 	seg es
 	mov	al,[0x37]
@@ -923,72 +731,32 @@
 	seg es
 	cmp	al,(di)
 l2:	loope 	l1
-	or	cx,cx
-	je	isgen
-	xor	bp,bp
-isgen:	ret
-
-idgenoa: .byte	0x77, 0x00, 0x99, 0x66
-
-genoa_md:
-	.byte	0x58, 0x20, 0x50
-	.byte	0x5a, 0x2a, 0x64
-	.byte	0x60, 0x19, 0x84
-	.byte	0x61, 0x1d, 0x84
-	.byte	0x62, 0x20, 0x84
-	.byte	0x63, 0x2c, 0x84
-	.byte	0x64, 0x3c, 0x84
-	.byte	0x6b, 0x4f, 0x64
-	.byte	0x72, 0x3c, 0x50
-	.byte	0x74, 0x42, 0x50
-	.byte	0x78, 0x4b, 0x64
-	.byte	0
-
-! OAK
-
-oak_test:
+	cmp	cx,#0x00
+	jne	nogen
+	lea	si,dscgenoa
+	lea	di,mogenoa
+	br	selmod
+nogen:	cld
 	lea	si,idoakvga
 	mov	di,#0x08
 	mov	cx,#0x08
 	repe
 	cmpsb
-	je	isoak
-	xor	bp,bp
-isoak:	ret
-
-idoakvga: .ascii  "OAK VGA "
-
-oak_md: .byte	0x4e, 0x3c, 0x50
-	.byte	0x4f, 0x3c, 0x84
-	.byte	0x50, 0x19, 0x84
-	.byte	0x51, 0x2b, 0x84
-	.byte	0
-
-! WD Paradise.
-
-paradise_test:
-	lea	si,idparadise
+	jne	nooak
+	lea	si,dscoakvga
+	lea	di,mooakvga
+	br	selmod
+nooak:	cld
+	lea	si,idparadise		! Check Paradise 'clues'
 	mov	di,#0x7d
 	mov	cx,#0x04
 	repe
 	cmpsb
-	je	ispara
-	xor	bp,bp
-ispara:	ret
-
-idparadise:	.ascii	"VGA="
-
-paradise_md:
-	.byte	0x41, 0x22, 0x50
-	.byte	0x47, 0x1c, 0x84
-	.byte	0x55, 0x19, 0x84
-	.byte	0x54, 0x2c, 0x84
-	.byte	0
-
-! Trident.
-
-trident_test:
-	mov	dx,#0x3c4
+	jne	nopara
+	lea	si,dscparadise
+	lea	di,moparadise
+	br	selmod
+nopara:	mov	dx,#0x3c4		! Check Trident 'clues'
 	mov	al,#0x0e
 	out	dx,al
 	inc	dx
@@ -1007,24 +775,11 @@
 clrb2:	out	dx,al
 	and	ah,#0x0f
 	cmp	ah,#0x02
-	je	istrid
-	xor	bp,bp
-istrid:	ret
-
-trident_md:
-	.byte	0x50, 0x1e, 0x50
-	.byte	0x51, 0x2b, 0x50
-	.byte	0x52, 0x3c, 0x50
-	.byte	0x57, 0x19, 0x84
-	.byte	0x58, 0x1e, 0x84
-	.byte	0x59, 0x2b, 0x84
-	.byte	0x5a, 0x3c, 0x84
-	.byte	0
-
-! Tseng.
-
-tseng_test:
-	mov	dx,#0x3cd
+	jne	notrid
+ev2tri:	lea	si,dsctrident
+	lea	di,motrident
+	jmp	selmod
+notrid:	mov	dx,#0x3cd		! Check Tseng 'clues'
 	in	al,dx			! Could things be this simple ! :-)
 	mov	bl,al
 	mov	al,#0x55
@@ -1034,22 +789,11 @@
 	mov	al,bl
 	out	dx,al
 	cmp	ah,#0x55
- 	je	istsen
-	xor	bp,bp
-istsen:	ret
-
-tseng_md:
-	.byte	0x26, 0x3c, 0x50
-	.byte	0x2a, 0x28, 0x64
-	.byte	0x23, 0x19, 0x84
-	.byte	0x24, 0x1c, 0x84
-	.byte	0x22, 0x2c, 0x84
-	.byte	0
-
-! Video7.
-
-video7_test:
-	mov	dx,#0x3cc
+ 	jne	notsen
+	lea	si,dsctseng
+	lea	di,motseng
+	jmp	selmod
+notsen:	mov	dx,#0x3cc		! Check Video7 'clues'
 	in	al,dx
 	mov	dx,#0x3b4
 	and	al,#0x01
@@ -1078,78 +822,115 @@
 	mov	al,#0x55
 	xor	al,#0xea
 	cmp	al,bh
-	je	isvid7
-	xor	bp,bp
-isvid7:	ret
-
-video7_md:
-	.byte	0x40, 0x2b, 0x50
-	.byte	0x43, 0x3c, 0x50
-	.byte	0x44, 0x3c, 0x64
-	.byte	0x41, 0x19, 0x84
-	.byte	0x42, 0x2c, 0x84
-	.byte	0x45, 0x1c, 0x84
-	.byte	0
-
-!
-! Displaying of the mode list.
-!
-
-listmodes:
-	lea	si,listhdr
+	jne	novid7
+	lea	si,dscvideo7
+	lea	di,movideo7
+	jmp	selmod
+novid7:	lea	si,dsunknown
+	lea	di,mounknown
+selmod:	xor	cx,cx
+	mov	cl,(di)
+	mov	ax,modesave
+	cmp	ax,#ASK_VGA
+	je	askmod
+	cmp	ax,#NORMAL_VGA
+	je	askmod
+	cmp	al,cl
+	jl	gotmode
+	push	si
+	lea	si,msg4
 	call	prtstr
-	lea	bx,modelist
-	mov	cl,#0x30
-listm1:	mov	modenr,cl
-	lea	si,modestring
+	pop	si
+askmod:	push	si
+	lea	si,msg2
 	call	prtstr
-	mov	al,(bx+3)
+	pop	si
+	push	si
+	push	cx
+tbl:	pop	bx
+	push	bx
+	mov	al,bl
+	sub	al,cl
+	call	modepr
+	lodsw
+	xchg	al,ah
 	call	dprnt
+	xchg	ah,al
+	push	ax
 	mov	al,#0x78
 	call	prnt1
-	mov	al,(bx+2)
+	pop	ax
 	call	dprnt
-#ifdef SHOW_BIOS_MODES
-	mov	al,#0x20
-	call	prnt1
-	mov	al,#0x28
-	call	prnt1
-	mov	al,(bx+1)
-	call	prthex
-	mov	al,(bx)
-	call	prthex
-	mov	al,#0x29
-	call	prnt1
-#endif
-	lea	si,crlf
+	push	si
+	lea	si,crlf		! print CR+LF
 	call	prtstr
-	add	bx,#4
-	inc	cl
-	cmp	cl,#0x3a
-	jnz	listm2
-	mov	cl,#0x61
-listm2:	cmp	bx,di
-	jc	listm1
-	lea	si,prompt
-	br	prtstr
+	pop	si
+	loop	tbl
+	pop	cx
+	lea	si,msg3
+	call	prtstr
+	pop	si
+	add	cl,#0x30
+	jmp	nonum
+nonumb:	call	beep
+nonum:	call	getkey
+	cmp	al,#0x30	! ascii `0'
+	jb	nonumb
+	cmp	al,#0x3a	! ascii `9'
+	jbe	number
+	cmp	al,#0x61	! ascii `a'
+	jb	nonumb
+	cmp	al,#0x7a	! ascii `z'
+	ja	nonumb
+	sub	al,#0x27
+	cmp	al,cl
+	jae	nonumb
+	sub	al,#0x30
+	jmp	gotmode
+number: cmp	al,cl
+	jae	nonumb
+	sub	al,#0x30
+gotmode:	xor	ah,ah
+	or	al,al
+	beq	vga50
+	push	ax
+	dec	ax
+	beq	vga28
+	add	di,ax
+	mov	al,(di)
+	int 	0x10
+	pop	ax
+	shl	ax,#1
+	add	si,ax
+	lodsw
+	pop	ds
+	ret
 
-! Routine to print a hexadecimal byte (AL) on screen.
+! Routine to write al into a VGA-register that is
+! accessed via an index register
+!
+! dx contains the address of the index register
+! al contains the index
+! ah contains the value to write to the data register (dx + 1)
+!
+! no registers are changed
 
-#ifdef SHOW_BIOS_MODES
-prthex:	push	ax
-	shr	al,#4
-	call	prth1
+outidx:	out	dx,al
+	push	ax
+	mov	al,ah
+	inc	dx
+	out	dx,al
+	dec	dx
 	pop	ax
-prth1:	and	al,#15
-	cmp	al,#10
-	jc	prth2
-	add	al,#7
-prth2:	add	al,#0x30
-	br	prnt1
-#endif
+	ret
+inidx:	out	dx,al
+	inc	dx
+	in	al,dx
+	dec	dx
+	ret
 
 ! Routine to print a decimal value on screen, the value to be
-! printed is put in AL (i.e 0-255). 
+! printed is put in al (i.e 0-255). 
 
 dprnt:	push	ax
 	push	cx
@@ -1170,65 +951,25 @@
 	ret
 
 !
-! Read a key and return the (US-)ascii code in al, scan code in ah
+! Routine to print the mode number key on screen. Mode numbers
+! 0-9 print the ascii values `0' to '9', 10-35 are represented by
+! the letters `a' to `z'. This routine prints some spaces around the
+! mode no.
 !
-getkey:
-	xor	ah,ah
-	int	0x16
-	ret
 
-!
-! Read a key with a timeout of 30 seconds. The cmos clock is used to get
-! the time.
-!
-getkt:
-	call	gettime
-	add	al,#30		! wait 30 seconds
-	cmp	al,#60
-	jl	lminute
-	sub	al,#60
-lminute:
-	mov	cl,al
-again:	mov	ah,#0x01
-	int	0x16
-	jnz	getkey		! key pressed, so get it
-	call	gettime
-	cmp	al,cl
-	jne	again
-	mov	al,#0x20	! timeout, return default char `space'
-	ret
-
-!
-! Flush the keyboard buffer
-!
-flush:	mov	ah,#0x01
-	int	0x16
-	jz	empty
-	xor	ah,ah
-	int	0x16
-	jmp	flush
-empty:	ret
-
-!
-! Read the cmos clock. Return the seconds in al
-!
-gettime:
-	push	cx
-	mov	ah,#0x02
-	int	0x1a
-	mov	al,dh			! dh contains the seconds
-	and	al,#0x0f
-	mov	ah,dh
-	mov	cl,#0x04
-	shr	ah,cl
-	aad
-	pop	cx
+modepr:	push	ax
+	cmp	al,#0x0a
+	jb	digit		! Here is no check for number > 35
+	add	al,#0x27
+digit:	add	al,#0x30
+	mov	modenr, al
+	push 	si
+	lea	si, modestring
+	call	prtstr
+	pop	si
+	pop	ax
 	ret
 
-!
-! Descriptor table for our protected mode transition.
-!
-
 gdt:
 	.word	0,0,0,0		! dummy
 
@@ -1252,36 +993,68 @@
 	.word	0x800		! gdt limit=2048, 256 GDT entries
 	.word	512+gdt,0x9	! gdt base = 0X9xxxx
 
-!
-! Assorted messages.
-!
-
-keymsg:		.ascii	"Press <RETURN> to see video modes available, <SPACE> to continue or wait 30 secs"
-		db	0x0d, 0x0a, 0x00
-listhdr:	.ascii	"Mode:  COLSxROWS:"
+msg1:		.ascii	"Press <RETURN> to see SVGA-modes available, <SPACE> to continue or wait 30 secs."
+		db	0x0d, 0x0a, 0x0a, 0x00
+msg2:		.ascii	"Mode:  COLSxROWS:"
 		db	0x0d, 0x0a, 0x0a, 0x00
-prompt:		db	0x0d, 0x0a
+msg3:		db	0x0d, 0x0a
 		.ascii	"Choose mode by pressing the corresponding number or letter."
 crlf:		db	0x0d, 0x0a, 0x00
-undefd:		.ascii	"You passed an undefined mode number to setup. Please choose a new mode."
+msg4:		.ascii	"You passed an undefined mode number to setup. Please choose a new mode."
 		db	0x0d, 0x0a, 0x0a, 0x07, 0x00
 modestring:	.ascii	"   "
 modenr:		db	0x00	! mode number
 		.ascii	":    "
 		db	0x00
-
-modesave:	.word	0	! Requsted mode ID.
+		
+idati:		.ascii	"761295520"
+idcandt:	.byte	0xa5
+idgenoa:	.byte	0x77, 0x00, 0x99, 0x66
+idparadise:	.ascii	"VGA="
+idoakvga:	.ascii  "OAK VGA "
+idS3:		.byte	0x81, 0x82, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95
+		.byte	0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa8, 0xb0
+
+! Manufacturer:	  Numofmodes+2:	Mode:
+! Number of modes is the number of chip-specific svga modes plus the extended
+! modes available on any vga (currently 2)
+
+moati:		.byte	0x06,	0x23, 0x33, 0x22, 0x21
+moahead:	.byte	0x07,	0x22, 0x23, 0x24, 0x2f, 0x34
+mocandt:	.byte	0x04,	0x60, 0x61
+mocirrus:	.byte	0x06,	0x1f, 0x20, 0x22, 0x31
+moeverex:	.byte	0x0c,	0x03, 0x04, 0x07, 0x08, 0x0a, 0x0b, 0x16, 0x18, 0x21, 0x40
+mogenoa:	.byte	0x0c,	0x58, 0x5a, 0x60, 0x61, 0x62, 0x63, 0x64, 0x72, 0x74, 0x78
+moparadise:	.byte	0x04,	0x55, 0x54
+motrident:	.byte	0x09,	0x50, 0x51, 0x52, 0x57, 0x58, 0x59, 0x5a
+motseng:	.byte	0x07,	0x26, 0x2a, 0x23, 0x24, 0x22
+movideo7:	.byte	0x08,	0x40, 0x43, 0x44, 0x41, 0x42, 0x45
+mooakvga:	.byte   0x08,   0x00, 0x07, 0x4e, 0x4f, 0x50, 0x51
+mo_S3:		.byte	0x04,	0x54, 0x55
+mounknown:	.byte	0x02
+
+!			msb = Cols lsb = Rows:
+! The first two modes are standard vga modes available on any vga.
+! mode 0 is 80x50 and mode 1 is 80x28
+
+dscati:		.word	0x5032, 0x501c, 0x8419, 0x842c, 0x641e, 0x6419
+dscahead:	.word	0x5032, 0x501c, 0x842c, 0x8419, 0x841c, 0xa032, 0x5042
+dsccandt:	.word	0x5032, 0x501c, 0x8419, 0x8432
+dsccirrus:	.word	0x5032, 0x501c, 0x8419, 0x842c, 0x841e, 0x6425
+dsceverex:	.word	0x5032, 0x501c, 0x5022, 0x503c, 0x642b, 0x644b, 0x8419, 0x842c, 0x501e, 0x641b, 0xa040, 0x841e
+dscgenoa:	.word	0x5032, 0x501c, 0x5020, 0x642a, 0x8419, 0x841d, 0x8420, 0x842c, 0x843c, 0x503c, 0x5042, 0x644b
+dscparadise:	.word	0x5032, 0x501c, 0x8419, 0x842c
+dsctrident:	.word 	0x5032, 0x501c, 0x501e, 0x502b, 0x503c, 0x8419, 0x841e, 0x842b, 0x843c
+dsctseng:	.word	0x5032, 0x501c, 0x503c, 0x6428, 0x8419, 0x841c, 0x842c
+dscvideo7:	.word	0x5032, 0x501c, 0x502b, 0x503c, 0x643c, 0x8419, 0x842c, 0x841c
+dscoakvga:	.word   0x5032, 0x501c, 0x2819, 0x5019, 0x503c, 0x843c, 0x8419, 0x842b
+dsc_S3:		.word	0x5032, 0x501c, 0x842b, 0x8419
+dsunknown:	.word	0x5032, 0x501c
+modesave:	.word	SVGA_MODE
 
 ! This must be last
 setup_sig1:	.word	SIG1
 setup_sig2:	.word	SIG2
-
-! After our code and data, we'll store the mode list.
-! Mode record:	.word	modenr
-!		.byte	lines
-!		.byte	columns
-! Mode numbers used: 0=current, >=0x100=VESA, -1=80x50, -2=80x28
-modelist:
 
 .text
 endtext:
diff -u --recursive --new-file v1.3.45/linux/arch/i386/config.in linux/arch/i386/config.in
--- v1.3.45/linux/arch/i386/config.in	Wed Nov  8 07:11:29 1995
+++ linux/arch/i386/config.in	Mon Dec 11 08:49:35 1995
@@ -56,7 +56,7 @@
 mainmenu_option next_comment
 comment 'CD-ROM drivers (not for SCSI or IDE/ATAPI drives)'
 
-bool 'Support non-SCSI/IDE/ATAPI drives' CONFIG_CD_NO_IDESCSI
+bool 'Support non-SCSI/IDE/ATAPI CDROM drives' CONFIG_CD_NO_IDESCSI
 if [ "$CONFIG_CD_NO_IDESCSI" != "n" ]; then
   source drivers/cdrom/Config.in
 fi
diff -u --recursive --new-file v1.3.45/linux/arch/i386/defconfig linux/arch/i386/defconfig
--- v1.3.45/linux/arch/i386/defconfig	Fri Nov 17 08:42:24 1995
+++ linux/arch/i386/defconfig	Mon Dec 11 09:55:42 1995
@@ -31,7 +31,9 @@
 #
 # CONFIG_BLK_DEV_HD is not set
 CONFIG_BLK_DEV_IDE=y
+CONFIG_BLK_DEV_IDEATAPI=y
 CONFIG_BLK_DEV_IDECD=y
+# CONFIG_BLK_DEV_IDETAPE is not set
 # CONFIG_BLK_DEV_TRITON is not set
 # CONFIG_BLK_DEV_XD is not set
 
@@ -119,6 +121,11 @@
 # CONFIG_MS_BUSMOUSE is not set
 # CONFIG_ATIXL_BUSMOUSE is not set
 # CONFIG_QIC02_TAPE is not set
+# CONFIG_APM is not set
+# CONFIG_APM_IGNORE_USER_SUSPEND is not set
+# CONFIG_APM_DO_ENABLE is not set
+CONFIG_APM_CPU_IDLE=y
+# CONFIG_APM_DISPLAY_BLANK is not set
 
 #
 # Sound
diff -u --recursive --new-file v1.3.45/linux/arch/i386/kernel/head.S linux/arch/i386/kernel/head.S
--- v1.3.45/linux/arch/i386/kernel/head.S	Sat Nov 11 17:41:32 1995
+++ linux/arch/i386/kernel/head.S	Mon Dec 11 09:55:42 1995
@@ -12,6 +12,7 @@
 #include <linux/tasks.h>
 #include <linux/linkage.h>
 #include <asm/segment.h>
+#include <linux/config.h>
 
 #define CL_MAGIC_ADDR	0x90020
 #define CL_MAGIC	0xA33F
@@ -382,7 +383,11 @@
 	ALIGN
 .word 0
 gdt_descr:
+#ifdef CONFIG_APM
+	.word (11+2*NR_TASKS)*8-1
+#else
 	.word (8+2*NR_TASKS)*8-1
+#endif
 	.long 0xc0000000+SYMBOL_NAME(gdt)
 
 /*
@@ -399,3 +404,8 @@
 	.quad 0x0000000000000000	/* not used */
 	.quad 0x0000000000000000	/* not used */
 	.fill 2*NR_TASKS,8,0		/* space for LDT's and TSS's etc */
+#ifdef CONFIG_APM
+	.quad 0x00c09a0000000000	/* APM CS    code */
+	.quad 0x00809a0000000000	/* APM CS 16 code (16 bit) */
+	.quad 0x00c0920000000000	/* APM DS    data */
+#endif
diff -u --recursive --new-file v1.3.45/linux/arch/i386/kernel/process.c linux/arch/i386/kernel/process.c
--- v1.3.45/linux/arch/i386/kernel/process.c	Tue Nov 21 13:22:05 1995
+++ linux/arch/i386/kernel/process.c	Mon Dec 11 09:55:42 1995
@@ -19,6 +19,8 @@
 #include <linux/ldt.h>
 #include <linux/user.h>
 #include <linux/a.out.h>
+#include <linux/interrupt.h>
+#include <linux/config.h>
 
 #include <asm/segment.h>
 #include <asm/pgtable.h>
@@ -29,8 +31,15 @@
 
 asmlinkage void ret_from_sys_call(void) __asm__("ret_from_sys_call");
 
+#ifdef CONFIG_APM
+extern int  apm_do_idle(void);
+extern void apm_do_busy(void);
+#endif
+
 static int hlt_counter=0;
 
+#define HARD_IDLE_TIMEOUT (HZ / 3)
+
 void disable_hlt(void)
 {
 	hlt_counter++;
@@ -41,11 +50,41 @@
 	hlt_counter--;
 }
 
+static void hard_idle(void)
+{
+	while (!need_resched) {
+		if (hlt_works_ok && !hlt_counter) {
+#ifdef CONFIG_APM
+				/* If the APM BIOS is not enabled, or there
+				 is an error calling the idle routine, we
+				 should hlt if possible.  We need to check
+				 need_resched again because an interrupt
+				 may have occured in apm_do_idle(). */
+			start_bh_atomic();
+			if (!apm_do_idle() && !need_resched)
+				__asm__("hlt");
+			end_bh_atomic();
+#else
+			__asm__("hlt");
+#endif
+	        }
+ 		if (need_resched) break;
+		schedule();
+	}
+#ifdef CONFIG_APM
+	apm_do_busy();
+#endif
+}
+
 /*
  * The idle loop on a i386..
  */
 asmlinkage int sys_idle(void)
 {
+#ifndef __SMP__
+        unsigned long start_idle = 0;
+#endif
+		
 	if (current->pid != 0)
 	{
 	/*	printk("Wrong process idled\n");	SMP bug check */
@@ -78,10 +117,17 @@
 	for (;;) {
 #ifdef __SMP__
 		if (cpu_data[smp_processor_id()].hlt_works_ok && !hlt_counter && !need_resched)
-#else	
-		if (hlt_works_ok && !hlt_counter && !need_resched)
-#endif		
 			__asm__("hlt");
+#else	
+		if (!start_idle) start_idle = jiffies;
+		if (jiffies - start_idle > HARD_IDLE_TIMEOUT) {
+			hard_idle();
+		} else {
+			if (hlt_works_ok && !hlt_counter && !need_resched)
+		        	__asm__("hlt");
+		}
+		if (need_resched) start_idle = 0;
+#endif
 		schedule();
 	}
 }
diff -u --recursive --new-file v1.3.45/linux/arch/i386/kernel/setup.c linux/arch/i386/kernel/setup.c
--- v1.3.45/linux/arch/i386/kernel/setup.c	Tue Nov 21 13:22:05 1995
+++ linux/arch/i386/kernel/setup.c	Mon Dec 11 09:55:42 1995
@@ -23,6 +23,9 @@
 #include <linux/ioport.h>
 #include <linux/delay.h>
 #include <linux/config.h>
+#ifdef CONFIG_APM
+#include <linux/apm_bios.h>
+#endif
 
 #include <asm/segment.h>
 #include <asm/system.h>
@@ -54,6 +57,9 @@
  */
 struct drive_info_struct { char dummy[32]; } drive_info;
 struct screen_info screen_info;
+#ifdef CONFIG_APM
+struct apm_bios_info apm_bios_info;
+#endif
 
 unsigned char aux_device_present;
 extern int ramdisk_size;
@@ -67,6 +73,9 @@
  */
 #define PARAM	empty_zero_page
 #define EXT_MEM_K (*(unsigned short *) (PARAM+2))
+#ifdef CONFIG_APM
+#define APM_BIOS_INFO (*(struct apm_bios_info *) (PARAM+64))
+#endif
 #define DRIVE_INFO (*(struct drive_info_struct *) (PARAM+0x80))
 #define SCREEN_INFO (*(struct screen_info *) (PARAM+0))
 #define MOUNT_ROOT_RDONLY (*(unsigned short *) (PARAM+0x1F2))
@@ -95,6 +104,9 @@
  	ROOT_DEV = to_kdev_t(ORIG_ROOT_DEV);
  	drive_info = DRIVE_INFO;
  	screen_info = SCREEN_INFO;
+#ifdef CONFIG_APM
+	apm_bios_info = APM_BIOS_INFO;
+#endif
 	aux_device_present = AUX_DEVICE_INFO;
 	memory_end = (1<<20) + (EXT_MEM_K<<10);
 	memory_end &= PAGE_MASK;
diff -u --recursive --new-file v1.3.45/linux/arch/i386/kernel/time.c linux/arch/i386/kernel/time.c
--- v1.3.45/linux/arch/i386/kernel/time.c	Wed Oct  4 14:14:29 1995
+++ linux/arch/i386/kernel/time.c	Mon Dec 11 09:55:42 1995
@@ -23,6 +23,7 @@
 
 #include <linux/mc146818rtc.h>
 #include <linux/timex.h>
+#include <linux/config.h>
 
 #define TIMER_IRQ 0
 
@@ -313,9 +314,8 @@
 	  )*60 + sec; /* finally seconds */
 }
 
-void time_init(void)
+unsigned long get_cmos_time(void)
 {
-	void (*irq_handler)(int, struct pt_regs *);
 	unsigned int year, mon, day, hour, min, sec;
 	int i;
 
@@ -350,11 +350,21 @@
 	  }
 	if ((year += 1900) < 1970)
 		year += 100;
-	xtime.tv_sec = mktime(year, mon, day, hour, min, sec);
+	return mktime(year, mon, day, hour, min, sec);
+}
+
+void time_init(void)
+{
+	void (*irq_handler)(int, struct pt_regs *);
+	xtime.tv_sec = get_cmos_time();
 	xtime.tv_usec = 0;
 
 	/* If we have the CPU hardware time counters, use them */
-	irq_handler = timer_interrupt;	
+	irq_handler = timer_interrupt;
+#ifndef CONFIG_APM
+				/* Don't use them if a suspend/resume could
+                                   corrupt the timer value.  This problem
+                                   needs more debugging. */
 	if (x86_capability & 16) {
 		irq_handler = pentium_timer_interrupt;
 		do_gettimeoffset = do_fast_gettimeoffset;
@@ -363,6 +373,7 @@
 			:"=a" (((unsigned long *) &init_timer_cc)[0]),
 			 "=d" (((unsigned long *) &init_timer_cc)[1]));
 	}
+#endif
 	if (request_irq(TIMER_IRQ, irq_handler, 0, "timer") != 0)
 		panic("Could not allocate timer IRQ!");
 }
diff -u --recursive --new-file v1.3.45/linux/drivers/block/Config.in linux/drivers/block/Config.in
--- v1.3.45/linux/drivers/block/Config.in	Wed Nov  8 07:11:30 1995
+++ linux/drivers/block/Config.in	Mon Dec 11 08:49:35 1995
@@ -15,7 +15,11 @@
     bool '   Use new IDE driver for primary/secondary i/f' CONFIG_BLK_DEV_IDE
   fi
   if [ "$CONFIG_BLK_DEV_IDE" = "y" ]; then
-    bool '   Include support for IDE/ATAPI CDROMs' CONFIG_BLK_DEV_IDECD
+    bool '   Include support for IDE/ATAPI CDROM or TAPE' CONFIG_BLK_DEV_IDEATAPI
+    if [ "$CONFIG_BLK_DEV_IDEATAPI" = "y" ]; then
+      bool '      Include support for IDE/ATAPI CDROM' CONFIG_BLK_DEV_IDECD
+      bool '      Include ALPHA support for IDE/ATAPI TAPE' CONFIG_BLK_DEV_IDETAPE
+    fi
     if [ "$CONFIG_PCI" = "y" ]; then
       bool '   PCI Triton IDE Bus Master DMA support' CONFIG_BLK_DEV_TRITON
     fi
diff -u --recursive --new-file v1.3.45/linux/drivers/block/MAKEDEV.ide linux/drivers/block/MAKEDEV.ide
--- v1.3.45/linux/drivers/block/MAKEDEV.ide	Tue Aug 15 20:39:00 1995
+++ linux/drivers/block/MAKEDEV.ide	Mon Dec 11 08:49:53 1995
@@ -27,3 +27,19 @@
 makedevs hdf 33 64
 makedevs hdg 34 0
 makedevs hdh 34 64
+
+# Create the ide-tape rewinding character device.
+
+rm -f /dev/ht0
+echo mknod /dev/ht0 c 37 0
+     mknod /dev/ht0 c 37 0
+chown root:disk /dev/ht0
+chmod 660 /dev/ht0
+
+# Create the ide-tape non rewinding character device.
+
+rm -f /dev/nht0
+echo mknod /dev/nht0 c 37 128
+     mknod /dev/nht0 c 37 128
+chown root:disk /dev/nht0
+chmod 660 /dev/nht0
diff -u --recursive --new-file v1.3.45/linux/drivers/block/Makefile linux/drivers/block/Makefile
--- v1.3.45/linux/drivers/block/Makefile	Mon Oct 23 18:02:01 1995
+++ linux/drivers/block/Makefile	Mon Dec 11 08:49:35 1995
@@ -44,6 +44,10 @@
 L_OBJS += ide-cd.o
 endif
 
+ifeq ($(CONFIG_BLK_DEV_IDETAPE),y)
+L_OBJS += ide-tape.o
+endif
+
 ifeq ($(CONFIG_BLK_DEV_XD),y)
 L_OBJS += xd.o
 endif
diff -u --recursive --new-file v1.3.45/linux/drivers/block/README.ide linux/drivers/block/README.ide
--- v1.3.45/linux/drivers/block/README.ide	Mon Nov 27 12:48:27 1995
+++ linux/drivers/block/README.ide	Mon Dec 11 08:49:53 1995
@@ -1,4 +1,4 @@
-README.ide -- Information regarding ide.c and ide-cd.c (IDE driver in 1.3.x)
+README.ide -- Information regarding ide.c and ide-cd.c (IDE driver in 1.3.xx)
 ================================================================================
 Supported by:  mlord@bnr.ca           -- disks, interfaces, probing
                snyder@fnald0.fnal.gov -- cdroms, ATAPI, audio
diff -u --recursive --new-file v1.3.45/linux/drivers/block/cmd640.c linux/drivers/block/cmd640.c
--- v1.3.45/linux/drivers/block/cmd640.c	Mon Nov 27 12:48:27 1995
+++ linux/drivers/block/cmd640.c	Mon Dec 11 08:49:53 1995
@@ -1,5 +1,5 @@
 /*
- *  linux/drivers/block/cmd640.c	Version 0.01  Nov 16, 1995
+ *  linux/drivers/block/cmd640.c	Version 0.02  Nov 30, 1995
  *
  *  Copyright (C) 1995  Linus Torvalds & author (see below)
  */
@@ -13,8 +13,56 @@
  *  Version 0.01	Initial version, hacked out of ide.c,
  *			and #include'd rather than compiled separately.
  *			This will get cleaned up in a subsequent release.
+ *
+ *  Version 0.02	Fixes for vlb initialization code, enable
+ *			read-ahead for versions 'B' and 'C' of chip by
+ *			default, some code cleanup.
+ *
  */
 
+/*
+ * CMD640 specific registers definition.
+ */
+
+#define VID		0x00
+#define DID		0x02
+#define PCMD		0x04
+#define PSTTS		0x06
+#define REVID		0x08
+#define PROGIF		0x09
+#define SUBCL		0x0a
+#define BASCL		0x0b
+#define BaseA0		0x10
+#define BaseA1		0x14
+#define BaseA2		0x18
+#define BaseA3		0x1c
+#define INTLINE		0x3c
+#define INPINE		0x3d
+
+#define	CFR		0x50
+#define   CFR_DEVREV		0x03
+#define   CFR_IDE01INTR		0x04
+#define	  CFR_DEVID		0x18
+#define	  CFR_AT_VESA_078h	0x20
+#define	  CFR_DSA1		0x40
+#define	  CFR_DSA0		0x80
+
+#define CNTRL		0x51
+#define	  CNTRL_DIS_RA0		0x40
+#define   CNTRL_DIS_RA1		0x80
+#define	  CNTRL_ENA_2ND		0x08
+
+#define	CMDTIM		0x52
+#define	ARTTIM0		0x53
+#define	DRWTIM0		0x54
+#define ARTTIM1 	0x55
+#define DRWTIM1		0x56
+#define ARTTIM23	0x57
+#define   DIS_RA2		0x04
+#define   DIS_RA3		0x08
+#define DRWTIM23	0x58
+#define BRST		0x59
+
 /* Interface to access cmd640x registers */
 static void (*put_cmd640_reg)(int key, int reg_no, int val);
 static byte (*get_cmd640_reg)(int key, int reg_no);
@@ -24,6 +72,7 @@
 static int	cmd640_chip_version;
 static int	cmd640_key;
 static byte	is_cmd640[MAX_HWIFS];
+static int 	bus_speed; /* MHz */
 
 /*
  * For some unknown reasons pcibios functions which read and write registers
@@ -51,7 +100,7 @@
 	save_flags(flags);
 	cli();
 	outl_p((reg_no & 0xfc) | key, 0xcf8);
-	b = inb(0xcfc + (reg_no & 3));
+	b = inb_p(0xcfc + (reg_no & 3));
 	restore_flags(flags);
 	return b;
 }
@@ -78,7 +127,7 @@
 	save_flags(flags);
 	cli();
 	outb_p(0x10, 0xcf8);
-	b = inb(key + reg_no);
+	b = inb_p(key + reg_no);
 	outb_p(0, 0xcf8);
 	restore_flags(flags);
 	return b;
@@ -92,8 +141,8 @@
 
 	save_flags(flags);
 	cli();
-	outb(reg_no, key + 8);
-	outb(val, key + 0xc);
+	outb_p(reg_no, key + 8);
+	outb_p(val, key + 0xc);
 	restore_flags(flags);
 }
 
@@ -104,8 +153,8 @@
 
 	save_flags(flags);
 	cli();
-	outb(reg_no, key + 8);
-	b = inb(key + 0xc);
+	outb_p(reg_no, key + 8);
+	b = inb_p(key + 0xc);
 	restore_flags(flags);
 	return b;
 }
@@ -164,12 +213,12 @@
 static int probe_for_cmd640_vlb(void) {
 	byte b;
 
-	outb(0x50, 0x178);
+	outb(CFR, 0x178);
 	b = inb(0x17c);
-	if (b == 0xff || b == 0 || (b & 0x20)) {
-		outb(0x50, 0xc78);
+	if (b == 0xff || b == 0 || (b & CFR_AT_VESA_078h)) {
+		outb(CFR, 0x78);
 		b = inb(0x7c);
-		if (b == 0xff || b == 0 || !(b & 0x20))
+		if (b == 0xff || b == 0 || !(b & CFR_AT_VESA_078h))
 			return 0;
 		cmd640_key = 0x70;
 	} else {
@@ -186,7 +235,10 @@
 
 int ide_probe_for_cmd640x(void)
 {
-	int i;
+	int  i;
+	int  second_port;
+	int  read_ahead;
+	byte b;
 
 	for (i = 0; i < MAX_HWIFS; i++)
 		is_cmd640[i] = 0;
@@ -217,19 +269,60 @@
 	 * Documented magic.
 	 */
 
-	cmd640_chip_version = get_cmd640_reg(cmd640_key, 0x50) & 3;
+	cmd640_chip_version = get_cmd640_reg(cmd640_key, CFR) & CFR_DEVREV;
 	if (cmd640_chip_version == 0) {
 		printk ("ide: wrong CMD640 version -- 0\n");
 		return 0;
 	}
 
-	put_cmd640_reg(cmd640_key, 0x51, get_cmd640_reg(cmd640_key, 0x51) | 0xc8);
-	put_cmd640_reg(cmd640_key, 0x57, 0);
-	put_cmd640_reg(cmd640_key, 0x57, get_cmd640_reg(cmd640_key, 0x57) | 0x0c);
+	/*
+	 * Do not initialize secondary controller for vlbus
+	 */
+	second_port = (bus_type != vlb);
+
+	/*
+	 * Set the maximum allowed bus speed (it is safest until we
+	 * 				      find how detect bus speed)
+	 * Normally PCI bus runs at 33MHz, but often works overclocked to 40
+	 */
+	bus_speed = (bus_type == vlb) ? 50 : 40; 
+
+#if 0	/* don't know if this is reliable yet */
+	/*
+	 * Enable readahead for versions above 'A'
+	 */
+	read_ahead = (cmd640_chip_version > 1);
+#else
+	read_ahead = 0;
+#endif
+	/*
+	 * Setup Control Register
+	 */
+	b = get_cmd640_reg(cmd640_key, CNTRL);	
+	if (second_port)
+		b |= CNTRL_ENA_2ND;
+	else
+		b &= ~CNTRL_ENA_2ND;
+	if (read_ahead)
+		b &= ~(CNTRL_DIS_RA0 | CNTRL_DIS_RA1);
+	else
+		b |= (CNTRL_DIS_RA0 | CNTRL_DIS_RA1);
+	put_cmd640_reg(cmd640_key, CNTRL, b);
+
+	/*
+	 * Initialize 2nd IDE port, if required
+	 */
+	if (second_port) {
+		/* We reset timings, and setup read-ahead */
+		b = read_ahead ? 0 : (DIS_RA2 | DIS_RA3);
+		put_cmd640_reg(cmd640_key, ARTTIM23, b);
+		put_cmd640_reg(cmd640_key, DRWTIM23, 0);
+	}
 
 	serialized = 1;
 
-	printk("ide: buggy CMD640 interface at ");
+	printk("ide: buggy CMD640%c interface at ", 
+	       'A' - 1 + cmd640_chip_version);
 	switch (bus_type) {
 		case vlb :
 			printk("local bus, port 0x%x", cmd640_key);
@@ -247,17 +340,17 @@
 	/*
 	 * Reset interface timings
 	 */
+	put_cmd640_reg(cmd640_key, CMDTIM, 0);
 
-	put_cmd640_reg(cmd640_key, 0x58, 0);
-	put_cmd640_reg(cmd640_key, 0x52, 0);
-
-	printk("\n ... serialized, disabled read-ahead, secondary interface enabled\n");
+	printk("\n ... serialized, %s read-ahead, secondary interface %s\n",
+	       read_ahead ? "enabled" : "disabled",
+	       second_port ? "enabled" : "disabled");
 
 	return 1;
 }
 
 static int as_clocks(int a) {
-	switch (a & 0xf0) {
+	switch (a & 0xc0) {
 		case 0 :	return 4;
 		case 0x40 :	return 2;
 		case 0x80 :	return 3;
@@ -274,42 +367,39 @@
 	int  b_reg;
 	byte b;
 	int  r52;
+	static int a = 0;
 
-	b_reg = if_num ? 0x57 : dr_num ? 0x55 : 0x53;
+	b_reg = if_num ? ARTTIM23 : dr_num ? ARTTIM1 : ARTTIM0;
 
 	if (if_num == 0) {
 		put_cmd640_reg(cmd640_key, b_reg, r1);
 		put_cmd640_reg(cmd640_key, b_reg + 1, r2);
 	} else {
 		b = get_cmd640_reg(cmd640_key, b_reg);
-		if ((b&1) == 0) {
-			put_cmd640_reg(cmd640_key, b_reg, r1);
-		} else {
-			if (as_clocks(b) < as_clocks(r1))
-				put_cmd640_reg(cmd640_key, b_reg, r1);
-		}
-		b = get_cmd640_reg(cmd640_key, b_reg + 1);
-		if (b == 0) {
+		if (a == 0 || as_clocks(b) < as_clocks(r1))
+			put_cmd640_reg(cmd640_key, b_reg, (b & 0xc0) | r1);
+		
+		if (a == 0) {
 			put_cmd640_reg(cmd640_key, b_reg + 1, r2);
 		} else {
-			r52 = (b&0xf) < (r2&0xf) ? (r2&0xf) : (b&0xf);
+			b = get_cmd640_reg(cmd640_key, b_reg + 1);
+			r52 =  (b&0x0f) < (r2&0x0f) ? (r2&0x0f) : (b&0x0f);
 			r52 |= (b&0xf0) < (r2&0xf0) ? (r2&0xf0) : (b&0xf0);
 			put_cmd640_reg(cmd640_key, b_reg+1, r52);
 		}
+		a = 1;
 	}
 
-	b = get_cmd640_reg(cmd640_key, 0x52);
+	b = get_cmd640_reg(cmd640_key, CMDTIM);
 	if (b == 0) {
-		put_cmd640_reg(cmd640_key, 0x52, r2);
+		put_cmd640_reg(cmd640_key, CMDTIM, r2);
 	} else {
-		r52 = (b&0xf) < (r2&0xf) ? (r2&0xf) : (b&0xf);
+		r52  = (b&0x0f) < (r2&0x0f) ? (r2&0x0f) : (b&0x0f);
 		r52 |= (b&0xf0) < (r2&0xf0) ? (r2&0xf0) : (b&0xf0);
-		put_cmd640_reg(cmd640_key, 0x52, r52);
+		put_cmd640_reg(cmd640_key, CMDTIM, r52);
 	}
 }
 
-static int bus_speed = 33; /* MHz */
-
 struct pio_timing {
 	int	mc_time;	/* Minimal cycle time (ns) */
 	int	av_time;	/* Address valid to DIOR-/DIOW- setup (ns) */
@@ -385,10 +475,10 @@
 	int i;
 
 	p_base = if_num ? 0x170 : 0x1f0;
-	outb(3, p_base + 1);
-	outb(mode_num | 8, p_base + 2);
-	outb((drv_num | 0xa) << 4, p_base + 6);
-	outb(0xef, p_base + 7);
+	outb_p(3, p_base + 1);
+	outb_p(mode_num | 8, p_base + 2);
+	outb_p((drv_num | 0xa) << 4, p_base + 6);
+	outb_p(0xef, p_base + 7);
 	for (i = 0; (i < 100) && (inb (p_base + 7) & 0x80); i++)
 		delay_10ms();
 }
@@ -442,3 +532,4 @@
 	cmd640_set_timing(interface_number, drive_number, r1, r2);
 	printk ("Mode and Timing set to PIO%d (0x%x 0x%x)\n", max_pio, r1, r2);
 }
+
diff -u --recursive --new-file v1.3.45/linux/drivers/block/ide-cd.c linux/drivers/block/ide-cd.c
--- v1.3.45/linux/drivers/block/ide-cd.c	Sat Nov 25 19:04:39 1995
+++ linux/drivers/block/ide-cd.c	Mon Dec 11 08:49:35 1995
@@ -72,6 +72,7 @@
  * 3.03  Oct 27, 1995 -- Some Creative drives have an id of just `CD'.
  *                       `DCI-2S10' drives are broken too.
  * 3.04  Nov 20, 1995 -- So are Vertros drives.
+ * 3.05  Dec  1, 1995 -- Changes to go with overhaul of ide.c and ide-tape.c
  *
  * NOTE: Direct audio reads will only work on some types of drive.
  * So far, i've received reports of success for Sony and Toshiba drives.
@@ -202,7 +203,7 @@
   ide_input_data (drive, buffer, bytecount / 4);
   if ((bytecount & 0x03) >= 2)
     {
-      insw (IDE_DATA_REG, buffer + (bytecount & ~0x03), 1);
+      insw (IDE_DATA_REG, ((byte *)buffer) + (bytecount & ~0x03), 1);
     }
 }
 
@@ -214,7 +215,7 @@
   ide_output_data (drive, buffer, bytecount / 4);
   if ((bytecount & 0x03) >= 2)
     {
-      outsw (IDE_DATA_REG, buffer + (bytecount & ~0x03), 1);
+      outsw (IDE_DATA_REG, ((byte *)buffer) + (bytecount & ~0x03), 1);
     }
 }
 
@@ -501,27 +502,8 @@
 {
   struct request *rq;
   struct packet_command *pc;
-  unsigned long flags;
   int len;
 
-  int major = HWIF(drive)->major;
-
-  save_flags (flags);
-  cli ();  /* safety */
-
-  rq = HWGROUP(drive)->rq;
-
-  /* If we're processing a request, put it back on the request queue. */
-  if (rq != NULL)
-    {
-      restore_request (rq);
-      rq->next = blk_dev[major].current_request;
-      blk_dev[major].current_request = rq;
-      HWGROUP(drive)->rq = NULL;
-    }
-
-  restore_flags (flags);
-
   /* If the request didn't explicitly specify where to put the sense data,
      use the statically allocated structure. */
   if (reqbuf == NULL)
@@ -544,29 +526,15 @@
   pc->buffer = (char *)reqbuf;
   pc->buflen = len;
   pc->sense_data = (struct atapi_request_sense *)failed_command;
-  
+
+  /* stuff the sense request in front of our current request */
+
   rq = &HWIF(drive)->request_sense_request;
-  rq->rq_status = RQ_ACTIVE;
-  rq->rq_dev = MKDEV (major, (drive->select.b.unit) << PARTN_BITS);
+  ide_init_drive_cmd (rq);
   rq->cmd = REQUEST_SENSE_COMMAND;
-  rq->errors = 0;
-  rq->sector = 0;
-  rq->nr_sectors = 0;
-  rq->current_nr_sectors = 0;
   rq->buffer = (char *)pc;
   rq->sem = sem;
-  rq->bh = NULL;
-  rq->bhtail = NULL;
-  rq->next = NULL;
-
-  save_flags (flags);
-  cli ();  /* safety */
-
-  /* Stick it onto the front of the queue. */
-  rq->next = blk_dev[major].current_request;
-  blk_dev[major].current_request = rq;
-
-  restore_flags (flags);
+  (void) ide_do_drive_cmd (drive, rq, ide_preempt);
 }
 
 
@@ -638,8 +606,8 @@
 	  struct packet_command *pc = (struct packet_command *)rq->buffer;
 	  pc->stat = 1;
 	  cdrom_end_request (1, drive);
-	  if (ide_error (drive, "request sense failure", stat))
-	    return 1;
+	  ide_error (drive, "request sense failure", stat);
+	  return 1;
 	}
 
       else if (cmd == PACKET_COMMAND)
@@ -734,8 +702,8 @@
 	  /* If there were other errors, go to the default handler. */
 	  else if ((err & ~ABRT_ERR) != 0)
 	    {
-	      if (ide_error (drive, "cdrom_decode_status", stat))
-		return 1;
+	      ide_error (drive, "cdrom_decode_status", stat);
+	      return 1;
 	    }
 
 	  /* Else, abort if we've racked up too many retries. */
@@ -752,7 +720,6 @@
     }
 
   /* Retry, or handle the next request. */
-  IDE_DO_REQUEST;
   return 1;
 }
 
@@ -781,7 +748,7 @@
 
   if (CDROM_CONFIG_FLAGS (drive)->drq_interrupt)
     {
-      ide_set_handler (drive, handler);
+      ide_set_handler (drive, handler, WAIT_CMD);
       OUT_BYTE (WIN_PACKETCMD, IDE_COMMAND_REG); /* packet command */
     }
   else
@@ -819,7 +786,7 @@
     }
 
   /* Arm the interrupt handler. */
-  ide_set_handler (drive, handler);
+  ide_set_handler (drive, handler, WAIT_CMD);
 
   /* Send the command to the device. */
   cdrom_out_bytes (drive, cmd_buf, cmd_len);
@@ -925,7 +892,6 @@
     }
 
   cdrom_end_request (0, drive);
-  IDE_DO_REQUEST;
   return -1;
 }
 
@@ -961,7 +927,6 @@
       else
         cdrom_end_request (1, drive);
 
-      IDE_DO_REQUEST;
       return;
     }
 
@@ -976,7 +941,6 @@
               drive->name, len);
       printk ("  This drive is not supported by this version of the driver\n");
       cdrom_end_request (0, drive);
-      IDE_DO_REQUEST;
       return;
     }
 
@@ -1040,7 +1004,7 @@
 
   /* Done moving data!
      Wait for another interrupt. */
-  ide_set_handler (drive, &cdrom_read_intr);
+  ide_set_handler (drive, &cdrom_read_intr, WAIT_CMD);
 }
 
 
@@ -1138,7 +1102,6 @@
           printk ("%s: cdrom_start_read_continuation: buffer botch (%ld)\n",
                   drive->name, rq->current_nr_sectors);
           cdrom_end_request (0, drive);
-          IDE_DO_REQUEST;
           return;
         }
 
@@ -1252,7 +1215,6 @@
           pc->stat = 1;
           cdrom_end_request (1, drive);
         }
-      IDE_DO_REQUEST;
       return;
     }
 
@@ -1327,7 +1289,7 @@
     }
 
   /* Now we wait for another interrupt. */
-  ide_set_handler (drive, &cdrom_pc_intr);
+  ide_set_handler (drive, &cdrom_pc_intr, WAIT_CMD);
 }
 
 
@@ -1367,40 +1329,6 @@
   schedule ();
 }
 
-
-static
-void cdrom_queue_request (ide_drive_t *drive, struct request *req)
-{
-  unsigned long flags;
-  struct request **p, **pfirst;
-  int major = HWIF(drive)->major;
-  struct semaphore sem = MUTEX_LOCKED;
-
-  req->rq_dev = MKDEV (major, (drive->select.b.unit) << PARTN_BITS);
-  req->rq_status = RQ_ACTIVE;
-  req->sem = &sem;
-  req->errors = 0;
-  req->next = NULL;
-
-  save_flags (flags);
-  cli ();
-
-  p = &blk_dev[major].current_request;
-  pfirst = p;
-  while ((*p) != NULL)
-    {
-      p = &((*p)->next);
-    }
-  *p = req;
-  if (p == pfirst)
-    blk_dev[major].request_fn ();
-
-  down (&sem);
-
-  restore_flags (flags);
-}
- 
- 
 static
 int cdrom_queue_packet_command (ide_drive_t *drive, struct packet_command *pc)
 {
@@ -1416,15 +1344,10 @@
 
   /* Start of retry loop. */
   do {
+    ide_init_drive_cmd (&req);
     req.cmd = PACKET_COMMAND;
-    req.sector = 0;
-    req.nr_sectors = 0;
-    req.current_nr_sectors = 0;
     req.buffer = (char *)pc;
-    req.bh = NULL;
-    req.bhtail = NULL;
-
-    cdrom_queue_request (drive, &req);
+    (void) ide_do_drive_cmd (drive, &req, ide_wait);
 
     if (pc->stat != 0)
       {
@@ -1491,8 +1414,8 @@
   else if (rq -> cmd == RESET_DRIVE_COMMAND)
     {
       cdrom_end_request (1, drive);
-      if (ide_do_reset (drive))
-	return;
+      ide_do_reset (drive);
+      return;
     }
 
   else if (rq -> cmd != READ)
@@ -2434,10 +2357,9 @@
     case CDROMRESET:
       {
 	struct request req;
-	memset (&req, 0, sizeof (req));
+	ide_init_drive_cmd (&req);
 	req.cmd = RESET_DRIVE_COMMAND;
-	cdrom_queue_request (drive, &req);
-	return 0;
+	return ide_do_drive_cmd (drive, &req, ide_wait);
       }
 #endif
 
diff -u --recursive --new-file v1.3.45/linux/drivers/block/ide-tape.c linux/drivers/block/ide-tape.c
--- v1.3.45/linux/drivers/block/ide-tape.c	Thu Jan  1 02:00:00 1970
+++ linux/drivers/block/ide-tape.c	Mon Dec 11 10:15:05 1995
@@ -0,0 +1,3313 @@
+/*
+ * linux/drivers/block/ide-tape.c	Version 1.0 - ALPHA	Dec  3, 1995
+ *
+ * Copyright (C) 1995 Gadi Oxman <tgud@tochnapc2.technion.ac.il>
+ *
+ * This driver was constructed as a student project in the software laboratory
+ * of the faculty of electrical engineering in the Technion - Israel's
+ * Institute Of Technology, with the guide of Avner Lottem and Dr. Ilana David.
+ *
+ * It is hereby placed under the terms of the GNU general public license.
+ * (See linux/COPYING).
+ */
+ 
+/*
+ * IDE ATAPI streaming tape driver.
+ *
+ * This driver is a part of the Linux ide driver and works in co-operation
+ * with linux/drivers/block/ide.c.
+ *
+ * This driver provides both a block device and a character device interface to
+ * the tape. The driver, in co-operation with ide.c, basically traverses the
+ * request-list for the block device interface. The character device interface,
+ * on the other hand, creates new requests, adds them to the request-list
+ * of the block device, and waits for their completion.
+ *
+ * The block device major and minor numbers are determined from the
+ * tape relative position in the ide interfaces, as explained in ide.c.
+ *
+ * The character device interface consists of two devices:
+ *
+ * ht0		major=37,minor=0	first IDE tape, rewind on close.
+ * nht0		major=37,minor=128	first IDE tape, no rewind on close.
+ *
+ * Run /usr/src/linux/drivers/block/MAKEDEV.ide to create the above entries.
+ * We currently support only one ide tape drive.
+ *
+ * Although we do support requests which originate from the buffer cache to
+ * some extent, it is recommended to use the character device interface when
+ * performing a long read or write operation (relative to the amount of free
+ * memory in your system). Otherwise, free memory will be used to cache tape
+ * blocks, those cached blocks won't be used, Linux's responsiveness will
+ * suffer as we start to swap.
+ *
+ * The general magnetic tape commands compatible interface, as defined by
+ * include/linux/mtio.h, is accessible through the character device.
+ * Our own ide-tape ioctl's can can be issued to either the block device or
+ * the character device.
+ *
+ * Opening the block device interface will be refused by default.
+ *
+ * Testing was done with a 2 GB CONNER CTMA 4000 IDE ATAPI Streaming Tape Drive.
+ *
+ * Ver 0.1   Nov  1 95   Pre-working code :-)
+ * Ver 0.2   Nov 23 95   A short backup (few megabytes) and restore procedure
+ *                        was successful ! (Using tar cvf ... on the block
+ *                        device interface).
+ *                       A longer backup resulted in major swapping, bad
+ *                        overall Linux performance and eventually failed as
+ *                        we received non serial read-ahead requests from the
+ *                        buffer cache.
+ * Ver 0.3   Nov 28 95   Long backups are now possible, thanks to the
+ *                        character device interface. Linux's responsiveness
+ *                        and performance doesn't seem to be much affected
+ *                        from the background backup procedure.
+ *                       Some general mtio.h magnetic tape operations are
+ *                        now supported by our character device. As a result,
+ *                        popular tape utilities are starting to work with
+ *                        ide tapes :-)
+ *                       The following configurations were tested:
+ *                       	1. An IDE ATAPI TAPE shares the same interface
+ *                       	   and irq with an IDE ATAPI CDROM.
+ *                        	2. An IDE ATAPI TAPE shares the same interface
+ *                          	   and irq with a normal IDE disk.
+ *                        Both configurations seemed to work just fine !
+ *                        However, to be on the safe side, it is meanwhile
+ *                        recommended to give the IDE TAPE its own interface
+ *                        and irq.
+ *                       The one thing which needs to be done here is to
+ *                        add a "request postpone" feature to ide.c,
+ *                        so that we won't have to wait for the tape to finish
+ *                        performing a long media access (DSC) request (such
+ *                        as a rewind) before we can access the other device
+ *                        on the same interface. This effect doesn't disturb
+ *                        normal operation most of the time because read/write
+ *                        requests are relatively fast, and once we are
+ *                        performing one tape r/w request, a lot of requests
+ *                        from the other device can be queued and ide.c will
+ *			  service all of them after this single tape request.
+ * Ver 1.0   ???         Integrated into Linux 1.3.??? development tree.
+ *                       On each read / write request, we now ask the drive
+ *                        if we can transfer a constant number of bytes
+ *                        (a parameter of the drive) only to its buffers,
+ *                        without causing actual media access. If we can't,
+ *                        we just wait until we can by polling the DSC bit.
+ *                        This ensures that while we are not transferring
+ *                        more bytes than the constant reffered to above, the
+ *                        interrupt latency will not become too high and
+ *                        we won't cause an interrupt timeout, as happened
+ *                        occasionally in the previous version.
+ *                       While polling for DSC, the current request is
+ *                        postponed and ide.c is free to handle requests from
+ *                        the other device. This is handled transparently to
+ *                        ide.c. The hwgroup locking method which was used
+ *                        in the previous version was removed.
+ *                       Use of new general features which are provided by
+ *                        ide.c for use with atapi devices.
+ *                        (Programming done by Mark Lord)
+ *                       Few potential bug fixes (Again, suggested by Mark)
+ *                       Single character device data transfers are now
+ *                        not limited in size, as they were before.
+ *                       We are asking the tape about its recommended
+ *                        transfer unit and send a larger data transfer
+ *                        as several transfers of the above size.
+ *                        For best results, use an integral number of this
+ *                        basic unit (which is shown during driver
+ *                        initialization). I will soon add an ioctl to get
+ *                        this important parameter.
+ *                       Our data transfer buffer is allocated on startup,
+ *                        rather than before each data transfer. This should
+ *                        ensure that we will indeed have a data buffer.
+ *
+ * We are currently in an *alpha* stage. The driver is not complete and not
+ * much tested. I would strongly suggest to:
+ *
+ *	1. Connect the tape to a separate interface and irq.
+ *	2. Be truly prepared for a kernel crash and the resulting data loss.
+ *	3. Don't rely too much on the resulting backups.
+ *
+ * Other than that, enjoy !
+ *
+ * Here are some words from the first releases of hd.c, which are quoted
+ * in ide.c and apply here as well:
+ *
+ * | Special care is recommended.  Have Fun!
+ *
+ */
+
+#include <linux/hdreg.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/mm.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <linux/major.h>
+#include <linux/blkdev.h>
+#include <linux/errno.h>
+#include <linux/hdreg.h>
+#include <linux/genhd.h>
+#include <linux/malloc.h>
+
+#include <asm/byteorder.h>
+#include <asm/irq.h>
+#include <asm/segment.h>
+#include <asm/io.h>
+
+#define _IDE_TAPE_C			/* For ide_end_request in blk.h */
+
+/*
+ *	Main Linux ide driver include file
+ *
+ *	Automatically includes our first include file - ide-tape1.h.
+ */
+ 
+#include "ide.h"		
+
+/*
+ *	Supported ATAPI tape drives packet commands
+ */
+
+#define	IDETAPE_TEST_UNIT_READY_CMD	0x00
+#define	IDETAPE_REWIND_CMD		0x01
+#define	IDETAPE_REQUEST_SENSE_CMD	0x03
+#define	IDETAPE_READ_CMD		0x08
+#define	IDETAPE_WRITE_CMD		0x0a
+#define	IDETAPE_WRITE_FILEMARK_CMD	0x10
+#define	IDETAPE_SPACE_CMD		0x11
+#define	IDETAPE_INQUIRY_CMD		0x12
+#define	IDETAPE_ERASE_CMD		0x19
+#define	IDETAPE_MODE_SENSE_CMD		0x1a
+#define	IDETAPE_LOCATE_CMD		0x2b
+#define	IDETAPE_READ_POSITION_CMD	0x34
+
+/*
+ *	Some defines for the SPACE command
+ *
+ *	(The code field in the SPACE packet command).
+ */
+ 
+#define	IDETAPE_SPACE_OVER_FILEMARK	1
+#define	IDETAPE_SPACE_TO_EOD		3
+
+/*
+ *	Our ioctls - We will use 0x034n and 0x035n
+ *
+ *	Nothing special meanwhile.
+ *	mtio.h MTIOCTOP compatible commands are supported on the character
+ *	device interface.
+ */
+
+#define IDETAPE_INQUIRY_IOCTL		0x0341
+#define	IDETAPE_LOCATE_IOCTL		0x0342
+
+#define IDETAPE_RESET_IOCTL		0x0350
+
+/*
+ *	Special requests for our block device strategy routine.
+ *
+ *	In order to service a character device command, we add special
+ *	requests to the tail of our block device request queue and wait
+ *	for their completion.
+ *
+ */
+
+/*
+ * 	IDETAPE_PACKET_COMMAND_REQUEST_TYPE1 is used to queue a packet command
+ *	in the request queue. We will wait for DSC before issuing the command
+ *	if it is still not set. In that case, we will temporary replace the
+ *	cmd field to type 2 and restore it back to type 1 when we receive DSC
+ *	and can start with sending the command.
+ */
+ 
+#define	IDETAPE_PACKET_COMMAND_REQUEST_TYPE1	90
+#define	IDETAPE_PACKET_COMMAND_REQUEST_TYPE2	91
+
+/*
+ *	IDETAPE_READ_REQUEST and IDETAPE_WRITE_REQUEST are used by our
+ *	character device interface to request read/write operations from
+ *	our block device interface.
+ *
+ *	In case a read or write request was requested by the buffer cache
+ *	and not by our character device interface, the cmd field in the
+ *	request will contain READ and WRITE instead.
+ *
+ *	We handle both cases in a similar way. The main difference is that
+ *	in our own requests, buffer head is NULL and idetape_end_request
+ *	will update the errors field if the request was not completed.
+ */
+
+#define	IDETAPE_READ_REQUEST			92
+#define	IDETAPE_WRITE_REQUEST			93
+
+/*
+ *	We are now able to postpone an idetape request in the stage
+ *	where it is polling for DSC and service requests from the other
+ *	ide device meanwhile.
+ */
+
+#define	IDETAPE_RQ_POSTPONED		0x1234
+
+/*
+ *	ATAPI Task File Registers (Re-definition of the ATA Task File
+ *	Registers for an ATAPI packet command).
+ * 	From Table 3-2 of QIC-157C.
+ */
+
+/* Read Access */
+
+#define	IDETAPE_DATA_OFFSET		(0)
+#define IDETAPE_ERROR_OFFSET		(1)
+#define	IDETAPE_IREASON_OFFSET		(2)
+#define IDETAPE_RESERVED3_OFFSET	(3)
+#define IDETAPE_BCOUNTL_OFFSET		(4)
+#define	IDETAPE_BCOUNTH_OFFSET		(5)
+#define IDETAPE_DRIVESEL_OFFSET		(6)
+#define	IDETAPE_STATUS_OFFSET		(7)
+
+#define	IDETAPE_DATA_REG		(HWIF(drive)->io_base+IDETAPE_DATA_OFFSET)
+#define IDETAPE_ERROR_REG		(HWIF(drive)->io_base+IDETAPE_ERROR_OFFSET)
+#define	IDETAPE_IREASON_REG		(HWIF(drive)->io_base+IDETAPE_IREASON_OFFSET)
+#define IDETAPE_RESERVED3_REG		(HWIF(drive)->io_base+IDETAPE_RESERVED3_OFFSET)
+#define IDETAPE_BCOUNTL_REG		(HWIF(drive)->io_base+IDETAPE_BCOUNTL_OFFSET)
+#define	IDETAPE_BCOUNTH_REG		(HWIF(drive)->io_base+IDETAPE_BCOUNTH_OFFSET)
+#define IDETAPE_DRIVESEL_REG		(HWIF(drive)->io_base+IDETAPE_DRIVESEL_OFFSET)
+#define	IDETAPE_STATUS_REG		(HWIF(drive)->io_base+IDETAPE_STATUS_OFFSET)
+
+/* Write Access */
+
+#define	IDETAPE_FEATURES_OFFSET		(1)
+#define IDETAPE_ATACOMMAND_OFFSET	(7)
+
+#define IDETAPE_FEATURES_REG		(HWIF(drive)->io_base+IDETAPE_FEATURES_OFFSET)
+#define IDETAPE_ATACOMMAND_REG		(HWIF(drive)->io_base+IDETAPE_ATACOMMAND_OFFSET)
+#define IDETAPE_CONTROL_REG		(HWIF(drive)->ctl_port)
+
+
+/*
+ *	Structure of the various task file registers
+ */
+
+/*
+ *	The ATAPI Status Register.
+ */
+ 
+typedef union {
+	unsigned all			:8;
+	struct {
+		unsigned check		:1;	/* Error occured */
+		unsigned idx		:1;	/* Reserved */
+		unsigned corr		:1;	/* Correctable error occured */
+		unsigned drq		:1;	/* Data is request by the device */
+		unsigned dsc		:1;	/* Set when a media access command is finished */
+						/* Reads / Writes are NOT media access commands */
+		unsigned reserved5	:1;	/* Reserved */
+		unsigned drdy		:1;	/* Ignored for ATAPI commands */
+						/* (The device is ready to accept ATA command) */
+		unsigned bsy		:1;	/* The device has access to the command block */
+	} b;
+} idetape_status_reg_t;
+
+/*
+ *	The ATAPI error register.
+ */
+ 
+typedef union {
+	unsigned all			:8;
+	struct {
+		unsigned ili		:1;	/* Illegal Length Indication */
+		unsigned eom		:1;	/* End Of Media Detected */
+		unsigned abrt		:1;	/* Aborted command - As defined by ATA */
+		unsigned mcr		:1;	/* Media Change Requested - As defined by ATA */
+		unsigned sense_key	:4;	/* Sense key of the last failed packet command */
+	} b;
+} idetape_error_reg_t;
+
+/*
+ *	ATAPI Feature Register
+ */
+ 
+typedef union {
+	unsigned all			:8;
+	struct {
+		unsigned dma		:1;	/* Using DMA of PIO */
+		unsigned reserved321	:3;	/* Reserved */
+		unsigned reserved654	:3;	/* Reserved (Tag Type) */
+		unsigned reserved7	:1;	/* Reserved */
+	} b;
+} idetape_feature_reg_t;
+
+/*
+ *	ATAPI Byte Count Register.
+ */
+ 
+typedef union {
+	unsigned all			:16;
+	struct {
+		unsigned low		:8;	/* LSB */
+		unsigned high		:8;	/* MSB */
+	} b;
+} idetape_bcount_reg_t;
+
+/*
+ *	ATAPI Interrupt Reason Register.
+ */
+ 
+typedef union {
+	unsigned all			:8;
+	struct {
+		unsigned cod		:1;	/* Information transferred is command (1) or data (0) */
+		unsigned io		:1;	/* The device requests us to read (1) or write (0) */
+		unsigned reserved	:6;	/* Reserved */
+	} b;
+} idetape_ireason_reg_t;
+
+/*
+ *	ATAPI Drive Select Register
+ */
+ 
+typedef union {	
+	unsigned all			:8;
+	struct {
+		unsigned sam_lun	:4;	/* Should be zero with ATAPI (not used) */
+		unsigned drv		:1;	/* The responding drive will be drive 0 (0) or drive 1 (1) */
+		unsigned one5		:1;	/* Should be set to 1 */
+		unsigned reserved6	:1;	/* Reserved */
+		unsigned one7		:1;	/* Should be set to 1 */
+	} b;
+} idetape_drivesel_reg_t;
+
+/*
+ *	ATAPI Device Control Register
+ */
+ 
+typedef union {			
+	unsigned all			:8;
+	struct {
+		unsigned zero0		:1;	/* Should be set to zero */
+		unsigned nien		:1;	/* Device interrupt is disabled (1) or enabled (0) */
+		unsigned srst		:1;	/* ATA software reset. ATAPI devices should use the new ATAPI srst. */
+		unsigned one3		:1;	/* Should be set to 1 */
+		unsigned reserved4567	:4;	/* Reserved */
+	} b;
+} idetape_control_reg_t;
+
+/*
+ *	idetape_chrdev_t provides the link between out character device
+ *	interface and our block device interface and the corresponding
+ *	ide_drive_t structure.
+ *
+ *	We currently support only one tape drive.
+ * 
+ */
+ 
+typedef struct {
+	ide_drive_t *drive;
+	int major,minor;
+	char name[4];
+} idetape_chrdev_t;
+
+/*
+ *	The following is used to format the general configuration word of
+ *	the ATAPI IDENTIFY DEVICE command.
+ */
+
+struct idetape_id_gcw {	
+
+	unsigned packet_size	:2;	/* Packet Size */
+	unsigned reserved2	:1;	/* Reserved */
+	unsigned reserved3	:1;	/* Reserved */
+	unsigned reserved4	:1;	/* Reserved */
+	unsigned drq_type	:2;	/* Command packet DRQ type */
+	unsigned removable	:1;	/* Removable media */
+	unsigned device_type	:5;	/* Device type */
+	unsigned reserved13	:1;	/* Reserved */
+	unsigned protocol	:2;	/* Protocol type */
+};
+
+/*
+ *	INQUIRY packet command - Data Format (From Table 6-8 of QIC-157C)
+ */
+ 
+typedef struct {
+	unsigned device_type	:5;	/* Peripheral Device Type */
+	unsigned reserved0_765	:3;	/* Peripheral Qualifier - Reserved */
+	unsigned reserved1_6t0	:7;	/* Reserved */
+	unsigned rmb		:1;	/* Removable Medium Bit */
+	unsigned ansi_version	:3;	/* ANSI Version */
+	unsigned ecma_version	:3;	/* ECMA Version */
+	unsigned iso_version	:2;	/* ISO Version */
+	unsigned response_format :4;	/* Response Data Format */
+	unsigned reserved3_45	:2;	/* Reserved */
+	unsigned reserved3_6	:1;	/* TrmIOP - Reserved */
+	unsigned reserved3_7	:1;	/* AENC - Reserved */
+	byte additional_length;		/* Additional Length (total_length-4) */
+	byte reserved_5;		/* Reserved */
+	byte reserved_6;		/* Reserved */
+	unsigned reserved7_0	:1;	/* SftRe - Reserved */
+	unsigned reserved7_1	:1;	/* CmdQue - Reserved */
+	unsigned reserved7_2	:1;	/* Reserved */
+	unsigned reserved7_3	:1;	/* Linked - Reserved */
+	unsigned reserved7_4	:1;	/* Sync - Reserved */
+	unsigned reserved7_5	:1;	/* WBus16 - Reserved */
+	unsigned reserved7_6	:1;	/* WBus32 - Reserved */
+	unsigned reserved7_7	:1;	/* RelAdr - Reserved */
+	byte vendor_id [8];		/* Vendor Identification */
+	byte product_id [16];		/* Product Identification */
+	byte revision_level [4];	/* Revision Level */
+	byte vendor_specific [20];	/* Vendor Specific - Optional */
+	byte reserved56t95 [40];	/* Reserved - Optional */
+	
+					/* Additional information may be returned */
+} idetape_inquiry_result_t;
+
+/*
+ *	READ POSITION packet command - Data Format (From Table 6-57)
+ */
+ 
+typedef struct {
+	unsigned reserved0_10	:2;	/* Reserved */
+	unsigned bpu		:1;	/* Block Position Unknown */	
+	unsigned reserved0_543	:3;	/* Reserved */
+	unsigned eop		:1;	/* End Of Partition */
+	unsigned bop		:1;	/* Begining Of Partition */
+	byte partition_num;		/* Partition Number */
+	byte reserved_2;		/* Reserved */
+	byte reserved_3;		/* Reserved */
+	unsigned long first_block;	/* First Block Location */
+	unsigned long last_block;	/* Last Block Location (Optional) */
+	byte reserved_12;		/* Reserved */
+	byte blocks_in_buffer_2;	/* Blocks In Buffer - MSB (Optional) */
+	byte blocks_in_buffer_1;
+	byte blocks_in_buffer_0;	/* Blocks In Buffer - LSB (Optional) */
+	unsigned long bytes_in_buffer;	/* Bytes In Buffer (Optional) */
+} idetape_read_position_result_t;
+
+/*
+ *	REQUEST SENSE packet command result - Data Format.
+ */
+
+typedef struct {
+	unsigned error_code	:7;	/* Current of deferred errors */
+	unsigned valid		:1;	/* The information field conforms to QIC-157C */
+	byte reserved_1;		/* Segment Number - Reserved */
+	unsigned sense_key	:4;	/* Sense Key */
+	unsigned reserved2_4	:1;	/* Reserved */
+	unsigned ili		:1;	/* Incorrect Length Indicator */
+	unsigned eom		:1;	/* End Of Medium */
+	unsigned filemark	:1;	/* Filemark */
+	unsigned long information;	/* Information - Command specific */
+	byte asl;			/* Additional sense length (n-7) */
+	unsigned long command_specific; /* Additional command specific information */
+	byte asc;			/* Additional Sense Code */
+	byte ascq;			/* Additional Sense Code Qualifier */
+	byte replaceable_unit_code;	/* Field Replaceable Unit Code */
+	unsigned sk_specific1 	:7;	/* Sense Key Specific */
+	unsigned sksv		:1;	/* Sense Key Specific informatio is valid */
+	byte sk_specific2;		/* Sense Key Specific */
+	byte sk_specific3;		/* Sense Key Specific */
+} idetape_request_sense_result_t;
+
+/*
+ *	Follows structures which are realted to the SELECT SENSE / MODE SENSE
+ *	packet commands. Those packet commands are still not supported
+ *	by ide-tape.
+ */
+
+#define	IDETAPE_CAPABILITIES_PAGE	0x2a
+
+/*
+ *	Mode Parameter Header for the MODE SENSE packet command
+ */
+
+typedef struct {
+	byte mode_data_length;		/* The length of the following data that is */
+					/* available to be transferred */
+	byte medium_type;		/* Medium Type */
+	byte dsp;			/* Device Specific Parameter */
+	byte bdl;			/* Block Descriptor Length */
+} idetape_mode_parameter_header_t;
+
+/*
+ *	Mode Parameter Block Descriptor the MODE SENSE packet command
+ *
+ *	Support for block descriptors is optional.
+ */
+
+typedef struct {
+	byte density_code;		/* Medium density code */
+	byte blocks1;			/* Number of blocks - MSB */
+	byte blocks2;			/* Number of blocks - Middle byte */
+	byte blocks3;			/* Number of blocks - LSB */
+	byte reserved4;			/* Reserved */
+	byte length1;			/* Block Length - MSB */
+	byte length2;			/* Block Length - Middle byte */
+	byte length3;			/* Block Length - LSB */
+} idetape_parameter_block_descriptor_t;
+
+/*
+ *	The Data Compression Page, as returned by the MODE SENSE packet command.
+ */
+ 
+typedef struct {
+	unsigned page_code	:6;	/* Page Code - Should be 0xf */
+	unsigned reserved	:1;	/* Reserved */
+	unsigned ps		:1;
+	byte page_length;		/* Page Length - Should be 14 */
+	unsigned reserved2	:6;	/* Reserved */
+	unsigned dcc		:1;	/* Data Compression Capable */
+	unsigned dce		:1;	/* Data Compression Enable */
+	unsigned reserved3	:5;	/* Reserved */
+	unsigned red		:2;	/* Report Exception on Decompression */
+	unsigned dde		:1;	/* Data Decompression Enable */
+	unsigned long ca;		/* Compression Algorithm */
+	unsigned long da;		/* Decompression Algorithm */
+	byte reserved_12;		/* Reserved */
+	byte reserved_13;		/* Reserved */
+	byte reserved_14;		/* Reserved */
+	byte reserved_15;		/* Reserved */
+} idetape_data_compression_page_t;
+
+/*
+ *	The Medium Partition Page, as returned by the MODE SENSE packet command.
+ */
+
+typedef struct {
+	unsigned page_code	:6;	/* Page Code - Should be 0x11 */
+	unsigned reserved1_6	:1;	/* Reserved */
+	unsigned ps		:1;
+	byte page_length;		/* Page Length - Should be 6 */
+	byte map;			/* Maximum Additional Partitions - Should be 0 */
+	byte apd;			/* Additional Partitions Defined - Should be 0 */
+	unsigned reserved4_012	:3;	/* Reserved */
+	unsigned psum		:2;	/* Should be 0 */
+	unsigned idp		:1;	/* Should be 0 */
+	unsigned sdp		:1;	/* Should be 0 */
+	unsigned fdp		:1;	/* Fixed Data Partitions */
+	byte mfr;			/* Medium Format Recognition */
+	byte reserved6;			/* Reserved */
+	byte reserved7;			/* Reserved */
+} idetape_medium_partition_page_t;
+
+/*
+ *	Prototypes of various functions in ide-tape.c
+ *
+ *	The following functions are called from ide.c, and their prototypes
+ *	are available in ide.h:
+ *
+ *		idetape_identify_device
+ *		idetape_setup
+ *		idetape_blkdev_ioctl
+ *		idetape_do_request
+ *		idetape_blkdev_open
+ *		idetape_blkdev_release
+ *		idetape_register_chrdev (void);
+ */
+
+/*
+ *	The following functions are used to transfer data from / to the
+ *	tape's data register.
+ */
+ 
+void idetape_input_data (ide_drive_t *drive,void *buffer, unsigned long bcount);
+void idetape_output_data (ide_drive_t *drive,void *buffer, unsigned long bcount);
+void idetape_discard_data (ide_drive_t *drive, unsigned long bcount);
+
+
+/*
+ *	Packet command related functions.
+ */
+ 
+void idetape_issue_packet_command  (ide_drive_t *drive,idetape_packet_command_t *pc,ide_handler_t *handler);
+void idetape_pc_intr (ide_drive_t *drive);
+
+/*
+ *	DSC handling functions.
+ */
+ 
+void idetape_postpone_request (ide_drive_t *drive);
+void idetape_poll_for_dsc (unsigned long data);
+void idetape_put_back_postponed_request (ide_drive_t *drive);
+void idetape_media_access_finished (ide_drive_t *drive);
+
+/*
+ *	Some more packet command related functions.
+ */
+ 
+void idetape_pc_callback (ide_drive_t *drive);
+void idetape_retry_pc (ide_drive_t *drive);
+void idetape_zero_packet_command (idetape_packet_command_t *pc);
+void idetape_queue_pc_head (ide_drive_t *drive,idetape_packet_command_t *pc,struct request *rq);
+
+idetape_packet_command_t *idetape_next_pc_storage (ide_drive_t *drive);
+struct request *idetape_next_rq_storage (ide_drive_t *drive);
+
+void idetape_end_request (byte uptodate, ide_hwgroup_t *hwgroup);
+
+/*
+ *	Various packet commands
+ */
+ 
+void idetape_create_inquiry_cmd (idetape_packet_command_t *pc);
+void idetape_inquiry_callback (ide_drive_t *drive);
+void idetape_create_locate_cmd (idetape_packet_command_t *pc,unsigned long block,byte partition);
+void idetape_create_rewind_cmd (idetape_packet_command_t *pc);
+void idetape_create_write_filemark_cmd (idetape_packet_command_t *pc,int write_filemark);
+void idetape_create_space_cmd (idetape_packet_command_t *pc,long count,byte cmd);
+void idetape_create_erase_cmd (idetape_packet_command_t *pc);
+void idetape_create_test_unit_ready_cmd (idetape_packet_command_t *pc);
+void idetape_create_read_position_cmd (idetape_packet_command_t *pc);
+void idetape_read_position_callback (ide_drive_t *drive);
+void idetape_create_read_cmd (idetape_packet_command_t *pc,unsigned long length);
+void idetape_read_callback (ide_drive_t *drive);
+void idetape_create_write_cmd (idetape_packet_command_t *pc,unsigned long length);
+void idetape_write_callback (ide_drive_t *drive);
+void idetape_create_request_sense_cmd (idetape_packet_command_t *pc);
+void idetape_create_mode_sense_cmd (idetape_packet_command_t *pc,byte page_code);
+void idetape_request_sense_callback (ide_drive_t *drive);
+
+void idetape_display_inquiry_result (byte *buffer);
+void idetape_analyze_error (ide_drive_t *drive,idetape_request_sense_result_t *result);
+
+/*
+ *	Character device callback functions.
+ *
+ *	We currently support:
+ *
+ *		OPEN, RELEASE, READ, WRITE and IOCTL.
+ */
+
+int idetape_chrdev_read (struct inode *inode, struct file *file, char *buf, int count);
+int idetape_chrdev_read_remainder (struct inode *inode, struct file *file, char *buf, int count);
+int idetape_chrdev_write (struct inode *inode, struct file *file, const char *buf, int count);
+int idetape_chrdev_write_remainder (struct inode *inode, struct file *file, const char *buf, int count);
+int idetape_chrdev_ioctl (struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg);
+int idetape_chrdev_open (struct inode *inode, struct file *file);
+void idetape_chrdev_release (struct inode *inode,struct file *file);
+
+/*
+ *	idetape_mtioctop implements general magnetic tape io control
+ *	commands, as defined in include/linux/mtio.h. Those commands are
+ *	accessed through the character device interface, using the MTIOCTOP
+ *	ioctl.
+ */
+ 
+int idetape_mtioctop (ide_drive_t *drive,short mt_op,int mt_count);
+int idetape_queue_rw_tail (ide_drive_t *drive,int cmd,int blocks,char *buffer);
+int idetape_queue_pc_tail (ide_drive_t *drive,idetape_packet_command_t *pc);
+
+void idetape_fake_read (ide_drive_t *drive);
+int idetape_position_tape (ide_drive_t *drive,unsigned long block);
+int idetape_rewind_tape (ide_drive_t *drive);
+
+/*
+ *	Used to get device information
+ */
+
+void idetape_get_mode_sense_results (ide_drive_t *drive);
+
+/*
+ *	General utility functions
+ */
+ 
+void idetape_fixstring (byte *s, const int bytecount, const int byteswap);
+unsigned long idetape_swap_long (unsigned long temp);
+unsigned short idetape_swap_short (unsigned short temp);
+
+/*
+ *	For general magnetic tape device compatibility.
+ */
+ 
+#include <linux/mtio.h>
+
+/*
+ *	Global variables
+ *
+ *	The variables below are used for the character device interface.
+ *
+ *	Additional state variables are defined in our ide_drive_t structure.
+ */
+ 
+idetape_chrdev_t idetape_chrdev;		/* Character device interface information */
+byte idetape_drive_already_found=0;		/* 1 when the above data structure is initialized */
+
+/*
+ *	Our character device supporting functions, passed to register_chrdev.
+ */
+ 
+static struct file_operations idetape_fops = {
+	NULL,			/* lseek - default */
+	idetape_chrdev_read,	/* read  */
+	idetape_chrdev_write,	/* write */
+	NULL,			/* readdir - bad */
+	NULL,			/* select */
+	idetape_chrdev_ioctl,	/* ioctl */
+	NULL,			/* mmap */
+	idetape_chrdev_open,	/* open */
+	idetape_chrdev_release,	/* release */
+	NULL,			/* fsync */
+	NULL,			/* fasync */
+	NULL,			/* check_media_change */
+	NULL			/* revalidate */
+};
+
+
+/*
+ *	idetape_identify_device is called by do_identify in ide.c during
+ *	the device probing stage to check the contents of the ATAPI IDENTIFY
+ *	command results, in case the device type is tape. We return:
+ *
+ *	1	If the tape can be supported by us, based on the information
+ *		we have so far.
+ *
+ *	0 	If this tape driver is not currently supported by us.
+ *
+ *	In case we decide to support the tape, we store the current drive
+ *	pointer in our character device global variables, so that we can
+ *	pass between both interfaces.
+ */
+ 
+int idetape_identify_device (ide_drive_t *drive,struct hd_driveid *id)
+
+{
+	struct idetape_id_gcw gcw;
+	unsigned short *ptr;
+	int support=1;
+#if IDETAPE_DEBUG
+	unsigned short mask,i;
+#endif /* IDETAPE_DEBUG */
+		
+	ptr=(unsigned short *) &gcw;
+	*ptr=id->config;
+
+#if IDETAPE_DEBUG
+	printk ("Dumping ATAPI Identify Device tape parameters\n");
+	
+	printk ("Protocol Type: ");
+	switch (gcw.protocol) {
+		case 0: case 1: printk ("ATA\n");break;
+		case 2:	printk ("ATAPI\n");break;
+		case 3: printk ("Reserved (Unknown to ide-tape)\n");break;
+	}
+	
+	printk ("Device Type: %x - ",gcw.device_type);	
+	switch (gcw.device_type) {
+		case 0: printk ("Direct-access Device\n");break;
+		case 1: printk ("Streaming Tape Device\n");break;
+		case 2: case 3: case 4: printk ("Reserved\n");break;
+		case 5: printk ("CD-ROM Device\n");break;
+		case 6: printk ("Reserved\n");
+		case 7: printk ("Optical memory Device\n");break;
+		case 0x1f: printk ("Unknown or no Device type\n");break;
+		default: printk ("Reserved\n");
+	}
+	printk ("Removable: %s",gcw.removable ? "Yes\n":"No\n");	
+		
+	printk ("Command Packet DRQ Type: ");
+	switch (gcw.drq_type) {
+		case 0: printk ("Microprocessor DRQ\n");break;
+		case 1: printk ("Interrupt DRQ\n");break;
+		case 2: printk ("Accelerated DRQ\n");break;
+		case 3: printk ("Reserved\n");break;
+	}
+	
+	printk ("Command Packet Size: ");
+	switch (gcw.packet_size) {
+		case 0: printk ("12 bytes\n");break;
+		case 1: printk ("16 bytes\n");break;
+		default: printk ("Reserved\n");break;
+	}
+	printk ("Model: %s\n",id->model);
+	printk ("Firmware Revision: %s\n",id->fw_rev);
+	printk ("Serial Number: %s\n",id->serial_no);
+	printk ("Write buffer size: %d bytes\n",id->buf_size*512);
+	printk ("DMA: %s",id->capability & 0x01 ? "Yes\n":"No\n");
+	printk ("LBA: %s",id->capability & 0x02 ? "Yes\n":"No\n");
+	printk ("IORDY can be disabled: %s",id->capability & 0x04 ? "Yes\n":"No\n");
+	printk ("IORDY supported: %s",id->capability & 0x08 ? "Yes\n":"Unknown\n");
+	printk ("PIO Cycle Timing Category: %d\n",id->tPIO);
+	printk ("DMA Cycle Timing Category: %d\n",id->tDMA);
+	printk ("Single Word DMA supported modes: ");
+	for (i=0,mask=1;i<8;i++,mask=mask << 1) {
+		if (id->dma_1word & mask)
+			printk ("%d ",i);
+		if (id->dma_1word & (mask << 8))
+			printk ("(active) ");
+	}
+	printk ("\n");
+
+	printk ("Multi Word DMA supported modes: ");
+	for (i=0,mask=1;i<8;i++,mask=mask << 1) {
+		if (id->dma_mword & mask)
+			printk ("%d ",i);
+		if (id->dma_mword & (mask << 8))
+			printk ("(active) ");
+	}
+	printk ("\n");
+
+	if (id->field_valid & 0x0002) {
+		printk ("Enhanced PIO Modes: %s\n",id->eide_pio_modes & 1 ? "Mode 3":"None");
+		printk ("Minimum Multi-word DMA cycle per word: ");
+		if (id->eide_dma_min == 0)
+			printk ("Not supported\n");
+		else
+			printk ("%d ns\n",id->eide_dma_min);
+
+		printk ("Manafactuer\'s Recommended Multi-word cycle: ");
+		if (id->eide_dma_time == 0)
+			printk ("Not supported\n");
+		else
+			printk ("%d ns\n",id->eide_dma_time);
+
+		printk ("Minimum PIO cycle without IORDY: ");
+		if (id->eide_pio == 0)
+			printk ("Not supported\n");
+		else
+			printk ("%d ns\n",id->eide_pio);
+
+		printk ("Minimum PIO cycle with IORDY: ");
+		if (id->eide_pio_iordy == 0)
+			printk ("Not supported\n");
+		else
+			printk ("%d ns\n",id->eide_pio_iordy);
+		
+	}
+
+	else {
+		printk ("According to the device, fields 64-70 are not valid.\n");
+	}
+#endif /* IDETAPE_DEBUG */
+
+	/* Check that we can support this device */
+
+	if (gcw.protocol !=2 ) {
+		printk ("ide-tape: Protocol is not ATAPI\n");support=0;
+	}
+
+	if (gcw.device_type != 1) {
+		printk ("ide-tape: Device type is not set to tape\n");support=0;
+	}
+
+	if (!gcw.removable) {
+		printk ("ide-tape: The removable flag is not set\n");support=0;
+	}
+
+	if (gcw.drq_type != 2) {
+		printk ("ide-tape: Sorry, DRQ types other than Accelerated DRQ\n");
+		printk ("ide-tape: are still not supproted by the driver\n");support=0;
+	}
+
+	if (gcw.packet_size != 0) {
+		printk ("ide-tape: Packet size is not 12 bytes long\n");
+		if (gcw.packet_size == 1)
+			printk ("ide-tape: Sorry, padding to 16 bytes is still not supported\n");
+		support=0;			
+	}
+
+	if (idetape_drive_already_found) {
+		printk ("ide-tape: Sorry, only one ide tape drive is supported by the driver\n");
+		support=0;
+	}
+	else {
+		idetape_drive_already_found=1;
+		idetape_chrdev.drive=drive;
+		idetape_chrdev.major=IDETAPE_MAJOR;
+		idetape_chrdev.minor=0;
+		idetape_chrdev.name[0]='h';
+		idetape_chrdev.name[1]='t';
+		idetape_chrdev.name[2]='0';
+		idetape_chrdev.name[3]=0;
+	}
+
+	return (support);		/* In case support=0, we will not install the driver */
+}
+
+/*
+ *	idetape_register_chrdev calls register_chrdev to register our character
+ *	device interface. The connection to the ide_drive_t structure, which
+ *	is used by the entire ide driver is provided by our global variable
+ *	idetape_chrdev.drive, which was initialized earlier, during the device
+ *	probing stage.
+ */
+ 
+void idetape_register_chrdev (void)
+
+{
+	int major,minor;
+	ide_drive_t *drive;
+
+	if (!idetape_drive_already_found)
+		return;
+
+	drive=idetape_chrdev.drive;
+	major=idetape_chrdev.major;
+	minor=idetape_chrdev.minor;
+	
+	if (register_chrdev (major,idetape_chrdev.name,&idetape_fops)) {
+		printk ("Unable to register character device interface !\n");
+		/* ??? */
+	}
+	else {
+		printk ("ide-tape: %s <-> %s : Character device interface on major = %d\n",
+			drive->name,idetape_chrdev.name,major);
+	}
+}
+
+/*
+ *	idetape_setup is called from the ide driver in the partition table
+ *	identification stage, to:
+ *
+ *		1.	Initialize our various state variables.
+ *		2.	Ask the tape for its capabilities.
+ *		3.	Allocate a buffer which will be used for data
+ *			transfer. The buffer size is chosen based on
+ *			the recommendation which we received in step (2).
+ *
+ *	Note that at this point ide.c already assigned us an irq, so that
+ *	we can queue requests here and wait for their completion.
+ */
+ 
+void idetape_setup (ide_drive_t *drive)
+
+{
+	int buffer_size;
+	idetape_tape_t *tape=&(drive->tape);
+
+#if IDETAPE_DEBUG
+	printk ("ide-tape: Reached idetape_setup\n");
+#endif /* IDETAPE_DEBUG */	
+	
+	drive->ready_stat = 0;			/* With an ATAPI device, we can issue packet commands */
+						/* regardless of the state of DRDY */
+	tape->block_address=0;			
+	tape->block_address_valid=0;
+	tape->locate_to=0;
+	tape->locate_retries=0;
+	tape->pc_stack_index=0;
+	tape->failed_pc=NULL;
+	tape->postponed_rq=NULL;
+	tape->last_written_valid=0;
+	tape->busy=0;
+	
+	idetape_get_mode_sense_results (drive);
+
+	buffer_size=tape->capabilities.ctl*tape->tape_block_size;
+	tape->data_buffer=kmalloc (buffer_size,GFP_KERNEL);
+	if (tape->data_buffer == NULL) {
+		printk ("ide-tape: FATAL - Can not allocate %d bytes for data transfer buffer\n",buffer_size);
+		printk ("ide-tape: Aborting character device installation\n");
+		idetape_drive_already_found=0;
+		unregister_chrdev (idetape_chrdev.major,idetape_chrdev.name);
+		return;
+	}
+	printk ("ide-tape: Speed - %d KBps. Recommended transfer unit - %d bytes.\n",tape->capabilities.speed,buffer_size);
+	return;
+}
+
+/*
+ *	idetape_get_mode_sense_results asks the tape about its various
+ *	parameters. In particular, we will adjust our data transfer buffer
+ *	size to the recommended value as returned by the tape.
+ */
+
+void idetape_get_mode_sense_results (ide_drive_t *drive)
+
+{
+	int retval;
+	idetape_tape_t *tape=&(drive->tape);
+	idetape_mode_parameter_header_t *header;
+	idetape_capabilities_page_t *capabilities;
+	idetape_packet_command_t pc;
+	
+	idetape_create_mode_sense_cmd (&pc,IDETAPE_CAPABILITIES_PAGE);
+	pc.buffer=pc.temp_buffer;
+	pc.buffer_size=IDETAPE_TEMP_BUFFER_SIZE;
+	pc.current_position=pc.temp_buffer;
+	retval=idetape_queue_pc_tail (drive,&pc);
+
+	header=(idetape_mode_parameter_header_t *) pc.buffer;	
+	capabilities=(idetape_capabilities_page_t *) (pc.buffer+sizeof (idetape_mode_parameter_header_t));
+
+	capabilities->max_speed=idetape_swap_short (capabilities->max_speed);
+	capabilities->ctl=idetape_swap_short (capabilities->ctl);
+	capabilities->speed=idetape_swap_short (capabilities->speed);
+	capabilities->buffer_size=idetape_swap_short (capabilities->buffer_size);
+
+	tape->capabilities=*capabilities;		/* Save us a copy */
+	tape->tape_block_size=capabilities->blk512 ? 512:1024;
+
+	if (retval) {
+		printk ("ide-tape: Can't get tape parameters\n");
+		printk ("ide-tape: Assuming some default parameters\n");
+		tape->tape_block_size=512;
+		tape->capabilities.ctl=26*1024;
+		return;
+	}
+
+#if IDETAPE_DEBUG
+	printk ("Dumping the results of the MODE SENSE packet command\n");
+	printk ("Mode Parameter Header:\n");
+	printk ("Mode Data Length - %d\n",header->mode_data_length);
+	printk ("Medium Type - %d\n",header->medium_type);
+	printk ("Device Specific Parameter - %d\n",header->dsp);
+	printk ("Block Descriptor Length - %d\n",header->bdl);
+	
+	printk ("Capabilities and Mechanical Status Page:\n");
+	printk ("Page code - %d\n",capabilities->page_code);
+	printk ("Page length - %d\n",capabilities->page_length);
+	printk ("Read only - %s\n",capabilities->ro ? "Yes":"No");
+	printk ("Supports reverse space - %s\n",capabilities->sprev ? "Yes":"No");
+	printk ("Supports erase initiated formatting - %s\n",capabilities->efmt ? "Yes":"No");
+	printk ("Supports QFA two Partition format - %s\n",capabilities->qfa ? "Yes":"No");
+	printk ("Supports locking the medium - %s\n",capabilities->lock ? "Yes":"No");
+	printk ("The volume is currently locked - %s\n",capabilities->locked ? "Yes":"No");
+	printk ("The device defaults in the prevent state - %s\n",capabilities->prevent ? "Yes":"No");
+	printk ("Supports ejecting the medium - %s\n",capabilities->eject ? "Yes":"No");
+	printk ("Supports error correction - %s\n",capabilities->ecc ? "Yes":"No");
+	printk ("Supports data compression - %s\n",capabilities->cmprs ? "Yes":"No");
+	printk ("Supports 512 bytes block size - %s\n",capabilities->blk512 ? "Yes":"No");
+	printk ("Supports 1024 bytes block size - %s\n",capabilities->blk1024 ? "Yes":"No");
+	printk ("Restricted byte count for PIO transfers - %s\n",capabilities->slowb ? "Yes":"No");
+	printk ("Maximum supported speed in KBps - %d\n",capabilities->max_speed);
+	printk ("Continuous transfer limits in blocks - %d\n",capabilities->ctl);
+	printk ("Current speed in KBps - %d\n",capabilities->speed);	
+	printk ("Buffer size - %d\n",capabilities->buffer_size*512);
+#endif /* IDETAPE_DEBUG */
+}
+
+/*
+ *	Packet Command Interface
+ *
+ *	The current Packet Command is available in tape->pc, and will not
+ *	change until we finish handling it. Each packet command is associated
+ *	with a callback function that will be called when the command is
+ *	finished.
+ *
+ *	The handling will be done in three stages:
+ *
+ *	1.	idetape_issue_packet_command will send the packet command to the
+ *		drive, and will set the interrupt handler to idetape_pc_intr.
+ *
+ *	2.	On each interrupt, idetape_pc_intr will be called. This step
+ *		will be repeated until the device signals us that no more
+ *		interrupts will be issued.
+ *
+ *	3.	ATAPI Tape media access commands have immediate status with a
+ *		delayed process. In case of a successfull initiation of a
+ *		media access packet command, the DSC bit will be set when the
+ *		actual execution of the command is finished. 
+ *		Since the tape drive will not issue an interrupt, we have to
+ *		poll for this event. In this case, we define the request as
+ *		"low priority request" by setting rq_status to
+ *		IDETAPE_RQ_POSTPONED, 	set a timer to poll for DSC and exit
+ *		the driver.
+ *
+ *		ide.c will then give higher priority to requests which
+ *		originate from the other device, until will change rq_status
+ *		to RQ_ACTIVE.
+ *
+ *	4.	When the packet command is finished, it will be checked for errors.
+ *
+ *	5.	In case an error was found, we queue a request sense packet command
+ *		in front of the request queue and retry the operation up to
+ *		IDETAPE_MAX_PC_RETRIES times.
+ *
+ *	6.	In case no error was found, or we decided to give up and not
+ *		to retry again, the callback function will be called and then
+ *		we will handle the next request.
+ *
+ */
+
+void idetape_issue_packet_command  (ide_drive_t *drive,idetape_packet_command_t *pc,ide_handler_t *handler)
+
+{
+	idetape_tape_t *tape;
+	idetape_bcount_reg_t bcount;
+        idetape_ireason_reg_t ireason;
+
+	tape=&(drive->tape);
+	        
+#ifdef IDETAPE_DEBUG
+	if (tape->pc->c[0] == IDETAPE_REQUEST_SENSE_CMD && pc->c[0] == IDETAPE_REQUEST_SENSE_CMD) {
+		printk ("ide-tape: ide-tape.c bug - Two request sense in serial were issued\n");
+		/* ??? Need to rethink about that */		
+	}
+#endif /* IDETAPE_DEBUG */
+
+	if (tape->failed_pc == NULL && pc->c[0] != IDETAPE_REQUEST_SENSE_CMD)
+		tape->failed_pc=pc;
+	tape->pc=pc;							/* Set the current packet command */
+
+	if (pc->retries > IDETAPE_MAX_PC_RETRIES) {
+		printk ("ide-tape: %s: I/O error, ",drive->name);
+		printk ("pc = %x, key = %x, asc = %x, ascq = %x\n",pc->c[0],tape->sense_key,tape->asc,tape->ascq);
+		printk ("ide-tape: Maximum retries reached - Giving up\n");
+		pc->error=1;					/* Giving up */
+		pc->active=0;
+		tape->failed_pc=NULL;
+#if IDETAPE_DEBUG
+		if (pc->callback==NULL)
+			printk ("ide-tape: ide-tape bug - Callback function not set !\n");
+		else
+#endif /* IDETAPE_DEBUG */
+			(*pc->callback)(drive);
+		return;
+	}
+
+#if IDETAPE_DEBUG
+	printk ("Retry number - %d\n",pc->retries);
+#endif /* IDETAPE_DEBUG */
+
+	pc->retries++;
+
+/*
+ *	We no longer call ide_wait_stat to wait for the drive to be ready,
+ *	as ide.c already does this for us in do_request.
+ */
+ 
+	pc->actually_transferred=0;					/* We haven't transferred any data yet */
+	pc->active=1;							/* Packet command started */
+	bcount.all=pc->request_transfer;				/* Request to transfer the entire buffer at once */
+
+									/* Initialize the task file registers */
+	OUT_BYTE (0,IDETAPE_FEATURES_REG);				/* Use PIO data transger, No DMA */
+	OUT_BYTE (bcount.b.high,IDETAPE_BCOUNTH_REG);
+	OUT_BYTE (bcount.b.low,IDETAPE_BCOUNTL_REG);
+	OUT_BYTE (drive->select.all,IDETAPE_DRIVESEL_REG);
+	
+	ide_set_handler (drive,handler,WAIT_CMD);			/* Set the interrupt routine */
+	OUT_BYTE (WIN_PACKETCMD,IDETAPE_ATACOMMAND_REG);		/* Issue the packet command */
+	if (ide_wait_stat (drive,DRQ_STAT,BUSY_STAT,WAIT_READY)) { 	/* Wait for DRQ to be ready - Assuming Accelerated DRQ */	
+		/*
+		 *	We currently only support tape drives which report
+		 *	accelerated DRQ assertion. For this case, specs
+		 *	allow up to 50us. We really shouldn't get here.
+		 *
+		 *	??? Still needs to think what to do if we reach
+		 *	here anyway.
+		 */
+		 
+		 printk ("ide-tape: Strange, packet command initiated yet DRQ isn't asserted\n");
+		 return;
+	}
+	
+	ireason.all=IN_BYTE (IDETAPE_IREASON_REG);
+	if (!ireason.b.cod || ireason.b.io) {
+		printk ("ide-tape: (IO,CoD) != (0,1) while issuing a packet command\n");
+		/* ??? */
+	}
+		
+	ide_output_data (drive,pc->c,12/4);			/* Send the actual packet */
+}
+
+/*
+ *	idetape_pc_intr is the usual interrupt handler which will be called
+ *	during a packet command. We will transfer some of the data (as
+ *	requested by the drive) and will re-point interrupt handler to us.
+ *	When data transfer is finished, we will act according to the
+ *	algorithm described before idetape_issue_packet_command.
+ *
+ */
+ 
+
+void idetape_pc_intr (ide_drive_t *drive)
+
+{
+	idetape_tape_t *tape;
+	idetape_status_reg_t status;
+	idetape_bcount_reg_t bcount;
+        idetape_ireason_reg_t ireason;
+	idetape_packet_command_t *pc;
+	
+	unsigned long temp;
+
+	tape=&(drive->tape);
+	
+	status.all=IN_BYTE (IDETAPE_STATUS_REG);		/* Clear the interrupt */
+
+#if IDETAPE_DEBUG
+	printk ("ide-tape: Reached idetape_pc_intr interrupt handler\n");
+#endif /* IDETAPE_DEBUG */	
+
+	pc=tape->pc;						/* Current packet command */
+		
+	if (!status.b.drq) {					/* No more interrupts */
+#if IDETAPE_DEBUG
+		printk ("Packet command completed\n");
+		printk ("Total bytes transferred: %lu\n",pc->actually_transferred);
+#endif /* IDETAPE_DEBUG */
+		if (status.b.check) {					/* Error detected */
+#if IDETAPE_DEBUG
+	/*
+	 *	Without debugging, we only log an error if we decided to
+	 *	give up retrying.
+	 */
+			printk ("ide-tape: %s: I/O error, ",drive->name);
+#endif /* IDETAPE_DEBUG */
+			idetape_retry_pc (drive);			/* Retry operation */
+			return;
+		}
+		pc->error=0;
+		if (pc->wait_for_dsc && !status.b.dsc) {				/* Media access command */
+			tape->dsc_polling_frequency=IDETAPE_DSC_FAST_MEDIA_ACCESS_FREQUENCY;
+			idetape_postpone_request (drive);		/* Allow ide.c to handle other requests */
+			return;
+		}
+		pc->active=0;
+		if (tape->failed_pc == pc)
+			tape->failed_pc=NULL;
+#if IDETAPE_DEBUG
+		if (pc->callback==NULL)			
+			printk ("ide-tape: ide-tape bug - Callback function not set !\n");
+		else
+#endif IDETAPE_DEBUG
+			(*pc->callback)(drive);			/* Command finished - Call the callback function */
+		return;
+	}
+
+	bcount.b.high=IN_BYTE (IDETAPE_BCOUNTH_REG);			/* Get the number of bytes to transfer */
+	bcount.b.low=IN_BYTE (IDETAPE_BCOUNTL_REG);			/* on this interrupt */
+	ireason.all=IN_BYTE (IDETAPE_IREASON_REG);			/* Read the interrupt reason register */
+
+	if (ireason.b.cod) {
+		printk ("ide-tape: CoD != 0 in idetape_pc_intr\n");
+		/* ??? */
+	}
+	if (ireason.b.io != !(pc->writing)) {			/* Hopefully, we will never get here */
+		printk ("ide-tape: We wanted to %s, ",pc->writing ? "Write":"Read");
+		printk ("but the tape wants us to %s !\n",ireason.b.io ? "Read":"Write");
+		/* ??? */		
+	}
+	
+	if (!pc->writing) {					/* Reading - Check that we have enough space */
+		temp=(unsigned long) pc->actually_transferred + bcount.all;
+		if ( temp > pc->request_transfer) {
+			printk ("ide-tape: The tape wants to send us more data than requested - ");
+			if (temp > pc->buffer_size) {
+				printk ("Discarding data\n");
+				idetape_discard_data (drive,bcount.all);
+				ide_set_handler (drive,&idetape_pc_intr,WAIT_CMD);
+				return;
+			}
+			else
+				printk ("Allowing transfer\n");
+		}
+	}
+#if IDETAPE_DEBUG	
+	if (bcount.all && !pc->buffer) {	
+		printk ("ide-tape: ide-tape.c bug - Buffer not set in idetape_pc_intr. Discarding data.\n");
+		
+		if (!pc->writing) {
+			printk ("ide-tape: Discarding data\n");
+			idetape_discard_data (drive,bcount.all);
+			ide_set_handler (drive,&idetape_pc_intr,WAIT_CMD);
+			return;
+		}
+		else {	/* ??? */
+		}
+	}
+#endif /* IDETAPE_DEBUG */
+	if (pc->writing)
+		idetape_output_data (drive,pc->current_position,bcount.all);	/* Write the current buffer */
+	else
+		idetape_input_data (drive,pc->current_position,bcount.all);	/* Read the current buffer */
+#if IDETAPE_DEBUG
+	printk ("ide-tape: %s %d bytes\n",pc->writing ? "Wrote":"Received",bcount.all);
+#endif /* IDETAPE_DEBUG */
+	pc->actually_transferred+=bcount.all;					/* Update the current position */
+	pc->current_position+=bcount.all;
+
+	ide_set_handler (drive,&idetape_pc_intr,WAIT_CMD);		/* And set the interrupt handler again */
+}
+
+/*
+ *	idetape_postpone_request postpones the current request so that
+ *	ide.c will be able to service requests from another device on
+ *	the same hwgroup while we are polling for DSC.
+ */
+
+void idetape_postpone_request (ide_drive_t *drive)
+
+{
+	idetape_tape_t *tape;
+	unsigned long flags;
+	struct request *rq;
+		
+	tape=&(drive->tape);
+
+#if IDETAPE_DEBUG
+	printk ("Reached idetape_postpone_request\n");
+	if (tape->postponed_rq != NULL)
+		printk ("ide-tape.c bug - postponed_rq not NULL in idetape_postpone_request\n");
+#endif /* IDETAPE_DEBUG */
+
+	tape->dsc_count=0;
+	tape->dsc_timer.expires=jiffies + tape->dsc_polling_frequency;	/* Set timer to poll for */
+	tape->dsc_timeout=jiffies+IDETAPE_DSC_TIMEOUT;			/* actual completion */
+	tape->dsc_timer.data=(unsigned long) drive;
+	tape->dsc_timer.function=&idetape_poll_for_dsc;
+	init_timer (&(tape->dsc_timer));
+
+	/*
+	 * Remove current request from the request queue:
+	 */
+	save_flags(flags);						/* Let ide.c handle another request */
+	cli();
+	tape->postponed_rq = rq = HWGROUP(drive)->rq;
+	rq->rq_status = IDETAPE_RQ_POSTPONED;	
+	blk_dev[MAJOR(rq->rq_dev)].current_request = rq->next;
+	HWGROUP(drive)->rq = NULL;
+	restore_flags(flags);
+
+	tape->dsc_polling_start=jiffies;	
+	add_timer(&(tape->dsc_timer));		/* Activate the polling timer */
+}
+
+
+/*
+ *	idetape_poll_for_dsc gets invoked by a timer (which was set
+ *	by idetape_postpone_request) to poll for the DSC bit
+ *	in the status register. When the DSC bit is set, or a timeout is
+ *	reached, we put back the postponed request in front of the request
+ *	queue.
+ */
+ 
+void idetape_poll_for_dsc (unsigned long data)
+
+{
+	ide_drive_t *drive;
+	idetape_tape_t *tape;
+	
+	idetape_status_reg_t status;
+	idetape_packet_command_t *pc;
+
+	drive=(ide_drive_t *) data;
+	tape=&(drive->tape);
+	pc=tape->pc;
+	
+#if IDETAPE_DEBUG
+	printk ("%s: idetape_poll_for_dsc called\n",drive->name);
+#endif /* IDETAPE_DEBUG */	
+
+	status.all=IN_BYTE (IDETAPE_STATUS_REG);
+
+	if (status.b.dsc)
+		tape->dsc_count++;
+	else {
+		if (tape->dsc_count)
+			printk ("ide-tape: DSC fluctuation detected - Restarting DSC count\n");
+		tape->dsc_count=0;
+	}
+
+	if (tape->dsc_count == IDETAPE_DSC_COUNT) {		/* DSC received */
+		tape->dsc_received=1;
+		del_timer (&(tape->dsc_timer));			/* Stop polling and put back the postponed */
+		idetape_put_back_postponed_request (drive);	/* request in the request queue */
+		return;
+	}
+		
+	if (jiffies > tape->dsc_timeout) 	{ /* Timeout */
+		tape->dsc_received=0;
+		del_timer (&(tape->dsc_timer));
+		/* ??? */
+		idetape_put_back_postponed_request (drive);
+		return;
+	}
+	
+	/* Poll again */
+
+	if (jiffies - tape->dsc_polling_start > IDETAPE_FAST_SLOW_THRESHOLD)
+		tape->dsc_timer.expires = jiffies + IDETAPE_DSC_SLOW_MEDIA_ACCESS_FREQUENCY;
+	else
+		tape->dsc_timer.expires = jiffies + tape->dsc_polling_frequency;
+/*	init_timer (&(tape->dsc_timer)); */
+	add_timer(&(tape->dsc_timer));
+	return;
+}
+
+/*
+ *	idetape_put_back_postponed_request gets called by
+ *	idetape_poll_for_dsc when we decided to stop polling - Either
+ *	becase we received DSC or because we decided to give up and
+ *	stop waiting.
+ */
+
+void idetape_put_back_postponed_request (ide_drive_t *drive)
+
+{
+	idetape_tape_t *tape = &(drive->tape);
+
+#if IDETAPE_DEBUG
+	printk ("ide-tape: Putting back postponed request\n");
+#endif /* IDETAPE_DEBUG */
+
+	(void) ide_do_drive_cmd (drive, tape->postponed_rq, ide_next);
+
+	/*
+	 * 	Note that the procedure done here is differnet than the method
+	 *	we are using in idetape_queue_pc_head - There we are putting
+	 *	request(s) before our currently called request.
+	 *
+	 *	Here, on the other hand, HWGROUP(drive)->rq is not our
+	 *	request but rather a request to another device. Therefore,
+	 *	we will let it finish and only then service our postponed
+	 *	request --> We don't touch HWGROUP(drive)->rq.
+	 */
+}
+
+void idetape_media_access_finished (ide_drive_t *drive)
+
+{
+	idetape_tape_t *tape=&(drive->tape);
+	idetape_status_reg_t status;
+	idetape_packet_command_t *pc;
+
+	pc=tape->pc;
+	
+	status.all=IN_BYTE (IDETAPE_STATUS_REG);
+
+	if (tape->dsc_received) {
+#if IDETAPE_DEBUG
+		printk ("DSC received\n");
+#endif /* IDETAPE_DEBUG */
+		pc->active=0;
+		if (status.b.check) {					/* Error detected */
+			printk ("ide-tape: %s: I/O error, ",drive->name);
+			idetape_retry_pc (drive);			/* Retry operation */
+			return;
+		}
+		pc->error=0;
+		if (tape->failed_pc == pc)
+			tape->failed_pc=NULL;
+#if IDETAPE_DEBUG
+		if (pc->callback==NULL)
+			printk ("ide-tape: ide-tape bug - Callback function not set !\n");
+		else
+#endif /* IDETAPE_DEBUG */
+			(*pc->callback)(drive);
+
+		return;
+	}
+	else {
+		pc->active=0;
+		printk ("ide-tape: %s: DSC timeout.\n",drive->name);
+		/* ??? */
+		pc->error=1;
+		tape->failed_pc=NULL;
+#if IDETAPE_DEBUG
+		if (pc->callback==NULL)
+			printk ("ide-tape: ide-tape bug - Callback function not set !\n");
+		else
+#endif /* IDETAPE_DEBUG */
+			(*pc->callback)(drive);
+		return;
+	}
+}
+
+
+/*
+ *	idetape_retry_pc is called when an error was detected during the
+ *	last packet command. We queue a request sense packet command in
+ *	the head of the request list.
+ */
+ 
+void idetape_retry_pc (ide_drive_t *drive)
+
+{
+	idetape_packet_command_t *pc;
+	struct request *new_rq;
+
+	idetape_error_reg_t error;
+	error.all=IN_BYTE (IDETAPE_ERROR_REG);
+	pc=idetape_next_pc_storage (drive);
+	new_rq=idetape_next_rq_storage (drive);
+	idetape_create_request_sense_cmd (pc); 
+	pc->buffer=pc->temp_buffer;
+	pc->buffer_size=IDETAPE_TEMP_BUFFER_SIZE;
+	pc->current_position=pc->temp_buffer;
+	idetape_queue_pc_head (drive,pc,new_rq);
+}
+
+/*
+ *	General packet command callback function.
+ */
+ 
+void idetape_pc_callback (ide_drive_t *drive)
+
+{
+	idetape_tape_t *tape;
+	struct request *rq;
+	
+	tape=&(drive->tape);
+	rq=HWGROUP(drive)->rq;
+	
+#if IDETAPE_DEBUG
+	printk ("ide-tape: Reached idetape_pc_callback\n");
+#endif /* IDETAPE_DEBUG */
+	if (!tape->pc->error) {
+#if IDETAPE_DEBUG
+		printk ("Request completed\n");
+#endif /* IDETAPE_DEBUG */
+		idetape_end_request (1,HWGROUP (drive));
+	}
+	else {
+		printk ("Aborting request\n");
+		idetape_end_request (0,HWGROUP (drive));
+	}
+	return;
+}
+
+
+void idetape_read_callback (ide_drive_t *drive)
+
+{
+	idetape_tape_t *tape;
+	struct request *rq;
+
+	tape=&(drive->tape);	
+	rq=HWGROUP(drive)->rq;
+#if IDETAPE_DEBUG	
+	printk ("ide-tape: Reached idetape_read_callback\n");
+#endif /* IDETAPE_DEBUG */
+	tape->block_address+=tape->pc->actually_transferred/512;
+	if (!tape->pc->error) {
+#if IDETAPE_DEBUG
+		printk ("Request completed\n");
+#endif /* IDETAPE_DEBUG */
+		rq->sector+=rq->current_nr_sectors;
+		rq->nr_sectors-=rq->current_nr_sectors;
+		rq->current_nr_sectors=0;
+		idetape_end_request (1,HWGROUP (drive));
+	}
+	else {
+		printk ("Aborting request\n");
+		idetape_end_request (0,HWGROUP (drive));
+	}
+	return;
+}
+
+void idetape_fake_read (ide_drive_t *drive)
+
+{
+	idetape_tape_t *tape;
+	struct request *rq;
+	unsigned long i;
+
+	tape=&(drive->tape);	
+	rq=HWGROUP(drive)->rq;
+#if IDETAPE_DEBUG	
+	printk ("ide-tape: Reached idetape_fake_read\n");
+#endif /* IDETAPE_DEBUG */
+
+#if IDETAPE_DEBUG
+	printk ("Request completed\n");
+#endif /* IDETAPE_DEBUG */
+	
+	for (i=0;i<rq->current_nr_sectors*512;i++)
+		rq->buffer [i]=0;
+
+	tape->block_address+=rq->current_nr_sectors;
+	rq->sector+=rq->current_nr_sectors;
+	rq->nr_sectors-=rq->current_nr_sectors;
+	rq->current_nr_sectors=0;
+	idetape_end_request (1,HWGROUP (drive));
+}
+
+void idetape_write_callback (ide_drive_t *drive)
+
+{
+	idetape_tape_t *tape;
+	struct request *rq;
+	
+	tape=&(drive->tape);
+	rq=HWGROUP(drive)->rq;
+#if IDETAPE_DEBUG	
+	printk ("ide-tape: Reached idetape_write_callback\n");
+#endif /* IDETAPE_DEBUG */
+	tape->block_address+=tape->pc->actually_transferred/512;
+	if (!tape->pc->error) {
+#if IDETAPE_DEBUG
+		printk ("Request completed\n");
+#endif /* IDETAPE_DEBUG */
+		rq->sector+=rq->current_nr_sectors;
+		rq->nr_sectors-=rq->current_nr_sectors;
+		rq->current_nr_sectors=0;
+		idetape_end_request (1,HWGROUP (drive));
+	}
+	else {
+		printk ("Aborting request\n");
+		idetape_end_request (0,HWGROUP (drive));
+	}
+	return;
+}
+
+void idetape_inquiry_callback (ide_drive_t *drive)
+
+{
+	idetape_tape_t *tape;
+	
+	tape=&(drive->tape);
+	
+	idetape_display_inquiry_result (tape->pc->buffer);
+	idetape_pc_callback (drive);
+	return;
+}
+
+/*
+ *	idetape_input_data is called to read data from the tape's data
+ *	register. We basically let ide_input_data do the job, but we also
+ *	take care about the remaining bytes which can not be transferred
+ *	in 32-bit data transfers.
+ */
+ 
+void idetape_input_data (ide_drive_t *drive,void *buffer, unsigned long bcount)
+
+{
+	unsigned long wcount;
+	
+	wcount=bcount >> 2;
+	bcount -= 4*wcount;
+	
+	if (wcount)
+		ide_input_data (drive,buffer,wcount);
+	
+	if (bcount) {
+		((byte *)buffer) += 4*wcount;
+		insb (IDETAPE_DATA_REG,buffer,bcount);
+	}
+}
+
+/*
+ *	idetape_output_data is used to write data to the tape.
+ */
+ 
+void idetape_output_data (ide_drive_t *drive,void *buffer, unsigned long bcount)
+
+{
+	unsigned long wcount;
+	
+	wcount=bcount >> 2;
+	bcount -= 4*wcount;
+	
+	if (wcount)
+		ide_output_data (drive,buffer,wcount);
+	
+	if (bcount) {
+		((byte *)buffer) += 4*wcount;
+		outsb (IDETAPE_DATA_REG,buffer,bcount);
+	}
+}
+
+/*
+ *	Too bad. The drive wants to send us data which we are not ready to accept.
+ *	Just throw it away.
+ */
+ 
+void idetape_discard_data (ide_drive_t *drive, unsigned long bcount)
+
+{
+	unsigned long i;
+	
+	for (i=0;i<bcount;i++)
+		IN_BYTE (IDETAPE_DATA_REG);
+}
+
+/*
+ *	Issue an INQUIRY packet command.
+ */
+ 
+void idetape_create_inquiry_cmd (idetape_packet_command_t *pc)
+
+{
+#if IDETAPE_DEBUG
+	printk ("ide-tape: Creating INQUIRY packet command\n");
+#endif /* IDETAPE_DEBUG */	
+	pc->request_transfer=36;
+	pc->callback=&idetape_inquiry_callback;
+	pc->writing=0;
+	
+	idetape_zero_packet_command (pc);		
+	pc->c[0]=IDETAPE_INQUIRY_CMD;
+	pc->c[4]=255;
+}
+
+/*
+ *	Format the INQUIRY command results.
+ */
+ 
+void idetape_display_inquiry_result (byte *buffer)
+
+{
+	idetape_inquiry_result_t *result;
+
+	result=(idetape_inquiry_result_t *) buffer;
+	idetape_fixstring (result->vendor_id,8,0);
+	idetape_fixstring (result->product_id,16,0);
+	idetape_fixstring (result->revision_level,4,0);
+
+	if (result->response_format != 2) {
+		printk ("The INQUIRY Data Format is unknown to us !\n");
+		printk ("Assuming QIC-157C format.\n");
+	}
+
+#if IDETAPE_DEBUG
+	printk ("Dumping INQUIRY command results:\n");
+	printk ("Response Data Format: %d - ",result->response_format);
+	switch (result->response_format) {
+		case 2:
+			printk ("As specified in QIC-157 Revision C\n");
+			break;
+		default:
+			printk ("Unknown\n");
+			break;
+	}
+	
+	printk ("Device Type: %x - ",result->device_type);	
+	switch (result->device_type) {
+		case 0: printk ("Direct-access Device\n");break;
+		case 1: printk ("Streaming Tape Device\n");break;
+		case 2: case 3: case 4: printk ("Reserved\n");break;
+		case 5: printk ("CD-ROM Device\n");break;
+		case 6: printk ("Reserved\n");
+		case 7: printk ("Optical memory Device\n");break;
+		case 0x1f: printk ("Unknown or no Device type\n");break;
+		default: printk ("Reserved\n");
+	}
+	
+	printk ("Removable Medium: %s",result->rmb ? "Yes\n":"No\n");
+
+	printk ("ANSI Version: %d - ",result->ansi_version);
+	switch (result->ansi_version) {
+		case 2:
+			printk ("QIC-157 Revision C\n");
+			break;
+		default:
+			printk ("Unknown\n");
+			break;
+	}
+
+	printk ("ECMA Version: ");
+	if (result->ecma_version)
+		printk ("%d\n",result->ecma_version);
+	else
+		printk ("Not supported\n");
+
+	printk ("ISO Version: ");
+	if (result->iso_version)
+		printk ("%d\n",result->iso_version);
+	else
+		printk ("Not supported\n");
+
+	printk ("Additional Length: %d\n",result->additional_length);
+	printk ("Vendor Identification: %s\n",result->vendor_id);
+	printk ("Product Identification: %s\n",result->product_id);
+	printk ("Product Revision Level: %s\n",result->revision_level);
+#endif /* IDETAPE_DEBUG */
+
+	if (result->device_type != 1)
+		printk ("Device type is not set to tape\n");
+
+	if (!result->rmb)
+		printk ("The removable flag is not set\n");
+
+	if (result->ansi_version != 2) {
+		printk ("The Ansi Version is unknown to us !\n");
+		printk ("Assuming compliance with QIC-157C specification.\n");
+	}
+}
+
+void idetape_create_request_sense_cmd (idetape_packet_command_t *pc)
+
+{
+#if IDETAPE_DEBUG
+	printk ("ide-tape: Creating REQUEST SENSE packet command\n");
+#endif /* IDETAPE_DEBUG */	
+	pc->request_transfer=18;
+	pc->callback=&idetape_request_sense_callback;
+	pc->writing=0;
+	
+	idetape_zero_packet_command (pc);	
+	pc->c[0]=IDETAPE_REQUEST_SENSE_CMD;
+	pc->c[4]=255;
+}
+
+void idetape_request_sense_callback (ide_drive_t *drive)
+
+{
+	idetape_tape_t *tape;
+	struct request *rq;
+
+	tape=&(drive->tape);	
+	rq=HWGROUP(drive)->rq;
+	
+#if IDETAPE_DEBUG
+	printk ("ide-tape: Reached idetape_request_sense_callback\n");
+#endif /* IDETAPE_DEBUG */
+	if (!tape->pc->error) {
+#if IDETAPE_DEBUG
+		printk ("Request completed\n");
+#endif /* IDETAPE_DEBUG */
+		idetape_analyze_error (drive,(idetape_request_sense_result_t *) tape->pc->buffer);
+		idetape_end_request (1,HWGROUP (drive));
+	}
+	else {
+		printk ("Error in REQUEST SENSE itself - Aborting request!\n");
+		idetape_end_request (0,HWGROUP (drive));
+	}
+	return;
+}
+
+/*
+ *	idetape_analyze_error is called on each failed packet command retry
+ *	to analyze the request sense. We currently do not utilize this
+ *	information.
+ */
+ 
+void idetape_analyze_error (ide_drive_t *drive,idetape_request_sense_result_t *result)
+
+{
+	idetape_tape_t *tape;
+	
+	tape=&(drive->tape);
+	tape->sense_key=result->sense_key;
+	tape->asc=result->asc;
+	tape->ascq=result->ascq;
+#if IDETAPE_DEBUG	
+	/*
+	 *	Without debugging, we only log an error if we decided to
+	 *	give up retrying.
+	 */
+	printk ("ide-tape: sense key = %x, asc = %x, ascq = %x\n",result->sense_key,result->asc,result->ascq);
+#endif /* IDETAPE_DEBUG */	
+	return;	
+}
+
+void idetape_create_test_unit_ready_cmd (idetape_packet_command_t *pc)
+
+{
+#if IDETAPE_DEBUG
+	printk ("ide-tape: Creating TEST UNIT READY packet command\n");
+#endif /* IDETAPE_DEBUG */	
+	pc->request_transfer=0;
+	pc->buffer=NULL;
+	pc->current_position=NULL;
+	pc->callback=&idetape_pc_callback;
+	pc->writing=0;
+	
+	idetape_zero_packet_command (pc);	
+	pc->c[0]=IDETAPE_TEST_UNIT_READY_CMD;
+}
+
+void idetape_create_locate_cmd (idetape_packet_command_t *pc,unsigned long block,byte partition)
+
+{
+	unsigned long *ptr;
+
+#if IDETAPE_DEBUG
+	printk ("ide-tape: Creating LOCATE packet command\n");
+#endif /* IDETAPE_DEBUG */
+	pc->request_transfer=0;
+	pc->buffer=NULL;
+	pc->current_position=NULL;
+	pc->buffer_size=0;
+	pc->wait_for_dsc=1;
+	pc->callback=&idetape_pc_callback;
+	pc->writing=0;
+		
+	idetape_zero_packet_command (pc);
+	pc->c [0]=IDETAPE_LOCATE_CMD;
+	pc->c [1]=2;
+	ptr=(unsigned long *) &(pc->c[3]);
+	*ptr=idetape_swap_long (block);
+	pc->c[8]=partition;
+}
+
+void idetape_create_rewind_cmd (idetape_packet_command_t *pc)
+
+{
+#if IDETAPE_DEBUG
+	printk ("ide-tape: Creating REWIND packet command\n");
+#endif /* IDETAPE_DEBUG */
+	pc->request_transfer=0;
+	pc->buffer=NULL;
+	pc->current_position=NULL;
+	pc->buffer_size=0;
+	pc->wait_for_dsc=1;
+	pc->callback=&idetape_pc_callback;
+	pc->writing=0;
+		
+	idetape_zero_packet_command (pc);
+	pc->c [0]=IDETAPE_REWIND_CMD;
+}
+
+/*
+ *	A mode sense command is used to "sense" tape parameters.
+ */
+
+void idetape_create_mode_sense_cmd (idetape_packet_command_t *pc,byte page_code)
+
+{
+#if IDETAPE_DEBUG
+		printk ("ide-tape: Creating MODE SENSE packet command - Page %d\n",page_code);
+#endif /* IDETAPE_DEBUG */
+
+	pc->wait_for_dsc=0;
+	pc->callback=&idetape_pc_callback;
+	pc->writing=0;
+
+	switch (page_code) {
+		case IDETAPE_CAPABILITIES_PAGE:
+			pc->request_transfer=24;
+	}
+		
+	idetape_zero_packet_command (pc);
+	pc->c [0]=IDETAPE_MODE_SENSE_CMD;
+	pc->c [1]=8;				/* DBD = 1 - Don't return block descriptors for now */
+	pc->c [2]=page_code;
+	pc->c [3]=255;				/* Don't limit the returned information */
+	pc->c [4]=255;				/* (We will just discard data in that case) */
+}
+
+/*
+ *	idetape_create_write_filemark_cmd will:
+ *
+ *		1.	Write a filemark if write_filemark=1.
+ *		2.	Flush the device buffers without writing a filemark
+ *			if write_filemark=0.
+ *
+ */
+ 
+void idetape_create_write_filemark_cmd (idetape_packet_command_t *pc,int write_filemark)
+
+{
+#if IDETAPE_DEBUG
+	printk ("Creating WRITE FILEMARK packet command\n");
+	if (!write_filemark)
+		printk ("which will only flush buffered data\n");
+#endif /* IDETAPE_DEBUG */
+	pc->request_transfer=0;
+	pc->buffer=NULL;
+	pc->current_position=NULL;
+	pc->buffer_size=0;
+	pc->wait_for_dsc=1;
+	pc->callback=&idetape_pc_callback;
+	pc->writing=0;
+		
+	idetape_zero_packet_command (pc);
+	pc->c [0]=IDETAPE_WRITE_FILEMARK_CMD;
+	if (write_filemark)
+		pc->c [4]=1;
+}
+
+void idetape_create_erase_cmd (idetape_packet_command_t *pc)
+
+{
+
+#if IDETAPE_DEBUG
+	printk ("Creating ERASE command\n");
+#endif /* IDETAPE_DEBUG */
+
+	pc->request_transfer=0;
+	pc->buffer=NULL;
+	pc->current_position=NULL;
+	pc->buffer_size=0;
+	pc->wait_for_dsc=1;
+	pc->callback=&idetape_pc_callback;
+	pc->writing=0;
+		
+	idetape_zero_packet_command (pc);
+	pc->c [0]=IDETAPE_ERASE_CMD;
+	pc->c [1]=1;
+}
+
+void idetape_create_read_cmd (idetape_packet_command_t *pc,unsigned long length)
+
+{
+	union convert {
+		unsigned all	:32;
+		struct {
+			unsigned b1	:8;
+			unsigned b2	:8;
+			unsigned b3	:8;
+			unsigned b4	:8;
+		} b;
+	} original;
+	
+#if IDETAPE_DEBUG
+	printk ("ide-tape: Creating READ packet command\n");
+#endif /* IDETAPE_DEBUG */
+
+	original.all=length;
+
+	pc->wait_for_dsc=0;
+	pc->callback=&idetape_read_callback;
+	pc->writing=0;
+
+	idetape_zero_packet_command (pc);
+	pc->c [0]=IDETAPE_READ_CMD;
+	pc->c [1]=1;
+	pc->c [4]=original.b.b1;
+	pc->c [3]=original.b.b2;
+	pc->c [2]=original.b.b3;
+
+	return;
+}
+
+void idetape_create_space_cmd (idetape_packet_command_t *pc,long count,byte cmd)
+
+{
+	union convert {
+		unsigned all	:32;
+		struct {
+			unsigned b1	:8;
+			unsigned b2	:8;
+			unsigned b3	:8;
+			unsigned b4	:8;
+		} b;
+	} original;
+	
+#if IDETAPE_DEBUG
+	printk ("ide-tape: Creating SPACE packet command\n");
+#endif /* IDETAPE_DEBUG */
+
+	original.all=count;
+
+	pc->request_transfer=0;
+	pc->buffer=NULL;
+	pc->current_position=NULL;
+	pc->buffer_size=0;
+	pc->wait_for_dsc=1;
+	pc->callback=&idetape_pc_callback;
+	pc->writing=0;
+
+	idetape_zero_packet_command (pc);
+	pc->c [0]=IDETAPE_SPACE_CMD;
+	pc->c [1]=cmd;
+	pc->c [4]=original.b.b1;
+	pc->c [3]=original.b.b2;
+	pc->c [2]=original.b.b3;
+
+	return;
+}
+
+void idetape_create_write_cmd (idetape_packet_command_t *pc,unsigned long length)
+
+{
+	union convert {
+		unsigned all	:32;
+		struct {
+			unsigned b1	:8;
+			unsigned b2	:8;
+			unsigned b3	:8;
+			unsigned b4	:8;
+		} b;
+	} original;
+	
+#if IDETAPE_DEBUG
+	printk ("ide-tape: Creating WRITE packet command\n");
+#endif /* IDETAPE_DEBUG */
+
+	original.all=length;
+
+	pc->wait_for_dsc=0;
+	pc->callback=&idetape_write_callback;
+	pc->writing=1;
+
+	idetape_zero_packet_command (pc);
+	pc->c [0]=IDETAPE_WRITE_CMD;
+	pc->c [1]=1;
+	pc->c [4]=original.b.b1;
+	pc->c [3]=original.b.b2;
+	pc->c [2]=original.b.b3;
+
+	return;
+}
+
+void idetape_create_read_position_cmd (idetape_packet_command_t *pc)
+
+{
+#if IDETAPE_DEBUG
+	printk ("ide-tape: Creating READ POSITION packet command\n");
+#endif /* IDETAPE_DEBUG */
+
+	pc->request_transfer=20;
+	pc->wait_for_dsc=0;
+	pc->callback=&idetape_read_position_callback;
+	pc->writing=0;
+
+	idetape_zero_packet_command (pc);
+	pc->c [0]=IDETAPE_READ_POSITION_CMD;
+	pc->c [1]=0;
+}
+
+void idetape_read_position_callback (ide_drive_t *drive)
+
+{
+	idetape_tape_t *tape;
+	struct request *rq;
+	idetape_read_position_result_t *result;
+	
+	tape=&(drive->tape);
+	
+#if IDETAPE_DEBUG
+	printk ("ide-tape: Reached idetape_read_position_callback\n");
+#endif /* IDETAPE_DEBUG */
+
+	rq=HWGROUP(drive)->rq;
+	
+	if (!tape->pc->error) {
+		result=(idetape_read_position_result_t *) tape->pc->buffer;
+#if IDETAPE_DEBUG
+		printk ("Request completed\n");
+		printk ("Dumping the results of the READ POSITION command\n");
+		printk ("BOP - %s\n",result->bop ? "Yes":"No");
+		printk ("EOP - %s\n",result->eop ? "Yes":"No");
+#endif /* IDETAPE_DEBUG */
+		if (result->bpu) {
+			printk ("ide-tape: Block location is unknown to the tape\n");
+			printk ("Aborting request\n");
+			tape->block_address_valid=0;
+			idetape_end_request (0,HWGROUP (drive));
+		}
+		else {
+#if IDETAPE_DEBUG
+			printk ("Block Location - %lu\n",idetape_swap_long (result->first_block));
+#endif /* IDETAPE_DEBUG */
+			tape->block_address=idetape_swap_long (result->first_block);
+			tape->block_address_valid=1;
+			idetape_end_request (1,HWGROUP (drive));
+		}
+	}
+	else {
+		printk ("Aborting request\n");
+		idetape_end_request (0,HWGROUP (drive));
+	}
+	return;
+}
+
+/*
+ *	Our special ide-tape ioctl's.
+ *
+ *	Currently there aren't any significant ioctl's.
+ *	mtio.h compatible commands should be issued to the character device
+ *	interface.
+ */
+ 
+int idetape_blkdev_ioctl (ide_drive_t *drive, struct inode *inode, struct file *file,
+			unsigned int cmd, unsigned long arg)
+{
+	idetape_packet_command_t pc;
+	int retval;
+	
+	pc.buffer=pc.temp_buffer;
+	pc.buffer_size=IDETAPE_TEMP_BUFFER_SIZE;
+	pc.current_position=pc.temp_buffer;
+
+#if IDETAPE_DEBUG	
+	printk ("ide-tape: Reached idetape_blkdev_ioctl\n");
+#endif /* IDETAPE_DEBUG */
+	switch (cmd) {
+		case IDETAPE_INQUIRY_IOCTL:
+#if IDETAPE_DEBUG
+			printk ("Adding INQUIRY packet command to the tail of the request queue\n");
+#endif /* IDETAPE_DEBUG */
+			idetape_create_inquiry_cmd (&pc);
+			pc.buffer=pc.temp_buffer;
+			pc.buffer_size=IDETAPE_TEMP_BUFFER_SIZE;
+			pc.current_position=pc.temp_buffer;
+			return (idetape_queue_pc_tail (drive,&pc));
+		case IDETAPE_LOCATE_IOCTL:
+#if IDETAPE_DEBUG
+			printk ("Adding LOCATE packet command to the tail of the request queue\n");
+#endif /* IDETAPE_DEBUG */
+			idetape_create_locate_cmd (&pc,arg,0);
+			retval=idetape_queue_pc_tail (drive,&pc);
+			if (retval!=0) return (retval);
+			
+			idetape_create_read_position_cmd (&pc);
+			pc.buffer=pc.temp_buffer;
+			pc.buffer_size=IDETAPE_TEMP_BUFFER_SIZE;
+			pc.current_position=pc.temp_buffer;
+			return (idetape_queue_pc_tail (drive,&pc));
+/*
+		case IDETAPE_RESET_IOCTL:
+			printk ("Resetting drive\n");
+			return (!ide_do_reset (drive));
+*/
+		default:
+			return -EIO;
+	}
+}
+
+/*
+ *	Functions which handle requests.
+ */
+
+/*
+ *	idetape_end_request is used to end a request.
+ *
+ *	It is very similiar to ide_end_request, with a major difference - If
+ *	we are handling our own requests rather than requests which originate
+ *	in the buffer cache, we set rq->errors to 1 if the request failed.
+ */
+
+void idetape_end_request (byte uptodate, ide_hwgroup_t *hwgroup)
+
+{
+	ide_drive_t *drive = hwgroup->drive;
+	struct request *rq = hwgroup->rq;
+
+	if (rq->cmd == READ || rq->cmd == WRITE) {	/* Buffer cache originated request */
+		ide_end_request (uptodate,hwgroup);	/* Let the common code handle it */
+		return;
+	}
+							/* Our own originated request */
+	rq->errors=!uptodate;				/* rq->errors will tell us if the request was successfull */
+	ide_end_drive_cmd (drive, 0, 0);
+
+	/* The "up(rq->sem);" does the necessary "wake_up()" for us,
+	 * providing we started sleeping with a "down()" call.
+	 * This may not be the case if the driver converts a READ or WRITE
+	 * request into a special internal rq->cmd type.   -ml
+	 */
+	 
+	 /*
+	  * As Mark explained, we do not need a "wake_up()" call here,
+	  * since we are always sleeping with a "down()" call.
+	  */
+
+}
+
+/*
+ *	idetape_do_request is our request handling function.	
+ */
+
+void idetape_do_request (ide_drive_t *drive, struct request *rq, unsigned long block)
+
+{
+	idetape_tape_t *tape;
+	idetape_packet_command_t *pc;
+	struct request *new_rq;
+	idetape_status_reg_t status;
+
+	tape=&(drive->tape);
+		
+#if IDETAPE_DEBUG
+	printk ("Current request:\n");
+	printk ("rq_status: %d, rq_dev: %u, cmd: %d, errors: %d\n",rq->rq_status,(unsigned int) rq->rq_dev,rq->cmd,rq->errors);
+	printk ("sector: %ld, nr_sectors: %ld, current_nr_sectors: %ld\n",rq->sector,rq->nr_sectors,rq->current_nr_sectors);
+#endif /* IDETAPE_DEBUG */
+
+	/* Retry a failed packet command */
+
+	if (tape->failed_pc != NULL && tape->pc->c[0] == IDETAPE_REQUEST_SENSE_CMD) {
+		idetape_issue_packet_command (drive,tape->failed_pc,&idetape_pc_intr);
+		return;
+	}
+
+	/* Check if we have a postponed request */
+	
+	if (tape->postponed_rq != NULL) {
+/* #if IDETAPE_DEBUG */
+		if (tape->postponed_rq->rq_status != RQ_ACTIVE || rq != tape->postponed_rq) {
+			printk ("ide-tape: ide-tape.c bug - Two DSC requests were queued\n");
+			idetape_end_request (0,HWGROUP (drive));
+			return;
+		}
+/* #endif */ /* IDETAPE_DEBUG */
+		if (rq->cmd == IDETAPE_PACKET_COMMAND_REQUEST_TYPE1) {
+	
+			/* Media access command */
+			
+			tape->postponed_rq = NULL;
+			idetape_media_access_finished (drive);
+			return;
+		}
+		
+		/*
+		 * Read / Write command - DSC polling was done before the
+		 * actual command - Continue normally so that the command
+		 * will be performed below.
+		 */
+		 
+		 tape->postponed_rq = NULL;
+	}	
+	
+
+	if (rq->cmd == READ || rq->cmd == IDETAPE_READ_REQUEST || rq->cmd == WRITE || rq->cmd == IDETAPE_WRITE_REQUEST) {
+
+		if (!tape->block_address_valid || tape->block_address!=rq->sector) {		/* Re-position the tape */
+		
+			if (tape->locate_to == rq->sector && tape->locate_retries > IDETAPE_LOCATE_RETRIES) {
+				printk ("ide-tape: Can not reach block %lu - Aborting request\n",rq->sector);
+				tape->locate_retries=0;
+				idetape_end_request (0,HWGROUP (drive));
+				return;
+			}
+						
+			if (tape->locate_to == rq->sector)
+				tape->locate_retries++;
+			else {
+				tape->locate_to=rq->sector;
+				tape->locate_retries=1;
+			}
+#if IDETAPE_DEBUG
+			printk ("ide-tape: We are not at the requested block\n");
+			printk ("ide-tape: Re-positioning tape\n");
+			printk ("ide-tape: Adding READ POSITION command to the head of the queue\n");
+#endif /* IDETAPE_DEBUG */
+			pc=idetape_next_pc_storage (drive);
+			new_rq=idetape_next_rq_storage (drive);
+			idetape_create_read_position_cmd (pc); 
+			pc->buffer=pc->temp_buffer;
+			pc->buffer_size=IDETAPE_TEMP_BUFFER_SIZE;
+			pc->current_position=pc->temp_buffer;
+			idetape_queue_pc_head (drive,pc,new_rq);
+#if IDETAPE_DEBUG			
+			printk ("ide-tape: Adding LOCATE %lu command to the head of the queue\n",rq->sector);
+#endif /* IDETAPE_DEBUG */
+			pc=idetape_next_pc_storage (drive);
+			new_rq=idetape_next_rq_storage (drive);
+			idetape_create_locate_cmd (pc,rq->sector,0);
+			idetape_queue_pc_head (drive,pc,new_rq);
+
+			if (!tape->block_address_valid) {		/* The tape doesn't know the position - help it */
+									/* by rewinding the tape */
+#if IDETAPE_DEBUG			
+				printk ("ide-tape: Adding LOCATE 0 command to the head of the queue\n");
+#endif /* IDETAPE_DEBUG */
+				pc=idetape_next_pc_storage (drive);
+				new_rq=idetape_next_rq_storage (drive);
+				idetape_create_locate_cmd (pc,0,0);
+				idetape_queue_pc_head (drive,pc,new_rq);
+			}
+			
+			return;
+		}
+		else
+			tape->locate_retries=0;
+	}
+	
+	switch (rq->cmd) {
+		case READ:
+		case IDETAPE_READ_REQUEST:
+#if IDETAPE_DEBUG
+			if (rq->cmd == READ)
+				printk ("ide-tape: Handling buffer cache READ request\n");
+			else
+				printk ("ide-tape: Handling our own (not buffer cache originated) READ request\n");
+#endif /* IDETAPE_DEBUG */			
+			status.all=IN_BYTE (IDETAPE_STATUS_REG);
+			if (!status.b.dsc) {				/* Tape buffer not ready to accept r/w command */
+#if IDETAPE_DEBUG
+				printk ("ide-tape: DSC != 1 - Postponing read request\n");
+#endif /* IDETAPE_DEBUG */	
+				tape->dsc_polling_frequency=IDETAPE_DSC_READ_WRITE_FREQUENCY;
+				idetape_postpone_request (drive);	/* Allow ide.c to process requests from */
+				return;
+			}			
+
+			tape->last_written_valid=0;
+			
+			pc=idetape_next_pc_storage (drive);
+
+			idetape_create_read_cmd (pc,rq->current_nr_sectors);
+			
+			pc->buffer=rq->buffer;
+			pc->buffer_size=rq->current_nr_sectors*512;
+			pc->current_position=rq->buffer;
+			pc->request_transfer=rq->current_nr_sectors*512;
+
+			idetape_issue_packet_command (drive,pc,&idetape_pc_intr);
+			return;
+		
+		case WRITE:
+		case IDETAPE_WRITE_REQUEST:
+#if IDETAPE_DEBUG
+			if (rq->cmd == WRITE)
+				printk ("ide-tape: Handling buffer cache WRITE request\n");
+			else
+				printk ("ide-tape: Handling our own (not buffer cache originated) WRITE request\n");
+#endif /* IDETAPE_DEBUG */			
+
+			status.all=IN_BYTE (IDETAPE_STATUS_REG);
+			if (!status.b.dsc) {				/* Tape buffer not ready to accept r/w command */
+#if IDETAPE_DEBUG
+				printk ("ide-tape: DSC != 1 - Postponing write request\n");
+#endif /* IDETAPE_DEBUG */	
+				tape->dsc_polling_frequency=IDETAPE_DSC_READ_WRITE_FREQUENCY;
+				idetape_postpone_request (drive);	/* Allow ide.c to process requests from */
+				return;
+			}			
+
+			tape->last_written_valid=1;
+			tape->last_written_block=rq->sector;
+			
+			pc=idetape_next_pc_storage (drive);
+
+			idetape_create_write_cmd (pc,rq->current_nr_sectors);
+			
+			pc->buffer=rq->buffer;
+			pc->buffer_size=rq->current_nr_sectors*512;
+			pc->current_position=rq->buffer;
+			pc->request_transfer=rq->current_nr_sectors*512;
+
+			idetape_issue_packet_command (drive,pc,&idetape_pc_intr);
+			return;
+					
+		case IDETAPE_PACKET_COMMAND_REQUEST_TYPE1:
+		case IDETAPE_PACKET_COMMAND_REQUEST_TYPE2:
+/*
+ *	This should be unnecessary (postponing of a general packet command),
+ *	but I have occasionally missed DSC on a media access command otherwise.
+ *	??? Still have to figure it out ...
+ */
+			status.all=IN_BYTE (IDETAPE_STATUS_REG);
+			if (!status.b.dsc) {				/* Tape buffers are still not ready */
+#if IDETAPE_DEBUG
+				printk ("ide-tape: DSC != 1 - Postponing packet command request\n");
+#endif IDETAPE_DEBUG
+				rq->cmd=IDETAPE_PACKET_COMMAND_REQUEST_TYPE2;	/* Note that we are waiting for DSC *before* we */
+										/* even issued the command */
+				tape->dsc_polling_frequency=IDETAPE_DSC_READ_WRITE_FREQUENCY;
+				idetape_postpone_request (drive);	/* Allow ide.c to process requests from */
+				return;
+			}
+			rq->cmd=IDETAPE_PACKET_COMMAND_REQUEST_TYPE1;
+			pc=(idetape_packet_command_t *) rq->buffer;
+			idetape_issue_packet_command (drive,pc,&idetape_pc_intr);
+			return;
+
+		default:
+			printk ("ide-tape: Unknown command in request - Aborting request\n");
+			idetape_end_request (0,HWGROUP (drive));
+	}	
+}
+
+/*
+ *	idetape_queue_pc_tail is based on the following functions:
+ *
+ *	ide_do_drive_cmd from ide.c
+ *	cdrom_queue_request and cdrom_queue_packet_command from ide-cd.c
+ *
+ *	We add a special packet command request to the tail of the request queue,
+ *	and wait for it to be serviced.
+ *
+ *	This is not to be called from within the request handling part
+ *	of the driver ! We allocate here data in the stack, and it is valid
+ *	until the request is finished. This is not the case for the bottom
+ *	part of the driver, where we are always leaving the functions to wait
+ *	for an interrupt or a timer event.
+ *
+ *	From the bottom part of the driver, we should allocate safe memory
+ *	using idetape_next_pc_storage and idetape_next_rq_storage, and add
+ *	the request to the request list without waiting for it to be serviced !
+ *	In that case, we usually use idetape_queue_pc_head.
+ */
+
+int idetape_queue_pc_tail (ide_drive_t *drive,idetape_packet_command_t *pc)
+{
+	struct request rq;
+
+	ide_init_drive_cmd (&rq);
+	rq.buffer = (char *) pc;
+	rq.cmd = IDETAPE_PACKET_COMMAND_REQUEST_TYPE1;
+	return ide_do_drive_cmd (drive, &rq, ide_wait);
+}
+
+/*
+ *	idetape_queue_pc_head generates a new packet command request in front
+ *	of the request queue, before the current request, so that it will be
+ *	processed immediately, on the next pass through the driver.
+ *
+ *	idetape_queue_pc_head is called from the request handling part of
+ *	the driver (the "bottom" part). Safe storage for the request should
+ *	be allocated with idetape_next_pc_storage and idetape_next_rq_storage
+ *	before calling idetape_queue_pc_head.
+ *
+ *	Memory for those requests is pre-allocated at initialization time, and
+ *	is limited to IDETAPE_PC_STACK requests. We assume that we have enough
+ *	space for the maximum possible number of inter-dependent packet commands.
+ *
+ *	The higher level of the driver - The ioctl handler and the character
+ *	device handling functions should queue request to the lower level part
+ *	and wait for their completion using idetape_queue_pc_tail or
+ *	idetape_queue_rw_tail.
+ */
+ 
+void idetape_queue_pc_head (ide_drive_t *drive,idetape_packet_command_t *pc,struct request *rq)
+
+{
+	ide_init_drive_cmd (rq);
+	rq->buffer = (char *) pc;
+	rq->cmd = IDETAPE_PACKET_COMMAND_REQUEST_TYPE1;
+	(void) ide_do_drive_cmd (drive, rq, ide_preempt);
+}
+
+/*
+ *	idetape_queue_rw_tail is typically called from the character device
+ *	interface to generate a read/write request for the block device interface
+ *	and wait for it to be serviced. Note that cmd will be different than
+ *	a buffer cache originated read/write request. This will be used
+ *	in idetape_end_request.
+ *
+ *	Returns 0 on success or -EIO if an error occured.
+ */
+
+int idetape_queue_rw_tail (ide_drive_t *drive,int cmd,int blocks,char *buffer)
+
+{
+	idetape_tape_t *tape = &(drive->tape);
+	struct request rq;
+
+#if IDETAPE_DEBUG
+	printk ("idetape_queue_rw_tail: cmd=%d\n",cmd);
+#endif /* IDETAPE_DEBUG */
+	/* build up a special read request, and add it to the queue */
+	
+	ide_init_drive_cmd (&rq);
+	rq.buffer = buffer;
+	rq.cmd = cmd;
+	rq.sector = tape->block_address;
+	rq.nr_sectors = blocks;
+	rq.current_nr_sectors = blocks;
+	return ide_do_drive_cmd (drive, &rq, ide_wait);
+}
+
+/*
+ *	Copied from ide.c (declared static there)
+ */
+
+void idetape_fixstring (byte *s, const int bytecount, const int byteswap)
+{
+	byte *p = s, *end = &s[bytecount & ~1]; /* bytecount must be even */
+
+	if (byteswap) {
+		/* convert from big-endian to host byte order */
+		for (p = end ; p != s;) {
+			unsigned short *pp = (unsigned short *) (p -= 2);
+			*pp = ntohs(*pp);
+		}
+	}
+
+	/* strip leading blanks */
+	while (s != end && *s == ' ')
+		++s;
+
+	/* compress internal blanks and strip trailing blanks */
+	while (s != end && *s) {
+		if (*s++ != ' ' || (s != end && *s && *s != ' '))
+			*p++ = *(s-1);
+	}
+
+	/* wipe out trailing garbage */
+	while (p != end)
+		*p++ = '\0';
+}
+
+/*
+ *	idetape_zero_packet_command just zeros a packet command and
+ *	sets the number of retries to 0, as we haven't retried it yet.
+ */
+ 
+void idetape_zero_packet_command (idetape_packet_command_t *pc)
+
+{
+	int i;
+	
+	for (i=0;i<12;i++)
+		pc->c[i]=0;
+	pc->retries=0;
+}
+
+/*
+ *	idetape_swap_shorts converts a 16 bit number from little endian
+ *	to big endian format.
+ */
+ 
+unsigned short idetape_swap_short (unsigned short temp)
+
+{
+	union convert {
+		unsigned all	:16;
+		struct {
+			unsigned b1	:8;
+			unsigned b2	:8;
+		} b;
+	} original,converted;
+	
+	original.all=temp;
+	converted.b.b1=original.b.b2;
+	converted.b.b2=original.b.b1;
+	return (converted.all);
+}
+
+/*
+ *	idetape_swap_long converts from little endian to big endian format.
+ */
+ 
+unsigned long idetape_swap_long (unsigned long temp)
+
+{
+	union convert {
+		unsigned all	:32;
+		struct {
+			unsigned b1	:8;
+			unsigned b2	:8;
+			unsigned b3	:8;
+			unsigned b4	:8;
+		} b;
+	} original,converted;
+	
+	original.all=temp;
+	converted.b.b1=original.b.b4;
+	converted.b.b2=original.b.b3;
+	converted.b.b3=original.b.b2;
+	converted.b.b4=original.b.b1;
+	return (converted.all);
+}
+
+
+/*
+ *	idetape_next_pc_storage returns a pointer to a place in which we can
+ *	safely store a packet command, even though we intend to leave the
+ *	driver. A storage space for a maximum of IDETAPE_PC_STACK packet
+ *	commands is allocated at initialization time.
+ */
+ 
+idetape_packet_command_t *idetape_next_pc_storage (ide_drive_t *drive)
+
+{
+	idetape_tape_t *tape;
+	
+	tape=&(drive->tape);
+#if IDETAPE_DEBUG
+	printk ("ide-tape: pc_stack_index=%d\n",tape->pc_stack_index);
+#endif /* IDETAPE_DEBUG */
+	if (tape->pc_stack_index==IDETAPE_PC_STACK)
+		tape->pc_stack_index=0;
+	return (&(tape->pc_stack [tape->pc_stack_index++]));
+}
+
+/*
+ *	idetape_next_rq_storage is used along with idetape_next_pc_storage.
+ *	Since we queue packet commands in the request queue, we need to
+ *	allocate a request, along with the allocation of a packet command.
+ */
+ 
+/**************************************************************
+ *                                                            *
+ *  This should get fixed to use kmalloc(GFP_ATOMIC, ..)      *
+ *  followed later on by kfree().   -ml                       *
+ *                                                            *
+ **************************************************************/
+ 
+struct request *idetape_next_rq_storage (ide_drive_t *drive)
+
+{
+	idetape_tape_t *tape;
+	
+	tape=&(drive->tape);
+
+#if IDETAPE_DEBUG
+	printk ("ide-tape: rq_stack_index=%d\n",tape->rq_stack_index);
+#endif /* IDETAPE_DEBUG */
+	if (tape->rq_stack_index==IDETAPE_PC_STACK)
+		tape->rq_stack_index=0;
+	return (&(tape->rq_stack [tape->rq_stack_index++]));
+}
+
+/*
+ *	Block device interface functions
+ *
+ *	The default action is not to allow direct access to the block device
+ *	interface (-EBUSY will be returned on open).
+ */
+
+int idetape_blkdev_open (struct inode *inode, struct file *filp, ide_drive_t *drive)
+
+{
+#if IDETAPE_ALLOW_OPENING_BLOCK_DEVICE
+	return (0);
+#else
+	printk ("ide-tape: The block device interface should not be used.\n");
+	printk ("ide-tape: Use the character device interfaces\n");
+	printk ("ide-tape: /dev/ht0 and /dev/nht0 instead.\n");
+	printk ("ide-tape: (Run linux/drivers/block/MAKEDEV.ide to create them)\n");
+	printk ("ide-tape: Refusing open request.\n");
+	return (-EBUSY);
+#endif /* IDETAPE_ALLOW_OPENING_BLOCK_DEVICE */
+}
+
+void idetape_blkdev_release (struct inode *inode, struct file *filp, ide_drive_t *drive)
+
+{
+	return;
+}
+
+/*
+ *	Character device interface functions
+ */
+
+/*
+ *	lseek is currently not installed.
+ */
+ 
+int idetape_chrdev_lseek (struct inode *inode, struct file *file, off_t offset, int origin)
+
+{
+	ide_drive_t *drive;
+	
+#if IDETAPE_DEBUG
+	printk ("Reached idetape_chrdev_lseek\n");
+#endif /* IDETAPE_DEBUG */
+
+	drive=idetape_chrdev.drive;
+	if (idetape_position_tape (drive,offset) != 0) {
+		printk ("ide-tape: Rewinding tape failed\n");
+		return (-1);
+	}
+	
+	return (0);
+}
+
+/*
+ *	Our character device read / write functions.
+ *
+ *	The tape is optimized to maximize throughpot when it is transfering
+ *	an integral number of the "continous transfer limit", which is
+ *	a parameter of the specific tape (26 KB on my particular tape). The
+ *	resulting increase in performance should be dramatical. In the
+ *	character device read/write functions, we split the current
+ *	request to units of the above size, and handle the remaining bytes
+ *	in some other sub-functions.
+ *
+ *	In case the count number is not even an integral number of the tape
+ *	block size (usually 512 or 1024 bytes), we will pad the transfer with
+ *	zeroes (write) or read the entire block and return only the requested
+ *	bytes (but the tape will be in the "wrong" position). Do not supply
+ *	such a count value unless you are going to close the device right
+ *	after this request.
+ *
+ *	Again, for best results use an integral number of the tape's parameter
+ *	(which is displayed in the driver installation stage). I will soon
+ *	add an ioctl to get this important parameter.
+ */
+
+/*
+ *	Our character device read function.
+ */
+
+int idetape_chrdev_read (struct inode *inode, struct file *file, char *buf, int count)
+
+{
+	ide_drive_t *drive;
+	idetape_tape_t *tape;
+	int blocks,remainder,retval,ctl_bytes;
+	char *buf_ptr;
+	unsigned long previous_block_address,actually_read;
+
+#if IDETAPE_DEBUG
+	printk ("Reached idetape_chrdev_read\n");
+#endif /* IDETAPE_DEBUG */
+
+	drive=idetape_chrdev.drive;
+	tape=&(drive->tape);
+	tape->last_dt_was_write=0;
+
+	if (count==0)
+		return (0);
+
+	actually_read=0;
+	buf_ptr=buf;
+	ctl_bytes=tape->capabilities.ctl*tape->tape_block_size;
+	blocks=count/ctl_bytes;
+	remainder=count%ctl_bytes;
+	
+	while (blocks) {
+#if IDETAPE_DEBUG
+		printk ("Adding a READ request to the block device request queue\n");
+#endif /* IDETAPE_DEBUG */
+		previous_block_address=tape->block_address;
+		retval=idetape_queue_rw_tail (drive,IDETAPE_READ_REQUEST,tape->capabilities.ctl,tape->data_buffer);
+		actually_read+=tape->tape_block_size*(tape->block_address-previous_block_address);
+
+		if (retval) {
+			printk ("ide-tape: Error occured while reading\n");
+			return (actually_read);
+		}
+#if IDETAPE_DEBUG
+	printk ("Copying %d bytes to the user space memory\n",ctl_bytes);
+#endif /* IDETAPE_DEBUG */
+
+		memcpy_tofs (buf_ptr,tape->data_buffer,ctl_bytes);
+		buf_ptr+=ctl_bytes;
+		blocks--;
+	}
+	if (remainder)
+		return (actually_read+idetape_chrdev_read_remainder (inode,file,buf_ptr,remainder));
+	else
+		return (actually_read);		
+}
+ 
+int idetape_chrdev_read_remainder (struct inode *inode, struct file *file, char *buf, int count)
+
+{
+	ide_drive_t *drive;
+	idetape_tape_t *tape;
+	int blocks,remainder,retval;
+	unsigned long previous_block_address,actually_read;
+
+#if IDETAPE_DEBUG
+	printk ("Reached idetape_chrdev_read_remainder\n");
+#endif /* IDETAPE_DEBUG */
+
+	drive=idetape_chrdev.drive;
+	tape=&(drive->tape);
+
+	tape->last_dt_was_write=0;
+
+	if (count==0)
+		return (0);
+
+
+	blocks=count/512;
+	remainder=count%512;
+	if (remainder) {
+#if IDETAPE_DEBUG
+	printk ("ide-tape: Padding read to block boundary\n");
+#endif /* IDETAPE_DEBUG */
+		blocks++;
+	}
+#if IDETAPE_DEBUG
+	printk ("Adding a READ request to the block device request queue\n");
+#endif /* IDETAPE_DEBUG */
+	previous_block_address=tape->block_address;
+	retval=idetape_queue_rw_tail (drive,IDETAPE_READ_REQUEST,blocks,tape->data_buffer);
+	if (retval) {
+		printk ("ide-tape: Error occured while reading\n");
+		actually_read=512*(tape->block_address-previous_block_address);
+		if (actually_read > count)
+			actually_read=count;
+		if (actually_read != 0)
+			memcpy_tofs (buf,tape->data_buffer,actually_read);
+		return (actually_read);
+	}
+#if IDETAPE_DEBUG
+	printk ("Copying %d bytes to the user space memory\n",count);
+#endif /* IDETAPE_DEBUG */
+	memcpy_tofs (buf,tape->data_buffer,count);
+	return (count);
+}
+
+int idetape_chrdev_write (struct inode *inode, struct file *file, const char *buf, int count)
+
+{
+	ide_drive_t *drive;
+	idetape_tape_t *tape;
+	int blocks,remainder,retval,ctl_bytes;
+	const char *buf_ptr;
+	unsigned long previous_block_address,actually_written;
+
+#if IDETAPE_DEBUG
+	printk ("Reached idetape_chrdev_write\n");
+#endif /* IDETAPE_DEBUG */
+
+	drive=idetape_chrdev.drive;
+	tape=&(drive->tape);
+	tape->last_dt_was_write=1;
+
+	if (count==0)
+		return (0);
+
+	actually_written=0;
+	buf_ptr=buf;
+	ctl_bytes=tape->capabilities.ctl*tape->tape_block_size;
+	blocks=count/ctl_bytes;
+	remainder=count%ctl_bytes;
+
+	while (blocks) {
+#if IDETAPE_DEBUG
+		printk ("Copying %d bytes from the user space memory\n",ctl_bytes);
+#endif /* IDETAPE_DEBUG */
+		memcpy_fromfs (tape->data_buffer,buf_ptr,ctl_bytes);
+		buf_ptr+=ctl_bytes;
+#if IDETAPE_DEBUG
+		printk ("Adding a WRITE request to the block device request queue\n");
+#endif /* IDETAPE_DEBUG */
+		previous_block_address=tape->block_address;
+		retval=idetape_queue_rw_tail (drive,IDETAPE_WRITE_REQUEST,tape->capabilities.ctl,tape->data_buffer);
+		actually_written+=tape->tape_block_size*(tape->block_address-previous_block_address);
+
+		if (retval) {
+			printk ("ide-tape: Error occured while writing\n");
+			return (actually_written);
+		}
+		blocks--;
+	}
+	if (remainder)
+		return (actually_written+idetape_chrdev_write_remainder (inode,file,buf_ptr,remainder));
+	else
+		return (actually_written);		
+}
+
+int idetape_chrdev_write_remainder (struct inode *inode, struct file *file, const char *buf, int count)
+
+{
+	ide_drive_t *drive;
+	idetape_tape_t *tape;
+	int blocks,remainder,retval;
+	char *ptr;
+	unsigned long previous_block_address,actually_written;
+
+#if IDETAPE_DEBUG
+	printk ("Reached idetape_chrdev_write_remainder\n");
+#endif /* IDETAPE_DEBUG */
+		
+	drive=idetape_chrdev.drive;
+	tape=&(drive->tape);
+
+	blocks=count/512;
+	remainder=count%512;
+	if (remainder)
+		blocks++;
+#if IDETAPE_DEBUG
+	printk ("Copying %d bytes from the user space memory\n",count);
+#endif /* IDETAPE_DEBUG */
+
+	memcpy_fromfs (tape->data_buffer,buf,count);
+	if (remainder) {
+#if IDETAPE_DEBUG
+	printk ("ide-tape: Padding written data to block boundary\n");
+#endif /* IDETAPE_DEBUG */
+		ptr=tape->data_buffer+(blocks-1)*512;
+		memset (ptr,0,remainder);
+	}
+#if IDETAPE_DEBUG
+	printk ("Adding a WRITE request to the block device request queue\n");
+#endif /* IDETAPE_DEBUG */
+
+	previous_block_address=tape->block_address;
+	retval=idetape_queue_rw_tail (drive,IDETAPE_WRITE_REQUEST,blocks,tape->data_buffer);
+	if (retval) {
+		printk ("ide-tape: Error occured while writing\n");
+		actually_written=512*(tape->block_address-previous_block_address);
+		if (actually_written > count)
+			actually_written=count;
+		return (actually_written);
+	}
+	return (count);
+}
+
+/*
+ *	Our character device ioctls.
+ *
+ *	General mtio.h magnetic io commands are supported here, and not in
+ *	the correspoding block interface.
+ *
+ *	Our own ide-tape ioctls are supported on both interfaces.
+ */
+
+int idetape_chrdev_ioctl (struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+
+{
+	struct mtop mtop;
+	ide_drive_t *drive;
+	int retval;
+
+#if IDETAPE_DEBUG
+	printk ("Reached idetape_chrdev_ioctl, cmd=%u\n",cmd);
+#endif
+
+	drive=idetape_chrdev.drive;
+
+	switch (cmd) {
+		case MTIOCTOP:
+			retval=verify_area (VERIFY_READ,(char *) arg,sizeof (struct mtop));
+			if (retval) return (retval);
+			memcpy_fromfs ((char *) &mtop, (char *) arg, sizeof (struct mtop));
+			return (idetape_mtioctop (drive,mtop.mt_op,mtop.mt_count));
+		default:
+			return (idetape_blkdev_ioctl (drive,inode,file,cmd,arg));
+	}
+}
+
+/*
+ *	idetape_mtioctop is called from idetape_chrdev_ioctl when
+ *	the general mtio MTIOCTOP ioctl is requested.
+ *
+ *	We currently support the following mtio.h operations:
+ *
+ *	MTFSF	-	Space over mt_count filemarks in the positive direction.
+ *			The tape is positioned after the last spaced filemark.
+ *
+ *	MTFSFM	-	Same as MTFSF, but the tape is positioned before the
+ *			last filemark.
+ *
+ *	MTBSF	-	Steps background over mt_count filemarks, tape is
+ *			positioned before the last filemark.
+ *
+ *	MTBSFM	-	Like MTBSF, only tape is positioned after the last filemark.
+ *
+ *	MTWEOF	-	Writes mt_count filemarks. Tape is positioned after
+ *			the last written filemark.
+ *
+ *	MTREW	-	Rewindes tape.
+ *
+ *	MTNOP	-	Flushes tape buffers.
+ *
+ *	MTEOM	-	Moves to the end of recorded data.
+ *
+ *	MTERASE	-	Erases tape.
+ *
+ *	The following commands are currently not supported:
+ *
+ *	MTFSR, MTBSR, MTFSS, MTBSS, MTWSM, MTOFFL, MTRETEN, MTSEEK, MTSETBLK,
+ *	MTSETDENSITY, MTSETDRVBUFFER, MT_ST_BOOLEANS, MT_ST_WRITE_THRESHOLD.
+ */
+ 
+int idetape_mtioctop (ide_drive_t *drive,short mt_op,int mt_count)
+
+{
+	int i,retval;
+	
+	idetape_packet_command_t pc;
+
+	pc.buffer=pc.temp_buffer;
+	pc.buffer_size=IDETAPE_TEMP_BUFFER_SIZE;
+	pc.current_position=pc.temp_buffer;
+
+	idetape_create_write_filemark_cmd (&pc,0);	/* Flush buffers */
+	retval=idetape_queue_pc_tail (drive,&pc);
+	if (retval) return (retval);
+
+	switch (mt_op) {
+		case MTFSF:
+#if IDETAPE_DEBUG
+			printk ("Handling MTFSF command\n");
+#endif /* IDETAPE_DEBUG */
+			idetape_create_space_cmd (&pc,mt_count,IDETAPE_SPACE_OVER_FILEMARK);
+			return (idetape_queue_pc_tail (drive,&pc));
+		case MTFSFM:
+#if IDETAPE_DEBUG
+			printk ("Handling MTFSFM command\n");
+#endif /* IDETAPE_DEBUG */
+			retval=idetape_mtioctop (drive,MTFSF,mt_count);
+			if (retval) return (retval);
+			return (idetape_mtioctop (drive,MTBSF,1));
+		case MTBSF:
+#if IDETAPE_DEBUG
+			printk ("Handling MTBSF command\n");
+#endif /* IDETAPE_DEBUG */
+			idetape_create_space_cmd (&pc,-mt_count,IDETAPE_SPACE_OVER_FILEMARK);
+			return (idetape_queue_pc_tail (drive,&pc));
+		case MTBSFM:
+#if IDETAPE_DEBUG
+			printk ("Handling MTBSFM command\n");
+#endif /* IDETAPE_DEBUG */
+			retval=idetape_mtioctop (drive,MTBSF,mt_count);
+			if (retval) return (retval);
+			return (idetape_mtioctop (drive,MTFSF,1));
+		case MTWEOF:
+#if IDETAPE_DEBUG
+			printk ("Handling MTWEOF command\n");
+#endif /* IDETAPE_DEBUG */
+		
+			for (i=0;i<mt_count;i++) {
+				idetape_create_write_filemark_cmd (&pc,1);
+				retval=idetape_queue_pc_tail (drive,&pc);
+				if (retval) return (retval);
+			}
+			return (0);
+		case MTREW:
+#if IDETAPE_DEBUG
+			printk ("Handling MTREW command\n");
+#endif /* IDETAPE_DEBUG */
+			return (idetape_rewind_tape (drive));
+		case MTNOP:
+#if IDETAPE_DEBUG
+			printk ("Handling MTNOP command\n");
+#endif /* IDETAPE_DEBUG */
+			idetape_create_write_filemark_cmd (&pc,0);
+			return (idetape_queue_pc_tail (drive,&pc));
+		case MTEOM:
+#if IDETAPE_DEBUG
+			printk ("Handling MTEOM command\n");
+#endif /* IDETAPE_DEBUG */
+		
+			idetape_create_space_cmd (&pc,0,IDETAPE_SPACE_TO_EOD);
+			return (idetape_queue_pc_tail (drive,&pc));
+		case MTERASE:
+#if IDETAPE_DEBUG
+			printk ("Handling MTERASE command\n");
+#endif /* IDETAPE_DEBUG */
+			retval=idetape_position_tape (drive,0);
+			if (retval) return (retval);
+			idetape_create_erase_cmd (&pc);
+			return (idetape_queue_pc_tail (drive,&pc));
+		default:
+			printk ("ide-tape: MTIO operation %d not supported\n",mt_op);
+			return (-EIO);
+	}
+}
+
+/*
+ *	Our character device open function.
+ */
+
+int idetape_chrdev_open (struct inode *inode, struct file *filp)
+
+{
+	ide_drive_t *drive;
+	idetape_tape_t *tape;
+	unsigned long flags;
+	unsigned int minor;
+		
+	save_flags (flags);
+	cli();
+
+#if IDETAPE_DEBUG
+	printk ("Reached idetape_chrdev_open\n");
+#endif /* IDETAPE_DEBUG */
+
+
+	drive=idetape_chrdev.drive;
+	tape=&(drive->tape);
+	minor=MINOR (inode->i_rdev);
+
+	if (minor!=0 && minor!=128) {		/* Currently supporting only one */
+		restore_flags (flags);		/* tape drive */
+		return (-ENXIO);
+	}
+
+	if (tape->busy) {
+		restore_flags (flags);		/* Allowing access only through one */
+		return (-EBUSY);		/* one file descriptor */
+	}
+
+	tape->busy=1;
+	restore_flags (flags);
+
+	if (!tape->block_address_valid) {
+		if (idetape_rewind_tape (drive)) {
+			printk ("ide-tape: Rewinding tape failed\n");
+			tape->busy=0;
+			return (-EIO);
+		}
+	}
+
+	tape->last_dt_was_write=0;
+	
+	return (0);
+}
+
+/*
+ *	Our character device release function.
+ */
+
+void idetape_chrdev_release (struct inode *inode, struct file *filp)
+
+{
+	ide_drive_t *drive;
+	idetape_tape_t *tape;
+	
+	unsigned int minor;
+	idetape_packet_command_t pc;
+	unsigned long flags;
+			
+#if IDETAPE_DEBUG
+	printk ("Reached idetape_chrdev_release\n");
+#endif /* IDETAPE_DEBUG */
+
+	drive=idetape_chrdev.drive;
+	tape=&(drive->tape);
+	minor=MINOR (inode->i_rdev);
+
+	if (tape->last_dt_was_write) {
+		idetape_create_write_filemark_cmd (&pc,1);	/* Write a filemark */
+		if (idetape_queue_pc_tail (drive,&pc)) {
+			printk ("ide-tape: Couldn't write a filemark\n");
+			/* ??? */
+		}
+	}
+	else {
+		idetape_create_write_filemark_cmd (&pc,0);	/* Flush buffers */
+		if (idetape_queue_pc_tail (drive,&pc)) {
+			printk ("ide-tape: Couldn't flush buffers\n");
+			/* ??? */
+		}
+	}
+
+	if (minor < 128) {
+		if (idetape_rewind_tape (drive)) {
+			printk ("ide-tape: Rewinding tape failed\n");
+			/* ??? */
+		}
+	}
+
+	save_flags (flags);
+	cli();
+	tape->busy=0;
+	restore_flags (flags);
+		
+	return;
+}
+
+/*
+ *	idetape_position_tape positions the tape to the requested block
+ *	using the LOCATE packet command. A READ POSITION command is then
+ *	issued to check where we are positioned.
+ *
+ *	Like all higher level operations, we queue the commands at the tail
+ *	of the request queue and wait for their completion.
+ *	
+ */
+ 
+int idetape_position_tape (ide_drive_t *drive,unsigned long block)
+
+{
+	int retval;
+	idetape_packet_command_t pc;
+
+	idetape_create_locate_cmd (&pc,block,0);
+	retval=idetape_queue_pc_tail (drive,&pc);
+	if (retval!=0) return (retval);
+			
+	idetape_create_read_position_cmd (&pc);
+	pc.buffer=pc.temp_buffer;
+	pc.buffer_size=IDETAPE_TEMP_BUFFER_SIZE;
+	pc.current_position=pc.temp_buffer;
+	return (idetape_queue_pc_tail (drive,&pc));
+}
+
+/*
+ *	Rewinds the tape to the Begining Of the current Partition (BOP).
+ *
+ *	We currently support only one partition.
+ */ 
+
+int idetape_rewind_tape (ide_drive_t *drive)
+
+{
+	int retval;
+	idetape_packet_command_t pc;
+#if IDETAPE_DEBUG
+	printk ("Reached idetape_rewind_tape\n");
+#endif /* IDETAPE_DEBUG */	
+
+	idetape_create_write_filemark_cmd (&pc,0);	/* Flush buffers */
+	retval=idetape_queue_pc_tail (drive,&pc);
+	if (retval) return (retval);
+	
+	idetape_create_rewind_cmd (&pc);
+	retval=idetape_queue_pc_tail (drive,&pc);
+	if (retval) return (retval);
+			
+	idetape_create_read_position_cmd (&pc);
+	pc.buffer=pc.temp_buffer;
+	pc.buffer_size=IDETAPE_TEMP_BUFFER_SIZE;
+	pc.current_position=pc.temp_buffer;
+	return (idetape_queue_pc_tail (drive,&pc));
+}
diff -u --recursive --new-file v1.3.45/linux/drivers/block/ide-tape.h linux/drivers/block/ide-tape.h
--- v1.3.45/linux/drivers/block/ide-tape.h	Thu Jan  1 02:00:00 1970
+++ linux/drivers/block/ide-tape.h	Mon Dec 11 08:49:54 1995
@@ -0,0 +1,327 @@
+/*
+ * linux/drivers/block/ide-tape.h	Version 1.0 - ALPHA	Dec  3, 1995
+ *
+ * Copyright (C) 1995 Gadi Oxman <tgud@tochnapc2.technion.ac.il>
+ */
+
+/*
+ * Include file for the IDE ATAPI streaming tape driver.
+ *
+ * This file contains various ide-tape related structures and function
+ * prototypes which are already used in ide.h.
+ *
+ * The various compile time options are described below.
+ */
+
+#ifndef IDETAPE_H
+#define IDETAPE_H 
+
+/**************************** Tunable parameters *****************************/
+
+/*
+ *	Setting IDETAPE_DEBUG to 1 will:
+ *
+ *		1.	Generally log all driver actions.
+ *		2.	Enable self-sanity checks in some places.
+ *
+ *	Use IDETAPE_DEBUG when encountering a problem with the driver.
+ *
+ *	Setting IDETAPE_DEBUG to 0 will restore normal operation mode:
+ *
+ *		1.	Disable logging normal successful operations.
+ *		2.	Disable self-sanity checks.
+ *		3.	Errors will still be logged, of course.
+ */
+ 
+#define	IDETAPE_DEBUG		0
+
+/*
+ *	After each failed packet command we issue a request sense command
+ *	and retry the packet command IDETAPE_MAX_PC_RETRIES times.
+ *
+ *	Setting IDETAPE_MAX_PC_RETRIES to 0 will disable retries.
+ */
+
+#define	IDETAPE_MAX_PC_RETRIES	2
+
+/*
+ *	In case the tape is not at the requested block, we re-position the
+ *	tape. Repeat the procedure for IDETAPE_LOCATE_RETRIES times before
+ *	we give up and abort the request. Note that this should not usually
+ *	happen when using only the character device interface.
+ */
+
+#define	IDETAPE_LOCATE_RETRIES	1
+
+/*
+ *	With each packet command, we allocate a buffer of
+ *	IDETAPE_TEMP_BUFFER_SIZE bytes. This is used for several packet
+ *	commands (Not for READ/WRITE commands).
+ *
+ *	The default below is too high - We should be using around 100 bytes
+ *	typically, but I didn't check all the cases, so I rather be on the
+ *	safe size.
+ */
+ 
+#define	IDETAPE_TEMP_BUFFER_SIZE 256
+
+/*
+ *	In various places in the driver, we need to allocate storage
+ *	for packet commands and requests, which will remain valid while
+ *	we leave the driver to wait for an interrupt or a timeout event.
+ *
+ *	In the corresponding ide_drive_t structure, we pre-allocate storage
+ *	for IDETAPE_PC_STACK packet commands and requests. This storage is
+ *	used as a circular array - Each time we reach the last entry, we
+ *	warp around to the first.
+ *
+ *	It is crucial that we have enough entries for the maximum number
+ *	of packet commands / sub-requests which we need to allocate during
+ *	the handling of a specific request.
+ *
+ *	Follows a worse case calculation of the required storage, with a
+ *	large safety margin. Hopefully. :-)
+ */
+
+#define	IDETAPE_PC_STACK	10+\
+				IDETAPE_MAX_PC_RETRIES+\
+				3*IDETAPE_LOCATE_RETRIES*IDETAPE_MAX_PC_RETRIES
+/*
+ *	Media access packet command (like the LOCATE command) have immediate
+ *	status with a delayed (and usually long) execution. The tape doesn't
+ *	issue an interrupt when the command is actually complete (so that the
+ *	bus is freed to use the other IDE device on the same interface), so we
+ *	must for poll for this event.
+ *
+ *	We set a timer with polling frequency of 1/IDETAPE_DSC_MEDIA_ACCESS_FREQUENCY
+ *	in this case. We also poll for DSC *before* read/write commands. At
+ *	this time the DSC role is changed and instead of signalling command
+ *	completion, it will signal buffer availability. Since read/write
+ *	commands are fast in comparision to media access commands, the polling
+ *	frequency here should be much higher.
+ *
+ *	We will insist of reading DSC=1 for IDETAPE_DSC_COUNT times in a row,
+ *	to accommodate for random fluctuations in the sampling of DSC.
+ *	We will also set IDETAPE_DSC_POLLING_FREQUENCY to a rather low
+ *	frequency which besides freeing the CPU and the bus will let
+ *	random fluctuations a time to settle down.
+ *
+ *	We also set a timeout for the timer, in case something goes wrong.
+ *	The timeout should be longer then the maximum execution time of a
+ *	tape operation. I still have to measure exactly how much time does
+ *	it take to space over a far filemark, etc. It seemed that 15 minutes
+ *	was way too low, so I am meanwhile setting it to a rather large
+ *	timeout - 2 Hours.
+ *
+ *	Once we pass a threshold, the polling frequency will change to
+ *	a slow frequency: On relatively fast operations, there is a point
+ *	in polling fast, but if we sense that the operation is taking too
+ *	much time, we will poll at a lower frequency.
+ */
+
+#define	IDETAPE_DSC_FAST_MEDIA_ACCESS_FREQUENCY	1*HZ		/* 1 second */
+#define	IDETAPE_FAST_SLOW_THRESHOLD		5*60*HZ		/* 5 minutes */
+#define IDETAPE_DSC_SLOW_MEDIA_ACCESS_FREQUENCY	60*HZ		/* 1 minute */
+#define	IDETAPE_DSC_READ_WRITE_FREQUENCY	HZ/20		/* 50 msec */
+#define	IDETAPE_DSC_TIMEOUT			2*60*60*HZ	/* 2 hours */
+#define IDETAPE_DSC_COUNT			1		/* Assume no DSC fluctuations */
+
+/*
+ *	As explained in many places through the code, we provide both a block
+ *	device and a character device interface to the tape. The block device
+ *	interface is needed for compatibility with ide.c. The character device
+ *	interface is the higher level of the driver, and passes requests
+ *	to the lower part of the driver which interfaces with ide.c.
+ *	Using the block device interface, we can bypass this high level
+ *	of the driver, talking directly with the lower level part.
+ *
+ *	It is intended that the character device interface will be used by
+ *	the user. To prevent mistakes in this regard, opening of the block
+ *	device interface will be refused if ALLOW_OPENING_BLOCK_DEVICE is 0.
+ *
+ *	Do not change the following parameter unless you are developing
+ *	the driver itself.
+ */
+ 
+#define IDETAPE_ALLOW_OPENING_BLOCK_DEVICE	0
+
+/*************************** End of tunable parameters ***********************/
+
+/*
+ *	Definitions which are already needed in ide.h
+ */
+ 
+/*
+ *	The following is currently not used.
+ */
+
+typedef enum {no_excess_data,excess_data_read,excess_data_write} excess_data_status_t;
+
+ 
+struct ide_drive_s;				/* Forward declaration - Will be defined later in ide.h */
+typedef void (idetape_pc_completed_t)(struct ide_drive_s *);
+
+/*
+ *	Our view of a packet command.
+ */
+
+typedef struct idetape_packet_command_s {
+	byte c [12];				/* Actual packet bytes */
+	
+	byte retries;				/* On each retry, we increment retries */
+	byte error;				/* Set when an error occured */
+	byte active;				/* Set when a packet command is in progress */
+	byte wait_for_dsc;			/* 1 When polling for DSC */
+	byte dsc_count;		
+	unsigned long request_transfer;		/* Bytes to transfer */
+	unsigned long actually_transferred; 	/* Bytes actually transferred */
+	unsigned long buffer_size;		/* Size of our data buffer */
+	byte *buffer;				/* Data buffer */
+	byte *current_position;			/* Pointer into the above buffer */
+	byte writing;				/* Data direction */		
+	idetape_pc_completed_t *callback; 	/* Called when this packet command is completed */
+	byte temp_buffer [IDETAPE_TEMP_BUFFER_SIZE];	/* Temporary buffer */
+} idetape_packet_command_t;
+
+/*
+ *	Capabilities and Mechanical Status Page
+ */
+
+typedef struct {
+	unsigned page_code	:6;	/* Page code - Should be 0x2a */
+	unsigned reserved1_67	:2;
+	byte page_length;		/* Page Length - Should be 0x12 */
+	byte reserved2;	
+	byte reserved3;	
+	unsigned ro		:1;	/* Read Only Mode */
+	unsigned reserved4_1234	:4;
+	unsigned sprev		:1;	/* Supports SPACE in the reverse direction */
+	unsigned reserved4_67	:2;
+	unsigned reserved5_012	:3;
+	unsigned efmt		:1;	/* Supports ERASE command initiated formatting */
+	unsigned reserved5_4	:1;
+	unsigned qfa		:1;	/* Supports the QFA two partition formats */
+	unsigned reserved5_67	:2;
+	unsigned lock		:1;	/* Supports locking the volume */
+	unsigned locked		:1;	/* The volume is locked */
+	unsigned prevent	:1;	/* The device defaults in the prevent state after power up */	
+	unsigned eject		:1;	/* The device can eject the volume */
+	unsigned reserved6_45	:2;	/* Reserved */	
+	unsigned ecc		:1;	/* Supports error correction */
+	unsigned cmprs		:1;	/* Supports data compression */
+	unsigned reserved7_0	:1;
+	unsigned blk512		:1;	/* Supports 512 bytes block size */
+	unsigned blk1024	:1;	/* Supports 1024 bytes block size */
+	unsigned reserved7_3_6	:4;
+	unsigned slowb		:1;	/* The device restricts the byte count for PIO */
+					/* transfers for slow buffer memory ??? */
+	unsigned short max_speed;	/* Maximum speed supported in KBps */
+	byte reserved10;
+	byte reserved11;
+	unsigned short ctl;		/* Continuous Transfer Limit in blocks */
+	unsigned short speed;		/* Current Speed, in KBps */
+	unsigned short buffer_size;	/* Buffer Size, in 512 bytes */
+	byte reserved18;
+	byte reserved19;
+} idetape_capabilities_page_t;
+
+/*
+ *	Most of our global data which we need to save even as we leave the
+ *	driver due to an interrupt or a timer event is stored in a variable
+ *	of type tape_info, defined below.
+ *
+ *	Additional global variables which provide the link between the
+ *	character device interface to this structure are defined in
+ *	ide-tape.c
+ */
+ 
+typedef struct {	
+
+	/*
+	 *	Since a typical character device operation requires more
+	 *	than one packet command, we provide here enough memory
+	 *	for the maximum of interconnected packet commands.
+	 *	The packet commands are stored in the circular array pc_stack.
+	 *	pc_stack_index points to the last used entry, and warps around
+	 *	to the start when we get to the last array entry.
+	 *
+	 *	pc points to the current processed packet command.
+	 *
+	 *	failed_pc points to the last failed packet command, or contains
+	 *	NULL if we do not need to retry any packet command. This is
+	 *	required since an additional packet command is needed before the
+	 *	retry, to get detailed information on what went wrong.
+      	 */
+
+	idetape_packet_command_t *pc;		/* Current packet command */
+	idetape_packet_command_t *failed_pc; 	/* Last failed packet command */
+	idetape_packet_command_t pc_stack [IDETAPE_PC_STACK]; /* Packet command stack */
+	byte pc_stack_index;			/* Next free packet command storage space */
+
+	/* 
+	 *	The Linux ide driver basically traverses the request lists
+	 *	of the ide block devices, finds the next request, completes
+	 *	it, and passes to the next one. This is done in ide_do_request.
+	 *
+	 *	In this regard, ide-tape.c is fully compatible with the rest of
+	 *	the ide driver - From the point of view of ide.c, we are just
+	 *	another ide block device which receives requests and completes
+	 *	them.
+	 *
+	 *	However, our requests usually don't originate in the buffer
+	 *	cache but rather in ide-tape.c itself. Here we provide safe
+	 *	storage for such requests.
+	 */
+
+	struct request rq_stack [IDETAPE_PC_STACK];
+	byte rq_stack_index;			/* We implement a circular array */
+
+	/*
+	 *	While polling for DSC we use postponed_rq to postpone the
+	 *	current request so that ide.c will be able to service
+	 *	pending requests on the other device.
+	 */
+
+	struct request *postponed_rq;
+	byte dsc_count;		
+	unsigned long dsc_polling_start;
+	struct timer_list dsc_timer;		/* Timer used to poll for dsc */
+	unsigned long dsc_polling_frequency;
+	unsigned long dsc_timeout;		/* Maximum waiting time */
+	byte dsc_received;
+
+	/* Position information */
+	
+	byte partition_num;			/* Currently not used */
+	unsigned long block_address;		/* Current block */
+	byte block_address_valid;		/* 0 When the tape position is unknown */
+						/* (To the tape or to us) */
+	unsigned long locate_to;		/* We want to reach this block, as a part */
+						/* of handling the current request */
+	byte locate_retries;			/* Each time, increase locate_retries */
+
+	unsigned long last_written_block;	/* Once writing started, we don't allow read */
+	byte last_written_valid;		/* access beyond the last written block */
+						/* ??? Should I remove this ? */
+
+	/* Last error information */
+	
+	byte sense_key,asc,ascq;
+
+	/* Character device operation */
+
+	unsigned char last_dt_was_write;	/* Last character device data transfer was a write */
+	byte busy;				/* Device already opened */	
+
+	/* Device information */
+	
+	unsigned short tape_block_size;
+	idetape_capabilities_page_t capabilities;	/* Capabilities and Mechanical Page */
+
+	/* Data buffer */
+	
+	char *data_buffer;
+
+} idetape_tape_t;
+
+#endif /* IDETAPE_H */
diff -u --recursive --new-file v1.3.45/linux/drivers/block/ide.c linux/drivers/block/ide.c
--- v1.3.45/linux/drivers/block/ide.c	Mon Nov 27 12:48:28 1995
+++ linux/drivers/block/ide.c	Mon Dec 11 08:49:54 1995
@@ -1,5 +1,5 @@
 /*
- *  linux/drivers/block/ide.c	Version 5.18  Nov 16, 1995
+ *  linux/drivers/block/ide.c	Version 5.22  Dec 10, 1995
  *
  *  Copyright (C) 1994, 1995  Linus Torvalds & authors (see below)
  */
@@ -170,6 +170,14 @@
  *  Version 5.18	new CMD640 code, moved to cmd640.c, #include'd for now
  *			new UMC8672 code, moved to umc8672.c, #include'd for now
  *			disallow turning on DMA when h/w not capable of DMA
+ *  Version 5.19	fix potential infinite timeout on resets
+ *			extend reset poll into a general purpose polling scheme
+ *			add atapi tape drive support from Gadi Oxman
+ *			simplify exit from _intr routines -- no IDE_DO_REQUEST
+ *  Version 5.20	leave current rq on blkdev request list during I/O
+ *			generalized ide_do_drive_cmd() for tape/cdrom driver use
+ *  Version 5.21	fix nasty cdrom/tape bug (ide_preempt was messed up)
+ *  Version 5.22	fix ide_xlate_1024() to work with/without drive->id
  *
  *  Driver compile-time options are in ide.h
  *
@@ -179,7 +187,6 @@
  *	- add ioctls to get/set interface timings on various interfaces
  *	- add Promise Caching controller support from peterd@pnd-pc.demon.co.uk
  *	- modify kernel to obtain BIOS geometry for drives on 2nd/3rd/4th i/f
- *	- find someone to work on IDE *tape drive* support
  */
 
 #undef REALLY_SLOW_IO		/* most systems can safely undef this */
@@ -246,10 +253,16 @@
 	return (t - i);
 }
 
-void ide_set_recovery_timer (ide_hwif_t *hwif)
+static void set_recovery_timer (ide_hwif_t *hwif)
 {
 	hwif->last_time = read_timer();
 }
+#define SET_RECOVERY_TIMER(drive) set_recovery_timer (drive)
+
+#else
+
+#define SET_RECOVERY_TIMER(drive)
+
 #endif /* DISK_RECOVERY_TIME */
 
 /*
@@ -391,6 +404,10 @@
  * Apparently, systems with multiple CMD640 chips may need something similar..
  *
  * This algorithm courtesy of malafoss@snakemail.hut.fi
+ *
+ * At least one user has reported that this code can confuse the floppy
+ * controller and/or driver -- perhaps this should be changed to use
+ * a read-modify-write sequence, so as not to disturb other bits in the reg?
  */
 
 void ide_hwif_select (ide_hwif_t *hwif)
@@ -419,15 +436,17 @@
  * timer is started to prevent us from waiting forever in case
  * something goes wrong (see the timer_expiry() handler later on).
  */
-void ide_set_handler (ide_drive_t *drive, ide_handler_t *handler)
+void ide_set_handler (ide_drive_t *drive, ide_handler_t *handler, unsigned int timeout)
 {
 	ide_hwgroup_t *hwgroup = HWGROUP(drive);
 #ifdef DEBUG
-	if (hwgroup->handler != NULL)
-		printk("%s: ide_set_handler: old handler not null\n", drive->name);
+	if (hwgroup->handler != NULL) {
+		printk("%s: ide_set_handler: handler not null; old=%p, new=%p\n",
+			drive->name, hwgroup->handler, handler);
+	}
 #endif
 	hwgroup->handler       = handler;
-	hwgroup->timer.expires = jiffies + WAIT_CMD;
+	hwgroup->timer.expires = jiffies + timeout;
 	add_timer(&(hwgroup->timer));
 }
 
@@ -468,8 +487,8 @@
 
 	if (!drive->present)
 		return 0;
-	if (drive->media != disk)
-		return 0x1fffff;	/* cdrom */
+	if (drive->media != ide_disk)
+		return 0x7fffffff;	/* cdrom or tape */
 	/* Determine capacity, and use LBA if the drive properly supports it */
 	if (id != NULL && (id->capability & 2) && lba_capacity_is_ok(id)) {
 		drive->select.b.lba = 1;
@@ -493,11 +512,15 @@
 	for (unit = 0; unit < gd->nr_real; ++unit) {
 		ide_drive_t *drive = &hwif->drives[unit];
 #ifdef CONFIG_BLK_DEV_IDECD
-		if (drive->present && drive->media == cdrom)
+		if (drive->present && drive->media == ide_cdrom)
 			ide_cdrom_setup(drive);
 #endif /* CONFIG_BLK_DEV_IDECD */
+#ifdef CONFIG_BLK_DEV_IDETAPE
+		if (drive->present && drive->media == ide_tape)
+			idetape_setup(drive);
+#endif /* CONFIG_BLK_DEV_IDETAPE */
 		drive->part[0].nr_sects = current_capacity(drive);
-		if (!drive->present || drive->media != disk) {
+		if (!drive->present || drive->media != ide_disk) {
 			drive->part[0].start_sect = -1; /* skip partition check */
 		}
 	}
@@ -554,95 +577,54 @@
 	hwif->gd = gendisk_head = gd;
 }
 
-static void unexpected_intr (int, ide_hwgroup_t *);
-/*
- * reset_ihandler() is a dummy interrupt handler which we install during
- * an ide interface reset operation.  This prevents other devices in the
- * same hwgroup from being serviced while we play around with shared resources.
- * If it ever gets invoked, we call unexpected_intr(), which treats the event
- * the same as a timer_expiry().
- */
-static void reset_ihandler (ide_drive_t *drive)
-{
-	unsigned long flags;
-
-	save_flags(flags);
-	cli();
-	unexpected_intr (HWIF(drive)->irq, HWGROUP(drive));
-	restore_flags(flags);
-}
-
-/*
- * start_reset_timer() sets up a timer event for 50ms in the future,
- * to poll for completion of an ide reset operation (no interrupt to help us).
- */
-static void start_reset_timer (ide_hwif_t *hwif)
-{
-	ide_hwgroup_t *hwgroup = hwif->hwgroup;
+static void do_reset1 (ide_drive_t *, int);		/* needed below */
 
-	hwgroup->reset_timeout = jiffies + WAIT_WORSTCASE; /* max waiting time */
-	hwgroup->handler = &reset_ihandler;		/* dummy irq handler */
-	hwgroup->timer.expires = jiffies + (HZ/20);	/* polling interval */
-	add_timer(&(hwgroup->timer));
-}
-
-#ifdef CONFIG_BLK_DEV_IDECD
+#ifdef CONFIG_BLK_DEV_IDEATAPI
 /*
- * atapi_reset_handler() gets invoked to poll the interface for completion every 50ms
+ * atapi_reset_pollfunc() gets invoked to poll the interface for completion every 50ms
  * during an atapi drive reset operation. If the drive has not yet responded,
  * and we have not yet hit our maximum waiting time, then the timer is restarted
  * for another 50ms.
- *
- * Returns 1 if waiting for another 50ms, returns 0 otherwise.
  */
-static int atapi_reset_handler (ide_hwgroup_t *hwgroup)
+static void atapi_reset_pollfunc (ide_drive_t *drive)
 {
-	ide_hwif_t *hwif = hwgroup->hwif;
-	ide_drive_t *drive = hwgroup->drive;
+	ide_hwgroup_t *hwgroup = HWGROUP(drive);
 	byte stat;
 
 	OUT_BYTE (drive->select.all, IDE_SELECT_REG);
 	udelay (10);
 
-	if (!OK_STAT(stat=GET_STAT(), 0, BUSY_STAT)) {
-		if (jiffies < hwgroup->reset_timeout) {
-			start_reset_timer (hwif);
-			return 1;
+	if (OK_STAT(stat=GET_STAT(), 0, BUSY_STAT)) {
+		printk("%s: ATAPI reset complete\n", drive->name);
+	} else {
+		if (jiffies < hwgroup->poll_timeout) {
+			ide_set_handler (drive, &atapi_reset_pollfunc, HZ/20);
+			return;	/* continue polling */
 		}
+		hwgroup->poll_timeout = 0;	/* end of polling */
 		printk("%s: ATAPI reset timed-out, status=0x%02x\n", drive->name, stat);
-		return ide_do_reset (drive);	/* do it the old fashioned way */
+		do_reset1 (drive, 1);	/* do it the old fashioned way */
 	}
-	hwgroup->doing_atapi_reset = 0;
-	hwgroup->handler = NULL;	/* allow new requests to be processed */
-	hwgroup->reset_timeout = 0;	/* signal end of ide reset operation */
-	printk("%s: ATAPI reset complete\n", drive->name);
-	return 0;
+	hwgroup->poll_timeout = 0;	/* done polling */
 }
-#endif /* CONFIG_BLK_DEV_IDECD */
+#endif /* CONFIG_BLK_DEV_IDEATAPI */
 
 /*
- * reset_handler() gets invoked to poll the interface for completion every 50ms
+ * reset_pollfunc() gets invoked to poll the interface for completion every 50ms
  * during an ide reset operation. If the drives have not yet responded,
  * and we have not yet hit our maximum waiting time, then the timer is restarted
  * for another 50ms.
- *
- * Returns 1 if waiting for another 50ms, returns 0 otherwise.
  */
-static int reset_handler (ide_hwgroup_t *hwgroup)
+static void reset_pollfunc (ide_drive_t *drive)
 {
-	ide_hwif_t *hwif = hwgroup->hwif;
-	ide_drive_t *drive = hwgroup->drive;
+	ide_hwgroup_t *hwgroup = HWGROUP(drive);
+	ide_hwif_t *hwif = HWIF(drive);
 	byte tmp;
 
-#ifdef CONFIG_BLK_DEV_IDECD
-	if (hwgroup->doing_atapi_reset)
-		return atapi_reset_handler(hwgroup);
-#endif /* CONFIG_BLK_DEV_IDECD */
-
 	if (!OK_STAT(tmp=GET_STAT(), 0, BUSY_STAT)) {
-		if (jiffies < hwgroup->reset_timeout) {
-			start_reset_timer (hwif);
-			return 1;
+		if (jiffies < hwgroup->poll_timeout) {
+			ide_set_handler (drive, &reset_pollfunc, HZ/20);
+			return;	/* continue polling */
 		}
 		printk("%s: reset timed-out, status=0x%02x\n", hwif->name, tmp);
 	} else  {
@@ -669,13 +651,11 @@
 			printk("\n");
 		}
 	}
-	hwgroup->handler = NULL;	/* allow new requests to be processed */
-	hwgroup->reset_timeout = 0;	/* signal end of ide reset operation */
-	return 0;
+	hwgroup->poll_timeout = 0;	/* done polling */
 }
 
 /*
- * ide_do_reset() attempts to recover a confused drive by resetting it.
+ * do_reset1() attempts to recover a confused drive by resetting it.
  * Unfortunately, resetting a disk drive actually resets all devices on
  * the same interface, so it can really be thought of as resetting the
  * interface rather than resetting the drive.
@@ -689,7 +669,7 @@
  * (up to 30 seconds worstcase).  So, instead of busy-waiting here for it,
  * we set a timer to poll at 50ms intervals.
  */
-int ide_do_reset (ide_drive_t *drive)
+static void do_reset1 (ide_drive_t *drive, int  do_not_try_atapi)
 {
 	unsigned int unit;
 	unsigned long flags;
@@ -699,24 +679,22 @@
 	save_flags(flags);
 	cli();		/* Why ? */
 
-#ifdef CONFIG_BLK_DEV_IDECD
+#ifdef CONFIG_BLK_DEV_IDEATAPI
 	/* For an ATAPI device, first try an ATAPI SRST. */
-	if (drive->media == cdrom) {
-		if (!hwgroup->doing_atapi_reset) {
-			hwgroup->doing_atapi_reset = 1;
+	if (drive->media != ide_disk) {
+		if (!do_not_try_atapi) {
 			if (!drive->keep_settings)
 				drive->unmask = 0;
 			OUT_BYTE (drive->select.all, IDE_SELECT_REG);
 			udelay (20);
 			OUT_BYTE (WIN_SRST, IDE_COMMAND_REG);
-			hwgroup->reset_timeout = jiffies + WAIT_WORSTCASE;
-			start_reset_timer (hwif); /* begin periodic polling */
+			hwgroup->poll_timeout = jiffies + WAIT_WORSTCASE;
+			ide_set_handler (drive, &atapi_reset_pollfunc, HZ/20);
 			restore_flags (flags);
-			return 1;
+			return;
 		}
 	}
-	hwgroup->doing_atapi_reset = 0;
-#endif /* CONFIG_BLK_DEV_IDECD */
+#endif /* CONFIG_BLK_DEV_IDEATAPI */
 
 	/*
 	 * First, reset any device state data we were maintaining
@@ -749,33 +727,45 @@
 	OUT_BYTE(drive->ctl|6,IDE_CONTROL_REG);	/* set SRST and nIEN */
 	udelay(5);			/* more than enough time */
 	OUT_BYTE(drive->ctl|2,IDE_CONTROL_REG);	/* clear SRST, leave nIEN */
-	hwgroup->reset_timeout = jiffies + WAIT_WORSTCASE;
-	start_reset_timer (hwif);	/* begin periodic polling */
+	hwgroup->poll_timeout = jiffies + WAIT_WORSTCASE;
+	ide_set_handler (drive, &reset_pollfunc, HZ/20);
 #endif	/* OK_TO_RESET_CONTROLLER */
 
 	restore_flags (flags);
-	return OK_TO_RESET_CONTROLLER;	/* 1 = we are waiting, 0 = done */
 }
 
 /*
- * Clean up after success/failure of an explicit (ioctl) drive cmd
+ * ide_do_reset() is the entry point to the drive/interface reset code.
+ */
+void ide_do_reset (ide_drive_t *drive)
+{
+	do_reset1 (drive, 0);
+}
+
+/*
+ * Clean up after success/failure of an explicit drive cmd
  */
-static void end_drive_cmd (ide_drive_t *drive, byte stat, byte err)
+void ide_end_drive_cmd (ide_drive_t *drive, byte stat, byte err)
 {
 	unsigned long flags;
 	struct request *rq = HWGROUP(drive)->rq;
-	byte *args = (byte *) rq->buffer;
 
-	rq->errors = !OK_STAT(stat,READY_STAT,BAD_STAT);
-	if (args) {
-		args[0] = stat;
-		args[1] = err;
-		args[2] = IN_BYTE(IDE_NSECTOR_REG);
+	if (rq->cmd == IDE_DRIVE_CMD) {
+		byte *args = (byte *) rq->buffer;
+		rq->errors = !OK_STAT(stat,READY_STAT,BAD_STAT);
+		if (args) {
+			args[0] = stat;
+			args[1] = err;
+			args[2] = IN_BYTE(IDE_NSECTOR_REG);
+		}
 	}
 	save_flags(flags);
 	cli();
-	up(rq->sem);
+	blk_dev[MAJOR(rq->rq_dev)].current_request = rq->next;
 	HWGROUP(drive)->rq = NULL;
+	rq->rq_status = RQ_INACTIVE;
+	if (rq->sem != NULL)
+		up(rq->sem);
 	restore_flags(flags);
 }
 
@@ -791,7 +781,7 @@
 	sti();
 	printk("%s: %s: status=0x%02x", drive->name, msg, stat);
 #if FANCY_STATUS_DUMPS
-	if (drive->media == disk) {
+	if (drive->media == ide_disk) {
 		printk(" { ");
 		if (stat & BUSY_STAT)
 			printk("Busy ");
@@ -812,7 +802,7 @@
 		err = GET_ERR();
 		printk("%s: %s: error=0x%02x", drive->name, msg, err);
 #if FANCY_STATUS_DUMPS
-		if (drive->media == disk) {
+		if (drive->media == ide_disk) {
 			printk(" { ");
 			if (err & BBD_ERR)	printk("BadSector ");
 			if (err & ECC_ERR)	printk("UncorrectableError ");
@@ -868,28 +858,25 @@
 
 /*
  * ide_error() takes action based on the error returned by the controller.
- *
- * Returns 1 if an ide reset operation has been initiated, in which case
- * the caller MUST simply return from the driver (through however many levels).
- * Returns 0 otherwise.
  */
-int ide_error (ide_drive_t *drive, const char *msg, byte stat)
+void ide_error (ide_drive_t *drive, const char *msg, byte stat)
 {
 	struct request *rq;
 	byte err;
 
 	err = ide_dump_status(drive, msg, stat);
 	if ((rq = HWGROUP(drive)->rq) == NULL || drive == NULL)
-		return 0;
-	if (rq->cmd == IDE_DRIVE_CMD) {	/* never retry an explicit DRIVE_CMD */
-		end_drive_cmd(drive, stat, err);
-		return 0;
+		return;
+	if (rq->cmd != READ && rq->cmd != WRITE) { /* retry only "normal" i/o */
+		rq->errors = 1;
+		ide_end_drive_cmd(drive, stat, err);
+		return;
 	}
 	if (stat & BUSY_STAT) {		/* other bits are useless when BUSY */
 		rq->errors |= ERROR_RESET;
 	} else {
-		if (drive->media == disk && (stat & ERR_STAT)) {
-			/* err has different meaning on cdrom */
+		if (drive->media == ide_disk && (stat & ERR_STAT)) {
+			/* err has different meaning on cdrom and tape */
 			if (err & BBD_ERR)		/* retries won't help this! */
 				rq->errors = ERROR_MAX;
 			else if (err & TRK0_ERR)	/* help it find track zero */
@@ -906,7 +893,7 @@
 		drive->using_dma = 0;
 		printk("%s: DMA disabled\n", drive->name);
 		--rq->errors;
-		return 0;
+		return;
 	}
 #endif /* CONFIG_BLK_DEV_TRITON */
 	if (rq->errors >= ERROR_MAX)
@@ -914,12 +901,12 @@
 	else {
 		if ((rq->errors & ERROR_RESET) == ERROR_RESET) {
 			++rq->errors;
-			return ide_do_reset(drive);
+			ide_do_reset(drive);
+			return;
 		} else if ((rq->errors & ERROR_RECAL) == ERROR_RECAL)
 			drive->special.b.recalibrate = 1;
 		++rq->errors;
 	}
-	return 0;
 }
 
 /*
@@ -933,9 +920,7 @@
 	struct request *rq;
 
 	if (!OK_STAT(stat=GET_STAT(),DATA_READY,BAD_R_STAT)) {
-		sti();
-		if (!ide_error(drive, "read_intr", stat))
-			IDE_DO_REQUEST;
+		ide_error(drive, "read_intr", stat);
 		return;
 	}
 	msect = drive->mult_count;
@@ -962,10 +947,8 @@
 	if (i > 0) {
 		if (msect)
 			goto read_next;
-		ide_set_handler (drive, &read_intr);
-		return;
+		ide_set_handler (drive, &read_intr, WAIT_CMD);
 	}
-	IDE_DO_REQUEST;
 }
 
 /*
@@ -975,7 +958,8 @@
 {
 	byte stat;
 	int i;
-	struct request *rq = HWGROUP(drive)->rq;
+	ide_hwgroup_t *hwgroup = HWGROUP(drive);
+	struct request *rq = hwgroup->rq;
 
 	if (OK_STAT(stat=GET_STAT(),DRIVE_READY,drive->bad_wstat)) {
 #ifdef DEBUG
@@ -990,19 +974,15 @@
 			i = --rq->nr_sectors;
 			--rq->current_nr_sectors;
 			if (rq->current_nr_sectors <= 0)
-				ide_end_request(1, HWGROUP(drive));
+				ide_end_request(1, hwgroup);
 			if (i > 0) {
 				ide_output_data (drive, rq->buffer, SECTOR_WORDS);
-				ide_set_handler (drive, &write_intr);
-				return;
+				ide_set_handler (drive, &write_intr, WAIT_CMD);
 			}
-			IDE_DO_REQUEST;
 			return;
 		}
 	}
-	sti();
-	if (!ide_error(drive, "write_intr", stat))
-		IDE_DO_REQUEST;
+	ide_error(drive, "write_intr", stat);
 }
 
 /*
@@ -1049,30 +1029,28 @@
 {
 	byte stat;
 	int i;
-	struct request *rq = &HWGROUP(drive)->wrq;
+	ide_hwgroup_t *hwgroup = HWGROUP(drive);
+	struct request *rq = &hwgroup->wrq;
 
 	if (OK_STAT(stat=GET_STAT(),DRIVE_READY,drive->bad_wstat)) {
 		if (stat & DRQ_STAT) {
 			if (rq->nr_sectors) {
 				multwrite(drive);
-				ide_set_handler (drive, &multwrite_intr);
+				ide_set_handler (drive, &multwrite_intr, WAIT_CMD);
 				return;
 			}
 		} else {
 			if (!rq->nr_sectors) {	/* all done? */
-				rq = HWGROUP(drive)->rq;
+				rq = hwgroup->rq;
 				for (i = rq->nr_sectors; i > 0;){
 					i -= rq->current_nr_sectors;
-					ide_end_request(1, HWGROUP(drive));
+					ide_end_request(1, hwgroup);
 				}
-				IDE_DO_REQUEST;
 				return;
 			}
 		}
 	}
-	sti();
-	if (!ide_error(drive, "multwrite_intr", stat))
-		IDE_DO_REQUEST;
+	ide_error(drive, "multwrite_intr", stat);
 }
 
 /*
@@ -1081,7 +1059,7 @@
  */
 static void ide_cmd(ide_drive_t *drive, byte cmd, byte nsect, ide_handler_t *handler)
 {
-	ide_set_handler (drive, handler);
+	ide_set_handler (drive, handler, WAIT_CMD);
 	OUT_BYTE(drive->ctl,IDE_CONTROL_REG);
 	OUT_BYTE(nsect,IDE_NSECTOR_REG);
 	OUT_BYTE(cmd,IDE_COMMAND_REG);
@@ -1102,7 +1080,6 @@
 		drive->special.b.recalibrate = 1;
 		(void) ide_dump_status(drive, "set_multmode", stat);
 	}
-	IDE_DO_REQUEST;
 }
 
 /*
@@ -1114,9 +1091,7 @@
 
 	sti();
 	if (!OK_STAT(stat,READY_STAT,BAD_STAT))
-		if (ide_error(drive, "set_geometry_intr", stat))
-			return;
-	IDE_DO_REQUEST;
+		ide_error(drive, "set_geometry_intr", stat);
 }
 
 /*
@@ -1128,9 +1103,7 @@
 
 	sti();
 	if (!OK_STAT(stat,READY_STAT,BAD_STAT))
-		if (ide_error(drive, "recal_intr", stat))
-			return;
-	IDE_DO_REQUEST;
+		ide_error(drive, "recal_intr", stat);
 }
 
 /*
@@ -1142,10 +1115,9 @@
 
 	sti();
 	if (OK_STAT(stat,READY_STAT,BAD_STAT))
-		end_drive_cmd (drive, stat, GET_ERR());
-	else if (ide_error(drive, "drive_cmd", stat)) /* calls end_drive_cmd */
-		return;
-	IDE_DO_REQUEST;
+		ide_end_drive_cmd (drive, stat, GET_ERR());
+	else
+		ide_error(drive, "drive_cmd", stat); /* calls ide_end_drive_cmd */
 }
 
 /*
@@ -1161,7 +1133,7 @@
 #endif
 	if (s->b.set_geometry) {
 		s->b.set_geometry = 0;
-		if (drive->media == disk) {
+		if (drive->media == ide_disk) {
 			OUT_BYTE(drive->sect,IDE_SECTOR_REG);
 			OUT_BYTE(drive->cyl,IDE_LCYL_REG);
 			OUT_BYTE(drive->cyl>>8,IDE_HCYL_REG);
@@ -1170,12 +1142,12 @@
 		}
 	} else if (s->b.recalibrate) {
 		s->b.recalibrate = 0;
-		if (drive->media == disk) {
+		if (drive->media == ide_disk) {
 			ide_cmd(drive, WIN_RESTORE, drive->sect, &recal_intr);
 		}
 	} else if (s->b.set_multmode) {
 		s->b.set_multmode = 0;
-		if (drive->media == disk) {
+		if (drive->media == ide_disk) {
 			if (drive->id && drive->mult_req > drive->id->max_multsect)
 				drive->mult_req = drive->id->max_multsect;
 			ide_cmd(drive, WIN_SETMULT, drive->mult_req, &set_multmode_intr);
@@ -1191,23 +1163,24 @@
  * This routine busy-waits for the drive status to be not "busy".
  * It then checks the status for all of the "good" bits and none
  * of the "bad" bits, and if all is okay it returns 0.  All other
- * cases return 1 after invoking ide_error()
+ * cases return 1 after invoking ide_error() -- caller should just return.
  *
  * This routine should get fixed to not hog the cpu during extra long waits..
  * That could be done by busy-waiting for the first jiffy or two, and then
  * setting a timer to wake up at half second intervals thereafter,
- * until WAIT_WORSTCASE is achieved, before timing out.
+ * until timeout is achieved, before timing out.
  */
 int ide_wait_stat (ide_drive_t *drive, byte good, byte bad, unsigned long timeout)
 {
 	byte stat;
 	unsigned long flags;
+
 test:
 	udelay(1);	/* spec allows drive 400ns to change "BUSY" */
 	if (OK_STAT((stat = GET_STAT()), good, bad))
 		return 0;	/* fast exit for most frequent case */
 	if (!(stat & BUSY_STAT)) {
-		(void) ide_error(drive, "status error", stat);
+		ide_error(drive, "status error", stat);
 		return 1;
 	}
 
@@ -1222,7 +1195,7 @@
 	} while (jiffies <= timeout);
 
 	restore_flags(flags);
-	(void) ide_error(drive, "status timeout", GET_STAT());
+	ide_error(drive, "status timeout", GET_STAT());
 	return 1;
 }
 
@@ -1268,7 +1241,7 @@
 		if (drive->using_dma && !(HWIF(drive)->dmaproc(ide_dma_read, drive)))
 			return;
 #endif /* CONFIG_BLK_DEV_TRITON */
-		ide_set_handler(drive, &read_intr);
+		ide_set_handler(drive, &read_intr, WAIT_CMD);
 		OUT_BYTE(drive->mult_count ? WIN_MULTREAD : WIN_READ, io_base+IDE_COMMAND_OFFSET);
 		return;
 	}
@@ -1287,10 +1260,10 @@
 			cli();
 		if (drive->mult_count) {
 			HWGROUP(drive)->wrq = *rq; /* scratchpad */
-			ide_set_handler (drive, &multwrite_intr);
+			ide_set_handler (drive, &multwrite_intr, WAIT_CMD);
 			multwrite(drive);
 		} else {
-			ide_set_handler (drive, &write_intr);
+			ide_set_handler (drive, &write_intr, WAIT_CMD);
 			ide_output_data(drive, rq->buffer, SECTOR_WORDS);
 		}
 		return;
@@ -1311,7 +1284,7 @@
 #ifdef DEBUG
 			printk("%s: DRIVE_CMD (null)\n", drive->name);
 #endif
-			end_drive_cmd(drive, GET_STAT(), GET_ERR());
+			ide_end_drive_cmd(drive, GET_STAT(), GET_ERR());
 			return;
 		}
 	}
@@ -1330,7 +1303,7 @@
 
 	sti();
 #ifdef DEBUG
-	printk("%s: ide_do_request: current=0x%08lx\n", hwif->name, (unsigned long) rq);
+	printk("%s: do_request: current=0x%08lx\n", hwif->name, (unsigned long) rq);
 #endif
 	minor = MINOR(rq->rq_dev);
 	unit = minor >> PARTN_BITS;
@@ -1372,17 +1345,25 @@
 	if (ide_wait_stat(drive, drive->ready_stat, BUSY_STAT|DRQ_STAT, WAIT_READY)) {
 		printk("%s: drive not ready for command\n", drive->name);
 		return;
-
 	}
 	if (!drive->special.all) {
-#ifdef CONFIG_BLK_DEV_IDECD
+#ifdef CONFIG_BLK_DEV_IDEATAPI
 		switch (drive->media) {
-			case disk:
+			case ide_disk:
 				do_rw_disk (drive, rq, block);
 				return;
-			case cdrom:
+#ifdef CONFIG_BLK_DEV_IDECD
+			case ide_cdrom:
 				ide_do_rw_cdrom (drive, block);
 				return;
+#endif /* CONFIG_BLK_DEV_IDECD */
+
+#ifdef CONFIG_BLK_DEV_IDETAPE
+			case ide_tape:
+				idetape_do_request (drive, rq, block);
+				return;
+#endif /* CONFIG_BLK_DEV_IDETAPE */
+
 			default:
 				printk("%s: media type %d not supported\n",
 					drive->name, drive->media);
@@ -1391,7 +1372,7 @@
 #else
 		do_rw_disk (drive, rq, block); /* simpler and faster */
 		return;
-#endif;
+#endif /* CONFIG_BLK_DEV_IDEATAPI */;
 	}
 	do_special(drive);
 	return;
@@ -1425,16 +1406,14 @@
 		ide_hwif_t *hwif = hwgroup->hwif;
 		struct request *rq;
 		if ((rq = hwgroup->rq) == NULL) {
-			hwgroup->drive = NULL;	/* paranoia */
 			do {
 				rq = blk_dev[hwif->major].current_request;
 				if (rq != NULL && rq->rq_status != RQ_INACTIVE)
 					goto got_rq;
 			} while ((hwif = hwif->next) != hwgroup->hwif);
 			return;		/* no work left for this hwgroup */
-		got_rq:
-			blk_dev[hwif->major].current_request = rq->next;
 		}
+	got_rq:	
 		do_request(hwgroup->hwif = hwif, hwgroup->rq = rq);
 		cli();
 	} while (hwgroup->handler == NULL);
@@ -1495,19 +1474,21 @@
 	save_flags(flags);
 	cli();
 
-	if (hwgroup->reset_timeout != 0) { /* ide reset in progress? */
-		if (!reset_handler(hwgroup))
-			do_hwgroup_request (hwgroup);
+	if (hwgroup->poll_timeout != 0) { /* polling in progress? */
+		ide_handler_t *handler = hwgroup->handler;
+		hwgroup->handler = NULL;
+		handler(drive);
 	} else if (hwgroup->handler == NULL) {	 /* not waiting for anything? */
 		sti(); /* drive must have responded just as the timer expired */
 		printk("%s: marginal timeout\n", drive->name);
-	} else {				 /* drive not responding */
-		hwgroup->handler = NULL;
+	} else {
+		hwgroup->handler = NULL;	/* abort the operation */
 		if (hwgroup->hwif->dmaproc)
 			(void) hwgroup->hwif->dmaproc (ide_dma_abort, drive);
-		if (!ide_error(drive, "irq timeout", GET_STAT()))
-			do_hwgroup_request (hwgroup);
+		ide_error(drive, "irq timeout", GET_STAT());
 	}
+	if (hwgroup->handler == NULL)
+		do_hwgroup_request (hwgroup);
 	restore_flags(flags);
 }
 
@@ -1541,15 +1522,6 @@
 	ide_hwif_t *hwif = hwgroup->hwif;
 
 	/*
-	 * check for ide reset in progress
-	 */
-	if (hwgroup->reset_timeout != 0) {
-		if (!reset_handler(hwgroup))
-			do_hwgroup_request (hwgroup);
-		return;
-	}
-
-	/*
 	 * handle the unexpected interrupt
 	 */
 	do {
@@ -1586,6 +1558,11 @@
 		if (drive->unmask)
 			sti();
 		handler(drive);
+		cli();	/* this is necessary, as next rq may be different irq */
+		if (hwgroup->handler == NULL) {
+			SET_RECOVERY_TIMER(HWIF(drive));
+			ide_do_request(hwgroup);
+		}
 	} else {
 		unexpected_intr(irq, hwgroup);
 	}
@@ -1621,49 +1598,83 @@
 }
 
 /*
- * This function issues a specific IDE drive command onto the
- * tail of the request queue, and waits for it to be completed.
- * If arg is NULL, it goes through all the motions,
- * but without actually sending a command to the drive.
+ * This function is intended to be used prior to invoking ide_do_drive_cmd().
  */
-int ide_do_drive_cmd(kdev_t rdev, char *args)
+void ide_init_drive_cmd (struct request *rq)
+{
+	rq->buffer = NULL;
+	rq->cmd = IDE_DRIVE_CMD;
+	rq->sector = 0;
+	rq->nr_sectors = 0;
+	rq->current_nr_sectors = 0;
+	rq->sem = NULL;
+	rq->bh = NULL;
+	rq->bhtail = NULL;
+	rq->next = NULL;
+
+#if 0	/* these are done each time through ide_do_drive_cmd() */
+	rq->errors = 0;
+	rq->rq_status = RQ_ACTIVE;
+	rq->rq_dev = ????;
+#endif
+}
+
+/*
+ * This function issues a special IDE device request
+ * onto the request queue.
+ *
+ * If action is ide_wait, then then rq is queued at the end of
+ * the request queue, and the function sleeps until it has been
+ * processed.  This is for use when invoked from an ioctl handler.
+ *
+ * If action is ide_preempt, then the rq is queued at the head of
+ * the request queue, displacing the currently-being-processed
+ * request and this function returns immediately without waiting
+ * for the new rq to be completed.  This is VERY DANGEROUS, and is
+ * intended for careful use by the ATAPI tape/cdrom driver code.
+ *
+ * If action is ide_next, then the rq is queued immediately after
+ * the currently-being-processed-request (if any), and the function
+ * returns without waiting for the new rq to be completed.  As above,
+ * This is VERY DANGEROUS, and is intended for careful use by the
+ * ATAPI tape/cdrom driver code.
+ */
+int ide_do_drive_cmd (ide_drive_t *drive, struct request *rq, ide_action_t action)
 {
 	unsigned long flags;
-	unsigned int major = MAJOR(rdev);
-	struct request rq, *cur_rq;
-	struct blk_dev_struct *bdev;
+	unsigned int major = HWIF(drive)->major;
+	struct request *cur_rq;
+	struct blk_dev_struct *bdev = &blk_dev[major];
 	struct semaphore sem = MUTEX_LOCKED;
 
-	/* build up a special request, and add it to the queue */
-	rq.buffer = args;
-	rq.cmd = IDE_DRIVE_CMD;
-	rq.errors = 0;
-	rq.sector = 0;
-	rq.nr_sectors = 0;
-	rq.current_nr_sectors = 0;
-	rq.sem = &sem;
-	rq.bh = NULL;
-	rq.bhtail = NULL;
-	rq.next = NULL;
-	rq.rq_status = RQ_ACTIVE;
-	rq.rq_dev = rdev;
-	bdev = &blk_dev[major];
+	rq->errors = 0;
+	rq->rq_status = RQ_ACTIVE;
+	rq->rq_dev = MKDEV(major,(drive->select.b.unit)<<PARTN_BITS);
+	if (action == ide_wait)
+		rq->sem = &sem;
 
 	save_flags(flags);
 	cli();
 	cur_rq = bdev->current_request;
-	if (cur_rq == NULL) {			/* empty request list? */
-		bdev->current_request = &rq;	/* service ours immediately */
-		bdev->request_fn();
+
+	if (cur_rq == NULL || action == ide_preempt) {
+		rq->next = cur_rq;
+		bdev->current_request = rq;
+		HWGROUP(drive)->rq = NULL;
+		if (action != ide_preempt)
+			bdev->request_fn();
 	} else {
-		while (cur_rq->next != NULL)	/* find end of request list */
-			cur_rq = cur_rq->next;
-		cur_rq->next = &rq;		/* add rq to the end */
+		if (action == ide_wait) {
+			while (cur_rq->next != NULL)	/* find end of list */
+				cur_rq = cur_rq->next;
+		}
+		rq->next = cur_rq->next;
+		cur_rq->next = rq;
 	}
-
-	down(&sem);				/* wait for it to be serviced */
+	if (action == ide_wait)
+		down(&sem);	/* wait for it to be serviced */
 	restore_flags(flags);
-	return rq.errors ? -EIO : 0;		/* return -EIO if errors */
+	return rq->errors ? -EIO : 0;	/* return -EIO if errors */
 }
 
 static int ide_open(struct inode * inode, struct file * filp)
@@ -1680,13 +1691,20 @@
 	drive->usage++;
 	restore_flags(flags);
 #ifdef CONFIG_BLK_DEV_IDECD
-	if (drive->media == cdrom)
+	if (drive->media == ide_cdrom)
 		return ide_cdrom_open (inode, filp, drive);
 #endif	/* CONFIG_BLK_DEV_IDECD */
+#ifdef CONFIG_BLK_DEV_IDETAPE
+	if (drive->media == ide_tape)
+		return idetape_blkdev_open (inode, filp, drive);
+#endif	/* CONFIG_BLK_DEV_IDETAPE */
 	if (drive->removeable) {
 		byte door_lock[] = {WIN_DOORLOCK,0,0,0};
+		struct request rq;
 		check_disk_change(inode->i_rdev);
-		ide_do_drive_cmd(inode->i_rdev, door_lock);
+		ide_init_drive_cmd (&rq);
+		rq.buffer = door_lock;
+		return ide_do_drive_cmd(drive, &rq, ide_wait);
 	}
 	return 0;
 }
@@ -1703,14 +1721,24 @@
 		sync_dev(inode->i_rdev);
 		drive->usage--;
 #ifdef CONFIG_BLK_DEV_IDECD
-		if (drive->media == cdrom)
+		if (drive->media == ide_cdrom) {
 			ide_cdrom_release (inode, file, drive);
-		else
+			return;
+		}
 #endif	/* CONFIG_BLK_DEV_IDECD */
+#ifdef CONFIG_BLK_DEV_IDETAPE
+		if (drive->media == ide_tape) {
+			idetape_blkdev_release (inode, file, drive);
+			return;
+		}
+#endif	/* CONFIG_BLK_DEV_IDETAPE */
 		if (drive->removeable) {
 			byte door_unlock[] = {WIN_DOORUNLOCK,0,0,0};
+			struct request rq;
 			invalidate_buffers(inode->i_rdev);
-			ide_do_drive_cmd(inode->i_rdev, door_unlock);
+			ide_init_drive_cmd (&rq);
+			rq.buffer = door_unlock;
+			(void) ide_do_drive_cmd(drive, &rq, ide_wait);
 		}
 	}
 }
@@ -1755,7 +1783,7 @@
 	};
 
 	drive->part[0].nr_sects = current_capacity(drive);
-	if (drive->media == disk)
+	if (drive->media == ide_disk)
 		resetup_one_dev(HWIF(drive)->gd, drive->select.b.unit);
 
 	drive->busy = 0;
@@ -1782,14 +1810,16 @@
 	int err;
 	ide_drive_t *drive;
 	unsigned long flags;
+	struct request rq;
 
+	ide_init_drive_cmd (&rq);
 	if (!inode || !(inode->i_rdev))
 		return -EINVAL;
 	if ((drive = get_info_ptr(inode->i_rdev)) == NULL)
 		return -ENODEV;
 	switch (cmd) {
 		case HDIO_GETGEO:
-			if (!loc || drive->media != disk) return -EINVAL;
+			if (!loc || drive->media != ide_disk) return -EINVAL;
 			err = verify_area(VERIFY_WRITE, loc, sizeof(*loc));
 			if (err) return err;
 			put_user(drive->bios_head, (byte *) &loc->heads);
@@ -1848,7 +1878,7 @@
 			return write_fs_long(arg, drive->bad_wstat == BAD_R_STAT);
 
 		case HDIO_SET_DMA:
-			if (drive->media != disk)
+			if (drive->media != ide_disk)
 				return -EPERM;
 			if (!drive->id || !(drive->id->capability & 1) || !HWIF(drive)->dmaproc)
 				return -EPERM;
@@ -1914,7 +1944,7 @@
 			drive->mult_req = arg;
 			drive->special.b.set_multmode = 1;
 			restore_flags(flags);
-			ide_do_drive_cmd (inode->i_rdev, NULL);
+			(void) ide_do_drive_cmd (drive, &rq, ide_wait);
 			return (drive->mult_count == arg) ? 0 : -EIO;
 
 		case HDIO_DRIVE_CMD:
@@ -1922,13 +1952,14 @@
 			unsigned long args;
 
 			if (NULL == (long *) arg)
-				err = ide_do_drive_cmd(inode->i_rdev,NULL);
+				err = ide_do_drive_cmd(drive, &rq, ide_wait);
 			else {
 				if (!(err = verify_area(VERIFY_READ,(long *)arg,sizeof(long))))
 				{
 					args = get_user((long *)arg);
 					if (!(err = verify_area(VERIFY_WRITE,(long *)arg,sizeof(long)))) {
-						err = ide_do_drive_cmd(inode->i_rdev,(char *)&args);
+						rq.buffer = (char *) &args;
+						err = ide_do_drive_cmd(drive, &rq, ide_wait);
 						put_user(args,(long *)arg);
 					}
 				}
@@ -1940,9 +1971,13 @@
 
 		default:
 #ifdef CONFIG_BLK_DEV_IDECD
-			if (drive->media == cdrom)
+			if (drive->media == ide_cdrom)
 				return ide_cdrom_ioctl(drive, inode, file, cmd, arg);
 #endif /* CONFIG_BLK_DEV_IDECD */
+#ifdef CONFIG_BLK_DEV_IDETAPE
+			if (drive->media == ide_tape)
+				return idetape_blkdev_ioctl(drive, inode, file, cmd, arg);
+#endif /* CONFIG_BLK_DEV_IDETAPE */
 			return -EPERM;
 	}
 }
@@ -1954,7 +1989,7 @@
 	if ((drive = get_info_ptr(i_rdev)) == NULL)
 		return -ENODEV;
 #ifdef CONFIG_BLK_DEV_IDECD
-	if (drive->media == cdrom)
+	if (drive->media == ide_cdrom)
 		return ide_cdrom_check_media_change (drive);
 #endif	/* CONFIG_BLK_DEV_IDECD */
 	if (drive->removeable) /* for disks */
@@ -2027,22 +2062,45 @@
 	/*
 	 * Check for an ATAPI device
 	 */
+
 	if (cmd == WIN_PIDENTIFY) {
+		byte type = (id->config >> 8) & 0x1f;
+		printk("%s: %s, ATAPI ", drive->name, id->model);
+		switch (type) {
+			case 0:				/* Early cdrom models used zero */
+			case 5:
 #ifdef CONFIG_BLK_DEV_IDECD
-		byte type = (id->config >> 8) & 0x0f;
-#endif	/* CONFIG_BLK_DEV_IDECD */
-		printk("%s: %s, ATAPI, ", drive->name, id->model);
-		drive->media = cdrom;
-#ifdef CONFIG_BLK_DEV_IDECD
-		if (type == 0 || type == 5)
-			printk("CDROM drive\n");
-		else
-			printk("UNKNOWN device\n");
-		drive->present = 1;
-		drive->removeable = 1;
+				printk ("CDROM drive\n");
+				drive->media = ide_cdrom;
+ 				drive->present = 1;
+				drive->removeable = 1;
+				return;
 #else
-		printk("not supported by this kernel\n");
-#endif	/* CONFIG_BLK_DEV_IDECD */
+				printk ("CDROM ");
+				break;
+#endif /* CONFIG_BLK_DEV_IDECD */
+			case 1:
+#ifdef CONFIG_BLK_DEV_IDETAPE
+				printk ("TAPE drive\n");
+				if (idetape_identify_device (drive,id)) {
+					drive->media = ide_tape;
+					drive->present = 1;
+					drive->removeable = 1;
+				}
+				else {
+					drive->present = 0;
+					printk ("ide-tape: The tape is not supported by this version of the driver\n");
+				}
+				return;
+#else
+				printk ("TAPE ");
+				break;
+#endif /* CONFIG_BLK_DEV_IDETAPE */
+			default:
+				printk("Type %d - Unknown device\n", type);
+				return;
+		}
+		printk("- not supported by this kernel\n");
 		return;
 	}
 
@@ -2052,7 +2110,7 @@
 			drive->removeable = 1;
 	}
 
-	drive->media = disk;
+	drive->media = ide_disk;
 	/* Extract geometry if we did not already have one for the drive */
 	if (!drive->present) {
 		drive->present = 1;
@@ -2211,15 +2269,15 @@
 static int do_probe (ide_drive_t *drive, byte cmd)
 {
 	int rc;
-#ifdef CONFIG_BLK_DEV_IDECD
+#ifdef CONFIG_BLK_DEV_IDEATAPI
 	if (drive->present) {	/* avoid waiting for inappropriate probes */
-		if ((drive->media == cdrom) && (cmd == WIN_IDENTIFY))
+		if ((drive->media != ide_disk) && (cmd == WIN_IDENTIFY))
 			return 4;
 	}
-#endif	/* CONFIG_BLK_DEV_IDECD */
+#endif	/* CONFIG_BLK_DEV_IDEATAPI */
 #ifdef DEBUG
-	printk("probing for %s: present=%d, type=%s, probetype=%s\n",
-		drive->name, drive->present, drive->media ? "cdrom":"disk",
+	printk("probing for %s: present=%d, media=%d, probetype=%s\n",
+		drive->name, drive->present, drive->media,
 		(cmd == WIN_IDENTIFY) ? "ATA" : "ATAPI");
 #endif
 #if SUPPORT_HT6560B
@@ -2263,19 +2321,19 @@
 	if (drive->noprobe)			/* skip probing? */
 		return drive->present;
 	if (do_probe(drive, WIN_IDENTIFY) >= 2) { /* if !(success||timed-out) */
-#ifdef CONFIG_BLK_DEV_IDECD
+#ifdef CONFIG_BLK_DEV_IDEATAPI
 		(void) do_probe(drive, WIN_PIDENTIFY); /* look for ATAPI device */
-#endif	/* CONFIG_BLK_DEV_IDECD */
+#endif	/* CONFIG_BLK_DEV_IDEATAPI */
 	}
 	if (!drive->present)
 		return 0;			/* drive not found */
 	if (drive->id == NULL) {		/* identification failed? */
-		if (drive->media == disk) {
+		if (drive->media == ide_disk) {
 			printk ("%s: non-IDE drive, CHS=%d/%d/%d\n",
 			 drive->name, drive->cyl, drive->head, drive->sect);
 		}
 #ifdef CONFIG_BLK_DEV_IDECD
-		else if (drive->media == cdrom) {
+		else if (drive->media == ide_cdrom) {
 			printk("%s: ATAPI cdrom (?)\n", drive->name);
 		}
 #endif	/* CONFIG_BLK_DEV_IDECD */
@@ -2284,7 +2342,7 @@
 			return 1;		/* drive was found */
 		}
 	}
-	if (drive->media == disk && !drive->select.b.lba) {
+	if (drive->media == ide_disk && !drive->select.b.lba) {
 		if (!drive->head || drive->head > 16) {
 			printk("%s: INVALID GEOMETRY: %d PHYSICAL HEADS?\n",
 			 drive->name, drive->head);
@@ -2568,14 +2626,14 @@
 				goto done;
 			case -3: /* "cdrom" */
 				drive->present = 1;
-				drive->media = cdrom;
+				drive->media = ide_cdrom;
 				hwif->noprobe = 0;
 				goto done;
 			case -4: /* "serialize" */
 				printk(" -- USE \"ide%c=serialize\" INSTEAD", '0'+hw);
 				goto do_serialize;
 			case 3: /* cyl,head,sect */
-				drive->media	= disk;
+				drive->media	= ide_disk;
 				drive->cyl	= drive->bios_cyl  = vals[0];
 				drive->head	= drive->bios_head = vals[1];
 				drive->sect	= drive->bios_sect = vals[2];
@@ -2679,12 +2737,17 @@
 	const byte *heads = head_vals;
 	unsigned long tracks;
 
-	if ((drive = get_info_ptr(i_rdev)) == NULL || drive->id == NULL)
+	if ((drive = get_info_ptr(i_rdev)) == NULL)
 		return 0;
 
-	drive->cyl  = drive->bios_cyl  = drive->id->cyls;
-	drive->head = drive->bios_head = drive->id->heads;
-	drive->sect = drive->bios_sect = drive->id->sectors;
+	if (drive->id) {
+		drive->bios_cyl  = drive->id->cyls;
+		drive->bios_head = drive->id->heads;
+		drive->bios_sect = drive->id->sectors;
+	}
+	drive->cyl  = drive->bios_cyl;
+	drive->head = drive->bios_head;
+	drive->sect = drive->bios_sect;
 	drive->special.b.set_geometry = 1;
 
 	tracks = drive->bios_cyl * drive->bios_head * drive->bios_sect / 63;
@@ -2801,11 +2864,8 @@
 		hwgroup->hwif 	 = hwif->next = hwif;
 		hwgroup->rq      = NULL;
 		hwgroup->handler = NULL;
-		hwgroup->drive   = NULL;
-		hwgroup->reset_timeout = 0;
-#ifdef CONFIG_BLK_DEV_IDECD
-		hwgroup->doing_atapi_reset = 0;
-#endif /* CONFIG_BLK_DEV_IDECD */
+		hwgroup->drive   = &hwif->drives[0];
+		hwgroup->poll_timeout = 0;
 		init_timer(&hwgroup->timer);
 		hwgroup->timer.function = &timer_expiry;
 		hwgroup->timer.data = (unsigned long) hwgroup;
@@ -3002,5 +3062,10 @@
 			hwif->present = 1;	/* success */
 		}
 	}
+
+#ifdef CONFIG_BLK_DEV_IDETAPE
+	idetape_register_chrdev();	/* Register character device interface to the ide tape */
+#endif /* CONFIG_BLK_DEV_IDETAPE */
+	
 	return 0;
 }
diff -u --recursive --new-file v1.3.45/linux/drivers/block/ide.h linux/drivers/block/ide.h
--- v1.3.45/linux/drivers/block/ide.h	Mon Nov 27 12:48:29 1995
+++ linux/drivers/block/ide.h	Mon Dec 11 11:28:22 1995
@@ -152,6 +152,10 @@
 #define WAIT_WORSTCASE	(30*HZ)	/* 30sec  - worst case when spinning up */
 #define WAIT_CMD	(10*HZ)	/* 10sec  - maximum wait for an IRQ to happen */
 
+#ifdef CONFIG_BLK_DEV_IDETAPE
+#include "ide-tape.h"
+#endif /* CONFIG_BLK_DEV_IDETAPE */
+
 #ifdef CONFIG_BLK_DEV_IDECD
 
 struct atapi_request_sense {
@@ -234,7 +238,8 @@
 /*
  * Now for the data we need to maintain per-drive:  ide_drive_t
  */
-typedef enum {disk, cdrom} media_t;
+
+typedef enum {ide_disk, ide_cdrom, ide_tape} ide_media_t;
 
 typedef union {
 	unsigned all			: 8;	/* all of the bits together */
@@ -271,7 +276,7 @@
 	unsigned removeable	: 1;	/* 1 if need to do check_media_change */
 	unsigned using_dma	: 1;	/* disk is using dma for read/write */
 	unsigned unmask		: 1;	/* flag: okay to unmask other irqs */
-	media_t		media;		/* disk, cdrom, tape */
+	ide_media_t	media;		/* disk, cdrom, tape */
 	select_t	select;		/* basic drive/head select reg value */
 	void		*hwif;		/* actually (ide_hwif_t *) */
 	byte		ctl;		/* "normal" value for IDE_CONTROL_REG */
@@ -295,6 +300,22 @@
 #ifdef CONFIG_BLK_DEV_IDECD
 	struct cdrom_info cdrom_info;	/* from ide-cd.c */
 #endif /* CONFIG_BLK_DEV_IDECD */
+
+#ifdef CONFIG_BLK_DEV_IDETAPE		/* ide-tape specific data */
+
+/*
+ *	Most of our global data which we need to save even as we leave the
+ *	driver due to an interrupt or a timer event is stored here.
+ *
+ *	Additional global variables which provide the link between the
+ *	character device interface to this structure are defined in
+ *	ide-tape.c
+ */
+
+	idetape_tape_t tape;
+
+#endif /* CONFIG_BLK_DEV_IDETAPE */
+
 	} ide_drive_t;
 
 /*
@@ -348,10 +369,7 @@
 	struct request		*rq;	/* current request */
 	struct timer_list	timer;	/* failsafe timer */
 	struct request		wrq;	/* local copy of current write rq */
-	unsigned long	reset_timeout;	/* timeout value during ide resets */
-#ifdef CONFIG_BLK_DEV_IDECD
-	int			doing_atapi_reset;
-#endif /* CONFIG_BLK_DEV_IDECD */
+	unsigned long		poll_timeout;	/* timeout value during long polls */
 	} ide_hwgroup_t;
 
 /*
@@ -368,16 +386,6 @@
 #endif
 
 /*
- * The main (re-)entry point for handling a new request is IDE_DO_REQUEST.
- * Note that IDE_DO_REQUEST should *only* ever be invoked from an interrupt
- * handler.  All others, such as a timer expiry handler, should call
- * do_hwgroup_request() instead (currently local to ide.c).
- */
-void ide_do_request (ide_hwgroup_t *);
-#define IDE_DO_REQUEST { SET_RECOVERY_TIMER(HWIF(drive)); ide_do_request(HWGROUP(drive)); }
-
-
-/*
  * This is used for (nearly) all data transfers from the IDE interface
  */
 void ide_input_data (ide_drive_t *drive, void *buffer, unsigned int wcount);
@@ -391,7 +399,7 @@
  * This is used on exit from the driver, to designate the next irq handler
  * and also to start the safety timer.
  */
-void ide_set_handler (ide_drive_t *drive, ide_handler_t *handler);
+void ide_set_handler (ide_drive_t *drive, ide_handler_t *handler, unsigned int timeout);
 
 /*
  * Error reporting, in human readable form (luxurious, but a memory hog).
@@ -400,18 +408,15 @@
 
 /*
  * ide_error() takes action based on the error returned by the controller.
- *
- * Returns 1 if an ide reset operation has been initiated, in which case
- * the caller MUST simply return from the driver (through however many levels).
- * Returns 0 otherwise.
+ * The calling function must return afterwards, to restart the request.
  */
-int ide_error (ide_drive_t *drive, const char *msg, byte stat);
+void ide_error (ide_drive_t *drive, const char *msg, byte stat);
 
 /*
  * This routine busy-waits for the drive status to be not "busy".
  * It then checks the status for all of the "good" bits and none
  * of the "bad" bits, and if all is okay it returns 0.  All other
- * cases return 1 after invoking ide_error()
+ * cases return 1 after invoking ide_error() -- caller should return.
  *
  */
 int ide_wait_stat (ide_drive_t *drive, byte good, byte bad, unsigned long timeout);
@@ -423,31 +428,51 @@
 
 /*
  * Start a reset operation for an IDE interface.
- * Returns 0 if the reset operation is still in progress,
- *  in which case the drive MUST return, to await completion.
- * Returns 1 if the reset is complete (success or failure).
+ * The caller should return immediately after invoking this.
  */
-int ide_do_reset (ide_drive_t *);
+void ide_do_reset (ide_drive_t *);
 
 /*
- * ide_alloc(): memory allocation for use *only* during driver initialization.
- * If "within_area" is non-zero, the memory will be allocated such that
- * it lies entirely within a "within_area" sized area (eg. 4096).  This is
- * needed for DMA stuff.  "within_area" must be a power of two (not validated).
- * All allocations are longword aligned.
+ * This function is intended to be used prior to invoking ide_do_drive_cmd().
  */
-void *ide_alloc (unsigned long bytecount, unsigned long within_area);
+void ide_init_drive_cmd (struct request *rq);
 
 /*
- * This function issues a specific IDE drive command onto the
- * tail of the request queue, and waits for it to be completed.
- * If arg is NULL, it goes through all the motions,
- * but without actually sending a command to the drive.
- *
- * The value of arg is passed to the internal handler as rq->buffer.
+ * "action" parameter type for ide_do_drive_cmd() below.
  */
-int ide_do_drive_cmd(kdev_t rdev, char *args);
+typedef enum
+	{ide_wait,	/* insert rq at end of list, and wait for it */
+	 ide_next,	/* insert rq immediately after current request */
+	 ide_preempt}	/* insert rq in front of current request */
+ ide_action_t;
 
+/*
+ * This function issues a special IDE device request
+ * onto the request queue.
+ *
+ * If action is ide_wait, then then rq is queued at the end of
+ * the request queue, and the function sleeps until it has been
+ * processed.  This is for use when invoked from an ioctl handler.
+ *
+ * If action is ide_preempt, then the rq is queued at the head of
+ * the request queue, displacing the currently-being-processed
+ * request and this function returns immediately without waiting
+ * for the new rq to be completed.  This is VERY DANGEROUS, and is
+ * intended for careful use by the ATAPI tape/cdrom driver code.
+ *
+ * If action is ide_next, then the rq is queued immediately after
+ * the currently-being-processed-request (if any), and the function
+ * returns without waiting for the new rq to be completed.  As above,
+ * This is VERY DANGEROUS, and is intended for careful use by the 
+ * ATAPI tape/cdrom driver code.
+ */
+int ide_do_drive_cmd (ide_drive_t *drive, struct request *rq, ide_action_t action);
+ 
+/*
+ * Clean up after success/failure of an explicit drive cmd.
+ * stat/err are used only when (HWGROUP(drive)->rq->cmd == IDE_DRIVE_CMD).
+ */
+void ide_end_drive_cmd (ide_drive_t *drive, byte stat, byte err);
 
 #ifdef CONFIG_BLK_DEV_IDECD
 /*
@@ -460,6 +485,57 @@
 void ide_cdrom_release (struct inode *, struct file *, ide_drive_t *);
 void ide_cdrom_setup (ide_drive_t *);
 #endif /* CONFIG_BLK_DEV_IDECD */
+
+#ifdef CONFIG_BLK_DEV_IDETAPE
+
+/*
+ *	Functions in ide-tape.c which are invoked from ide.c:
+ */
+
+/*
+ *	idetape_identify_device is called during device probing stage to
+ *	probe for an ide atapi tape drive and to initialize global variables
+ *	in ide-tape.c which provide the link between the character device
+ *	and the correspoding block device.
+ *
+ *	Returns 1 if an ide tape was detected and is supported.
+ *	Returns 0 otherwise.
+ */
+ 
+int idetape_identify_device (ide_drive_t *drive,struct hd_driveid *id);
+
+/*
+ *	idetape_setup is called a bit later than idetape_identify_device,
+ *	during the search for disk partitions, to initialize various tape
+ *	state variables in ide_drive_t *drive.
+ */
+ 
+void idetape_setup (ide_drive_t *drive);
+
+/*
+ *	idetape_do_request is our request function. It is called by ide.c
+ *	to process a new request.
+ */
+
+void idetape_do_request (ide_drive_t *drive, struct request *rq, unsigned long block);
+
+/*
+ *	Block device interface functions.
+ */
+  
+int idetape_blkdev_ioctl (ide_drive_t *drive, struct inode *inode, struct file *file,
+			unsigned int cmd, unsigned long arg);
+int idetape_blkdev_open (struct inode *inode, struct file *filp, ide_drive_t *drive);
+void idetape_blkdev_release (struct inode *inode, struct file *filp, ide_drive_t *drive);
+
+/*
+ *	idetape_register_chrdev initializes the character device interface to
+ *	the ide tape drive.
+ */
+ 
+void idetape_register_chrdev (void);
+
+#endif /* CONFIG_BLK_DEV_IDETAPE */
 
 #ifdef CONFIG_BLK_DEV_TRITON
 void ide_init_triton (byte, byte);
diff -u --recursive --new-file v1.3.45/linux/drivers/block/ll_rw_blk.c linux/drivers/block/ll_rw_blk.c
--- v1.3.45/linux/drivers/block/ll_rw_blk.c	Mon Nov 27 12:48:29 1995
+++ linux/drivers/block/ll_rw_blk.c	Mon Dec 11 08:49:35 1995
@@ -232,20 +232,24 @@
 static inline void drive_stat_acct(int cmd, unsigned long nr_sectors, short disk_index)
 {
 	kstat.dk_drive[disk_index]++;
-	if (cmd == READ || cmd == READA) {
+	if (cmd == READ) {
 		kstat.dk_drive_rio[disk_index]++;
 		kstat.dk_drive_rblk[disk_index] += nr_sectors;
 	}
-	else if (cmd == WRITE || cmd == WRITEA) {
+	else if (cmd == WRITE) {
 		kstat.dk_drive_wio[disk_index]++;
 		kstat.dk_drive_wblk[disk_index] += nr_sectors;
-	}
+	} else
+		printk("drive_stat_acct: cmd not R/W?\n");
 }
 
 /*
  * add-request adds a request to the linked list.
  * It disables interrupts so that it can muck with the
  * request-lists in peace.
+ *
+ * By this point, req->cmd is always either READ/WRITE, never READA/WRITEA,
+ * which is important for drive_stat_acct() above.
  */
 static void add_request(struct blk_dev_struct * dev, struct request * req)
 {
@@ -302,21 +306,6 @@
 	struct request * req;
 	int rw_ahead, max_req;
 
-/* WRITEA/READA is special case - it is not really needed, so if the */
-/* buffer is locked, we just forget about it, else it's a normal read */
-	rw_ahead = (rw == READA || rw == WRITEA);
-	if (rw_ahead) {
-		if (buffer_locked(bh))
-			return;
-		if (rw == READA)
-			rw = READ;
-		else
-			rw = WRITE;
-	}
-	if (rw!=READ && rw!=WRITE) {
-		printk("Bad block dev command, must be R/W/RA/WA\n");
-		return;
-	}
 	count = bh->b_size >> 9;
 	sector = bh->b_blocknr * count;
 	if (blk_size[major])
@@ -330,21 +319,46 @@
 		return;
 	/* Maybe the above fixes it, and maybe it doesn't boot. Life is interesting */
 	lock_buffer(bh);
-	if ((rw == WRITE && !buffer_dirty(bh)) || (rw == READ && buffer_uptodate(bh))) {
-		unlock_buffer(bh);
-		return;
-	}
 
-/* we don't allow the write-requests to fill up the queue completely:
- * we want some room for reads: they take precedence. The last third
- * of the requests are only for reads.
- */
-	max_req = (rw == READ) ? NR_REQUEST : ((NR_REQUEST*2)/3);
+	rw_ahead = 0;	/* normal case; gets changed below for READA/WRITEA */
+	switch (rw) {
+		case READA:
+			rw_ahead = 1;
+			rw = READ;	/* drop into READ */
+		case READ:
+			if (buffer_uptodate(bh)) {
+				unlock_buffer(bh); /* Hmmph! Already have it */
+				return;
+			}
+			kstat.pgpgin++;
+			max_req = NR_REQUEST;	/* reads take precedence */
+			break;
+		case WRITEA:
+			rw_ahead = 1;
+			rw = WRITE;	/* drop into WRITE */
+		case WRITE:
+			if (!buffer_dirty(bh)) {
+				unlock_buffer(bh); /* Hmmph! Nothing to write */
+				return;
+			}
+			/* We don't allow the write-requests to fill up the
+			 * queue completely:  we want some room for reads,
+			 * as they take precedence. The last third of the
+			 * requests are only for reads.
+			 */
+			kstat.pgpgout++;
+			max_req = (NR_REQUEST * 2) / 3;
+			break;
+		default:
+			printk("make_request: bad block dev cmd, must be R/W/RA/WA\n");
+			unlock_buffer(bh);
+			return;
+	}
 
 /* look for a free request. */
 	cli();
 
-/* The scsi disk drivers and the IDE driver completely remove the request
+/* The scsi disk and cdrom drivers completely remove the request
  * from the queue when they start processing an entry.  For this reason
  * it is safe to continue to add links to the top entry for those devices.
  */
@@ -357,11 +371,7 @@
 	     || major == IDE3_MAJOR)
 	    && (req = blk_dev[major].current_request))
 	{
-#ifdef CONFIG_BLK_DEV_HD
-	        if (major == HD_MAJOR || major == FLOPPY_MAJOR)
-#else
-		if (major == FLOPPY_MAJOR)
-#endif CONFIG_BLK_DEV_HD
+		if (major != SCSI_DISK_MAJOR && major != SCSI_CDROM_MAJOR)
 			req = req->next;
 		while (req) {
 			if (req->rq_dev == bh->b_dev &&
@@ -438,12 +448,18 @@
 		       kdevname(dev), sector);
 		return;
 	}
-	if (rw!=READ && rw!=WRITE)
-		panic("Bad block dev command, must be R/W");
-	if (rw == WRITE && is_read_only(dev)) {
-		printk("Can't page to read-only device %s\n",
-		       kdevname(dev));
-		return;
+	switch (rw) {
+		case READ:
+			break;
+		case WRITE:
+			if (is_read_only(dev)) {
+				printk("Can't page to read-only device %s\n",
+					kdevname(dev));
+				return;
+			}
+			break;
+		default:
+			panic("ll_rw_page: bad block dev cmd, must be R/W");
 	}
 	req = get_request_wait(NR_REQUEST, dev);
 /* fill up the request-info, and add it to the queue */
@@ -524,10 +540,6 @@
 		if (bh[i]) {
 			set_bit(BH_Req, &bh[i]->b_state);
 			make_request(major, rw, bh[i]);
-			if (rw == READ || rw == READA)
-				kstat.pgpgin++;
-			else
-				kstat.pgpgout++;
 		}
 	}
 	unplug_device(dev);
@@ -555,17 +567,19 @@
 		printk("ll_rw_swap_file: trying to swap nonexistent block-device\n");
 		return;
 	}
-
-	if (rw != READ && rw != WRITE) {
-		printk("ll_rw_swap: bad block dev command, must be R/W");
-		return;
-	}
-	if (rw == WRITE && is_read_only(dev)) {
-		printk("Can't swap to read-only device %s\n",
-		       kdevname(dev));
-		return;
+	switch (rw) {
+		case READ:
+			break;
+		case WRITE:
+			if (is_read_only(dev)) {
+				printk("Can't swap to read-only device %s\n",
+					kdevname(dev));
+				return;
+			}
+			break;
+		default:
+			panic("ll_rw_swap: bad block dev cmd, must be R/W");
 	}
-	
 	buffersize = PAGE_SIZE / nb;
 
 	for (j=0, i=0; i<nb;)
diff -u --recursive --new-file v1.3.45/linux/drivers/block/triton.c linux/drivers/block/triton.c
--- v1.3.45/linux/drivers/block/triton.c	Mon Nov 27 12:48:29 1995
+++ linux/drivers/block/triton.c	Mon Dec 11 08:49:54 1995
@@ -1,5 +1,5 @@
 /*
- *  linux/drivers/block/triton.c	Version 1.03  Nov 16, 1995
+ *  linux/drivers/block/triton.c	Version 1.04  Dec 1, 1995
  *
  *  Copyright (c) 1995  Mark Lord
  *  May be copied or modified under the terms of the GNU General Public License
@@ -73,6 +73,13 @@
  *
  * If you have any drive models to add, email your results to:  mlord@bnr.ca
  * Keep an eye on /var/adm/messages for "DMA disabled" messages.
+ *
+ * Some people have reported trouble with Intel Zappa motherboards.
+ * This can be fixed by upgrading the AMI BIOS to version 1.00.04.BS0,
+ * available from ftp://ftp.intel.com/pub/bios/10004bs0.exe
+ * (thanks to Glen Morrell <glen@spin.Stanford.edu> for researching this).
+ *
+ * And, yes, Intel Zappa boards really *do* use the Triton IDE ports.
  */
 #define _TRITON_C
 #include <linux/config.h>
@@ -141,14 +148,12 @@
 				i -= rq->current_nr_sectors;
 				ide_end_request(1, HWGROUP(drive));
 			}
-			IDE_DO_REQUEST;
 			return;
 		}
 		printk("%s: bad DMA status: 0x%02x\n", drive->name, dma_stat);
 	}
 	sti();
-	if (!ide_error(drive, "dma_intr", stat))
-		IDE_DO_REQUEST;
+	ide_error(drive, "dma_intr", stat);
 }
 
 /*
@@ -269,7 +274,7 @@
 	outl(virt_to_bus (HWIF(drive)->dmatable), dma_base + 4); /* PRD table */
 	outb(reading, dma_base);			/* specify r/w */
 	outb(0x26, dma_base+2);				/* clear status bits */
-	ide_set_handler (drive, &dma_intr);		/* issue cmd to drive */
+	ide_set_handler (drive, &dma_intr, WAIT_CMD);	/* issue cmd to drive */
 	OUT_BYTE(reading ? WIN_READDMA : WIN_WRITEDMA, IDE_COMMAND_REG);
 	outb(inb(dma_base)|1, dma_base);		/* begin DMA */
 	return 0;
@@ -307,7 +312,7 @@
 	int rc = 0, h;
 	unsigned short bmiba, pcicmd;
 	unsigned int timings;
-	unsigned char *dmatable = NULL;
+	unsigned long dmatable = 0;
 	extern ide_hwif_t ide_hwifs[];
 
 	/*
@@ -331,7 +336,7 @@
 	if ((rc = pcibios_read_config_dword(bus, fn, 0x40, &timings)))
 		goto quit;
 	if (!(timings & 0x80008000)) {
-		printk("ide: Triton IDE ports are not enabled\n");
+		printk("ide: neither Triton IDE port is enabled\n");
 		goto quit;
 	}
 	printk("ide: Triton BM-IDE on PCI bus %d function %d\n", bus, fn);
@@ -363,15 +368,15 @@
 		} else {
 			request_region(base, 8, hwif->name);
 			hwif->dma_base = base;
-			if (dmatable == NULL) {
+			if (!dmatable) {
 				/*
 				 * Since we know we are on a PCI bus, we could
 				 * actually use __get_free_pages() here instead
 				 * of __get_dma_pages() -- no ISA limitations.
 				 */
-				dmatable = (void *) __get_dma_pages(GFP_KERNEL, 0);
+				dmatable = __get_dma_pages(GFP_KERNEL, 0);
 			}
-			if (dmatable != NULL) {
+			if (dmatable) {
 				hwif->dmatable = (unsigned long *) dmatable;
 				dmatable += (PRD_ENTRIES * PRD_BYTES);
 				outl(virt_to_bus(hwif->dmatable), base + 4);
diff -u --recursive --new-file v1.3.45/linux/drivers/cdrom/aztcd.c linux/drivers/cdrom/aztcd.c
--- v1.3.45/linux/drivers/cdrom/aztcd.c	Mon Nov 27 12:48:29 1995
+++ linux/drivers/cdrom/aztcd.c	Mon Dec 11 11:18:47 1995
@@ -1,5 +1,5 @@
-#define AZT_VERSION "2.0"
-/*      $Id: aztcd.c,v 2.0 1995/11/10 19:33:41 root Exp root $
+#define AZT_VERSION "2.1"
+/*      $Id: aztcd.c,v 2.10 1995/12/03 11:55:09 root Exp root $
 	linux/drivers/block/aztcd.c - AztechCD268 CDROM driver
 
 	Copyright (C) 1994,1995 Werner Zimmermann (zimmerma@rz.fht-esslingen.de)
@@ -134,13 +134,15 @@
                 with kernel 1.3.33. Will definitely not work with older kernels.
                 Programming done by Linus himself.
                 Werner Zimmermann, October 11, 1995
-	V1.90   Support for Conrad TXC drives, thank's to Jochen Koch and Olaf Koluza.
+	V1.90   Support for Conrad TXC drives, thank's to Jochen Koch and Olaf Kaluza.
 	        Werner Zimmermann, October 21, 1995
         V2.00   Changed #include "blk.h" to <linux/blk.h> as the directory
                 structure was changed. README.aztcd is now /usr/src/docu-
                 mentation/cdrom/aztcd
                 Werner Zimmermann, November 10, 95
-
+        V2.10   Started to modify azt_poll to prevent reading beyond end of
+                tracks.
+                Werner Zimmermann, December 3, 95
 	NOTE: 
 	Points marked with ??? are questionable !
 */
@@ -258,6 +260,7 @@
 static void do_aztcd_request(void);
 static void azt_hsg2msf(long hsg, struct msf *msf);
 static void azt_bin2bcd(unsigned char *p);
+static long azt_msf2hsg(struct msf *mp);
 static int  azt_bcd2bin(unsigned char bcd);
 static int  aztStatus(void);
 static int  getAztStatus(void);
@@ -604,10 +607,6 @@
 }
 
 
-long azt_msf2hsg(struct msf *mp)
-{ return azt_bcd2bin(mp -> frame) + azt_bcd2bin(mp -> sec) * 75
-		                  + azt_bcd2bin(mp -> min) * 4500 - CD_BLOCK_OFFSET;
-}
 
 static int aztcd_ioctl(struct inode *ip, struct file *fp, unsigned int cmd, unsigned long arg)
 {       int i, st;
@@ -1180,11 +1179,28 @@
 
 	  if (CURRENT_VALID) {
 	    struct azt_Play_msf msf;
+	    int i;
 	    azt_next_bn = CURRENT -> sector / 4;
 	    azt_hsg2msf(azt_next_bn, &msf.start);
-	    azt_read_count=AZT_BUF_SIZ;    /*??? fast, because we read ahead*/
-/*          azt_read_count= CURRENT->nr_sectors;      slow
-*/
+	    i = 0;
+	    /* find out in which track we are */
+	    while (azt_msf2hsg(&msf.start)>azt_msf2hsg(&Toc[++i].trackTime)) {};
+	    if (azt_msf2hsg(&msf.start)<azt_msf2hsg(&Toc[i].trackTime)-AZT_BUF_SIZ)
+               { azt_read_count=AZT_BUF_SIZ;  /*fast, because we read ahead*/
+               /*azt_read_count=CURRENT->nr_sectors;    slow, no read ahead*/
+               }
+	    else /* don't read beyond end of track */           
+#if AZT_MULTISESSION 
+               { azt_read_count=(azt_msf2hsg(&Toc[i].trackTime)/4)*4-azt_msf2hsg(&msf.start);  
+                 if (azt_read_count < 0) azt_read_count=0;
+		 if (azt_read_count > AZT_BUF_SIZ) azt_read_count=AZT_BUF_SIZ;
+                 printk("aztcd: warning - trying to read beyond end of track\n");
+/*               printk("%i %i %li %li\n",i,azt_read_count,azt_msf2hsg(&msf.start),azt_msf2hsg(&Toc[i].trackTime));
+*/             }
+#else
+	       { azt_read_count=AZT_BUF_SIZ;
+	       }
+#endif
 	    msf.end.min = 0;
 	    msf.end.sec = 0;            
 	    msf.end.frame = azt_read_count ;/*Mitsumi here reads 0xffffff sectors*/
@@ -1629,26 +1645,26 @@
 	else          max_count=count;
 	printk("aztcd: FirmwareVersion=");
 	for (count=1;count<max_count;count++) printk("%c",result[count]);
-	printk("<<<\n");
+	printk("<<>> ");
 
 	if ((result[1]=='A')&&(result[2]=='Z')&&(result[3]=='T'))
-	 { printk("aztcd: AZTECH drive detected\n"); /*AZTECH*/    
+	 { printk("AZTECH drive detected\n"); /*AZTECH*/    
 	 }
 	else if ((result[2]=='C')&&(result[3]=='D')&&(result[4]=='D'))
-	 { printk("aztcd: ORCHID or WEARNES drive detected\n"); /*ORCHID or WEARNES*/
+	 { printk("ORCHID or WEARNES drive detected\n"); /*ORCHID or WEARNES*/
 	 }
 	else if ((result[1]==0x03)&&(result[2]=='5'))
-	 { printk("aztcd: TXC drive detected\n"); /*Conrad TXC*/
+	 { printk("TXC drive detected\n"); /*Conrad TXC*/
 	 }
 	else                                               /*OTHERS or none*/
-	 { printk("aztcd: : unknown drive or firmware version detected\n");
-	   printk("                      azt may not run stable, if you want to try anyhow,\n");
-	   printk("                      boot with: aztcd=<BaseAddress>,0x79\n");
+	 { printk("\nunknown drive or firmware version detected\n");
+	   printk("aztcd may not run stable, if you want to try anyhow,\n");
+	   printk("boot with: aztcd=<BaseAddress>,0x79\n");
 	   if ((azt_cont!=0x79))     
 	     { printk("aztcd: FirmwareVersion=");
 	       for (count=1;count<5;count++) printk("%c",result[count]);
-	       printk("\n");
-	       printk("aztcd: Aborted\n");
+	       printk("<<>> ");
+	       printk("Aborted\n");
                return -EIO;
 	     }
 	 }
@@ -1666,8 +1682,8 @@
 	azt_invalidate_buffers();
 	aztPresent = 1;
 	aztCloseDoor();
-	printk("aztcd: End Init\n");
-        return (0);
+/*	printk("aztcd: End Init\n");
+*/      return (0);
 }
 
 
@@ -1687,6 +1703,10 @@
 	azt_bin2bcd(&msf -> frame);
 }
 
+static long azt_msf2hsg(struct msf *mp)
+{ return azt_bcd2bin(mp -> frame) + azt_bcd2bin(mp -> sec) * 75
+		                  + azt_bcd2bin(mp -> min) * 4500 - CD_BLOCK_OFFSET;
+}
 
 static void azt_bin2bcd(unsigned char *p)
 {       int u, t;
@@ -1700,8 +1720,6 @@
 {       return (bcd >> 4) * 10 + (bcd & 0xF);
 }
 
-
-
 /*
  * Read a value from the drive.  Should return quickly, so a busy wait
  * is used to avoid excessive rescheduling. The read command itself must
@@ -2047,7 +2065,7 @@
       }
 
   Toc[DiskInfo.last + 1].diskTime = DiskInfo.diskLength;
-
+  Toc[DiskInfo.last].trackTime    = DiskInfo.diskLength;
 
 #ifdef AZT_DEBUG_MULTISESSION 
   printk("aztcd: exiting aztGetToc\n");
diff -u --recursive --new-file v1.3.45/linux/drivers/char/Config.in linux/drivers/char/Config.in
--- v1.3.45/linux/drivers/char/Config.in	Mon Nov 13 12:36:43 1995
+++ linux/drivers/char/Config.in	Mon Dec 11 09:55:42 1995
@@ -29,3 +29,10 @@
     comment '>>> Which is available from ftp://ftp.funet.fi/pub/OS/Linux/BETA/QIC-02/'
 fi
 fi
+bool 'Advanced Power Management BIOS support' CONFIG_APM
+if [ "$CONFIG_APM" = "y" ]; then
+  bool '   Ignore USER SUSPEND' CONFIG_APM_IGNORE_USER_SUSPEND
+  bool '   Enable PM at boot time' CONFIG_APM_DO_ENABLE
+  bool '   Make CPU Idle calls when idle' CONFIG_APM_CPU_IDLE
+  bool '   Enable console blanking using APM' CONFIG_APM_DISPLAY_BLANK
+fi
diff -u --recursive --new-file v1.3.45/linux/drivers/char/Makefile linux/drivers/char/Makefile
--- v1.3.45/linux/drivers/char/Makefile	Mon Nov 13 12:36:43 1995
+++ linux/drivers/char/Makefile	Mon Dec 11 09:55:42 1995
@@ -19,6 +19,7 @@
 L_OBJS   := tty_io.o n_tty.o console.o keyboard.o serial.o \
 	tty_ioctl.o pty.o vt.o mem.o vc_screen.o random.o \
 	defkeymap.o consolemap.o selection.o
+SYMTAB_OBJS :=
 
 ifeq ($(CONFIG_CYCLADES),y)
 L_OBJS += cyclades.o
@@ -94,6 +95,11 @@
 
 ifdef CONFIG_QIC02_TAPE
 L_OBJS += tpqic02.o 
+endif
+
+ifdef CONFIG_APM
+L_OBJS += apm_bios.o
+SYMTAB_OBJS += apm_bios.o
 endif
 
 ifdef M
diff -u --recursive --new-file v1.3.45/linux/drivers/char/apm_bios.c linux/drivers/char/apm_bios.c
--- v1.3.45/linux/drivers/char/apm_bios.c	Thu Jan  1 02:00:00 1970
+++ linux/drivers/char/apm_bios.c	Mon Dec 11 10:15:58 1995
@@ -0,0 +1,1086 @@
+/*
+ * APM BIOS driver for Linux
+ * Copyright 1994, 1995 Stephen Rothwell (Stephen.Rothwell@pd.necisa.oz.au)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * $Id: apm_bios.c,v 0.22 1995/03/09 14:12:02 sfr Exp $
+ *
+ * October 1995, Rik Faith (faith@cs.unc.edu):
+ *    Minor enhancements and updates (to the patch set) for 1.3.x
+ *
+ * Reference:
+ *
+ *   Intel Corporation, Microsoft Corporation. Advanced Power Management
+ *   (APM) BIOS Interface Specification, Revision 1.1, September 1993.
+ *   Intel Order Number 241704-001.  Microsoft Part Number 781-110-X01.
+ *
+ * [This document is available free from Intel by calling 800.628.8686 (fax
+ * 916.356.6100) or 800.548.4725.  It is also available from Microsoft by
+ * calling 206.882.8080; and is ftpable from
+ * ftp://ftp.intel.com/pub/IAL/software_specs/apmv11.doc]
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+
+#include <asm/system.h>
+#include <asm/segment.h>
+
+#include <linux/types.h>
+#include <linux/stddef.h>
+#include <linux/timer.h>
+#include <linux/fcntl.h>
+#include <linux/malloc.h>
+#include <linux/linkage.h>
+#include <linux/apm_bios.h>
+
+static struct symbol_table	apm_syms = {
+#include <linux/symtab_begin.h>
+	X(apm_register_callback),
+	X(apm_unregister_callback),
+#include <linux/symtab_end.h>
+};
+
+extern unsigned long get_cmos_time(void);
+
+/* Configurable options:
+ *  
+ * CONFIG_APM_IGNORE_USER_SUSPEND: define to ignore USER SUSPEND requests.
+ * This is necessary on the NEC Versa M series, which generates these when
+ * resuming from SYSTEM SUSPEND.  However, enabling this on other laptops
+ * will cause the laptop to generate a CRITICAL SUSPEND when an appropriate
+ * USER SUSPEND is ignored -- this may prevent the APM driver from updating
+ * the system time on a RESUME.
+ *
+ * CONFIG_APM_DO_ENABLE: enable APM features at boot time.  From page 36 of
+ * the specification: "When disabled, the APM BIOS does not automatically
+ * power manage devices, enter the Standby State, enter the Suspend State,
+ * or take power saving steps in response to CPU Idle calls."  This driver
+ * will make CPU Idle calls when Linux is idle (unless this feature is
+ * turned off -- see below).  This should always save battery power, but
+ * more complicated APM features will be dependent on your BIOS
+ * implementation.  You may need to turn this option off if your computer
+ * hangs at boot time when using APM support, or if it beeps continuously
+ * instead of suspending.  Turn this off if you have a NEC UltraLite Versa
+ * 33/C or a Toshiba T400CDT.  This is off by default since most machines
+ * do fine without this feature.
+ *
+ * CONFIG_APM_CPU_IDLE: enable calls to APM CPU Idle/CPU Busy inside the
+ * idle loop.  On some machines, this can activate improved power savings,
+ * such as a slowed CPU clock rate, when the machine is idle.  These idle
+ * call is made after the idle loop has run for some length of time (e.g.,
+ * 333 mS).  On some machines, this will cause a hang at boot time or
+ * whenever the CPU becomes idle.
+ *
+ * CONFIG_APM_DISPLAY_BLANK: enable console blanking using the APM.  Some
+ * laptops can use this to turn of the LCD backlight when the VC screen
+ * blanker blanks the screen.  Note that this is only used by the VC screen
+ * blanker, and probably won't turn off the backlight when using X11.
+ *
+ * If you are debugging the APM support for your laptop, note that code for
+ * all of these options is contained in this file, so you can #define or
+ * #undef these on the next line to avoid recompiling the whole kernel.
+ *
+ */
+
+/* KNOWN PROBLEM MACHINES:
+ *
+ * U: TI 4000M TravelMate: BIOS is *NOT* APM compliant
+ *                         [Confirmed by TI representative]
+ * 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
+ *
+ * Legend: U = unusable with APM patches
+ *         P = partially usable with APM patches
+ */
+
+/*
+ * define to have debug messages
+ */
+#undef APM_DEBUG
+
+/*
+ * define to always call the APM BIOS busy routine even if the clock was
+ * not slowed by the idle routine
+ */
+#define ALWAYS_CALL_BUSY
+
+/*
+ * define to disable interrupts in APM BIOS calls (the CPU Idle BIOS call
+ * should turn interrupts on before it does a 'hlt')
+ */
+#define APM_NOINTS
+
+/*
+ * define to make the APM BIOS calls zero all data segment registers (do
+ * that an incorrect BIOS implementation will cause a kernel panic if it
+ * tries to write to arbitrary memory)
+ */
+#define APM_ZERO_SEGS
+
+/*
+ * Need to poll the APM BIOS every second
+ */
+#define APM_CHECK_TIMEOUT	(HZ)
+
+/*
+ * These are the actual BIOS calls in assembler.  Depending on
+ * APM_ZERO_SEGS and APM_NOINTS, we are being really paranoid here!  Not
+ * only are interrupts disabled, but all the segment registers (except SS)
+ * are saved and zeroed this means that if the BIOS tries to reference any
+ * data without explicitly loading the segment registers, the kernel will
+ * fault immediately rather than have some unforeseen circumstances for the
+ * rest of the kernel.  And it will be very obvious!  :-) Doing this
+ * depends on CS referring to the same physical memory as DS so that DS can
+ * be zeroed before the call. Unfortunately, we can't do anything about the
+ * stack segment/pointer.  Also, we tell the compiler that everything could
+ * change.
+ */
+#ifdef APM_NOINTS
+#	define APM_DO_CLI	"cli\n\t"
+#else
+#	define APM_DO_CLI
+#endif
+#ifdef APM_ZERO_SEGS
+#	define APM_DO_ZERO_SEGS	\
+		"pushl %%ds\n\t" \
+		"pushl %%es\n\t" \
+		"pushl %%fs\n\t" \
+		"pushl %%gs\n\t" \
+		"xorl %%edx, %%edx\n\t" \
+		"mov %%dx, %%ds\n\t" \
+		"mov %%dx, %%es\n\t" \
+		"mov %%dx, %%fs\n\t" \
+		"mov %%dx, %%gs\n\t"
+#	define APM_DO_RESTORE_SEGS	\
+		"popl %%gs\n\t" \
+		"popl %%fs\n\t" \
+		"popl %%es\n\t" \
+		"popl %%ds\n\t"
+#else
+#	define APM_DO_ZERO_SEGS
+#	define APM_DO_RESTORE_SEGS
+#endif
+
+#define APM_BIOS_CALL(error_reg) \
+	__asm__ __volatile__( \
+		APM_DO_ZERO_SEGS \
+		"pushfl\n\t" \
+		APM_DO_CLI \
+		"lcall %%cs:" SYMBOL_NAME_STR(apm_bios_entry) "\n\t" \
+		"setc %%" # error_reg "\n\t" \
+		"popfl\n\t" \
+		APM_DO_RESTORE_SEGS
+#define APM_BIOS_CALL_END \
+		: "ax", "bx", "cx", "dx", "si", "di", "bp", "memory")
+
+#ifdef CONFIG_APM_CPU_IDLE
+#define APM_SET_CPU_IDLE(error) \
+	APM_BIOS_CALL(al) \
+	: "=a" (error) \
+	: "0" (0x5305) \
+	APM_BIOS_CALL_END
+#endif
+
+#define APM_SET_CPU_BUSY(error) \
+	APM_BIOS_CALL(al) \
+	: "=a" (error) \
+	: "0" (0x5306) \
+	APM_BIOS_CALL_END
+
+#define APM_SET_POWER_STATE(state, error) \
+	APM_BIOS_CALL(al) \
+	: "=a" (error) \
+	: "0" (0x5307), "b" (0x0001), "c" (state) \
+	APM_BIOS_CALL_END
+
+#ifdef CONFIG_APM_DISPLAY_BLANK
+#define APM_SET_DISPLAY_POWER_STATE(state, error) \
+	APM_BIOS_CALL(al) \
+	: "=a" (error) \
+	: "0" (0x5307), "b" (0x01ff), "c" (state) \
+	APM_BIOS_CALL_END
+#endif
+
+#ifdef CONFIG_APM_DO_ENABLE
+#define APM_ENABLE_POWER_MANAGEMENT(device, error) \
+	APM_BIOS_CALL(al) \
+	: "=a" (error) \
+	: "0" (0x5308), "b" (device), "c" (1) \
+	APM_BIOS_CALL_END
+#endif
+
+#define APM_GET_POWER_STATUS(bx, cx, dx, error) \
+	APM_BIOS_CALL(al) \
+	: "=a" (error), "=b" (bx), "=c" (cx), "=d" (dx) \
+	: "0" (0x530a), "1" (1) \
+	APM_BIOS_CALL_END
+
+#define APM_GET_EVENT(event, error)	\
+	APM_BIOS_CALL(al) \
+	: "=a" (error), "=b" (event) \
+	: "0" (0x530b) \
+	APM_BIOS_CALL_END
+
+#define APM_DRIVER_VERSION(ver, ax, error) \
+	APM_BIOS_CALL(bl) \
+	: "=a" (ax), "=b" (error) \
+	: "0" (0x530e), "1" (0), "c" (ver) \
+	APM_BIOS_CALL_END
+
+#define APM_ENGAGE_POWER_MANAGEMENT(device, error) \
+	APM_BIOS_CALL(al) \
+	: "=a" (error) \
+	: "0" (0x530f), "b" (device), "c" (1) \
+	APM_BIOS_CALL_END
+
+/*
+ * Forward declarations
+ */
+static void	suspend(void);
+static void	standby(void);
+static void	set_time(void);
+
+static void	check_events(void);
+static void	do_apm_timer(unsigned long);
+
+static int	do_open(struct inode *, struct file *);
+static void	do_release(struct inode *, struct file *);
+static int	do_read(struct inode *, struct file *, char *, int);
+static int	do_select(struct inode *, struct file *, int,
+			  select_table *);
+static int	do_ioctl(struct inode *, struct file *, u_int, u_long);
+
+extern int	apm_register_callback(int (*)(apm_event_t));
+extern void	apm_unregister_callback(int (*)(apm_event_t));
+
+/*
+ * Local variables
+ */
+static asmlinkage struct {
+	unsigned long	offset;
+	unsigned short	segment;
+}				apm_bios_entry;
+static int			apm_enabled = 0;
+#ifdef CONFIG_APM_CPU_IDLE
+static int			clock_slowed = 0;
+#endif
+static int			apm_major;
+static int			suspends_pending = 0;
+static int			standbys_pending = 0;
+
+static long			clock_cmos_diff;
+static int			got_clock_diff = 0;
+
+static struct wait_queue *	process_list = NULL;
+static struct apm_bios_struct *	user_list = NULL;
+
+static struct timer_list	apm_timer;
+
+static char			driver_version[] = "0.6b";
+
+#ifdef APM_DEBUG
+static char *	apm_event_name[] = {
+	"system standby",
+	"system suspend",
+	"normal resume",
+	"critical resume",
+	"low battery",
+	"power status change",
+	"update time",
+	"critical suspend",
+	"user standby",
+	"user suspend",
+	"system standby resume"
+};
+#define NR_APM_EVENT_NAME	\
+		(sizeof(apm_event_name) / sizeof(apm_event_name[0]))
+#endif
+
+static struct file_operations apm_bios_fops = {
+	NULL,		/* lseek */
+	do_read,
+	NULL,		/* write */
+	NULL,		/* readdir */
+	do_select,
+	do_ioctl,
+	NULL,		/* mmap */
+	do_open,
+	do_release,
+	NULL,		/* fsync */
+	NULL		/* fasync */
+};
+
+typedef struct callback_list_t {
+	int (*				callback)(apm_event_t);
+	struct callback_list_t *	next;
+} callback_list_t;
+
+static callback_list_t *	callback_list = NULL;
+
+typedef struct lookup_t {
+	int	key;
+	char *	msg;
+} lookup_t;
+
+static const lookup_t error_table[] = {
+/* N/A	{ APM_SUCCESS,		"Operation succeeded" }, */
+	{ APM_DISABLED,		"Power management disabled" },
+	{ APM_CONNECTED,	"Real mode interface already connected" },
+	{ APM_NOT_CONNECTED,	"Interface not connected" },
+	{ APM_16_CONNECTED,	"16 bit interface already connected" },
+/* N/A	{ APM_16_UNSUPPORTED,	"16 bit interface not supported" }, */
+	{ APM_32_CONNECTED,	"32 bit interface already connected" },
+	{ APM_32_UNSUPPORTED,	"32 bit interface not supported" },
+	{ APM_BAD_DEVICE,	"Unrecognized device ID" },
+	{ APM_BAD_PARAM,	"Parameter out of range" },
+	{ APM_NOT_ENGAGED,	"Interface not engaged" },
+	{ APM_BAD_STATE,	"Unable to enter requested state" },
+/* N/A	{ APM_NO_EVENTS,	"No events pending" }, */
+	{ APM_NOT_PRESENT,	"No APM present" }
+};
+#define ERROR_COUNT	(sizeof(error_table)/sizeof(lookup_t))
+
+static int apm_driver_version(u_short *val)
+{
+	u_short	error;
+
+	APM_DRIVER_VERSION(*val, *val, error);
+
+	if (error & 0xff)
+		return (*val >> 8);
+	return APM_SUCCESS;
+}
+
+static int apm_get_event(apm_event_t *event)
+{
+	u_short	error;
+
+	APM_GET_EVENT(*event, error);
+	if (error & 0xff)
+		return (error >> 8);
+	return APM_SUCCESS;
+}
+
+static int apm_set_power_state(u_short state)
+{
+	u_short	error;
+
+	APM_SET_POWER_STATE(state, error);
+	if (error & 0xff)
+		return (error >> 8);
+	return APM_SUCCESS;
+}
+
+#ifdef CONFIG_APM_DISPLAY_BLANK
+static int apm_set_display_power_state(u_short state)
+{
+	u_short	error;
+
+	APM_SET_DISPLAY_POWER_STATE(state, error);
+	if (error & 0xff)
+		return (error >> 8);
+	return APM_SUCCESS;
+}
+#endif
+
+#ifdef CONFIG_APM_DO_ENABLE
+static int apm_enable_power_management(void)
+{
+	u_short	error;
+
+	APM_ENABLE_POWER_MANAGEMENT((apm_bios_info.version > 0x100)
+				    ? 0x0001 : 0xffff,
+				    error);
+	if (error & 0xff)
+		return (error >> 8);
+	return APM_SUCCESS;
+}
+#endif
+
+static int apm_get_power_status(u_short *status, u_short *bat, u_short *life)
+{
+	u_short	error;
+
+	APM_GET_POWER_STATUS(*status, *bat, *life, error);
+	if (error & 0xff)
+		return (error >> 8);
+	return APM_SUCCESS;
+}
+
+static int apm_engage_power_management(u_short device)
+{
+	u_short	error;
+
+	APM_ENGAGE_POWER_MANAGEMENT(device, error);
+	if (error & 0xff)
+		return (error >> 8);
+	return APM_SUCCESS;
+}
+
+static void apm_error(char *str, int err)
+{
+	int	i;
+
+	for (i = 0; i < ERROR_COUNT; i++)
+		if (error_table[i].key == err) break;
+	if (i < ERROR_COUNT)
+		printk("apm_bios: %s: %s\n", str, error_table[i].msg);
+	else
+		printk("apm_bios: %s: unknown error code %#2.2x\n", str, err);
+}
+
+int apm_display_blank(void)
+{
+#ifdef CONFIG_APM_DISPLAY_BLANK
+	int	error;
+
+	if (apm_bios_info.version == 0)
+		return 0;
+	error = apm_set_display_power_state(APM_STATE_STANDBY);
+	if (error == APM_SUCCESS)
+		return 1;
+	apm_error("set display standby", error);
+#endif
+	return 0;
+}
+
+int apm_display_unblank(void)
+{
+#ifdef CONFIG_APM_DISPLAY_BLANK
+	int error;
+
+	if (apm_bios_info.version == 0)
+		return 0;
+	error = apm_set_display_power_state(APM_STATE_READY);
+	if (error == APM_SUCCESS)
+		return 1;
+	apm_error("set display ready", error);
+#endif
+	return 0;
+}
+
+int apm_register_callback(int (*callback)(apm_event_t))
+{
+	callback_list_t *	new;
+
+	new = kmalloc(sizeof(callback_list_t), GFP_KERNEL);
+	if (new == NULL)
+		return -ENOMEM;
+	new->callback = callback;
+	new->next = callback_list;
+	callback_list = new;
+	return 0;
+}
+
+void apm_unregister_callback(int (*callback)(apm_event_t))
+{
+	callback_list_t **	ptr;
+	callback_list_t *	old;
+
+	ptr = &callback_list;
+	for (ptr = &callback_list; *ptr != NULL; ptr = &(*ptr)->next)
+		if ((*ptr)->callback == callback)
+			break;
+	old = *ptr;
+	*ptr = old->next;
+	kfree_s(old, sizeof(callback_list_t));
+}
+	
+static int queue_empty(struct apm_bios_struct * as)
+{
+	return as->event_head == as->event_tail;
+}
+
+static apm_event_t get_queued_event(struct apm_bios_struct * as)
+{
+	as->event_tail = (as->event_tail + 1) % APM_MAX_EVENTS;
+	return as->events[as->event_tail];
+}
+
+static int queue_event(apm_event_t event)
+{
+	struct apm_bios_struct *	as;
+	
+	if (user_list == NULL)
+		return 0;
+	for (as = user_list; as != NULL; as = as->next) {
+		as->event_head = (as->event_head + 1) % APM_MAX_EVENTS;
+		if (as->event_head == as->event_tail)
+			as->event_tail = (as->event_tail + 1) % APM_MAX_EVENTS;
+		as->events[as->event_head] = event;
+		if (!as->suser)
+			continue;
+		switch (event) {
+		case APM_SYS_SUSPEND:
+		case APM_USER_SUSPEND:
+			as->suspends_pending++;
+			suspends_pending++;
+			break;
+
+		case APM_SYS_STANDBY:
+		case APM_USER_STANDBY:
+			as->standbys_pending++;
+			standbys_pending++;
+			break;
+		}
+	}
+	wake_up_interruptible(&process_list);
+	return 1;
+}
+
+static void set_time(void)
+{
+	unsigned long	flags;
+
+	if (!got_clock_diff)	/* Don't know time zone, can't set clock */
+		return;
+
+	save_flags(flags);
+	cli();
+	CURRENT_TIME = get_cmos_time() + clock_cmos_diff;
+	restore_flags(flags);
+}
+
+static void suspend(void)
+{
+	unsigned long	flags;
+	int		err;
+
+				/* Estimate time zone so that set_time can
+                                   update the clock */
+	save_flags(flags);
+	cli();
+	clock_cmos_diff = CURRENT_TIME - get_cmos_time();
+	got_clock_diff = 1;
+	restore_flags(flags);
+	
+	err = apm_set_power_state(APM_STATE_SUSPEND);
+	if (err)
+		apm_error("suspend", err);
+	set_time();
+}
+
+static void standby(void)
+{
+	int	err;
+
+	err = apm_set_power_state(APM_STATE_STANDBY);
+	if (err)
+		apm_error("standby", err);
+}
+
+static apm_event_t get_event(void)
+{
+	int		error;
+	apm_event_t	event;
+
+	static int notified = 0;
+
+	error = apm_get_event(&event);
+	if (error == APM_SUCCESS)
+		return event;
+
+	if ((error != APM_NO_EVENTS) && (notified++ == 0))
+		apm_error("get_event", error);
+
+	return 0;
+}
+
+static void send_event(apm_event_t event, apm_event_t undo)
+{
+	callback_list_t *	call;
+	callback_list_t *	fix;
+    
+	for (call = callback_list; call != NULL; call = call->next) {
+		if (call->callback(event) && undo) {
+			for (fix = callback_list; fix != call; fix = fix->next)
+				fix->callback(undo);
+			if (apm_bios_info.version > 0x100)
+				apm_set_power_state(APM_STATE_REJECT);
+			return;
+		}
+	}
+
+	queue_event(event);
+}
+
+static void check_events(void)
+{
+	apm_event_t	event;
+
+	while ((event = get_event()) != 0) {
+		switch (event) {
+		case APM_SYS_STANDBY:
+		case APM_USER_STANDBY:
+			send_event(event, APM_STANDBY_RESUME);
+			if (standbys_pending <= 0)
+				standby();
+			break;
+
+		case APM_USER_SUSPEND:
+#ifdef CONFIG_APM_IGNORE_USER_SUSPEND
+			apm_set_power_state(APM_STATE_REJECT);
+			break;
+#endif
+		case APM_SYS_SUSPEND:
+			send_event(event, APM_NORMAL_RESUME);
+			if (suspends_pending <= 0)
+				suspend();
+			break;
+
+		case APM_NORMAL_RESUME:
+		case APM_CRITICAL_RESUME:
+		case APM_STANDBY_RESUME:
+			set_time();
+			send_event(event, 0);
+			break;
+
+		case APM_LOW_BATTERY:
+		case APM_POWER_STATUS_CHANGE:
+			send_event(event, 0);
+			break;
+
+		case APM_UPDATE_TIME:
+			set_time();
+			break;
+
+		case APM_CRITICAL_SUSPEND:
+			suspend();
+			break;
+		}
+#ifdef APM_DEBUG
+		if (event <= NR_APM_EVENT_NAME)
+			printk("APM BIOS received %s notify\n",
+			       apm_event_name[event - 1]);
+		else
+			printk("APM BIOS received unknown event 0x%02x\n",
+			       event);
+#endif
+	}
+}
+
+static void do_apm_timer(unsigned long unused)
+{
+	int	err;
+
+	static int	pending_count = 0;
+
+	if (((standbys_pending > 0) || (suspends_pending > 0))
+	    && (apm_bios_info.version > 0x100)
+	    && (pending_count-- <= 0)) {
+		pending_count = 4;
+
+		err = apm_set_power_state(APM_STATE_BUSY);
+		if (err)
+			apm_error("busy", err);
+	}
+	check_events();
+
+	init_timer(&apm_timer);
+	apm_timer.expires = APM_CHECK_TIMEOUT + jiffies;
+	add_timer(&apm_timer);
+}
+
+int apm_do_idle(void)
+{
+#ifdef CONFIG_APM_CPU_IDLE
+	unsigned short	error;
+
+	if (!apm_enabled)
+		return 0;
+
+	APM_SET_CPU_IDLE(error);
+	if (error & 0xff)
+		return 0;
+
+	clock_slowed = (apm_bios_info.flags & APM_IDLE_SLOWS_CLOCK) != 0;
+	return 1;
+#else
+	return 0;
+#endif
+}
+
+void apm_do_busy(void)
+{
+#ifdef CONFIG_APM_CPU_IDLE
+	unsigned short	error;
+
+#ifndef ALWAYS_CALL_BUSY
+	if (!clock_slowed)
+		return;
+#endif
+
+	APM_SET_CPU_BUSY(error);
+
+	clock_slowed = 0;
+#endif
+}
+
+static int check_apm_bios_struct(struct apm_bios_struct *as, const char *func)
+{
+	if ((as == NULL) || (as->magic != APM_BIOS_MAGIC)) {
+		printk("apm_bios: %s passed bad filp", func);
+		return 1;
+	}
+	return 0;
+}
+
+static int do_read(struct inode *inode, struct file *fp, char *buf, int count)
+{
+	struct apm_bios_struct *	as;
+	int			i;
+	apm_event_t		event;
+	struct wait_queue	wait = { current,	NULL };
+
+	as = fp->private_data;
+	if (check_apm_bios_struct(as, "read"))
+		return -EIO;
+	if (count < sizeof(apm_event_t))
+		return -EINVAL;
+	if (queue_empty(as)) {
+		if (fp->f_flags & O_NONBLOCK)
+			return -EAGAIN;
+		add_wait_queue(&process_list, &wait);
+repeat:
+		current->state = TASK_INTERRUPTIBLE;
+		if (queue_empty(as)
+		    && !(current->signal & ~current->blocked)) {
+			schedule();
+			goto repeat;
+		}
+		current->state = TASK_RUNNING;
+		remove_wait_queue(&process_list, &wait);
+	}
+	i = count;
+	while ((i >= sizeof(event)) && !queue_empty(as)) {
+		event = get_queued_event(as);
+		memcpy_tofs(buf, &event, sizeof(event));
+		buf += sizeof(event);
+		i -= sizeof(event);
+	}
+	if (i < count)
+		return count - i;
+	if (current->signal & ~current->blocked)
+		return -ERESTARTSYS;
+	return 0;
+}
+
+static int do_select(struct inode *inode, struct file *fp, int sel_type,
+		     select_table * wait)
+{
+	struct apm_bios_struct *	as;
+
+	as = fp->private_data;
+	if (check_apm_bios_struct(as, "select"))
+		return 0;
+	if (sel_type != SEL_IN)
+		return 0;
+	if (!queue_empty(as))
+		return 1;
+	select_wait(&process_list, wait);
+	return 0;
+}
+
+static int do_ioctl(struct inode * inode, struct file *filp,
+		    u_int cmd, u_long arg)
+{
+	struct apm_bios_struct *	as;
+
+	as = filp->private_data;
+	if (check_apm_bios_struct(as, "ioctl"))
+		return -EIO;
+	switch (cmd) {
+	case APM_IOC_STANDBY:
+		if (as->standbys_pending > 0) {
+			as->standbys_pending--;
+			standbys_pending--;
+			if (standbys_pending <= 0)
+				standby();
+		}
+		break;
+	case APM_IOC_SUSPEND:
+		if (as->suspends_pending > 0) {
+			as->suspends_pending--;
+			suspends_pending--;
+			if (suspends_pending <= 0)
+				suspend();
+		}
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static void do_release(struct inode * inode, struct file * filp)
+{
+	struct apm_bios_struct *	as;
+
+	as = filp->private_data;
+	filp->private_data = NULL;
+	if (check_apm_bios_struct(as, "release"))
+		return;
+	if (as->standbys_pending > 0) {
+		standbys_pending -= as->standbys_pending;
+		if (standbys_pending <= 0)
+			standby();
+	}
+	if (as->suspends_pending > 0) {
+		suspends_pending -= as->suspends_pending;
+		if (suspends_pending <= 0)
+			suspend();
+	}
+	if (user_list == as)
+		user_list = as->next;
+	else {
+		struct apm_bios_struct *	as1;
+
+		for (as1 = user_list;
+		     (as1 != NULL) && (as1->next != as);
+		     as1 = as1->next)
+			;
+		if (as1 == NULL)
+			printk("apm_bios: filp not in user list");
+		else
+			as1->next = as->next;
+	}
+	kfree_s(as, sizeof(*as));
+}
+
+static int do_open(struct inode * inode, struct file * filp)
+{
+	struct apm_bios_struct *	as;
+
+	as = (struct apm_bios_struct *)kmalloc(sizeof(*as), GFP_KERNEL);
+	if (as == NULL) {
+		printk("apm_bios: cannot allocate struct of size %d bytes",
+		       sizeof(*as));
+		return -ENOMEM;
+	}
+	as->magic = APM_BIOS_MAGIC;
+	as->event_tail = as->event_head = 0;
+	as->suspends_pending = as->standbys_pending = 0;
+	as->suser = suser();
+	as->next = user_list;
+	user_list = as;
+	filp->private_data = as;
+	return 0;
+}
+
+int apm_proc(char *buf)
+{
+	char *		p;
+	char *		power_stat;
+	char *		bat_stat;
+	unsigned short	bx;
+	unsigned short	cx;
+	unsigned short	dx;
+	unsigned short	error;
+
+	if (apm_bios_info.version == 0)
+		return 0;
+	p = buf;
+	p += sprintf(p, "BIOS version: %d.%d\nFlags: 0x%02x\n",
+		     (apm_bios_info.version >> 8) & 0xff,
+		     apm_bios_info.version & 0xff,
+		     apm_bios_info.flags);
+	if ((apm_bios_info.flags & APM_32_BIT_SUPPORT) == 0)
+		return p - buf;
+	p += sprintf(p, "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)
+		p += sprintf(p, " cseg len %x, dseg len %x",
+			     apm_bios_info.cseg_len, apm_bios_info.dseg_len);
+	*p++ = '\n';
+	error = apm_get_power_status(&bx, &cx, &dx);
+	if (error) {
+		strcpy(p, "Power status not available\n");
+		p += strlen(p);
+		return p - buf;
+	}
+	switch ((bx >> 8) & 0xff) {
+	case 0: power_stat = "off line"; break;
+	case 1: power_stat = "on line"; break;
+	case 2: power_stat = "on backup power"; break;
+	default: power_stat = "unknown"; break;
+	}
+	switch (bx & 0xff) {
+	case 0: bat_stat = "high"; break;
+	case 1: bat_stat = "low"; break;
+	case 2: bat_stat = "critical"; break;
+	case 3: bat_stat = "charging"; break;
+	default: bat_stat = "unknown"; break;
+	}
+	p += sprintf(p, "AC: %s\nBattery status: %s\nBattery life: ",
+		     power_stat, bat_stat);
+	if ((cx & 0xff) == 0xff) {
+		strcpy(p, "unknown");
+		p += strlen(p);
+	} else
+		p += sprintf(p, "%d%%", cx & 0xff);
+	*p++ = '\n';
+	if (apm_bios_info.version > 0x100) {
+		p += sprintf(p, "Battery flag: 0x%02x\nBattery life: ",
+			     (cx >> 8) & 0xff);
+		if (dx == 0xffff) {
+			strcpy(p, "unknown");
+			p += strlen(p);
+		}
+		else
+			p += sprintf(p, "%d %s", dx & 0x7fff,
+				     ((dx & 0x8000) == 0)
+				     ? "seconds" : "minutes");
+		*p++ = '\n';
+	}
+	return p - buf;
+}
+
+static int apm_setup(void)
+{
+	unsigned short	bx;
+	unsigned short	cx;
+	unsigned short	dx;
+	unsigned short	error;
+	char *		power_stat;
+	char *		bat_stat;
+
+	if (apm_bios_info.version == 0) {
+		printk("APM BIOS not found.\n");
+		return -1;
+	}
+	printk("APM BIOS version %c.%c Flags 0x%02x (Driver version %s)\n",
+	       ((apm_bios_info.version >> 8) & 0xff) + '0',
+	       (apm_bios_info.version & 0xff) + '0',
+	       apm_bios_info.flags,
+	       driver_version);
+	if ((apm_bios_info.flags & APM_32_BIT_SUPPORT) == 0) {
+		printk("    No 32 bit BIOS support\n");
+		return -1;
+	}
+
+	/*
+	 * Fix for the Compaq Contura 3/25c which reports BIOS version 0.1
+	 * but is reportedly a 1.0 BIOS.
+	 */
+	if (apm_bios_info.version == 0x001)
+		apm_bios_info.version = 0x100;
+
+	printk("    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("\n");
+
+	apm_bios_entry.offset = apm_bios_info.offset;
+	apm_bios_entry.segment = APM_CS;
+	set_base(gdt[APM_CS >> 3],
+		 0xc0000000 + ((unsigned long)apm_bios_info.cseg << 4));
+	set_base(gdt[APM_CS_16 >> 3],
+		 0xc0000000 + ((unsigned long)apm_bios_info.cseg_16 << 4));
+	set_base(gdt[APM_DS >> 3],
+		 0xc0000000 + ((unsigned long)apm_bios_info.dseg << 4));
+	if (apm_bios_info.version == 0x100) {
+		set_limit(gdt[APM_CS >> 3], 64 * 1024);
+		set_limit(gdt[APM_CS_16 >> 3], 64 * 1024);
+		set_limit(gdt[APM_DS >> 3], 64 * 1024);
+	} else {
+		set_limit(gdt[APM_CS >> 3], apm_bios_info.cseg_len);
+		/*
+		 * This is not clear from the spec, but at least one
+		 * machine needs this to be a 64k segment.
+		 */
+		set_limit(gdt[APM_CS_16 >> 3], 64 * 1024);
+		set_limit(gdt[APM_DS >> 3], apm_bios_info.dseg_len);
+		apm_bios_info.version = 0x0101;
+		error = apm_driver_version(&apm_bios_info.version);
+		if (error != 0)
+			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;
+		}
+	}
+
+	error = apm_get_power_status(&bx, &cx, &dx);
+	if (error)
+		printk("    Power status not available\n");
+	else {
+		switch ((bx >> 8) & 0xff) {
+		case 0: power_stat = "off line"; break;
+		case 1: power_stat = "on line"; break;
+		case 2: power_stat = "on backup power"; break;
+		default: power_stat = "unknown"; break;
+		}
+		switch (bx & 0xff) {
+		case 0: bat_stat = "high"; break;
+		case 1: bat_stat = "low"; break;
+		case 2: bat_stat = "critical"; break;
+		case 3: bat_stat = "charging"; break;
+		default: bat_stat = "unknown"; break;
+		}
+		printk("    AC %s, battery status %s, battery life ",
+		       power_stat, bat_stat);
+		if ((cx & 0xff) == 0xff)
+			printk("unknown\n");
+		else
+			printk("%d%%\n", cx & 0xff);
+		if (apm_bios_info.version > 0x100) {
+			printk("    battery flag 0x%02x, battery life ",
+			       (cx >> 8) & 0xff);
+			if (dx == 0xffff)
+				printk("unknown\n");
+			else
+				printk("%d %s\n", dx & 0x7fff,
+				       ((dx & 0x8000) == 0)
+				       ? "seconds" : "minutes");
+		}
+	}
+
+#ifdef CONFIG_APM_DO_ENABLE
+	/*
+	 * This call causes my NEC UltraLite Versa 33/C to hang if it is
+	 * booted with PM disabled but not in the docking station.
+	 * Unfortunate ...
+	 */
+	error = apm_enable_power_management();
+	if (error)
+		apm_error("enable power management", error);
+#endif
+
+	init_timer(&apm_timer);
+	apm_timer.function = do_apm_timer;
+	apm_timer.expires = APM_CHECK_TIMEOUT + jiffies;
+	add_timer(&apm_timer);
+
+	register_symtab(&apm_syms);
+
+	apm_enabled = 1;
+
+	if ((apm_major = register_chrdev(0, "apm_bios", &apm_bios_fops)) < 0)
+		printk("APM BIOS: Cannot allocate major device number\n");
+
+	return 0;
+}
+
+void apm_bios_init(void)
+{
+	apm_setup();
+}
diff -u --recursive --new-file v1.3.45/linux/drivers/char/console.c linux/drivers/char/console.c
--- v1.3.45/linux/drivers/char/console.c	Mon Nov 27 12:48:30 1995
+++ linux/drivers/char/console.c	Mon Dec 11 09:55:42 1995
@@ -93,6 +93,9 @@
 #include <linux/major.h>
 #include <linux/mm.h>
 #include <linux/ioport.h>
+#ifdef CONFIG_APM
+#include <linux/apm_bios.h>
+#endif
 
 #include <asm/io.h>
 #include <asm/system.h>
@@ -2076,6 +2079,11 @@
 {
 	int currcons;
 
+#ifdef CONFIG_APM
+	if (apm_display_blank())
+		return;
+#endif
+
 	if (console_blanked)
 		return;
 
@@ -2110,6 +2118,11 @@
 	int currcons;
 	int resetorg;
 	long offset;
+
+#ifdef CONFIG_APM
+	if (apm_display_unblank())
+		return;
+#endif
 
 	if (!console_blanked)
 		return;
diff -u --recursive --new-file v1.3.45/linux/drivers/char/lp.c linux/drivers/char/lp.c
--- v1.3.45/linux/drivers/char/lp.c	Thu Nov  9 11:23:48 1995
+++ linux/drivers/char/lp.c	Thu Nov 30 14:03:38 1995
@@ -503,6 +503,16 @@
 					memset(&LP_STAT(minor), 0, sizeof(struct lp_stats));
 			}
 			break;
+ 		case LPGETFLAGS:
+ 			retval = verify_area(VERIFY_WRITE, (void *) arg,
+ 			    sizeof(int));
+ 		    	if (retval)
+ 		    		return retval;
+ 			else {
+ 				int status = LP_F(minor);
+				memcpy_tofs((int *) arg, &status, sizeof(int));
+			}
+			break;
 		default:
 			retval = -EINVAL;
 	}
diff -u --recursive --new-file v1.3.45/linux/drivers/net/3c501.c linux/drivers/net/3c501.c
--- v1.3.45/linux/drivers/net/3c501.c	Sat Nov 25 19:04:40 1995
+++ linux/drivers/net/3c501.c	Thu Nov 30 11:15:53 1995
@@ -79,9 +79,9 @@
     "3c501.c: 9/23/94 Donald Becker (becker@cesdis.gsfc.nasa.gov).\n";
 
 /*
-  Braindamage remaining:
-  The 3c501 board.
-  */
+ *	Braindamage remaining:
+ *	The 3c501 board.
+ */
 
 #include <linux/module.h>
 
@@ -110,7 +110,10 @@
    { 0x280, 0x300, 0};
 
 
-/* Index to functions. */
+/*
+ *	Index to functions. 
+ */
+ 
 int el1_probe(struct device *dev);
 static int  el1_probe1(struct device *dev, int ioaddr);
 static int  el_open(struct device *dev);
@@ -129,8 +132,12 @@
 #endif			/* Anything above 5 is wordy death! */
 static int el_debug = EL_DEBUG;
  
-/* Board-specific info in dev->priv. */
-struct net_local {
+/* 
+ *	Board-specific info in dev->priv. 
+ */
+ 
+struct net_local 
+{
     struct enet_statistics stats;
     int tx_pkt_start;		/* The length of the current Tx packet. */
     int collisions;		/* Tx collisions this packet */
@@ -158,7 +165,10 @@
 #define EL1_SAPROM	0x0C
 #define EL1_DATAPORT 	0x0f
 
-/* Writes to the ax command register. */
+/*
+ *	Writes to the ax command register.
+ */
+ 
 #define AX_OFF	0x00			/* Irq off, buffer access on */
 #define AX_SYS  0x40			/* Load the buffer */
 #define AX_XMIT 0x44			/* Transmit a packet */
@@ -166,14 +176,20 @@
 #define AX_LOOP	0x0C			/* Loopback mode */
 #define AX_RESET 0x80
 
-/* Normal receive mode written to RX_STATUS.  We must intr on short packets
-   to avoid bogus rx lockups. */
+/*
+ *	Normal receive mode written to RX_STATUS.  We must intr on short packets
+ *	to avoid bogus rx lockups.
+ */
+ 
 #define RX_NORM 0xA8		/* 0x68 == all addrs, 0xA8 only to me. */
 #define RX_PROM 0x68		/* Senior Prom, uhmm promiscuous mode. */
 #define RX_MULT 0xE8		/* Accept multicast packets. */
-#define TX_NORM 0x0A	/* Interrupt on everything that might hang the chip */
+#define TX_NORM 0x0A		/* Interrupt on everything that might hang the chip */
 
-/* TX_STATUS register. */
+/*
+ *	TX_STATUS register. 
+ */
+ 
 #define TX_COLLISION 0x02
 #define TX_16COLLISIONS 0x04
 #define TX_READY 0x08
@@ -183,468 +199,551 @@
 #define RX_GOOD	0x30		/* Good packet 0x20, or simple overflow 0x10. */
 
 
-/* The boilerplate probe code. */
+/*
+ *	The boilerplate probe code.
+ */
+ 
 #ifdef HAVE_DEVLIST
-struct netdev_entry el1_drv =
-{"3c501", el1_probe1, EL1_IO_EXTENT, netcard_portlist};
+struct netdev_entry el1_drv = {"3c501", el1_probe1, EL1_IO_EXTENT, netcard_portlist};
 #else
-int
-el1_probe(struct device *dev)
+
+int el1_probe(struct device *dev)
 {
-    int i;
-    int base_addr = dev ? dev->base_addr : 0;
+	int i;
+	int base_addr = dev ? dev->base_addr : 0;
 
-    if (base_addr > 0x1ff)	/* Check a single specified location. */
-	return el1_probe1(dev, base_addr);
-    else if (base_addr != 0)	/* Don't probe at all. */
-	return ENXIO;
-
-    for (i = 0; netcard_portlist[i]; i++) {
-	int ioaddr = netcard_portlist[i];
-	if (check_region(ioaddr, EL1_IO_EXTENT))
-	    continue;
-	if (el1_probe1(dev, ioaddr) == 0)
-	    return 0;
-    }
+	if (base_addr > 0x1ff)	/* Check a single specified location. */
+		return el1_probe1(dev, base_addr);
+	else if (base_addr != 0)	/* Don't probe at all. */
+		return ENXIO;
 
-    return ENODEV;
+	for (i = 0; netcard_portlist[i]; i++) 
+	{
+		int ioaddr = netcard_portlist[i];
+		if (check_region(ioaddr, EL1_IO_EXTENT))
+			continue;
+		if (el1_probe1(dev, ioaddr) == 0)
+			return 0;
+	}
+
+	return ENODEV;
 }
 #endif
 
-/* The actual probe. */ 
-static int
-el1_probe1(struct device *dev, int ioaddr)
-{
-    #ifndef MODULE
-
-    const char *mname;		/* Vendor name */
-    unsigned char station_addr[6];
-    int autoirq = 0;
-    int i;
-
-    /* Read the station address PROM data from the special port.  */
-    for (i = 0; i < 6; i++) {
-	outw(i, ioaddr + EL1_DATAPTR);
-	station_addr[i] = inb(ioaddr + EL1_SAPROM);
-    }
-    /* Check the first three octets of the S.A. for 3Com's prefix, or
-       for the Sager NP943 prefix. */ 
-    if (station_addr[0] == 0x02  &&  station_addr[1] == 0x60
-	&& station_addr[2] == 0x8c) {
-	mname = "3c501";
-    } else if (station_addr[0] == 0x00  &&  station_addr[1] == 0x80
-	&& station_addr[2] == 0xC8) {
-	mname = "NP943";
-    } else
-	return ENODEV;
+/*
+ *	The actual probe. 
+ */ 
 
-    /* Grab the region so we can find the another board if autoIRQ fails. */
-    request_region(ioaddr, EL1_IO_EXTENT,"3c501");
+static int el1_probe1(struct device *dev, int ioaddr)
+{
+#ifndef MODULE
 
-    /* We auto-IRQ by shutting off the interrupt line and letting it float
-       high. */
-    if (dev->irq < 2) {
+	const char *mname;		/* Vendor name */
+	unsigned char station_addr[6];
+	int autoirq = 0;
+	int i;
 
-	autoirq_setup(2);
+	/*
+	 *	Read the station address PROM data from the special port.  
+	 */
+	 
+	for (i = 0; i < 6; i++) 
+	{
+		outw(i, ioaddr + EL1_DATAPTR);
+		station_addr[i] = inb(ioaddr + EL1_SAPROM);
+	}
+	/*
+	 *	Check the first three octets of the S.A. for 3Com's prefix, or
+	 *	for the Sager NP943 prefix. 
+	 */ 
+	 
+	if (station_addr[0] == 0x02  &&  station_addr[1] == 0x60
+		&& station_addr[2] == 0x8c) 
+	{
+		mname = "3c501";
+	} else if (station_addr[0] == 0x00  &&  station_addr[1] == 0x80
+	&& station_addr[2] == 0xC8) 
+	{
+		mname = "NP943";
+    	}
+    	else
+		return ENODEV;
 
-	inb(RX_STATUS);		/* Clear pending interrupts. */
-	inb(TX_STATUS);
-	outb(AX_LOOP + 1, AX_CMD);
+	/*
+	 *	Grab the region so we can find the another board if autoIRQ fails. 
+	 */
 
-	outb(0x00, AX_CMD);
+	request_region(ioaddr, EL1_IO_EXTENT,"3c501");
 
-	autoirq = autoirq_report(1);
+	/*	
+	 *	We auto-IRQ by shutting off the interrupt line and letting it float
+	 *	high.
+	 */
 
-	if (autoirq == 0) {
-	    printk("%s probe at %#x failed to detect IRQ line.\n",
-		   mname, ioaddr);
-	    return EAGAIN;
-	}
-    }
+	if (dev->irq < 2) 
+	{
+		autoirq_setup(2);
+		inb(RX_STATUS);		/* Clear pending interrupts. */
+		inb(TX_STATUS);
+		outb(AX_LOOP + 1, AX_CMD);
+
+		outb(0x00, AX_CMD);
+	
+		autoirq = autoirq_report(1);
 
-    outb(AX_RESET+AX_LOOP, AX_CMD);			/* Loopback mode. */
+		if (autoirq == 0) 
+		{
+			printk("%s probe at %#x failed to detect IRQ line.\n",
+				mname, ioaddr);
+			return EAGAIN;
+		}
+	}
 
-    dev->base_addr = ioaddr;
-    memcpy(dev->dev_addr, station_addr, ETH_ALEN);
-    if (dev->mem_start & 0xf)
-	el_debug = dev->mem_start & 0x7;
-    if (autoirq)
-	dev->irq = autoirq;
+	outb(AX_RESET+AX_LOOP, AX_CMD);			/* Loopback mode. */
+	dev->base_addr = ioaddr;
+	memcpy(dev->dev_addr, station_addr, ETH_ALEN);
+
+	if (dev->mem_start & 0xf)
+		el_debug = dev->mem_start & 0x7;
+	if (autoirq)
+		dev->irq = autoirq;
 
-    printk("%s: %s EtherLink at %#lx, using %sIRQ %d.\n",
-	   dev->name, mname, dev->base_addr,
-	   autoirq ? "auto":"assigned ", dev->irq);
+	printk("%s: %s EtherLink at %#lx, using %sIRQ %d.\n", dev->name, mname, dev->base_addr,
+			autoirq ? "auto":"assigned ", dev->irq);
 	   
 #ifdef CONFIG_IP_MULTICAST
-    printk("WARNING: Use of the 3c501 in a multicast kernel is NOT recommended.\n");
+	printk("WARNING: Use of the 3c501 in a multicast kernel is NOT recommended.\n");
 #endif    
 
-    if (el_debug)
-	printk("%s", version);
+	if (el_debug)
+		printk("%s", version);
+
+	/*
+	 *	Initialize the device structure. 
+	 */
+	 
+	dev->priv = kmalloc(sizeof(struct net_local), GFP_KERNEL);
+	if (dev->priv == NULL)
+		return -ENOMEM;
+	memset(dev->priv, 0, sizeof(struct net_local));
+
+	/*
+	 *	The EL1-specific entries in the device structure. 
+	 */
+	 
+	dev->open = &el_open;
+	dev->hard_start_xmit = &el_start_xmit;
+	dev->stop = &el1_close;
+	dev->get_stats = &el1_get_stats;
+	dev->set_multicast_list = &set_multicast_list;
+
+	/*
+	 *	Setup the generic properties 
+	 */
 
-    /* Initialize the device structure. */
-    dev->priv = kmalloc(sizeof(struct net_local), GFP_KERNEL);
-    if (dev->priv == NULL)
-	return -ENOMEM;
-    memset(dev->priv, 0, sizeof(struct net_local));
-
-    /* The EL1-specific entries in the device structure. */
-    dev->open = &el_open;
-    dev->hard_start_xmit = &el_start_xmit;
-    dev->stop = &el1_close;
-    dev->get_stats = &el1_get_stats;
-    dev->set_multicast_list = &set_multicast_list;
-    /* Setup the generic properties */
-    ether_setup(dev);
+	ether_setup(dev);
 
 #endif /* !MODULE */
-    return 0;
+
+	return 0;
 }
 
-/* Open/initialize the board. */
-static int
-el_open(struct device *dev)
+/*
+ *	Open/initialize the board. 
+ */
+ 
+static int el_open(struct device *dev)
 {
-    int ioaddr = dev->base_addr;
+	int ioaddr = dev->base_addr;
 
-    if (el_debug > 2)
-	printk("%s: Doing el_open()...", dev->name);
+	if (el_debug > 2)
+		printk("%s: Doing el_open()...", dev->name);
 
-    if (request_irq(dev->irq, &el_interrupt, 0, "3c501")) {
-	return -EAGAIN;
-    }
-    irq2dev_map[dev->irq] = dev;
+	if (request_irq(dev->irq, &el_interrupt, 0, "3c501")) 
+		return -EAGAIN;
 
-    el_reset(dev);
+	irq2dev_map[dev->irq] = dev;
+	el_reset(dev);
 
-    dev->start = 1;
+	dev->start = 1;
 
-    outb(AX_RX, AX_CMD);	/* Aux control, irq and receive enabled */
-    MOD_INC_USE_COUNT;
-    return 0;
+	outb(AX_RX, AX_CMD);	/* Aux control, irq and receive enabled */
+	MOD_INC_USE_COUNT;
+	return 0;
 }
 
-static int
-el_start_xmit(struct sk_buff *skb, struct device *dev)
+static int el_start_xmit(struct sk_buff *skb, struct device *dev)
 {
-    struct net_local *lp = (struct net_local *)dev->priv;
-    int ioaddr = dev->base_addr;
-    unsigned long flags;
+	struct net_local *lp = (struct net_local *)dev->priv;
+	int ioaddr = dev->base_addr;
+	unsigned long flags;
 
-    if (dev->tbusy) {
-	if (jiffies - dev->trans_start < 20) {
-	    if (el_debug > 2)
-		printk(" transmitter busy, deferred.\n");
-	    return 1;
+	if (dev->tbusy) 
+	{
+		if (jiffies - dev->trans_start < 20) 
+		{
+			if (el_debug > 2)
+				printk(" transmitter busy, deferred.\n");
+			return 1;
+		}
+		if (el_debug)
+			printk ("%s: transmit timed out, txsr %#2x axsr=%02x rxsr=%02x.\n",
+				dev->name, inb(TX_STATUS), inb(AX_STATUS), inb(RX_STATUS));
+		lp->stats.tx_errors++;
+		outb(TX_NORM, TX_CMD);
+		outb(RX_NORM, RX_CMD);
+		outb(AX_OFF, AX_CMD);	/* Just trigger a false interrupt. */
+		outb(AX_RX, AX_CMD);	/* Aux control, irq and receive enabled */
+		dev->tbusy = 0;
+		dev->trans_start = jiffies;
 	}
-	if (el_debug)
-	    printk ("%s: transmit timed out, txsr %#2x axsr=%02x rxsr=%02x.\n",
-		    dev->name, inb(TX_STATUS), inb(AX_STATUS), inb(RX_STATUS));
-	lp->stats.tx_errors++;
-	outb(TX_NORM, TX_CMD);
-	outb(RX_NORM, RX_CMD);
-	outb(AX_OFF, AX_CMD);	/* Just trigger a false interrupt. */
-	outb(AX_RX, AX_CMD);	/* Aux control, irq and receive enabled */
-	dev->tbusy = 0;
-	dev->trans_start = jiffies;
-    }
 
-    if (skb == NULL) {
-	dev_tint(dev);
-	return 0;
-    }
-
-    save_flags(flags);
-    /* Avoid incoming interrupts between us flipping tbusy and flipping
-       mode as the driver assumes tbusy is a faithful indicator of card
-       state */
-    cli();
-    /* Avoid timer-based retransmission conflicts. */
-    if (set_bit(0, (void*)&dev->tbusy) != 0)
-    {
-    	restore_flags(flags);
-	printk("%s: Transmitter access conflict.\n", dev->name);
-    }
-    else {
-	int gp_start = 0x800 - (ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN);
-	unsigned char *buf = skb->data;
+	if (skb == NULL) 
+	{
+		dev_tint(dev);
+		return 0;
+	}
 
-load_it_again_sam:
-	lp->tx_pkt_start = gp_start;
-    	lp->collisions = 0;
+	save_flags(flags);
 
 	/*
-	 *	Command mode with status cleared should [in theory]
-	 *	mean no more interrupts can be pending on the card.
+	 *	Avoid incoming interrupts between us flipping tbusy and flipping
+	 *	mode as the driver assumes tbusy is a faithful indicator of card
+	 *	state
 	 */
-	outb(AX_SYS, AX_CMD);
-	inb(RX_STATUS);
-	inb(TX_STATUS);
+	 
+	cli();
+	
+	/*
+	 *	Avoid timer-based retransmission conflicts. 
+	 */
+	 
+	if (set_bit(0, (void*)&dev->tbusy) != 0)
+	{
+		restore_flags(flags);
+		printk("%s: Transmitter access conflict.\n", dev->name);
+	}
+	else
+	{
+		int gp_start = 0x800 - (ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN);
+		unsigned char *buf = skb->data;
+
+load_it_again_sam:
+		lp->tx_pkt_start = gp_start;
+    		lp->collisions = 0;
+
+		/*
+		 *	Command mode with status cleared should [in theory]
+		 *	mean no more interrupts can be pending on the card.
+		 */
 	
-	lp->loading=1;
+		outb(AX_SYS, AX_CMD);
+		inb(RX_STATUS);
+		inb(TX_STATUS);
 	
-	/* 
-	 *	Turn interrupts back on while we spend a pleasant afternoon
-	 *	loading bytes into the board 
-	 */
-	restore_flags(flags);
-	outw(0x00, RX_BUF_CLR);		/* Set rx packet area to 0. */
-	outw(gp_start, GP_LOW);		/* aim - packet will be loaded into buffer start */
-	outsb(DATAPORT,buf,skb->len);	/* load buffer (usual thing each byte increments the pointer) */
-	outw(gp_start, GP_LOW);		/* the board reuses the same register */
-	if(lp->loading==2)		/* A receive upset our load, despite our best efforts */
-	{
-		if(el_debug>2)
-			printk("%s: burped during tx load.\n", dev->name);
-		goto load_it_again_sam;	/* Sigh... */
-	}
-	outb(AX_XMIT, AX_CMD);		/* fire ... Trigger xmit.  */
-	dev->trans_start = jiffies;
-    }
-
-    if (el_debug > 2)
-	printk(" queued xmit.\n");
-    dev_kfree_skb (skb, FREE_WRITE);
-    return 0;
+		lp->loading=1;
+	
+		/* 
+		 *	Turn interrupts back on while we spend a pleasant afternoon
+		 *	loading bytes into the board 
+		 */
+
+		restore_flags(flags);
+		outw(0x00, RX_BUF_CLR);		/* Set rx packet area to 0. */
+		outw(gp_start, GP_LOW);		/* aim - packet will be loaded into buffer start */
+		outsb(DATAPORT,buf,skb->len);	/* load buffer (usual thing each byte increments the pointer) */
+		outw(gp_start, GP_LOW);		/* the board reuses the same register */
+		if(lp->loading==2)		/* A receive upset our load, despite our best efforts */
+		{
+			if(el_debug>2)
+				printk("%s: burped during tx load.\n", dev->name);
+			goto load_it_again_sam;	/* Sigh... */
+		}
+		outb(AX_XMIT, AX_CMD);		/* fire ... Trigger xmit.  */
+		dev->trans_start = jiffies;
+	}
+
+	if (el_debug > 2)
+		printk(" queued xmit.\n");
+	dev_kfree_skb (skb, FREE_WRITE);
+	return 0;
 }
 
 
-/* The typical workload of the driver:
-   Handle the ether interface interrupts. */
-static void
-el_interrupt(int irq, struct pt_regs *regs)
-{
-    struct device *dev = (struct device *)(irq2dev_map[irq]);
-    struct net_local *lp;
-    int ioaddr;
-    int axsr;			/* Aux. status reg. */
+/*
+ *	The typical workload of the driver:
+ *	Handle the ether interface interrupts. 
+ */
+
+static void el_interrupt(int irq, struct pt_regs *regs)
+{
+	struct device *dev = (struct device *)(irq2dev_map[irq]);
+	struct net_local *lp;
+	int ioaddr;
+	int axsr;			/* Aux. status reg. */
 
-    if (dev == NULL  ||  dev->irq != irq) {
-	printk ("3c501 driver: irq %d for unknown device.\n", irq);
-	return;
-    }
+	if (dev == NULL  ||  dev->irq != irq) 
+	{
+		printk ("3c501 driver: irq %d for unknown device.\n", irq);
+		return;
+	}
+
+	ioaddr = dev->base_addr;
+	lp = (struct net_local *)dev->priv;
+
+	/*
+	 *	What happened ?
+	 */
+	 
+	axsr = inb(AX_STATUS);
 
-    ioaddr = dev->base_addr;
-    lp = (struct net_local *)dev->priv;
-    axsr = inb(AX_STATUS);
-
-    if (el_debug > 3)
-      printk("%s: el_interrupt() aux=%#02x", dev->name, axsr);
-    if (dev->interrupt)
-	printk("%s: Reentering the interrupt driver!\n", dev->name);
-    dev->interrupt = 1;
+	/*
+	 *	Log it
+	 */
+
+	if (el_debug > 3)
+		printk("%s: el_interrupt() aux=%#02x", dev->name, axsr);
+	if (dev->interrupt)
+		printk("%s: Reentering the interrupt driver!\n", dev->name);
+	dev->interrupt = 1;
     
-    lp->loading=2;		/* So we can spot loading interruptions */
+	lp->loading=2;		/* So we can spot loading interruptions */
 
-    if (dev->tbusy) {
+	if (dev->tbusy) 
+	{
     
-    	/*
-    	 *	Board in transmit mode.
-    	 */
+    		/*
+    		 *	Board in transmit mode.
+    		 */
     	 
-	int txsr = inb(TX_STATUS);
+		int txsr = inb(TX_STATUS);
 
-	if (el_debug > 6)
-	    printk(" txsr=%02x gp=%04x rp=%04x", txsr, inw(GP_LOW),
-		   inw(RX_LOW));
-
-	if ((axsr & 0x80) && (txsr & TX_READY) == 0) {
-	/*
-	 *	FIXME: is there a logic to whether to keep on trying or
-	 *	reset immediately ?
-	 */
-	    printk("%s: Unusual interrupt during Tx, txsr=%02x axsr=%02x"
-		   " gp=%03x rp=%03x.\n", dev->name, txsr, axsr,
-		   inw(ioaddr + EL1_DATAPTR), inw(ioaddr + EL1_RXPTR));
-	    dev->tbusy = 0;
-	    mark_bh(NET_BH);
-	} else if (txsr & TX_16COLLISIONS) {
-	/*
-	 *	Timed out
-	 */
-	    if (el_debug)
-		printk("%s: Transmit failed 16 times, ethernet jammed?\n",
-		       dev->name);
-	    outb(AX_SYS, AX_CMD);
-	    lp->stats.tx_aborted_errors++;
-	} else if (txsr & TX_COLLISION) {	/* Retrigger xmit. */
-	    if (el_debug > 6)
-		printk(" retransmitting after a collision.\n");
-	/*
-	 *	Poor little chip can't reset its own start pointer
-	 */
-	    outb(AX_SYS, AX_CMD);
-	    outw(lp->tx_pkt_start, GP_LOW);
-	    outb(AX_XMIT, AX_CMD);
-	    lp->stats.collisions++;
-	    dev->interrupt = 0;
-	    return;
-	} else {
-	/*
-	 *	It worked.. we will now fall through and receive
-	 */
-	    lp->stats.tx_packets++;
-	    if (el_debug > 6)
-		printk(" Tx succeeded %s\n",
-		       (txsr & TX_RDY) ? "." : "but tx is busy!");
-	/*
-	 *	This is safe the interrupt is atomic WRT itself.
-	 */
-	    dev->tbusy = 0;
-	    mark_bh(NET_BH);	/* In case more to transmit */
+		if (el_debug > 6)
+			printk(" txsr=%02x gp=%04x rp=%04x", txsr, inw(GP_LOW),inw(RX_LOW));
+
+		if ((axsr & 0x80) && (txsr & TX_READY) == 0) 
+		{
+			/*
+			 *	FIXME: is there a logic to whether to keep on trying or
+			 *	reset immediately ?
+			 */
+			printk("%s: Unusual interrupt during Tx, txsr=%02x axsr=%02x"
+			   " gp=%03x rp=%03x.\n", dev->name, txsr, axsr,
+			inw(ioaddr + EL1_DATAPTR), inw(ioaddr + EL1_RXPTR));
+			dev->tbusy = 0;
+			mark_bh(NET_BH);
+		} 
+		else if (txsr & TX_16COLLISIONS) 
+		{
+			/*
+			 *	Timed out
+			 */
+			if (el_debug)
+				printk("%s: Transmit failed 16 times, ethernet jammed?\n",dev->name);
+			outb(AX_SYS, AX_CMD);
+			lp->stats.tx_aborted_errors++;
+		}
+		else if (txsr & TX_COLLISION) 
+		{	
+			/*
+			 *	Retrigger xmit. 
+			 */
+			 
+			if (el_debug > 6)
+				printk(" retransmitting after a collision.\n");
+			/*
+			 *	Poor little chip can't reset its own start pointer
+			 */
+			
+			outb(AX_SYS, AX_CMD);
+			outw(lp->tx_pkt_start, GP_LOW);
+			outb(AX_XMIT, AX_CMD);
+			lp->stats.collisions++;
+			dev->interrupt = 0;
+			return;
+		}
+		else
+		{
+			/*
+			 *	It worked.. we will now fall through and receive
+			 */
+			lp->stats.tx_packets++;
+			if (el_debug > 6)
+				printk(" Tx succeeded %s\n",
+		       			(txsr & TX_RDY) ? "." : "but tx is busy!");
+			/*
+			 *	This is safe the interrupt is atomic WRT itself.
+			 */
+
+			dev->tbusy = 0;
+			mark_bh(NET_BH);	/* In case more to transmit */
+		}
 	}
-    } else {
-    
-    	/*
-    	 *	In receive mode.
-    	 */
+	else
+	{
+    		/*
+    		 *	In receive mode.
+    		 */
     	 
-	int rxsr = inb(RX_STATUS);
-	if (el_debug > 5)
-	    printk(" rxsr=%02x txsr=%02x rp=%04x", rxsr, inb(TX_STATUS),
-		   inw(RX_LOW));
-
-	/*
-	 *	Just reading rx_status fixes most errors. 
-	 */
-	if (rxsr & RX_MISSED)
-	    lp->stats.rx_missed_errors++;
-	if (rxsr & RX_RUNT) {	/* Handled to avoid board lock-up. */
-	    lp->stats.rx_length_errors++;
-	    if (el_debug > 5) printk(" runt.\n");
-	} else if (rxsr & RX_GOOD) {
-	/*
-	 *	Receive worked.
-	 */
-	    el_receive(dev);
-	} else {			/* Nothing?  Something is broken! */
-	    if (el_debug > 2)
-		printk("%s: No packet seen, rxsr=%02x **resetting 3c501***\n",
-		       dev->name, rxsr);
-	    el_reset(dev);
+		int rxsr = inb(RX_STATUS);
+		if (el_debug > 5)
+			printk(" rxsr=%02x txsr=%02x rp=%04x", rxsr, inb(TX_STATUS),inw(RX_LOW));
+		/*
+		 *	Just reading rx_status fixes most errors. 
+		 */
+		if (rxsr & RX_MISSED)
+			lp->stats.rx_missed_errors++;
+		if (rxsr & RX_RUNT) 
+		{	/* Handled to avoid board lock-up. */
+			lp->stats.rx_length_errors++;
+			if (el_debug > 5) 
+				printk(" runt.\n");
+		} 
+		else if (rxsr & RX_GOOD) 
+		{
+			/*
+			 *	Receive worked.
+			 */
+			el_receive(dev);
+		}
+		else
+		{
+			/*
+			 *	Nothing?  Something is broken!
+			 */
+			if (el_debug > 2)
+				printk("%s: No packet seen, rxsr=%02x **resetting 3c501***\n",
+					dev->name, rxsr);
+			el_reset(dev);
+		}
+		if (el_debug > 3)
+			printk(".\n");
 	}
-	if (el_debug > 3)
-	    printk(".\n");
-    }
 
-    /*
-     *	Move into receive mode 
-     */
-    outb(AX_RX, AX_CMD);
-    outw(0x00, RX_BUF_CLR);
-    inb(RX_STATUS);		/* Be certain that interrupts are cleared. */
-    inb(TX_STATUS);
-    dev->interrupt = 0;
-    return;
+	/*
+	 *	Move into receive mode 
+	 */
+
+	outb(AX_RX, AX_CMD);
+	outw(0x00, RX_BUF_CLR);
+	inb(RX_STATUS);		/* Be certain that interrupts are cleared. */
+	inb(TX_STATUS);
+	dev->interrupt = 0;
+	return;
 }
 
 
-/* We have a good packet. Well, not really "good", just mostly not broken.
-   We must check everything to see if it is good. */
-static void
-el_receive(struct device *dev)
+/*
+ *	We have a good packet. Well, not really "good", just mostly not broken.
+ *	We must check everything to see if it is good. 
+ */
+
+static void el_receive(struct device *dev)
 {
-    struct net_local *lp = (struct net_local *)dev->priv;
-    int ioaddr = dev->base_addr;
-    int pkt_len;
-    struct sk_buff *skb;
+	struct net_local *lp = (struct net_local *)dev->priv;
+	int ioaddr = dev->base_addr;
+	int pkt_len;
+	struct sk_buff *skb;
 
-    pkt_len = inw(RX_LOW);
+	pkt_len = inw(RX_LOW);
 
-    if (el_debug > 4)
-      printk(" el_receive %d.\n", pkt_len);
+	if (el_debug > 4)
+		printk(" el_receive %d.\n", pkt_len);
 
-    if ((pkt_len < 60)  ||  (pkt_len > 1536)) {
-	if (el_debug)
-	  printk("%s: bogus packet, length=%d\n", dev->name, pkt_len);
-	lp->stats.rx_over_errors++;
-	return;
-    }
+	if ((pkt_len < 60)  ||  (pkt_len > 1536)) 
+	{
+		if (el_debug)
+			printk("%s: bogus packet, length=%d\n", dev->name, pkt_len);
+		lp->stats.rx_over_errors++;
+		return;
+	}
     
-    /*
-     *	Command mode so we can empty the buffer
-     */
+	/*
+	 *	Command mode so we can empty the buffer
+	 */
      
-    outb(AX_SYS, AX_CMD);
-
-    skb = dev_alloc_skb(pkt_len+2);
-    /*
-     *	Start of frame
-     */
-    outw(0x00, GP_LOW);
-    if (skb == NULL) {
-	printk("%s: Memory squeeze, dropping packet.\n", dev->name);
-	lp->stats.rx_dropped++;
-	return;
-    } else {
-    	skb_reserve(skb,2);	/* Force 16 byte alignment */
-	skb->dev = dev;
+	outb(AX_SYS, AX_CMD);
+	skb = dev_alloc_skb(pkt_len+2);
 
 	/*
-	 *	The read increments through the bytes. The interrupt
-	 *	handler will fix the pointer when it returns to 
-	 *	receive mode.
+	 *	Start of frame
 	 */
-	 
-	insb(DATAPORT, skb_put(skb,pkt_len), pkt_len);
-	skb->protocol=eth_type_trans(skb,dev);
-	netif_rx(skb);
-	lp->stats.rx_packets++;
-    }
-    return;
-}
-
-static void 
-el_reset(struct device *dev)
-{
-    int ioaddr = dev->base_addr;
-
-    if (el_debug> 2)
-	printk("3c501 reset...");
-    outb(AX_RESET, AX_CMD);	/* Reset the chip */
-    outb(AX_LOOP, AX_CMD);	/* Aux control, irq and loopback enabled */
-    {
-	int i;
-	for (i = 0; i < 6; i++)	/* Set the station address. */
-	    outb(dev->dev_addr[i], ioaddr + i);
-    }
+
+	outw(0x00, GP_LOW);
+	if (skb == NULL) 
+	{
+		printk("%s: Memory squeeze, dropping packet.\n", dev->name);
+		lp->stats.rx_dropped++;
+		return;
+	}
+	else
+	{
+    		skb_reserve(skb,2);	/* Force 16 byte alignment */
+		skb->dev = dev;
+		/*
+		 *	The read increments through the bytes. The interrupt
+		 *	handler will fix the pointer when it returns to 
+		 *	receive mode.
+		 */
+		insb(DATAPORT, skb_put(skb,pkt_len), pkt_len);
+		skb->protocol=eth_type_trans(skb,dev);
+		netif_rx(skb);
+		lp->stats.rx_packets++;
+	}
+	return;
+}
+
+static void  el_reset(struct device *dev)
+{
+	int ioaddr = dev->base_addr;
+
+	if (el_debug> 2)
+		printk("3c501 reset...");
+	outb(AX_RESET, AX_CMD);		/* Reset the chip */
+	outb(AX_LOOP, AX_CMD);		/* Aux control, irq and loopback enabled */
+	{
+		int i;
+		for (i = 0; i < 6; i++)	/* Set the station address. */
+			outb(dev->dev_addr[i], ioaddr + i);
+	}
     
-    outw(0, RX_BUF_CLR);		/* Set rx packet area to 0. */
-    cli();			/* Avoid glitch on writes to CMD regs */
-    outb(TX_NORM, TX_CMD);		/* tx irq on done, collision */
-    outb(RX_NORM, RX_CMD);	/* Set Rx commands. */
-    inb(RX_STATUS);		/* Clear status. */
-    inb(TX_STATUS);
-    dev->interrupt = 0;
-    dev->tbusy = 0;
-    sti();
+	outw(0, RX_BUF_CLR);		/* Set rx packet area to 0. */
+	cli();				/* Avoid glitch on writes to CMD regs */
+	outb(TX_NORM, TX_CMD);		/* tx irq on done, collision */
+	outb(RX_NORM, RX_CMD);		/* Set Rx commands. */
+	inb(RX_STATUS);			/* Clear status. */
+	inb(TX_STATUS);
+	dev->interrupt = 0;
+	dev->tbusy = 0;
+	sti();
 }
 
-static int
-el1_close(struct device *dev)
+static int el1_close(struct device *dev)
 {
-    int ioaddr = dev->base_addr;
+	int ioaddr = dev->base_addr;
 
-    if (el_debug > 2)
-	printk("%s: Shutting down ethercard at %#x.\n", dev->name, ioaddr);
+	if (el_debug > 2)
+		printk("%s: Shutting down ethercard at %#x.\n", dev->name, ioaddr);
 
-    dev->tbusy = 1;
-    dev->start = 0;
+	dev->tbusy = 1;
+	dev->start = 0;
 
-    /* Free and disable the IRQ. */
-    free_irq(dev->irq);
-    outb(AX_RESET, AX_CMD);	/* Reset the chip */
-    irq2dev_map[dev->irq] = 0;
+	/*
+	 *	Free and disable the IRQ. 
+	 */
 
-    MOD_DEC_USE_COUNT;
-    return 0;
+	free_irq(dev->irq);
+	outb(AX_RESET, AX_CMD);		/* Reset the chip */
+	irq2dev_map[dev->irq] = 0;
+
+	MOD_DEC_USE_COUNT;
+	return 0;
 }
 
-static struct enet_statistics *
-el1_get_stats(struct device *dev)
+static struct enet_statistics *el1_get_stats(struct device *dev)
 {
-    struct net_local *lp = (struct net_local *)dev->priv;
-    return &lp->stats;
+	struct net_local *lp = (struct net_local *)dev->priv;
+	return &lp->stats;
 }
 
 /*
@@ -674,18 +773,21 @@
 }
 
 #ifdef MODULE
+
 static char devicename[9] = { 0, };
-static struct device dev_3c501 = {
+
+static struct device dev_3c501 = 
+{
 	devicename, /* device name is inserted by linux/drivers/net/net_init.c */
 	0, 0, 0, 0,
 	0x280, 5,
-	0, 0, 0, NULL, el1_probe };
+	0, 0, 0, NULL, el1_probe 
+};
 
 static int io=0x280;
 static int irq=5;
 	
-int
-init_module(void)
+int init_module(void)
 {
 	dev_3c501.irq=irq;
 	dev_3c501.base_addr=io;
@@ -694,19 +796,27 @@
 	return 0;
 }
 
-void
-cleanup_module(void)
+void cleanup_module(void)
 {
-	/* No need to check MOD_IN_USE, as sys_delete_module() checks. */
+	/*
+	 *	No need to check MOD_IN_USE, as sys_delete_module() checks.
+	 */
+	 
 	unregister_netdev(&dev_3c501);
 
-	/* Free up the private structure, or leak memory :-)  */
+	/*
+	 *	Free up the private structure, or leak memory :-) 
+	 */
+	 
 	kfree(dev_3c501.priv);
 	dev_3c501.priv = NULL;	/* gets re-allocated by el1_probe1 */
 
-	/* If we don't do this, we can't re-insmod it later. */
+	/*
+	 *	If we don't do this, we can't re-insmod it later. 
+	 */
 	release_region(dev_3c501.base_addr, EL1_IO_EXTENT);
 }
+
 #endif /* MODULE */
 
 /*
diff -u --recursive --new-file v1.3.45/linux/drivers/net/arcnet.c linux/drivers/net/arcnet.c
--- v1.3.45/linux/drivers/net/arcnet.c	Mon Nov 27 12:48:30 1995
+++ linux/drivers/net/arcnet.c	Thu Nov 30 14:05:00 1995
@@ -17,6 +17,22 @@
          
 	**********************
 
+	v2.21 ALPHA (95/11/29)
+	  - "Unknown protocol ID" messages now also indicate the station
+	    which sent the unrecognized packet, to aid in debugging network
+	    confusion.  Also, if anyone knows why Novell servers send packets
+	    with protocol ID 0xEC, be sure to tell me.  For now they're
+	    ignored.
+	  - Rearranged ARC_P_* handling a bit, so it makes slightly more
+	    sense.
+	  - We were clearing irq2dev_map too soon, and causing spurious
+	    "irq %d for unknown device" messages.  Moved all the set/clear
+	    irq2dev_map operations to more intelligent places.
+	  - 1.2.x kernels really didn't work with 2.20 ALPHA.  Maybe this
+	    will fix it.
+	  - Fixed the setting of set_multicast_list.  Since we don't have
+	    multicast support, there's no point in using this at all.
+
 	v2.20 ALPHA (95/11/12)
 	  - Added a bit of protocol confusion to the arc0 code to allow
 	    trxnet-compatible IPX support - and the use of all that new
@@ -156,7 +172,7 @@
 */
 
 static const char *version =
- "arcnet.c: v2.20 ALPHA 95/11/12 Avery Pennarun <apenwarr@foxnet.net>\n";
+ "arcnet.c: v2.21 ALPHA 95/11/29 Avery Pennarun <apenwarr@foxnet.net>\n";
 
  
 
@@ -166,9 +182,20 @@
 
 /* are we Linux 1.2.x? */
 #if LINUX_VERSION_CODE < 0x10300
-#define LINUX12
+# define LINUX12
 #endif
 
+/* for older kernels with older module.h */
+#ifdef LINUX12 
+# ifdef MODULE
+   char kernel_version[] = UTS_RELEASE;
+# else
+#  undef  MOD_INC_USE_COUNT
+#  define MOD_INC_USE_COUNT
+#  undef  MOD_DEC_USE_COUNT
+#  define MOD_DEC_USE_COUNT
+# endif
+#endif
 
 #include <linux/kernel.h>
 #include <linux/sched.h>
@@ -414,6 +441,7 @@
 #define ARC_P_ARP	213		/* 0xD5 */
 #define ARC_P_RARP	214		/* 0xD6 */
 #define ARC_P_IPX	250		/* 0xFA */
+#define ARC_P_NOVELL_EC	236		/* 0xEC */
 
 	/* Old RFC1051 Protocol ID's */
 #define ARC_P_IP_RFC1051 240		/* 0xF0 */
@@ -575,8 +603,9 @@
 	int length,u_char saddr, u_char daddr);
 
 static struct enet_statistics *arcnet_get_stats(struct device *dev);
+/*
 static void set_multicast_list(struct device *dev);
-
+*/
 	/* functions for header/arp/etc building */
 #ifdef LINUX12
 int arcnetA_header(unsigned char *buff,struct device *dev,
@@ -745,8 +774,10 @@
 			printk("%6s: unable to get IRQ %d (irqval=%d).\n",
 				dev->name,dev->irq, irqval);
 			return EAGAIN;
-		 }
-	 }
+		}
+		
+		irq2dev_map[dev->irq]=dev;
+	}
 	 
 	/* Grab the region so we can find another board if autoIRQ fails. */
 	request_region(dev->base_addr, ARCNET_TOTAL_SIZE,"arcnet");
@@ -765,9 +796,7 @@
 	dev->stop=arcnet_close;
 	dev->hard_start_xmit=arcnetA_send_packet;
 	dev->get_stats=arcnet_get_stats;
-#ifdef HAVE_MULTICAST
-	dev->set_multicast_list = &set_multicast_list;
-#endif
+	/*dev->set_multicast_list = &set_multicast_list;*/
 
 	/* Fill in the fields of the device structure with generic
 	 * values.
@@ -1097,7 +1126,7 @@
 #ifdef LINUX12
 	dev->type_trans=arcnetS_type_trans;
 #endif
-	BUGMSG(D_EXTRA,"ARCnet RFC1051 (NetBsd, AmiTCP) protocol initialized.\n");
+	BUGMSG(D_EXTRA,"ARCnet RFC1051 (NetBSD, AmiTCP) protocol initialized.\n");
 
 	return 0;
 }
@@ -1132,8 +1161,6 @@
 
 	BUGLVL(D_EXTRA) printk(version);
 
-	irq2dev_map[dev->irq] = dev;
-
 	BUGMSG(D_EXTRA,"arcnet_open: resetting card.\n");
 	
 	/* try to reset - twice if it fails the first time */
@@ -1198,11 +1225,7 @@
 	
 	TBUSY=1;
 	START=0;
-	
-	/* very important! */
-	irq2dev_map[dev->irq] = NULL;
 
-	
 	/* Flush TX and disable RX */
 	outb(0,INTMASK);	/* no IRQ's (except RESET, of course) */
 	outb(NOTXcmd,COMMAND);  /* stop transmit */
@@ -1883,6 +1906,8 @@
 arcnet_interrupt(int irq,struct pt_regs *regs)
 {
 	struct device *dev = (struct device *)(irq2dev_map[irq]);
+	
+	BUGMSG(D_DURING,"in arcnet_interrupt\n");
 
 	if (dev==NULL)
 	{
@@ -1915,8 +1940,8 @@
 	outb(0,INTMASK);
 	INTERRUPT = 1;
 
-	BUGMSG(D_DURING,"in net_interrupt (status=%Xh)\n",inb(STATUS));
-		
+	BUGMSG(D_DURING,"in arcnet_inthandler (status=%Xh)\n",inb(STATUS));
+
 #if 1 /* Whatever you do, don't set this to 0. */
 	do
 	{
@@ -2212,6 +2237,7 @@
 	case ARC_P_ARP:
 	case ARC_P_RARP:
 	case ARC_P_IPX:
+	case ARC_P_NOVELL_EC:
 		arcnetA_rx(lp->adev,arcsoft,length,saddr,daddr);
 		break;
 	case ARC_P_ETHER:
@@ -2221,9 +2247,10 @@
 	case ARC_P_ARP_RFC1051:
 		arcnetS_rx(lp->sdev,arcsoft,length,saddr,daddr);
 		break;
+	case ARC_P_LANSOFT: /* don't understand.  fall through. */
 	default:
-		printk("%6s: received unknown protocol %d (%Xh)\n",
-			dev->name,arcsoft[0],arcsoft[0]);
+		printk("%6s: received unknown protocol %d (%Xh) from station %d.\n",
+			dev->name,arcsoft[0],arcsoft[0],saddr);
 		lp->stats.rx_errors++;
 		lp->stats.rx_crc_errors++;
 		break;
@@ -2683,6 +2710,7 @@
 	return &lp->stats;
 }
 
+#if 0
 /* 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
@@ -2692,7 +2720,7 @@
 static void
 set_multicast_list(struct device *dev)
 {
-#if 0	  /* no promiscuous mode at all on most (all?) ARCnet models */
+#if 0	  /* no promiscuous mode at all on most ARCnet models */
 	struct arcnet_local *lp=(struct arcnet_local *)(dev->priv);
 
 	short ioaddr = dev->base_addr;
@@ -2702,6 +2730,7 @@
 		outw(99, ioaddr);		/* Disable promiscuous mode, use normal mode */
 #endif
 }
+#endif
 
 /* Create the ARCnet ClientData header for an arbitrary protocol layer
  *
@@ -2962,9 +2991,10 @@
 	case ARC_P_IP:		return htons(ETH_P_IP);
 	case ARC_P_ARP:		return htons(ETH_P_ARP);
 	case ARC_P_RARP:	return htons(ETH_P_RARP);
-	case ARC_P_IPX:		return htons(ETH_P_802_3);
-	case ARC_P_ATALK:   return htons(ETH_P_ATALK); /* untested appletalk */
-	case ARC_P_LANSOFT: /* don't understand.  fall through. */
+
+	case ARC_P_IPX:
+	case ARC_P_NOVELL_EC:
+		return htons(ETH_P_802_3);
 	default:
 		BUGMSG(D_EXTRA,"received packet of unknown protocol id %d (%Xh)\n",
 				head->protocol_id,head->protocol_id);
@@ -3005,6 +3035,7 @@
 	{
 	case ARC_P_IP_RFC1051:	return htons(ETH_P_IP);
 	case ARC_P_ARP_RFC1051:	return htons(ETH_P_ARP);
+	case ARC_P_ATALK:   return htons(ETH_P_ATALK); /* untested appletalk */
 	default:
 		BUGMSG(D_EXTRA,"received packet of unknown protocol id %d (%Xh)\n",
 				head->protocol_id,head->protocol_id);
@@ -3067,7 +3098,14 @@
 cleanup_module(void)
 {
 	if (thiscard.start) arcnet_close(&thiscard);
-	if (thiscard.irq) free_irq(thiscard.irq);
+	
+	if (thiscard.irq)
+	{
+		free_irq(thiscard.irq);
+		/* very important! */
+		irq2dev_map[thiscard.irq] = NULL;
+	}
+	
 	if (thiscard.base_addr) release_region(thiscard.base_addr,
 						ARCNET_TOTAL_SIZE);
 	unregister_netdev(&thiscard);
diff -u --recursive --new-file v1.3.45/linux/drivers/net/de4x5.c linux/drivers/net/de4x5.c
--- v1.3.45/linux/drivers/net/de4x5.c	Sat Nov 25 19:04:41 1995
+++ linux/drivers/net/de4x5.c	Mon Dec 11 09:46:54 1995
@@ -435,7 +435,7 @@
 /*static void    srom_busy(u_int command, u_long address);*/
 static void    sendto_srom(u_int command, u_long addr);
 static int     getfrom_srom(u_long addr);
-static void    SetMulticastFilter(struct device *dev, int num_addrs, char *addrs);
+static void    SetMulticastFilter(struct device *dev);
 static int     get_hw_addr(struct device *dev);
 
 static void    eisa_probe(struct device *dev, u_long iobase);
@@ -901,7 +901,7 @@
   barrier();
 
   /* Build the setup frame depending on filtering mode */
-  SetMulticastFilter(dev, 0, NULL);
+  SetMulticastFilter(dev);
 
   if (lp->chipset != DC21140) {
     load_packet(dev, lp->setup_frame, HASH_F|TD_SET|SETUP_FRAME_LEN, NULL);
@@ -1386,40 +1386,33 @@
 static void
 set_multicast_list(struct device *dev)
 {
-	struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
-	u_long iobase = dev->base_addr;
+  struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+  u_long iobase = dev->base_addr;
 
-	/* First, double check that the adapter is open */
-	if (irq2dev_map[dev->irq] != NULL) 
-	{
-		if (num_addrs >= 0) 
-		{
-		   	SetMulticastFilter(dev);
-			if (lp->setup_f == HASH_PERF) 
-			{
-				load_packet(dev, lp->setup_frame, TD_IC | HASH_F | TD_SET | 
+  /* First, double check that the adapter is open */
+  if (irq2dev_map[dev->irq] != NULL) {
+    if (dev->flags & IFF_PROMISC) {         /* set promiscuous mode */
+      u32 omr;
+      omr = inl(DE4X5_OMR);
+      omr |= OMR_PR;
+      outl(omr, DE4X5_OMR);
+    } else { 
+      SetMulticastFilter(dev);
+      if (lp->setup_f == HASH_PERF) {
+	load_packet(dev, lp->setup_frame, TD_IC | HASH_F | TD_SET | 
 		                                        SETUP_FRAME_LEN, NULL);
-      			}
-      			else
-      			{
-				load_packet(dev, lp->setup_frame, TD_IC | PERFECT_F | TD_SET | 
+      } else {
+	load_packet(dev, lp->setup_frame, TD_IC | PERFECT_F | TD_SET | 
 		                                        SETUP_FRAME_LEN, NULL);
-			}
+      }
+      
+      lp->tx_new = (++lp->tx_new) % lp->txRingSize;
+      outl(POLL_DEMAND, DE4X5_TPD);                /* Start the TX */
+      dev->trans_start = jiffies;
+    }
+  }
 
-			lp->tx_new = (++lp->tx_new) % lp->txRingSize;
-			outl(POLL_DEMAND, DE4X5_TPD);                /* Start the TX */
-			dev->trans_start = jiffies;
-		}
-		else
-		{ 
-			/* set promiscuous mode */
-			u32 omr;
-			omr = inl(DE4X5_OMR);
-			omr |= OMR_PR;
-			outl(omr, DE4X5_OMR);
-		}
-	}
-	return;
+  return;
 }
 
 /*
@@ -1439,76 +1432,49 @@
   unsigned char *addrs;
 
   omr = inl(DE4X5_OMR);
+  omr &= ~OMR_PR;
   pa = build_setup_frame(dev, ALL);          /* Build the basic frame */
 
-  if (lp->setup_f == HASH_PERF) 
-  {
-  	if (num_addrs >= HASH_TABLE_LEN || (dev->flags&IFF_ALLMULTI)) 
-  	{
-  		/* Pass all multicasts */
-     		omr |= OMR_PM;
-	}
-	else
-	{
-		omr &= ~OMR_PM;
+  if ((dev->flags & IFF_ALLMULTI) || (dev->mc_count > 14)) {
+    omr |= OMR_PM;                           /* Pass all multicasts */
+  } else if (lp->setup_f == HASH_PERF) {
                                              /* Now update the MCA table */
-		for (i=0;i<num_addrs;i++) 
-		{     
-			/* for each address in the list */
-			addrs=dmi->dmi_addr;
-			dmi=dmi->next;
-			if ((*addrs & 0x01) == 1) 
-			{       
-				/* multicast address? */ 
-				crc = 0xffffffff;                  /* init CRC for each address */
-				for (byte=0;byte<ETH_ALEN;byte++) 
-				{
-					/* for each address byte */
-	                        	/* process each address bit */ 
-					for (bit = *addrs++,j=0;j<8;j++, bit>>=1) 
-					{
-						crc = (crc >> 1) ^ (((crc ^ bit) & 0x01) ? poly : 0);
-					}
-				}
-				hashcode = crc & HASH_BITS;        /* hashcode is 9 LSb of CRC */
-
-				byte = hashcode >> 3;              /* bit[3-8] -> byte in filter */
-				bit = 1 << (hashcode & 0x07);      /* bit[0-2] -> bit in byte */
-
-				byte <<= 1;                        /* calc offset into setup frame */
-				if (byte & 0x02) 
-				{
-					byte -= 1;
-				}
-				lp->setup_frame[byte] |= bit;
-	  
-			}
-			else
-			{                             /* skip this address */
-				addrs += ETH_ALEN;
-			}
-		}
+    for (i=0;i<dev->mc_count;i++) {          /* for each address in the list */
+      addrs=dmi->dmi_addr;
+      dmi=dmi->next;
+      if ((*addrs & 0x01) == 1) {            /* multicast address? */ 
+	crc = 0xffffffff;                    /* init CRC for each address */
+	for (byte=0;byte<ETH_ALEN;byte++) {  /* for each address byte */
+	                                     /* process each address bit */ 
+	  for (bit = *addrs++,j=0;j<8;j++, bit>>=1) {
+	    crc = (crc >> 1) ^ (((crc ^ bit) & 0x01) ? poly : 0);
+	  }
 	}
-	else
-	{                                   /* Perfect filtering */
-		omr &= ~OMR_PM;
-		for (j=0; j<dev->mc_count; j++) 
-		{
-			addrs=dmi->dmi_addr;
-			dmi=dmi->next;
-			for (i=0; i<ETH_ALEN; i++) 
-			{ 
-				*(pa + (i&1)) = *addrs++;
-				if (i & 0x01) pa += 4;
-			}
-		}
+	hashcode = crc & HASH_BITS;          /* hashcode is 9 LSb of CRC */
+	
+	byte = hashcode >> 3;                /* bit[3-8] -> byte in filter */
+	bit = 1 << (hashcode & 0x07);        /* bit[0-2] -> bit in byte */
+	
+	byte <<= 1;                          /* calc offset into setup frame */
+	if (byte & 0x02) {
+	  byte -= 1;
 	}
+	lp->setup_frame[byte] |= bit;
+      }
+    }
+  } else {                                   /* Perfect filtering */
+    for (j=0; j<dev->mc_count; j++) {
+      addrs=dmi->dmi_addr;
+      dmi=dmi->next;
+      for (i=0; i<ETH_ALEN; i++) { 
+	*(pa + (i&1)) = *addrs++;
+	if (i & 0x01) pa += 4;
+      }
+    }
+  }
+  outl(omr, DE4X5_OMR);
 
-	if (dev->mc_count == 0)
-		omr &= ~OMR_PR;
-	outl(omr, DE4X5_OMR);
-
-	return;
+  return;
 }
 
 /*
@@ -2616,10 +2582,10 @@
       if (ioc->len != HASH_TABLE_LEN) {         /* MCA changes */
 	if (!(status = verify_area(VERIFY_READ, (void *)ioc->data, ETH_ALEN * ioc->len))) {
 	  memcpy_fromfs(tmp.addr, ioc->data, ETH_ALEN * ioc->len);
-	  set_multicast_list(dev, ioc->len, tmp.addr);
+	  set_multicast_list(dev);
 	}
       } else {
-	set_multicast_list(dev, ioc->len, NULL);
+	set_multicast_list(dev);
       }
     } else {
       status = -EPERM;
@@ -2628,7 +2594,7 @@
     break;
   case DE4X5_CLR_MCA:                /* Clear all multicast addresses */
     if (suser()) {
-      set_multicast_list(dev, 0, NULL);
+      set_multicast_list(dev);
     } else {
       status = -EPERM;
     }
diff -u --recursive --new-file v1.3.45/linux/drivers/net/ppp.c linux/drivers/net/ppp.c
--- v1.3.45/linux/drivers/net/ppp.c	Thu Nov  9 11:23:50 1995
+++ linux/drivers/net/ppp.c	Thu Nov 30 15:59:40 1995
@@ -1846,8 +1846,8 @@
 				if (ppp->flags & SC_DEBUG)
 					printk (KERN_DEBUG
 						"ppp_tty_read: no data "
-						"(EWOULDBLOCK)\n");
-				return -EWOULDBLOCK;
+						"(EAGAIN)\n");
+				return -EAGAIN;
 			}
 			current->timeout = 0;
 
@@ -3359,7 +3359,7 @@
 
 		status = register_netdev (dev);
 		if (status == 0) {
-			printk ("registered device %s\n", dev->name);
+			printk (KERN_INFO "registered device %s\n", dev->name);
 			return (ppp);
 		}
 
diff -u --recursive --new-file v1.3.45/linux/drivers/net/slhc.c linux/drivers/net/slhc.c
--- v1.3.45/linux/drivers/net/slhc.c	Thu Nov  9 11:23:50 1995
+++ linux/drivers/net/slhc.c	Mon Dec 11 08:40:14 1995
@@ -131,7 +131,7 @@
 			kfree((unsigned char *)comp);
 			return NULL;
 		}
-		memset(comp->tstate, 0, rslots * sizeof(struct cstate));
+		memset(comp->tstate, 0, tslots * sizeof(struct cstate));
 		comp->tslot_limit = tslots - 1;
 	}
 
diff -u --recursive --new-file v1.3.45/linux/drivers/net/tulip.c linux/drivers/net/tulip.c
--- v1.3.45/linux/drivers/net/tulip.c	Mon Nov 27 12:48:30 1995
+++ linux/drivers/net/tulip.c	Thu Nov 30 11:15:53 1995
@@ -684,7 +684,7 @@
 		/* Log any net taps. */
 		printk("%s: Promiscuous mode enabled.\n", dev->name);
 	}
-	else if (dev->mc_count > 15 || (dev->mc_flags&IFF_ALLMULTI)) 
+	else if (dev->mc_count > 15 || (dev->flags&IFF_ALLMULTI)) 
 	{
 		/* Too many to filter perfectly -- accept all multicasts. */
 		outl(csr6 | 0x0080, ioaddr + CSR6);
@@ -727,11 +727,12 @@
 set_mac_address(struct device *dev, void *addr)
 {
 	int i;
+	struct sockaddr *sa=(struct sockaddr *)addr;
 	if (dev->start)
 		return -EBUSY;
 	printk("%s: Setting MAC address to ", dev->name);
 	for (i = 0; i < 6; i++)
-		printk(" %2.2x", dev->dev_addr[i] = ((unsigned char *)addr)[i]);
+		printk(" %2.2x", dev->dev_addr[i] = sa->sa_data[i]);
 	printk(".\n");
 	return 0;
 }
diff -u --recursive --new-file v1.3.45/linux/drivers/pci/pci.c linux/drivers/pci/pci.c
--- v1.3.45/linux/drivers/pci/pci.c	Sat Nov 25 19:04:47 1995
+++ linux/drivers/pci/pci.c	Mon Dec 11 07:27:14 1995
@@ -71,6 +71,7 @@
 	DEVICE( CIRRUS,		CIRRUS_5434_8,	"GD 5434"),
 	DEVICE( CIRRUS,		CIRRUS_6729,	"CL 6729"),
 	DEVICE( CIRRUS,		CIRRUS_7542,	"CL 7542"),
+	DEVICE( CIRRUS,		CIRRUS_7543,	"CL 7543"),
 	DEVICE( AMD,		AMD_LANCE,	"79C970"),
 	DEVICE( AMD,		AMD_SCSI,	"53C974"),
 	DEVICE( TRIDENT,	TRIDENT_9420,	"TG 9420"),
@@ -141,7 +142,8 @@
 	DEVICE( ALLIANCE,	ALLIANCE_PROVIDEO, "Provideo"),
 	DEVICE( MUTECH,		MUTECH_MV1000,	"MV-1000"),
 	DEVICE( ZEITNET,	ZEITNET_1221,	"1221"),
-	DEVICE( HAL,		HAL_RIO,	"RIO host"),
+	DEVICE( SPECIALIX,	SPECIALIX_XIO,	"XIO/SIO host"),
+	DEVICE( SPECIALIX,	SPECIALIX_RIO,	"RIO host"),
 	DEVICE( CYCLADES,	CYCLADES_Y,	"Cyclome-Y"),
 	DEVICE( SYMPHONY,	SYMPHONY_101,	"82C101"),
 	DEVICE( TEKRAM,		TEKRAM_DC290,	"DC-290"),
@@ -422,7 +424,7 @@
 	      case PCI_VENDOR_ID_ALLIANCE:	return "Alliance";
 	      case PCI_VENDOR_ID_MUTECH:	return "Mutech";
 	      case PCI_VENDOR_ID_ZEITNET:	return "ZeitNet";
-	      case PCI_VENDOR_ID_HAL:		return "HAL";
+	      case PCI_VENDOR_ID_SPECIALIX:	return "Specialix";
 	      case PCI_VENDOR_ID_CYCLADES:	return "Cyclades";
 	      case PCI_VENDOR_ID_SYMPHONY:	return "Symphony";
 	      case PCI_VENDOR_ID_TEKRAM:	return "Tekram";
@@ -724,7 +726,7 @@
 		dev->device = (l >> 16) & 0xffff;
 
 		/*
-		 * Check to see if we now about this device and report
+		 * Check to see if we know about this device and report
 		 * a message at boot time.  This is the only way to
 		 * learn about new hardware...
 		 */
diff -u --recursive --new-file v1.3.45/linux/drivers/scsi/eata_dma.c linux/drivers/scsi/eata_dma.c
--- v1.3.45/linux/drivers/scsi/eata_dma.c	Wed Nov  8 07:11:34 1995
+++ linux/drivers/scsi/eata_dma.c	Mon Dec 11 07:27:14 1995
@@ -48,13 +48,12 @@
  * Thanks also to Greg Hosler who did a lot of testing and  *
  * found quite a number of bugs during the development.	    *
  ************************************************************
- *  last change: 95/11/03                 OS: Linux 1.3.37  *
+ *  last change: 95/11/29                 OS: Linux 1.3.45  *
  ************************************************************/
 
 /* Look in eata_dma.h for configuration and revision information */
 
 #include <linux/module.h>
- 
 #include <linux/kernel.h>
 #include <linux/sched.h>
 #include <linux/string.h>
@@ -98,7 +97,7 @@
 static struct eata_sp *status = 0;   /* Statuspacket array   */
 static void *dma_scratch = 0;
 
-static u32 fake_int_base;
+static struct eata_register *fake_int_base;
 static int fake_int_result;
 static int fake_int_happened;
 
@@ -117,9 +116,9 @@
 
 void eata_fake_int_handler(s32 irq, struct pt_regs * regs)
 {
-    fake_int_result = inb(fake_int_base + HA_RSTATUS);
+    fake_int_result = inb((ulong)fake_int_base + HA_RSTATUS);
     fake_int_happened = TRUE;
-    DBG(DBG_INTR3, printk("eata_fake_int_handler called irq%d base %#x"
+    DBG(DBG_INTR3, printk("eata_fake_int_handler called irq%d base %p"
 			  " res %#x\n", irq, fake_int_base, fake_int_result));
     return;
 }
@@ -166,141 +165,141 @@
     save_flags(flags);
     cli();
 
-    for (x = 1, sh = first_HBA; x <= registered_HBAs; x++, sh = SD(sh)->prev) {
+    for (x = 1, sh = first_HBA; x <= registered_HBAs; x++, sh = SD(sh)->next) {
 	if (sh->irq != irq)
 	    continue;
-	if (!(inb((uint)sh->base + HA_RAUXSTAT) & HA_AIRQ))
-	    continue;
-	
-	int_counter++;
-	
-	sp=&SD(sh)->sp;
-	
-	if((cp = sp->ccb) == NULL) {
-	    eata_stat = inb(base + HA_RSTATUS);
-	    printk("eata_dma: Board: %x Spurious IRQ %lx "
-		   "received. CCB pointer not set.\n", base, (long)cp);
-	    restore_flags(flags);
-	    return;
-	}
-	cmd = cp->cmd;
-	base = (uint) cmd->host->base;
-	
-	hba_stat = sp->hba_stat;
-	
-	scsi_stat = (sp->scsi_stat >> 1) & 0x1f; 
-       
-	if (sp->EOC == FALSE) {
-	    eata_stat = inb(base + HA_RSTATUS);
-	    printk("eata_dma: int_handler, board: %x cmd %lx returned "
-		   "unfinished.\nEATA: %x HBA: %x SCSI: %x spadr %lx spadrirq "
-		   "%lx, irq%d\n", base, (long)cp, eata_stat, hba_stat, 
-		   scsi_stat,(long)&status, (long)&status[irq], irq);
-	    DBG(DBG_DELAY, DEL2(800));
-	    restore_flags(flags);
-	    return;
-	} 
-	
-	if (cp->status == LOCKED) {
-	    cp->status = FREE;
-	    eata_stat = inb(base + HA_RSTATUS);
-	    printk("eata_dma: int_handler, freeing locked queueslot\n");
-	    DBG(DBG_INTR && DBG_DELAY, DEL2(800));
-	    restore_flags(flags);
-	    return;
-	}
 	
-	eata_stat = inb(base + HA_RSTATUS); 
-	DBG(DBG_INTR, printk("IRQ %d received, base %#.4x, pid %ld, target: "
-			     "%x, lun: %x, ea_s: %#.2x, hba_s: %#.2x \n", 
-			     irq, base, cmd->pid, cmd->target, cmd->lun, 
-			     eata_stat, hba_stat));
-
-	switch (hba_stat) {
-	case HA_NO_ERROR:	/* NO Error */
-	    if (scsi_stat == CONDITION_GOOD
-		&& cmd->device->type == TYPE_DISK
-		&& (HD(cmd)->t_state[cp->cp_channel][cp->cp_id] == RESET))
-		result = DID_BUS_BUSY << 16;	    
-	    else if (scsi_stat == GOOD) {
-		HD(cmd)->t_state[cp->cp_channel][cp->cp_id] = OK;
-		if(HD(cmd)->do_latency == TRUE && cp->timestamp) {
-		    uint time;
-		    time = jiffies - cp->timestamp;
-		    if((cp->rw_latency) == TRUE) { /* was WRITE */
-			if(HD(cmd)->writes_lat[cp->sizeindex][1] > time)
-			    HD(cmd)->writes_lat[cp->sizeindex][1] = time;
-			if(HD(cmd)->writes_lat[cp->sizeindex][2] < time)
-			    HD(cmd)->writes_lat[cp->sizeindex][2] = time;
-			HD(cmd)->writes_lat[cp->sizeindex][3] += time;
-			HD(cmd)->writes_lat[cp->sizeindex][0]++;
-		    } else {
-			if(HD(cmd)->reads_lat[cp->sizeindex][1] > time)
-			    HD(cmd)->reads_lat[cp->sizeindex][1] = time;
-			if(HD(cmd)->reads_lat[cp->sizeindex][2] < time)
-			    HD(cmd)->reads_lat[cp->sizeindex][2] = time;
-			HD(cmd)->reads_lat[cp->sizeindex][3] += time;
-			HD(cmd)->reads_lat[cp->sizeindex][0]++;
+	while(inb((uint)sh->base + HA_RAUXSTAT) & HA_AIRQ) {
+	    
+	    int_counter++;
+	    
+	    sp = &SD(sh)->sp;
+	    cp = sp->ccb;
+	    
+	    if(cp == NULL) {
+		eata_stat = inb((uint)sh->base + HA_RSTATUS);
+		printk("eata_dma: int_handler, Spurious IRQ %d "
+		       "received. CCB pointer not set.\n", irq);
+		break;
+	    }
+
+	    cmd = cp->cmd;
+	    base = (uint) cmd->host->base;
+       	    hba_stat = sp->hba_stat;
+	    
+	    scsi_stat = (sp->scsi_stat >> 1) & 0x1f; 
+	    
+	    if (sp->EOC == FALSE) {
+		eata_stat = inb(base + HA_RSTATUS);
+		printk("eata_dma: int_handler, board: %x cmd %lx returned "
+		       "unfinished.\nEATA: %x HBA: %x SCSI: %x spadr %lx "
+		       "spadrirq %lx, irq%d\n", base, (long)cp, eata_stat, 
+		       hba_stat, scsi_stat,(long)&status, (long)&status[irq], 
+		       irq);
+		DBG(DBG_DELAY, DEL2(800));
+		break;
+	    } 
+	    
+	    if (cp->status == LOCKED) {
+		cp->status = FREE;
+		eata_stat = inb(base + HA_RSTATUS);
+		printk("eata_dma: int_handler, freeing locked queueslot\n");
+		DBG(DBG_INTR && DBG_DELAY, DEL2(800));
+		break;
+	    }
+	    
+	    eata_stat = inb(base + HA_RSTATUS); 
+	    DBG(DBG_INTR, printk("IRQ %d received, base %#.4x, pid %ld, "
+				 "target: %x, lun: %x, ea_s: %#.2x, hba_s: "
+				 "%#.2x \n", irq, base, cmd->pid, cmd->target,
+				 cmd->lun, eata_stat, hba_stat));
+	    
+	    switch (hba_stat) {
+	    case HA_NO_ERROR:	/* NO Error */
+		if (scsi_stat == CONDITION_GOOD
+		    && cmd->device->type == TYPE_DISK
+		    && (HD(cmd)->t_state[cp->cp_channel][cp->cp_id] == RESET))
+		    result = DID_BUS_BUSY << 16;	    
+		else if (scsi_stat == GOOD) {
+		    HD(cmd)->t_state[cp->cp_channel][cp->cp_id] = OK;
+		    if(HD(cmd)->do_latency == TRUE && cp->timestamp) {
+			uint time;
+			time = jiffies - cp->timestamp;
+			if((cp->rw_latency) == TRUE) { /* was WRITE */
+			    if(HD(cmd)->writes_lat[cp->sizeindex][1] > time)
+				HD(cmd)->writes_lat[cp->sizeindex][1] = time;
+			    if(HD(cmd)->writes_lat[cp->sizeindex][2] < time)
+				HD(cmd)->writes_lat[cp->sizeindex][2] = time;
+			    HD(cmd)->writes_lat[cp->sizeindex][3] += time;
+			    HD(cmd)->writes_lat[cp->sizeindex][0]++;
+			} else {
+			    if(HD(cmd)->reads_lat[cp->sizeindex][1] > time)
+				HD(cmd)->reads_lat[cp->sizeindex][1] = time;
+			    if(HD(cmd)->reads_lat[cp->sizeindex][2] < time)
+				HD(cmd)->reads_lat[cp->sizeindex][2] = time;
+			    HD(cmd)->reads_lat[cp->sizeindex][3] += time;
+			    HD(cmd)->reads_lat[cp->sizeindex][0]++;
+			}
 		    }
 		}
-	    }
-	    else if (scsi_stat == CHECK_CONDITION
-		     && cmd->device->type == TYPE_DISK
-		     && (cmd->sense_buffer[2] & 0xf) == RECOVERED_ERROR)
-		result = DID_BUS_BUSY << 16;
-	    else
-		result = DID_OK << 16;
-	    HD(cmd)->t_timeout[cp->cp_channel][cp->cp_id] = OK;
-	    break;
-	case HA_ERR_SEL_TO:	/* Selection Timeout */
-	    result = DID_BAD_TARGET << 16;  
-	    break;
-	case HA_ERR_CMD_TO:	/* Command Timeout   */
-	    if (HD(cmd)->t_timeout[cp->cp_channel][cp->cp_id] > 1)
+		else if (scsi_stat == CHECK_CONDITION
+			 && cmd->device->type == TYPE_DISK
+			 && (cmd->sense_buffer[2] & 0xf) == RECOVERED_ERROR)
+		    result = DID_BUS_BUSY << 16;
+		else
+		    result = DID_OK << 16;
+		HD(cmd)->t_timeout[cp->cp_channel][cp->cp_id] = OK;
+		break;
+	    case HA_ERR_SEL_TO:	        /* Selection Timeout */
+		result = DID_BAD_TARGET << 16;  
+		break;
+	    case HA_ERR_CMD_TO:	        /* Command Timeout   */
+		if (HD(cmd)->t_timeout[cp->cp_channel][cp->cp_id] > 1)
+		    result = DID_ERROR << 16;
+		else {
+		    result = DID_TIME_OUT << 16;
+		    HD(cmd)->t_timeout[cp->cp_channel][cp->cp_id]++;
+		}
+		break;
+	    case HA_ERR_RESET:		/* SCSI Bus Reset Received */
+	    case HA_INIT_POWERUP:	/* Initial Controller Power-up */
+		if (cmd->device->type != TYPE_TAPE)
+		    result = DID_BUS_BUSY << 16;
+		else
+		    result = DID_ERROR << 16;
+		
+		for (i = 0; i < MAXTARGET; i++)
+		    HD(cmd)->t_state[cp->cp_channel][i] = RESET;
+		break;
+	    case HA_UNX_BUSPHASE:	/* Unexpected Bus Phase */
+	    case HA_UNX_BUS_FREE:	/* Unexpected Bus Free */
+	    case HA_BUS_PARITY:	        /* Bus Parity Error */
+	    case HA_SCSI_HUNG:	        /* SCSI Hung */
+	    case HA_UNX_MSGRJCT:	/* Unexpected Message Reject */
+	    case HA_RESET_STUCK:        /* SCSI Bus Reset Stuck */
+	    case HA_RSENSE_FAIL:        /* Auto Request-Sense Failed */
+	    case HA_PARITY_ERR:	        /* Controller Ram Parity */
+	    default:
 		result = DID_ERROR << 16;
-	    else {
-		result = DID_TIME_OUT << 16;
-		HD(cmd)->t_timeout[cp->cp_channel][cp->cp_id]++;
+		break;
 	    }
-	    break;
-	case HA_ERR_RESET:		/* SCSI Bus Reset Received */
-	case HA_INIT_POWERUP:		/* Initial Controller Power-up */
-	    if (cmd->device->type != TYPE_TAPE)
-		result = DID_BUS_BUSY << 16;
-	    else
-		result = DID_ERROR << 16;
+	    cmd->result = result | (scsi_stat << 1); 
 	    
-	    for (i = 0; i < MAXTARGET; i++)
-		HD(cmd)->t_state[cp->cp_channel][i] = RESET;
-	    break;
-	case HA_UNX_BUSPHASE:	    /* Unexpected Bus Phase */
-	case HA_UNX_BUS_FREE:	    /* Unexpected Bus Free */
-	case HA_BUS_PARITY:	    /* Bus Parity Error */
-	case HA_SCSI_HUNG:	    /* SCSI Hung */
-	case HA_UNX_MSGRJCT:	    /* Unexpected Message Reject */
-	case HA_RESET_STUCK:	    /* SCSI Bus Reset Stuck */
-	case HA_RSENSE_FAIL:	    /* Auto Request-Sense Failed */
-	case HA_PARITY_ERR:	    /* Controller Ram Parity */
-	default:
-	    result = DID_ERROR << 16;
-	    break;
-	}
-	cmd->result = result | (scsi_stat << 1); 
-	
 #if DBG_INTR2
-	if (scsi_stat || result || hba_stat || eata_stat != 0x50 
-	    || cmd->scsi_done == NULL || cmd->device->id == 7) 
-	    printk("HBA: %d, channel %d, id: %d, lun %d, pid %ld:\n" 
-		   "eata_stat %#x, hba_stat %#.2x, scsi_stat %#.2x, "
-		   "sense_key: %#x, result: %#.8x\n", 
-		   x, cmd->device->channel, cmd->device->id, cmd->device->lun,
-		   cmd->pid, eata_stat, hba_stat, scsi_stat, 
-		   cmd->sense_buffer[2] & 0xf, cmd->result); 
-	DBG(DBG_INTR&&DBG_DELAY,DEL2(800));
+	    if (scsi_stat || result || hba_stat || eata_stat != 0x50 
+		|| cmd->scsi_done == NULL || cmd->device->id == 7) 
+		printk("HBA: %d, channel %d, id: %d, lun %d, pid %ld:\n" 
+		       "eata_stat %#x, hba_stat %#.2x, scsi_stat %#.2x, "
+		       "sense_key: %#x, result: %#.8x\n", x, 
+		       cmd->device->channel, cmd->device->id, cmd->device->lun,
+		       cmd->pid, eata_stat, hba_stat, scsi_stat, 
+		       cmd->sense_buffer[2] & 0xf, cmd->result); 
+	    DBG(DBG_INTR&&DBG_DELAY,DEL2(800));
 #endif
-	
-	cp->status = FREE;	    /* now we can release the slot  */
-	cmd->scsi_done(cmd);
+	    
+	    cp->status = FREE;	    /* now we can release the slot  */
+	    cmd->scsi_done(cmd);
+	}
     }
     restore_flags(flags);
     
@@ -471,7 +470,7 @@
     if (cmd->use_sg) {
 	cp->scatter = TRUE;	/* SG mode     */
 	if (cp->sg_list == NULL) {
-	    cp->sg_list = kmalloc(SG_SIZE_BIG*sizeof(struct eata_sg_list),
+	    cp->sg_list = kmalloc(sh->sg_tablesize * sizeof(struct eata_sg_list),
 				  GFP_ATOMIC | GFP_DMA);
 	}
 	if (cp->sg_list == NULL)
@@ -529,8 +528,8 @@
 
 int eata_abort(Scsi_Cmnd * cmd)
 {
-    ulong flags;
     ulong loop = R_LIMIT;
+    ulong flags;
 
     save_flags(flags);
     cli();
@@ -539,24 +538,14 @@
 			   " reason %x\n", cmd->pid, cmd->target, cmd->lun, 
 			   cmd->abort_reason));
     DBG(DBG_ABNORM && DBG_DELAY, DEL2(500));
-    
-    
-    while (inb((u32)(cmd->host->base) + HA_RAUXSTAT) & HA_ABUSY)
+
+    while (inb((u32)(cmd->host->base) + HA_RAUXSTAT) & HA_ABUSY) {
 	if (--loop == 0) {
 	    printk("eata_dma: abort, timeout error.\n");
 	    restore_flags(flags);
 	    DBG(DBG_ABNORM && DBG_DELAY, DEL2(500));
 	    return (SCSI_ABORT_ERROR);
 	}
-    if (CD(cmd)->status == USED) {
-	DBG(DBG_ABNORM, printk("Returning: SCSI_ABORT_BUSY\n"));
-	restore_flags(flags);
-	return (SCSI_ABORT_BUSY);  /* SNOOZE */ 
-    }
-    if (CD(cmd)->status == FREE) {
-	DBG(DBG_ABNORM, printk("Returning: SCSI_ABORT_NOT_RUNNING\n")); 
-	restore_flags(flags);
-	return (SCSI_ABORT_NOT_RUNNING);
     }
     if (CD(cmd)->status == RESET) {
 	restore_flags(flags);
@@ -570,6 +559,16 @@
 	DBG(DBG_ABNORM && DBG_DELAY, DEL2(500));
 	return (SCSI_ABORT_NOT_RUNNING);
     }
+    if (CD(cmd)->status == USED) {
+	DBG(DBG_ABNORM, printk("Returning: SCSI_ABORT_BUSY\n"));
+	restore_flags(flags);
+	return (SCSI_ABORT_BUSY);  /* SNOOZE */ 
+    }
+    if (CD(cmd)->status == FREE) {
+	DBG(DBG_ABNORM, printk("Returning: SCSI_ABORT_NOT_RUNNING\n")); 
+	restore_flags(flags);
+	return (SCSI_ABORT_NOT_RUNNING);
+    }
     restore_flags(flags);
     panic("eata_dma: abort: invalid slot status\n");
 }
@@ -728,7 +727,7 @@
     cp->cp_cdb[4] = 56;
     cp->cp_cdb[5] = 0;
 
-    fake_int_base = base;
+    fake_int_base = (struct eata_register *) base;
     fake_int_result = FALSE;
     fake_int_happened = FALSE;
 
@@ -828,7 +827,6 @@
 
 void print_config(struct get_conf *gc)
 {
-    printk("Please check values: (read config data)\n");
     printk("LEN: %d ver:%d OCS:%d TAR:%d TRNXFR:%d MORES:%d DMAS:%d\n",
 	   (u32) ntohl(gc->len), gc->version,
 	   gc->OCS_enabled, gc->TAR_support, gc->TRNXFR, gc->MORE_support,
@@ -1075,7 +1073,7 @@
 		       "This might be a PM2012 with a defective Firmware\n");
 	}
     }
-    
+
     if (gc->SECOND)
 	hd->primary = FALSE;
     else
@@ -1193,7 +1191,7 @@
 				    pci_index, &pci_bus, &pci_device_fn))
 		break;
 	    DBG(DBG_PROBE && DBG_PCI, 
-		printk("eata_dma: HBA at bus %d, device %d,"
+		printk("eata_dma: find_PCI, HBA at bus %d, device %d,"
 		       " function %d, index %d\n", (s32)pci_bus, 
 		       (s32)((pci_device_fn & 0xf8) >> 3),
 		       (s32)(pci_device_fn & 7), pci_index));
@@ -1206,17 +1204,17 @@
 					       (u16 *) & com_adr))) {
 			if (!((com_adr & PCI_COMMAND_IO) && 
 			      (com_adr & PCI_COMMAND_MASTER))) {
-			    printk("eata_dma: HBA has IO or BUSMASTER mode disabled\n");
+			    printk("eata_dma: find_PCI, HBA has IO or BUSMASTER mode disabled\n");
 			    continue;
 			}
 		    } else
-			printk("eata_dma: error %x while reading "
+			printk("eata_dma: find_PCI, error %x while reading "
 			       "PCI_COMMAND\n", error);
 		} else
-		    printk("eata_dma: DEVICECLASSID %x didn't match\n", 
+		    printk("eata_dma: find_PCI, DEVICECLASSID %x didn't match\n", 
 			   rev_device);
 	    } else {
-		printk("eata_dma: error %x while reading PCI_CLASS_BASE\n", 
+		printk("eata_dma: find_PCI, error %x while reading PCI_CLASS_BASE\n", 
 		       error);
 		continue;
 	    }
diff -u --recursive --new-file v1.3.45/linux/drivers/scsi/eata_dma.h linux/drivers/scsi/eata_dma.h
--- v1.3.45/linux/drivers/scsi/eata_dma.h	Mon Sep 25 12:26:22 1995
+++ linux/drivers/scsi/eata_dma.h	Mon Dec 11 07:27:14 1995
@@ -16,7 +16,7 @@
 
 #define VER_MAJOR 2
 #define VER_MINOR 5
-#define VER_SUB   "8"
+#define VER_SUB   "8a"
 
 
 /************************************************************************
diff -u --recursive --new-file v1.3.45/linux/drivers/scsi/eata_generic.h linux/drivers/scsi/eata_generic.h
--- v1.3.45/linux/drivers/scsi/eata_generic.h	Wed Nov  8 07:11:34 1995
+++ linux/drivers/scsi/eata_generic.h	Mon Dec 11 07:27:14 1995
@@ -3,7 +3,7 @@
 * Linux EATA SCSI drivers				*
 * (c) 1993,94,95 Michael Neuffer			*
 *********************************************************
-* last change: 95/06/20					*
+* last change: 95/11/07					*
 ********************************************************/
 
 
@@ -64,7 +64,7 @@
 #define MAX_PCI_BUS	16	       /* Maximum # Of Busses Allowed	 */
 
 #define SG_SIZE		64 
-#define SG_SIZE_BIG	509	       /* max. 509 */
+#define SG_SIZE_BIG	509	       /* max. 509 elements, one 4k page */
 
 #define C_P_L_DIV	2 /* 1 <= C_P_L_DIV <= 8	    
 			   * You can use this parameter to fine-tune
diff -u --recursive --new-file v1.3.45/linux/drivers/scsi/hosts.c linux/drivers/scsi/hosts.c
--- v1.3.45/linux/drivers/scsi/hosts.c	Fri Nov 17 08:42:27 1995
+++ linux/drivers/scsi/hosts.c	Mon Dec 11 07:27:13 1995
@@ -158,6 +158,10 @@
 
 static Scsi_Host_Template builtin_scsi_hosts[] =
 {
+/* BusLogic must come before aha1542.c */
+#ifdef CONFIG_SCSI_BUSLOGIC
+    BUSLOGIC,
+#endif
 #ifdef CONFIG_SCSI_U14_34F
     ULTRASTOR_14_34F,
 #endif
@@ -166,10 +170,6 @@
 #endif
 #ifdef CONFIG_SCSI_AHA152X
     AHA152X,
-#endif
-/* BusLogic must come before aha1542.c */
-#ifdef CONFIG_SCSI_BUSLOGIC
-    BUSLOGIC,
 #endif
 #ifdef CONFIG_SCSI_AHA1542
     AHA1542,
diff -u --recursive --new-file v1.3.45/linux/drivers/scsi/scsi.c linux/drivers/scsi/scsi.c
--- v1.3.45/linux/drivers/scsi/scsi.c	Tue Nov 21 13:22:10 1995
+++ linux/drivers/scsi/scsi.c	Mon Dec 11 10:52:52 1995
@@ -62,6 +62,10 @@
 static int update_timeout (Scsi_Cmnd *, int);
 static void print_inquiry(unsigned char *data);
 static void scsi_times_out (Scsi_Cmnd * SCpnt, int pid);
+static int scan_scsis_single (int channel,int dev,int lun,int * max_scsi_dev ,
+                 Scsi_Device ** SDpnt, Scsi_Cmnd * SCpnt,
+                 struct Scsi_Host *shpnt, char * scsi_result);
+
 
 static unsigned char * dma_malloc_freelist = NULL;
 static int scsi_need_isa_bounce_buffers;
@@ -371,392 +375,365 @@
  *  lun address of all sequential devices to the tape driver, all random
  *  devices to the disk driver.
  */
-static
-void scan_scsis (struct Scsi_Host * shpnt, unchar hardcoded, 
-		 unchar hchannel, unchar hid, unchar hlun)
-{
-    int dev, lun, type, channel;
-    unsigned char scsi_cmd [12];
-    unsigned char scsi_result0 [256];
-    unsigned char * scsi_result;
-    Scsi_Device * SDpnt, *SDtail;
-    struct Scsi_Device_Template * sdtpnt;
-    int                 bflags;
-    int                 max_dev_lun = 0;
-    Scsi_Cmnd  *SCpnt;
-    
-    ++in_scan_scsis;
-    lun = 0;
-    type = -1;
-    SCpnt = (Scsi_Cmnd *) scsi_init_malloc(sizeof(Scsi_Cmnd), GFP_ATOMIC|GFP_DMA);
-    SDpnt = (Scsi_Device *) scsi_init_malloc(sizeof (Scsi_Device), GFP_ATOMIC);
-    SDtail = scsi_devices;
-    
-    if(scsi_devices) while(SDtail->next) SDtail = SDtail->next;
-    
-    /* Make sure we have something that is valid for DMA purposes */
-    scsi_result = ((!dma_malloc_freelist  || !shpnt->unchecked_isa_dma)
-		   ?  &scsi_result0[0] : scsi_malloc(512));
-    
-    if(scsi_result == NULL) {
-	printk("Unable to obtain scsi_result buffer\n");
-	goto leave;
-    }
-    
-    shpnt->host_queue = SCpnt; /* We need this so that commands can time out */
+static void scan_scsis (struct Scsi_Host *shpnt, unchar hardcoded,
+                 unchar hchannel, unchar hid, unchar hlun)
+{
+  int dev, lun, channel;
+  unsigned char scsi_result0[256];
+  unsigned char *scsi_result;
+  Scsi_Device *SDpnt;
+  int max_dev_lun;
+  Scsi_Cmnd *SCpnt;
 
-    if(hardcoded == 1) {
-	channel = hchannel;
-	dev = hid;
-	lun = hlun;
-	goto crude; /* Anyone remember good ol' BASIC ?  :-) */
-    }
+  in_scan_scsis++;
+  SCpnt = (Scsi_Cmnd *) scsi_init_malloc (sizeof (Scsi_Cmnd), GFP_ATOMIC | GFP_DMA);
+  SDpnt = (Scsi_Device *) scsi_init_malloc (sizeof (Scsi_Device), GFP_ATOMIC);
+
+  /* Make sure we have something that is valid for DMA purposes */
+  scsi_result = ((!dma_malloc_freelist || !shpnt->unchecked_isa_dma)
+                 ? &scsi_result0[0] : scsi_malloc (512));
+
+  if (scsi_result == NULL) {
+    printk ("Unable to obtain scsi_result buffer\n");
+    goto leave;
+  }
+
+  shpnt->host_queue = SCpnt;    /* We need this so that commands can time out */
+
+  if (hardcoded == 1) {
+    channel = hchannel;
+    if(channel > shpnt->max_channel) goto leave;
+    dev = hid;
+    if(dev >= shpnt->max_id) goto leave;
+    lun = hlun;
+    if(lun >= shpnt->max_lun) goto leave;
+    scan_scsis_single (channel, dev, lun, &max_dev_lun,
+                   &SDpnt, SCpnt, shpnt, scsi_result);
+  }
+  else {
+    for (channel = 0; channel <= shpnt->max_channel; channel++) {
+      for (dev = 0; dev < shpnt->max_id; ++dev) {
+        if (shpnt->this_id != dev) {
+          /*
+           * We need the for so our continue, etc. work fine. We put this in
+           * a variable so that we can override it during the scan if we
+           * detect a device *KNOWN* to have multiple logical units.
+           */
+          max_dev_lun = (max_scsi_luns < shpnt->max_lun ?
+                         max_scsi_luns : shpnt->max_lun);
+
+          for (lun = 0; lun < max_dev_lun; ++lun) {
+
+            if (!scan_scsis_single (channel, dev, lun, &max_dev_lun,
+                                    &SDpnt, SCpnt, shpnt, scsi_result))
+              break; /* break means don't probe for luns!=0 */
+          }                     /* for lun ends */
+        }                       /* if this_id != id ends */
+      }                         /* for dev ends */
+    }                           /* for channel ends */
+
+leave:
+    shpnt->host_queue = NULL;   /* No longer needed here */
+
+     /* Last device block does not exist.  Free memory. */
+    if (SDpnt != NULL)
+      scsi_init_free ((char *) SDpnt, sizeof (Scsi_Device));
 
-    for (channel = 0; channel <= shpnt->max_channel; channel++)
-    {
-	for (dev = 0; dev < shpnt->max_id; ++dev) {
-	    if (shpnt->this_id != dev) {
-		
-		/*
-		 * We need the for so our continue, etc. work fine.
-		 * We put this in a variable so that we can override
-		 * it during the scan if we detect a device *KNOWN*
-		 * to have multiple logical units.
-		 */
-		max_dev_lun = (max_scsi_luns < shpnt->max_lun ? 
-			       max_scsi_luns : shpnt->max_lun);
+    if (SCpnt != NULL)
+      scsi_init_free ((char *) SCpnt, sizeof (Scsi_Cmnd));
 
-		for (lun = 0; lun < max_dev_lun; ++lun)
-		{
-		crude:
-		    memset(SDpnt, 0, sizeof(Scsi_Device));
-		    SDpnt->host = shpnt;
-		    SDpnt->id = dev;
-		    SDpnt->lun = lun;
-		    SDpnt->channel = channel;
+    /* If we allocated a buffer so we could do DMA, free it now */
+    if (scsi_result != &scsi_result0[0] && scsi_result != NULL)
+      scsi_free (scsi_result, 512);
 
-		    /* Some low level driver could use device->type (DB) */
-		    SDpnt->type = -1;
-		    /*
-		     * Assume that the device will have handshaking problems, 
-		     * and then fix this field later if it turns out it doesn't
-		     */
-		    SDpnt->borken = 1;
-                    SDpnt->was_reset = 0;
-                    SDpnt->expecting_cc_ua = 0;
-		    
-		    scsi_cmd[0] = TEST_UNIT_READY;
-		    scsi_cmd[1] = lun << 5;
-		    scsi_cmd[2] = scsi_cmd[3] = scsi_cmd[4] = scsi_cmd[5] = 0;
-		    
-		    memset(SCpnt, 0,  sizeof(Scsi_Cmnd));
-		    SCpnt->host = SDpnt->host;
-		    SCpnt->device = SDpnt;
-		    SCpnt->target = SDpnt->id;
-		    SCpnt->lun = SDpnt->lun;
-		    SCpnt->channel = SDpnt->channel;
+    in_scan_scsis--;
+  }
+}
 
-		    {
-			/*
-			 * Do the actual command, and wait for it to finish
-			 */
-			struct semaphore sem = MUTEX_LOCKED;
-			SCpnt->request.sem = &sem;
-			SCpnt->request.rq_status = RQ_SCSI_BUSY;
-			scsi_do_cmd (SCpnt, (void *)  scsi_cmd, 
-				     (void *) scsi_result,
-				     256,  scan_scsis_done, SCSI_TIMEOUT + 4 * HZ, 5);
-			down(&sem);
-		    }
+/*
+ * The worker for scan_scsis.
+ * Returning 0 means Please don't ask further for lun!=0, 1 means OK go on.
+ * Global variables used : scsi_devices(linked list), the_result
+ */
+int scan_scsis_single (int channel, int dev, int lun, int *max_dev_lun,
+    Scsi_Device **SDpnt2, Scsi_Cmnd * SCpnt, struct Scsi_Host * shpnt, 
+    char *scsi_result)
+{
+  unsigned char scsi_cmd[12];
+  struct Scsi_Device_Template *sdtpnt;
+  Scsi_Device * SDtail, *SDpnt=*SDpnt2;
+  int bflags, type=-1;
+
+  SDtail = scsi_devices;
+  if (scsi_devices)
+    while (SDtail->next)
+      SDtail = SDtail->next;
+
+  memset (SDpnt, 0, sizeof (Scsi_Device));
+  SDpnt->host = shpnt;
+  SDpnt->id = dev;
+  SDpnt->lun = lun;
+  SDpnt->channel = channel;
+
+  /* Some low level driver could use device->type (DB) */
+  SDpnt->type = -1;
+
+  /*
+   * Assume that the device will have handshaking problems, and then fix this
+   * field later if it turns out it doesn't
+   */
+  SDpnt->borken = 1;
+  SDpnt->was_reset = 0;
+  SDpnt->expecting_cc_ua = 0;
+
+  scsi_cmd[0] = TEST_UNIT_READY;
+  scsi_cmd[1] = lun << 5;
+  scsi_cmd[2] = scsi_cmd[3] = scsi_cmd[4] = scsi_cmd[5] = 0;
+
+  memset (SCpnt, 0, sizeof (Scsi_Cmnd));
+  SCpnt->host = SDpnt->host;
+  SCpnt->device = SDpnt;
+  SCpnt->target = SDpnt->id;
+  SCpnt->lun = SDpnt->lun;
+  SCpnt->channel = SDpnt->channel;
+  {
+    struct semaphore sem = MUTEX_LOCKED;
+    SCpnt->request.sem = &sem;
+    SCpnt->request.rq_status = RQ_SCSI_BUSY;
+    scsi_do_cmd (SCpnt, (void *) scsi_cmd,
+                 (void *) scsi_result,
+                 256, scan_scsis_done, SCSI_TIMEOUT + 4 * HZ, 5);
+    down (&sem);
+  }
 
 #if defined(DEBUG) || defined(DEBUG_INIT)
-		    printk("scsi: scan SCSIS id %d lun %d\n", dev, lun);
-		    printk("scsi: return code %08x\n", SCpnt->result);
+  printk ("scsi: scan_scsis_single id %d lun %d. Return code %d\n",
+          dev, lun, SCpnt->result);
 #endif
 
-		    if(SCpnt->result) {
-			if (((driver_byte(SCpnt->result) & DRIVER_SENSE) ||
-			     (status_byte(SCpnt->result) & CHECK_CONDITION)) &&
-			    ((SCpnt->sense_buffer[0] & 0x70) >> 4) == 7) {
-			    if(((SCpnt->sense_buffer[2] & 0xf) != NOT_READY) &&
-			       ((SCpnt->sense_buffer[2] & 0xf) != UNIT_ATTENTION))
-				continue;
-			}
-			else
-			    break;
-		    }
-		    
+  if (SCpnt->result) {
+    if (((driver_byte (SCpnt->result) & DRIVER_SENSE) ||
+         (status_byte (SCpnt->result) & CHECK_CONDITION)) &&
+        ((SCpnt->sense_buffer[0] & 0x70) >> 4) == 7) {
+      if (((SCpnt->sense_buffer[2] & 0xf) != NOT_READY) &&
+          ((SCpnt->sense_buffer[2] & 0xf) != UNIT_ATTENTION))
+        return 1;
+    }
+    else
+      return 0;
+  }
+
 #if defined (DEBUG) || defined(DEBUG_INIT)
-		    printk("scsi: performing INQUIRY\n");
+  printk ("scsi: performing INQUIRY\n");
 #endif
+  /*
+   * Build an INQUIRY command block.
+   */
+  scsi_cmd[0] = INQUIRY;
+  scsi_cmd[1] = (lun << 5) & 0xe0;
+  scsi_cmd[2] = 0;
+  scsi_cmd[3] = 0;
+  scsi_cmd[4] = 255;
+  scsi_cmd[5] = 0;
+  SCpnt->cmd_len = 0;
+  {
+    struct semaphore sem = MUTEX_LOCKED;
+    SCpnt->request.sem = &sem;
+    SCpnt->request.rq_status = RQ_SCSI_BUSY;
+    scsi_do_cmd (SCpnt, (void *) scsi_cmd,
+                 (void *) scsi_result,
+                 256, scan_scsis_done, SCSI_TIMEOUT, 3);
+    down (&sem);
+  }
 
-		    /*
-		     * Build an INQUIRY command block.
-		     */
-		    scsi_cmd[0] = INQUIRY;
-		    scsi_cmd[1] = (lun << 5) & 0xe0;
-		    scsi_cmd[2] = 0;
-		    scsi_cmd[3] = 0;
-		    scsi_cmd[4] = 255;
-		    scsi_cmd[5] = 0;
-		    
-		    SCpnt->cmd_len = 0;
-
-		    {
-			struct semaphore sem = MUTEX_LOCKED;
-			SCpnt->request.sem = &sem;
-			SCpnt->request.rq_status = RQ_SCSI_BUSY;
-			scsi_do_cmd (SCpnt, (void *)  scsi_cmd, 
-				 (void *) scsi_result,
-				 256,  scan_scsis_done, SCSI_TIMEOUT, 3);
-			down(&sem);
-		    }
-		    
-		    the_result = SCpnt->result;
-		    
+  the_result = SCpnt->result;
 #if defined(DEBUG) || defined(DEBUG_INIT)
-		    if (!the_result)
-			printk("scsi: INQUIRY successful\n");
-		    else
-			printk("scsi: INQUIRY failed with code %08x\n", the_result);
-#endif
-		    
-		    if(the_result) break;
-		    
-		    /* skip other luns on this device */
-		    
-		    if (!the_result)
-		    {
-			/* It would seem some TOSHIBA CDROM 
-			 * gets things wrong 
-			 */
-			if (!strncmp(scsi_result+8,"TOSHIBA",7) &&
-			    !strncmp(scsi_result+16,"CD-ROM",6) &&
-			    scsi_result[0] == TYPE_DISK) {
-			    scsi_result[0] = TYPE_ROM;
-			    scsi_result[1] |= 0x80;  /* removable */
-			}
-			
-			if (!strncmp(scsi_result+8,"NEC",3)) {
-			    if (!strncmp(scsi_result+16,"CD-ROM DRIVE:84 ",16) || 
-				!strncmp(scsi_result+16,"CD-ROM DRIVE:25",15))
-				SDpnt->manufacturer = SCSI_MAN_NEC_OLDCDR;
-			    else
-				SDpnt->manufacturer = SCSI_MAN_NEC;
-			} else if (!strncmp(scsi_result+8,"TOSHIBA",7))
-			    SDpnt->manufacturer = SCSI_MAN_TOSHIBA;
-			else if (!strncmp(scsi_result+8,"SONY",4))
-			    SDpnt->manufacturer = SCSI_MAN_SONY;
-                        else if (!strncmp(scsi_result+8, "PIONEER", 7))
-                            SDpnt->manufacturer = SCSI_MAN_PIONEER;
-			else
-			    SDpnt->manufacturer = SCSI_MAN_UNKNOWN;
-			
-			memcpy(SDpnt->vendor, scsi_result+8, 8);
-			memcpy(SDpnt->model, scsi_result+16, 16);
-			memcpy(SDpnt->rev, scsi_result+32, 4);
- 
-			SDpnt->removable = (0x80 & scsi_result[1]) >> 7;
-			SDpnt->lockable = SDpnt->removable;
-			SDpnt->changed = 0;
-			SDpnt->access_count = 0;
-			SDpnt->busy = 0;
-			SDpnt->has_cmdblocks = 0;
-			/*
-			 * Currently, all sequential devices are assumed to be
-			 * tapes, all random devices disk, with the appropriate
-			 * read only flags set for ROM / WORM treated as RO.
-			 */
-			
-			switch (type = (scsi_result[0] & 0x1f))
-			{
-			case TYPE_TAPE :
-			case TYPE_DISK :
-			case TYPE_MOD :
-			case TYPE_PROCESSOR :
-			case TYPE_SCANNER :
-			    SDpnt->writeable = 1;
-			    break;
-			case TYPE_WORM :
-			case TYPE_ROM :
-			    SDpnt->writeable = 0;
-			    break;
-			default :
-#if 0
-#ifdef DEBUG
-			    printk("scsi: unknown type %d\n", type);
-			    print_inquiry(scsi_result);
-#endif
-			    type = -1;
+  printk ("scsi: INQUIRY %s with code 0x%x\n",
+          the_result ? "failed" : "successful", the->result);
 #endif
-			}
-			
-			SDpnt->single_lun = 0;
-			SDpnt->soft_reset =
-			    (scsi_result[7] & 1) && ((scsi_result[3] &7) == 2);
-			SDpnt->random = (type == TYPE_TAPE) ? 0 : 1;
-			SDpnt->type = (type & 0x1f);
-			
-			if (type != -1)
-			{
-			    print_inquiry(scsi_result);
-			    
-			    for(sdtpnt = scsi_devicelist; sdtpnt; 
-				sdtpnt = sdtpnt->next)
-				if(sdtpnt->detect) SDpnt->attached +=
-				    (*sdtpnt->detect)(SDpnt);
-			    
-			    SDpnt->scsi_level = scsi_result[2] & 0x07;
-			    if (SDpnt->scsi_level >= 2 ||
-				(SDpnt->scsi_level == 1 &&
-				 (scsi_result[3] & 0x0f) == 1))
-				SDpnt->scsi_level++;
-			    /*
-			     * Set the tagged_queue flag for SCSI-II devices 
-			     * that purport to support
-			     * tagged queuing in the INQUIRY data.
-			     */
-			    
-			    SDpnt->tagged_queue = 0;
-			    
-			    if ((SDpnt->scsi_level >= SCSI_2) &&
-				(scsi_result[7] & 2)) {
-				SDpnt->tagged_supported = 1;
-				SDpnt->current_tag = 0;
-			    }
-			    
-			    /*
-			     * Accommodate drivers that want to sleep when 
-			     * they should be in a polling loop.
-			     */
-
-			    SDpnt->disconnect = 0;
-
-			    /*
-			     * Get any flags for this device.
-			     */
-			    bflags = get_device_flags(scsi_result);
-
-			    
-			    /*
-			     * Some revisions of the Texel CD ROM drives have 
-			     * handshaking problems when used with the Seagate
-			     * controllers.  Before we know what type of device
-			     * we're talking to, we assume it's borken and then
-			     * change it here if it turns out that it isn't
-			     * a TEXEL drive.
-			     */
-			    if( (bflags & BLIST_BORKEN) == 0 )
-			    {
-				SDpnt->borken = 0;
-			    }
-			    
-			    
-			    /* These devices need this "key" to unlock the
-			     * devices so we can use it 
-			     */
-			    if( (bflags & BLIST_KEY) != 0 ) {
-				printk("Unlocked floptical drive.\n");
-				SDpnt->lockable = 0;
-				scsi_cmd[0] = MODE_SENSE;
-				scsi_cmd[1] = (lun << 5) & 0xe0;
-				scsi_cmd[2] = 0x2e;
-				scsi_cmd[3] = 0;
-				scsi_cmd[4] = 0x2a;
-				scsi_cmd[5] = 0;
-				
-				SCpnt->cmd_len = 0;
-				{
-				    struct semaphore sem = MUTEX_LOCKED;
-				    SCpnt->request.rq_status = RQ_SCSI_BUSY;
-				    SCpnt->request.sem = &sem;
-				    scsi_do_cmd (SCpnt, (void *)  scsi_cmd,
-					     (void *) scsi_result, 0x2a,  
-					     scan_scsis_done, SCSI_TIMEOUT, 3);
-				    down(&sem);
-				}
-			    }
-			    /* Add this device to the linked list at the end */
-			    if(SDtail)
-				SDtail->next = SDpnt;
-			    else
-				scsi_devices = SDpnt;
-			    SDtail = SDpnt;
-			    
-			    SDpnt = (Scsi_Device *) scsi_init_malloc(sizeof (Scsi_Device), GFP_ATOMIC);
-			    /* Some scsi devices cannot be polled for lun != 0
-			     * due to firmware bugs 
-			     */
-			    if(bflags & BLIST_NOLUN) break;
-
-			    /*
-			     * If we want to only allow I/O to one of the luns
-			     * attached to this device at a time, then we set 
-                             * this flag.
-			     */
-			    if(bflags & BLIST_SINGLELUN)
-			    {
-				SDpnt->single_lun = 1;
-			    }
-
-			    /*
-			     * If this device is known to support multiple 
-                             * units, override the other settings, and scan 
-                             * all of them.
-			     */
-			    if(bflags & BLIST_FORCELUN)
-			    {
-				/*
-				 * We probably want to make this a variable, 
-                                 * but this will do for now.
-				 */
-				max_dev_lun = 8;
-			    }
- 
 
-			    /* Old drives like the MAXTOR XT-3280 say vers=0 */
-			    if ((scsi_result[2] & 0x07) == 0)
-				break;
-			    /* Some scsi-1 peripherals do not handle lun != 0.
-			     * I am assuming that scsi-2 peripherals do better 
-			     */
-			    if((scsi_result[2] & 0x07) == 1 &&
-			       (scsi_result[3] & 0x0f) == 0) break;
-			}
-		    }       /* if result == DID_OK ends */
+  if (the_result)
+    return 0;     /* assume no peripheral if any sort of error */
 
-		    /*
-		     * This might screw us up with multi-lun devices, but the 
-                     * user can scan for them too.
-		     */
-		    if(hardcoded == 1)
-			goto leave;
-		} /* for lun ends */
-	    } /* if this_id != id ends */
-	} /* for dev ends */
-    } /* for channel ends */
-    
- leave:
-    shpnt->host_queue = NULL;  /* No longer needed here */
-    
-    /* Last device block does not exist.  Free memory. */
-    if(SDpnt != NULL)
-	scsi_init_free((char *) SDpnt, sizeof(Scsi_Device));
-    
-    if(SCpnt != NULL)
-	scsi_init_free((char *) SCpnt, sizeof(Scsi_Cmnd));
-    
-    /* If we allocated a buffer so we could do DMA, free it now */
-    if (scsi_result != &scsi_result0[0] && scsi_result != NULL) 
-	scsi_free(scsi_result, 512);
-    
-    in_scan_scsis = 0;
-}       /* scan_scsis  ends */
+  /*
+   * It would seem some TOSHIBA CDROM gets things wrong
+   */
+  if (!strncmp (scsi_result + 8, "TOSHIBA", 7) &&
+      !strncmp (scsi_result + 16, "CD-ROM", 6) &&
+      scsi_result[0] == TYPE_DISK) {
+    scsi_result[0] = TYPE_ROM;
+    scsi_result[1] |= 0x80;     /* removable */
+  }
+
+  if (!strncmp (scsi_result + 8, "NEC", 3)) {
+    if (!strncmp (scsi_result + 16, "CD-ROM DRIVE:84 ", 16) ||
+        !strncmp (scsi_result + 16, "CD-ROM DRIVE:25", 15))
+      SDpnt->manufacturer = SCSI_MAN_NEC_OLDCDR;
+    else
+      SDpnt->manufacturer = SCSI_MAN_NEC;
+  }
+  else if (!strncmp (scsi_result + 8, "TOSHIBA", 7))
+    SDpnt->manufacturer = SCSI_MAN_TOSHIBA;
+  else if (!strncmp (scsi_result + 8, "SONY", 4))
+    SDpnt->manufacturer = SCSI_MAN_SONY;
+  else if (!strncmp (scsi_result + 8, "PIONEER", 7))
+    SDpnt->manufacturer = SCSI_MAN_PIONEER;
+  else
+    SDpnt->manufacturer = SCSI_MAN_UNKNOWN;
+
+  memcpy (SDpnt->vendor, scsi_result + 8, 8);
+  memcpy (SDpnt->model, scsi_result + 16, 16);
+  memcpy (SDpnt->rev, scsi_result + 32, 4);
+
+  SDpnt->removable = (0x80 & scsi_result[1]) >> 7;
+  SDpnt->lockable = SDpnt->removable;
+  SDpnt->changed = 0;
+  SDpnt->access_count = 0;
+  SDpnt->busy = 0;
+  SDpnt->has_cmdblocks = 0;
+  /*
+   * Currently, all sequential devices are assumed to be tapes, all random
+   * devices disk, with the appropriate read only flags set for ROM / WORM
+   * treated as RO.
+   */
+  switch (type = (scsi_result[0] & 0x1f)) {
+  case TYPE_TAPE:
+  case TYPE_DISK:
+  case TYPE_MOD:
+  case TYPE_PROCESSOR:
+  case TYPE_SCANNER:
+    SDpnt->writeable = 1;
+    break;
+  case TYPE_WORM:
+  case TYPE_ROM:
+    SDpnt->writeable = 0;
+    break;
+  default:
+    printk ("scsi: unknown type %d\n", type);
+  }
+
+  SDpnt->single_lun = 0;
+  SDpnt->soft_reset =
+    (scsi_result[7] & 1) && ((scsi_result[3] & 7) == 2);
+  SDpnt->random = (type == TYPE_TAPE) ? 0 : 1;
+  SDpnt->type = (type & 0x1f);
+
+  print_inquiry (scsi_result);
+
+  for (sdtpnt = scsi_devicelist; sdtpnt;
+       sdtpnt = sdtpnt->next)
+    if (sdtpnt->detect)
+      SDpnt->attached +=
+        (*sdtpnt->detect) (SDpnt);
+
+  SDpnt->scsi_level = scsi_result[2] & 0x07;
+  if (SDpnt->scsi_level >= 2 ||
+      (SDpnt->scsi_level == 1 &&
+       (scsi_result[3] & 0x0f) == 1))
+    SDpnt->scsi_level++;
+
+  /*
+   * Set the tagged_queue flag for SCSI-II devices that purport to support
+   * tagged queuing in the INQUIRY data.
+   */
+  SDpnt->tagged_queue = 0;
+  if ((SDpnt->scsi_level >= SCSI_2) &&
+      (scsi_result[7] & 2)) {
+    SDpnt->tagged_supported = 1;
+    SDpnt->current_tag = 0;
+  }
+
+  /*
+   * Accommodate drivers that want to sleep when they should be in a polling
+   * loop.
+   */
+  SDpnt->disconnect = 0;
+
+  /*
+   * Get any flags for this device.
+   */
+  bflags = get_device_flags (scsi_result);
+
+  /*
+   * Some revisions of the Texel CD ROM drives have handshaking problems when
+   * used with the Seagate controllers.  Before we know what type of device
+   * we're talking to, we assume it's borken and then change it here if it
+   * turns out that it isn't a TEXEL drive.
+   */
+  if ((bflags & BLIST_BORKEN) == 0)
+    SDpnt->borken = 0;
+
+  /*
+   * These devices need this "key" to unlock the devices so we can use it
+   */
+  if ((bflags & BLIST_KEY) != 0) {
+    printk ("Unlocked floptical drive.\n");
+    SDpnt->lockable = 0;
+    scsi_cmd[0] = MODE_SENSE;
+    scsi_cmd[1] = (lun << 5) & 0xe0;
+    scsi_cmd[2] = 0x2e;
+    scsi_cmd[3] = 0;
+    scsi_cmd[4] = 0x2a;
+    scsi_cmd[5] = 0;
+    SCpnt->cmd_len = 0;
+    {
+      struct semaphore sem = MUTEX_LOCKED;
+      SCpnt->request.rq_status = RQ_SCSI_BUSY;
+      SCpnt->request.sem = &sem;
+      scsi_do_cmd (SCpnt, (void *) scsi_cmd,
+                   (void *) scsi_result, 0x2a,
+                   scan_scsis_done, SCSI_TIMEOUT, 3);
+      down (&sem);
+    }
+  }
+  /* Add this device to the linked list at the end */
+  if (SDtail)
+    SDtail->next = SDpnt;
+  else
+    scsi_devices = SDpnt;
+  SDtail = SDpnt;
+
+  SDpnt = (Scsi_Device *) scsi_init_malloc (sizeof (Scsi_Device), GFP_ATOMIC);
+  *SDpnt2=SDpnt;
+  if (!SDpnt)
+    printk ("scsi: scan_scsis_single: No memory\n");
+
+
+  /*
+   * Some scsi devices cannot be polled for lun != 0 due to firmware bugs
+   */
+  if (bflags & BLIST_NOLUN)
+    return 0;                   /* break; */
+
+  /*
+   * If we want to only allow I/O to one of the luns attached to this device
+   * at a time, then we set this flag.
+   */
+  if (bflags & BLIST_SINGLELUN)
+    SDpnt->single_lun = 1;
+
+  /*
+   * If this device is known to support multiple units, override the other
+   * settings, and scan all of them.
+   */
+  if (bflags & BLIST_FORCELUN)
+    *max_dev_lun = 8;
+  /*
+   * We assume the device can't handle lun!=0 if: - it reports scsi-0 (ANSI
+   * SCSI Revision 0) (old drives like MAXTOR XT-3280) or - it reports scsi-1
+   * (ANSI SCSI Revision 1) and Response Data Format 0
+   */
+  if (((scsi_result[2] & 0x07) == 0)
+      ||
+      ((scsi_result[2] & 0x07) == 1 &&
+       (scsi_result[3] & 0x0f) == 0))
+    return 0;
+  return 1;
+}
 
 /*
  *  Flag bits for the internal_timeout array
  */
-
 #define NORMAL_TIMEOUT 0
 #define IN_ABORT 1
 #define IN_RESET 2
@@ -1153,8 +1130,8 @@
 #ifdef DEBUG_DELAY
 	clock = jiffies + 4 * HZ;
 	while (jiffies < clock);
-	printk("done(host = %d, result = %04x) : routine at %08x\n", 
-	       host->host_no, temp);
+	printk("done(host = %d, result = %04x) : routine at %p\n", 
+	       host->host_no, temp, host->hostt->command);
 #endif
 	scsi_done(SCpnt);
     }
@@ -2413,7 +2390,7 @@
     struct Scsi_Host *HBA_ptr;
     int  parameter[4];
     char *p;
-    int	  size, len = 0;
+    int	  i,size, len = 0;
     off_t begin = 0;
     off_t pos = 0;
 
@@ -2458,21 +2435,29 @@
 	return (len);     
     }
 
+    /*
+     * Usage: echo "scsi singledevice 0 1 2 3" >/proc/scsi/scsi
+     * with  "0 1 2 3" replaced by your "Host Channel Id Lun".
+     * Consider this feature ALPHA, as you can easily hang your
+     * scsi system (depending on your low level driver).
+     */
     if(!buffer || length < 25 || strncmp("scsi", buffer, 4))
 	return(-EINVAL);
 
     if(!strncmp("singledevice", buffer + 5, 12)) {
 	p = buffer + 17;
 
-	parameter[0] = simple_strtoul(p , &p, 0);
-	parameter[1] = simple_strtoul(p , &p, 0);
-	parameter[2] = simple_strtoul(p , &p, 0);
-	parameter[3] = simple_strtoul(p , &p, 0);
-
-	while(scd && scd->host->host_no != parameter[0] 
-	      && scd->channel != parameter[1] 
-	      && scd->id != parameter[2] 
-	      && scd->lun != parameter[3]) {
+	for(i=0; i<4; i++)	{
+	    p++;
+	    parameter[i] = simple_strtoul(p, &p, 0);
+	}
+	printk("scsi singledevice %d %d %d %d\n", parameter[0], parameter[1],
+			parameter[2], parameter[3]);
+
+	while(scd && (scd->host->host_no != parameter[0] 
+	      || scd->channel != parameter[1] 
+	      || scd->id != parameter[2] 
+	      || scd->lun != parameter[3])) {
 	    scd = scd->next;
 	}
 	if(scd)
@@ -2484,7 +2469,7 @@
 	    return(-ENXIO);
 
 	scan_scsis (HBA_ptr, 1, parameter[1], parameter[2], parameter[3]);
-	return(0);
+	return(length);
     }
     return(-EINVAL);
 }
@@ -3035,7 +3020,7 @@
 	for(SCpnt=shpnt->host_queue; SCpnt; SCpnt = SCpnt->next)
 	{
 	    /*  (0) 0:0:0:0 (802 123434 8 8 0) (3 3 2) (%d %d %d) %d %x      */
-	    printk("(%d) %d:%d:%d:%d (%s %ld %ld %ld %ld) (%d %d %x) (%d %d %d) %x %x %x\n",
+	    printk("(%d) %d:%d:%d:%d (%s %ld %ld %ld %d) (%d %d %x) (%d %d %d) %x %x %x\n",
 		   i++, SCpnt->host->host_no,
 		   SCpnt->channel,
 		   SCpnt->target,
diff -u --recursive --new-file v1.3.45/linux/drivers/scsi/scsi_syms.c linux/drivers/scsi/scsi_syms.c
--- v1.3.45/linux/drivers/scsi/scsi_syms.c	Fri Nov 17 08:42:27 1995
+++ linux/drivers/scsi/scsi_syms.c	Thu Nov 30 14:04:51 1995
@@ -59,6 +59,7 @@
     X(kernel_scsi_ioctl),
     X(need_isa_buffer),
     X(request_queueable),
+    X(in_scan_scsis),
 #if defined(CONFIG_PROC_FS)
     X(proc_print_scsidevice),
 #endif
diff -u --recursive --new-file v1.3.45/linux/drivers/scsi/scsicam.c linux/drivers/scsi/scsicam.c
--- v1.3.45/linux/drivers/scsi/scsicam.c	Sat Nov 11 17:41:33 1995
+++ linux/drivers/scsi/scsicam.c	Thu Nov 30 14:04:51 1995
@@ -86,8 +86,8 @@
     unsigned int  *cyls, unsigned int *hds, unsigned int *secs) {
     struct partition *p, *largest = NULL;
     int i, largest_cyl;
-    int cyl, end_head, end_cyl, end_sector;
-    unsigned int logical_end, physical_end;
+    int cyl, ext_cyl, end_head, end_cyl, end_sector;
+    unsigned int logical_end, physical_end, ext_physical_end;
     
 
     if (*(unsigned short *) (bh->b_data+510) == 0xAA55) {
@@ -111,6 +111,7 @@
     	end_cyl = largest->end_cyl + ((largest->end_sector & 0xc0) << 2);
     	end_head = largest->end_head;
     	end_sector = largest->end_sector & 0x3f;
+
 #ifdef DEBUG
 	printk ("scsicam_bios_param : end at h = %d, c = %d, s = %d\n",
 	    end_head, end_cyl, end_sector);
@@ -122,12 +123,25 @@
 	/* This is the actual _sector_ number at the end */
 	logical_end = largest->start_sect + largest->nr_sects;
 
-    	if (logical_end == physical_end) {
+	/* This is for >1023 cylinders */
+        ext_cyl= (logical_end-(end_head * end_sector + end_sector))
+                                        /(end_head + 1) / end_sector;
+	ext_physical_end = ext_cyl * (end_head + 1) * end_sector +
+            end_head * end_sector + end_sector;
+
+#ifdef DEBUG
+	printk("scsicam_bios_param : logical_end=%d physical_end=%d ext_physical_end=%d ext_cyl=%d\n"
+			,logical_end,physical_end,ext_physical_end,ext_cyl);
+#endif
+
+    	if ((logical_end == physical_end) ||
+	    (end_cyl==1023 && ext_physical_end==logical_end)) {
     	    *secs = end_sector;
     	    *hds = end_head + 1;
     	    *cyls = capacity / ((end_head + 1) * end_sector);
     	    return 0;
     	}
+	
 #ifdef DEBUG
 	printk ("scsicam_bios_param : logical (%u) != physical (%u)\n",
 	    logical_end, physical_end);
diff -u --recursive --new-file v1.3.45/linux/drivers/scsi/sg.c linux/drivers/scsi/sg.c
--- v1.3.45/linux/drivers/scsi/sg.c	Tue Nov 21 13:22:11 1995
+++ linux/drivers/scsi/sg.c	Thu Nov 30 16:00:33 1995
@@ -215,7 +215,7 @@
 	if (filp->f_flags & O_NONBLOCK)
 	{
 	    restore_flags(flags);
-	    return -EWOULDBLOCK;
+	    return -EAGAIN;
 	}
 	interruptible_sleep_on(&device->read_wait);
 	if (current->signal & ~current->blocked)
@@ -323,7 +323,7 @@
     while(device->pending)
     {
 	if (filp->f_flags & O_NONBLOCK)
-	    return -EWOULDBLOCK;
+	    return -EAGAIN;
 #ifdef DEBUG
 	printk("sg_write: sleeping on pending request\n");
 #endif     
@@ -392,7 +392,7 @@
 	wake_up(&device->write_wait);
 	sg_free(device->buff,device->buff_len);
 	device->buff = NULL;
-	return -EWOULDBLOCK;
+	return -EAGAIN;
     } 
 #ifdef DEBUG
     printk("device allocated\n");
diff -u --recursive --new-file v1.3.45/linux/fs/Makefile linux/fs/Makefile
--- v1.3.45/linux/fs/Makefile	Wed Sep 13 12:45:31 1995
+++ linux/fs/Makefile	Mon Dec 11 06:56:35 1995
@@ -13,7 +13,7 @@
 O_OBJS    = open.o read_write.o inode.o devices.o file_table.o buffer.o \
 		super.o  block_dev.o stat.o exec.o pipe.o namei.o fcntl.o \
 		ioctl.o readdir.o select.o fifo.o locks.o filesystems.o \
-		dcache.o $(BINFMTS) 
+		dcache.o dquot.o $(BINFMTS) 
 
 MOD_LIST_NAME := FS_MODULES
 ALL_SUB_DIRS = minix ext ext2 msdos proc isofs nfs xiafs umsdos hpfs sysv smbfs
diff -u --recursive --new-file v1.3.45/linux/fs/buffer.c linux/fs/buffer.c
--- v1.3.45/linux/fs/buffer.c	Mon Nov 27 12:48:31 1995
+++ linux/fs/buffer.c	Mon Dec 11 07:58:14 1995
@@ -222,6 +222,7 @@
 	sync_supers(dev);
 	sync_inodes(dev);
 	sync_buffers(dev, 0);
+	sync_dquots(dev, -1);
 }
 
 int fsync_dev(kdev_t dev)
@@ -229,6 +230,7 @@
 	sync_buffers(dev, 0);
 	sync_supers(dev);
 	sync_inodes(dev);
+	sync_dquots(dev, -1);
 	return sync_buffers(dev, 1);
 }
 
diff -u --recursive --new-file v1.3.45/linux/fs/dquot.c linux/fs/dquot.c
--- v1.3.45/linux/fs/dquot.c	Thu Jan  1 02:00:00 1970
+++ linux/fs/dquot.c	Mon Dec 11 07:58:31 1995
@@ -0,0 +1,1060 @@
+/*
+ * Implementation of the diskquota system for the LINUX operating
+ * system. QUOTA is implemented using the BSD systemcall interface as
+ * the means of communication with the user level. Currently only the
+ * ext2-filesystem has support for diskquotas. Other filesystems may
+ * be added in future time. This file contains the generic routines
+ * called by the different filesystems on allocation of an inode or
+ * block. These routines take care of the administration needed to
+ * have a consistent diskquota tracking system. The ideas of both
+ * user and group quotas are based on the Melbourne quota system as
+ * used on BSD derivated systems. The internal implementation is 
+ * based on the LINUX inode-subsystem with added complexity of the
+ * diskquota system. This implementation is not based on any BSD
+ * kernel sourcecode.
+ * 
+ * Version: $Id: dquot.c,v 5.6 1995/11/15 20:30:27 mvw Exp mvw $
+ * 
+ * Author:  Marco van Wieringen <mvw@mcs.ow.nl> <mvw@tnix.net>
+ *
+ * (C) Copyright 1994, 1995 Marco van Wieringen 
+ *
+ */
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/fcntl.h>
+#include <linux/stat.h>
+#include <linux/tty.h>
+#include <linux/malloc.h>
+#include <linux/mount.h>
+
+#include <asm/segment.h>
+#include <sys/sysmacros.h>
+
+#define __DQUOT_VERSION__	"dquot_5.6.0"
+
+static char quotamessage[MAX_QUOTA_MESSAGE];
+static char *quotatypes[] = INITQFNAMES;
+
+static int nr_dquots = 0, nr_free_dquots = 0;
+static struct dquot *hash_table[NR_DQHASH];
+static struct dquot *first_dquot;
+static struct dqstats dqstats;
+
+static struct wait_queue *dquot_wait = (struct wait_queue *)NULL;
+
+extern void add_dquot_ref(kdev_t dev, short type);
+extern void reset_dquot_ptrs(kdev_t dev, short type);
+
+#ifndef min
+#define min(a,b) ((a) < (b)) ? (a) : (b)
+#endif
+
+/*
+ * Functions for management of the hashlist.
+ */
+static inline int const hashfn(kdev_t dev, unsigned int id, short type)
+{
+	return((HASHDEV(dev) ^ id) * (MAXQUOTAS - type)) % NR_DQHASH;
+}
+
+static inline struct dquot **const hash(kdev_t dev, unsigned int id, short type)
+{
+	return(hash_table + hashfn(dev, id, type));
+}
+
+static inline int has_quota_enabled(kdev_t dev, short type)
+{
+	struct vfsmount *vfsmnt;
+
+	return((vfsmnt = lookup_vfsmnt(dev)) != (struct vfsmount *)NULL &&
+	       (vfsmnt->mnt_quotas[type] != (struct file *)NULL));
+}
+
+static void insert_dquot_free(struct dquot *dquot)
+{
+	dquot->dq_next = first_dquot;
+	dquot->dq_prev = first_dquot->dq_prev;
+	dquot->dq_next->dq_prev = dquot;
+	dquot->dq_prev->dq_next = dquot;
+	first_dquot = dquot;
+}
+
+static void remove_dquot_free(struct dquot *dquot)
+{
+	if (first_dquot == dquot)
+		first_dquot = first_dquot->dq_next;
+	if (dquot->dq_next)
+		dquot->dq_next->dq_prev = dquot->dq_prev;
+	if (dquot->dq_prev)
+		dquot->dq_prev->dq_next = dquot->dq_next;
+	dquot->dq_next = dquot->dq_prev = NODQUOT;
+}
+
+static void insert_dquot_hash(struct dquot *dquot)
+{
+	struct dquot **hash_ent;
+
+	hash_ent = hash(dquot->dq_dev, dquot->dq_id, dquot->dq_type);
+	dquot->dq_hash_next = *hash_ent;
+	dquot->dq_hash_prev = NODQUOT;
+	if (dquot->dq_hash_next)
+		dquot->dq_hash_next->dq_hash_prev = dquot;
+	*hash_ent = dquot;
+}
+
+static void remove_dquot_hash(struct dquot *dquot)
+{
+	struct dquot **hash_ent;
+
+	hash_ent = hash(dquot->dq_dev, dquot->dq_id, dquot->dq_type);
+	if (*hash_ent == dquot)
+		*hash_ent = dquot->dq_hash_next;
+	if (dquot->dq_hash_next)
+		dquot->dq_hash_next->dq_hash_prev = dquot->dq_hash_prev;
+	if (dquot->dq_hash_prev)
+		dquot->dq_hash_prev->dq_hash_next = dquot->dq_hash_next;
+	dquot->dq_hash_prev = dquot->dq_hash_next = NODQUOT;
+}
+
+static void put_last_free(struct dquot *dquot)
+{
+	remove_dquot_free(dquot);
+	dquot->dq_prev = first_dquot->dq_prev;
+	dquot->dq_prev->dq_next = dquot;
+	dquot->dq_next = first_dquot;
+	dquot->dq_next->dq_prev = dquot;
+}
+
+static void grow_dquots(void)
+{
+	struct dquot *dquot;
+	int cnt;
+
+	if (!(dquot = (struct dquot*) get_free_page(GFP_KERNEL)))
+		return;
+	dqstats.pages_allocated++;
+	cnt = PAGE_SIZE / sizeof(struct dquot);
+	nr_dquots += cnt;
+	nr_free_dquots += cnt;
+	if (!first_dquot) {
+		dquot->dq_next = dquot->dq_prev = first_dquot = dquot++;
+		cnt--;
+	}
+	for (; cnt; cnt--)
+		insert_dquot_free(dquot++);
+}
+
+/*
+ * Functions for locking and waiting on dquots.
+ */
+static void __wait_on_dquot(struct dquot *dquot)
+{
+	struct wait_queue wait = {current, NULL};
+
+	add_wait_queue(&dquot->dq_wait, &wait);
+repeat:
+	current->state = TASK_UNINTERRUPTIBLE;
+	if (dquot->dq_flags & DQ_LOCKED) {
+		dquot->dq_flags |= DQ_WANT;
+		schedule();
+		goto repeat;
+	}
+	remove_wait_queue(&dquot->dq_wait, &wait);
+	current->state = TASK_RUNNING;
+}
+
+static inline void wait_on_dquot(struct dquot *dquot)
+{
+	if (dquot->dq_flags & DQ_LOCKED)
+		__wait_on_dquot(dquot);
+}
+
+static inline void lock_dquot(struct dquot *dquot)
+{
+	wait_on_dquot(dquot);
+	dquot->dq_flags |= DQ_LOCKED;
+}
+
+static inline void unlock_dquot(struct dquot *dquot)
+{
+	dquot->dq_flags &= ~DQ_LOCKED;
+	if (dquot->dq_flags & DQ_WANT) {
+		dquot->dq_flags &= ~DQ_WANT;
+		wake_up(&dquot->dq_wait);
+	}
+}
+/*
+ * Note that we don't want to disturb any wait-queues when we discard
+ * an dquot.
+ *
+ * FIXME: As soon as we have a nice solution for the inode problem we
+ *		  can also fix this one. I.e. the volatile part.
+ */
+static void clear_dquot(struct dquot * dquot)
+{
+	struct wait_queue *wait;
+
+	wait_on_dquot(dquot);
+	remove_dquot_hash(dquot);
+	remove_dquot_free(dquot);
+	wait = ((volatile struct dquot *) dquot)->dq_wait;
+	if (dquot->dq_count)
+		nr_free_dquots++;
+	memset(dquot, 0, sizeof(*dquot));
+	((volatile struct dquot *) dquot)->dq_wait = wait;
+	insert_dquot_free(dquot);
+}
+
+static void write_dquot(struct dquot *dquot)
+{
+	short type = dquot->dq_type;
+	struct file *filp = dquot->dq_mnt->mnt_quotas[type];
+	unsigned short fs;
+
+	if (!(dquot->dq_flags & DQ_MOD) || (filp == (struct file *)NULL))
+		return;
+	lock_dquot(dquot);
+	down(&dquot->dq_mnt->mnt_sem);
+	if (filp->f_op->lseek) {
+		if (filp->f_op->lseek(filp->f_inode, filp,
+		    dqoff(dquot->dq_id), 0) != dqoff(dquot->dq_id)) {
+			up(&dquot->dq_mnt->mnt_sem);
+			unlock_dquot(dquot);
+			return;
+		}
+	} else
+		filp->f_pos = dqoff(dquot->dq_id);
+	fs = get_fs();
+	set_fs(KERNEL_DS);
+	if (filp->f_op->write(filp->f_inode, filp,
+	   (char *)&dquot->dq_dqb, sizeof(struct dqblk)) == sizeof(struct dqblk))
+		dquot->dq_flags &= ~DQ_MOD;
+	up(&dquot->dq_mnt->mnt_sem);
+	set_fs(fs);
+	unlock_dquot(dquot);
+	dqstats.writes++;
+}
+
+static void read_dquot(struct dquot *dquot)
+{
+	short type = dquot->dq_type;
+	struct file *filp = dquot->dq_mnt->mnt_quotas[type];
+	unsigned short fs;
+
+	if (filp == (struct file *)NULL)
+		return;
+	lock_dquot(dquot);
+	down(&dquot->dq_mnt->mnt_sem);
+	if (filp->f_op->lseek) {
+		if (filp->f_op->lseek(filp->f_inode, filp,
+		    dqoff(dquot->dq_id), 0) != dqoff(dquot->dq_id)) {
+			up(&dquot->dq_mnt->mnt_sem);
+			unlock_dquot(dquot);
+			return;
+		}
+	} else
+		filp->f_pos = dqoff(dquot->dq_id);
+	fs = get_fs();
+	set_fs(KERNEL_DS);
+	filp->f_op->read(filp->f_inode, filp, (char *)&dquot->dq_dqb, sizeof(struct dqblk));
+	up(&dquot->dq_mnt->mnt_sem);
+	set_fs(fs);
+	if (dquot->dq_bhardlimit == 0 && dquot->dq_bsoftlimit == 0 &&
+	    dquot->dq_ihardlimit == 0 && dquot->dq_isoftlimit == 0)
+		dquot->dq_flags |= DQ_FAKE;
+	unlock_dquot(dquot);
+	dqstats.reads++;
+}
+
+int sync_dquots(kdev_t dev, short type)
+{
+	struct dquot *dquot = first_dquot;
+	int i;
+
+	dqstats.syncs++;
+	for (i = 0; i < nr_dquots * 2; i++, dquot = dquot->dq_next) {
+		if (dev == NODEV || dquot->dq_count == 0 || dquot->dq_dev != dev)
+			continue;
+		if (type != -1 && dquot->dq_type != type)
+			continue;
+		wait_on_dquot(dquot);
+		if (dquot->dq_flags & DQ_MOD)
+			write_dquot(dquot);
+	}
+	return(0);
+}
+
+/*
+ * Trash the cache for a certain type on a device.
+ */
+void invalidate_dquots(kdev_t dev, short type)
+{
+	struct dquot *dquot, *next;
+	int cnt;
+
+	next = first_dquot;
+	for (cnt = nr_dquots ; cnt > 0 ; cnt--) {
+		dquot = next;
+		next = dquot->dq_next;
+		if (dquot->dq_dev != dev || dquot->dq_type != type)
+			continue;
+		if (dquot->dq_flags & DQ_LOCKED) {
+			printk("VFS: dquot busy on removed device %s\n", kdevname(dev));
+			continue;
+		}
+		if (dquot->dq_flags & DQ_MOD)
+			write_dquot(dquot);
+		dqstats.drops++;
+		clear_dquot(dquot);
+	}
+}
+
+static inline void dquot_incr_inodes(struct dquot *dquot, unsigned long number)
+{
+	lock_dquot(dquot);
+	dquot->dq_curinodes += number;
+	dquot->dq_flags |= DQ_MOD;
+	unlock_dquot(dquot);
+}
+
+static inline void dquot_incr_blocks(struct dquot *dquot, unsigned long number)
+{
+	lock_dquot(dquot);
+	dquot->dq_curblocks += number;
+	dquot->dq_flags |= DQ_MOD;
+	unlock_dquot(dquot);
+}
+
+static inline void dquot_decr_inodes(struct dquot *dquot, unsigned long number)
+{
+	lock_dquot(dquot);
+	if (dquot->dq_curinodes > number)
+		dquot->dq_curinodes -= number;
+	else
+		dquot->dq_curinodes = 0;
+	if (dquot->dq_curinodes < dquot->dq_isoftlimit)
+		dquot->dq_itime = (time_t) 0;
+	dquot->dq_flags &= ~DQ_INODES;
+	dquot->dq_flags |= DQ_MOD;
+	unlock_dquot(dquot);
+}
+
+static inline void dquot_decr_blocks(struct dquot *dquot, unsigned long number)
+{
+	lock_dquot(dquot);
+	if (dquot->dq_curblocks > number)
+		dquot->dq_curblocks -= number;
+	else
+		dquot->dq_curblocks = 0;
+	if (dquot->dq_curblocks < dquot->dq_bsoftlimit)
+		dquot->dq_btime = (time_t) 0;
+	dquot->dq_flags &= ~DQ_BLKS;
+	dquot->dq_flags |= DQ_MOD;
+	unlock_dquot(dquot);
+}
+
+static inline int need_print_warning(short type, struct dquot *dquot)
+{
+	switch (type) {
+		case USRQUOTA:
+			return(current->fsuid == dquot->dq_id);
+		case GRPQUOTA:
+			return(current->fsgid == dquot->dq_id);
+	}
+	return(0);
+}
+
+static int check_idq(struct dquot *dquot, short type, u_long short inodes)
+{
+	if (inodes <= 0 || dquot->dq_flags & DQ_FAKE)
+		return(QUOTA_OK);
+	if (dquot->dq_ihardlimit &&
+	   (dquot->dq_curinodes + inodes) > dquot->dq_ihardlimit && !fsuser()) {
+		if ((dquot->dq_flags & DQ_INODES) == 0 &&
+                     need_print_warning(type, dquot)) {
+			sprintf(quotamessage, "%s: write failed, %s file limit reached\r\n",
+			        dquot->dq_mnt->mnt_dirname, quotatypes[type]);
+			tty_write_message(current->tty, quotamessage);
+			dquot->dq_flags |= DQ_INODES;
+		}
+		return(NO_QUOTA);
+	}
+	if (dquot->dq_isoftlimit &&
+	   (dquot->dq_curinodes + inodes) > dquot->dq_isoftlimit &&
+	    dquot->dq_itime && CURRENT_TIME >= dquot->dq_itime && !fsuser()) {
+                if (need_print_warning(type, dquot)) {
+			sprintf(quotamessage, "%s: warning, %s file quota exceeded to long.\r\n",
+		        	dquot->dq_mnt->mnt_dirname, quotatypes[type]);
+			tty_write_message(current->tty, quotamessage);
+		}
+		return(NO_QUOTA);
+	}
+	if (dquot->dq_isoftlimit &&
+	   (dquot->dq_curinodes + inodes) > dquot->dq_isoftlimit &&
+	    dquot->dq_itime == 0 && !fsuser()) {
+                if (need_print_warning(type, dquot)) {
+			sprintf(quotamessage, "%s: warning, %s file quota exceeded\r\n",
+		        	dquot->dq_mnt->mnt_dirname, quotatypes[type]);
+			tty_write_message(current->tty, quotamessage);
+		}
+		dquot->dq_itime = CURRENT_TIME + dquot->dq_mnt->mnt_iexp[type];
+	}
+	return(QUOTA_OK);
+}
+
+static int check_bdq(struct dquot *dquot, short type, u_long blocks)
+{
+	if (blocks <= 0 || dquot->dq_flags & DQ_FAKE)
+		return(QUOTA_OK);
+	if (dquot->dq_bhardlimit &&
+	   (dquot->dq_curblocks + blocks) > dquot->dq_bhardlimit && !fsuser()) {
+		if ((dquot->dq_flags & DQ_BLKS) == 0 &&
+                     need_print_warning(type, dquot)) {
+			sprintf(quotamessage, "%s: write failed, %s disk limit reached.\r\n",
+			        dquot->dq_mnt->mnt_dirname, quotatypes[type]);
+			tty_write_message(current->tty, quotamessage);
+			dquot->dq_flags |= DQ_BLKS;
+		}
+		return(NO_QUOTA);
+	}
+	if (dquot->dq_bsoftlimit &&
+	   (dquot->dq_curblocks + blocks) > dquot->dq_bsoftlimit &&
+	    dquot->dq_btime && CURRENT_TIME >= dquot->dq_btime && !fsuser()) {
+                if (need_print_warning(type, dquot)) {
+			sprintf(quotamessage, "%s: write failed, %s disk quota exceeded to long.\r\n",
+		        	dquot->dq_mnt->mnt_dirname, quotatypes[type]);
+			tty_write_message(current->tty, quotamessage);
+		}
+		return(NO_QUOTA);
+	}
+	if (dquot->dq_bsoftlimit &&
+	   (dquot->dq_curblocks + blocks) > dquot->dq_bsoftlimit &&
+	    dquot->dq_btime == 0 && !fsuser()) {
+                if (need_print_warning(type, dquot)) {
+			sprintf(quotamessage, "%s: warning, %s disk quota exceeded\r\n",
+		        	dquot->dq_mnt->mnt_dirname, quotatypes[type]);
+			tty_write_message(current->tty, quotamessage);
+		}
+		dquot->dq_btime = CURRENT_TIME + dquot->dq_mnt->mnt_bexp[type];
+	}
+	return(QUOTA_OK);
+}
+
+static void dqput(struct dquot *dquot)
+{
+	if (!dquot)
+		return;
+	/*
+	 * If the dq_mnt pointer isn't initialized this entry needs no
+	 * checking and doesn't need to be written. It just an empty
+	 * dquot that is put back into the freelist.
+	 */
+	if (dquot->dq_mnt != (struct vfsmount *)NULL) {
+		dqstats.drops++;
+		wait_on_dquot(dquot);
+		if (!dquot->dq_count) {
+			printk("VFS: dqput: trying to free free dquot\n");
+			printk("VFS: device %s, dquot of %s %d\n", kdevname(dquot->dq_dev),
+			       quotatypes[dquot->dq_type], dquot->dq_id);
+			return;
+		}
+repeat:
+		if (dquot->dq_count > 1) {
+			dquot->dq_count--;
+			return;
+		}
+		wake_up(&dquot_wait);
+		if (dquot->dq_flags & DQ_MOD) {
+			write_dquot(dquot);	/* we can sleep - so do again */
+			wait_on_dquot(dquot);
+			goto repeat;
+		}
+	}
+	if (dquot->dq_count) {
+		dquot->dq_count--;
+		nr_free_dquots++;
+	}
+	return;
+}
+
+static struct dquot *get_empty_dquot(void)
+{
+	struct dquot *dquot, *best;
+	int cnt;
+
+	if (nr_dquots < NR_DQUOTS && nr_free_dquots < (nr_dquots >> 2))
+		grow_dquots();
+
+repeat:
+	dquot = first_dquot;
+	best = NODQUOT;
+	for (cnt = 0; cnt < nr_dquots; dquot = dquot->dq_next, cnt++) {
+		if (!dquot->dq_count) {
+			if (!best)
+				best = dquot;
+			if (!(dquot->dq_flags & DQ_MOD) && !(dquot->dq_flags & DQ_LOCKED)) {
+				best = dquot;
+				break;
+			}
+		}
+	}
+	if (!best || best->dq_flags & DQ_MOD || best->dq_flags & DQ_LOCKED)
+		if (nr_dquots < NR_DQUOTS) {
+			grow_dquots();
+			goto repeat;
+		}
+	dquot = best;
+	if (!dquot) {
+		printk("VFS: No free dquots - contact mvw@mcs.ow.org\n");
+		sleep_on(&dquot_wait);
+		goto repeat;
+	}
+	if (dquot->dq_flags & DQ_LOCKED) {
+		wait_on_dquot(dquot);
+		goto repeat;
+	}
+	if (dquot->dq_flags & DQ_MOD) {
+		write_dquot(dquot);
+		goto repeat;
+	}
+	if (dquot->dq_count)
+		goto repeat;
+	clear_dquot(dquot);
+	dquot->dq_count = 1;
+	nr_free_dquots--;
+	if (nr_free_dquots < 0) {
+		printk ("VFS: get_empty_dquot: bad free dquot count.\n");
+		nr_free_dquots = 0;
+	}
+	return(dquot);
+}
+
+static struct dquot *dqget(kdev_t dev, unsigned int id, short type)
+{
+	struct dquot *dquot, *empty;
+	struct vfsmount *vfsmnt;
+
+	if ((vfsmnt = lookup_vfsmnt(dev)) == (struct vfsmount *)NULL ||
+	    (vfsmnt->mnt_quotas[type] == (struct file *)0))
+		return(NODQUOT);
+	dqstats.lookups++;
+	empty = get_empty_dquot();
+repeat:
+	dquot = *(hash(dev, id, type));
+	while (dquot) {
+		if (dquot->dq_dev != dev || dquot->dq_id != id) {
+			dquot = dquot->dq_hash_next;
+			continue;
+		}
+		wait_on_dquot(dquot);
+		if (dquot->dq_dev != dev || dquot->dq_id != id)
+			goto repeat;
+		if (!dquot->dq_count)
+			nr_free_dquots--;
+		dquot->dq_count++;
+		if (empty)
+			dqput(empty);
+		dqstats.cache_hits++;
+		return(dquot);
+	}
+	if (!empty)
+		return(NODQUOT);
+	dquot = empty;
+	dquot->dq_id = id;
+	dquot->dq_type = type;
+	dquot->dq_dev = dev;
+	dquot->dq_mnt = vfsmnt;
+	put_last_free(dquot);
+	insert_dquot_hash(dquot);
+	read_dquot(dquot);
+	return(dquot);
+}
+
+/*
+ * Initialize a dquot-struct with new quota info. This is used by the
+ * systemcall interface functions.
+ */ 
+static int set_dqblk(kdev_t dev, int id, short type, int flags, struct dqblk *dqblk)
+{
+	struct dquot *dquot;
+	struct dqblk dq_dqblk;
+	int error;
+
+	if (dqblk == (struct dqblk *)NULL)
+		return(-EFAULT);
+
+	if (flags & QUOTA_SYSCALL) {
+		if ((error = verify_area(VERIFY_READ, dqblk, sizeof(struct dqblk))) != 0)
+			return(error);
+		memcpy_fromfs(&dq_dqblk, dqblk, sizeof(struct dqblk));
+	} else {
+		memcpy(&dq_dqblk, dqblk, sizeof(struct dqblk));
+	}
+	if ((dquot = dqget(dev, id, type)) != NODQUOT) {
+		lock_dquot(dquot);
+		if (id > 0 && ((flags & SET_QUOTA) || (flags & SET_QLIMIT))) {
+			dquot->dq_bhardlimit = dq_dqblk.dqb_bhardlimit;
+			dquot->dq_bsoftlimit = dq_dqblk.dqb_bsoftlimit;
+			dquot->dq_ihardlimit = dq_dqblk.dqb_ihardlimit;
+			dquot->dq_isoftlimit = dq_dqblk.dqb_isoftlimit;
+		}
+		if ((flags & SET_QUOTA) || (flags & SET_USE)) {
+			if (dquot->dq_isoftlimit &&
+			    dquot->dq_curinodes < dquot->dq_isoftlimit &&
+			    dq_dqblk.dqb_curinodes >= dquot->dq_isoftlimit)
+				dquot->dq_itime = CURRENT_TIME + dquot->dq_mnt->mnt_iexp[type];
+			dquot->dq_curinodes = dq_dqblk.dqb_curinodes;
+			if (dquot->dq_curinodes < dquot->dq_isoftlimit)
+				dquot->dq_flags &= ~DQ_INODES;
+			if (dquot->dq_bsoftlimit &&
+			    dquot->dq_curblocks < dquot->dq_bsoftlimit &&
+			    dq_dqblk.dqb_curblocks >= dquot->dq_bsoftlimit)
+				dquot->dq_btime = CURRENT_TIME + dquot->dq_mnt->mnt_bexp[type];
+			dquot->dq_curblocks = dq_dqblk.dqb_curblocks;
+			if (dquot->dq_curblocks < dquot->dq_bsoftlimit)
+				dquot->dq_flags &= ~DQ_BLKS;
+		}
+		if (id == 0) {
+			/* 
+			 * Change in expiretimes, change them in dq_mnt.
+			 */
+			dquot->dq_mnt->mnt_bexp[type] = dquot->dq_btime = dq_dqblk.dqb_btime;
+			dquot->dq_mnt->mnt_iexp[type] = dquot->dq_itime = dq_dqblk.dqb_itime;
+		}
+		if (dq_dqblk.dqb_bhardlimit == 0 && dq_dqblk.dqb_bsoftlimit == 0 &&
+		    dq_dqblk.dqb_ihardlimit == 0 && dq_dqblk.dqb_isoftlimit == 0)
+			dquot->dq_flags |= DQ_FAKE;
+		else
+			dquot->dq_flags &= ~DQ_FAKE;
+		dquot->dq_flags |= DQ_MOD;
+		unlock_dquot(dquot);
+		dqput(dquot);
+	}
+	return(0);
+}
+
+static int get_quota(kdev_t dev, int id, short type, struct dqblk *dqblk)
+{
+	struct dquot *dquot;
+	int error;
+
+	if (has_quota_enabled(dev, type)) {
+		if (dqblk == (struct dqblk *)NULL)
+			return(-EFAULT);
+
+		if ((error = verify_area(VERIFY_WRITE, dqblk, sizeof(struct dqblk))) != 0)
+			return(error);
+
+		if ((dquot = dqget(dev, id, type)) != NODQUOT) {
+			memcpy_tofs(dqblk, (char *)&dquot->dq_dqb, sizeof(struct dqblk));
+			dqput(dquot);
+			return(0);
+		}
+	}
+	return(-ESRCH);
+}
+
+static int get_stats(caddr_t addr)
+{
+	int error;
+
+	if ((error = verify_area(VERIFY_WRITE, addr, sizeof(struct dqstats))) != 0)
+		return(error);
+
+	dqstats.allocated_dquots = nr_dquots;
+	dqstats.free_dquots = nr_free_dquots;
+	memcpy_tofs(addr, (caddr_t)&dqstats, sizeof(struct dqstats));
+	return(0);
+}
+
+/*
+ * Initialize pointer in a inode to the right dquots.
+ */
+void dquot_initialize(struct inode *inode, short type)
+{
+	unsigned int id = 0;
+	short cnt;
+
+	if (S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode)) {
+		for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
+			if (type != -1 && cnt != type)
+				continue;
+			if (!has_quota_enabled(inode->i_dev, cnt))
+				continue;
+			if (inode->i_dquot[cnt] == NODQUOT) {
+				switch (cnt) {
+					case USRQUOTA:
+						id = inode->i_uid;
+						break;
+					case GRPQUOTA:
+						id = inode->i_gid;
+						break;
+				}
+				inode->i_dquot[cnt] = dqget(inode->i_dev, id, cnt);
+				inode->i_flags |= S_WRITE;
+			}
+		}
+	}
+}
+
+void dquot_drop(struct inode *inode)
+{
+	short cnt;
+
+	for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
+		if (inode->i_dquot[cnt] == NODQUOT)
+			continue;
+		dqput(inode->i_dquot[cnt]);
+		inode->i_dquot[cnt] = NODQUOT;
+	}
+	inode->i_flags &= ~S_WRITE;
+}
+
+/*
+ * This is a simple algorithm that calculates the size of a file in blocks.
+ * This is only used on filesystems that do not have a i_blocks count.
+ */
+static u_long isize_to_blocks(size_t isize, size_t blksize)
+{
+	u_long blocks;
+	u_long indirect;
+
+	if (!blksize)
+		blksize = BLOCK_SIZE;
+	blocks = (isize / blksize) + ((isize % blksize) ? 1 : 0);
+	if (blocks > 10) {
+		indirect = ((blocks - 11) >> 8) + 1; /* single indirect blocks */
+		if (blocks > (10 + 256)) {
+			indirect += ((blocks - 267) >> 16) + 1; /* double indirect blocks */
+			if (blocks > (10 + 256 + (256 << 8)))
+				indirect++; /* triple indirect blocks */
+		}
+		blocks += indirect;
+	}
+	return(blocks);
+}
+
+/*
+ * Externaly referenced funtions trough dq_operations.
+ */
+int dquot_alloc_block(const struct inode *inode, unsigned long number)
+{
+	unsigned short cnt;
+
+	for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
+		if (inode->i_dquot[cnt] == NODQUOT)
+			continue;
+		if (check_bdq(inode->i_dquot[cnt], cnt, number))
+			return(NO_QUOTA);
+	}
+	for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
+		if (inode->i_dquot[cnt] == NODQUOT)
+			continue;
+		dquot_incr_blocks(inode->i_dquot[cnt], number);
+	}
+	return(QUOTA_OK);
+}
+
+int dquot_alloc_inode(const struct inode *inode, unsigned long number)
+{
+	unsigned short cnt;
+
+	for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
+		if (inode->i_dquot[cnt] == NODQUOT)
+			continue;
+		if (check_idq(inode->i_dquot[cnt], cnt, number))
+			return(NO_QUOTA);
+	}
+	for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
+		if (inode->i_dquot[cnt] == NODQUOT)
+			continue;
+		dquot_incr_inodes(inode->i_dquot[cnt], number);
+	}
+	return(QUOTA_OK);
+}
+
+void dquot_free_block(const struct inode *inode, unsigned long number)
+{
+	unsigned short cnt;
+
+	for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
+		if (inode->i_dquot[cnt] == NODQUOT)
+			continue;
+		dquot_decr_blocks(inode->i_dquot[cnt], number);
+	}
+}
+
+void dquot_free_inode(const struct inode *inode, unsigned long number)
+{
+	unsigned short cnt;
+
+	for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
+		if (inode->i_dquot[cnt] == NODQUOT)
+			continue;
+		dquot_decr_inodes(inode->i_dquot[cnt], number);
+	}
+}
+
+/*
+ * Transfer the number of inode and blocks from one diskquota to an other.
+ */
+int dquot_transfer(struct inode *inode, struct iattr *iattr, char direction)
+{
+	unsigned long blocks;
+	struct dquot *transfer_from[MAXQUOTAS];
+	struct dquot *transfer_to[MAXQUOTAS];
+	short cnt, disc;
+
+	/*
+	 * Find out if this filesystems uses i_blocks.
+	 */
+	if (inode->i_blksize == 0)
+		blocks = isize_to_blocks(inode->i_size, BLOCK_SIZE);
+	else
+		blocks = (inode->i_blocks / 2);
+
+	/*
+	 * Build the transfer_from and transfer_to lists and check quotas to see
+	 * if operation is permitted.
+	 */
+	for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
+		transfer_from[cnt] = NODQUOT;
+		transfer_to[cnt] = NODQUOT;
+
+		if (!has_quota_enabled(inode->i_dev, cnt))
+			continue;
+
+		switch (cnt) {
+			case USRQUOTA:
+				if (inode->i_uid == iattr->ia_uid)
+					continue;
+				transfer_from[cnt] = dqget(inode->i_dev, (direction) ? iattr->ia_uid : inode->i_uid, cnt);
+				transfer_to[cnt] = dqget(inode->i_dev, (direction) ? inode->i_uid : iattr->ia_uid, cnt);
+				break;
+			case GRPQUOTA:
+				if (inode->i_gid == iattr->ia_gid)
+					continue;
+				transfer_from[cnt] = dqget(inode->i_dev, (direction) ? iattr->ia_gid : inode->i_gid, cnt);
+				transfer_to[cnt] = dqget(inode->i_dev, (direction) ? inode->i_gid : iattr->ia_gid, cnt);
+				break;
+		}
+
+		if (check_idq(transfer_to[cnt], cnt, 1) == NO_QUOTA ||
+		    check_bdq(transfer_to[cnt], cnt, blocks) == NO_QUOTA) {
+			for (disc = 0; disc <= cnt; disc++) {
+				dqput(transfer_from[disc]);
+				dqput(transfer_to[disc]);
+			}
+			return(NO_QUOTA);
+		}
+	}
+
+	/*
+	 * Finaly perform the needed transfer from transfer_from to transfer_to.
+	 * And release any pointer to dquots not needed anymore.
+	 */
+	for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
+		/*
+		 * Skip changes for same uid or gid or for non-existing quota-type.
+		 */
+		if (transfer_from[cnt] == NODQUOT && transfer_to[cnt] == NODQUOT)
+			continue;
+
+		if (transfer_from[cnt] != NODQUOT) {
+			dquot_decr_inodes(transfer_from[cnt], 1);
+			dquot_decr_blocks(transfer_from[cnt], blocks);
+		}
+		if (transfer_to[cnt] != NODQUOT) {
+			dquot_incr_inodes(transfer_to[cnt], 1);
+			dquot_incr_blocks(transfer_to[cnt], blocks);
+		}
+		if (inode->i_dquot[cnt] != NODQUOT) {
+			dqput(transfer_from[cnt]);
+			dqput(inode->i_dquot[cnt]);
+			inode->i_dquot[cnt] = transfer_to[cnt];
+		} else {
+			dqput(transfer_from[cnt]);
+			dqput(transfer_to[cnt]);
+		}
+	}
+	return(QUOTA_OK);
+}
+
+void dquot_init(void)
+{
+	printk("VFS: Diskquotas version %s initialized\r\n", __DQUOT_VERSION__);
+	memset(hash_table, 0, sizeof(hash_table));
+	memset((caddr_t)&dqstats, 0, sizeof(dqstats));
+	first_dquot = NODQUOT;
+}
+
+/*
+ * Definitions of diskquota operations.
+ */
+struct dquot_operations dquot_operations = {
+	dquot_initialize,
+	dquot_drop,
+	dquot_alloc_block,
+	dquot_alloc_inode,
+	dquot_free_block,
+	dquot_free_inode,
+	dquot_transfer
+};
+
+/*
+ * Turn quota off on a device. type == -1 ==> quotaoff for all types (umount)
+ */
+int quota_off(kdev_t dev, short type)
+{
+	struct vfsmount *vfsmnt;
+	short cnt;
+
+	for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
+		if (type != -1 && cnt != type)
+			continue;
+		if ((vfsmnt = lookup_vfsmnt(dev)) == (struct vfsmount *)NULL ||
+	     	     vfsmnt->mnt_quotas[cnt] == (struct file *)NULL)
+			continue;
+		vfsmnt->mnt_sb->dq_op = (struct dquot_operations *)NULL;
+		reset_dquot_ptrs(dev, cnt);
+		invalidate_dquots(dev, cnt);
+		close_fp(vfsmnt->mnt_quotas[cnt]);
+		vfsmnt->mnt_quotas[cnt] = (struct file *)NULL;
+		vfsmnt->mnt_iexp[cnt] = vfsmnt->mnt_bexp[cnt] = (time_t)NULL;
+	}
+	return(0);
+}
+
+int quota_on(kdev_t dev, short type, char *path)
+{
+	struct file *filp = (struct file *)NULL;
+	struct vfsmount *vfsmnt;
+	struct inode *inode;
+	struct dquot *dquot;
+	char *tmp;
+	int error;
+
+	if ((vfsmnt = lookup_vfsmnt(dev)) == (struct vfsmount *)NULL)
+		return(-ENODEV);
+	if (vfsmnt->mnt_quotas[type] != (struct file *)NULL)
+		return(-EBUSY);
+	if ((error = getname(path, &tmp)) != 0)
+		return(error);
+	error = open_namei(tmp, O_RDWR, 0600, &inode, 0);
+	putname(tmp);
+	if (error)
+		return(error);
+	if (!S_ISREG(inode->i_mode)) {
+		iput(inode);
+		return(-EACCES);
+	}
+	if ((filp = get_empty_filp()) != (struct file *)NULL) {
+		filp->f_mode = (O_RDWR + 1) & O_ACCMODE;
+		filp->f_flags = O_RDWR;
+		filp->f_inode = inode;
+		filp->f_pos = 0;
+		filp->f_reada = 0;
+		filp->f_op = inode->i_op->default_file_ops;
+		if (filp->f_op->read || filp->f_op->write) {
+			if ((error = get_write_access(inode)) == 0) {
+				if (filp->f_op && filp->f_op->open)
+					error = filp->f_op->open(inode, filp);
+				if (error == 0) {
+					vfsmnt->mnt_quotas[type] = filp;
+					dquot = dqget(dev, 0, type);
+					vfsmnt->mnt_iexp[type] = (dquot) ? dquot->dq_itime : MAX_IQ_TIME;
+					vfsmnt->mnt_bexp[type] = (dquot) ? dquot->dq_btime : MAX_DQ_TIME;
+					dqput(dquot);
+					vfsmnt->mnt_sb->dq_op = &dquot_operations;
+					add_dquot_ref(dev, type);
+					return(0);
+				}
+				put_write_access(inode);
+			}
+		} else
+			error = -EIO;
+	  filp->f_count--;
+	} else
+		error = -EMFILE;
+	iput(inode);
+	return(error);
+}
+
+/*
+ * Ok this is the systemcall interface, this communicates with
+ * the userlevel programs. Currently this only supports diskquota
+ * calls. Maybe we need to add the process quotas etc in the future.
+ * But we probably better use rlimits for that.
+ */
+asmlinkage int sys_quotactl(int cmd, const char *special, int id, caddr_t addr)
+{
+	int cmds = 0, type = 0, flags = 0;
+	struct inode *ino;
+	kdev_t dev;
+
+	cmds = cmd >> SUBCMDSHIFT;
+	type = cmd & SUBCMDMASK;
+
+	if ((u_int) type >= MAXQUOTAS)
+		return(-EINVAL);
+	switch (cmds) {
+		case Q_SYNC:
+		case Q_GETSTATS:
+			break;
+		case Q_GETQUOTA:
+			if (((type == USRQUOTA && current->uid != id) ||
+			     (type == GRPQUOTA && current->gid != id)) && !fsuser())
+				return(-EPERM);
+			break;
+		default:
+			if (!fsuser())
+				return(-EPERM);
+	}
+
+	if (special == (char *)NULL && (cmds == Q_SYNC || cmds == Q_GETSTATS))
+		dev = 0;
+	else {
+		if (namei(special, &ino))
+			return(-EINVAL);
+		dev = ino->i_rdev;
+		if (!S_ISBLK(ino->i_mode)) {
+			iput(ino);
+			return(-ENOTBLK);
+		}
+		iput(ino);
+	}
+
+	switch (cmds) {
+		case Q_QUOTAON:
+			return(quota_on(dev, type, (char *) addr));
+		case Q_QUOTAOFF:
+			return(quota_off(dev, type));
+		case Q_GETQUOTA:
+			return(get_quota(dev, id, type, (struct dqblk *) addr));
+		case Q_SETQUOTA:
+			flags |= SET_QUOTA;
+			break;
+		case Q_SETUSE:
+			flags |= SET_USE;
+			break;
+		case Q_SETQLIM:
+			flags |= SET_QLIMIT;
+			break;
+		case Q_SYNC:
+			return(sync_dquots(dev, type));
+		case Q_GETSTATS:
+			return(get_stats(addr));
+		default:
+			return(-EINVAL);
+	}
+
+	flags |= QUOTA_SYSCALL;
+	if (has_quota_enabled(dev, type))
+		return(set_dqblk(dev, id, type, flags, (struct dqblk *) addr));
+	return(-ESRCH);
+}
diff -u --recursive --new-file v1.3.45/linux/fs/exec.c linux/fs/exec.c
--- v1.3.45/linux/fs/exec.c	Tue Nov 21 13:22:12 1995
+++ linux/fs/exec.c	Mon Dec 11 06:56:35 1995
@@ -666,7 +666,7 @@
 		goto exec_error2;
 	}
 	/* better not execute files which are being written to */
-	if (bprm.inode->i_wcount > 0) {
+	if (bprm.inode->i_writecount > 0) {
 		retval = -ETXTBSY;
 		goto exec_error2;
 	}
diff -u --recursive --new-file v1.3.45/linux/fs/ext2/balloc.c linux/fs/ext2/balloc.c
--- v1.3.45/linux/fs/ext2/balloc.c	Mon Nov 27 12:48:31 1995
+++ linux/fs/ext2/balloc.c	Mon Dec 11 06:56:35 1995
@@ -165,7 +165,7 @@
 	return load__block_bitmap (sb, block_group);
 }
 
-void ext2_free_blocks (struct super_block * sb, unsigned long block,
+void ext2_free_blocks (const struct inode * inode, unsigned long block,
 		       unsigned long count)
 {
 	struct buffer_head * bh;
@@ -174,9 +174,11 @@
 	unsigned long bit;
 	unsigned long i;
 	int bitmap_nr;
+	struct super_block * sb;
 	struct ext2_group_desc * gdp;
 	struct ext2_super_block * es;
 
+	sb = inode->i_sb;
 	if (!sb) {
 		printk ("ext2_free_blocks: nonexistent device");
 		return;
@@ -224,6 +226,8 @@
 				      "bit already cleared for block %lu", 
 				      block);
 		else {
+			if (sb->dq_op)
+				sb->dq_op->free_block(inode, fs_to_dq_blocks(1, inode->i_blksize));
 			gdp->bg_free_blocks_count++;
 			es->s_free_blocks_count++;
 		}
@@ -249,21 +253,23 @@
  * each block group the search first looks for an entire free byte in the block
  * bitmap, and then for any free bit if that fails.
  */
-int ext2_new_block (struct super_block * sb, unsigned long goal,
-		    u32 * prealloc_count,
-		    u32 * prealloc_block)
+int ext2_new_block (const struct inode * inode, unsigned long goal,
+		    u32 * prealloc_count, u32 * prealloc_block, int * err)
 {
 	struct buffer_head * bh;
 	struct buffer_head * bh2;
 	char * p, * r;
 	int i, j, k, tmp;
 	int bitmap_nr;
+	struct super_block * sb;
 	struct ext2_group_desc * gdp;
 	struct ext2_super_block * es;
 
+	*err = -ENOSPC;
 #ifdef EXT2FS_DEBUG
 	static int goal_hits = 0, goal_attempts = 0;
 #endif
+	sb = inode->i_sb;
 	if (!sb) {
 		printk ("ext2_new_block: nonexistent device");
 		return 0;
@@ -394,6 +400,16 @@
 
 	ext2_debug ("using block group %d(%d)\n", i, gdp->bg_free_blocks_count);
 
+	/*
+	 * Check quota for allocation of this block.
+	 */
+	if (sb->dq_op)
+		if (sb->dq_op->alloc_block (inode, fs_to_dq_blocks(1, inode->i_blksize))) {
+			unlock_super (sb);
+			*err = -EDQUOT;
+			return 0;
+		}
+
 	tmp = j + i * EXT2_BLOCKS_PER_GROUP(sb) + es->s_first_data_block;
 
 	if (test_opt (sb, CHECK_STRICT) &&
@@ -407,6 +423,8 @@
 	if (set_bit (j, bh->b_data)) {
 		ext2_warning (sb, "ext2_new_block",
 			      "bit already set for block %d", j);
+		if (sb->dq_op)
+			sb->dq_op->free_block(inode, fs_to_dq_blocks(1, inode->i_blksize));
 		goto repeat;
 	}
 
@@ -421,8 +439,14 @@
 		*prealloc_block = tmp + 1;
 		for (k = 1;
 		     k < 8 && (j + k) < EXT2_BLOCKS_PER_GROUP(sb); k++) {
-			if (set_bit (j + k, bh->b_data))
+			if (sb->dq_op)
+				if (sb->dq_op->alloc_block(inode, fs_to_dq_blocks(1, inode->i_blksize)))
+					break;
+			if (set_bit (j + k, bh->b_data)) {
+				if (sb->dq_op)
+					sb->dq_op->free_block(inode, fs_to_dq_blocks(1, inode->i_blksize));
 				break;
+			}
 			(*prealloc_count)++;
 		}	
 		gdp->bg_free_blocks_count -= *prealloc_count;
@@ -466,6 +490,7 @@
 	mark_buffer_dirty(sb->u.ext2_sb.s_sbh, 1);
 	sb->s_dirt = 1;
 	unlock_super (sb);
+	*err = 0;
 	return j;
 }
 
diff -u --recursive --new-file v1.3.45/linux/fs/ext2/ialloc.c linux/fs/ext2/ialloc.c
--- v1.3.45/linux/fs/ext2/ialloc.c	Wed Mar 22 10:33:59 1995
+++ linux/fs/ext2/ialloc.c	Mon Dec 11 10:10:07 1995
@@ -252,7 +252,8 @@
 		ll_rw_block (WRITE, 1, &bh);
 		wait_on_buffer (bh);
 	}
-
+	if (sb->dq_op)
+		sb->dq_op->free_inode (inode, 1);
 	sb->s_dirt = 1;
 	clear_inode (inode);
 	unlock_super (sb);
@@ -303,7 +304,7 @@
  * For other inodes, search forward from the parent directory\'s block
  * group to find a free inode.
  */
-struct inode * ext2_new_inode (const struct inode * dir, int mode)
+struct inode * ext2_new_inode (const struct inode * dir, int mode, int * err)
 {
 	struct super_block * sb;
 	struct buffer_head * bh;
@@ -325,6 +326,7 @@
 repeat:
 	gdp = NULL; i=0;
 	
+	*err = -ENOSPC;
 	if (S_ISDIR(mode)) {
 		avefreei = es->s_free_inodes_count /
 			sb->u.ext2_sb.s_groups_count;
@@ -481,9 +483,21 @@
 	insert_inode_hash(inode);
 	inc_inode_version (inode, gdp, mode);
 
+	unlock_super (sb);
+	if (sb->dq_op) {
+		sb->dq_op->initialize (inode, -1);
+		if (sb->dq_op->alloc_inode (inode, 1)) {
+			sb->dq_op->drop (inode);
+			inode->i_nlink = 0;
+			iput (inode);
+			*err = -EDQUOT;
+			return NULL;
+		}
+		inode->i_flags |= S_WRITE;
+	}
 	ext2_debug ("allocating inode %lu\n", inode->i_ino);
 
-	unlock_super (sb);
+	*err = 0;
 	return inode;
 }
 
diff -u --recursive --new-file v1.3.45/linux/fs/ext2/inode.c linux/fs/ext2/inode.c
--- v1.3.45/linux/fs/ext2/inode.c	Mon Nov 27 12:48:31 1995
+++ linux/fs/ext2/inode.c	Mon Dec 11 06:56:35 1995
@@ -62,17 +62,17 @@
 void ext2_discard_prealloc (struct inode * inode)
 {
 #ifdef EXT2_PREALLOCATE
+	unsigned short total;
+
 	if (inode->u.ext2_i.i_prealloc_count) {
-		int i = inode->u.ext2_i.i_prealloc_count;
+		total = inode->u.ext2_i.i_prealloc_count;
 		inode->u.ext2_i.i_prealloc_count = 0;
-		ext2_free_blocks (inode->i_sb,
-				  inode->u.ext2_i.i_prealloc_block,
-				  i);
+		ext2_free_blocks (inode, inode->u.ext2_i.i_prealloc_block, total);
 	}
 #endif
 }
 
-static int ext2_alloc_block (struct inode * inode, unsigned long goal)
+static int ext2_alloc_block (struct inode * inode, unsigned long goal, int * err)
 {
 #ifdef EXT2FS_DEBUG
 	static unsigned long alloc_hits = 0, alloc_attempts = 0;
@@ -110,15 +110,14 @@
 		ext2_debug ("preallocation miss (%lu/%lu).\n",
 			    alloc_hits, ++alloc_attempts);
 		if (S_ISREG(inode->i_mode))
-			result = ext2_new_block
-				(inode->i_sb, goal,
+			result = ext2_new_block (inode, goal,
 				 &inode->u.ext2_i.i_prealloc_count,
-				 &inode->u.ext2_i.i_prealloc_block);
+				 &inode->u.ext2_i.i_prealloc_block, err);
 		else
-			result = ext2_new_block (inode->i_sb, goal, 0, 0);
+			result = ext2_new_block (inode, goal, 0, 0, err);
 	}
 #else
-	result = ext2_new_block (inode->i_sb, goal, 0, 0);
+	result = ext2_new_block (inode, goal, 0, 0, err);
 #endif
 
 	return result;
@@ -225,12 +224,12 @@
 
 	ext2_debug ("goal = %d.\n", goal);
 
-	tmp = ext2_alloc_block (inode, goal);
+	tmp = ext2_alloc_block (inode, goal, err);
 	if (!tmp)
 		return NULL;
 	result = getblk (inode->i_dev, tmp, inode->i_sb->s_blocksize);
 	if (*p) {
-		ext2_free_blocks (inode->i_sb, tmp, 1);
+		ext2_free_blocks (inode, tmp, 1);
 		brelse (result);
 		goto repeat;
 	}
@@ -297,14 +296,14 @@
 		if (!goal)
 			goal = bh->b_blocknr;
 	}
-	tmp = ext2_alloc_block (inode, goal);
+	tmp = ext2_alloc_block (inode, goal, err);
 	if (!tmp) {
 		brelse (bh);
 		return NULL;
 	}
 	result = getblk (bh->b_dev, tmp, blocksize);
 	if (*p) {
-		ext2_free_blocks (inode->i_sb, tmp, 1);
+		ext2_free_blocks (inode, tmp, 1);
 		brelse (result);
 		goto repeat;
 	}
diff -u --recursive --new-file v1.3.45/linux/fs/ext2/namei.c linux/fs/ext2/namei.c
--- v1.3.45/linux/fs/ext2/namei.c	Mon Nov 27 12:48:31 1995
+++ linux/fs/ext2/namei.c	Mon Dec 11 06:56:35 1995
@@ -375,10 +375,10 @@
 	*result = NULL;
 	if (!dir)
 		return -ENOENT;
-	inode = ext2_new_inode (dir, mode);
+	inode = ext2_new_inode (dir, mode, &err);
 	if (!inode) {
 		iput (dir);
-		return -ENOSPC;
+		return err;
 	}
 	inode->i_op = &ext2_file_inode_operations;
 	inode->i_mode = mode;
@@ -421,10 +421,10 @@
 		iput (dir);
 		return -EEXIST;
 	}
-	inode = ext2_new_inode (dir, mode);
+	inode = ext2_new_inode (dir, mode, &err);
 	if (!inode) {
 		iput (dir);
-		return -ENOSPC;
+		return err;
 	}
 	inode->i_uid = current->fsuid;
 	inode->i_mode = mode;
@@ -488,10 +488,10 @@
 		iput (dir);
 		return -EMLINK;
 	}
-	inode = ext2_new_inode (dir, S_IFDIR);
+	inode = ext2_new_inode (dir, S_IFDIR, &err);
 	if (!inode) {
 		iput (dir);
-		return -ENOSPC;
+		return err;
 	}
 	inode->i_op = &ext2_dir_inode_operations;
 	inode->i_size = inode->i_sb->s_blocksize;
@@ -622,6 +622,8 @@
 	retval = -EPERM;
 	if (!(inode = iget (dir->i_sb, de->inode)))
 		goto end_rmdir;
+	if (inode->i_sb->dq_op)
+		inode->i_sb->dq_op->initialize (inode, -1);
 	if (inode->i_dev != dir->i_dev)
 		goto end_rmdir;
 	if (de->inode != inode->i_ino) {
@@ -702,6 +704,8 @@
 		goto end_unlink;
 	if (!(inode = iget (dir->i_sb, de->inode)))
 		goto end_unlink;
+	if (inode->i_sb->dq_op)
+		inode->i_sb->dq_op->initialize (inode, -1);
 	retval = -EPERM;
 	if (S_ISDIR(inode->i_mode))
 		goto end_unlink;
@@ -757,9 +761,9 @@
 	int l;
 	char c;
 
-	if (!(inode = ext2_new_inode (dir, S_IFLNK))) {
+	if (!(inode = ext2_new_inode (dir, S_IFLNK, &err))) {
 		iput (dir);
-		return -ENOSPC;
+		return err;
 	}
 	inode->i_mode = S_IFLNK | S_IRWXUGO;
 	inode->i_op = &ext2_symlink_inode_operations;
@@ -795,6 +799,7 @@
 	}
 	inode->i_size = i;
 	inode->i_dirt = 1;
+
 	bh = ext2_find_entry (dir, name, len, &de);
 	if (bh) {
 		inode->i_nlink--;
@@ -967,6 +972,9 @@
 		if (!new_inode) {
 			brelse (new_bh);
 			new_bh = NULL;
+		} else {
+			if (new_inode->i_sb->dq_op)
+				new_inode->i_sb->dq_op->initialize (new_inode, -1);
 		}
 	}
 	if (new_inode == old_inode) {
diff -u --recursive --new-file v1.3.45/linux/fs/ext2/super.c linux/fs/ext2/super.c
--- v1.3.45/linux/fs/ext2/super.c	Mon Nov 13 12:36:46 1995
+++ linux/fs/ext2/super.c	Mon Dec 11 06:56:35 1995
@@ -249,6 +249,12 @@
 				return 0;
 			}
 		}
+		/* Silently ignore the quota options */
+		else if (!strcmp (this_char, "grpquota")
+		         || !strcmp (this_char, "noquota")
+		         || !strcmp (this_char, "quota")
+		         || !strcmp (this_char, "usrquota"))
+			/* Don't do anything ;-) */ ;
 		else {
 			printk ("EXT2-fs: Unrecognized mount option %s\n", this_char);
 			return 0;
diff -u --recursive --new-file v1.3.45/linux/fs/ext2/truncate.c linux/fs/ext2/truncate.c
--- v1.3.45/linux/fs/ext2/truncate.c	Mon Nov 27 12:48:31 1995
+++ linux/fs/ext2/truncate.c	Mon Dec 11 06:56:35 1995
@@ -96,14 +96,14 @@
 		} else if (free_count > 0 && block_to_free == tmp - free_count)
 			free_count++;
 		else {
-			ext2_free_blocks (inode->i_sb, block_to_free, free_count);
+			ext2_free_blocks (inode, block_to_free, free_count);
 			block_to_free = tmp;
 			free_count = 1;
 		}
-/*		ext2_free_blocks (inode->i_sb, tmp, 1); */
+/*		ext2_free_blocks (inode, tmp, 1); */
 	}
 	if (free_count > 0)
-		ext2_free_blocks (inode->i_sb, block_to_free, free_count);
+		ext2_free_blocks (inode, block_to_free, free_count);
 	return retry;
 }
 
@@ -163,16 +163,16 @@
 		} else if (free_count > 0 && block_to_free == tmp - free_count)
 			free_count++;
 		else {
-			ext2_free_blocks (inode->i_sb, block_to_free, free_count);
+			ext2_free_blocks (inode, block_to_free, free_count);
 			block_to_free = tmp;
 			free_count = 1;
 		}
-/*		ext2_free_blocks (inode->i_sb, tmp, 1); */
+/*		ext2_free_blocks (inode, tmp, 1); */
 		inode->i_blocks -= blocks;
 		inode->i_dirt = 1;
 	}
 	if (free_count > 0)
-		ext2_free_blocks (inode->i_sb, block_to_free, free_count);
+		ext2_free_blocks (inode, block_to_free, free_count);
 	ind = (u32 *) ind_bh->b_data;
 	for (i = 0; i < addr_per_block; i++)
 		if (*(ind++))
@@ -185,7 +185,7 @@
 			*p = 0;
 			inode->i_blocks -= blocks;
 			inode->i_dirt = 1;
-			ext2_free_blocks (inode->i_sb, tmp, 1);
+			ext2_free_blocks (inode, tmp, 1);
 		}
 	if (IS_SYNC(inode) && buffer_dirty(ind_bh)) {
 		ll_rw_block (WRITE, 1, &ind_bh);
@@ -245,7 +245,7 @@
 			*p = 0;
 			inode->i_blocks -= blocks;
 			inode->i_dirt = 1;
-			ext2_free_blocks (inode->i_sb, tmp, 1);
+			ext2_free_blocks (inode, tmp, 1);
 		}
 	if (IS_SYNC(inode) && buffer_dirty(dind_bh)) {
 		ll_rw_block (WRITE, 1, &dind_bh);
@@ -304,7 +304,7 @@
 			*p = 0;
 			inode->i_blocks -= blocks;
 			inode->i_dirt = 1;
-			ext2_free_blocks (inode->i_sb, tmp, 1);
+			ext2_free_blocks (inode, tmp, 1);
 		}
 	if (IS_SYNC(inode) && buffer_dirty(tind_bh)) {
 		ll_rw_block (WRITE, 1, &tind_bh);
diff -u --recursive --new-file v1.3.45/linux/fs/file_table.c linux/fs/file_table.c
--- v1.3.45/linux/fs/file_table.c	Fri Nov 17 08:42:27 1995
+++ linux/fs/file_table.c	Mon Dec 11 07:58:43 1995
@@ -119,3 +119,33 @@
 
 	return NULL;
 }
+
+void add_dquot_ref(dev_t dev, short type)
+{
+	struct file *filp;
+	int cnt;
+
+	for (filp = first_file, cnt = 0; cnt < nr_files; cnt++, filp = filp->f_next) {
+		if (!filp->f_count || !filp->f_inode || filp->f_inode->i_dev != dev)
+			continue;
+		if (filp->f_mode & FMODE_WRITE && filp->f_inode->i_sb->dq_op) {
+			filp->f_inode->i_sb->dq_op->initialize(filp->f_inode, type);
+			filp->f_inode->i_flags |= S_WRITE;
+		}
+	}
+}
+
+void reset_dquot_ptrs(dev_t dev, short type)
+{
+	struct file *filp;
+	int cnt;
+
+	for (filp = first_file, cnt = 0; cnt < nr_files; cnt++, filp = filp->f_next) {
+		if (!filp->f_count || !filp->f_inode || filp->f_inode->i_dev != dev)
+			continue;
+		if (IS_WRITABLE(filp->f_inode)) {
+			filp->f_inode->i_dquot[type] = NODQUOT;
+			filp->f_inode->i_flags &= ~S_WRITE;
+		}
+	}
+}
diff -u --recursive --new-file v1.3.45/linux/fs/hpfs/hpfs_fs.c linux/fs/hpfs/hpfs_fs.c
--- v1.3.45/linux/fs/hpfs/hpfs_fs.c	Thu Nov  9 11:23:51 1995
+++ linux/fs/hpfs/hpfs_fs.c	Mon Dec 11 06:56:36 1995
@@ -600,7 +600,7 @@
 				return 0;
 		}
 		else
-			return 0;
+			return 1;
 	}
 
 	return 1;
diff -u --recursive --new-file v1.3.45/linux/fs/inode.c linux/fs/inode.c
--- v1.3.45/linux/fs/inode.c	Fri Nov 17 08:42:27 1995
+++ linux/fs/inode.c	Mon Dec 11 06:56:36 1995
@@ -149,6 +149,10 @@
 	struct wait_queue * wait;
 
 	wait_on_inode(inode);
+	if (IS_WRITABLE(inode)) {
+		if (inode->i_sb->dq_op)
+			inode->i_sb->dq_op->drop(inode);
+	}
 	remove_inode_hash(inode);
 	remove_inode_free(inode);
 	wait = ((volatile struct inode *) inode)->i_wait;
@@ -389,28 +393,38 @@
 		inode->i_count--;
 		return;
 	}
+
 	wake_up(&inode_wait);
 	if (inode->i_pipe) {
 		unsigned long page = (unsigned long) PIPE_BASE(*inode);
 		PIPE_BASE(*inode) = NULL;
 		free_page(page);
 	}
+
 	if (inode->i_sb && inode->i_sb->s_op && inode->i_sb->s_op->put_inode) {
 		inode->i_sb->s_op->put_inode(inode);
 		if (!inode->i_nlink)
 			return;
 	}
+
 	if (inode->i_dirt) {
 		write_inode(inode);	/* we can sleep - so do again */
 		wait_on_inode(inode);
 		goto repeat;
 	}
+
 	inode->i_count--;
+	if (IS_WRITABLE(inode)) {
+		if (inode->i_sb->dq_op)
+			inode->i_sb->dq_op->drop(inode);
+	}
+
 	if (inode->i_mmap) {
 		printk("iput: inode %lu on device %s still has mappings.\n",
 			inode->i_ino, kdevname(inode->i_dev));
 		inode->i_mmap = NULL;
 	}
+
 	nr_free_inodes++;
 	return;
 }
@@ -499,7 +513,7 @@
 	return inode;
 }
 
-struct inode * __iget(struct super_block * sb, int nr, int crossmntp)
+struct inode *__iget(struct super_block * sb, int nr, int crossmntp)
 {
 	static struct wait_queue * update_wait = NULL;
 	struct inode_hash_entry * h;
@@ -526,7 +540,6 @@
 	inode->i_sb = sb;
 	inode->i_dev = sb->s_dev;
 	inode->i_ino = nr;
-	inode->i_flags = sb->s_flags;
 	put_last_free(inode);
 	insert_inode_hash(inode);
 	read_inode(inode);
diff -u --recursive --new-file v1.3.45/linux/fs/isofs/inode.c linux/fs/isofs/inode.c
--- v1.3.45/linux/fs/isofs/inode.c	Sat Nov 25 19:04:47 1995
+++ linux/fs/isofs/inode.c	Mon Dec 11 06:56:36 1995
@@ -142,7 +142,7 @@
 		    break;
 		  }
 		}
-		else return 0;
+		else return 1;
 	}
 	return 1;
 }
diff -u --recursive --new-file v1.3.45/linux/fs/msdos/inode.c linux/fs/msdos/inode.c
--- v1.3.45/linux/fs/msdos/inode.c	Mon Nov 13 12:36:46 1995
+++ linux/fs/msdos/inode.c	Mon Dec 11 06:56:36 1995
@@ -155,7 +155,7 @@
 				printk ("MSDOS FS: Invalid blocksize (512 or 1024)\n");
 			}
 		}
-		else return 0;
+		else return 1;
 	}
 	return 1;
 }
diff -u --recursive --new-file v1.3.45/linux/fs/namei.c linux/fs/namei.c
--- v1.3.45/linux/fs/namei.c	Mon Nov 27 12:48:32 1995
+++ linux/fs/namei.c	Mon Dec 11 06:56:36 1995
@@ -136,13 +136,13 @@
 					return -ETXTBSY;
 			}
 		}
-	inode->i_wcount++;
+	inode->i_writecount++;
 	return 0;
 }
 
 void put_write_access(struct inode * inode)
 {
-	inode->i_wcount--;
+	inode->i_writecount--;
 }
 
 /*
@@ -151,7 +151,7 @@
  * fathers (pseudo-roots, mount-points)
  */
 int lookup(struct inode * dir,const char * name, int len,
-	struct inode ** result)
+           struct inode ** result)
 {
 	struct super_block * sb;
 	int perm;
@@ -185,7 +185,7 @@
 		*result = dir;
 		return 0;
 	}
-	return dir->i_op->lookup(dir,name,len,result);
+	return dir->i_op->lookup(dir, name, len, result);
 }
 
 int follow_link(struct inode * dir, struct inode * inode,
@@ -211,8 +211,8 @@
  * dir_namei() returns the inode of the directory of the
  * specified name, and the name within that directory.
  */
-static int dir_namei(const char * pathname, int * namelen, const char ** name,
-	struct inode * base, struct inode ** res_inode)
+static int dir_namei(const char *pathname, int *namelen, const char **name,
+                     struct inode * base, struct inode **res_inode)
 {
 	char c;
 	const char * thisname;
@@ -237,7 +237,7 @@
 		if (!c)
 			break;
 		base->i_count++;
-		error = lookup(base,thisname,len,&inode);
+		error = lookup(base, thisname, len, &inode);
 		if (error) {
 			iput(base);
 			return error;
@@ -257,24 +257,24 @@
 }
 
 static int _namei(const char * pathname, struct inode * base,
-	int follow_links, struct inode ** res_inode)
+                  int follow_links, struct inode ** res_inode)
 {
-	const char * basename;
+	const char *basename;
 	int namelen,error;
 	struct inode * inode;
 
 	*res_inode = NULL;
-	error = dir_namei(pathname,&namelen,&basename,base,&base);
+	error = dir_namei(pathname, &namelen, &basename, base, &base);
 	if (error)
 		return error;
 	base->i_count++;	/* lookup uses up base */
-	error = lookup(base,basename,namelen,&inode);
+	error = lookup(base, basename, namelen, &inode);
 	if (error) {
 		iput(base);
 		return error;
 	}
 	if (follow_links) {
-		error = follow_link(base,inode,0,0,&inode);
+		error = follow_link(base, inode, 0, 0, &inode);
 		if (error)
 			return error;
 	} else
@@ -283,14 +283,14 @@
 	return 0;
 }
 
-int lnamei(const char * pathname, struct inode ** res_inode)
+int lnamei(const char *pathname, struct inode **res_inode)
 {
 	int error;
 	char * tmp;
 
-	error = getname(pathname,&tmp);
+	error = getname(pathname, &tmp);
 	if (!error) {
-		error = _namei(tmp,NULL,0,res_inode);
+		error = _namei(tmp, NULL, 0, res_inode);
 		putname(tmp);
 	}
 	return error;
@@ -303,14 +303,14 @@
  * Open, link etc use their own routines, but this is enough for things
  * like 'chmod' etc.
  */
-int namei(const char * pathname, struct inode ** res_inode)
+int namei(const char *pathname, struct inode **res_inode)
 {
 	int error;
 	char * tmp;
 
-	error = getname(pathname,&tmp);
+	error = getname(pathname, &tmp);
 	if (!error) {
-		error = _namei(tmp,NULL,1,res_inode);
+		error = _namei(tmp, NULL, 1, res_inode);
 		putname(tmp);
 	}
 	return error;
@@ -330,7 +330,7 @@
  * for symlinks (where the permissions are checked later).
  */
 int open_namei(const char * pathname, int flag, int mode,
-	struct inode ** res_inode, struct inode * base)
+               struct inode ** res_inode, struct inode * base)
 {
 	const char * basename;
 	int namelen,error;
@@ -338,7 +338,7 @@
 
 	mode &= S_IALLUGO & ~current->fs->umask;
 	mode |= S_IFREG;
-	error = dir_namei(pathname,&namelen,&basename,base,&dir);
+	error = dir_namei(pathname, &namelen, &basename, base, &dir);
 	if (error)
 		return error;
 	if (!namelen) {			/* special case: '/usr/' etc */
@@ -357,7 +357,7 @@
 	dir->i_count++;		/* lookup eats the dir */
 	if (flag & O_CREAT) {
 		down(&dir->i_sem);
-		error = lookup(dir,basename,namelen,&inode);
+		error = lookup(dir, basename, namelen, &inode);
 		if (!error) {
 			if (flag & O_EXCL) {
 				iput(inode);
@@ -371,14 +371,16 @@
 			error = -EROFS;
 		else {
 			dir->i_count++;		/* create eats the dir */
-			error = dir->i_op->create(dir,basename,namelen,mode,res_inode);
+			if (dir->i_sb->dq_op)
+				dir->i_sb->dq_op->initialize(dir, -1);
+			error = dir->i_op->create(dir, basename, namelen, mode, res_inode);
 			up(&dir->i_sem);
 			iput(dir);
 			return error;
 		}
 		up(&dir->i_sem);
 	} else
-		error = lookup(dir,basename,namelen,&inode);
+		error = lookup(dir, basename, namelen, &inode);
 	if (error) {
 		iput(dir);
 		return error;
@@ -409,7 +411,7 @@
 	/*
 	 * An append-only file must be opened in append mode for writing
 	 */
-	if (IS_APPEND(inode) && ((flag & 2) && !(flag & O_APPEND))) {
+	if (IS_APPEND(inode) && ((flag & FMODE_WRITE) && !(flag & O_APPEND))) {
 		iput(inode);
 		return -EPERM;
 	}
@@ -420,6 +422,8 @@
 			iput(inode);
 			return error;
 		}
+		if (inode->i_sb->dq_op)
+			inode->i_sb->dq_op->initialize(inode, -1);
 		newattrs.ia_size = 0;
 		newattrs.ia_valid = ATTR_SIZE;
 		if ((error = notify_change(inode, &newattrs))) {
@@ -434,7 +438,10 @@
 		up(&inode->i_sem);
 		inode->i_dirt = 1;
 		put_write_access(inode);
-	}
+	} else
+		if (flag & FMODE_WRITE)
+			if (inode->i_sb->dq_op)
+				inode->i_sb->dq_op->initialize(inode, -1);
 	*res_inode = inode;
 	return 0;
 }
@@ -446,7 +453,7 @@
 	struct inode * dir;
 
 	mode &= ~current->fs->umask;
-	error = dir_namei(filename,&namelen,&basename, NULL, &dir);
+	error = dir_namei(filename, &namelen, &basename, NULL, &dir);
 	if (error)
 		return error;
 	if (!namelen) {
@@ -466,6 +473,8 @@
 		return -EPERM;
 	}
 	dir->i_count++;
+	if (dir->i_sb->dq_op)
+		dir->i_sb->dq_op->initialize(dir, -1);
 	down(&dir->i_sem);
 	error = dir->i_op->mknod(dir,basename,namelen,mode,dev);
 	up(&dir->i_sem);
@@ -503,7 +512,7 @@
 	int namelen, error;
 	struct inode * dir;
 
-	error = dir_namei(pathname,&namelen,&basename,NULL,&dir);
+	error = dir_namei(pathname, &namelen, &basename, NULL, &dir);
 	if (error)
 		return error;
 	if (!namelen) {
@@ -523,6 +532,8 @@
 		return -EPERM;
 	}
 	dir->i_count++;
+	if (dir->i_sb->dq_op)
+		dir->i_sb->dq_op->initialize(dir, -1);
 	down(&dir->i_sem);
 	error = dir->i_op->mkdir(dir, basename, namelen, mode & 0777 & ~current->fs->umask);
 	up(&dir->i_sem);
@@ -549,7 +560,7 @@
 	int namelen, error;
 	struct inode * dir;
 
-	error = dir_namei(name,&namelen,&basename,NULL,&dir);
+	error = dir_namei(name, &namelen, &basename, NULL, &dir);
 	if (error)
 		return error;
 	if (!namelen) {
@@ -575,6 +586,8 @@
 		iput(dir);
 		return -EPERM;
 	}
+	if (dir->i_sb->dq_op)
+		dir->i_sb->dq_op->initialize(dir, -1);
 	return dir->i_op->rmdir(dir,basename,namelen);
 }
 
@@ -597,7 +610,7 @@
 	int namelen, error;
 	struct inode * dir;
 
-	error = dir_namei(name,&namelen,&basename,NULL,&dir);
+	error = dir_namei(name, &namelen, &basename, NULL, &dir);
 	if (error)
 		return error;
 	if (!namelen) {
@@ -623,6 +636,8 @@
 		iput(dir);
 		return -EPERM;
 	}
+	if (dir->i_sb->dq_op)
+		dir->i_sb->dq_op->initialize(dir, -1);
 	return dir->i_op->unlink(dir,basename,namelen);
 }
 
@@ -645,7 +660,7 @@
 	const char * basename;
 	int namelen, error;
 
-	error = dir_namei(newname,&namelen,&basename,NULL,&dir);
+	error = dir_namei(newname, &namelen, &basename, NULL, &dir);
 	if (error)
 		return error;
 	if (!namelen) {
@@ -665,6 +680,8 @@
 		return -EPERM;
 	}
 	dir->i_count++;
+	if (dir->i_sb->dq_op)
+		dir->i_sb->dq_op->initialize(dir, -1);
 	down(&dir->i_sem);
 	error = dir->i_op->symlink(dir,basename,namelen,oldname);
 	up(&dir->i_sem);
@@ -695,7 +712,7 @@
 	const char * basename;
 	int namelen, error;
 
-	error = dir_namei(newname,&namelen,&basename,NULL,&dir);
+	error = dir_namei(newname, &namelen, &basename, NULL, &dir);
 	if (error) {
 		iput(oldinode);
 		return error;
@@ -734,6 +751,8 @@
 		return -EPERM;
 	}
 	dir->i_count++;
+	if (dir->i_sb->dq_op)
+		dir->i_sb->dq_op->initialize(dir, -1);
 	down(&dir->i_sem);
 	error = dir->i_op->link(oldinode, dir, basename, namelen);
 	up(&dir->i_sem);
@@ -766,7 +785,7 @@
 	const char * old_base, * new_base;
 	int old_len, new_len, error;
 
-	error = dir_namei(oldname,&old_len,&old_base,NULL,&old_dir);
+	error = dir_namei(oldname, &old_len, &old_base, NULL, &old_dir);
 	if (error)
 		return error;
 	if ((error = permission(old_dir,MAY_WRITE | MAY_EXEC)) != 0) {
@@ -779,7 +798,7 @@
 		iput(old_dir);
 		return -EPERM;
 	}
-	error = dir_namei(newname,&new_len,&new_base,NULL,&new_dir);
+	error = dir_namei(newname, &new_len, &new_base, NULL, &new_dir);
 	if (error) {
 		iput(old_dir);
 		return error;
@@ -820,6 +839,8 @@
 		return -EPERM;
 	}
 	new_dir->i_count++;
+	if (new_dir->i_sb->dq_op)
+		new_dir->i_sb->dq_op->initialize(new_dir, -1);
 	down(&new_dir->i_sem);
 	error = old_dir->i_op->rename(old_dir, old_base, old_len, 
 		new_dir, new_base, new_len);
diff -u --recursive --new-file v1.3.45/linux/fs/open.c linux/fs/open.c
--- v1.3.45/linux/fs/open.c	Mon Nov 27 12:48:32 1995
+++ linux/fs/open.c	Mon Dec 11 06:56:36 1995
@@ -122,7 +122,7 @@
 		return -EBADF;
 	if (!(inode = file->f_inode))
 		return -ENOENT;
-	if (S_ISDIR(inode->i_mode) || !(file->f_mode & 2))
+	if (S_ISDIR(inode->i_mode) || !(file->f_mode & FMODE_WRITE))
 		return -EACCES;
 	if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
 		return -EPERM;
@@ -481,7 +481,7 @@
 	error = open_namei(filename,flag,mode,&inode,NULL);
 	if (error)
 		goto cleanup_file;
-	if (f->f_mode & 2) {
+	if (f->f_mode & FMODE_WRITE) {
 		error = get_write_access(inode);
 		if (error)
 			goto cleanup_inode;
@@ -516,7 +516,7 @@
 	if (f->f_op && f->f_op->release)
 		f->f_op->release(inode,f);
 cleanup_all:
-	if (f->f_mode & 2)
+	if (f->f_mode & FMODE_WRITE)
 		put_write_access(inode);
 cleanup_inode:
 	iput(inode);
@@ -562,7 +562,8 @@
 		filp->f_op->release(inode,filp);
 	filp->f_count--;
 	filp->f_inode = NULL;
-	if (filp->f_mode & 2) put_write_access(inode);
+	if (filp->f_mode & FMODE_WRITE)
+		put_write_access(inode);
 	iput(inode);
 	return 0;
 }
diff -u --recursive --new-file v1.3.45/linux/fs/proc/array.c linux/fs/proc/array.c
--- v1.3.45/linux/fs/proc/array.c	Tue Nov 21 13:22:12 1995
+++ linux/fs/proc/array.c	Mon Dec 11 09:55:42 1995
@@ -41,6 +41,9 @@
 #include <linux/ioport.h>
 #include <linux/config.h>
 #include <linux/mm.h>
+#ifdef CONFIG_APM
+#include <linux/apm_bios.h>
+#endif
 
 #include <asm/segment.h>
 #include <asm/pgtable.h>
@@ -457,7 +460,7 @@
 	if (tsk->state < 0 || tsk->state > 5)
 		state = '.';
 	else
-		state = "RSDZTD"[tsk->state];
+		state = "RSDZTW"[tsk->state];
 	vsize = eip = esp = 0;
 	if (tsk->mm) {
 		struct vm_area_struct *vma = tsk->mm->mmap;
@@ -820,6 +823,10 @@
 
 		case PROC_IOPORTS:
 			return get_ioport_list(page);
+#ifdef CONFIG_APM
+		case PROC_APM:
+			return apm_proc(page);
+#endif
 	}
 	return -EBADF;
 }
diff -u --recursive --new-file v1.3.45/linux/fs/proc/inode.c linux/fs/proc/inode.c
--- v1.3.45/linux/fs/proc/inode.c	Wed Nov  8 07:11:37 1995
+++ linux/fs/proc/inode.c	Mon Dec 11 06:56:36 1995
@@ -66,7 +66,7 @@
 			if (*value)
 				return 0;
 		}
-		else return 0;
+		else return 1;
 	}
 	return 1;
 }
diff -u --recursive --new-file v1.3.45/linux/fs/proc/root.c linux/fs/proc/root.c
--- v1.3.45/linux/fs/proc/root.c	Mon Sep 25 12:26:23 1995
+++ linux/fs/proc/root.c	Mon Dec 11 09:55:42 1995
@@ -13,6 +13,9 @@
 #include <linux/proc_fs.h>
 #include <linux/stat.h>
 #include <linux/config.h>
+#ifdef CONFIG_APM
+#include <linux/apm_bios.h>
+#endif
 
 /*
  * Offset of the first process in the /proc root directory..
@@ -293,7 +296,13 @@
 		PROC_IOPORTS, 7, "ioports",
 		S_IFREG | S_IRUGO, 1, 0, 0,
 	});
-
+#ifdef CONFIG_APM
+	proc_register(&proc_root, &(struct proc_dir_entry) {
+	        PROC_APM, 3, "apm",
+		S_IFREG | S_IRUGO, 1, 0, 0,
+	});
+#endif
+		   
 	if (prof_shift) {
 		proc_register(&proc_root, &(struct proc_dir_entry) {
 			PROC_PROFILE, 7, "profile",
diff -u --recursive --new-file v1.3.45/linux/fs/smbfs/dir.c linux/fs/smbfs/dir.c
--- v1.3.45/linux/fs/smbfs/dir.c	Sat Nov 11 17:41:34 1995
+++ linux/fs/smbfs/dir.c	Mon Dec 11 06:56:36 1995
@@ -874,7 +874,6 @@
                 if ((error = smb_proc_unlink(SMB_SERVER(dir), path, len)) == 0)
                         smb_invalid_dir_cache(dir->i_ino);
         }
-
 	iput(dir);
 	return error;
 }
diff -u --recursive --new-file v1.3.45/linux/fs/smbfs/proc.c linux/fs/smbfs/proc.c
--- v1.3.45/linux/fs/smbfs/proc.c	Sat Nov 11 17:41:34 1995
+++ linux/fs/smbfs/proc.c	Mon Dec 11 08:49:35 1995
@@ -264,7 +264,7 @@
 {
 	return (SMB_CMD(packet) == command &&
 		SMB_WCT(packet) >= wct &&
-		(bcc == -1 || SMB_BCC(packet) == bcc)) ? 0 : -EIO;
+		(bcc == -1 || SMB_BCC(packet) >= bcc)) ? 0 : -EIO;
 }
 
 static int
@@ -1139,7 +1139,8 @@
 smb_proc_readdir_long(struct smb_server *server, struct inode *dir, int fpos,
                       int cache_size, struct smb_dirent *entry)
 {
-        int max_matches = 512;
+        int max_matches = 64; /* this should actually be based on the 
+				 maxxmit */
   
         /* NT uses 260, OS/2 uses 2. Both accept 1. */
         int info_level = 1;
@@ -1600,6 +1601,10 @@
 #ifdef LANMAN2
           { PROTOCOL_LANMAN2,"LM1.2X002"},
 #endif
+#ifdef NT1
+	  { PROTOCOL_NT1,"NT LM 0.12"},
+	  { PROTOCOL_NT1,"NT LANMAN 1.0"},
+#endif
           {-1, NULL} };
 	char dev[] = "A:";
 	int i, plength;
@@ -1712,29 +1717,70 @@
                 DPRINTK("smb_proc_connect: blkmode = %d\n",
                         WVAL(server->packet, smb_vwv5));
 
-                server->maxxmt = WVAL(server->packet, smb_vwv2);
-                server->maxmux = WVAL(server->packet, smb_vwv3);
-                server->maxvcs = WVAL(server->packet, smb_vwv4);
-                server->blkmode= WVAL(server->packet, smb_vwv5);
-                server->sesskey= DVAL(server->packet, smb_vwv6);
-
-                smb_setup_header(server, SMBsesssetupX, 10,
-                                 2 + userlen + passlen);
-
-                WSET(server->packet, smb_vwv0, 0x00ff);
-                WSET(server->packet, smb_vwv1, 0);
-                WSET(server->packet, smb_vwv2, given_max_xmit);
-                WSET(server->packet, smb_vwv3, 2);
-                WSET(server->packet, smb_vwv4, server->pid);
-                DSET(server->packet, smb_vwv5, server->sesskey);
-                WSET(server->packet, smb_vwv7, passlen + 1);
-                WSET(server->packet, smb_vwv8, 0);
-                WSET(server->packet, smb_vwv9, 0);
-
-                p = SMB_BUF(server->packet);
-                strcpy(p, server->m.password);
-                p += passlen + 1;
-                strcpy(p, server->m.username);
+		if (server->protocol >= PROTOCOL_NT1) {
+			server->maxxmt = DVAL(server->packet,smb_vwv3+1);
+			server->maxmux = WVAL(server->packet, smb_vwv1+1);
+			server->maxvcs = WVAL(server->packet, smb_vwv2+1);
+			server->blkmode= DVAL(server->packet, smb_vwv9+1);
+			server->sesskey= DVAL(server->packet, smb_vwv7+1);
+		} else {
+			server->maxxmt = WVAL(server->packet, smb_vwv2);
+			server->maxmux = WVAL(server->packet, smb_vwv3);
+			server->maxvcs = WVAL(server->packet, smb_vwv4);
+			server->blkmode= WVAL(server->packet, smb_vwv5);
+			server->sesskey= DVAL(server->packet, smb_vwv6);
+		}
+
+
+		if (server->protocol >= PROTOCOL_NT1) {
+			char *workgroup = "WORKGROUP";
+			char *OS_id = "Unix";
+			char *client_id = "ksmbfs";
+
+			smb_setup_header(server, SMBsesssetupX, 13,
+					 5 + userlen + passlen +
+					 strlen(workgroup) + strlen(OS_id) +
+					 strlen(client_id));
+			
+			WSET(server->packet, smb_vwv0, 0x00ff);
+			WSET(server->packet, smb_vwv1, 0);
+			WSET(server->packet, smb_vwv2, given_max_xmit);
+			WSET(server->packet, smb_vwv3, 2);
+			WSET(server->packet, smb_vwv4, server->pid);
+			DSET(server->packet, smb_vwv5, server->sesskey);
+			WSET(server->packet, smb_vwv7, passlen + 1);
+			WSET(server->packet, smb_vwv8, 0);
+			WSET(server->packet, smb_vwv9, 0);
+
+			p = SMB_BUF(server->packet);
+			strcpy(p, server->m.password);
+			p += passlen + 1;
+			strcpy(p, server->m.username);
+			p += userlen + 1;
+			strcpy(p, workgroup);
+			p += strlen(p) + 1;
+			strcpy(p, OS_id);
+			p += strlen(p) + 1;
+			strcpy(p, client_id);
+		} else {
+			smb_setup_header(server, SMBsesssetupX, 10,
+					 2 + userlen + passlen);
+
+			WSET(server->packet, smb_vwv0, 0x00ff);
+			WSET(server->packet, smb_vwv1, 0);
+			WSET(server->packet, smb_vwv2, given_max_xmit);
+			WSET(server->packet, smb_vwv3, 2);
+			WSET(server->packet, smb_vwv4, server->pid);
+			DSET(server->packet, smb_vwv5, server->sesskey);
+			WSET(server->packet, smb_vwv7, passlen + 1);
+			WSET(server->packet, smb_vwv8, 0);
+			WSET(server->packet, smb_vwv9, 0);
+
+			p = SMB_BUF(server->packet);
+			strcpy(p, server->m.password);
+			p += passlen + 1;
+			strcpy(p, server->m.username);
+		}
 
                 if ((result = smb_request_ok(server,SMBsesssetupX,3,0)) < 0) {
                         DPRINTK("smb_proc_connect: SMBsessetupX failed\n");
diff -u --recursive --new-file v1.3.45/linux/fs/super.c linux/fs/super.c
--- v1.3.45/linux/fs/super.c	Fri Nov 17 08:42:28 1995
+++ linux/fs/super.c	Mon Dec 11 07:46:21 1995
@@ -2,18 +2,22 @@
  *  linux/fs/super.c
  *
  *  Copyright (C) 1991, 1992  Linus Torvalds
- */
-
-/*
- * super.c contains code to handle the super-block tables.
+ *
+ *  super.c contains code to handle: - mount structures
+ *                                   - super-block tables.
+ *                                   - mount systemcall
+ *                                   - umount systemcall
  *
  * GK 2/5/95  -  Changed to support mounting the root fs via NFS
  */
+
 #include <stdarg.h>
 
 #include <linux/config.h>
 #include <linux/sched.h>
 #include <linux/kernel.h>
+#include <linux/mount.h>
+#include <linux/malloc.h>
 #include <linux/major.h>
 #include <linux/stat.h>
 #include <linux/errno.h>
@@ -32,8 +36,6 @@
 
 extern int root_mountflags;
 
-struct super_block super_blocks[NR_SUPER];
-
 static int do_remount_sb(struct super_block *sb, int flags, char * data);
 
 #ifdef CONFIG_ROOT_NFS
@@ -43,24 +45,118 @@
 /* this is initialized in init/main.c */
 kdev_t ROOT_DEV;
 
-static struct file_system_type * file_systems = NULL;
+struct super_block super_blocks[NR_SUPER];
+static struct file_system_type *file_systems = (struct file_system_type *) NULL;
+static struct vfsmount *vfsmntlist = (struct vfsmount *) NULL,
+                       *vfsmnttail = (struct vfsmount *) NULL,
+                       *mru_vfsmnt = (struct vfsmount *) NULL;
 
-int register_filesystem(struct file_system_type * fs)
+/* 
+ * This part handles the management of the list of mounted filesystems.
+ */
+struct vfsmount *lookup_vfsmnt(kdev_t dev)
 {
-	struct file_system_type ** tmp;
+	struct vfsmount *lptr;
 
-	if (!fs)
-		return -EINVAL;
-	if (fs->next)
-		return -EBUSY;
-	tmp = &file_systems;
-	while (*tmp) {
-		if (strcmp((*tmp)->name, fs->name) == 0)
-			return -EBUSY;
-		tmp = &(*tmp)->next;
+	if (vfsmntlist == (struct vfsmount *)NULL)
+		return ((struct vfsmount *)NULL);
+
+	if (mru_vfsmnt != (struct vfsmount *)NULL &&
+	    mru_vfsmnt->mnt_dev == dev)
+		return (mru_vfsmnt);
+
+	for (lptr = vfsmntlist;
+	     lptr != (struct vfsmount *)NULL;
+	     lptr = lptr->mnt_next)
+		if (lptr->mnt_dev == dev) {
+			mru_vfsmnt = lptr;
+			return (lptr);
+		}
+
+	return ((struct vfsmount *)NULL);
+	/* NOTREACHED */
+}
+
+struct vfsmount *add_vfsmnt(kdev_t dev, const char *dev_name, const char *dir_name)
+{
+	struct vfsmount *lptr;
+	char *tmp;
+
+	if ((lptr = (struct vfsmount *)
+	     kmalloc(sizeof(struct vfsmount), GFP_KERNEL)) == (struct vfsmount *)NULL)
+		return ((struct vfsmount *)NULL);
+	memset(lptr, 0, sizeof(struct vfsmount));
+
+	lptr->mnt_dev = dev;
+	lptr->mnt_sem.count = 1;
+	if (dev_name && !getname(dev_name, &tmp)) {
+		if ((lptr->mnt_devname =
+		    (char *) kmalloc(strlen(tmp), GFP_KERNEL)) != (char *)NULL)
+			strcpy(lptr->mnt_devname, tmp);
+		putname(tmp);
 	}
-	*tmp = fs;
-	return 0;
+	if (dir_name && !getname(dir_name, &tmp)) {
+		if ((lptr->mnt_dirname =
+		    (char *) kmalloc(strlen(tmp), GFP_KERNEL)) != (char *)NULL)
+			strcpy(lptr->mnt_dirname, tmp);
+		putname(tmp);
+	}
+
+	if (vfsmntlist == (struct vfsmount *)NULL) {
+		vfsmntlist = vfsmnttail = lptr;
+	} else {
+		vfsmnttail->mnt_next = lptr;
+		vfsmnttail = lptr;
+	}
+	return (lptr);
+}
+
+void remove_vfsmnt(kdev_t dev)
+{
+	struct vfsmount *lptr, *tofree;
+
+	if (vfsmntlist == (struct vfsmount *)NULL)
+		return;
+	lptr = vfsmntlist;
+	if (lptr->mnt_dev == dev) {
+		tofree = lptr;
+		vfsmntlist = lptr->mnt_next;
+		if (vfsmnttail->mnt_dev == dev)
+			vfsmnttail = vfsmntlist;
+	} else {
+		while (lptr->mnt_next != (struct vfsmount *)NULL) {
+			if (lptr->mnt_next->mnt_dev == dev)
+				break;
+			lptr = lptr->mnt_next;
+		}
+		tofree = lptr->mnt_next;
+		if (tofree == (struct vfsmount *)NULL)
+			return;
+		lptr->mnt_next = lptr->mnt_next->mnt_next;
+		if (vfsmnttail->mnt_dev == dev)
+			vfsmnttail = lptr;
+	}
+	kfree(tofree->mnt_devname);
+	kfree(tofree->mnt_dirname);
+	kfree_s(tofree, sizeof(struct vfsmount));
+}
+
+int register_filesystem(struct file_system_type * fs)
+{
+        struct file_system_type ** tmp;
+
+        if (!fs)
+                return -EINVAL;
+        if (fs->next)
+                return -EBUSY;
+        tmp = &file_systems;
+        while (*tmp) {
+                if (strcmp((*tmp)->name, fs->name) == 0)
+                        return -EBUSY;
+                tmp = &(*tmp)->next;
+        }
+        *tmp = fs;
+        return 0;
 }
 
 int unregister_filesystem(struct file_system_type * fs)
@@ -364,11 +460,20 @@
 	int retval;
 	
 	if (dev==ROOT_DEV) {
-		/* Special case for "unmounting" root.  We just try to remount
-		   it readonly, and sync() the device. */
+		/*
+		 * Special case for "unmounting" root. We just try to remount
+		 * it readonly, and sync() the device.
+		 */
 		if (!(sb=get_super(dev)))
 			return -ENOENT;
 		if (!(sb->s_flags & MS_RDONLY)) {
+			/*
+			 * Make sure all quotas are turned off on this device we need to mount
+			 * it readonly so no more writes by the quotasystem.
+			 * If later on the remount fails to bad there are no quotas running
+			 * anymore. Turn them on again by hand.
+			 */
+			quota_off(dev, -1);
 			fsync_dev(dev);
 			retval = do_remount_sb(sb, MS_RDONLY, 0);
 			if (retval)
@@ -381,6 +486,12 @@
 	if (!sb->s_covered->i_mount)
 		printk("VFS: umount(%s): mounted inode has i_mount=NULL\n",
 		       kdevname(dev));
+	/*
+	 * Before checking if the filesystem is still busy make sure the kernel
+	 * doesn't hold any quotafiles open on that device. If the umount fails
+	 * to bad there are no quotas running anymore. Turn them on again by hand.
+	 */
+	quota_off(dev, -1);
 	if (!fs_may_umount(dev, sb->s_mounted))
 		return -EBUSY;
 	sb->s_covered->i_mount = NULL;
@@ -391,6 +502,7 @@
 	if (sb->s_op && sb->s_op->write_super && sb->s_dirt)
 		sb->s_op->write_super(sb);
 	put_super(dev);
+	remove_vfsmnt(dev);
 	return 0;
 }
 
@@ -415,9 +527,9 @@
 
 	if (!suser())
 		return -EPERM;
-	retval = namei(name,&inode);
+	retval = namei(name, &inode);
 	if (retval) {
-		retval = lnamei(name,&inode);
+		retval = lnamei(name, &inode);
 		if (retval)
 			return retval;
 	}
@@ -466,13 +578,15 @@
  * We also have to flush all inode-data for this device, as the new mount
  * might need new info.
  */
-int do_mount(kdev_t dev, const char * dir, const char * type, int flags, void * data)
+
+int do_mount(kdev_t dev, const char * dev_name, const char * dir_name, const char * type, int flags, void * data)
 {
 	struct inode * dir_i;
 	struct super_block * sb;
+	struct vfsmount *vfsmnt;
 	int error;
 
-	error = namei(dir,&dir_i);
+	error = namei(dir_name, &dir_i);
 	if (error)
 		return error;
 	if (dir_i->i_count != 1 || dir_i->i_mount) {
@@ -496,6 +610,8 @@
 		iput(dir_i);
 		return -EBUSY;
 	}
+	vfsmnt = add_vfsmnt(dev, dev_name, dir_name);
+	vfsmnt->mnt_sb = sb;
 	sb->s_covered = dir_i;
 	dir_i->i_mount = sb->s_mounted;
 	return 0;		/* we don't iput(dir_i) - see umount */
@@ -534,7 +650,7 @@
 	struct inode *dir_i;
 	int retval;
 
-	retval = namei(dir,&dir_i);
+	retval = namei(dir, &dir_i);
 	if (retval)
 		return retval;
 	if (dir_i != dir_i->i_sb->s_mounted) {
@@ -619,7 +735,7 @@
 	t = fstype->name;
 	fops = NULL;
 	if (fstype->requires_dev) {
-		retval = namei(dev_name,&inode);
+		retval = namei(dev_name, &inode);
 		if (retval)
 			return retval;
 		if (!S_ISBLK(inode->i_mode)) {
@@ -666,7 +782,7 @@
 			return retval;
 		}
 	}
-	retval = do_mount(dev,dir_name,t,flags,(void *) page);
+	retval = do_mount(dev,dev_name,dir_name,t,flags,(void *) page);
 	free_page(page);
 	if (retval && fops && fops->release)
 		fops->release(inode, NULL);
@@ -678,10 +794,11 @@
 {
 	struct file_system_type * fs_type;
 	struct super_block * sb;
+	struct vfsmount *vfsmnt;
 	struct inode * inode, d_inode;
 	struct file filp;
 	int retval;
-
+  
 	memset(super_blocks, 0, sizeof(super_blocks));
 #ifdef CONFIG_ROOT_NFS
 	if (MAJOR(ROOT_DEV) == UNNAMED_MAJOR) {
@@ -737,9 +854,9 @@
 	for (fs_type = file_systems ; fs_type ; fs_type = fs_type->next) {
 		if(retval)
 			break;
-		if (!fs_type->requires_dev)
-			continue;
-		sb = read_super(ROOT_DEV,fs_type->name,root_mountflags,NULL,1);
+  		if (!fs_type->requires_dev)
+  			continue;
+  		sb = read_super(ROOT_DEV,fs_type->name,root_mountflags,NULL,1);
 		if (sb) {
 			inode = sb->s_mounted;
 			inode->i_count += 3 ;	/* NOTE! it is logically used 4 times, not 1 */
@@ -750,6 +867,8 @@
 			printk ("VFS: Mounted root (%s filesystem)%s.\n",
 				fs_type->name,
 				(sb->s_flags & MS_RDONLY) ? " readonly" : "");
+			vfsmnt = add_vfsmnt(ROOT_DEV, "rootfs", "/");
+			vfsmnt->mnt_sb = sb;
 			return;
 		}
 	}
diff -u --recursive --new-file v1.3.45/linux/include/asm-alpha/mman.h linux/include/asm-alpha/mman.h
--- v1.3.45/linux/include/asm-alpha/mman.h	Tue Nov 21 13:22:12 1995
+++ linux/include/asm-alpha/mman.h	Mon Dec 11 09:07:22 1995
@@ -30,4 +30,8 @@
 #define MCL_CURRENT	 8192		/* lock all currently mapped pages */
 #define MCL_FUTURE	16384		/* lock all additions to address space */
 
+/* compatibility flags */
+#define MAP_ANON	MAP_ANONYMOUS
+#define MAP_FILE	0
+
 #endif /* __ALPHA_MMAN_H__ */
diff -u --recursive --new-file v1.3.45/linux/include/asm-alpha/page.h linux/include/asm-alpha/page.h
--- v1.3.45/linux/include/asm-alpha/page.h	Tue Nov 21 13:22:12 1995
+++ linux/include/asm-alpha/page.h	Fri Dec  1 07:19:29 1995
@@ -48,24 +48,6 @@
 
 #endif
 
-#define invalidate_all() \
-__asm__ __volatile__( \
-	"lda $16,-2($31)\n\t" \
-	".long 51" \
-	: : :"$1", "$16", "$17", "$22","$23","$24","$25")
-
-#define invalidate() \
-__asm__ __volatile__( \
-	"lda $16,-1($31)\n\t" \
-	".long 51" \
-	: : :"$1", "$16", "$17", "$22","$23","$24","$25")
-
-/* Certain architectures need to do special things when pte's
- * within a page table are directly modified.  Thus, the following
- * hook is made available.
- */
-#define set_pte(pteptr, pteval) ((*(pteptr)) = (pteval))
-
 /* to align the pointer to the (next) page boundary */
 #define PAGE_ALIGN(addr)		(((addr)+PAGE_SIZE-1)&PAGE_MASK)
 
diff -u --recursive --new-file v1.3.45/linux/include/asm-alpha/pgtable.h linux/include/asm-alpha/pgtable.h
--- v1.3.45/linux/include/asm-alpha/pgtable.h	Mon Sep 25 12:26:23 1995
+++ linux/include/asm-alpha/pgtable.h	Fri Dec  1 07:19:29 1995
@@ -9,6 +9,24 @@
  * in <asm/page.h> (currently 8192).
  */
 
+#define invalidate_all() \
+__asm__ __volatile__( \
+	"lda $16,-2($31)\n\t" \
+	".long 51" \
+	: : :"$1", "$16", "$17", "$22","$23","$24","$25")
+
+#define invalidate() \
+__asm__ __volatile__( \
+	"lda $16,-1($31)\n\t" \
+	".long 51" \
+	: : :"$1", "$16", "$17", "$22","$23","$24","$25")
+
+/* Certain architectures need to do special things when pte's
+ * within a page table are directly modified.  Thus, the following
+ * hook is made available.
+ */
+#define set_pte(pteptr, pteval) ((*(pteptr)) = (pteval))
+
 /* PMD_SHIFT determines the size of the area a second-level page table can map */
 #define PMD_SHIFT	(PAGE_SHIFT + (PAGE_SHIFT-3))
 #define PMD_SIZE	(1UL << PMD_SHIFT)
diff -u --recursive --new-file v1.3.45/linux/include/asm-alpha/socket.h linux/include/asm-alpha/socket.h
--- v1.3.45/linux/include/asm-alpha/socket.h	Tue Jul 11 10:02:59 1995
+++ linux/include/asm-alpha/socket.h	Tue Nov 28 08:32:43 1995
@@ -40,5 +40,6 @@
 /* linux-specific, might as well be the same as on i386 */
 #define SO_NO_CHECK	11
 #define SO_PRIORITY	12
+#define SO_BSDCOMPAT	14
 
 #endif /* _ASM_SOCKET_H */
diff -u --recursive --new-file v1.3.45/linux/include/asm-alpha/unistd.h linux/include/asm-alpha/unistd.h
--- v1.3.45/linux/include/asm-alpha/unistd.h	Sun Oct 29 11:38:49 1995
+++ linux/include/asm-alpha/unistd.h	Mon Dec 11 06:56:36 1995
@@ -100,6 +100,7 @@
 #define __NR_getrlimit		144
 #define __NR_setrlimit		145
 #define __NR_setsid		147
+#define __NR_quotactl		148
 #define __NR_getsockname	150
 #define __NR_sigaction		156
 #define __NR_msgctl		200
diff -u --recursive --new-file v1.3.45/linux/include/asm-i386/bitops.h linux/include/asm-i386/bitops.h
--- v1.3.45/linux/include/asm-i386/bitops.h	Mon Nov 27 12:48:32 1995
+++ linux/include/asm-i386/bitops.h	Tue Nov 28 07:41:45 1995
@@ -64,7 +64,7 @@
  */
 extern __inline__ int test_bit(int nr, const void * addr)
 {
-	return 1UL & (((unsigned int *) addr)[nr >> 5] >> (nr & 31));
+	return 1UL & (((const unsigned int *) addr)[nr >> 5] >> (nr & 31));
 }
 
 /*
diff -u --recursive --new-file v1.3.45/linux/include/asm-i386/mman.h linux/include/asm-i386/mman.h
--- v1.3.45/linux/include/asm-i386/mman.h	Tue Nov 21 13:22:13 1995
+++ linux/include/asm-i386/mman.h	Mon Dec 11 09:06:30 1995
@@ -24,4 +24,8 @@
 #define MCL_CURRENT	1		/* lock all current mappings */
 #define MCL_FUTURE	2		/* lock all future mappings */
 
+/* compatibility flags */
+#define MAP_ANON	MAP_ANONYMOUS
+#define MAP_FILE	0
+
 #endif /* __I386_MMAN_H__ */
diff -u --recursive --new-file v1.3.45/linux/include/asm-i386/page.h linux/include/asm-i386/page.h
--- v1.3.45/linux/include/asm-i386/page.h	Tue Nov 21 13:22:13 1995
+++ linux/include/asm-i386/page.h	Thu Nov 30 14:12:41 1995
@@ -50,41 +50,6 @@
 
 #endif
 
-/*
- * TLB invalidation:
- *
- *  - invalidate() invalidates the current task TLBs
- *  - invalidate_all() invalidates all processes TLBs
- *  - invalidate_task(task) invalidates the specified tasks TLB's
- *  - invalidate_page(task, vmaddr) invalidates one page
- *
- * ..but the i386 has somewhat limited invalidation capabilities.
- */
- 
-#ifndef __SMP__
-#define invalidate() \
-__asm__ __volatile__("movl %%cr3,%%eax\n\tmovl %%eax,%%cr3": : :"ax")
-
-#define invalidate_all() invalidate()
-#define invalidate_task(task) \
-do { if ((task)->mm == current->mm) invalidate(); } while (0)
-#define invalidate_page(task,addr) \
-do { if ((task)->mm == current->mm) invalidate(); } while (0)
-
-#else
-#include <asm/smp.h>
-#define local_invalidate() \
-__asm__ __volatile__("movl %%cr3,%%eax\n\tmovl %%eax,%%cr3": : :"ax")
-#define invalidate() \
-	smp_invalidate();
-#endif
-
-/* Certain architectures need to do special things when pte's
- * within a page table are directly modified.  Thus, the following
- * hook is made available.
- */
-#define set_pte(pteptr, pteval) ((*(pteptr)) = (pteval))
-
 /* to align the pointer to the (next) page boundary */
 #define PAGE_ALIGN(addr)	(((addr)+PAGE_SIZE-1)&PAGE_MASK)
 
diff -u --recursive --new-file v1.3.45/linux/include/asm-i386/pgtable.h linux/include/asm-i386/pgtable.h
--- v1.3.45/linux/include/asm-i386/pgtable.h	Wed Nov  8 07:11:41 1995
+++ linux/include/asm-i386/pgtable.h	Thu Nov 30 14:12:41 1995
@@ -17,6 +17,48 @@
  * the i386 page table tree.
  */
 
+/*
+ * TLB invalidation:
+ *
+ *  - invalidate() invalidates the current mm struct TLBs
+ *  - invalidate_all() invalidates all processes TLBs
+ *  - invalidate_mm(mm) invalidates the specified mm context TLB's
+ *  - invalidate_page(mm, vmaddr) invalidates one page
+ *  - invalidate_range(mm, start, end) invalidates a range of pages
+ *
+ * ..but the i386 has somewhat limited invalidation capabilities.
+ */
+ 
+#ifndef __SMP__
+#define invalidate() \
+__asm__ __volatile__("movl %%cr3,%%eax\n\tmovl %%eax,%%cr3": : :"ax")
+#else
+#include <asm/smp.h>
+#define local_invalidate() \
+__asm__ __volatile__("movl %%cr3,%%eax\n\tmovl %%eax,%%cr3": : :"ax")
+#define invalidate() \
+	smp_invalidate();
+#endif
+
+/*
+ * We aren't very clever about this yet. On a 486+ we could actually do
+ * page-granularity invalidates for better performance in some cases.
+ * And SMP could certainly avoid some global invalidates..
+ */
+#define invalidate_all() invalidate()
+#define invalidate_mm(mm_struct) \
+do { if ((mm_struct) == current->mm) invalidate(); } while (0)
+#define invalidate_page(mm_struct,addr) \
+do { if ((mm_struct) == current->mm) invalidate(); } while (0)
+#define invalidate_range(mm_struct,start,end) \
+do { if ((mm_struct) == current->mm) invalidate(); } while (0)
+
+/* Certain architectures need to do special things when pte's
+ * within a page table are directly modified.  Thus, the following
+ * hook is made available.
+ */
+#define set_pte(pteptr, pteval) ((*(pteptr)) = (pteval))
+
 /* PMD_SHIFT determines the size of the area a second-level page table can map */
 #define PMD_SHIFT	22
 #define PMD_SIZE	(1UL << PMD_SHIFT)
diff -u --recursive --new-file v1.3.45/linux/include/linux/apm_bios.h linux/include/linux/apm_bios.h
--- v1.3.45/linux/include/linux/apm_bios.h	Thu Jan  1 02:00:00 1970
+++ linux/include/linux/apm_bios.h	Mon Dec 11 09:55:42 1995
@@ -0,0 +1,140 @@
+#ifndef _LINUX_APM_H
+#define _LINUX_APM_H
+
+/*
+ * Include file for the interface to an APM BIOS
+ * Copyright 1994, 1995 Stephen Rothwell (Stephen.Rothwell@pd.necisa.oz.au)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * $Id: apm_bios.h,v 0.9 1995/03/09 13:50:05 sfr Exp $
+ */
+
+typedef unsigned short	apm_event_t;
+
+#ifdef __KERNEL__
+
+#include <linux/tasks.h>	/* for NR_TASKS */
+#include <linux/sched.h>	/* for _TSS */
+
+#define APM_CS		_TSS(NR_TASKS)
+#define APM_CS_16	(APM_CS + 8)
+#define APM_DS		(APM_CS_16 + 8)
+
+struct apm_bios_info {
+	unsigned short	version;
+	unsigned short	cseg;
+	unsigned long	offset;
+	unsigned short	cseg_16;
+	unsigned short	dseg;
+	unsigned short	flags;
+	unsigned short	cseg_len;
+	unsigned short	dseg_len;
+};
+
+				/* Results of APM Installation Check */
+#define APM_16_BIT_SUPPORT	0x0001
+#define APM_32_BIT_SUPPORT	0x0002
+#define APM_IDLE_SLOWS_CLOCK	0x0004
+#define APM_BIOS_DISABLED      	0x0008
+#define APM_BIOS_DISENGAGED     0x0010
+
+/*
+ * Maximum number of events stored
+ */
+#define APM_MAX_EVENTS		20
+
+/*
+ * The per-file APM data
+ */
+struct apm_bios_struct {
+	int		magic;
+	struct apm_bios_struct *	next;
+	int		suser;
+	int		suspends_pending;
+	int		standbys_pending;
+	int		event_head;
+	int		event_tail;
+	apm_event_t	events[APM_MAX_EVENTS];
+};
+
+/*
+ * The magic number in apm_bios_struct
+ */
+#define APM_BIOS_MAGIC		0x4101
+
+/*
+ * in init/main.c
+ */
+extern struct apm_bios_info	apm_bios_info;
+
+extern void		apm_bios_init(void);
+
+extern int		apm_register_callback(int (*callback)(apm_event_t));
+extern void		apm_unregister_callback(int (*callback)(apm_event_t));
+
+extern int		apm_proc(char *);
+
+extern int		apm_display_blank(void);
+extern int		apm_display_unblank(void);
+
+#endif	/* __KERNEL__ */
+
+/*
+ * Power states
+ */
+#define APM_STATE_READY		0x0000
+#define APM_STATE_STANDBY	0x0001
+#define APM_STATE_SUSPEND	0x0002
+#define APM_STATE_OFF		0x0003
+#define APM_STATE_BUSY		0x0004
+#define APM_STATE_REJECT	0x0005
+
+/*
+ * Events (results of Get PM Event)
+ */
+#define APM_SYS_STANDBY		0x0001
+#define APM_SYS_SUSPEND		0x0002
+#define APM_NORMAL_RESUME	0x0003
+#define APM_CRITICAL_RESUME	0x0004
+#define APM_LOW_BATTERY		0x0005
+#define APM_POWER_STATUS_CHANGE	0x0006
+#define APM_UPDATE_TIME		0x0007
+#define APM_CRITICAL_SUSPEND	0x0008
+#define APM_USER_STANDBY	0x0009
+#define APM_USER_SUSPEND	0x000a
+#define APM_STANDBY_RESUME	0x000b
+
+/*
+ * Error codes
+ */
+#define APM_SUCCESS		0x00
+#define APM_DISABLED		0x01
+#define APM_CONNECTED		0x02
+#define APM_NOT_CONNECTED	0x03
+#define APM_16_CONNECTED	0x05
+#define APM_16_UNSUPPORTED	0x06
+#define APM_32_CONNECTED	0x07
+#define APM_32_UNSUPPORTED	0x08
+#define APM_BAD_DEVICE		0x09
+#define APM_BAD_PARAM		0x0a
+#define APM_NOT_ENGAGED		0x0b
+#define APM_BAD_STATE		0x60
+#define APM_NO_EVENTS		0x80
+#define APM_NOT_PRESENT		0x86
+
+/* ioctl operations */
+#include <linux/ioctl.h>
+
+#define APM_IOC_STANDBY		_IO('A', 1)
+#define APM_IOC_SUSPEND		_IO('A', 2)
+
+#endif	/* LINUX_APM_H */
diff -u --recursive --new-file v1.3.45/linux/include/linux/ax25.h linux/include/linux/ax25.h
--- v1.3.45/linux/include/linux/ax25.h	Wed Sep 27 16:00:00 1995
+++ linux/include/linux/ax25.h	Thu Nov 30 11:15:53 1995
@@ -32,6 +32,12 @@
 	ax25_address digi_addr[AX25_MAX_DIGIS];
 };
 
+struct ax25_bpqaddr_struct
+{
+	char dev[16];
+	ax25_address addr;
+};
+
 #define AX25_WINDOW	1
 #define AX25_T1		2
 #define AX25_N2		3
@@ -45,6 +51,7 @@
 #define SIOCAX25ADDUID		(SIOCPROTOPRIVATE+1)
 #define SIOCAX25DELUID		(SIOCPROTOPRIVATE+2)
 #define SIOCAX25NOUID		(SIOCPROTOPRIVATE+3)
+#define	SIOCAX25BPQADDR		(SIOCPROTOPRIVATE+4)
 #define	SIOCAX25GETPARMS	(SIOCPROTOPRIVATE+5)
 #define	SIOCAX25SETPARMS	(SIOCPROTOPRIVATE+6)
 
diff -u --recursive --new-file v1.3.45/linux/include/linux/blk.h linux/include/linux/blk.h
--- v1.3.45/linux/include/linux/blk.h	Mon Nov 27 12:48:36 1995
+++ linux/include/linux/blk.h	Mon Dec 11 11:27:54 1995
@@ -336,7 +336,7 @@
 
 #if ! SCSI_MAJOR(MAJOR_NR)
 
-#if defined(_IDE_CD_C) || defined(_TRITON_C) /* shares copy with ide.c */
+#if defined(_IDE_CD_C) || defined(_TRITON_C) || defined(_IDE_TAPE_C) /* shares copy with ide.c */
 void ide_end_request(byte uptodate, ide_hwgroup_t *hwgroup);
 #else
 
@@ -375,6 +375,7 @@
 		}
 	}
 #ifdef IDE_DRIVER
+	blk_dev[MAJOR(req->rq_dev)].current_request = req->next;
 	hwgroup->rq = NULL;
 #else
 	DEVICE_OFF(req->rq_dev);
diff -u --recursive --new-file v1.3.45/linux/include/linux/ext2_fs.h linux/include/linux/ext2_fs.h
--- v1.3.45/linux/include/linux/ext2_fs.h	Sun Aug 13 14:45:35 1995
+++ linux/include/linux/ext2_fs.h	Mon Dec 11 06:56:36 1995
@@ -404,9 +404,9 @@
 extern int ext2_permission (struct inode *, int);
 
 /* balloc.c */
-extern int ext2_new_block (struct super_block *, unsigned long,
-			   __u32 *, __u32 *);
-extern void ext2_free_blocks (struct super_block *, unsigned long,
+extern int ext2_new_block (const struct inode *, unsigned long,
+			   __u32 *, __u32 *, int *);
+extern void ext2_free_blocks (const struct inode *, unsigned long,
 			      unsigned long);
 extern unsigned long ext2_count_free_blocks (struct super_block *);
 extern void ext2_check_blocks_bitmap (struct super_block *);
@@ -427,7 +427,7 @@
 extern int ext2_sync_file (struct inode *, struct file *);
 
 /* ialloc.c */
-extern struct inode * ext2_new_inode (const struct inode *, int);
+extern struct inode * ext2_new_inode (const struct inode *, int, int *);
 extern void ext2_free_inode (struct inode *);
 extern unsigned long ext2_count_free_inodes (struct super_block *);
 extern void ext2_check_inodes_bitmap (struct super_block *);
diff -u --recursive --new-file v1.3.45/linux/include/linux/ext_fs.h linux/include/linux/ext_fs.h
--- v1.3.45/linux/include/linux/ext_fs.h	Mon Jun 12 16:49:41 1995
+++ linux/include/linux/ext_fs.h	Mon Dec 11 06:56:36 1995
@@ -63,7 +63,7 @@
 extern int ext_open(struct inode * inode, struct file * filp);
 extern void ext_release(struct inode * inode, struct file * filp);
 extern int ext_lookup(struct inode * dir,const char * name, int len,
-	struct inode ** result);
+                      struct inode ** result);
 extern int ext_create(struct inode * dir,const char * name, int len, int mode,
 	struct inode ** result);
 extern int ext_mkdir(struct inode * dir, const char * name, int len, int mode);
diff -u --recursive --new-file v1.3.45/linux/include/linux/fs.h linux/include/linux/fs.h
--- v1.3.45/linux/include/linux/fs.h	Mon Nov 27 12:48:36 1995
+++ linux/include/linux/fs.h	Mon Dec 11 06:56:37 1995
@@ -39,6 +39,9 @@
 #define MAY_WRITE 2
 #define MAY_READ 4
 
+#define FMODE_READ 1
+#define FMODE_WRITE 2
+
 #define READ 0
 #define WRITE 1
 #define READA 2		/* read-ahead - don't pause */
@@ -56,15 +59,15 @@
 /*
  * These are the fs-independent mount-flags: up to 16 flags are supported
  */
-#define MS_RDONLY	 1 /* mount read-only */
-#define MS_NOSUID	 2 /* ignore suid and sgid bits */
-#define MS_NODEV	 4 /* disallow access to device special files */
-#define MS_NOEXEC	 8 /* disallow program execution */
-#define MS_SYNCHRONOUS	16 /* writes are synced at once */
-#define MS_REMOUNT	32 /* alter flags of a mounted FS */
-
-#define S_APPEND    256 /* append-only file */
-#define S_IMMUTABLE 512 /* immutable file */
+#define MS_RDONLY	 1	/* Mount read-only */
+#define MS_NOSUID	 2	/* Ignore suid and sgid bits */
+#define MS_NODEV	 4	/* Disallow access to device special files */
+#define MS_NOEXEC	 8	/* Disallow program execution */
+#define MS_SYNCHRONOUS	16	/* Writes are synced at once */
+#define MS_REMOUNT	32	/* Alter flags of a mounted FS */
+#define S_WRITE		128	/* Write on file/directory/symlink */
+#define S_APPEND	256	/* Append-only file */
+#define S_IMMUTABLE	512	/* Immutable file */
 
 /*
  * Flags that can be altered by MS_REMOUNT
@@ -74,8 +77,8 @@
 /*
  * Magic mount flag number. Has to be or-ed to the flag values.
  */
-#define MS_MGC_VAL 0xC0ED0000 /* magic flag number to indicate "new" flags */
-#define MS_MGC_MSK 0xffff0000 /* magic flag number mask */
+#define MS_MGC_VAL 0xC0ED0000	/* magic flag number to indicate "new" flags */
+#define MS_MGC_MSK 0xffff0000	/* magic flag number mask */
 
 /*
  * Note that read-only etc flags are inode-specific: setting some file-system
@@ -91,6 +94,7 @@
 #define IS_NOEXEC(inode) ((inode)->i_flags & MS_NOEXEC)
 #define IS_SYNC(inode) ((inode)->i_flags & MS_SYNCHRONOUS)
 
+#define IS_WRITABLE(inode) ((inode)->i_flags & S_WRITE)
 #define IS_APPEND(inode) ((inode)->i_flags & S_APPEND)
 #define IS_IMMUTABLE(inode) ((inode)->i_flags & S_IMMUTABLE)
 
@@ -222,6 +226,8 @@
 	time_t		ia_ctime;
 };
 
+#include <linux/quota.h>
+
 struct inode {
 	kdev_t		i_dev;
 	unsigned long	i_ino;
@@ -238,17 +244,17 @@
 	unsigned long	i_blocks;
 	unsigned long	i_version;
 	struct semaphore i_sem;
-	struct inode_operations * i_op;
-	struct super_block * i_sb;
-	struct wait_queue * i_wait;
-	struct file_lock * i_flock;
-	struct vm_area_struct * i_mmap;
-	struct inode * i_next, * i_prev;
-	struct inode * i_hash_next, * i_hash_prev;
-	struct inode * i_bound_to, * i_bound_by;
-	struct inode * i_mount;
+	struct inode_operations *i_op;
+	struct super_block *i_sb;
+	struct wait_queue *i_wait;
+	struct file_lock *i_flock;
+	struct vm_area_struct *i_mmap;
+	struct dquot *i_dquot[MAXQUOTAS];
+	struct inode *i_next, *i_prev;
+	struct inode *i_hash_next, *i_hash_prev;
+	struct inode *i_bound_to, *i_bound_by;
+	struct inode *i_mount;
 	unsigned short i_count;
-	unsigned short i_wcount;
 	unsigned short i_flags;
 	unsigned char i_lock;
 	unsigned char i_dirt;
@@ -256,6 +262,7 @@
 	unsigned char i_sock;
 	unsigned char i_seek;
 	unsigned char i_update;
+	unsigned short i_writecount;
 	union {
 		struct pipe_inode_info pipe_i;
 		struct minix_inode_info minix_i;
@@ -330,6 +337,7 @@
 	unsigned char s_dirt;
 	struct file_system_type *s_type;
 	struct super_operations *s_op;
+	struct dquot_operations *dq_op;
 	unsigned long s_flags;
 	unsigned long s_magic;
 	unsigned long s_time;
@@ -404,6 +412,16 @@
 	int (*remount_fs) (struct super_block *, int *, char *);
 };
 
+struct dquot_operations {
+	void (*initialize) (struct inode *, short);
+	void (*drop) (struct inode *);
+	int (*alloc_block) (const struct inode *, unsigned long);
+	int (*alloc_inode) (const struct inode *, unsigned long);
+	void (*free_block) (const struct inode *, unsigned long);
+	void (*free_inode) (const struct inode *, unsigned long);
+	int (*transfer) (struct inode *, struct iattr *, char);
+};
+
 struct file_system_type {
 	struct super_block *(*read_super) (struct super_block *, void *, int);
 	const char *name;
@@ -510,8 +528,8 @@
 extern int namei(const char * pathname, struct inode ** res_inode);
 extern int lnamei(const char * pathname, struct inode ** res_inode);
 extern int permission(struct inode * inode,int mask);
-extern int get_write_access(struct inode * inode);
-extern void put_write_access(struct inode * inode);
+extern int get_write_access(struct inode *inode);
+extern void put_write_access(struct inode *inode);
 extern int open_namei(const char * pathname, int flag, int mode,
 	struct inode ** res_inode, struct inode * base);
 extern int do_mknod(const char * filename, int mode, dev_t dev);
@@ -575,7 +593,7 @@
 
 extern inline struct inode * iget(struct super_block * sb,int nr)
 {
-	return __iget(sb,nr,1);
+	return __iget(sb, nr, 1);
 }
 
 #endif /* __KERNEL__ */
diff -u --recursive --new-file v1.3.45/linux/include/linux/if.h linux/include/linux/if.h
--- v1.3.45/linux/include/linux/if.h	Tue Aug 15 20:39:04 1995
+++ linux/include/linux/if.h	Thu Nov 30 11:15:53 1995
@@ -97,7 +97,6 @@
 	union
 	{
 		char	ifrn_name[IFNAMSIZ];		/* if name, e.g. "en0" */
-		char	ifrn_hwaddr[IFHWADDRLEN];	/* Obsolete */
 	} ifr_ifrn;
 	
 	union {
@@ -116,7 +115,6 @@
 };
 
 #define ifr_name	ifr_ifrn.ifrn_name	/* interface name 	*/
-#define old_ifr_hwaddr	ifr_ifrn.ifrn_hwaddr	/* interface hardware   */
 #define ifr_hwaddr	ifr_ifru.ifru_hwaddr	/* MAC address 		*/
 #define	ifr_addr	ifr_ifru.ifru_addr	/* address		*/
 #define	ifr_dstaddr	ifr_ifru.ifru_dstaddr	/* other end of p-p lnk	*/
diff -u --recursive --new-file v1.3.45/linux/include/linux/igmp.h linux/include/linux/igmp.h
--- v1.3.45/linux/include/linux/igmp.h	Sat Nov 25 19:04:56 1995
+++ linux/include/linux/igmp.h	Thu Nov 30 11:15:53 1995
@@ -2,7 +2,7 @@
  *	Linux NET3:	Internet Gateway Management Protocol  [IGMP]
  *
  *	Authors:
- *		Alan Cox <Alan.Cox@linux.org>	
+ *		Alan Cox <Alan.Cox@linux.org>
  *
  *	Extended to talk the BSD extended IGMP protocol of mrouted 3.6
  *
@@ -65,8 +65,8 @@
 						/* specifies time in 10th of seconds	 */
 
 #define IGMP_AGE_THRESHOLD		540	/* If this host don't hear any IGMP V1	*/
-						/* message in this period of time, revert */
-						/* to IGMP v2 router */
+						/* message in this period of time,	*/
+						/* revert to IGMP v2 router.		*/
 
 #define IGMP_ALL_HOSTS		htonl(0xE0000001L)
 #define IGMP_ALL_ROUTER 	htonl(0xE0000002L)
@@ -94,12 +94,13 @@
 	int users;
 };
 
-struct router_info
+struct ip_router_info
 {
 	struct device *dev;
-	int    type; /* type of router which is querier on this interface */
-	int    time; /* # of slow timeouts since last old query */
-	struct router_info *next;
+	int    type;	/* type of router which is querier on this interface */
+	int    time;	/* # of slow timeouts since last old query */
+	struct timer_list timer;
+	struct ip_router_info *next;
 };
 
 extern struct ip_mc_list *ip_mc_head;
diff -u --recursive --new-file v1.3.45/linux/include/linux/iso_fs.h linux/include/linux/iso_fs.h
--- v1.3.45/linux/include/linux/iso_fs.h	Mon Jun 12 16:49:41 1995
+++ linux/include/linux/iso_fs.h	Mon Dec 11 06:56:37 1995
@@ -167,7 +167,7 @@
 extern int isofs_open(struct inode * inode, struct file * filp);
 extern void isofs_release(struct inode * inode, struct file * filp);
 extern int isofs_lookup(struct inode * dir,const char * name, int len,
-	struct inode ** result);
+			struct inode ** result);
 extern unsigned long isofs_count_free_inodes(struct super_block *sb);
 extern int isofs_new_block(int dev);
 extern int isofs_free_block(int dev, int block);
diff -u --recursive --new-file v1.3.45/linux/include/linux/lp.h linux/include/linux/lp.h
--- v1.3.45/linux/include/linux/lp.h	Tue Oct 10 18:46:38 1995
+++ linux/include/linux/lp.h	Thu Nov 30 14:03:38 1995
@@ -69,6 +69,7 @@
 #define LPGETSTATUS 0x060b  /* return LP_S(minor) */
 #define LPRESET     0x060c  /* reset printer */
 #define LPGETSTATS  0x060d  /* get statistics (struct lp_stats) */
+#define LPGETFLAGS  0x060e  /* get status flags */
 
 /* timeout for printk'ing a timeout, in jiffies (100ths of a second).
    This is also used for re-checking error conditions if LP_ABORT is
diff -u --recursive --new-file v1.3.45/linux/include/linux/major.h linux/include/linux/major.h
--- v1.3.45/linux/include/linux/major.h	Fri Nov 17 08:42:28 1995
+++ linux/include/linux/major.h	Mon Dec 11 08:49:53 1995
@@ -99,6 +99,7 @@
 #define IDE2_MAJOR	33
 #define IDE3_MAJOR	34
 #define NETLINK_MAJOR	36
+#define IDETAPE_MAJOR	37
 
 /*
  * Tests for SCSI devices.
diff -u --recursive --new-file v1.3.45/linux/include/linux/minix_fs.h linux/include/linux/minix_fs.h
--- v1.3.45/linux/include/linux/minix_fs.h	Mon Jun 12 16:49:42 1995
+++ linux/include/linux/minix_fs.h	Mon Dec 11 06:56:37 1995
@@ -82,7 +82,7 @@
 #ifdef __KERNEL__
 
 extern int minix_lookup(struct inode * dir,const char * name, int len,
-	struct inode ** result);
+                        struct inode ** result);
 extern int minix_create(struct inode * dir,const char * name, int len, int mode,
 	struct inode ** result);
 extern int minix_mkdir(struct inode * dir, const char * name, int len, int mode);
diff -u --recursive --new-file v1.3.45/linux/include/linux/mount.h linux/include/linux/mount.h
--- v1.3.45/linux/include/linux/mount.h	Thu Jan  1 02:00:00 1970
+++ linux/include/linux/mount.h	Mon Dec 11 06:56:37 1995
@@ -0,0 +1,30 @@
+/*
+ *
+ * Definitions for mount interface. This describes the in the kernel build 
+ * linkedlist with mounted filesystems.
+ *
+ * Author:  Marco van Wieringen <mvw@mcs.ow.nl> <mvw@tnix.net> <mvw@cistron.nl>
+ *
+ * Version: $Id: mount.h,v 1.3 1994/07/20 22:01:00 mvw Exp mvw $
+ *
+ */
+#ifndef _LINUX_MOUNT_H
+#define _LINUX_MOUNT_H
+
+struct vfsmount
+{
+   dev_t mnt_dev;                      /* Device this applies to */
+   char *mnt_devname;                  /* Name of device e.g. /dev/dsk/hda1 */
+   char *mnt_dirname;                  /* Name of directory mounted on */
+   unsigned int mnt_flags;             /* Flags of this device */
+   struct semaphore mnt_sem;           /* lock device while I/O in progress */
+   struct super_block *mnt_sb;         /* pointer to superblock */
+   struct file *mnt_quotas[MAXQUOTAS]; /* fp's to quotafiles */
+   time_t mnt_iexp[MAXQUOTAS];         /* expiretime for inodes */
+   time_t mnt_bexp[MAXQUOTAS];         /* expiretime for blocks */
+   struct vfsmount *mnt_next;          /* pointer to next in linkedlist */
+};
+
+struct vfsmount *lookup_vfsmnt(dev_t dev);
+
+#endif /* _LINUX_MOUNT_H */
diff -u --recursive --new-file v1.3.45/linux/include/linux/msdos_fs.h linux/include/linux/msdos_fs.h
--- v1.3.45/linux/include/linux/msdos_fs.h	Sat Nov 11 17:41:34 1995
+++ linux/include/linux/msdos_fs.h	Mon Dec 11 06:56:37 1995
@@ -148,7 +148,7 @@
 /* namei.c */
 
 extern int msdos_lookup(struct inode *dir,const char *name,int len,
-	struct inode **result);
+                        struct inode **result);
 extern int msdos_create(struct inode *dir,const char *name,int len,int mode,
 	struct inode **result);
 extern int msdos_mkdir(struct inode *dir,const char *name,int len,int mode);
diff -u --recursive --new-file v1.3.45/linux/include/linux/pci.h linux/include/linux/pci.h
--- v1.3.45/linux/include/linux/pci.h	Sat Nov 25 19:04:56 1995
+++ linux/include/linux/pci.h	Thu Nov 30 14:04:51 1995
@@ -264,6 +264,7 @@
 #define PCI_DEVICE_ID_CIRRUS_5434_8	0x00A8
 #define PCI_DEVICE_ID_CIRRUS_6729	0x1100
 #define PCI_DEVICE_ID_CIRRUS_7542	0x1200
+#define PCI_DEVICE_ID_CIRRUS_7543	0x1202
 
 #define PCI_VENDOR_ID_IBM		0x1014
 
@@ -415,8 +416,9 @@
 #define PCI_VENDOR_ID_ZEITNET		0x1193
 #define PCI_DEVICE_ID_ZEITNET_1221	0x0001
 
-#define PCI_VENDOR_ID_HAL		0x11cd
-#define PCI_DEVICE_ID_HAL_RIO		0x8000
+#define PCI_VENDOR_ID_SPECIALIX		0x11cb
+#define PCI_DEVICE_ID_SPECIALIX_XIO	0x4000
+#define PCI_DEVICE_ID_SPECIALIX_RIO	0x8000
 
 #define PCI_VENDOR_ID_CYCLADES		0x120e
 #define PCI_DEVICE_ID_CYCLADES_Y	0x0100
diff -u --recursive --new-file v1.3.45/linux/include/linux/proc_fs.h linux/include/linux/proc_fs.h
--- v1.3.45/linux/include/linux/proc_fs.h	Fri Nov 17 08:42:28 1995
+++ linux/include/linux/proc_fs.h	Mon Dec 11 09:55:42 1995
@@ -34,6 +34,7 @@
 	PROC_KSYMS,
 	PROC_DMA,	
 	PROC_IOPORTS,
+	PROC_APM,
 	PROC_PROFILE /* whether enabled or not */
 };
 
@@ -87,6 +88,7 @@
 	PROC_NET_NR,
 	PROC_NET_SOCKSTAT,
 	PROC_NET_RTCACHE,
+	PROC_NET_AX25_BPQETHER,
 	PROC_NET_LAST
 };
 
@@ -111,6 +113,7 @@
 	PROC_SCSI_ULTRASTOR,
 	PROC_SCSI_7000FASST,
 	PROC_SCSI_EATA2X,
+	PROC_SCSI_SSC,
 	PROC_SCSI_SCSI_DEBUG,	
 	PROC_SCSI_NOT_PRESENT,
 	PROC_SCSI_FILE,                        /* I'm asuming here that we */
diff -u --recursive --new-file v1.3.45/linux/include/linux/quota.h linux/include/linux/quota.h
--- v1.3.45/linux/include/linux/quota.h	Thu Jan  1 02:00:00 1970
+++ linux/include/linux/quota.h	Mon Dec 11 06:56:37 1995
@@ -0,0 +1,219 @@
+/*
+ * Copyright (c) 1982, 1986 Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Robert Elz at The University of Melbourne.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *   This product includes software developed by the University of
+ *   California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * Version: $Id: quota.h,v 1.8 1995/03/11 11:43:07 mvw Exp mvw $
+ */
+
+#ifndef _LINUX_QUOTA_
+#define _LINUX_QUOTA_
+
+#include <linux/errno.h>
+
+/*
+ * Convert diskblocks to blocks and the other way around.
+ * currently only to fool the BSD source. :-)
+ */
+#define dbtob(num) (num << 10)
+#define btodb(num) (num >> 10)
+
+/*
+ * Convert count of filesystem blocks to diskquota blocks, meant
+ * for filesystems where i_blksize != BLOCK_SIZE
+ */
+#define fs_to_dq_blocks(num, blksize) (((num) * (blksize)) / BLOCK_SIZE)
+
+/*
+ * Definitions for disk quotas imposed on the average user
+ * (big brother finally hits Linux).
+ *
+ * The following constants define the amount of time given a user
+ * before the soft limits are treated as hard limits (usually resulting
+ * in an allocation failure). The timer is started when the user crosses
+ * their soft limit, it is reset when they go below their soft limit.
+ */
+#define MAX_IQ_TIME  604800	/* (7*24*60*60) 1 week */
+#define MAX_DQ_TIME  604800	/* (7*24*60*60) 1 week */
+
+#define MAXQUOTAS 2
+#define USRQUOTA  0		/* element used for user quotas */
+#define GRPQUOTA  1		/* element used for group quotas */
+
+/*
+ * Definitions for the default names of the quotas files.
+ */
+#define INITQFNAMES { \
+   "user",      /* USRQUOTA */ \
+   "group",   /* GRPQUOTA */ \
+   "undefined", \
+};
+
+#define QUOTAFILENAME "quota"
+#define QUOTAGROUP "staff"
+
+#define NR_DQHASH 43            /* Just an arbitrary number any suggestions ? */
+#define NR_DQUOTS 256           /* Number of quotas active at one time */
+
+/*
+ * Command definitions for the 'quotactl' system call.
+ * The commands are broken into a main command defined below
+ * and a subcommand that is used to convey the type of
+ * quota that is being manipulated (see above).
+ */
+#define SUBCMDMASK  0x00ff
+#define SUBCMDSHIFT 8
+#define QCMD(cmd, type)  (((cmd) << SUBCMDSHIFT) | ((type) & SUBCMDMASK))
+
+#define Q_QUOTAON  0x0100	/* enable quotas */
+#define Q_QUOTAOFF 0x0200	/* disable quotas */
+#define Q_GETQUOTA 0x0300	/* get limits and usage */
+#define Q_SETQUOTA 0x0400	/* set limits and usage */
+#define Q_SETUSE   0x0500	/* set usage */
+#define Q_SYNC     0x0600	/* sync disk copy of a filesystems quotas */
+#define Q_SETQLIM  0x0700	/* set limits */
+#define Q_GETSTATS 0x0800	/* get collected stats */
+
+/*
+ * The following structure defines the format of the disk quota file
+ * (as it appears on disk) - the file is an array of these structures
+ * indexed by user or group number.
+ */
+struct dqblk {
+    __u32 dqb_bhardlimit;	/* absolute limit on disk blks alloc */
+    __u32 dqb_bsoftlimit;	/* preferred limit on disk blks */
+    __u32 dqb_curblocks;	/* current block count */
+    __u32 dqb_ihardlimit;	/* maximum # allocated inodes */
+    __u32 dqb_isoftlimit;	/* preferred inode limit */
+    __u32 dqb_curinodes;	/* current # allocated inodes */
+    time_t dqb_btime;		/* time limit for excessive disk use */
+    time_t dqb_itime;		/* time limit for excessive files */
+};
+
+/*
+ * Shorthand notation.
+ */
+#define	dq_bhardlimit	dq_dqb.dqb_bhardlimit
+#define	dq_bsoftlimit	dq_dqb.dqb_bsoftlimit
+#define	dq_curblocks	dq_dqb.dqb_curblocks
+#define	dq_ihardlimit	dq_dqb.dqb_ihardlimit
+#define	dq_isoftlimit	dq_dqb.dqb_isoftlimit
+#define	dq_curinodes	dq_dqb.dqb_curinodes
+#define	dq_btime	dq_dqb.dqb_btime
+#define	dq_itime	dq_dqb.dqb_itime
+
+#define dqoff(UID)      ((off_t)((UID) * sizeof (struct dqblk)))
+
+struct dqstats {
+   __u32 lookups;
+   __u32 drops;
+   __u32 reads;
+   __u32 writes;
+   __u32 cache_hits;
+   __u32 pages_allocated;
+   __u32 allocated_dquots;
+   __u32 free_dquots;
+   __u32 syncs;
+};
+
+#ifdef __KERNEL__
+
+/*
+ * Maximum lenght of a message generated in the quota system,
+ * that needs to be kicked onto the tty.
+ */
+#define MAX_QUOTA_MESSAGE 75
+
+#define DQ_LOCKED     0x01	/* locked for update */
+#define DQ_WANT       0x02	/* wanted for update */
+#define DQ_MOD        0x04	/* dquot modified since read */
+#define DQ_BLKS       0x10	/* uid/gid has been warned about blk limit */
+#define DQ_INODES     0x20	/* uid/gid has been warned about inode limit */
+#define DQ_FAKE       0x40	/* no limits only usage */
+
+struct dquot {
+   unsigned int dq_id;		/* id this applies to (uid, gid) */
+   short dq_type;		/* type of quota */
+   kdev_t dq_dev;                /* Device this applies to */
+   short dq_flags;		/* see DQ_* */
+   short dq_count;		/* reference count */
+   struct vfsmount *dq_mnt;     /* vfsmountpoint this applies to */
+   struct dqblk dq_dqb;         /* diskquota usage */
+   struct wait_queue *dq_wait;	/* pointer to waitqueue */
+   struct dquot *dq_prev;	/* pointer to prev dquot */
+   struct dquot *dq_next;	/* pointer to next dquot */
+   struct dquot *dq_hash_prev;	/* pointer to prev dquot */
+   struct dquot *dq_hash_next;	/* pointer to next dquot */
+};
+
+#define NODQUOT (struct dquot *)NULL
+
+/*
+ * Flags used for set_dqblk.
+ */
+#define QUOTA_SYSCALL     0x01
+#define SET_QUOTA         0x02
+#define SET_USE           0x04
+#define SET_QLIMIT        0x08
+
+#define QUOTA_OK          0
+#define NO_QUOTA          1
+
+/*
+ * declaration of quota_function calls in kernel.
+ */
+int quota_off (kdev_t dev, short type);
+int sync_dquots (kdev_t dev, short type);
+
+extern void dquot_initialize(struct inode *inode, short type);
+extern void dquot_drop(struct inode *inode);
+extern int dquot_alloc_block(const struct inode *inode, unsigned long number);
+extern int dquot_alloc_inode(const struct inode *inode, unsigned long number);
+extern void dquot_free_block(const struct inode *inode, unsigned long number);
+extern void dquot_free_inode(const struct inode *inode, unsigned long number);
+extern int dquot_transfer(struct inode *inode, struct iattr *iattr, char direction);
+
+extern void invalidate_dquots(kdev_t dev, short type);
+extern int quota_off(kdev_t dev, short type);
+extern int sync_dquots(kdev_t dev, short type);
+
+#else
+
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+int quotactl __P ((int, const char *, int, caddr_t));
+__END_DECLS
+
+#endif /* __KERNEL__ */
+#endif /* _QUOTA_ */
diff -u --recursive --new-file v1.3.45/linux/include/linux/sched.h linux/include/linux/sched.h
--- v1.3.45/linux/include/linux/sched.h	Tue Nov 21 13:22:13 1995
+++ linux/include/linux/sched.h	Mon Dec 11 07:10:15 1995
@@ -133,6 +133,7 @@
 struct mm_struct {
 	int count;
 	pgd_t * pgd;
+	unsigned long context;
 	unsigned long start_code, end_code, start_data, end_data;
 	unsigned long start_brk, brk, start_stack, start_mmap;
 	unsigned long arg_start, arg_end, env_start, env_end;
@@ -145,6 +146,7 @@
 #define INIT_MM { \
 		1, \
 		swapper_pg_dir, \
+		0, \
 		0, 0, 0, 0, \
 		0, 0, 0, 0, \
 		0, 0, 0, 0, \
diff -u --recursive --new-file v1.3.45/linux/include/linux/smb.h linux/include/linux/smb.h
--- v1.3.45/linux/include/linux/smb.h	Fri Jul  7 08:54:54 1995
+++ linux/include/linux/smb.h	Mon Dec 11 08:49:35 1995
@@ -50,6 +50,7 @@
 
 #define LANMAN1
 #define LANMAN2
+#define NT1
 
 enum smb_protocol { 
 	PROTOCOL_NONE, 
diff -u --recursive --new-file v1.3.45/linux/include/linux/sys.h linux/include/linux/sys.h
--- v1.3.45/linux/include/linux/sys.h	Thu Jun 29 19:02:55 1995
+++ linux/include/linux/sys.h	Mon Dec 11 06:56:37 1995
@@ -27,10 +27,4 @@
  * These are system calls that haven't been implemented yet
  * but have an entry in the table for future expansion..
  */
-#ifdef __ELF__
-#define sys_quotactl	sys_ni_syscall
-#else
-#define _sys_quotactl	_sys_ni_syscall
-#endif
-
 #endif
diff -u --recursive --new-file v1.3.45/linux/include/linux/sysv_fs.h linux/include/linux/sysv_fs.h
--- v1.3.45/linux/include/linux/sysv_fs.h	Mon Sep 18 14:54:09 1995
+++ linux/include/linux/sysv_fs.h	Mon Dec 11 07:17:03 1995
@@ -362,7 +362,7 @@
  */
 
 extern int sysv_lookup(struct inode * dir,const char * name, int len,
-	struct inode ** result);
+	               struct inode ** result);
 extern int sysv_create(struct inode * dir,const char * name, int len, int mode,
 	struct inode ** result);
 extern int sysv_mkdir(struct inode * dir, const char * name, int len, int mode);
diff -u --recursive --new-file v1.3.45/linux/include/linux/tty.h linux/include/linux/tty.h
--- v1.3.45/linux/include/linux/tty.h	Tue Nov 21 13:22:13 1995
+++ linux/include/linux/tty.h	Mon Dec 11 07:10:15 1995
@@ -297,6 +297,7 @@
 extern int tty_unregister_driver(struct tty_driver *driver);
 extern int tty_read_raw_data(struct tty_struct *tty, unsigned char *bufp,
 			     int buflen);
+extern void tty_write_message(struct tty_struct *tty, char *msg);
 
 extern int is_orphaned_pgrp(int pgrp);
 extern int is_ignored(int sig);
diff -u --recursive --new-file v1.3.45/linux/include/net/ax25.h linux/include/net/ax25.h
--- v1.3.45/linux/include/net/ax25.h	Wed Sep 27 16:00:00 1995
+++ linux/include/net/ax25.h	Mon Dec 11 11:27:19 1995
@@ -203,6 +203,9 @@
 extern void ax25_dev_device_up(struct device *);
 extern void ax25_dev_device_down(struct device *);
 extern int  ax25_dev_ioctl(unsigned int, void *);
+extern int  ax25_bpq_get_info(char *, char **, off_t, int, int);
+extern ax25_address *ax25_bpq_get_addr(struct device *);
+extern int  ax25_bpq_ioctl(unsigned int, void *);
 
 /* ax25_subr.c */
 extern void ax25_clear_queues(ax25_cb *);
diff -u --recursive --new-file v1.3.45/linux/include/net/netlink.h linux/include/net/netlink.h
--- v1.3.45/linux/include/net/netlink.h	Fri Nov 17 08:42:29 1995
+++ linux/include/net/netlink.h	Mon Dec 11 11:34:40 1995
@@ -1,9 +1,9 @@
 #ifndef __NET_NETLINK_H
 #define __NET_NETLINK_H
 
-#define NET_MAJOR 18		/* Major 18 is reserved for networking 		*/
-#define MAX_LINKS 3		/* 18,0 for route updates, 18,1 for SKIP 	*/
-#define MAX_QBYTES 32768	/* Maximum bytes in the queue 			*/
+#define NET_MAJOR 36		/* Major 18 is reserved for networking 						*/
+#define MAX_LINKS 4		/* 18,0 for route updates, 18,1 for SKIP, 18,2 debug tap 18,3 PPP reserved 	*/
+#define MAX_QBYTES 32768	/* Maximum bytes in the queue 							*/
 
 #include <linux/config.h>
 
diff -u --recursive --new-file v1.3.45/linux/init/main.c linux/init/main.c
--- v1.3.45/linux/init/main.c	Mon Nov 27 12:48:36 1995
+++ linux/init/main.c	Mon Dec 11 09:55:42 1995
@@ -29,6 +29,9 @@
 #include <linux/hdreg.h>
 #include <linux/mm.h>
 #include <linux/major.h>
+#ifdef CONFIG_APM
+#include <linux/apm_bios.h>
+#endif
 
 #include <asm/bugs.h>
 
@@ -127,6 +130,8 @@
 char nfs_root_addrs[128] = { "" };
 #endif
 
+extern void dquot_init(void);
+
 static char * argv_init[MAX_INIT_ARGS+2] = { "init", NULL, };
 static char * envp_init[MAX_INIT_ENVS+2] = { "HOME=/", "TERM=linux", NULL, };
 
@@ -400,6 +405,7 @@
 		if (!strncmp(line, "nfsroot=", 8)) {
 			int n;
 			line += 8;
+			ROOT_DEV = MKDEV(UNNAMED_MAJOR, 255);
 			if (line[0] == '/' || (line[0] >= '0' && line[0] <= '9')) {
 				strncpy(nfs_root_name, line, sizeof(nfs_root_name));
 				nfs_root_name[sizeof(nfs_root_name)] = '\0';
@@ -580,6 +586,10 @@
 #ifdef CONFIG_SYSVIPC
 	ipc_init();
 #endif
+#ifdef CONFIG_APM
+	apm_bios_init();
+#endif
+	dquot_init();
 	sti();
 	check_bugs();
 
diff -u --recursive --new-file v1.3.45/linux/kernel/ksyms.c linux/kernel/ksyms.c
--- v1.3.45/linux/kernel/ksyms.c	Sat Nov 25 19:04:57 1995
+++ linux/kernel/ksyms.c	Thu Nov 30 14:04:51 1995
@@ -336,6 +336,7 @@
 #endif	
 	X(jiffies),
 	X(xtime),
+	X(do_gettimeofday),
 	X(loops_per_sec),
 	X(need_resched),
 	X(kill_proc),
diff -u --recursive --new-file v1.3.45/linux/kernel/module.c linux/kernel/module.c
--- v1.3.45/linux/kernel/module.c	Mon Oct 16 18:38:24 1995
+++ linux/kernel/module.c	Thu Nov 30 14:04:50 1995
@@ -547,8 +547,10 @@
 			*p++ = *q++;
 		if (mp->state == MOD_UNINITIALIZED)
 			q = "  (uninitialized)";
-		else if (mp->state == MOD_RUNNING)
-			q = "";
+		else if (mp->state == MOD_RUNNING) {
+			sprintf(size,"\t%ld",GET_USE_COUNT(mp));
+			q=size;
+		}
 		else if (mp->state == MOD_DELETED)
 			q = "  (deleted)";
 		else
diff -u --recursive --new-file v1.3.45/linux/kernel/printk.c linux/kernel/printk.c
--- v1.3.45/linux/kernel/printk.c	Sun Oct 29 11:38:49 1995
+++ linux/kernel/printk.c	Mon Dec 11 06:56:37 1995
@@ -20,6 +20,8 @@
 #include <linux/sched.h>
 #include <linux/kernel.h>
 #include <linux/mm.h>
+#include <linux/tty.h>
+#include <linux/tty_driver.h>
 
 #define LOG_BUF_LEN	4096
 
@@ -235,4 +237,17 @@
 			msg_level = -1;
 		j = 0;
 	}
+}
+
+/*
+ * Write a message to a certain tty, not just the console. This is used for
+ * messages that need to be redirected to a specific tty.
+ * We don't put it into the syslog queue right now maybe in the future if
+ * really needed.
+ */
+void tty_write_message(struct tty_struct *tty, char *msg)
+{
+	if (tty && tty->driver.write)
+		tty->driver.write(tty, 0, msg, strlen(msg));
+	return;
 }
diff -u --recursive --new-file v1.3.45/linux/lib/Makefile linux/lib/Makefile
--- v1.3.45/linux/lib/Makefile	Tue Aug 15 20:39:05 1995
+++ linux/lib/Makefile	Mon Nov 27 15:54:00 1995
@@ -7,7 +7,6 @@
 #
 
 L_TARGET := lib.a
-L_OBJS   := ctype.o _exit.o open.o close.o errno.o write.o dup.o \
-            setsid.o execve.o wait.o string.o vsprintf.o
+L_OBJS   := errno.o ctype.o string.o vsprintf.o
 
 include $(TOPDIR)/Rules.make
diff -u --recursive --new-file v1.3.45/linux/lib/_exit.c linux/lib/_exit.c
--- v1.3.45/linux/lib/_exit.c	Wed Jan  4 21:16:06 1995
+++ linux/lib/_exit.c	Thu Jan  1 02:00:00 1970
@@ -1,21 +0,0 @@
-/*
- *  linux/lib/_exit.c
- *
- *  Copyright (C) 1991, 1992  Linus Torvalds
- */
-
-#define __LIBRARY__
-#include <linux/unistd.h>
-
-#define exit __sys_exit
-static inline _syscall0(int, exit)
-#undef exit
-
-volatile void _exit(int exit_code)
-{
-fake_volatile:
-	__sys_exit();
-	goto fake_volatile;
-}
-
-
diff -u --recursive --new-file v1.3.45/linux/lib/close.c linux/lib/close.c
--- v1.3.45/linux/lib/close.c	Wed Dec  1 14:44:15 1993
+++ linux/lib/close.c	Thu Jan  1 02:00:00 1970
@@ -1,11 +0,0 @@
-/*
- *  linux/lib/close.c
- *
- *  Copyright (C) 1991, 1992  Linus Torvalds
- */
-
-#define __LIBRARY__
-#include <linux/unistd.h>
-
-_syscall1(int,close,int,fd)
-
diff -u --recursive --new-file v1.3.45/linux/lib/dup.c linux/lib/dup.c
--- v1.3.45/linux/lib/dup.c	Wed Dec  1 14:44:15 1993
+++ linux/lib/dup.c	Thu Jan  1 02:00:00 1970
@@ -1,11 +0,0 @@
-/*
- *  linux/lib/dup.c
- *
- *  Copyright (C) 1991, 1992  Linus Torvalds
- */
-
-#define __LIBRARY__
-#include <linux/unistd.h>
-
-_syscall1(int,dup,int,fd)
-
diff -u --recursive --new-file v1.3.45/linux/lib/execve.c linux/lib/execve.c
--- v1.3.45/linux/lib/execve.c	Wed Dec  1 14:44:15 1993
+++ linux/lib/execve.c	Thu Jan  1 02:00:00 1970
@@ -1,11 +0,0 @@
-/*
- *  linux/lib/execve.c
- *
- *  Copyright (C) 1991, 1992  Linus Torvalds
- */
-
-#define __LIBRARY__
-#include <linux/unistd.h>
-
-_syscall3(int,execve,const char *,file,char **,argv,char **,envp)
-
diff -u --recursive --new-file v1.3.45/linux/lib/open.c linux/lib/open.c
--- v1.3.45/linux/lib/open.c	Wed Jan  4 21:16:06 1995
+++ linux/lib/open.c	Thu Jan  1 02:00:00 1970
@@ -1,24 +0,0 @@
-/*
- *  linux/lib/open.c
- *
- *  Copyright (C) 1991, 1992  Linus Torvalds
- */
-
-#define __LIBRARY__
-#include <linux/unistd.h>
-#include <stdarg.h>
-
-#define open __sys_open
-static inline _syscall3(int, open, const char *, filename, int, flag, int, mode)
-#undef open
-
-int open(const char * filename, int flag, ...)
-{
-	int res;
-	va_list arg;
-
-	va_start(arg,flag);
-	res = __sys_open(filename, flag, va_arg(arg, int));
-	va_end(arg);
-	return res;
-}
diff -u --recursive --new-file v1.3.45/linux/lib/setsid.c linux/lib/setsid.c
--- v1.3.45/linux/lib/setsid.c	Wed Dec  1 14:44:15 1993
+++ linux/lib/setsid.c	Thu Jan  1 02:00:00 1970
@@ -1,12 +0,0 @@
-/*
- *  linux/lib/setsid.c
- *
- *  Copyright (C) 1991, 1992  Linus Torvalds
- */
-
-#define __LIBRARY__
-#include <linux/types.h>
-#include <linux/unistd.h>
-
-_syscall0(pid_t,setsid)
-
diff -u --recursive --new-file v1.3.45/linux/lib/wait.c linux/lib/wait.c
--- v1.3.45/linux/lib/wait.c	Wed Dec  1 14:44:15 1993
+++ linux/lib/wait.c	Thu Jan  1 02:00:00 1970
@@ -1,17 +0,0 @@
-/*
- *  linux/lib/wait.c
- *
- *  Copyright (C) 1991, 1992  Linus Torvalds
- */
-
-#define __LIBRARY__
-#include <linux/unistd.h>
-#include <linux/types.h>
-
-_syscall3(pid_t,waitpid,pid_t,pid,int *,wait_stat,int,options)
-
-pid_t wait(int * wait_stat)
-{
-	return waitpid(-1,wait_stat,0);
-}
-
diff -u --recursive --new-file v1.3.45/linux/lib/write.c linux/lib/write.c
--- v1.3.45/linux/lib/write.c	Wed Dec  1 14:44:15 1993
+++ linux/lib/write.c	Thu Jan  1 02:00:00 1970
@@ -1,12 +0,0 @@
-/*
- *  linux/lib/write.c
- *
- *  Copyright (C) 1991, 1992  Linus Torvalds
- */
-
-#define __LIBRARY__
-#include <linux/unistd.h>
-#include <linux/types.h>
-
-_syscall3(int,write,int,fd,const char *,buf,off_t,count)
-
diff -u --recursive --new-file v1.3.45/linux/mm/mmap.c linux/mm/mmap.c
--- v1.3.45/linux/mm/mmap.c	Tue Nov 21 13:22:14 1995
+++ linux/mm/mmap.c	Mon Dec 11 06:56:37 1995
@@ -95,7 +95,7 @@
 			return -EINVAL;
 		}
 		if (flags & MAP_DENYWRITE) {
-			if (file->f_inode->i_wcount > 0)
+			if (file->f_inode->i_writecount > 0)
 				return -ETXTBSY;
 		}
 	} else if ((flags & MAP_TYPE) != MAP_PRIVATE)
diff -u --recursive --new-file v1.3.45/linux/net/Changes linux/net/Changes
--- v1.3.45/linux/net/Changes	Sat Nov 25 19:04:57 1995
+++ linux/net/Changes	Thu Nov 30 11:15:54 1995
@@ -277,8 +277,8 @@
 o	First parts of the SKIP support			[IN, not useful]
 o	TCP ICMP (SOSS should work again)		[IN]
 o	IPFW support for TOS changing (Al Longyear)	[IN]
-o	DECNET PPP test code				[IN]
-o	NFS root					[IN]
+o	DECNET PPP test code [Steve]			[IN]
+o	NFS root	[Miguel/Gero]			[IN]
 o	Path MTU discovery [ANK]			[IN]
 
 -------->>>>> 1.3.44 <<<<<<--------
@@ -291,6 +291,14 @@
 o	Fixes to the name set functions (maybe fixes
 	netrom)		[Steve]				[IN]
 o	Packet protocol labelling (not IPX yet)		[IN]
+o	Faster buffer copy/clone [Linus]		[IN]
+
+-------->>>>> 1.3.46 <<<<<<--------
+
+o	AX.25/NetROM fixes/changes [John Naylor]	[IN]
+o	Further attempts to fix the IPX memory bug	[IN]
+o	ARP fixes (Assorted)				[IN]
+o	Driver fixes for multicast lists		[IN]
 
 ---------- Things I thought Linus had for a while and not merged ----------------
 
diff -u --recursive --new-file v1.3.45/linux/net/Config.in linux/net/Config.in
--- v1.3.45/linux/net/Config.in	Mon Oct 23 18:02:21 1995
+++ linux/net/Config.in	Thu Nov 30 11:15:54 1995
@@ -12,6 +12,7 @@
 bool 'Appletalk DDP' CONFIG_ATALK
 bool 'Amateur Radio AX.25 Level 2' CONFIG_AX25
 if [ "$CONFIG_AX25" = "y" ]; then
+  bool 'AX.25 over Ethernet' CONFIG_BPQETHER
   bool 'Amateur Radio NET/ROM' CONFIG_NETROM
 fi
 bool 'Kernel/User network link driver(ALPHA)' CONFIG_NETLINK
diff -u --recursive --new-file v1.3.45/linux/net/ax25/af_ax25.c linux/net/ax25/af_ax25.c
--- v1.3.45/linux/net/ax25/af_ax25.c	Sat Nov 25 19:04:57 1995
+++ linux/net/ax25/af_ax25.c	Thu Nov 30 11:15:54 1995
@@ -651,7 +651,6 @@
 struct device *ax25rtr_get_dev(ax25_address *addr)
 {
 	struct device *dev;
-	ax25_address dev_addr;
 	
 	for (dev = dev_base; dev != NULL; dev = dev->next) {
 		if (dev->flags & IFF_UP) {
@@ -660,11 +659,16 @@
 					if (ax25cmp(addr, (ax25_address *)dev->dev_addr) == 0)
 						return dev;
 					break;
-				case ARPHRD_ETHER:
-					if (arp_query((unsigned char *)&dev_addr, dev->pa_addr, dev))
-						if (ax25cmp(addr, &dev_addr) == 0)
-							return dev;
+#ifdef CONFIG_BPQETHER
+				case ARPHRD_ETHER: {
+						ax25_address *dev_addr;
+
+						if ((dev_addr = ax25_bpq_get_addr(dev)) != NULL)
+							if (ax25cmp(addr, dev_addr) == 0)
+								return dev;
+					}
 					break;
+#endif
 				default:
 					break;
 			}
@@ -1659,17 +1663,18 @@
 	return ax25_rcv(skb, dev, (ax25_address *)dev->dev_addr, ptype);
 }
 
+#ifdef CONFIG_BPQETHER
 /*
  *	Receive an AX.25 frame via an Ethernet interface.
  */
 static int bpq_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *ptype)
 {
-	ax25_address port_call;
+	ax25_address *port_call;
 	int len;
 
 	skb->sk = NULL;		/* Initially we don't know who its for */
 
-	if (!arp_query((unsigned char *)&port_call, dev->pa_addr, dev)) {
+	if ((port_call = ax25_bpq_get_addr(dev)) == NULL) {
 		kfree_skb(skb, FREE_READ);	/* We have no port callsign */
 		return 0;
 	}
@@ -1679,8 +1684,9 @@
 	skb_pull(skb, 2);	/* Remove the length bytes */
 	skb_trim(skb, len);	/* Set the length of the data */
 
-	return ax25_rcv(skb, dev, &port_call, ptype);
+	return ax25_rcv(skb, dev, port_call, ptype);
 }
+#endif
 
 static int ax25_sendmsg(struct socket *sock, struct msghdr *msg, int len, int noblock, int flags)
 {
@@ -1970,6 +1976,13 @@
 			ax25_uid_policy = amount;
 			return 0;
 
+#ifdef CONFIG_BPQETHER
+		case SIOCAX25BPQADDR:
+			if (!suser())
+				return -EPERM;
+			return ax25_bpq_ioctl(cmd, (void *)arg);
+#endif
+
 		case SIOCAX25GETPARMS:
 		case SIOCAX25SETPARMS:
 			return ax25_dev_ioctl(cmd, (void *)arg);
@@ -2097,6 +2110,7 @@
 	NULL,
 };
 
+#ifdef CONFIG_BPQETHER
 static struct packet_type bpq_packet_type = 
 {
 	0,	/* MUTTER ntohs(ETH_P_BPQ),*/
@@ -2105,6 +2119,7 @@
 	NULL,
 	NULL,
 };
+#endif
 
 static struct notifier_block ax25_dev_notifier = {
 	ax25_device_event,
@@ -2116,8 +2131,10 @@
 	sock_register(ax25_proto_ops.family, &ax25_proto_ops);
 	ax25_packet_type.type = htons(ETH_P_AX25);
 	dev_add_pack(&ax25_packet_type);	
+#ifdef CONFIG_BPQETHER
 	bpq_packet_type.type  = htons(ETH_P_BPQ);
 	dev_add_pack(&bpq_packet_type);
+#endif
 	register_netdevice_notifier(&ax25_dev_notifier);
 			  
 	proc_net_register(&(struct proc_dir_entry) {
@@ -2140,6 +2157,17 @@
 	});
 
 	printk("G4KLX/GW4PTS AX.25 for Linux. Version 0.30 BETA for Linux NET3.032 (Linux 1.3.35)\n");
+
+#ifdef CONFIG_BPQETHER
+	proc_net_register(&(struct proc_dir_entry) {
+		PROC_NET_AX25_BPQETHER, 13, "ax25_bpqether",
+		S_IFREG | S_IRUGO, 1, 0, 0,
+		0, &proc_net_inode_operations,
+		ax25_bpq_get_info
+	});
+
+	printk("G8BPQ Encapsulation of AX.25 frames enabled\n");
+#endif
 }
 
 /*
@@ -2149,9 +2177,7 @@
 
 void ax25_queue_xmit(struct sk_buff *skb, struct device *dev, int pri)
 {
-	static char bcast_addr[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
 	unsigned char *ptr;
-	int size;
 	
 #ifdef CONFIG_FIREWALL
 
@@ -2163,10 +2189,12 @@
 	
 #endif	
 	skb->protocol = htons (ETH_P_AX25);
-
-	if (dev->type == ARPHRD_ETHER) 
+#ifdef CONFIG_BPQETHER
+	if(dev->type == ARPHRD_ETHER)
 	{
-		if (skb_headroom(skb) < AX25_BPQ_HEADER_LEN) 
+		static char bcast_addr[6]={0xFF,0xFF,0xFF,0xFF,0xFF,0xFF};
+		int size;
+		if(skb_headroom(skb) < AX25_BPQ_HEADER_LEN)
 		{
 			printk("ax25_queue_xmit: not enough space to add BPQ Ether header\n");
 			skb->free = 1;
@@ -2182,12 +2210,15 @@
 		*ptr++ = (size + 5) / 256;
 
 		dev->hard_header(skb, dev, ETH_P_BPQ, bcast_addr, NULL, 0);
+
+		dev_queue_xmit(skb, dev, pri);
+
+		return;
 	} 
-	else 
-	{
-		ptr = skb_push(skb, 1);
-		*ptr++ = 0;			/* KISS */
-	}
+#endif
+
+	ptr = skb_push(skb, 1);
+	*ptr++ = 0;			/* KISS */
 
 	dev_queue_xmit(skb, dev, pri);
 }
diff -u --recursive --new-file v1.3.45/linux/net/ax25/ax25_route.c linux/net/ax25/ax25_route.c
--- v1.3.45/linux/net/ax25/ax25_route.c	Wed Sep 27 16:00:01 1995
+++ linux/net/ax25/ax25_route.c	Thu Nov 30 11:15:54 1995
@@ -593,4 +593,97 @@
 	return 0;
 }
 
+#ifdef CONFIG_BPQETHER
+static struct ax25_bpqdev {
+	struct ax25_bpqdev *next;
+	struct device *dev;
+	ax25_address callsign;
+} *ax25_bpqdev = NULL;
+
+int ax25_bpq_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
+{
+	struct ax25_bpqdev *bpqdev;
+	int len     = 0;
+	off_t pos   = 0;
+	off_t begin = 0;
+  
+	cli();
+
+	len += sprintf(buffer, "dev  callsign\n");
+
+	for (bpqdev = ax25_bpqdev; bpqdev != NULL; bpqdev = bpqdev->next) {
+		len += sprintf(buffer + len, "%-4s %-9s\n",
+			bpqdev->dev ? bpqdev->dev->name : "???",
+			ax2asc(&bpqdev->callsign));
+
+		pos = begin + len;
+
+		if (pos < offset) {
+			len   = 0;
+			begin = pos;
+		}
+		
+		if (pos > offset + length)
+			break;
+	}
+
+	sti();
+
+	*start = buffer + (offset - begin);
+	len   -= (offset - begin);
+
+	if (len > length) len = length;
+
+	return len;
+} 
+
+ax25_address *ax25_bpq_get_addr(struct device *dev)
+{
+	struct ax25_bpqdev *bpqdev;
+	
+	for (bpqdev = ax25_bpqdev; bpqdev != NULL; bpqdev = bpqdev->next)
+		if (bpqdev->dev == dev)
+			return &bpqdev->callsign;
+
+	return NULL;
+}
+
+int ax25_bpq_ioctl(unsigned int cmd, void *arg)
+{
+	unsigned long flags;
+	struct ax25_bpqdev *bpqdev;
+	struct ax25_bpqaddr_struct bpqaddr;
+	struct device *dev;
+	int err;
+
+	switch (cmd) {
+		case SIOCAX25BPQADDR:
+			if ((err = verify_area(VERIFY_READ, arg, sizeof(bpqaddr))) != 0)
+				return err;
+			memcpy_fromfs(&bpqaddr, arg, sizeof(bpqaddr));
+			if ((dev = dev_get(bpqaddr.dev)) == NULL)
+				return -EINVAL;
+			for (bpqdev = ax25_bpqdev; bpqdev != NULL; bpqdev = bpqdev->next) {
+				if (bpqdev->dev == dev) {
+					bpqdev->callsign = bpqaddr.addr;
+					return 0;
+				}
+			}
+			if ((bpqdev = (struct ax25_bpqdev *)kmalloc(sizeof(struct ax25_bpqdev), GFP_ATOMIC)) == NULL)
+				return -ENOMEM;
+			bpqdev->dev      = dev;
+			bpqdev->callsign = bpqaddr.addr;
+			save_flags(flags);
+			cli();
+			bpqdev->next = ax25_bpqdev;
+			ax25_bpqdev  = bpqdev;
+			restore_flags(flags);
+			break;
+	}
+
+	return 0;
+}
+
+#endif
+
 #endif
diff -u --recursive --new-file v1.3.45/linux/net/core/datagram.c linux/net/core/datagram.c
--- v1.3.45/linux/net/core/datagram.c	Wed Nov  8 07:11:45 1995
+++ linux/net/core/datagram.c	Thu Nov 30 11:15:54 1995
@@ -3,10 +3,10 @@
  *
  *	Generic datagram handling routines. These are generic for all protocols. Possibly a generic IP version on top
  *	of these would make sense. Not tonight however 8-).
- *	This is used because UDP, RAW, PACKET and the to be released IPX layer all have identical select code and mostly
- *	identical recvfrom() code. So we share it here. The select was shared before but buried in udp.c so I moved it.
+ *	This is used because UDP, RAW, PACKET, DDP, IPX, AX.25 and NetROM layer all have identical select code and mostly
+ *	identical recvmsg() code. So we share it here. The select was shared before but buried in udp.c so I moved it.
  *
- *	Authors:	Alan Cox <iiitac@pyr.swan.ac.uk>. (datagram_select() from old udp.c code)
+ *	Authors:	Alan Cox <alan@cymru.net>. (datagram_select() from old udp.c code)
  *
  *	Fixes:
  *		Alan Cox	:	NULL return from skb_peek_copy() understood
@@ -19,9 +19,6 @@
  *		Linus Torvalds	:	BSD semantic fixes.
  *		Alan Cox	:	Datagram iovec handling
  *
- *	Note:
- *		A lot of this will change when the protocol/socket separation
- *	occurs. Using this will make things reasonably clean.
  */
 
 #include <linux/types.h>
diff -u --recursive --new-file v1.3.45/linux/net/ipv4/arp.c linux/net/ipv4/arp.c
--- v1.3.45/linux/net/ipv4/arp.c	Sat Nov 25 19:04:58 1995
+++ linux/net/ipv4/arp.c	Thu Nov 30 11:15:54 1995
@@ -1603,6 +1603,7 @@
 /*
  * Test if a hardware address is all zero
  */
+
 static inline int empty(unsigned char * addr, int len)
 {
 	while (len > 0) {
@@ -1628,12 +1629,7 @@
 	u32 ip;
 
 	/*
-	 * Find out about the hardware type.
-	 * We have to be compatible with BSD UNIX, so we have to
-	 * assume that a "not set" value (i.e. 0) means Ethernet.
-	 *
-	 * ANK: Hey, who wrote it? Do you really mean that BSD considers 
-	 *	ARPHRD_NETROM as ARPHRD_ETHER, or somthing another?
+	 *	Find out about the hardware type.
 	 */
 	
 	si = (struct sockaddr_in *) &r->arp_pa;
diff -u --recursive --new-file v1.3.45/linux/net/ipv4/igmp.c linux/net/ipv4/igmp.c
--- v1.3.45/linux/net/ipv4/igmp.c	Sat Nov 25 19:04:58 1995
+++ linux/net/ipv4/igmp.c	Thu Nov 30 11:15:55 1995
@@ -28,9 +28,11 @@
  *					desired by mrouted, fixed the fact it has been
  *					broken since 1.3.6 and cleaned up a few minor
  *					points.
+ *
  *		Chih-Jen Chang	:	Tried to revise IGMP to Version 2
- *		Tsu-Sheng Tsao		chihjenc@scf.usc.edu and tsusheng@scf.usc.edu
- *					The enhancements are based on Steve Deering's ipmulti-3.5 code
+ *		Tsu-Sheng Tsao		E-mail: chihjenc@scf.usc.edu and tsusheng@scf.usc.edu
+ *					The enhancements are mainly based on Steve Deering's 
+ * 					ipmulti-3.5 source code.
  *		Chih-Jen Chang	:	Added the igmp_get_mrouter_info and
  *		Tsu-Sheng Tsao		igmp_set_mrouter_info to keep track of
  *					the mrouted version on that device.
@@ -38,6 +40,8 @@
  *		Tsu-Sheng Tsao		igmp_heard_query(). Using this parameter
  *					to identify the multicast router verion
  *					and do what the IGMP version 2 specified.
+ *		Chih-Jen Chang	:	Added a timer to revert to IGMP V2 router
+ *		Tsu-Sheng Tsao		if the specified time expired.
  */
 
 
@@ -66,20 +70,33 @@
 
 
 /*
+ *	If time expired, change the router type to IGMP_NEW_ROUTER.
+ */
+
+static void ip_router_timer_expire(unsigned long data)
+{
+	struct ip_router_info *i=(struct ip_router_info *)data;
+
+	del_timer(&i->timer);
+	i->type=IGMP_NEW_ROUTER;	/* Revert to new multicast router */
+	i->time=0;
+}
+
+/*
  *	Multicast router info manager
  */
 
-struct	router_info	*router_info_head=(struct router_info *)0;
+struct	ip_router_info	*ip_router_info_head=(struct ip_router_info *)0;
 
 /*
  *	Get the multicast router info on that device
  */
 
-static	struct	router_info	*igmp_get_mrouter_info(struct device *dev)
+static	struct	ip_router_info	*igmp_get_mrouter_info(struct device *dev)
 {
-	register struct router_info *i;
+	register struct ip_router_info *i;
 
-	for(i=router_info_head;i!=NULL;i=i->next)
+	for(i=ip_router_info_head;i!=NULL;i=i->next)
 	{
 		if (i->dev == dev)
 		{
@@ -90,12 +107,17 @@
 	/*
 	 *  Not found. Create a new entry. The default is IGMP V2 router
 	 */
-	i=(struct router_info *)kmalloc(sizeof(*i), GFP_KERNEL);
+	i=(struct ip_router_info *)kmalloc(sizeof(*i), GFP_KERNEL);
 	i->dev = dev;
 	i->type = IGMP_NEW_ROUTER;
 	i->time = IGMP_AGE_THRESHOLD;
-	i->next = router_info_head;
-	router_info_head = i;
+	i->next = ip_router_info_head;
+	ip_router_info_head = i;
+
+	init_timer(&i->timer);
+	i->timer.data=(unsigned long)i;
+	i->timer.function=&ip_router_timer_expire;
+
 	return i;
 }
 
@@ -103,16 +125,27 @@
  *	Set the multicast router info on that device
  */
 
-static	struct	router_info	*igmp_set_mrouter_info(struct device *dev,int type,int time)
+static	struct	ip_router_info	*igmp_set_mrouter_info(struct device *dev,int type,int time)
 {
-	register struct router_info *i;
+	register struct ip_router_info *i;
 
-	for(i=router_info_head;i!=NULL;i=i->next)
+	for(i=ip_router_info_head;i!=NULL;i=i->next)
 	{
 		if (i->dev == dev)
 		{
+			if(i->type==IGMP_OLD_ROUTER)
+			{
+				del_timer(&i->timer);
+			}
+
 			i->type = type;
 			i->time = time;
+
+			if(i->type==IGMP_OLD_ROUTER)
+			{
+				i->timer.expires=jiffies+i->time*HZ;
+				add_timer(&i->timer);
+			}
 			return i;
 		}
 	}
@@ -120,12 +153,22 @@
 	/*
 	 *  Not found. Create a new entry.
 	 */
-	i=(struct router_info *)kmalloc(sizeof(*i), GFP_KERNEL);
+	i=(struct ip_router_info *)kmalloc(sizeof(*i), GFP_KERNEL);
 	i->dev = dev;
 	i->type = type;
 	i->time = time;
-	i->next = router_info_head;
-	router_info_head = i;
+	i->next = ip_router_info_head;
+	ip_router_info_head = i;
+
+	init_timer(&i->timer);
+	i->timer.data=(unsigned long)i;
+	i->timer.function=&ip_router_timer_expire;
+	if(i->type==IGMP_OLD_ROUTER)
+	{
+		i->timer.expires=jiffies+i->time*HZ;
+		add_timer(&i->timer);
+	}
+
 	return i;
 }
 
@@ -134,7 +177,6 @@
  *	Timer management
  */
 
-
 static void igmp_stop_timer(struct ip_mc_list *im)
 {
 	del_timer(&im->timer);
@@ -197,7 +239,7 @@
 static void igmp_timer_expire(unsigned long data)
 {
 	struct ip_mc_list *im=(struct ip_mc_list *)data;
-	struct router_info *r;
+	struct ip_router_info *r;
 	igmp_stop_timer(im);
 	r=igmp_get_mrouter_info(im->interface);
 	if(r->type==IGMP_NEW_ROUTER)
@@ -337,7 +379,7 @@
 
 extern __inline__ void igmp_group_added(struct ip_mc_list *im)
 {
-	struct router_info *r;
+	struct ip_router_info *r;
 	igmp_init_timer(im);
 	ip_mc_filter_add(im->interface, im->multiaddr);
 	r=igmp_get_mrouter_info(im->interface);
@@ -462,20 +504,9 @@
 void ip_mc_allhost(struct device *dev)
 {
 	struct ip_mc_list *i;
-	/*
-	 *	Don't add to non multicast units
-	 */
-	if(!(dev->flags&IFF_MULTICAST))
-		return;
-	/*
-	 *	No duplicates
-	 */
 	for(i=dev->ip_mc_list;i!=NULL;i=i->next)
 		if(i->multiaddr==IGMP_ALL_HOSTS)
 			return;
-	/*
-	 *	Add it
-	 */
 	i=(struct ip_mc_list *)kmalloc(sizeof(*i), GFP_KERNEL);
 	if(!i)
 		return;
diff -u --recursive --new-file v1.3.45/linux/net/ipv4/raw.c linux/net/ipv4/raw.c
--- v1.3.45/linux/net/ipv4/raw.c	Sat Nov 25 19:04:58 1995
+++ linux/net/ipv4/raw.c	Thu Nov 30 11:15:55 1995
@@ -29,6 +29,7 @@
  *		Alan Cox	:	BSD style RAW socket demultiplexing.
  *		Alan Cox	:	Beginnings of mrouted support.
  *		Alan Cox	:	Added IP_HDRINCL option.
+ *		Alan Cox	:	Skip broadcast check if BSDism set.
  *
  *		This program is free software; you can redistribute it and/or
  *		modify it under the terms of the GNU General Public License
@@ -228,7 +229,11 @@
 	if (sin.sin_addr.s_addr == INADDR_ANY)
 		sin.sin_addr.s_addr = ip_my_addr();
 
-	if (sk->broadcast == 0 && ip_chk_addr(sin.sin_addr.s_addr)==IS_BROADCAST)
+	/*
+	 *	BSD raw sockets forget to check SO_BROADCAST ....
+	 */
+	 
+	if (!sk->bsdism && sk->broadcast == 0 && ip_chk_addr(sin.sin_addr.s_addr)==IS_BROADCAST)
 		return -EACCES;
 
 	if(sk->ip_hdrincl)
diff -u --recursive --new-file v1.3.45/linux/net/ipv4/route.c linux/net/ipv4/route.c
--- v1.3.45/linux/net/ipv4/route.c	Fri Nov 17 08:42:34 1995
+++ linux/net/ipv4/route.c	Thu Nov 30 11:15:55 1995
@@ -37,6 +37,8 @@
  *		Alan Cox	:	Faster /proc handling
  *	Alexey Kuznetsov	:	Massive rework to support tree based routing,
  *					routing caches and better behaviour.
+ *		
+ *		Olaf Erb	:	irtt wasnt being copied right.
  *
  *		This program is free software; you can redistribute it and/or
  *		modify it under the terms of the GNU General Public License
@@ -496,6 +498,7 @@
 	fi->fib_refcnt++;
 	fi->fib_next = fib_info_list;
 	fi->fib_prev = NULL;
+	fi->fib_irtt = irtt;
 	if (fib_info_list)
 		fib_info_list->fib_prev = fi;
 	fib_info_list = fi;
diff -u --recursive --new-file v1.3.45/linux/net/ipx/af_ipx.c linux/net/ipx/af_ipx.c
--- v1.3.45/linux/net/ipx/af_ipx.c	Sat Nov 25 19:04:58 1995
+++ linux/net/ipx/af_ipx.c	Thu Nov 30 11:15:55 1995
@@ -324,12 +324,12 @@
 	return NOTIFY_DONE;
 }
 
-static int
-ipxitf_def_skb_handler(struct sock *sock, struct sk_buff *skb)
+static int ipxitf_def_skb_handler(struct sock *sock, struct sk_buff *skb)
 {
 	int	retval;
 
-	if((retval = sock_queue_rcv_skb(sock, skb))<0) {
+	if((retval = sock_queue_rcv_skb(sock, skb))<0) 
+	{
 		/*
 	 	 *	We do a FREE_WRITE here because this indicates how
 	 	 *	to treat the socket with which the packet is 
@@ -343,8 +343,13 @@
 	return retval;
 }
 
-static int
-ipxitf_demux_socket(ipx_interface *intrfc, struct sk_buff *skb, int copy) 
+/*
+ *	On input if skb->sk is set the buffer is a socket sending. We need to ensure the
+ *	accounting is kept right in these cases. At the moment the socket write queue is
+ *	charged for the memory.
+ */
+ 
+static int ipxitf_demux_socket(ipx_interface *intrfc, struct sk_buff *skb, int copy) 
 {
 	ipx_packet	*ipx = (ipx_packet *)(skb->h.raw);
 	ipx_socket	*sock1 = NULL, *sock2 = NULL;
@@ -359,33 +364,42 @@
 	 *	The *SPECIAL* socket list contains: 0x452(SAP), 0x453(RIP) and
 	 *	0x456(Diagnostic).
 	 */
-	if (ipx_primary_net && (intrfc != ipx_primary_net)) {
-		switch (ntohs(ipx->ipx_dest.sock)) {
-		case 0x452:
-		case 0x453:
-		case 0x456:
-			/*
-			 *	The appropriate thing to do here is to
-			 * 	dup the packet and route to the primary net
-			 *	interface via ipxitf_send; however, we'll cheat
-			 *	and just demux it here.
-			 */
-			sock2 = ipxitf_find_socket(ipx_primary_net, 
+	 
+	if (ipx_primary_net && (intrfc != ipx_primary_net)) 
+	{
+		switch (ntohs(ipx->ipx_dest.sock)) 
+		{
+			case 0x452:
+			case 0x453:
+			case 0x456:
+				/*
+				 *	The appropriate thing to do here is to
+				 * 	dup the packet and route to the primary net
+				 *	interface via ipxitf_send; however, we'll cheat
+				 *	and just demux it here.
+				 */
+				sock2 = ipxitf_find_socket(ipx_primary_net, 
 					ipx->ipx_dest.sock);
-			break;
-		default:
-			break;
+				break;
+			default:
+				break;
 		}
 	}
 
-	/* if there is nothing to do, return */
-	if ((sock1 == NULL) && (sock2 == NULL)) {
+	/* 
+	 *	if there is nothing to do, return. The kfree will
+	 *	cancel any charging.
+	 */
+	 
+	if (sock1 == NULL && sock2 == NULL) 
+	{
 		if (!copy) 
 			kfree_skb(skb,FREE_WRITE);
 		return 0;
 	}
 
-	/* This next segment of code is a little awkward, but it sets it up
+	/*
+	 * This next segment of code is a little awkward, but it sets it up
 	 * so that the appropriate number of copies of the SKB are made and 
 	 * that skb1 and skb2 point to it (them) so that it (they) can be 
 	 * demuxed to sock1 and/or sock2.  If we are unable to make enough
@@ -396,43 +410,48 @@
 	 * this is ok as no socket owns an input buffer.
 	 */
 	 
-	if(skb->sk)
+	if(skb->sk && !copy)
 	{
 		skb->sk->wmem_alloc -= skb->truesize;	/* Adjust */
 		skb->sk=NULL;				/* Disown */
 	}
 
 	 
-	if (copy) {
+	if (copy) 
+	{
 		skb1 = skb_clone(skb, GFP_ATOMIC);
-		if (skb1 != NULL) {
+		if (skb1 != NULL) 
 			skb1->arp = skb1->free = 1;
-		}
-	} else {
+	} 
+	else 
+	{
 		skb1 = skb;
 	}
 	
-	if (skb1 == NULL) return -ENOMEM; 
+	if (skb1 == NULL) 
+		return -ENOMEM; 
 
-	/* Do we need 2 SKBs? */
-	if (sock1 && sock2) {
+	/*
+	 *	Do we need 2 SKBs? 
+	 */
+	 
+	if (sock1 && sock2) 
+	{
 		skb2 = skb_clone(skb1, GFP_ATOMIC);
-		if (skb2 != NULL) {
+		if (skb2 != NULL) 
 			skb2->arp = skb2->free = 1;
-		}
-	} else {
-		skb2 = skb1;
 	}
+	else 
+		skb2 = skb1;
 		
-	if (sock1) {
+	if (sock1)
 		(void) ipxitf_def_skb_handler(sock1, skb1);
-	}
 
-	if (skb2 == NULL) return -ENOMEM;
+	if (skb2 == NULL) 
+		return -ENOMEM;
 
-	if (sock2) {
+	if (sock2)
 		(void) ipxitf_def_skb_handler(sock2, skb2);
-	}
 
 	return 0;
 }
@@ -443,29 +462,13 @@
 	struct sk_buff	*skb2;
 	int	in_offset = skb->h.raw - skb->head;
 	int	out_offset = intrfc->if_ipx_offset;
-#if 0
-	char	*oldraw;
-#endif	
 	int	len;
 
 	/* Hopefully, most cases */
 	if (in_offset >= out_offset) {
-/*		skb_push(skb,out_offset);*/
-		skb->arp = skb->free = 1;
-		return skb;
-	}
-
-#if 0
-	/* Existing SKB will work, just need to move things around a little */
-	if (in_offset > out_offset) {
-		oldraw = skb->h.raw;
-		skb->h.raw = &(skb->data[out_offset]);
-		memmove(skb->h.raw, oldraw, skb->len);
-		skb->len += out_offset;
 		skb->arp = skb->free = 1;
 		return skb;
 	}
-#endif	
 
 	/* Need new SKB */
 	len = skb->len + out_offset;
@@ -481,8 +484,7 @@
 	return skb2;
 }
 
-static int
-ipxitf_send(ipx_interface *intrfc, struct sk_buff *skb, char *node)
+static int ipxitf_send(ipx_interface *intrfc, struct sk_buff *skb, char *node)
 {
 	ipx_packet	*ipx = (ipx_packet *)(skb->h.raw);
 	struct device	*dev = intrfc->if_dev;
@@ -491,29 +493,51 @@
 	int		send_to_wire = 1;
 	int		addr_len;
 	
-	/* We need to know how many skbuffs it will take to send out this
-	 * packet to avoid unnecessary copies.
+	/* 
+	 *	We need to know how many skbuffs it will take to send out this
+	 *	packet to avoid unnecessary copies.
 	 */
+	 
 	if ((dl == NULL) || (dev == NULL) || (dev->flags & IFF_LOOPBACK)) 
 		send_to_wire = 0;
 
-	/* See if this should be demuxed to sockets on this interface */
-	if (ipx->ipx_dest.net == intrfc->if_netnum) {
+	/*
+	 *	See if this should be demuxed to sockets on this interface 
+	 *
+	 *	We want to ensure the original was eaten or that we only use
+	 *	up clones.
+	 */
+	 
+	if (ipx->ipx_dest.net == intrfc->if_netnum) 
+	{
+		/*
+		 *	To our own node, loop and free the original.
+		 */
 		if (memcmp(intrfc->if_node, node, IPX_NODE_LEN) == 0) 
 			return ipxitf_demux_socket(intrfc, skb, 0);
-		if (memcmp(ipx_broadcast_node, node, IPX_NODE_LEN) == 0) {
+		/*
+		 *	Broadcast, loop and possibly keep to send on.
+		 */
+		if (memcmp(ipx_broadcast_node, node, IPX_NODE_LEN) == 0) 
+		{
 			ipxitf_demux_socket(intrfc, skb, send_to_wire);
-			if (!send_to_wire) return 0;
+			if (!send_to_wire) 
+				return 0;
 		}
 	}
 
-	/* if the originating net is not equal to our net; this is routed */
-	if (ipx->ipx_source.net != intrfc->if_netnum) {
+	/*
+	 *	if the originating net is not equal to our net; this is routed 
+	 */
+	 
+	if (ipx->ipx_source.net != intrfc->if_netnum) 
+	{
 		if (++(ipx->ipx_tctrl) > ipxcfg_max_hops) 
 			send_to_wire = 0;
 	}
 
-	if (!send_to_wire) {
+	if (!send_to_wire) 
+	{
 		/*
 		 *	We do a FREE_WRITE here because this indicates how
 		 *	to treat the socket with which the packet is 
@@ -526,45 +550,47 @@
 		return 0;
 	}
 
-	/* determine the appropriate hardware address */
+	/*
+	 *	Determine the appropriate hardware address 
+	 */
+	 
 	addr_len = dev->addr_len;
-	if (memcmp(ipx_broadcast_node, node, IPX_NODE_LEN) == 0) {
+	if (memcmp(ipx_broadcast_node, node, IPX_NODE_LEN) == 0) 
 		memcpy(dest_node, dev->broadcast, addr_len);
-	} else {
+	else
 		memcpy(dest_node, &(node[IPX_NODE_LEN-addr_len]), addr_len);
-	}
 
-	/* make any compensation for differing physical/data link size */
+	/*
+	 *	Make any compensation for differing physical/data link size 
+	 */
+	 
 	skb = ipxitf_adjust_skbuff(intrfc, skb);
-	if (skb == NULL) return 0;
+	if (skb == NULL) 
+		return 0;
 
 	/* set up data link and physical headers */
 	skb->dev = dev;
 	dl->datalink_header(dl, skb, dest_node);
-#ifdef ALREADY_DONE_GUV
-	if (skb->sk != NULL) {
-		/* This is an outbound packet from this host.  We need to 
-		 * increment the write count.
-		 */
-		skb->sk->wmem_alloc += skb->truesize;
-	}
-#endif
 #if 0
-	/* Now log the packet just before transmission */
+	/* 
+	 *	Now log the packet just before transmission 
+	 */
+	 
 	dump_pkt("IPX snd:", (ipx_packet *)skb->h.raw);
 	dump_data("ETH hdr:", skb->data, skb->h.raw - skb->data);
 #endif
 
-	/* Send it out */
+	/*
+	 *	Send it out 
+	 */
+	 
 	dev_queue_xmit(skb, dev, SOPRI_NORMAL);
 	return 0;
 }
 
-static int
-ipxrtr_add_route(unsigned long, ipx_interface *, unsigned char *);
+static int ipxrtr_add_route(unsigned long, ipx_interface *, unsigned char *);
 
-static int
-ipxitf_add_local_route(ipx_interface *intrfc)
+static int ipxitf_add_local_route(ipx_interface *intrfc)
 {
 	return ipxrtr_add_route(intrfc->if_netnum, intrfc, NULL);
 }
@@ -1757,8 +1783,10 @@
 	if (sk->zapped) return -EIO; /* Socket not bound */
 	if(flags) return -EINVAL;
 		
-	if(usipx) {
-		if(sk->ipx_port == 0) {
+	if(usipx) 
+	{
+		if(sk->ipx_port == 0) 
+		{
 			struct sockaddr_ipx uaddr;
 			int ret;
 
@@ -1772,7 +1800,9 @@
 			return -EINVAL;
 		if(usipx->sipx_family != AF_IPX)
 			return -EINVAL;
-	} else {
+	}
+	else 
+	{
 		if(sk->state!=TCP_ESTABLISHED)
 			return -ENOTCONN;
 		usipx=&local_sipx;
diff -u --recursive --new-file v1.3.45/linux/net/netlink.c linux/net/netlink.c
--- v1.3.45/linux/net/netlink.c	Fri Nov 17 08:42:34 1995
+++ linux/net/netlink.c	Thu Nov 30 15:58:24 1995
@@ -90,7 +90,7 @@
 		if(file->f_flags&O_NONBLOCK)
 		{
 			sti();
-			return -EWOULDBLOCK;
+			return -EAGAIN;
 		}
 		interruptible_sleep_on(&read_space_wait[minor]);
 		if(current->signal & ~current->blocked)
@@ -199,7 +199,7 @@
 		save_flags(flags);
 		cli();
 		if(rdq_size[unit]+skb->len>MAX_QBYTES)
-			ret=-EWOULDBLOCK;
+			ret=-EAGAIN;
 		else
 		{	
 			skb_queue_tail(&skb_queue_rd[unit], skb);
diff -u --recursive --new-file v1.3.45/linux/net/netrom/af_netrom.c linux/net/netrom/af_netrom.c
--- v1.3.45/linux/net/netrom/af_netrom.c	Wed Nov  8 07:11:46 1995
+++ linux/net/netrom/af_netrom.c	Thu Nov 30 11:15:55 1995
@@ -1368,7 +1368,7 @@
 {
 	sock_register(nr_proto_ops.family, &nr_proto_ops);
 	register_netdevice_notifier(&nr_dev_notifier);
-	printk("G4KLX NET/ROM for Linux. Version 0.3 ALPHA for AX25.030 Linux 1.3.35\n");
+	printk("G4KLX NET/ROM for Linux. Version 0.4 ALPHA for AX25.030 Linux 1.3.45\n");
 
 	nr_default.quality    = NR_DEFAULT_QUAL;
 	nr_default.obs_count  = NR_DEFAULT_OBS;
diff -u --recursive --new-file v1.3.45/linux/net/netrom/nr_route.c linux/net/netrom/nr_route.c
--- v1.3.45/linux/net/netrom/nr_route.c	Fri Nov 17 08:42:34 1995
+++ linux/net/netrom/nr_route.c	Thu Nov 30 11:15:55 1995
@@ -515,7 +515,6 @@
 static struct device *nr_ax25_dev_get(char *devname)
 {
 	struct device *dev;
-	ax25_address callsign;
 
 	if ((dev = dev_get(devname)) == NULL)
 		return NULL;
@@ -523,9 +522,11 @@
 	if ((dev->flags & IFF_UP) && dev->type == ARPHRD_AX25)
 		return dev;
 
+#ifdef CONFIG_BPQETHER
 	if ((dev->flags & IFF_UP) && dev->type == ARPHRD_ETHER)
-		if (arp_query((unsigned char *)&callsign, dev->pa_addr, dev))
+		if (ax25_bpq_get_addr(dev) != NULL)
 			return dev;
+#endif
 	
 	return NULL;
 }
